source: trunk/src/widgets/qlistbox.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: 116.7 KB
Line 
1/**********************************************************************
2** $Id: qlistbox.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of QListBox widget class
5**
6** Created : 941121
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 "qglobal.h"
39#if defined(Q_CC_BOR)
40// needed for qsort() because of a std namespace problem on Borland
41#include "qplatformdefs.h"
42#endif
43
44#include "qlistbox.h"
45#ifndef QT_NO_LISTBOX
46#include "qmemarray.h"
47#include "qfontmetrics.h"
48#include "qpainter.h"
49#include "qstrlist.h"
50#include "qpixmap.h"
51#include "qapplication.h"
52#include "qptrdict.h"
53#include "qtimer.h"
54#include "qstringlist.h"
55#include "qstyle.h"
56#include "qpopupmenu.h"
57#if defined(QT_ACCESSIBILITY_SUPPORT)
58#include "qaccessible.h"
59#endif
60
61#include <stdlib.h>
62
63class QListBoxPrivate
64{
65public:
66 QListBoxPrivate( QListBox *lb ):
67 head( 0 ), last( 0 ), cache( 0 ), cacheIndex( -1 ), current( 0 ),
68 highlighted( 0 ), columnPos( 1 ), rowPos( 1 ), rowPosCache( 0 ),
69 columnPosOne( 0 ), rowMode( QListBox::FixedNumber ),
70 columnMode( QListBox::FixedNumber ), numRows( 1 ), numColumns( 1 ),
71 currentRow( 0 ), currentColumn( 0 ),
72 mousePressRow( -1 ), mousePressColumn( -1 ),
73 mouseMoveRow( -1 ), mouseMoveColumn( -1 ), mouseInternalPress( FALSE ),
74 scrollTimer( 0 ), updateTimer( 0 ), visibleTimer( 0 ),
75 selectionMode( QListBox::Single ),
76 count( 0 ),
77 listBox( lb ), currInputString( QString::null ),
78 rowModeWins( FALSE ),
79 ignoreMoves( FALSE ),
80 layoutDirty( TRUE ),
81 mustPaintAll( TRUE ),
82 dragging( FALSE ),
83 dirtyDrag ( FALSE ),
84 variableHeight( TRUE /* !!! ### FALSE */ ),
85 variableWidth( FALSE ),
86 inMenuMode( FALSE )
87 {}
88 int findItemByName( int item, const QString &text );
89 ~QListBoxPrivate();
90
91 QListBoxItem * head, *last, *cache;
92 int cacheIndex;
93 QListBoxItem * current, *highlighted, *tmpCurrent;
94
95 QMemArray<int> columnPos;
96 QMemArray<int> rowPos;
97 int rowPosCache;
98 int columnPosOne;
99
100 QListBox::LayoutMode rowMode;
101 QListBox::LayoutMode columnMode;
102 int numRows;
103 int numColumns;
104
105 int currentRow;
106 int currentColumn;
107 int mousePressRow;
108 int mousePressColumn;
109 int mouseMoveRow;
110 int mouseMoveColumn;
111 bool mouseInternalPress;
112
113 QTimer * scrollTimer;
114 QTimer * updateTimer;
115 QTimer * visibleTimer;
116 QTimer * resizeTimer;
117
118 QPoint scrollPos;
119
120 QListBox::SelectionMode selectionMode;
121
122 int count;
123
124
125 QListBox *listBox;
126 QString currInputString;
127 QTimer *inputTimer;
128
129 QListBoxItem *pressedItem, *selectAnchor;
130
131 uint select :1;
132 uint pressedSelected :1;
133 uint rowModeWins :1;
134 uint ignoreMoves :1;
135 uint clearing :1;
136 uint layoutDirty :1;
137 uint mustPaintAll :1;
138 uint dragging :1;
139 uint dirtyDrag :1;
140 uint variableHeight :1;
141 uint variableWidth :1;
142 uint inMenuMode :1;
143
144 QRect *rubber;
145 QPtrDict<bool> selectable;
146
147 struct SortableItem {
148 QListBoxItem *item;
149 };
150};
151
152
153QListBoxPrivate::~QListBoxPrivate()
154{
155 Q_ASSERT( !head );
156}
157
158
159/*!
160 \class QListBoxItem qlistbox.h
161 \brief The QListBoxItem class is the base class of all list box items.
162
163 This class is an abstract base class used for all list box items.
164 If you need to insert customized items into a QListBox you must
165 inherit this class and reimplement paint(), height() and width().
166
167 \sa QListBox
168*/
169
170/*!
171 \fn bool QListBox::dragSelect() const
172 \internal
173*/
174
175/*!
176 \fn void QListBox::setDragSelect( bool )
177 \internal
178*/
179
180/*!
181 \fn bool QListBox::autoScroll() const
182 \internal
183*/
184
185/*!
186 \fn void QListBox::setAutoScroll( bool )
187 \internal
188*/
189
190/*!
191 \fn bool QListBox::autoScrollBar() const
192
193 Returns TRUE if vScrollBarMode() is \c Auto; otherwise returns
194 FALSE.
195*/
196
197/*!
198 \fn void QListBox::setAutoScrollBar( bool enable )
199
200 If \a enable is TRUE sets setVScrollBarMode() to \c AlwaysOn;
201 otherwise sets setVScrollBarMode() to \c AlwaysOff.
202*/
203
204/*!
205 \fn bool QListBox::scrollBar() const
206
207 Returns FALSE if vScrollBarMode() is \c AlwaysOff; otherwise
208 returns TRUE.
209*/
210
211/*!
212 \fn void QListBox::setScrollBar( bool enable )
213
214 If \a enable is TRUE sets setVScrollBarMode() to \c AlwaysOn;
215 otherwise sets setVScrollBarMode() to \c AlwaysOff.
216*/
217
218/*!
219 \fn bool QListBox::autoBottomScrollBar() const
220
221 Returns TRUE if hScrollBarMode() is \c Auto; otherwise returns
222 FALSE.
223*/
224
225/*!
226 \fn void QListBox::setAutoBottomScrollBar( bool enable )
227
228 If \a enable is TRUE sets setHScrollBarMode() to \c AlwaysOn;
229 otherwise sets setHScrollBarMode() to \c AlwaysOff.
230*/
231
232/*!
233 \fn bool QListBox::bottomScrollBar() const
234
235 Returns FALSE if vScrollBarMode() is \c AlwaysOff; otherwise
236 returns TRUE.
237*/
238
239/*!
240 \fn void QListBox::setBottomScrollBar( bool enable )
241
242 If \a enable is TRUE sets setHScrollBarMode() to \c AlwaysOn;
243 otherwise sets setHScrollBarMode() to \c AlwaysOff.
244*/
245
246/*!
247 \fn bool QListBox::smoothScrolling() const
248 \internal
249*/
250
251/*!
252 \fn void QListBox::setSmoothScrolling( bool )
253 \internal
254*/
255
256/*!
257 \fn bool QListBox::autoUpdate() const
258 \internal
259*/
260
261/*!
262 \fn void QListBox::setAutoUpdate( bool )
263 \internal
264*/
265
266/*!
267 \fn void QListBox::setFixedVisibleLines( int lines )
268 \internal
269*/
270
271/*!
272 \obsolete
273 \fn int QListBox::cellHeight( int i ) const
274 Returns the item height of item \a i.
275 \sa itemHeight()
276*/
277
278/*!
279 \obsolete
280 \overload int QListBox::cellHeight() const
281 Returns the item height of the first item, item 0.
282 \sa itemHeight()
283*/
284
285/*!
286 \obsolete
287 \fn int QListBox::cellWidth() const
288 Returns the maximum item width.
289 \sa maxItemWidth()
290*/
291
292/*!
293 \overload int QListBox::cellWidth(int i) const
294 \internal
295*/
296
297/*!
298 \obsolete
299 \fn int QListBox::numCols() const
300 Returns the number of columns.
301 \sa numColumns()
302*/
303
304/*!
305 \fn void QListBox::updateCellWidth()
306 \internal
307*/
308
309/*!
310 \obsolete
311 \fn int QListBox::totalWidth() const
312 Returns contentsWidth().
313*/
314
315/*!
316 \obsolete
317 \fn int QListBox::totalHeight() const
318 Returns contentsHeight().
319*/
320
321/*!
322 \obsolete
323 \fn int QListBox::findItem( int yPos ) const
324 Returns the index of the item a point (0, \a yPos).
325 \sa index() itemAt()
326*/
327
328
329/*!
330 Constructs an empty list box item in the list box \a listbox.
331*/
332
333QListBoxItem::QListBoxItem( QListBox* listbox )
334{
335 lbox = listbox;
336 s = FALSE;
337 dirty = TRUE;
338 custom_highlight = FALSE;
339 p = n = 0;
340
341 // just something that'll look noticeable in the debugger
342 x = y = 42;
343
344 if (listbox)
345 listbox->insertItem( this );
346}
347
348/*!
349 Constructs an empty list box item in the list box \a listbox and
350 inserts it after the item \a after or at the beginning if \a after
351 is 0.
352*/
353
354QListBoxItem::QListBoxItem( QListBox* listbox, QListBoxItem *after )
355{
356 lbox = listbox;
357 s = FALSE;
358 dirty = TRUE;
359 custom_highlight = FALSE;
360 p = n = 0;
361
362 // just something that'll be noticeable in the debugger
363 x = y = 42;
364
365 if (listbox)
366 listbox->insertItem( this, after );
367}
368
369
370/*!
371 Destroys the list box item.
372*/
373
374QListBoxItem::~QListBoxItem()
375{
376 if ( lbox )
377 lbox->takeItem( this );
378}
379
380
381/*!
382 Defines whether the list box item is responsible for drawing
383 itself in a highlighted state when being selected.
384
385 If \a b is FALSE (the default), the list box will draw some
386 default highlight indicator before calling paint().
387
388 \sa selected(), paint()
389*/
390void QListBoxItem::setCustomHighlighting( bool b )
391{
392 custom_highlight = b;
393}
394
395/*!
396 \fn void QListBoxItem::paint( QPainter *p )
397
398 Implement this function to draw your item. The painter, \a p, is
399 already open for painting.
400
401 \sa height(), width()
402*/
403
404/*!
405 \fn int QListBoxItem::width( const QListBox* lb ) const
406
407 Reimplement this function to return the width of your item. The \a
408 lb parameter is the same as listBox() and is provided for
409 convenience and compatibility.
410
411 The default implementation returns
412 \l{QApplication::globalStrut()}'s width.
413
414 \sa paint(), height()
415*/
416int QListBoxItem::width(const QListBox*) const
417{
418 return QApplication::globalStrut().width();
419}
420
421/*!
422 \fn int QListBoxItem::height( const QListBox* lb ) const
423
424 Implement this function to return the height of your item. The \a
425 lb parameter is the same as listBox() and is provided for
426 convenience and compatibility.
427
428 The default implementation returns
429 \l{QApplication::globalStrut()}'s height.
430
431 \sa paint(), width()
432*/
433int QListBoxItem::height(const QListBox*) const
434{
435 return QApplication::globalStrut().height();
436}
437
438
439/*!
440 Returns the text of the item. This text is also used for sorting.
441
442 \sa setText()
443*/
444QString QListBoxItem::text() const
445{
446 return txt;
447}
448
449/*!
450 Returns the pixmap associated with the item, or 0 if there isn't
451 one.
452
453 The default implementation returns 0.
454*/
455const QPixmap *QListBoxItem::pixmap() const
456{
457 return 0;
458}
459
460/*!
461 If \a b is TRUE (the default) then this item can be selected by
462 the user; otherwise this item cannot be selected by the user.
463
464 \sa isSelectable()
465*/
466
467void QListBoxItem::setSelectable( bool b )
468{
469 if ( !listBox() )
470 return;
471 bool *sel = listBox()->d->selectable.find( this );
472 if ( !sel )
473 listBox()->d->selectable.insert( this, new bool( b ) );
474 else
475 listBox()->d->selectable.replace( this, new bool( b ) );
476}
477
478/*!
479 Returns TRUE if this item is selectable (the default); otherwise
480 returns FALSE.
481
482 \sa setSelectable()
483*/
484
485bool QListBoxItem::isSelectable() const
486{
487 if ( !listBox() )
488 return TRUE;
489 bool *sel = listBox()->d->selectable.find( ( (QListBoxItem*)this ) );
490 if ( !sel )
491 return TRUE;
492 else
493 return *sel;
494}
495
496/*!
497 \fn void QListBoxItem::setText( const QString &text )
498
499 Sets the text of the QListBoxItem to \a text. This \a text is also
500 used for sorting. The text is not shown unless explicitly drawn in
501 paint().
502
503 \sa text()
504*/
505
506
507/*!
508 \class QListBoxText qlistbox.h
509 \brief The QListBoxText class provides list box items that display text.
510
511 \ingroup advanced
512
513 The text is drawn in the widget's current font. If you need
514 several different fonts, you must implement your own subclass of
515 QListBoxItem.
516
517 \sa QListBox, QListBoxItem
518*/
519
520
521/*!
522 Constructs a list box item in list box \a listbox showing the text
523 \a text.
524*/
525QListBoxText::QListBoxText( QListBox *listbox, const QString &text )
526 :QListBoxItem( listbox )
527{
528 setText( text );
529}
530
531/*!
532 Constructs a list box item showing the text \a text.
533*/
534
535QListBoxText::QListBoxText( const QString &text )
536 :QListBoxItem()
537{
538 setText( text );
539}
540
541/*!
542 Constructs a list box item in list box \a listbox showing the text
543 \a text. The item is inserted after the item \a after, or at the
544 beginning if \a after is 0.
545*/
546
547QListBoxText::QListBoxText( QListBox* listbox, const QString &text, QListBoxItem *after )
548 : QListBoxItem( listbox, after )
549{
550 setText( text );
551}
552
553/*!
554 Destroys the item.
555*/
556
557QListBoxText::~QListBoxText()
558{
559}
560
561/*!
562 Draws the text using \a painter.
563*/
564
565void QListBoxText::paint( QPainter *painter )
566{
567 int itemHeight = height( listBox() );
568 QFontMetrics fm = painter->fontMetrics();
569 int yPos = ( ( itemHeight - fm.height() ) / 2 ) + fm.ascent();
570 painter->drawText( 3, yPos, text() );
571}
572
573/*!
574 Returns the height of a line of text in list box \a lb.
575
576 \sa paint(), width()
577*/
578
579int QListBoxText::height( const QListBox* lb ) const
580{
581 int h = lb ? lb->fontMetrics().lineSpacing() + 2 : 0;
582 return QMAX( h, QApplication::globalStrut().height() );
583}
584
585/*!
586 Returns the width of this line in list box \a lb.
587
588 \sa paint(), height()
589*/
590
591int QListBoxText::width( const QListBox* lb ) const
592{
593 int w = lb ? lb->fontMetrics().width( text() ) + 6 : 0;
594 return QMAX( w, QApplication::globalStrut().width() );
595}
596
597int QListBoxText::RTTI = 1;
598
599/*!
600 \fn int QListBoxText::rtti() const
601
602 \reimp
603
604 Returns 1.
605
606 Make your derived classes return their own values for rtti(), and
607 you can distinguish between listbox items. You should use values
608 greater than 1000 preferably a large random number, to allow for
609 extensions to this class.
610*/
611
612int QListBoxText::rtti() const
613{
614 return RTTI;
615}
616
617/*!
618 \class QListBoxPixmap qlistbox.h
619 \brief The QListBoxPixmap class provides list box items with a
620 pixmap and optional text.
621
622 \ingroup advanced
623
624 Items of this class are drawn with the pixmap on the left with the
625 optional text to the right of the pixmap.
626
627 \sa QListBox, QListBoxItem
628*/
629
630
631/*!
632 Constructs a new list box item in list box \a listbox showing the
633 pixmap \a pixmap.
634*/
635
636QListBoxPixmap::QListBoxPixmap( QListBox* listbox, const QPixmap &pixmap )
637 : QListBoxItem( listbox )
638{
639 pm = pixmap;
640}
641
642/*!
643 Constructs a new list box item showing the pixmap \a pixmap.
644*/
645
646QListBoxPixmap::QListBoxPixmap( const QPixmap &pixmap )
647 : QListBoxItem()
648{
649 pm = pixmap;
650}
651
652/*!
653 Constructs a new list box item in list box \a listbox showing the
654 pixmap \a pixmap. The item gets inserted after the item \a after,
655 or at the beginning if \a after is 0.
656*/
657
658QListBoxPixmap::QListBoxPixmap( QListBox* listbox, const QPixmap &pixmap, QListBoxItem *after )
659 : QListBoxItem( listbox, after )
660{
661 pm = pixmap;
662}
663
664
665/*!
666 Destroys the item.
667*/
668
669QListBoxPixmap::~QListBoxPixmap()
670{
671}
672
673
674/*!
675 Constructs a new list box item in list box \a listbox showing the
676 pixmap \a pix and the text \a text.
677*/
678QListBoxPixmap::QListBoxPixmap( QListBox* listbox, const QPixmap &pix, const QString& text)
679 : QListBoxItem( listbox )
680{
681 pm = pix;
682 setText( text );
683}
684
685/*!
686 Constructs a new list box item showing the pixmap \a pix and the
687 text to \a text.
688*/
689QListBoxPixmap::QListBoxPixmap( const QPixmap & pix, const QString& text)
690 : QListBoxItem()
691{
692 pm = pix;
693 setText( text );
694}
695
696/*!
697 Constructs a new list box item in list box \a listbox showing the
698 pixmap \a pix and the string \a text. The item gets inserted after
699 the item \a after, or at the beginning if \a after is 0.
700*/
701QListBoxPixmap::QListBoxPixmap( QListBox* listbox, const QPixmap & pix, const QString& text,
702 QListBoxItem *after )
703 : QListBoxItem( listbox, after )
704{
705 pm = pix;
706 setText( text );
707}
708
709/*!
710 \fn const QPixmap *QListBoxPixmap::pixmap() const
711
712 Returns the pixmap associated with the item.
713*/
714
715
716/*!
717 Draws the pixmap using \a painter.
718*/
719
720void QListBoxPixmap::paint( QPainter *painter )
721{
722 int itemHeight = height( listBox() );
723 int yPos;
724
725 const QPixmap *pm = pixmap();
726 if ( pm && ! pm->isNull() ) {
727 yPos = ( itemHeight - pm->height() ) / 2;
728 painter->drawPixmap( 3, yPos, *pm);
729 }
730
731 if ( !text().isEmpty() ) {
732 QFontMetrics fm = painter->fontMetrics();
733 yPos = ( ( itemHeight - fm.height() ) / 2 ) + fm.ascent();
734 painter->drawText( pm->width() + 5, yPos, text() );
735 }
736}
737
738/*!
739 Returns the height of the pixmap in list box \a lb.
740
741 \sa paint(), width()
742*/
743
744int QListBoxPixmap::height( const QListBox* lb ) const
745{
746 int h;
747 if ( text().isEmpty() )
748 h = pm.height();
749 else
750 h = QMAX( pm.height(), lb->fontMetrics().lineSpacing() + 2 );
751 return QMAX( h, QApplication::globalStrut().height() );
752}
753
754/*!
755 Returns the width of the pixmap plus some margin in list box \a lb.
756
757 \sa paint(), height()
758*/
759
760int QListBoxPixmap::width( const QListBox* lb ) const
761{
762 if ( text().isEmpty() )
763 return QMAX( pm.width() + 6, QApplication::globalStrut().width() );
764 return QMAX( pm.width() + lb->fontMetrics().width( text() ) + 6,
765 QApplication::globalStrut().width() );
766}
767
768int QListBoxPixmap::RTTI = 2;
769
770/*!
771 \fn int QListBoxPixmap::rtti() const
772
773 \reimp
774
775 Returns 2.
776
777 Make your derived classes return their own values for rtti(), and
778 you can distinguish between listbox items. You should use values
779 greater than 1000 preferably a large random number, to allow for
780 extensions to this class.
781*/
782
783int QListBoxPixmap::rtti() const
784{
785 return RTTI;
786}
787
788/*!
789 \class QListBox qlistbox.h
790 \brief The QListBox widget provides a list of selectable, read-only items.
791
792 \ingroup advanced
793 \mainclass
794
795 This is typically a single-column list in which either no item or
796 one item is selected, but it can also be used in many other ways.
797
798 QListBox will add scroll bars as necessary, but it isn't intended
799 for \e really big lists. If you want more than a few thousand
800 items, it's probably better to use a different widget mainly
801 because the scroll bars won't provide very good navigation, but
802 also because QListBox may become slow with huge lists. (See
803 QListView and QTable for possible alternatives.)
804
805 There are a variety of selection modes described in the
806 QListBox::SelectionMode documentation. The default is \c Single
807 selection mode, but you can change it using setSelectionMode().
808 (setMultiSelection() is still provided for compatibility with Qt
809 1.x. We recommend using setSelectionMode() in all code.)
810
811 Because QListBox offers multiple selection it must display
812 keyboard focus and selection state separately. Therefore there are
813 functions both to set the selection state of an item, i.e.
814 setSelected(), and to set which item displays keyboard focus, i.e.
815 setCurrentItem().
816
817 The list box normally arranges its items in a single column and
818 adds a vertical scroll bar if required. It is possible to have a
819 different fixed number of columns (setColumnMode()), or as many
820 columns as will fit in the list box's assigned screen space
821 (setColumnMode(FitToWidth)), or to have a fixed number of rows
822 (setRowMode()) or as many rows as will fit in the list box's
823 assigned screen space (setRowMode(FitToHeight)). In all these
824 cases QListBox will add scroll bars, as appropriate, in at least
825 one direction.
826
827 If multiple rows are used, each row can be as high as necessary
828 (the normal setting), or you can request that all items will have
829 the same height by calling setVariableHeight(FALSE). The same
830 applies to a column's width, see setVariableWidth().
831
832 The QListBox's items are QListBoxItem objects. QListBox provides
833 methods to insert new items as strings, as pixmaps, and as
834 QListBoxItem * (insertItem() with various arguments), and to
835 replace an existing item with a new string, pixmap or QListBoxItem
836 (changeItem() with various arguments). You can also remove items
837 singly with removeItem() or clear() the entire list box. Note that
838 if you create a QListBoxItem yourself and insert it, QListBox
839 takes ownership of the item.
840
841 You can also create a QListBoxItem, such as QListBoxText or
842 QListBoxPixmap, with the list box as first parameter. The item
843 will then append itself. When you delete an item it is
844 automatically removed from the list box.
845
846 The list of items can be arbitrarily large; QListBox will add
847 scroll bars if necessary. QListBox can display a single-column
848 (the common case) or multiple-columns, and offers both single and
849 multiple selection. QListBox does not support multiple-column
850 items (but QListView and QTable do), or tree hierarchies (but
851 QListView does).
852
853 The list box items can be accessed both as QListBoxItem objects
854 (recommended) and using integer indexes (the original QListBox
855 implementation used an array of strings internally, and the API
856 still supports this mode of operation). Everything can be done
857 using the new objects, and most things can be done using indexes.
858
859 Each item in a QListBox contains a QListBoxItem. One of the items
860 can be the current item. The currentChanged() signal and the
861 highlighted() signal are emitted when a new item becomes current,
862 e.g. because the user clicks on it or QListBox::setCurrentItem()
863 is called. The selected() signal is emitted when the user
864 double-clicks on an item or presses Enter on the current item.
865
866 If the user does not select anything, no signals are emitted and
867 currentItem() returns -1.
868
869 A list box has \c WheelFocus as a default focusPolicy(), i.e. it
870 can get keyboard focus by tabbing, clicking and through the use of
871 the mouse wheel.
872
873 New items can be inserted using insertItem(), insertStrList() or
874 insertStringList(). inSort() is obsolete because this method is
875 quite inefficient. It's preferable to insert the items normally
876 and call sort() afterwards, or to insert a sorted QStringList().
877
878 By default, vertical and horizontal scroll bars are added and
879 removed as necessary. setHScrollBarMode() and setVScrollBarMode()
880 can be used to change this policy.
881
882 If you need to insert types other than strings and pixmaps, you
883 must define new classes which inherit QListBoxItem.
884
885 \warning The list box assumes ownership of all list box items and
886 will delete them when it does not need them any more.
887
888 <img src=qlistbox-m.png> <img src=qlistbox-w.png>
889
890 \sa QListView QComboBox QButtonGroup
891 \link guibooks.html#fowler GUI Design Handbook: List Box (two
892 sections)\endlink
893*/
894
895/*!
896 \enum QListBox::SelectionMode
897
898 This enumerated type is used by QListBox to indicate how it reacts
899 to selection by the user.
900
901 \value Single When the user selects an item, any already-selected
902 item becomes unselected and the user cannot unselect the selected
903 item. This means that the user can never clear the selection, even
904 though the selection may be cleared by the application programmer
905 using QListBox::clearSelection().
906
907 \value Multi When the user selects an item the selection status
908 of that item is toggled and the other items are left alone.
909
910 \value Extended When the user selects an item the selection is
911 cleared and the new item selected. However, if the user presses
912 the Ctrl key when clicking on an item, the clicked item gets
913 toggled and all other items are left untouched. And if the user
914 presses the Shift key while clicking on an item, all items between
915 the current item and the clicked item get selected or unselected,
916 depending on the state of the clicked item. Also, multiple items
917 can be selected by dragging the mouse while the left mouse button
918 is kept pressed.
919
920 \value NoSelection Items cannot be selected.
921
922 In other words, \c Single is a real single-selection list box, \c
923 Multi is a real multi-selection list box, \c Extended is a list
924 box in which users can select multiple items but usually want to
925 select either just one or a range of contiguous items, and \c
926 NoSelection is for a list box where the user can look but not
927 touch.
928*/
929
930
931/*!
932 \enum QListBox::LayoutMode
933
934 This enum type is used to specify how QListBox lays out its rows
935 and columns.
936
937 \value FixedNumber There is a fixed number of rows (or columns).
938
939 \value FitToWidth There are as many columns as will fit
940 on-screen.
941
942 \value FitToHeight There are as many rows as will fit on-screen.
943
944 \value Variable There are as many rows as are required by the
945 column mode. (Or as many columns as required by the row mode.)
946
947 Example: When you call setRowMode( FitToHeight ), columnMode()
948 automatically becomes \c Variable to accommodate the row mode
949 you've set.
950*/
951
952/*!
953 \fn void QListBox::onItem( QListBoxItem *i )
954
955 This signal is emitted when the user moves the mouse cursor onto
956 an item, similar to the QWidget::enterEvent() function. \a i is
957 the QListBoxItem that the mouse has moved on.
958*/
959
960// ### bug here too? enter/leave event may noit considered. move the
961// mouse out of the window and back in, to the same item - does it
962// work?
963
964/*!
965 \fn void QListBox::onViewport()
966
967 This signal is emitted when the user moves the mouse cursor from
968 an item to an empty part of the list box.
969*/
970
971
972/*!
973 Constructs a new empty list box called \a name and with parent \a
974 parent.
975
976 Performance is boosted by modifying the widget flags \a f so that
977 only part of the QListBoxItem children is redrawn. This may be
978 unsuitable for custom QListBoxItem classes, in which case \c
979 WStaticContents and \c WNoAutoErase should be cleared
980 immediately after construction.
981
982 \sa QWidget::clearWFlags() Qt::WidgetFlags
983*/
984
985QListBox::QListBox( QWidget *parent, const char *name, WFlags f )
986 : QScrollView( parent, name, f | WStaticContents | WNoAutoErase )
987{
988 d = new QListBoxPrivate( this );
989 d->updateTimer = new QTimer( this, "listbox update timer" );
990 d->visibleTimer = new QTimer( this, "listbox visible timer" );
991 d->inputTimer = new QTimer( this, "listbox input timer" );
992 d->resizeTimer = new QTimer( this, "listbox resize timer" );
993 d->clearing = FALSE;
994 d->pressedItem = 0;
995 d->selectAnchor = 0;
996 d->select = FALSE;
997 d->rubber = 0;
998 d->selectable.setAutoDelete( TRUE );
999
1000 setMouseTracking( TRUE );
1001 viewport()->setMouseTracking( TRUE );
1002
1003 connect( d->updateTimer, SIGNAL(timeout()),
1004 this, SLOT(refreshSlot()) );
1005 connect( d->visibleTimer, SIGNAL(timeout()),
1006 this, SLOT(ensureCurrentVisible()) );
1007 connect( d->resizeTimer, SIGNAL( timeout() ),
1008 this, SLOT( adjustItems() ) );
1009 viewport()->setBackgroundMode( PaletteBase );
1010 setBackgroundMode( PaletteBackground, PaletteBase );
1011 viewport()->setFocusProxy( this );
1012 viewport()->setFocusPolicy( WheelFocus );
1013}
1014
1015
1016QListBox * QListBox::changedListBox = 0;
1017
1018/*!
1019 Destroys the list box. Deletes all list box items.
1020*/
1021
1022QListBox::~QListBox()
1023{
1024 if ( changedListBox == this )
1025 changedListBox = 0;
1026 clear();
1027 delete d;
1028 d = 0;
1029}
1030
1031/*!
1032 \fn void QListBox::pressed( QListBoxItem *item )
1033
1034 This signal is emitted when the user presses any mouse button. If
1035 \a item is not 0, the cursor is on \a item. If \a item is 0, the
1036 mouse cursor isn't on any item.
1037
1038 Note that you must not delete any QListBoxItem objects in slots
1039 connected to this signal.
1040*/
1041
1042/*!
1043 \overload void QListBox::pressed( QListBoxItem *item, const QPoint &pnt )
1044
1045 This signal is emitted when the user presses any mouse button. If
1046 \a item is not 0, the cursor is on \a item. If \a item is 0, the
1047 mouse cursor isn't on any item.
1048
1049 \a pnt is the position of the mouse cursor in the global
1050 coordinate system (QMouseEvent::globalPos()).
1051
1052 Note that you must not delete any QListBoxItem objects in slots
1053 connected to this signal.
1054
1055 \sa mouseButtonPressed() rightButtonPressed() clicked()
1056*/
1057
1058/*!
1059 \fn void QListBox::clicked( QListBoxItem *item )
1060
1061 This signal is emitted when the user clicks any mouse button. If
1062 \a item is not 0, the cursor is on \a item. If \a item is 0, the
1063 mouse cursor isn't on any item.
1064
1065 Note that you must not delete any QListBoxItem objects in slots
1066 connected to this signal.
1067*/
1068
1069/*!
1070 \overload void QListBox::clicked( QListBoxItem *item, const QPoint &pnt )
1071
1072 This signal is emitted when the user clicks any mouse button. If
1073 \a item is not 0, the cursor is on \a item. If \a item is 0, the
1074 mouse cursor isn't on any item.
1075
1076 \a pnt is the position of the mouse cursor in the global
1077 coordinate system (QMouseEvent::globalPos()). (If the click's
1078 press and release differs by a pixel or two, \a pnt is the
1079 position at release time.)
1080
1081 Note that you must not delete any QListBoxItem objects in slots
1082 connected to this signal.
1083*/
1084
1085/*!
1086 \fn void QListBox::mouseButtonClicked (int button, QListBoxItem * item, const QPoint & pos)
1087
1088 This signal is emitted when the user clicks mouse button \a
1089 button. If \a item is not 0, the cursor is on \a item. If \a item
1090 is 0, the mouse cursor isn't on any item.
1091
1092 \a pos is the position of the mouse cursor in the global
1093 coordinate system (QMouseEvent::globalPos()). (If the click's
1094 press and release differs by a pixel or two, \a pos is the
1095 position at release time.)
1096
1097 Note that you must not delete any QListBoxItem objects in slots
1098 connected to this signal.
1099*/
1100
1101/*!
1102 \fn void QListBox::mouseButtonPressed (int button, QListBoxItem * item, const QPoint & pos)
1103
1104 This signal is emitted when the user presses mouse button \a
1105 button. If \a item is not 0, the cursor is on \a item. If \a item
1106 is 0, the mouse cursor isn't on any item.
1107
1108 \a pos is the position of the mouse cursor in the global
1109 coordinate system (QMouseEvent::globalPos()).
1110
1111 Note that you must not delete any QListBoxItem objects in slots
1112 connected to this signal.
1113*/
1114
1115/*!
1116 \fn void QListBox::doubleClicked( QListBoxItem *item )
1117
1118 This signal is emitted whenever an item is double-clicked. It's
1119 emitted on the second button press, not the second button release.
1120 If \a item is not 0, the cursor is on \a item. If \a item is 0,
1121 the mouse cursor isn't on any item.
1122*/
1123
1124
1125/*!
1126 \fn void QListBox::returnPressed( QListBoxItem * )
1127
1128 This signal is emitted when Enter or Return is pressed. The
1129 argument is currentItem().
1130*/
1131
1132/*!
1133 \fn void QListBox::rightButtonClicked( QListBoxItem *, const QPoint& )
1134
1135 This signal is emitted when the right button is clicked (i.e. when
1136 it's released at the same point where it was pressed). The
1137 arguments are the relevant QListBoxItem (may be 0) and the point
1138 in global coordinates.
1139*/
1140
1141
1142/*!
1143 \fn void QListBox::rightButtonPressed (QListBoxItem *, const QPoint & )
1144
1145 This signal is emitted when the right button is pressed. The
1146 arguments are the relevant QListBoxItem (may be 0) and the point
1147 in global coordinates.
1148*/
1149
1150/*!
1151 \fn void QListBox::contextMenuRequested( QListBoxItem *item, const QPoint & pos )
1152
1153 This signal is emitted when the user invokes a context menu with
1154 the right mouse button or with special system keys, with \a item
1155 being the item under the mouse cursor or the current item,
1156 respectively.
1157
1158 \a pos is the position for the context menu in the global
1159 coordinate system.
1160*/
1161
1162/*!
1163 \fn void QListBox::selectionChanged()
1164
1165 This signal is emitted when the selection set of a list box
1166 changes. This signal is emitted in each selection mode. If the
1167 user selects five items by drag-selecting, QListBox tries to emit
1168 just one selectionChanged() signal so the signal can be connected
1169 to computationally expensive slots.
1170
1171 \sa selected() currentItem()
1172*/
1173
1174/*!
1175 \overload void QListBox::selectionChanged( QListBoxItem *item )
1176
1177 This signal is emitted when the selection in a \c Single selection
1178 list box changes. \a item is the newly selected list box item.
1179
1180 \sa selected() currentItem()
1181*/
1182
1183/*!
1184 \fn void QListBox::currentChanged( QListBoxItem *item )
1185
1186 This signal is emitted when the user makes a new item the current
1187 item. \a item is the new current list box item.
1188
1189 \sa setCurrentItem() currentItem()
1190*/
1191
1192/*!
1193 \fn void QListBox::highlighted( int index )
1194
1195 This signal is emitted when the user makes a new item the current
1196 item. \a index is the index of the new current item.
1197
1198 \sa currentChanged() selected() currentItem() selectionChanged()
1199*/
1200
1201/*!
1202 \overload void QListBox::highlighted( QListBoxItem * )
1203
1204 This signal is emitted when the user makes a new item the current
1205 item. The argument is a pointer to the new current item.
1206
1207 \sa currentChanged() selected() currentItem() selectionChanged()
1208*/
1209
1210/*!
1211 \overload void QListBox::highlighted( const QString &)
1212
1213 This signal is emitted when the user makes a new item the current
1214 item and the item is (or has) as string. The argument is the text
1215 of the new current item.
1216
1217 \sa currentChanged() selected() currentItem() selectionChanged()
1218*/
1219
1220/*!
1221 \fn void QListBox::selected( int index )
1222
1223 This signal is emitted when the user double-clicks on an item or
1224 presses Enter on the current item. \a index is the index of the
1225 selected item.
1226
1227 \sa currentChanged() highlighted() selectionChanged()
1228*/
1229
1230/*!
1231 \overload void QListBox::selected( QListBoxItem * )
1232
1233 This signal is emitted when the user double-clicks on an item or
1234 presses Enter on the current item. The argument is a pointer to
1235 the new selected item.
1236
1237 \sa currentChanged() highlighted() selectionChanged()
1238*/
1239
1240/*!
1241 \overload void QListBox::selected( const QString &)
1242
1243 This signal is emitted when the user double-clicks on an item or
1244 presses Enter on the current item, and the item is (or has) a
1245 string. The argument is the text of the selected item.
1246
1247 \sa currentChanged() highlighted() selectionChanged()
1248*/
1249
1250/*! \reimp */
1251
1252void QListBox::setFont( const QFont &font )
1253{
1254 QScrollView::setFont( font );
1255 triggerUpdate( TRUE );
1256}
1257
1258
1259/*!
1260 \property QListBox::count
1261 \brief the number of items in the list box
1262*/
1263
1264uint QListBox::count() const
1265{
1266 return d->count;
1267}
1268
1269
1270/*!
1271 Inserts the string list \a list into the list at position \a
1272 index.
1273
1274 If \a index is negative, \a list is inserted at the end of the
1275 list. If \a index is too large, the operation is ignored.
1276
1277 \warning This function uses \c{const char *} rather than QString,
1278 so we recommend against using it. It is provided so that legacy
1279 code will continue to work, and so that programs that certainly
1280 will not need to handle code outside a single 8-bit locale can use
1281 it. See insertStringList() which uses real QStrings.
1282
1283 \warning This function is never significantly faster than a loop
1284 around insertItem().
1285
1286 \sa insertItem(), insertStringList()
1287*/
1288
1289void QListBox::insertStrList( const QStrList *list, int index )
1290{
1291 if ( !list ) {
1292#if defined(QT_CHECK_NULL)
1293 Q_ASSERT( list != 0 );
1294#endif
1295 return;
1296 }
1297 insertStrList( *list, index );
1298}
1299
1300
1301
1302/*!
1303 Inserts the string list \a list into the list at position \a
1304 index.
1305
1306 If \a index is negative, \a list is inserted at the end of the
1307 list. If \a index is too large, the operation is ignored.
1308
1309 \warning This function is never significantly faster than a loop
1310 around insertItem().
1311
1312 \sa insertItem(), insertStrList()
1313*/
1314
1315void QListBox::insertStringList( const QStringList & list, int index )
1316{
1317 if ( index < 0 )
1318 index = count();
1319 for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it )
1320 insertItem( new QListBoxText(*it), index++ );
1321}
1322
1323
1324
1325/*!
1326 \overload
1327
1328 Inserts the string list \a list into the list at position \a
1329 index.
1330
1331 If \a index is negative, \a list is inserted at the end of the
1332 list. If \a index is too large, the operation is ignored.
1333
1334 \warning This function uses \c{const char *} rather than QString,
1335 so we recommend against using it. It is provided so that legacy
1336 code will continue to work, and so that programs that certainly
1337 will not need to handle code outside a single 8-bit locale can use
1338 it. See insertStringList() which uses real QStrings.
1339
1340 \warning This function is never significantly faster than a loop
1341 around insertItem().
1342
1343 \sa insertItem(), insertStringList()
1344*/
1345
1346void QListBox::insertStrList( const QStrList & list, int index )
1347{
1348 QStrListIterator it( list );
1349 const char* txt;
1350 if ( index < 0 )
1351 index = count();
1352 while ( (txt=it.current()) ) {
1353 ++it;
1354 insertItem( new QListBoxText(QString::fromLatin1(txt)),
1355 index++ );
1356 }
1357 if ( hasFocus() && !d->current )
1358 setCurrentItem( d->head );
1359}
1360
1361
1362/*!
1363 \overload
1364
1365 Inserts the \a numStrings strings of the array \a strings into the
1366 list at position \a index.
1367
1368 If \a index is negative, insertStrList() inserts \a strings at the
1369 end of the list. If \a index is too large, the operation is
1370 ignored.
1371
1372 \warning This function uses \c{const char *} rather than QString,
1373 so we recommend against using it. It is provided so that legacy
1374 code will continue to work, and so that programs that certainly
1375 will not need to handle code outside a single 8-bit locale can use
1376 it. See insertStringList() which uses real QStrings.
1377
1378 \warning This function is never significantly faster than a loop
1379 around insertItem().
1380
1381 \sa insertItem(), insertStringList()
1382*/
1383
1384void QListBox::insertStrList( const char **strings, int numStrings, int index )
1385{
1386 if ( !strings ) {
1387#if defined(QT_CHECK_NULL)
1388 Q_ASSERT( strings != 0 );
1389#endif
1390 return;
1391 }
1392 if ( index < 0 )
1393 index = count();
1394 int i = 0;
1395 while ( (numStrings<0 && strings[i]!=0) || i<numStrings ) {
1396 insertItem( new QListBoxText( QString::fromLatin1(strings[i])),
1397 index + i );
1398 i++;
1399 }
1400 if ( hasFocus() && !d->current )
1401 setCurrentItem( d->head );
1402}
1403
1404/*!
1405 Inserts the item \a lbi into the list at position \a index.
1406
1407 If \a index is negative or larger than the number of items in the
1408 list box, \a lbi is inserted at the end of the list.
1409
1410 \sa insertStrList()
1411*/
1412
1413void QListBox::insertItem( const QListBoxItem *lbi, int index )
1414{
1415#if defined ( QT_CHECK_NULL )
1416 Q_ASSERT( lbi != 0 );
1417#else
1418 if ( !lbi )
1419 return;
1420#endif
1421
1422 if ( index < 0 )
1423 index = d->count;
1424
1425 if ( index >= d->count ) {
1426 insertItem( lbi, d->last );
1427 return;
1428 }
1429
1430 QListBoxItem * item = (QListBoxItem *)lbi;
1431 d->count++;
1432 d->cache = 0;
1433
1434 item->lbox = this;
1435 if ( !d->head || index == 0 ) {
1436 item->n = d->head;
1437 item->p = 0;
1438 d->head = item;
1439 item->dirty = TRUE;
1440 if ( item->n )
1441 item->n->p = item;
1442 } else {
1443 QListBoxItem * i = d->head;
1444 while ( i->n && index > 1 ) {
1445 i = i->n;
1446 index--;
1447 }
1448 if ( i->n ) {
1449 item->n = i->n;
1450 item->p = i;
1451 item->n->p = item;
1452 item->p->n = item;
1453 } else {
1454 i->n = item;
1455 item->p = i;
1456 item->n = 0;
1457 }
1458 }
1459
1460 if ( hasFocus() && !d->current ) {
1461 d->current = d->head;
1462 updateItem( d->current );
1463 emit highlighted( d->current );
1464 emit highlighted( d->current->text() );
1465 emit highlighted( index );
1466 }
1467
1468 triggerUpdate( TRUE );
1469}
1470
1471/*!
1472 \overload
1473
1474 Inserts the item \a lbi into the list after the item \a after, or
1475 at the beginning if \a after is 0.
1476
1477 \sa insertStrList()
1478*/
1479
1480void QListBox::insertItem( const QListBoxItem *lbi, const QListBoxItem *after )
1481{
1482#if defined ( QT_CHECK_NULL )
1483 Q_ASSERT( lbi != 0 );
1484#else
1485 if ( !lbi )
1486 return;
1487#endif
1488
1489 QListBoxItem * item = (QListBoxItem*)lbi;
1490 d->count++;
1491 d->cache = 0;
1492
1493 item->lbox = this;
1494 if ( !d->head || !after ) {
1495 item->n = d->head;
1496 item->p = 0;
1497 d->head = item;
1498 item->dirty = TRUE;
1499 if ( item->n )
1500 item->n->p = item;
1501 } else {
1502 QListBoxItem * i = (QListBoxItem*) after;
1503 if ( i ) {
1504 item->n = i->n;
1505 item->p = i;
1506 if ( item->n )
1507 item->n->p = item;
1508 if ( item->p )
1509 item->p->n = item;
1510 }
1511 }
1512
1513 if ( after == d->last )
1514 d->last = (QListBoxItem*) lbi;
1515
1516 if ( hasFocus() && !d->current ) {
1517 d->current = d->head;
1518 updateItem( d->current );
1519 emit highlighted( d->current );
1520 emit highlighted( d->current->text() );
1521 emit highlighted( index( d->current ) );
1522 }
1523
1524 triggerUpdate( TRUE );
1525}
1526
1527/*!
1528 \overload
1529
1530 Inserts a new list box text item with the text \a text into the
1531 list at position \a index.
1532
1533 If \a index is negative, \a text is inserted at the end of the
1534 list.
1535
1536 \sa insertStrList()
1537*/
1538
1539void QListBox::insertItem( const QString &text, int index )
1540{
1541 insertItem( new QListBoxText(text), index );
1542}
1543
1544/*!
1545 \overload
1546
1547 Inserts a new list box pixmap item with the pixmap \a pixmap into
1548 the list at position \a index.
1549
1550 If \a index is negative, \a pixmap is inserted at the end of the
1551 list.
1552
1553 \sa insertStrList()
1554*/
1555
1556void QListBox::insertItem( const QPixmap &pixmap, int index )
1557{
1558 insertItem( new QListBoxPixmap(pixmap), index );
1559}
1560
1561/*!
1562 \overload
1563
1564 Inserts a new list box pixmap item with the pixmap \a pixmap and
1565 the text \a text into the list at position \a index.
1566
1567 If \a index is negative, \a pixmap is inserted at the end of the
1568 list.
1569
1570 \sa insertStrList()
1571*/
1572
1573void QListBox::insertItem( const QPixmap &pixmap, const QString &text, int index )
1574{
1575 insertItem( new QListBoxPixmap(pixmap, text), index );
1576}
1577
1578/*!
1579 Removes and deletes the item at position \a index. If \a index is
1580 equal to currentItem(), a new item becomes current and the
1581 currentChanged() and highlighted() signals are emitted.
1582
1583 \sa insertItem(), clear()
1584*/
1585
1586void QListBox::removeItem( int index )
1587{
1588 bool wasVisible = itemVisible( currentItem() );
1589 delete item( index );
1590 triggerUpdate( TRUE );
1591 if ( wasVisible )
1592 ensureCurrentVisible();
1593}
1594
1595
1596/*!
1597 Deletes all the items in the list.
1598
1599 \sa removeItem()
1600*/
1601
1602void QListBox::clear()
1603{
1604 setContentsPos( 0, 0 );
1605 bool blocked = signalsBlocked();
1606 blockSignals( TRUE );
1607 d->clearing = TRUE;
1608 d->current = 0;
1609 QListBoxItem * i = d->head;
1610 d->head = 0;
1611 while ( i ) {
1612 QListBoxItem * n = i->n;
1613 i->n = i->p = 0;
1614 delete i;
1615 i = n;
1616 }
1617 d->count = 0;
1618 d->numRows = 1;
1619 d->numColumns = 1;
1620 d->currentRow = 0;
1621 d->currentColumn = 0;
1622 d->mousePressRow = -1;
1623 d->mousePressColumn = -1;
1624 d->mouseMoveRow = -1;
1625 d->mouseMoveColumn = -1;
1626 d->selectable.clear();
1627 clearSelection();
1628 blockSignals( blocked );
1629 triggerUpdate( TRUE );
1630 d->last = 0;
1631 d->clearing = FALSE;
1632}
1633
1634
1635/*!
1636 Returns the text at position \a index, or QString::null if there
1637 is no text at that position.
1638
1639 \sa pixmap()
1640*/
1641
1642QString QListBox::text( int index ) const
1643{
1644 QListBoxItem * i = item( index );
1645 if ( i )
1646 return i->text();
1647 return QString::null;
1648}
1649
1650
1651/*!
1652 Returns a pointer to the pixmap at position \a index, or 0 if
1653 there is no pixmap there.
1654
1655 \sa text()
1656*/
1657
1658const QPixmap *QListBox::pixmap( int index ) const
1659{
1660 QListBoxItem * i = item( index );
1661 if ( i )
1662 return i->pixmap();
1663 return 0;
1664}
1665
1666/*!
1667 \overload
1668
1669 Replaces the item at position \a index with a new list box text
1670 item with text \a text.
1671
1672 The operation is ignored if \a index is out of range.
1673
1674 \sa insertItem(), removeItem()
1675*/
1676
1677void QListBox::changeItem( const QString &text, int index )
1678{
1679 if( index >= 0 && index < (int)count() )
1680 changeItem( new QListBoxText(text), index );
1681}
1682
1683/*!
1684 \overload
1685
1686 Replaces the item at position \a index with a new list box pixmap
1687 item with pixmap \a pixmap.
1688
1689 The operation is ignored if \a index is out of range.
1690
1691 \sa insertItem(), removeItem()
1692*/
1693
1694void QListBox::changeItem( const QPixmap &pixmap, int index )
1695{
1696 if( index >= 0 && index < (int)count() )
1697 changeItem( new QListBoxPixmap(pixmap), index );
1698}
1699
1700/*!
1701 \overload
1702
1703 Replaces the item at position \a index with a new list box pixmap
1704 item with pixmap \a pixmap and text \a text.
1705
1706 The operation is ignored if \a index is out of range.
1707
1708 \sa insertItem(), removeItem()
1709*/
1710
1711void QListBox::changeItem( const QPixmap &pixmap, const QString &text, int index )
1712{
1713 if( index >= 0 && index < (int)count() )
1714 changeItem( new QListBoxPixmap(pixmap, text), index );
1715}
1716
1717
1718
1719/*!
1720 Replaces the item at position \a index with \a lbi. If \a index is
1721 negative or too large, changeItem() does nothing.
1722
1723 The item that has been changed will become selected.
1724
1725 \sa insertItem(), removeItem()
1726*/
1727
1728void QListBox::changeItem( const QListBoxItem *lbi, int index )
1729{
1730 if ( !lbi || index < 0 || index >= (int)count() )
1731 return;
1732
1733 removeItem( index );
1734 insertItem( lbi, index );
1735 setCurrentItem( index );
1736}
1737
1738
1739/*!
1740 \property QListBox::numItemsVisible
1741 \brief the number of visible items.
1742
1743 Both partially and entirely visible items are counted.
1744*/
1745
1746int QListBox::numItemsVisible() const
1747{
1748 doLayout();
1749
1750 int columns = 0;
1751
1752 int x = contentsX();
1753 int i=0;
1754 while ( i < (int)d->columnPos.size()-1 &&
1755 d->columnPos[i] < x )
1756 i++;
1757 if ( i < (int)d->columnPos.size()-1 &&
1758 d->columnPos[i] > x )
1759 columns++;
1760 x += visibleWidth();
1761 while ( i < (int)d->columnPos.size()-1 &&
1762 d->columnPos[i] < x ) {
1763 i++;
1764 columns++;
1765 }
1766
1767 int y = contentsY();
1768 int rows = 0;
1769 while ( i < (int)d->rowPos.size()-1 &&
1770 d->rowPos[i] < y )
1771 i++;
1772 if ( i < (int)d->rowPos.size()-1 &&
1773 d->rowPos[i] > y )
1774 rows++;
1775 y += visibleHeight();
1776 while ( i < (int)d->rowPos.size()-1 &&
1777 d->rowPos[i] < y ) {
1778 i++;
1779 rows++;
1780 }
1781
1782 return rows*columns;
1783}
1784
1785int QListBox::currentItem() const
1786{
1787 if ( !d->current || !d->head )
1788 return -1;
1789
1790 return index( d->current );
1791}
1792
1793
1794/*!
1795 \property QListBox::currentText
1796 \brief the text of the current item.
1797
1798 This is equivalent to text(currentItem()).
1799*/
1800
1801
1802/*!
1803 \property QListBox::currentItem
1804 \brief the current highlighted item
1805
1806 When setting this property, the highlighting is moved to the item
1807 and the list box scrolled as necessary.
1808
1809 If no item is current, currentItem() returns -1.
1810*/
1811
1812void QListBox::setCurrentItem( int index )
1813{
1814 setCurrentItem( item( index ) );
1815}
1816
1817
1818/*!
1819 \overload
1820
1821 Sets the current item to the QListBoxItem \a i.
1822*/
1823void QListBox::setCurrentItem( QListBoxItem * i )
1824{
1825 if ( !i || d->current == i )
1826 return;
1827
1828 QRect mfrect = itemRect( i );
1829 if ( mfrect.isValid() )
1830 setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE );
1831
1832 QListBoxItem * o = d->current;
1833 d->current = i;
1834 int ind = index( i );
1835
1836 if ( i && selectionMode() == Single ) {
1837 bool changed = FALSE;
1838 if ( o && o->s ) {
1839 changed = TRUE;
1840 o->s = FALSE;
1841 }
1842 if ( i && !i->s && d->selectionMode != NoSelection && i->isSelectable() ) {
1843 i->s = TRUE;
1844 changed = TRUE;
1845 emit selectionChanged( i );
1846#if defined(QT_ACCESSIBILITY_SUPPORT)
1847 QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged );
1848#endif
1849 }
1850 if ( changed ) {
1851 emit selectionChanged();
1852#if defined(QT_ACCESSIBILITY_SUPPORT)
1853 QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection );
1854#endif
1855 }
1856 }
1857
1858 d->currentColumn = ind / numRows();
1859 d->currentRow = ind % numRows();
1860 if ( o )
1861 updateItem( o );
1862 if ( i )
1863 updateItem( i );
1864 // scroll after the items are redrawn
1865 d->visibleTimer->start( 1, TRUE );
1866
1867 QString tmp;
1868 if ( i )
1869 tmp = i->text();
1870 emit highlighted( i );
1871 if ( !tmp.isNull() )
1872 emit highlighted( tmp );
1873 emit highlighted( ind );
1874 emit currentChanged( i );
1875
1876#if defined(QT_ACCESSIBILITY_SUPPORT)
1877 QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::Focus );
1878#endif
1879}
1880
1881
1882/*!
1883 Returns a pointer to the item at position \a index, or 0 if \a
1884 index is out of bounds.
1885
1886 \sa index()
1887*/
1888
1889QListBoxItem *QListBox::item( int index ) const
1890{
1891 if ( index < 0 || index > d->count -1 )
1892 return 0;
1893
1894 QListBoxItem * i = d->head;
1895
1896 if ( d->cache && index > 0 ) {
1897 i = d->cache;
1898 int idx = d->cacheIndex;
1899 while ( i && idx < index ) {
1900 idx++;
1901 i = i->n;
1902 }
1903 while ( i && idx > index ) {
1904 idx--;
1905 i = i->p;
1906 }
1907 } else {
1908 int idx = index;
1909 while ( i && idx > 0 ) {
1910 idx--;
1911 i = i->n;
1912 }
1913 }
1914
1915 if ( index > 0 ) {
1916 d->cache = i;
1917 d->cacheIndex = index;
1918 }
1919
1920 return i;
1921}
1922
1923
1924/*!
1925 Returns the index of \a lbi, or -1 if the item is not in this list
1926 box or \a lbi is 0.
1927
1928 \sa item()
1929*/
1930
1931int QListBox::index( const QListBoxItem * lbi ) const
1932{
1933 if ( !lbi )
1934 return -1;
1935 QListBoxItem * i_n = d->head;
1936 int c_n = 0;
1937 if ( d->cache ) {
1938 i_n = d->cache;
1939 c_n = d->cacheIndex;
1940 }
1941 QListBoxItem* i_p = i_n;
1942 int c_p = c_n;
1943 while ( ( i_n != 0 || i_p != 0 ) && i_n != lbi && i_p != lbi ) {
1944 if ( i_n ) {
1945 c_n++;
1946 i_n = i_n->n;
1947 }
1948 if ( i_p ) {
1949 c_p--;
1950 i_p = i_p->p;
1951 }
1952 }
1953 if ( i_p == lbi )
1954 return c_p;
1955 if ( i_n == lbi )
1956 return c_n;
1957 return -1;
1958}
1959
1960
1961
1962/*!
1963 Returns TRUE if the item at position \a index is at least partly
1964 visible; otherwise returns FALSE.
1965*/
1966
1967bool QListBox::itemVisible( int index )
1968{
1969 QListBoxItem * i = item( index );
1970 return i ? itemVisible( i ) : FALSE;
1971}
1972
1973
1974/*!
1975 \overload
1976
1977 Returns TRUE if \a item is at least partly visible; otherwise
1978 returns FALSE.
1979*/
1980
1981bool QListBox::itemVisible( const QListBoxItem * item )
1982{
1983 if ( d->layoutDirty )
1984 doLayout();
1985
1986 int i = index( item );
1987 int col = i / numRows();
1988 int row = i % numRows();
1989 return ( d->columnPos[col] < contentsX()+visibleWidth() &&
1990 d->rowPos[row] < contentsY()+visibleHeight() &&
1991 d->columnPos[col+1] > contentsX() &&
1992 d->rowPos[row+1] > contentsY() );
1993}
1994
1995
1996/*! \reimp */
1997
1998void QListBox::mousePressEvent( QMouseEvent *e )
1999{
2000 mousePressEventEx( e );
2001}
2002
2003void QListBox::mousePressEventEx( QMouseEvent *e )
2004{
2005 d->mouseInternalPress = TRUE;
2006 QListBoxItem * i = itemAt( e->pos() );
2007
2008 if ( !i && !d->current && d->head ) {
2009 d->current = d->head;
2010 updateItem( d->head );
2011 }
2012
2013 if ( !i && ( d->selectionMode != Single || e->button() == RightButton )
2014 && !( e->state() & ControlButton ) )
2015 clearSelection();
2016
2017 d->select = d->selectionMode == Multi ? ( i ? !i->isSelected() : FALSE ) : TRUE;
2018 d->pressedSelected = i && i->s;
2019
2020 if ( i )
2021 d->selectAnchor = i;
2022 if ( i ) {
2023 switch( selectionMode() ) {
2024 default:
2025 case Single:
2026 if ( !i->s || i != d->current ) {
2027 if ( i->isSelectable() )
2028 setSelected( i, TRUE );
2029 else
2030 setCurrentItem( i );
2031 }
2032 break;
2033 case Extended:
2034 if ( i ) {
2035 if ( !(e->state() & QMouseEvent::ShiftButton) &&
2036 !(e->state() & QMouseEvent::ControlButton) ) {
2037 if ( !i->isSelected() ) {
2038 bool b = signalsBlocked();
2039 blockSignals( TRUE );
2040 clearSelection();
2041 blockSignals( b );
2042 }
2043 setSelected( i, TRUE );
2044 d->dragging = TRUE; // always assume dragging
2045 } else if ( e->state() & ShiftButton ) {
2046 d->pressedSelected = FALSE;
2047 QListBoxItem *oldCurrent = item( currentItem() );
2048 bool down = index( oldCurrent ) < index( i );
2049
2050 QListBoxItem *lit = down ? oldCurrent : i;
2051 bool select = d->select;
2052 bool blocked = signalsBlocked();
2053 blockSignals( TRUE );
2054 for ( ;; lit = lit->n ) {
2055 if ( !lit ) {
2056 triggerUpdate( FALSE );
2057 break;
2058 }
2059 if ( down && lit == i ) {
2060 setSelected( i, select );
2061 triggerUpdate( FALSE );
2062 break;
2063 }
2064 if ( !down && lit == oldCurrent ) {
2065 setSelected( oldCurrent, select );
2066 triggerUpdate( FALSE );
2067 break;
2068 }
2069 setSelected( lit, select );
2070 }
2071 blockSignals( blocked );
2072 emit selectionChanged();
2073 } else if ( e->state() & ControlButton ) {
2074 setSelected( i, !i->isSelected() );
2075 d->pressedSelected = FALSE;
2076 }
2077 setCurrentItem( i );
2078 }
2079 break;
2080 case Multi:
2081 //d->current = i;
2082 setSelected( i, !i->s );
2083 setCurrentItem( i );
2084 break;
2085 case NoSelection:
2086 setCurrentItem( i );
2087 break;
2088 }
2089 } else {
2090 bool unselect = TRUE;
2091 if ( e->button() == LeftButton ) {
2092 if ( d->selectionMode == Multi ||
2093 d->selectionMode == Extended ) {
2094 d->tmpCurrent = d->current;
2095 d->current = 0;
2096 updateItem( d->tmpCurrent );
2097 if ( d->rubber )
2098 delete d->rubber;
2099 d->rubber = 0;
2100 d->rubber = new QRect( e->x(), e->y(), 0, 0 );
2101
2102 if ( d->selectionMode == Extended && !( e->state() & ControlButton ) )
2103 selectAll( FALSE );
2104 unselect = FALSE;
2105 }
2106 if ( unselect && ( e->button() == RightButton ||
2107 ( selectionMode() == Multi || selectionMode() == Extended ) ) )
2108 clearSelection();
2109 }
2110 }
2111
2112 // for sanity, in case people are event-filtering or whatnot
2113 delete d->scrollTimer;
2114 d->scrollTimer = 0;
2115 if ( i ) {
2116 d->mousePressColumn = d->currentColumn;
2117 d->mousePressRow = d->currentRow;
2118 } else {
2119 d->mousePressColumn = -1;
2120 d->mousePressRow = -1;
2121 }
2122 d->ignoreMoves = FALSE;
2123
2124 d->pressedItem = i;
2125
2126 emit pressed( i );
2127 emit pressed( i, e->globalPos() );
2128 emit mouseButtonPressed( e->button(), i, e->globalPos() );
2129 if ( e->button() == RightButton )
2130 emit rightButtonPressed( i, e->globalPos() );
2131}
2132
2133
2134/*! \reimp */
2135
2136void QListBox::mouseReleaseEvent( QMouseEvent *e )
2137{
2138 if ( d->selectionMode == Extended &&
2139 d->dragging ) {
2140 d->dragging = FALSE;
2141 if (d->current != d->pressedItem) {
2142 updateSelection(); // when we drag, we get an update after we release
2143 }
2144 }
2145
2146 if ( d->rubber ) {
2147 drawRubber();
2148 delete d->rubber;
2149 d->rubber = 0;
2150 d->current = d->tmpCurrent;
2151 updateItem( d->current );
2152 }
2153 if ( d->scrollTimer )
2154 mouseMoveEvent( e );
2155 delete d->scrollTimer;
2156 d->scrollTimer = 0;
2157 d->ignoreMoves = FALSE;
2158
2159 if ( d->selectionMode == Extended &&
2160 d->current == d->pressedItem &&
2161 d->pressedSelected && d->current ) {
2162 bool block = signalsBlocked();
2163 blockSignals( TRUE );
2164 clearSelection();
2165 blockSignals( block );
2166 d->current->s = TRUE;
2167 emit selectionChanged();
2168 }
2169
2170 QListBoxItem * i = itemAt( e->pos() );
2171 bool emitClicked = d->mousePressColumn != -1 && d->mousePressRow != -1 || !d->pressedItem;
2172 emitClicked = emitClicked && d->pressedItem == i;
2173 d->pressedItem = 0;
2174 d->mousePressRow = -1;
2175 d->mousePressColumn = -1;
2176 d->mouseInternalPress = FALSE;
2177 if ( emitClicked ) {
2178 emit clicked( i );
2179 emit clicked( i, e->globalPos() );
2180 emit mouseButtonClicked( e->button(), i, e->globalPos() );
2181 if ( e->button() == RightButton )
2182 emit rightButtonClicked( i, e->globalPos() );
2183 }
2184}
2185
2186
2187/*! \reimp */
2188
2189void QListBox::mouseDoubleClickEvent( QMouseEvent *e )
2190{
2191 bool ok = TRUE;
2192 QListBoxItem *i = itemAt( e->pos() );
2193 if ( !i || selectionMode() == NoSelection )
2194 ok = FALSE;
2195
2196 d->ignoreMoves = TRUE;
2197
2198 if ( d->current && ok ) {
2199 QListBoxItem * i = d->current;
2200 QString tmp = d->current->text();
2201 emit selected( currentItem() );
2202 emit selected( i );
2203 if ( !tmp.isNull() )
2204 emit selected( tmp );
2205 emit doubleClicked( i );
2206 }
2207}
2208
2209
2210/*! \reimp */
2211
2212void QListBox::mouseMoveEvent( QMouseEvent *e )
2213{
2214 QListBoxItem * i = itemAt( e->pos() );
2215 if ( i != d->highlighted ) {
2216 if ( i ) {
2217 emit onItem( i );
2218 } else {
2219 emit onViewport();
2220 }
2221 d->highlighted = i;
2222 }
2223
2224 if ( d->rubber ) {
2225 QRect r = d->rubber->normalize();
2226 drawRubber();
2227 d->rubber->setCoords( d->rubber->x(), d->rubber->y(), e->x(), e->y() );
2228 doRubberSelection( r, d->rubber->normalize() );
2229 drawRubber();
2230 return;
2231 }
2232
2233 if ( ( (e->state() & ( RightButton | LeftButton | MidButton ) ) == 0 ) ||
2234 d->ignoreMoves )
2235 return;
2236
2237 // hack to keep the combo (and what else?) working: if we get a
2238 // move outside the listbox without having seen a press, discard
2239 // it.
2240 if ( !QRect( 0, 0, visibleWidth(), visibleHeight() ).contains( e->pos() ) &&
2241 ( d->mousePressColumn < 0 && d->mousePressRow < 0 ||
2242 (e->state() == NoButton && !d->pressedItem) ) )
2243 return;
2244
2245 // figure out in what direction to drag-select and perhaps scroll
2246 int dx = 0;
2247 int x = e->x();
2248 if ( x >= visibleWidth() ) {
2249 x = visibleWidth()-1;
2250 dx = 1;
2251 } else if ( x < 0 ) {
2252 x = 0;
2253 dx = -1;
2254 }
2255 d->mouseMoveColumn = columnAt( x + contentsX() );
2256
2257 // sanitize mousePressColumn, if we got here without a mouse press event
2258 if ( d->mousePressColumn < 0 && d->mouseMoveColumn >= 0 )
2259 d->mousePressColumn = d->mouseMoveColumn;
2260 if ( d->mousePressColumn < 0 && d->currentColumn >= 0 )
2261 d->mousePressColumn = d->currentColumn;
2262
2263 // if it's beyond the last column, use the last one
2264 if ( d->mouseMoveColumn < 0 )
2265 d->mouseMoveColumn = dx >= 0 ? numColumns()-1 : 0;
2266
2267 // repeat for y
2268 int dy = 0;
2269 int y = e->y();
2270 if ( y >= visibleHeight() ) {
2271 y = visibleHeight()-1;
2272 dy = 1;
2273 } else if ( y < 0 ) {
2274 y = 0;
2275 dy = -1;
2276 }
2277 d->mouseMoveRow = rowAt( y + contentsY() );
2278
2279 if ( d->mousePressRow < 0 && d->mouseMoveRow >= 0 )
2280 d->mousePressRow = d->mouseMoveRow;
2281 if ( d->mousePressRow < 0 && d->currentRow >= 0 )
2282 d->mousePressRow = d->currentRow;
2283
2284 if ( d->mousePressRow < 0 )
2285 d->mousePressRow = rowAt( x + contentsX() );
2286
2287 d->scrollPos = QPoint( dx, dy );
2288
2289 if ( ( dx || dy ) && !d->scrollTimer && e->state() == LeftButton && e->button() != LeftButton ) {
2290 // start autoscrolling if necessary
2291 d->scrollTimer = new QTimer( this );
2292 connect( d->scrollTimer, SIGNAL(timeout()),
2293 this, SLOT(doAutoScroll()) );
2294 d->scrollTimer->start( 100, FALSE );
2295 doAutoScroll();
2296 } else if ( !d->scrollTimer ) {
2297 // or just select the required bits
2298 updateSelection();
2299 }
2300}
2301
2302
2303
2304void QListBox::updateSelection()
2305{
2306 if ( d->mouseMoveColumn >= 0 && d->mouseMoveRow >= 0 &&
2307 d->mousePressColumn >= 0 && d->mousePressRow >= 0 ) {
2308 QListBoxItem * i = item( d->mouseMoveColumn * numRows() +
2309 d->mouseMoveRow );
2310#if defined(QT_ACCESSIBILITY_SUPPORT)
2311 int ind = index(i);
2312#endif
2313 if ( selectionMode() == Single || selectionMode() == NoSelection ) {
2314 if ( i && ( d->mouseInternalPress || testWFlags(WType_Popup) ) )
2315 setCurrentItem( i );
2316 } else {
2317 if ( d->selectionMode == Extended && (
2318 ( d->current == d->pressedItem && d->pressedSelected ) ||
2319 (d->dirtyDrag && !d->dragging) ) ) {
2320 if (d->dirtyDrag && !d->dragging) // emit after dragging stops
2321 d->dirtyDrag = FALSE;
2322 else
2323 clearSelection(); // dont reset drag-selected items
2324 d->pressedItem = 0;
2325 if ( i && i->isSelectable() ) {
2326 bool block = signalsBlocked();
2327 blockSignals( TRUE );
2328 i->s = TRUE;
2329 blockSignals( block );
2330 emit selectionChanged();
2331#if defined(QT_ACCESSIBILITY_SUPPORT)
2332 QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged );
2333 QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection );
2334 QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::SelectionAdd );
2335#endif
2336 }
2337 triggerUpdate( FALSE );
2338 } else {
2339 int c = QMIN( d->mouseMoveColumn, d->mousePressColumn );
2340 int r = QMIN( d->mouseMoveRow, d->mousePressRow );
2341 int c2 = QMAX( d->mouseMoveColumn, d->mousePressColumn );
2342 int r2 = QMAX( d->mouseMoveRow, d->mousePressRow );
2343 bool changed = FALSE;
2344 while( c <= c2 ) {
2345 QListBoxItem * i = item( c*numRows()+r );
2346 int rtmp = r;
2347 while( i && rtmp <= r2 ) {
2348 if ( (bool)i->s != (bool)d->select && i->isSelectable() ) {
2349 i->s = d->select;
2350#if defined(QT_ACCESSIBILITY_SUPPORT)
2351 QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged );
2352 QAccessible::updateAccessibility( viewport(), ind+1, d->select ? QAccessible::SelectionAdd : QAccessible::SelectionRemove );
2353#endif
2354 i->dirty = TRUE;
2355 d->dirtyDrag = changed = TRUE;
2356 }
2357 i = i->n;
2358 rtmp++;
2359 }
2360 c++;
2361 }
2362 if ( changed ) {
2363 if (!d->dragging) // emit after dragging stops instead
2364 emit selectionChanged();
2365#if defined(QT_ACCESSIBILITY_SUPPORT)
2366 QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection );
2367#endif
2368 triggerUpdate( FALSE );
2369 }
2370 }
2371 if ( i )
2372 setCurrentItem( i );
2373 }
2374 }
2375}
2376
2377void QListBox::repaintSelection()
2378{
2379 if ( d->numColumns == 1 ) {
2380 for ( uint i = topItem(); itemVisible( i ) && i < count(); ++i ) {
2381 QListBoxItem *it = item(i);
2382 if ( !it )
2383 break;
2384 if ( it->isSelected() )
2385 updateItem( it );
2386 }
2387 } else {
2388 for ( uint i = 0; i < count(); ++i ) {
2389 QListBoxItem *it = item(i);
2390 if ( !it )
2391 break;
2392 if ( it->isSelected() )
2393 updateItem( it );
2394 }
2395 }
2396}
2397
2398/*! \reimp
2399*/
2400
2401void QListBox::contentsContextMenuEvent( QContextMenuEvent *e )
2402{
2403 if ( !receivers( SIGNAL(contextMenuRequested(QListBoxItem*,const QPoint&)) ) ) {
2404 e->ignore();
2405 return;
2406 }
2407 if ( e->reason() == QContextMenuEvent::Keyboard ) {
2408 QListBoxItem *i = item( currentItem() );
2409 if ( i ) {
2410 QRect r = itemRect( i );
2411 emit contextMenuRequested( i, mapToGlobal( r.topLeft() + QPoint( width() / 2, r.height() / 2 ) ) );
2412 }
2413 } else {
2414 QListBoxItem * i = itemAt( contentsToViewport( e->pos() ) );
2415 emit contextMenuRequested( i, e->globalPos() );
2416 }
2417}
2418
2419/*!\reimp
2420*/
2421void QListBox::keyPressEvent( QKeyEvent *e )
2422{
2423 if ( ( e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab )
2424 && e->state() & Qt::ControlButton )
2425 e->ignore();
2426
2427 if ( count() == 0 ) {
2428 e->ignore();
2429 return;
2430 }
2431
2432 QListBoxItem *old = d->current;
2433 if ( !old ) {
2434 setCurrentItem( d->head );
2435 if ( d->selectionMode == Single )
2436 setSelected( d->head, TRUE );
2437 e->ignore();
2438 return;
2439 }
2440
2441 bool selectCurrent = FALSE;
2442 switch ( e->key() ) {
2443 case Key_Up:
2444 {
2445 d->currInputString = QString::null;
2446 if ( currentItem() > 0 ) {
2447 setCurrentItem( currentItem() - 1 );
2448 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2449 }
2450 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2451 d->selectAnchor = d->current;
2452 }
2453 break;
2454 case Key_Down:
2455 {
2456 d->currInputString = QString::null;
2457 if ( currentItem() < (int)count() - 1 ) {
2458 setCurrentItem( currentItem() + 1 );
2459 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2460 }
2461 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2462 d->selectAnchor = d->current;
2463 }
2464 break;
2465 case Key_Left:
2466 {
2467 d->currInputString = QString::null;
2468 if ( currentColumn() > 0 ) {
2469 setCurrentItem( currentItem() - numRows() );
2470 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2471 } else if ( numColumns() > 1 && currentItem() > 0 ) {
2472 int row = currentRow();
2473 setCurrentItem( currentRow() - 1 + ( numColumns() - 1 ) * numRows() );
2474
2475 if ( currentItem() == -1 )
2476 setCurrentItem( row - 1 + ( numColumns() - 2 ) * numRows() );
2477
2478 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2479 } else {
2480 QApplication::sendEvent( horizontalScrollBar(), e );
2481 }
2482 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2483 d->selectAnchor = d->current;
2484 }
2485 break;
2486 case Key_Right:
2487 {
2488 d->currInputString = QString::null;
2489 if ( currentColumn() < numColumns()-1 ) {
2490 int row = currentRow();
2491 int i = currentItem();
2492 QListBoxItem *it = item( i + numRows() );
2493 if ( !it )
2494 it = item( count()-1 );
2495 setCurrentItem( it );
2496
2497 if ( currentItem() == -1 ) {
2498 if ( row < numRows() - 1 )
2499 setCurrentItem( row + 1 );
2500 else
2501 setCurrentItem( i );
2502 }
2503
2504 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2505 } else if ( numColumns() > 1 && currentRow() < numRows() ) {
2506 if ( currentRow() + 1 < numRows() ) {
2507 setCurrentItem( currentRow() + 1 );
2508 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2509 }
2510 } else {
2511 QApplication::sendEvent( horizontalScrollBar(), e );
2512 }
2513 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2514 d->selectAnchor = d->current;
2515 }
2516 break;
2517 case Key_Next:
2518 {
2519 d->currInputString = QString::null;
2520 int i = 0;
2521 if ( numColumns() == 1 ) {
2522 i = currentItem() + numItemsVisible();
2523 i = i > (int)count() - 1 ? (int)count() - 1 : i;
2524 setCurrentItem( i );
2525 setBottomItem( i );
2526 } else {
2527 // I'm not sure about this behavior...
2528 if ( currentRow() == numRows() - 1 )
2529 i = currentItem() + numRows();
2530 else
2531 i = currentItem() + numRows() - currentRow() - 1;
2532 i = i > (int)count() - 1 ? (int)count() - 1 : i;
2533 setCurrentItem( i );
2534 }
2535 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2536 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2537 d->selectAnchor = d->current;
2538 }
2539 break;
2540 case Key_Prior:
2541 {
2542 selectCurrent = TRUE;
2543 d->currInputString = QString::null;
2544 int i;
2545 if ( numColumns() == 1 ) {
2546 i = currentItem() - numItemsVisible();
2547 i = i < 0 ? 0 : i;
2548 setCurrentItem( i );
2549 setTopItem( i );
2550 } else {
2551 // I'm not sure about this behavior...
2552 if ( currentRow() == 0 )
2553 i = currentItem() - numRows();
2554 else
2555 i = currentItem() - currentRow();
2556 i = i < 0 ? 0 : i;
2557 setCurrentItem( i );
2558 }
2559 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2560 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2561 d->selectAnchor = d->current;
2562 }
2563 break;
2564 case Key_Space:
2565 {
2566 selectCurrent = TRUE;
2567 d->currInputString = QString::null;
2568 toggleCurrentItem();
2569 if ( selectionMode() == Extended && d->current->isSelected() )
2570 emit highlighted( currentItem() );
2571 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2572 d->selectAnchor = d->current;
2573 }
2574 break;
2575 case Key_Return:
2576 case Key_Enter:
2577 {
2578 selectCurrent = TRUE;
2579 d->currInputString = QString::null;
2580 if ( currentItem() >= 0 && selectionMode() != NoSelection ) {
2581 QString tmp = item( currentItem() )->text();
2582 emit selected( currentItem());
2583 emit selected( item( currentItem() ) );
2584 if ( !tmp.isEmpty() )
2585 emit selected( tmp );
2586 emit returnPressed( item( currentItem() ) );
2587 }
2588 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2589 d->selectAnchor = d->current;
2590 }
2591 break;
2592 case Key_Home:
2593 {
2594 selectCurrent = TRUE;
2595 d->currInputString = QString::null;
2596 setCurrentItem( 0 );
2597 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2598 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2599 d->selectAnchor = d->current;
2600 }
2601 break;
2602 case Key_End:
2603 {
2604 selectCurrent = TRUE;
2605 d->currInputString = QString::null;
2606 int i = (int)count() - 1;
2607 setCurrentItem( i );
2608 handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton );
2609 if ( !( e->state() & ShiftButton ) || !d->selectAnchor )
2610 d->selectAnchor = d->current;
2611 }
2612 break;
2613 default:
2614 {
2615 if ( !e->text().isEmpty() && e->text()[ 0 ].isPrint() && count() ) {
2616 int curItem = currentItem();
2617 if ( curItem == -1 )
2618 curItem = 0;
2619 if ( !d->inputTimer->isActive() ) {
2620 d->currInputString = e->text();
2621 curItem = d->findItemByName( ++curItem, d->currInputString );
2622 } else {
2623 d->inputTimer->stop();
2624 d->currInputString += e->text();
2625 int oldCurItem = curItem;
2626 curItem = d->findItemByName( curItem, d->currInputString );
2627 if ( curItem < 0 ) {
2628 curItem = d->findItemByName( ++oldCurItem, e->text() );
2629 d->currInputString = e->text();
2630 }
2631 }
2632 if ( curItem >= 0 )
2633 setCurrentItem( curItem );
2634 if ( curItem >= 0 && selectionMode() == QListBox::Extended ) {
2635 bool changed = FALSE;
2636 bool block = signalsBlocked();
2637 blockSignals( TRUE );
2638 selectAll( FALSE );
2639 blockSignals( block );
2640 QListBoxItem *i = item( curItem );
2641 if ( !i->s && i->isSelectable() ) {
2642 changed = TRUE;
2643 i->s = TRUE;
2644 updateItem( i );
2645 }
2646 if ( changed )
2647 emit selectionChanged();
2648 }
2649 d->inputTimer->start( 400, TRUE );
2650 } else {
2651 d->currInputString = QString::null;
2652 if ( e->state() & ControlButton ) {
2653 switch ( e->key() ) {
2654 case Key_A:
2655 selectAll( TRUE );
2656 break;
2657 }
2658 } else {
2659 e->ignore();
2660 }
2661 }
2662 }
2663 }
2664
2665 if ( selectCurrent && selectionMode() == Single &&
2666 d->current && !d->current->s ) {
2667 updateItem( d->current );
2668 setSelected( d->current, TRUE );
2669 }
2670}
2671
2672
2673/*!\reimp
2674*/
2675void QListBox::focusInEvent( QFocusEvent* )
2676{
2677 d->mousePressRow = -1;
2678 d->mousePressColumn = -1;
2679 d->inMenuMode = FALSE;
2680 if ( QFocusEvent::reason() != QFocusEvent::Mouse && !d->current && d->head ) {
2681 d->current = d->head;
2682 QListBoxItem *i = d->current;
2683 QString tmp;
2684 if ( i )
2685 tmp = i->text();
2686 int tmp2 = index( i );
2687 emit highlighted( i );
2688 if ( !tmp.isNull() )
2689 emit highlighted( tmp );
2690 emit highlighted( tmp2 );
2691 emit currentChanged( i );
2692 }
2693 if ( style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this ) )
2694 repaintSelection();
2695
2696 if ( d->current ) {
2697 updateItem( currentItem() );
2698 QRect mfrect = itemRect( d->current );
2699 if ( mfrect.isValid() )
2700 setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE );
2701 }
2702}
2703
2704
2705/*!\reimp
2706*/
2707void QListBox::focusOutEvent( QFocusEvent* )
2708{
2709 if (style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this )) {
2710 d->inMenuMode =
2711 QFocusEvent::reason() == QFocusEvent::Popup ||
2712 (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar"));
2713 if ( !d->inMenuMode )
2714 repaintSelection();
2715 }
2716
2717 if ( d->current )
2718 updateItem( currentItem() );
2719}
2720
2721/*!\reimp
2722*/
2723bool QListBox::eventFilter( QObject *o, QEvent *e )
2724{
2725 //### remove in 4.0
2726 return QScrollView::eventFilter( o, e );
2727}
2728
2729/*!
2730 Repaints the item at position \a index in the list.
2731*/
2732
2733void QListBox::updateItem( int index )
2734{
2735 if ( index >= 0 )
2736 updateItem( item( index ) );
2737}
2738
2739
2740/*!
2741 \overload
2742
2743 Repaints the QListBoxItem \a i.
2744*/
2745
2746void QListBox::updateItem( QListBoxItem * i )
2747{
2748 if ( !i )
2749 return;
2750 i->dirty = TRUE;
2751 d->updateTimer->start( 0, TRUE );
2752}
2753
2754
2755/*!
2756 \property QListBox::selectionMode
2757 \brief the selection mode of the list box
2758
2759 Sets the list box's selection mode, which may be one of \c Single
2760 (the default), \c Extended, \c Multi or \c NoSelection.
2761
2762 \sa SelectionMode
2763*/
2764
2765void QListBox::setSelectionMode( SelectionMode mode )
2766{
2767 if ( d->selectionMode == mode )
2768 return;
2769
2770 if ( ( selectionMode() == Multi || selectionMode() == Extended )
2771 && ( mode == QListBox::Single || mode == QListBox::NoSelection ) ){
2772 clearSelection();
2773 if ( ( mode == QListBox::Single ) && currentItem() )
2774 setSelected( currentItem(), TRUE );
2775 }
2776
2777 d->selectionMode = mode;
2778 triggerUpdate( TRUE );
2779}
2780
2781
2782QListBox::SelectionMode QListBox::selectionMode() const
2783{
2784 return d->selectionMode;
2785}
2786
2787
2788/*!
2789 \obsolete
2790 \property QListBox::multiSelection
2791 \brief whether or not the list box is in Multi selection mode
2792
2793 Consider using the \l QListBox::selectionMode property instead of
2794 this property.
2795
2796 When setting this property, Multi selection mode is used if set to TRUE and
2797 to Single selection mode if set to FALSE.
2798
2799 When getting this property, TRUE is returned if the list box is in
2800 Multi selection mode or Extended selection mode, and FALSE if it is
2801 in Single selection mode or NoSelection mode.
2802
2803 \sa selectionMode
2804*/
2805
2806bool QListBox::isMultiSelection() const
2807{
2808 return selectionMode() == Multi || selectionMode() == Extended;
2809}
2810
2811void QListBox::setMultiSelection( bool enable )
2812{
2813 setSelectionMode( enable ? Multi : Single );
2814}
2815
2816
2817/*!
2818 Toggles the selection status of currentItem() and repaints if the
2819 list box is a \c Multi selection list box.
2820
2821 \sa setMultiSelection()
2822*/
2823
2824void QListBox::toggleCurrentItem()
2825{
2826 if ( selectionMode() == Single ||
2827 selectionMode() == NoSelection ||
2828 !d->current )
2829 return;
2830
2831 if ( d->current->s || d->current->isSelectable() ) {
2832 d->current->s = !d->current->s;
2833 emit selectionChanged();
2834#if defined(QT_ACCESSIBILITY_SUPPORT)
2835 int ind = index( d->current );
2836 QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection );
2837 QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged );
2838 QAccessible::updateAccessibility( viewport(), ind+1, d->current->s ? QAccessible::SelectionAdd : QAccessible::SelectionRemove );
2839#endif
2840 }
2841 updateItem( d->current );
2842}
2843
2844
2845/*!
2846 \overload
2847
2848 If \a select is TRUE the item at position \a index is selected;
2849 otherwise the item is deselected.
2850*/
2851
2852void QListBox::setSelected( int index, bool select )
2853{
2854 setSelected( item( index ), select );
2855}
2856
2857
2858/*!
2859 Selects \a item if \a select is TRUE or unselects it if \a select
2860 is FALSE, and repaints the item appropriately.
2861
2862 If the list box is a \c Single selection list box and \a select is
2863 TRUE, setSelected() calls setCurrentItem().
2864
2865 If the list box is a \c Single selection list box, \a select is
2866 FALSE, setSelected() calls clearSelection().
2867
2868 \sa setMultiSelection(), setCurrentItem(), clearSelection(), currentItem()
2869*/
2870
2871void QListBox::setSelected( QListBoxItem * item, bool select )
2872{
2873 if ( !item || !item->isSelectable() ||
2874 (bool)item->s == select || d->selectionMode == NoSelection )
2875 return;
2876
2877 int ind = index( item );
2878 bool emitHighlighted = (d->current != item) ||
2879 ( item->s != (uint) select && select );
2880 if ( selectionMode() == Single ) {
2881 if ( d->current != item ) {
2882 QListBoxItem *o = d->current;
2883 if ( d->current && d->current->s )
2884 d->current->s = FALSE;
2885 d->current = item;
2886#if defined(QT_ACCESSIBILITY_SUPPORT)
2887 QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::Focus );
2888#endif
2889 d->currentColumn = ind / numRows();
2890 d->currentRow = ind % numRows();
2891
2892 if ( o )
2893 updateItem( o );
2894 }
2895 }
2896
2897 item->s = (uint)select;
2898 updateItem( item );
2899
2900 if ( d->selectionMode == Single && select ) {
2901 emit selectionChanged( item );
2902#if defined(QT_ACCESSIBILITY_SUPPORT)
2903 QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged );
2904#endif
2905 }
2906 emit selectionChanged();
2907#if defined(QT_ACCESSIBILITY_SUPPORT)
2908 QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection );
2909 if ( d->selectionMode != Single )
2910 QAccessible::updateAccessibility( viewport(), ind+1, select ? QAccessible::SelectionAdd : QAccessible::SelectionRemove );
2911#endif
2912
2913 if ( emitHighlighted ) {
2914 QString tmp;
2915 if ( d->current )
2916 tmp = d->current->text();
2917 int tmp2 = index( d->current );
2918 emit highlighted( d->current );
2919 if ( !tmp.isNull() )
2920 emit highlighted( tmp );
2921 emit highlighted( tmp2 );
2922 emit currentChanged( d->current );
2923 }
2924}
2925
2926
2927/*!
2928 Returns TRUE if item \a i is selected; otherwise returns FALSE.
2929*/
2930
2931bool QListBox::isSelected( int i ) const
2932{
2933 if ( selectionMode() == Single && i != currentItem() )
2934 return FALSE;
2935
2936 QListBoxItem * lbi = item( i );
2937 if ( !lbi )
2938 return FALSE; // should not happen
2939 return lbi->s;
2940}
2941
2942
2943/*!
2944 \overload
2945
2946 Returns TRUE if item \a i is selected; otherwise returns FALSE.
2947*/
2948
2949bool QListBox::isSelected( const QListBoxItem * i ) const
2950{
2951 if ( !i )
2952 return FALSE;
2953
2954 return i->s;
2955}
2956
2957/*! Returns the selected item if the list box is in
2958single-selection mode and an item is selected.
2959
2960If no items are selected or the list box is in another selection mode
2961this function returns 0.
2962
2963\sa setSelected() setMultiSelection()
2964*/
2965
2966QListBoxItem* QListBox::selectedItem() const
2967{
2968 if ( d->selectionMode != Single )
2969 return 0;
2970 if ( isSelected( currentItem() ) )
2971 return d->current;
2972 return 0;
2973}
2974
2975
2976/*!
2977 Deselects all items, if possible.
2978
2979 Note that a \c Single selection list box will automatically select
2980 an item if it has keyboard focus.
2981*/
2982
2983void QListBox::clearSelection()
2984{
2985 selectAll( FALSE );
2986}
2987
2988/*!
2989 In \c Multi and \c Extended modes, this function sets all items to
2990 be selected if \a select is TRUE, and to be unselected if \a
2991 select is FALSE.
2992
2993 In \c Single and \c NoSelection modes, this function only changes
2994 the selection status of currentItem().
2995*/
2996
2997void QListBox::selectAll( bool select )
2998{
2999 if ( selectionMode() == Multi || selectionMode() == Extended ) {
3000 bool b = signalsBlocked();
3001 blockSignals( TRUE );
3002 for ( int i = 0; i < (int)count(); i++ )
3003 setSelected( i, select );
3004 blockSignals( b );
3005 emit selectionChanged();
3006 } else if ( d->current ) {
3007 QListBoxItem * i = d->current;
3008 setSelected( i, select );
3009 }
3010}
3011
3012/*!
3013 Inverts the selection. Only works in \c Multi and \c Extended
3014 selection mode.
3015*/
3016
3017void QListBox::invertSelection()
3018{
3019 if ( d->selectionMode == Single ||
3020 d->selectionMode == NoSelection )
3021 return;
3022
3023 bool b = signalsBlocked();
3024 blockSignals( TRUE );
3025 for ( int i = 0; i < (int)count(); i++ )
3026 setSelected( i, !item( i )->isSelected() );
3027 blockSignals( b );
3028 emit selectionChanged();
3029}
3030
3031
3032/*!
3033 \obsolete
3034 Not used anymore; provided for binary compatibility
3035*/
3036
3037void QListBox::emitChangedSignal( bool )
3038{
3039}
3040
3041
3042/*! \reimp */
3043
3044QSize QListBox::sizeHint() const
3045{
3046 if ( cachedSizeHint().isValid() )
3047 return cachedSizeHint();
3048
3049 constPolish();
3050 doLayout();
3051
3052 int i=0;
3053 while( i < 10 &&
3054 i < (int)d->columnPos.size()-1 &&
3055 d->columnPos[i] < 200 )
3056 i++;
3057 int x;
3058 x = QMIN( 200, d->columnPos[i] +
3059 2 * style().pixelMetric( QStyle::PM_DefaultFrameWidth ) );
3060 x = QMAX( 40, x );
3061
3062 i = 0;
3063 while( i < 10 &&
3064 i < (int)d->rowPos.size()-1 &&
3065 d->rowPos[i] < 200 )
3066 i++;
3067 int y;
3068 y = QMIN( 200, d->rowPos[i] +
3069 2 * style().pixelMetric( QStyle::PM_DefaultFrameWidth ) );
3070 y = QMAX( 40, y );
3071
3072 QSize s( x, y );
3073 setCachedSizeHint( s );
3074 return s;
3075}
3076
3077/*!
3078 \reimp
3079*/
3080
3081QSize QListBox::minimumSizeHint() const
3082{
3083 return QScrollView::minimumSizeHint();
3084}
3085
3086
3087/*!
3088 Ensures that a single paint event will occur at the end of the
3089 current event loop iteration. If \a doLayout is TRUE, the layout
3090 is also redone.
3091*/
3092
3093void QListBox::triggerUpdate( bool doLayout )
3094{
3095 if ( doLayout )
3096 d->layoutDirty = d->mustPaintAll = TRUE;
3097 d->updateTimer->start( 0, TRUE );
3098}
3099
3100
3101void QListBox::setColumnMode( LayoutMode mode )
3102{
3103 if ( mode == Variable )
3104 return;
3105 d->rowModeWins = FALSE;
3106 d->columnMode = mode;
3107 triggerUpdate( TRUE );
3108}
3109
3110
3111void QListBox::setColumnMode( int columns )
3112{
3113 if ( columns < 1 )
3114 columns = 1;
3115 d->columnMode = FixedNumber;
3116 d->numColumns = columns;
3117 d->rowModeWins = FALSE;
3118 triggerUpdate( TRUE );
3119}
3120
3121void QListBox::setRowMode( LayoutMode mode )
3122{
3123 if ( mode == Variable )
3124 return;
3125 d->rowModeWins = TRUE;
3126 d->rowMode = mode;
3127 triggerUpdate( TRUE );
3128}
3129
3130
3131void QListBox::setRowMode( int rows )
3132{
3133 if ( rows < 1 )
3134 rows = 1;
3135 d->rowMode = FixedNumber;
3136 d->numRows = rows;
3137 d->rowModeWins = TRUE;
3138 triggerUpdate( TRUE );
3139}
3140
3141/*!
3142 \property QListBox::columnMode
3143 \brief the column layout mode for this list box.
3144
3145 setColumnMode() sets the layout mode and adjusts the number of
3146 displayed columns. The row layout mode automatically becomes \c
3147 Variable, unless the column mode is \c Variable.
3148
3149 \sa setRowMode() columnMode() rowMode numColumns
3150*/
3151
3152
3153QListBox::LayoutMode QListBox::columnMode() const
3154{
3155 if ( d->rowModeWins )
3156 return Variable;
3157 else
3158 return d->columnMode;
3159}
3160
3161
3162/*!
3163 \property QListBox::rowMode
3164 \brief the row layout mode for this list box
3165
3166 This property is normally \c Variable.
3167
3168 setRowMode() sets the layout mode and adjusts the number of
3169 displayed rows. The column layout mode automatically becomes \c
3170 Variable, unless the row mode is \c Variable.
3171
3172 \sa columnMode rowMode
3173*/
3174
3175
3176QListBox::LayoutMode QListBox::rowMode() const
3177{
3178 if ( d->rowModeWins )
3179 return d->rowMode;
3180 else
3181 return Variable;
3182}
3183
3184
3185/*!
3186 \property QListBox::numColumns
3187 \brief the number of columns in the list box
3188
3189 This is normally 1, but can be different if \l
3190 QListBox::columnMode or \l QListBox::rowMode has been set.
3191
3192 \sa columnMode rowMode numRows
3193*/
3194
3195int QListBox::numColumns() const
3196{
3197 if ( count() == 0 )
3198 return 0;
3199 if ( !d->rowModeWins && d->columnMode == FixedNumber )
3200 return d->numColumns;
3201 doLayout();
3202 return d->columnPos.size()-1;
3203}
3204
3205
3206/*!
3207 \property QListBox::numRows
3208 \brief the number of rows in the list box.
3209
3210 This is equal to the number of items in the default single-column
3211 layout, but can be different.
3212
3213 \sa columnMode rowMode numColumns
3214*/
3215
3216int QListBox::numRows() const
3217{
3218 if ( count() == 0 )
3219 return 0;
3220 if ( d->rowModeWins && d->rowMode == FixedNumber )
3221 return d->numRows;
3222 doLayout();
3223 return d->rowPos.size()-1;
3224}
3225
3226
3227/*!
3228 This function does the hard layout work. You should never need to
3229 call it.
3230*/
3231
3232void QListBox::doLayout() const
3233{
3234 if ( !d->layoutDirty || d->resizeTimer->isActive() )
3235 return;
3236 constPolish();
3237 int c = count();
3238 switch( rowMode() ) {
3239 case FixedNumber:
3240 // columnMode() is known to be Variable
3241 tryGeometry( d->numRows, (c+d->numRows-1)/d->numRows );
3242 break;
3243 case FitToHeight:
3244 // columnMode() is known to be Variable
3245 if ( d->head ) {
3246 // this is basically the FitToWidth code, but edited to use rows.
3247 int maxh = 0;
3248 QListBoxItem * i = d->head;
3249 while ( i ) {
3250 int h = i->height(this);
3251 if ( maxh < h )
3252 maxh = h;
3253 i = i->n;
3254 }
3255 int vh = viewportSize( 1, 1 ).height();
3256 do {
3257 int rows = vh / maxh;
3258 if ( rows > c )
3259 rows = c;
3260 if ( rows < 1 )
3261 rows = 1;
3262 if ( variableHeight() && rows < c ) {
3263 do {
3264 ++rows;
3265 tryGeometry( rows, (c+rows-1)/rows );
3266 } while ( rows <= c &&
3267 d->rowPos[(int)d->rowPos.size()-1] <= vh );
3268 --rows;
3269 }
3270 tryGeometry( rows, (c+rows-1)/rows );
3271 int nvh = viewportSize( d->columnPos[(int)d->columnPos.size()-1],
3272 d->rowPos[(int)d->rowPos.size()-1] ).height();
3273 if ( nvh < vh )
3274 vh = nvh;
3275 } while ( d->rowPos.size() > 2 &&
3276 vh < d->rowPos[(int)d->rowPos.size()-1] );
3277 } else {
3278 tryGeometry( 1, 1 );
3279 }
3280 break;
3281 case Variable:
3282 if ( columnMode() == FixedNumber ) {
3283 tryGeometry( (count()+d->numColumns-1)/d->numColumns,
3284 d->numColumns );
3285 } else if ( d->head ) { // FitToWidth, at least one item
3286 int maxw = 0;
3287 QListBoxItem * i = d->head;
3288 while ( i ) {
3289 int w = i->width(this);
3290 if ( maxw < w )
3291 maxw = w;
3292 i = i->n;
3293 }
3294 int vw = viewportSize( 1, 1 ).width();
3295 do {
3296 int cols = vw / maxw;
3297 if ( cols > c )
3298 cols = c;
3299 if ( cols < 1 )
3300 cols = 1;
3301 if ( variableWidth() && cols < c ) {
3302 do {
3303 ++cols;
3304 tryGeometry( (c+cols-1)/cols, cols );
3305 } while ( cols <= c &&
3306 d->columnPos[(int)d->columnPos.size()-1] <= vw );
3307 --cols;
3308 }
3309 tryGeometry( (c+cols-1)/cols, cols );
3310 int nvw = viewportSize( d->columnPos[(int)d->columnPos.size()-1],
3311 d->rowPos[(int)d->rowPos.size()-1] ).width();
3312 if ( nvw < vw )
3313 vw = nvw;
3314 } while ( d->columnPos.size() > 2 &&
3315 vw < d->columnPos[(int)d->columnPos.size()-1] );
3316 } else {
3317 tryGeometry( 1, 1 );
3318 }
3319 break;
3320 }
3321
3322 d->layoutDirty = FALSE;
3323 int w = d->columnPos[(int)d->columnPos.size()-1];
3324 int h = d->rowPos[(int)d->rowPos.size()-1];
3325 QSize s( viewportSize( w, h ) );
3326 w = QMAX( w, s.width() );
3327
3328 d->columnPosOne = d->columnPos[1];
3329 // extend the column for simple single-column listboxes
3330 if ( columnMode() == FixedNumber && d->numColumns == 1 &&
3331 d->columnPos[1] < w )
3332 d->columnPos[1] = w;
3333 ((QListBox *)this)->resizeContents( w, h );
3334}
3335
3336
3337/*!
3338 Lay the items out in a \a columns by \a rows array. The array may
3339 be too big: doLayout() is expected to call this with the right
3340 values.
3341*/
3342
3343void QListBox::tryGeometry( int rows, int columns ) const
3344{
3345 if ( columns < 1 )
3346 columns = 1;
3347 d->columnPos.resize( columns+1 );
3348
3349 if ( rows < 1 )
3350 rows = 1;
3351 d->rowPos.resize( rows+1 );
3352
3353 // funky hack I: dump the height/width of each column/row in
3354 // {column,row}Pos for later conversion to positions.
3355 int c;
3356 for( c=0; c<=columns; c++ )
3357 d->columnPos[c] = 0;
3358 int r;
3359 for( r=0; r<=rows; r++ )
3360 d->rowPos[r] = 0;
3361 r = c = 0;
3362 QListBoxItem * i = d->head;
3363 while ( i && c < columns ) {
3364 if ( i == d->current ) {
3365 d->currentRow = r;
3366 d->currentColumn = c;
3367 }
3368
3369 int w = i->width(this);
3370 if ( d->columnPos[c] < w )
3371 d->columnPos[c] = w;
3372 int h = i->height(this);
3373 if ( d->rowPos[r] < h )
3374 d->rowPos[r] = h;
3375 i = i->n;
3376 r++;
3377 if ( r == rows ) {
3378 r = 0;
3379 c++;
3380 }
3381 }
3382 // funky hack II: if not variable {width,height}, unvariablify it.
3383 if ( !variableWidth() ) {
3384 int w = 0;
3385 for( c=0; c<columns; c++ )
3386 if ( w < d->columnPos[c] )
3387 w = d->columnPos[c];
3388 for( c=0; c<columns; c++ )
3389 d->columnPos[c] = w;
3390 }
3391 if ( !variableHeight() ) {
3392 int h = 0;
3393 for( r=0; r<rows; r++ )
3394 if ( h < d->rowPos[r] )
3395 h = d->rowPos[r];
3396 for( r=0; r<rows; r++ )
3397 d->rowPos[r] = h;
3398 }
3399 // repair the hacking.
3400 int x = 0;
3401 for( c=0; c<=columns; c++ ) {
3402 int w = d->columnPos[c];
3403 d->columnPos[c] = x;
3404 x += w;
3405 }
3406 int y = 0;
3407 for( r=0; r<=rows; r++ ) {
3408 int h = d->rowPos[r];
3409 d->rowPos[r] = y;
3410 y += h;
3411 }
3412}
3413
3414
3415/*!
3416 Returns the row index of the current item, or -1 if no item is the
3417 current item.
3418*/
3419
3420int QListBox::currentRow() const
3421{
3422 if ( !d->current )
3423 return -1;
3424 if ( d->currentRow < 0 )
3425 d->layoutDirty = TRUE;
3426 if ( d->layoutDirty )
3427 doLayout();
3428 return d->currentRow;
3429}
3430
3431
3432/*!
3433 Returns the column index of the current item, or -1 if no item is
3434 the current item.
3435*/
3436
3437int QListBox::currentColumn() const
3438{
3439 if ( !d->current )
3440 return -1;
3441 if ( d->currentColumn < 0 )
3442 d->layoutDirty = TRUE;
3443 if ( d->layoutDirty )
3444 doLayout();
3445 return d->currentColumn;
3446}
3447
3448
3449void QListBox::setTopItem( int index )
3450{
3451 if ( index >= (int)count() || count() == 0 )
3452 return;
3453 int col = index / numRows();
3454 int y = d->rowPos[index-col*numRows()];
3455 if ( d->columnPos[col] >= contentsX() &&
3456 d->columnPos[col+1] <= contentsX() + visibleWidth() )
3457 setContentsPos( contentsX(), y );
3458 else
3459 setContentsPos( d->columnPos[col], y );
3460}
3461
3462/*!
3463 Scrolls the list box so the item at position \a index in the list
3464 is displayed in the bottom row of the list box.
3465
3466 \sa setTopItem()
3467*/
3468
3469void QListBox::setBottomItem( int index )
3470{
3471 if ( index >= (int)count() || count() == 0 )
3472 return;
3473 int col = index / numRows();
3474 int y = d->rowPos[1+index-col*numRows()] - visibleHeight();
3475 if ( y < 0 )
3476 y = 0;
3477 if ( d->columnPos[col] >= contentsX() &&
3478 d->columnPos[col+1] <= contentsX() + visibleWidth() )
3479 setContentsPos( contentsX(), y );
3480 else
3481 setContentsPos( d->columnPos[col], y );
3482}
3483
3484
3485/*!
3486 Returns the item at point \a p, which is in on-screen coordinates,
3487 or a 0 if there is no item at \a p.
3488*/
3489
3490QListBoxItem * QListBox::itemAt( const QPoint& p ) const
3491{
3492 if ( d->layoutDirty )
3493 doLayout();
3494 QPoint np = p;
3495
3496 // take into acount frame margin to get to viewport
3497 np -= QPoint( margin(), margin() );
3498 if ((np.x() < 0) || (np.y() < 0))
3499 return 0;
3500
3501 // take into account contents position
3502 np = viewportToContents( np );
3503
3504 int x = np.x();
3505 int y = np.y();
3506
3507 // return 0 when y is below the last row
3508 if ( y > d->rowPos[ numRows() ] )
3509 return 0;
3510
3511 int col = columnAt( x );
3512 int row = rowAt( y );
3513
3514 QListBoxItem *i = item( col * numRows() + row );
3515 if ( i && numColumns() > 1 ) {
3516 if ( d->columnPos[ col ] + i->width( this ) >= x )
3517 return i;
3518 } else {
3519 if ( d->columnPos[ col + 1 ] >= x )
3520 return i;
3521 }
3522 return 0;
3523}
3524
3525
3526/*!
3527 Ensures that the current item is visible.
3528*/
3529
3530void QListBox::ensureCurrentVisible()
3531{
3532 if ( !d->current )
3533 return;
3534
3535 doLayout();
3536
3537 int row = currentRow();
3538 int column = currentColumn();
3539 int w = ( d->columnPos[column+1] - d->columnPos[column] ) / 2;
3540 int h = ( d->rowPos[row+1] - d->rowPos[row] ) / 2;
3541 // next four lines are Bad. they mean that for pure left-to-right
3542 // languages, textual list box items are displayed better than
3543 // before when there is little space. for non-textual items, or
3544 // other languages, it means... that you really should have enough
3545 // space in the first place :)
3546 if ( numColumns() == 1 )
3547 w = 0;
3548 if ( w*2 > viewport()->width() )
3549 w = viewport()->width()/2;
3550
3551 ensureVisible( d->columnPos[column] + w, d->rowPos[row] + h, w, h);
3552}
3553
3554
3555/*! \internal */
3556
3557void QListBox::doAutoScroll()
3558{
3559 if ( d->scrollPos.x() < 0 ) {
3560 // scroll left
3561 int x = contentsX() - horizontalScrollBar()->lineStep();
3562 if ( x < 0 )
3563 x = 0;
3564 if ( x != contentsX() ) {
3565 d->mouseMoveColumn = columnAt( x );
3566 updateSelection();
3567 if ( x < contentsX() )
3568 setContentsPos( x, contentsY() );
3569 }
3570 } else if ( d->scrollPos.x() > 0 ) {
3571 // scroll right
3572 int x = contentsX() + horizontalScrollBar()->lineStep();
3573 if ( x + visibleWidth() > contentsWidth() )
3574 x = contentsWidth() - visibleWidth();
3575 if ( x != contentsX() ) {
3576 d->mouseMoveColumn = columnAt( x + visibleWidth() - 1 );
3577 updateSelection();
3578 if ( x > contentsX() )
3579 setContentsPos( x, contentsY() );
3580 }
3581 }
3582
3583 if ( d->scrollPos.y() < 0 ) {
3584 // scroll up
3585 int y = contentsY() - verticalScrollBar()->lineStep();
3586 if ( y < 0 )
3587 y = 0;
3588 if ( y != contentsY() ) {
3589 y = contentsY() - verticalScrollBar()->lineStep();
3590 d->mouseMoveRow = rowAt( y );
3591 updateSelection();
3592 }
3593 } else if ( d->scrollPos.y() > 0 ) {
3594 // scroll down
3595 int y = contentsY() + verticalScrollBar()->lineStep();
3596 if ( y + visibleHeight() > contentsHeight() )
3597 y = contentsHeight() - visibleHeight();
3598 if ( y != contentsY() ) {
3599 y = contentsY() + verticalScrollBar()->lineStep();
3600 d->mouseMoveRow = rowAt(y + visibleHeight() - 1 );
3601 updateSelection();
3602 }
3603 }
3604
3605 if ( d->scrollPos == QPoint( 0, 0 ) ) {
3606 delete d->scrollTimer;
3607 d->scrollTimer = 0;
3608 }
3609}
3610
3611
3612/*!
3613 \property QListBox::topItem
3614 \brief the index of an item at the top of the screen.
3615
3616 When getting this property and the listbox has multiple columns,
3617 an arbitrary item is selected and returned.
3618
3619 When setting this property, the list box is scrolled so the item
3620 at position \e index in the list is displayed in the top row of
3621 the list box.
3622*/
3623
3624int QListBox::topItem() const
3625{
3626 doLayout();
3627
3628 // move rightwards to the best column
3629 int col = columnAt( contentsX() );
3630 int row = rowAt( contentsY() );
3631 return col * numRows() + row;
3632}
3633
3634
3635/*!
3636 \property QListBox::variableHeight
3637 \brief whether this list box has variable-height rows
3638
3639 When the list box has variable-height rows (the default), each row
3640 is as high as the highest item in that row. When it has same-sized
3641 rows, all rows are as high as the highest item in the list box.
3642
3643 \sa variableWidth
3644*/
3645
3646bool QListBox::variableHeight() const
3647{
3648 return d->variableHeight;
3649}
3650
3651
3652void QListBox::setVariableHeight( bool enable )
3653{
3654 if ( (bool)d->variableHeight == enable )
3655 return;
3656
3657 d->variableHeight = enable;
3658 triggerUpdate( TRUE );
3659}
3660
3661
3662/*!
3663 \property QListBox::variableWidth
3664 \brief whether this list box has variable-width columns
3665
3666 When the list box has variable-width columns, each column is as
3667 wide as the widest item in that column. When it has same-sized
3668 columns (the default), all columns are as wide as the widest item
3669 in the list box.
3670
3671 \sa variableHeight
3672*/
3673
3674bool QListBox::variableWidth() const
3675{
3676 return d->variableWidth;
3677}
3678
3679
3680void QListBox::setVariableWidth( bool enable )
3681{
3682 if ( (bool)d->variableWidth == enable )
3683 return;
3684
3685 d->variableWidth = enable;
3686 triggerUpdate( TRUE );
3687}
3688
3689
3690/*!
3691 Repaints only what really needs to be repainted.
3692*/
3693void QListBox::refreshSlot()
3694{
3695 if ( d->mustPaintAll ||
3696 d->layoutDirty ) {
3697 d->mustPaintAll = FALSE;
3698 bool currentItemVisible = itemVisible( currentItem() );
3699 doLayout();
3700 if ( hasFocus() &&
3701 currentItemVisible &&
3702 d->currentColumn >= 0 &&
3703 d->currentRow >= 0 &&
3704 ( d->columnPos[d->currentColumn] < contentsX() ||
3705 d->columnPos[d->currentColumn+1]>contentsX()+visibleWidth() ||
3706 d->rowPos[d->currentRow] < contentsY() ||
3707 d->rowPos[d->currentRow+1] > contentsY()+visibleHeight() ) )
3708 ensureCurrentVisible();
3709 viewport()->repaint( FALSE );
3710 return;
3711 }
3712
3713 QRegion r;
3714 int x = contentsX();
3715 int y = contentsY();
3716 int col = columnAt( x );
3717 int row = rowAt( y );
3718 int top = row;
3719 while( col < (int)d->columnPos.size()-1 && d->columnPos[col+1] < x )
3720 col++;
3721 while( top < (int)d->rowPos.size()-1 && d->rowPos[top+1] < y )
3722 top++;
3723 QListBoxItem * i = item( col * numRows() + row );
3724
3725 while ( i && (int)col < numColumns() &&
3726 d->columnPos[col] < x + visibleWidth() ) {
3727 int cw = d->columnPos[col+1] - d->columnPos[col];
3728 while ( i && row < numRows() && d->rowPos[row] <
3729 y + visibleHeight() ) {
3730 if ( i->dirty )
3731 r = r.unite( QRect( d->columnPos[col] - x, d->rowPos[row] - y,
3732 cw, d->rowPos[row+1] - d->rowPos[row] ) );
3733 row++;
3734 i = i->n;
3735 }
3736 col++;
3737 if ( numColumns() > 1 ) {
3738 row = top;
3739 i = item( col * numRows() + row );
3740 }
3741 }
3742
3743 if ( r.isEmpty() )
3744 viewport()->repaint( FALSE );
3745 else
3746 viewport()->repaint( r, FALSE );
3747}
3748
3749
3750/*! \reimp */
3751
3752void QListBox::viewportPaintEvent( QPaintEvent * e )
3753{
3754 doLayout();
3755 QWidget* vp = viewport();
3756 QPainter p( vp );
3757 QRegion r = e->region();
3758
3759#if 0
3760 {
3761 // this stuff has been useful enough times that from now I'm
3762 // leaving it in the source.
3763 uint i = 0;
3764 qDebug( "%s/%s: %i rects", className(), name(), r.rects().size() );
3765 while( i < r.rects().size() ) {
3766 qDebug( "rect %d: %d, %d, %d, %d", i,
3767 r.rects()[i].left(), r.rects()[i].top(),
3768 r.rects()[i].width(), r.rects()[i].height() );
3769 i++;
3770 }
3771 qDebug( "" );
3772 }
3773#endif
3774
3775 int x = contentsX();
3776 int y = contentsY();
3777 int w = vp->width();
3778 int h = vp->height();
3779
3780 int col = columnAt( x );
3781 int top = rowAt( y );
3782 int row = top;
3783
3784 QListBoxItem * i = item( col*numRows() + row );
3785
3786 const QColorGroup & g = colorGroup();
3787 p.setPen( g.text() );
3788 p.setBackgroundColor( backgroundBrush().color() );
3789 while ( i && (int)col < numColumns() && d->columnPos[col] < x + w ) {
3790 int cw = d->columnPos[col+1] - d->columnPos[col];
3791 while ( i && (int)row < numRows() && d->rowPos[row] < y + h ) {
3792 int ch = d->rowPos[row+1] - d->rowPos[row];
3793 QRect itemRect( d->columnPos[col]-x, d->rowPos[row]-y, cw, ch );
3794 QRegion tempRegion( itemRect );
3795 QRegion itemPaintRegion( tempRegion.intersect( r ) );
3796 if ( !itemPaintRegion.isEmpty() ) {
3797 p.save();
3798 p.setClipRegion( itemPaintRegion );
3799 p.translate( d->columnPos[col]-x, d->rowPos[row]-y );
3800 paintCell( &p, row, col );
3801 p.restore();
3802 r = r.subtract( itemPaintRegion );
3803 }
3804 row++;
3805 if ( i->dirty ) {
3806 // reset dirty flag only if the entire item was painted
3807 if ( itemPaintRegion == QRegion( itemRect ) )
3808 i->dirty = FALSE;
3809 }
3810 i = i->n;
3811 }
3812 col++;
3813 if ( numColumns() > 1 ) {
3814 row = top;
3815 i = item( col * numRows() + row );
3816 }
3817 }
3818
3819 if ( r.isEmpty() )
3820 return;
3821 p.setClipRegion( r );
3822 p.fillRect( 0, 0, w, h, viewport()->backgroundBrush() );
3823}
3824
3825
3826/*!
3827 Returns the height in pixels of the item with index \a index. \a
3828 index defaults to 0.
3829
3830 If \a index is too large, this function returns 0.
3831*/
3832
3833int QListBox::itemHeight( int index ) const
3834{
3835 if ( index >= (int)count() || index < 0 )
3836 return 0;
3837 int r = index % numRows();
3838 return d->rowPos[r+1] - d->rowPos[r];
3839}
3840
3841
3842/*!
3843 Returns the index of the column at \a x, which is in the listbox's
3844 coordinates, not in on-screen coordinates.
3845
3846 If there is no column that spans \a x, columnAt() returns -1.
3847*/
3848
3849int QListBox::columnAt( int x ) const
3850{
3851 if ( x < 0 )
3852 return -1;
3853 if ( !d->columnPos.size() )
3854 return -1;
3855 if ( x >= d->columnPos[(int)d->columnPos.size()-1 ] )
3856 return numColumns() - 1;
3857
3858 int col = 0;
3859 while( col < (int)d->columnPos.size()-1 && d->columnPos[col+1] < x )
3860 col++;
3861 return col;
3862}
3863
3864
3865/*!
3866 Returns the index of the row at \a y, which is in the listbox's
3867 coordinates, not in on-screen coordinates.
3868
3869 If there is no row that spans \a y, rowAt() returns -1.
3870*/
3871
3872int QListBox::rowAt( int y ) const
3873{
3874 if ( y < 0 )
3875 return -1;
3876
3877 // find the top item, use bsearch for speed
3878 int l = 0;
3879 int r = d->rowPos.size() - 2;
3880 if ( r < 0 )
3881 return -1;
3882 if ( l <= d->rowPosCache && d->rowPosCache <= r ) {
3883 if ( d->rowPos[ QMAX( l, d->rowPosCache - 10 ) ] <= y
3884 && y <= d->rowPos[ QMIN( r, d->rowPosCache + 10 ) ] ) {
3885 l = QMAX( l, d->rowPosCache - 10 );
3886 r = QMIN( r, d->rowPosCache + 10 );
3887 }
3888 }
3889 int i = ( (l+r+1) / 2 );
3890 while ( r - l ) {
3891 if ( d->rowPos[i] > y )
3892 r = i -1;
3893 else
3894 l = i;
3895 i = ( (l+r+1) / 2 );
3896 }
3897 d->rowPosCache = i;
3898 if ( d->rowPos[i] <= y && y <= d->rowPos[i+1] )
3899 return i;
3900
3901 return d->count - 1;
3902}
3903
3904
3905/*!
3906 Returns the rectangle on the screen that \a item occupies in
3907 viewport()'s coordinates, or an invalid rectangle if \a item is 0
3908 or is not currently visible.
3909*/
3910
3911QRect QListBox::itemRect( QListBoxItem *item ) const
3912{
3913 if ( d->resizeTimer->isActive() )
3914 return QRect( 0, 0, -1, -1 );
3915 if ( !item )
3916 return QRect( 0, 0, -1, -1 );
3917
3918 int i = index( item );
3919 int col = i / numRows();
3920 int row = i % numRows();
3921
3922 int x = d->columnPos[ col ] - contentsX();
3923 int y = d->rowPos[ row ] - contentsY();
3924
3925 QRect r( x, y, d->columnPos[ col + 1 ] - d->columnPos[ col ],
3926 d->rowPos[ row + 1 ] - d->rowPos[ row ] );
3927 if ( r.intersects( QRect( 0, 0, visibleWidth(), visibleHeight() ) ) )
3928 return r;
3929 return QRect( 0, 0, -1, -1 );
3930}
3931
3932
3933#ifndef QT_NO_COMPAT
3934
3935/*!
3936 \obsolete
3937
3938 Using this method is quite inefficient. We suggest to use insertItem()
3939 for inserting and sort() afterwards.
3940
3941 Inserts \a lbi at its sorted position in the list box and returns the
3942 position.
3943
3944 All items must be inserted with inSort() to maintain the sorting
3945 order. inSort() treats any pixmap (or user-defined type) as
3946 lexicographically less than any string.
3947
3948 \sa insertItem(), sort()
3949*/
3950int QListBox::inSort( const QListBoxItem * lbi )
3951{
3952 qObsolete( "QListBox", "inSort", "insertItem" );
3953 if ( !lbi )
3954 return -1;
3955
3956 QListBoxItem * i = d->head;
3957 int c = 0;
3958
3959 while( i && i->text() < lbi->text() ) {
3960 i = i->n;
3961 c++;
3962 }
3963 insertItem( lbi, c );
3964 return c;
3965}
3966
3967/*!
3968 \obsolete
3969 \overload
3970 Using this method is quite inefficient. We suggest to use insertItem()
3971 for inserting and sort() afterwards.
3972
3973 Inserts a new item of \a text at its sorted position in the list box and
3974 returns the position.
3975
3976 All items must be inserted with inSort() to maintain the sorting
3977 order. inSort() treats any pixmap (or user-defined type) as
3978 lexicographically less than any string.
3979
3980 \sa insertItem(), sort()
3981*/
3982int QListBox::inSort( const QString& text )
3983{
3984 qObsolete( "QListBox", "inSort", "insertItem" );
3985 return inSort( new QListBoxText(text) );
3986}
3987
3988#endif
3989
3990
3991/*! \reimp */
3992
3993void QListBox::resizeEvent( QResizeEvent *e )
3994{
3995 d->layoutDirty = ( d->layoutDirty ||
3996 rowMode() == FitToHeight ||
3997 columnMode() == FitToWidth );
3998
3999 if ( !d->layoutDirty && columnMode() == FixedNumber &&
4000 d->numColumns == 1) {
4001 int w = d->columnPosOne;
4002 QSize s( viewportSize( w, contentsHeight() ) );
4003 w = QMAX( w, s.width() );
4004 d->columnPos[1] = QMAX( w, d->columnPosOne );
4005 resizeContents( d->columnPos[1], contentsHeight() );
4006 }
4007
4008 if ( d->resizeTimer->isActive() )
4009 d->resizeTimer->stop();
4010 if ( d->rowMode == FixedNumber && d->columnMode == FixedNumber ) {
4011 bool currentItemVisible = itemVisible( currentItem() );
4012 doLayout();
4013 QScrollView::resizeEvent( e );
4014 if ( currentItemVisible )
4015 ensureCurrentVisible();
4016 if ( d->current )
4017 viewport()->repaint( itemRect( d->current ), FALSE );
4018 } else if ( ( d->columnMode == FitToWidth || d->rowMode == FitToHeight ) && !(isVisible()) ) {
4019 QScrollView::resizeEvent( e );
4020 } else if ( d->layoutDirty ) {
4021 d->resizeTimer->start( 100, TRUE );
4022 resizeContents( contentsWidth() - ( e->oldSize().width() - e->size().width() ),
4023 contentsHeight() - ( e->oldSize().height() - e->size().height() ) );
4024 QScrollView::resizeEvent( e );
4025 } else {
4026 QScrollView::resizeEvent( e );
4027 }
4028}
4029
4030/*!
4031 \internal
4032*/
4033
4034void QListBox::adjustItems()
4035{
4036 triggerUpdate( TRUE );
4037 ensureCurrentVisible();
4038}
4039
4040
4041/*!
4042 Provided for compatibility with the old QListBox. We recommend
4043 using QListBoxItem::paint() instead.
4044
4045 Repaints the cell at \a row, \a col using painter \a p.
4046*/
4047
4048void QListBox::paintCell( QPainter * p, int row, int col )
4049{
4050 bool drawActiveSelection = hasFocus() || d->inMenuMode ||
4051 !style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this );
4052 const QColorGroup &g = ( drawActiveSelection ? colorGroup() : palette().inactive() );
4053
4054 int cw = d->columnPos[col+1] - d->columnPos[col];
4055 int ch = d->rowPos[row+1] - d->rowPos[row];
4056 QListBoxItem * i = item( col*numRows()+row );
4057 p->save();
4058 if ( i->s ) {
4059 if ( i->custom_highlight ) {
4060 p->fillRect( 0, 0, cw, ch,
4061 g.brush( QPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) );
4062 p->setPen( g.highlightedText() );
4063 p->setBackgroundColor( g.highlight() );
4064 }
4065 else if ( numColumns() == 1 ) {
4066 p->fillRect( 0, 0, cw, ch, g.brush( QColorGroup::Highlight ) );
4067 p->setPen( g.highlightedText() );
4068 p->setBackgroundColor( g.highlight() );
4069 } else {
4070 int iw = i->width( this );
4071 p->fillRect( 0, 0, iw, ch, g.brush( QColorGroup::Highlight ) );
4072 p->fillRect( iw, 0, cw - iw + 1, ch,
4073 g.brush( QPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) );
4074 p->setPen( g.highlightedText() );
4075 p->setBackgroundColor( g.highlight() );
4076 }
4077 } else {
4078 p->fillRect( 0, 0, cw, ch,
4079 g.brush( QPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) );
4080 }
4081
4082 i->paint( p );
4083
4084 if ( d->current == i && hasFocus() && !i->custom_highlight ) {
4085 if ( numColumns() > 1 )
4086 cw = i->width( this );
4087
4088 style().drawPrimitive( QStyle::PE_FocusRect, p, QRect( 0, 0, cw, ch ), g,
4089 QStyle::Style_FocusAtBorder,
4090 QStyleOption(i->isSelected() ? g.highlight() : g.base() ) );
4091 }
4092
4093 p->restore();
4094}
4095
4096/*!
4097 Returns the width of the widest item in the list box.
4098*/
4099
4100long QListBox::maxItemWidth() const
4101{
4102 if ( d->layoutDirty )
4103 doLayout();
4104 long m = 0;
4105 int i = d->columnPos.size();
4106 while( i-- )
4107 if ( m < d->columnPos[i] )
4108 m = d->columnPos[i];
4109 return m;
4110}
4111
4112
4113/*! \reimp */
4114
4115void QListBox::showEvent( QShowEvent * )
4116{
4117 d->ignoreMoves = FALSE;
4118 d->mousePressRow = -1;
4119 d->mousePressColumn = -1;
4120 d->mustPaintAll = FALSE;
4121 ensureCurrentVisible();
4122}
4123
4124#ifndef QT_NO_COMPAT
4125
4126/*!
4127 \obsolete
4128
4129 Returns the vertical pixel-coordinate in \a *yPos, of the list box
4130 item at position \a index in the list. Returns FALSE if the item is
4131 outside the visible area.
4132*/
4133bool QListBox::itemYPos( int index, int *yPos ) const
4134{
4135 qObsolete( "QListBox", "itemYPos" );
4136 QListBoxItem* i = item(index);
4137 if ( !i )
4138 return FALSE;
4139 if ( yPos )
4140 *yPos = i->y;
4141 return TRUE;
4142}
4143
4144#endif
4145
4146/*!
4147 \fn bool QListBoxItem::isSelected() const
4148
4149 Returns TRUE if the item is selected; otherwise returns FALSE.
4150
4151 \sa QListBox::isSelected(), isCurrent()
4152*/
4153
4154/*!
4155 \fn bool QListBoxItem::selected() const
4156 \obsolete
4157*/
4158
4159/*!
4160 Returns TRUE if the item is the current item; otherwise returns
4161 FALSE.
4162
4163 \sa QListBox::currentItem(), QListBox::item(), isSelected()
4164*/
4165bool QListBoxItem::isCurrent() const
4166{
4167 return listBox() && listBox()->hasFocus() &&
4168 listBox()->item( listBox()->currentItem() ) == this;
4169}
4170/*!
4171 \fn bool QListBoxItem::current() const
4172 \obsolete
4173*/
4174
4175/*!
4176 \fn void QListBox::centerCurrentItem()
4177
4178 If there is a current item, the list box is scrolled so that this
4179 item is displayed centered.
4180
4181 \sa QListBox::ensureCurrentVisible()
4182*/
4183
4184/*!
4185 Returns a pointer to the list box containing this item.
4186*/
4187
4188QListBox * QListBoxItem::listBox() const
4189{
4190 return lbox;
4191}
4192
4193
4194/*!
4195 Removes \a item from the list box and causes an update of the
4196 screen display. The item is not deleted. You should normally not
4197 need to call this function because QListBoxItem::~QListBoxItem()
4198 calls it. The normal way to delete an item is with \c delete.
4199
4200 \sa QListBox::insertItem()
4201*/
4202void QListBox::takeItem( const QListBoxItem * item )
4203{
4204 if ( !item || d->clearing )
4205 return;
4206 d->cache = 0;
4207 d->count--;
4208 if ( item == d->last )
4209 d->last = d->last->p;
4210 if ( item->p && item->p->n == item )
4211 item->p->n = item->n;
4212 if ( item->n && item->n->p == item )
4213 item->n->p = item->p;
4214 if ( d->head == item ) {
4215 d->head = item->n;
4216 d->currentColumn = d->currentRow = -1;
4217 }
4218
4219 if ( d->current == item ) {
4220 d->current = item->n ? item->n : item->p;
4221 QListBoxItem *i = d->current;
4222 QString tmp;
4223 if ( i )
4224 tmp = i->text();
4225 int tmp2 = index( i );
4226 emit highlighted( i );
4227 if ( !tmp.isNull() )
4228 emit highlighted( tmp );
4229 emit highlighted( tmp2 );
4230 emit currentChanged( i );
4231 }
4232
4233 if ( d->selectAnchor == item )
4234 d->selectAnchor = d->current;
4235
4236 if ( item->s )
4237 emit selectionChanged();
4238 ((QListBoxItem *)item)->lbox = 0;
4239 triggerUpdate( TRUE );
4240}
4241
4242/*!
4243 \internal
4244 Finds the next item after start beginning with \a text.
4245*/
4246
4247int QListBoxPrivate::findItemByName( int start, const QString &text )
4248{
4249 if ( start < 0 || (uint)start >= listBox->count() )
4250 start = 0;
4251 QString match = text.lower();
4252 if ( match.length() < 1 )
4253 return start;
4254 QString curText;
4255 int item = start;
4256 do {
4257 curText = listBox->text( item ).lower();
4258 if ( curText.startsWith( match ) )
4259 return item;
4260 item++;
4261 if ( (uint)item == listBox->count() )
4262 item = 0;
4263 } while ( item != start );
4264 return -1;
4265}
4266
4267/*!
4268 \internal --- obsolete!
4269*/
4270
4271void QListBox::clearInputString()
4272{
4273 d->currInputString = QString::null;
4274}
4275
4276/*!
4277 Finds the first list box item that has the text \a text and
4278 returns it, or returns 0 of no such item could be found. If
4279 \c ComparisonFlags are specified in \a compare then these flags
4280 are used, otherwise the default is a case-insensitive, "begins
4281 with" search.
4282
4283 \sa Qt::StringComparisonMode
4284*/
4285
4286QListBoxItem *QListBox::findItem( const QString &text, ComparisonFlags compare ) const
4287{
4288 if ( text.isEmpty() )
4289 return 0;
4290
4291 if ( compare == CaseSensitive || compare == 0 )
4292 compare |= ExactMatch;
4293
4294 QString itmtxt;
4295 QString comtxt = text;
4296 if ( ! (compare & CaseSensitive ) )
4297 comtxt = text.lower();
4298
4299 QListBoxItem *item;
4300 if ( d->current )
4301 item = d->current;
4302 else
4303 item = d->head;
4304
4305 QListBoxItem *beginsWithItem = 0;
4306 QListBoxItem *endsWithItem = 0;
4307 QListBoxItem *containsItem = 0;
4308
4309 if ( item ) {
4310 for ( ; item; item = item->n ) {
4311 if ( ! (compare & CaseSensitive) )
4312 itmtxt = item->text().lower();
4313 else
4314 itmtxt = item->text();
4315
4316 if ( compare & ExactMatch && itmtxt == comtxt )
4317 return item;
4318 if ( compare & BeginsWith && !beginsWithItem && itmtxt.startsWith( comtxt ) )
4319 beginsWithItem = containsItem = item;
4320 if ( compare & EndsWith && !endsWithItem && itmtxt.endsWith( comtxt ) )
4321 endsWithItem = containsItem = item;
4322 if ( compare & Contains && !containsItem && itmtxt.contains( comtxt ) )
4323 containsItem = item;
4324 }
4325
4326 if ( d->current && d->head ) {
4327 item = d->head;
4328 for ( ; item && item != d->current; item = item->n ) {
4329 if ( ! (compare & CaseSensitive) )
4330 itmtxt = item->text().lower();
4331 else
4332 itmtxt = item->text();
4333
4334 if ( compare & ExactMatch && itmtxt == comtxt )
4335 return item;
4336 if ( compare & BeginsWith && !beginsWithItem && itmtxt.startsWith( comtxt ) )
4337 beginsWithItem = containsItem = item;
4338 if ( compare & EndsWith && !endsWithItem && itmtxt.endsWith( comtxt ) )
4339 endsWithItem = containsItem = item;
4340 if ( compare & Contains && !containsItem && itmtxt.contains( comtxt ) )
4341 containsItem = item;
4342 }
4343 }
4344 }
4345
4346 // Obey the priorities
4347 if ( beginsWithItem )
4348 return beginsWithItem;
4349 else if ( endsWithItem )
4350 return endsWithItem;
4351 else if ( containsItem )
4352 return containsItem;
4353 return 0;
4354}
4355
4356/*!
4357 \internal
4358*/
4359
4360void QListBox::drawRubber()
4361{
4362 if ( !d->rubber )
4363 return;
4364 if ( !d->rubber->width() && !d->rubber->height() )
4365 return;
4366 QPainter p( viewport() );
4367 p.setRasterOp( NotROP );
4368 style().drawPrimitive( QStyle::PE_RubberBand, &p, d->rubber->normalize(),
4369 colorGroup() );
4370 p.end();
4371}
4372
4373/*!
4374 \internal
4375*/
4376
4377void QListBox::doRubberSelection( const QRect &old, const QRect &rubber )
4378{
4379 QListBoxItem *i = d->head;
4380 QRect ir, pr;
4381 bool changed = FALSE;
4382 for ( ; i; i = i->n ) {
4383 ir = itemRect( i );
4384 if ( ir == QRect( 0, 0, -1, -1 ) )
4385 continue;
4386 if ( i->isSelected() && !ir.intersects( rubber ) && ir.intersects( old ) ) {
4387 i->s = FALSE;
4388 pr = pr.unite( ir );
4389 changed = TRUE;
4390 } else if ( !i->isSelected() && ir.intersects( rubber ) ) {
4391 if ( i->isSelectable() ) {
4392 i->s = TRUE;
4393 pr = pr.unite( ir );
4394 changed = TRUE;
4395 }
4396 }
4397 }
4398 if ( changed ) {
4399 emit selectionChanged();
4400#if defined(QT_ACCESSIBILITY_SUPPORT)
4401 QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection );
4402#endif
4403 }
4404 viewport()->repaint( pr, TRUE );
4405}
4406
4407
4408/*!
4409 Returns TRUE if the user is selecting items using a rubber band
4410 rectangle; otherwise returns FALSE.
4411*/
4412
4413bool QListBox::isRubberSelecting() const
4414{
4415 return d->rubber != 0;
4416}
4417
4418
4419/*!
4420 Returns the item that comes after this in the list box. If this is
4421 the last item, 0 is returned.
4422
4423 \sa prev()
4424*/
4425
4426QListBoxItem *QListBoxItem::next() const
4427{
4428 return n;
4429}
4430
4431/*!
4432 Returns the item which comes before this in the list box. If this
4433 is the first item, 0 is returned.
4434
4435 \sa next()
4436*/
4437
4438QListBoxItem *QListBoxItem::prev() const
4439{
4440 return p;
4441}
4442
4443/*!
4444 Returns the first item in this list box. If the list box is empty,
4445 returns 0.
4446*/
4447
4448QListBoxItem *QListBox::firstItem() const
4449{
4450 return d->head;
4451}
4452
4453#if defined(Q_C_CALLBACKS)
4454extern "C" {
4455#endif
4456
4457#ifdef Q_OS_TEMP
4458static int _cdecl cmpListBoxItems( const void *n1, const void *n2 )
4459#else
4460static int cmpListBoxItems( const void *n1, const void *n2 )
4461#endif
4462{
4463 if ( !n1 || !n2 )
4464 return 0;
4465
4466 QListBoxPrivate::SortableItem *i1 = (QListBoxPrivate::SortableItem *)n1;
4467 QListBoxPrivate::SortableItem *i2 = (QListBoxPrivate::SortableItem *)n2;
4468
4469 return i1->item->text().localeAwareCompare( i2->item->text() );
4470}
4471
4472#if defined(Q_C_CALLBACKS)
4473}
4474#endif
4475
4476/*!
4477 If \a ascending is TRUE sorts the items in ascending order;
4478 otherwise sorts in descending order.
4479
4480 To compare the items, the text (QListBoxItem::text()) of the items
4481 is used.
4482*/
4483
4484void QListBox::sort( bool ascending )
4485{
4486 if ( count() == 0 )
4487 return;
4488
4489 d->cache = 0;
4490
4491 QListBoxPrivate::SortableItem *items = new QListBoxPrivate::SortableItem[ count() ];
4492
4493 QListBoxItem *item = d->head;
4494 int i = 0;
4495 for ( ; item; item = item->n )
4496 items[ i++ ].item = item;
4497
4498 qsort( items, count(), sizeof( QListBoxPrivate::SortableItem ), cmpListBoxItems );
4499
4500 QListBoxItem *prev = 0;
4501 item = 0;
4502 if ( ascending ) {
4503 for ( i = 0; i < (int)count(); ++i ) {
4504 item = items[ i ].item;
4505 if ( item ) {
4506 item->p = prev;
4507 item->dirty = TRUE;
4508 if ( item->p )
4509 item->p->n = item;
4510 item->n = 0;
4511 }
4512 if ( i == 0 )
4513 d->head = item;
4514 prev = item;
4515 }
4516 } else {
4517 for ( i = (int)count() - 1; i >= 0 ; --i ) {
4518 item = items[ i ].item;
4519 if ( item ) {
4520 item->p = prev;
4521 item->dirty = TRUE;
4522 if ( item->p )
4523 item->p->n = item;
4524 item->n = 0;
4525 }
4526 if ( i == (int)count() - 1 )
4527 d->head = item;
4528 prev = item;
4529 }
4530 }
4531 d->last = item;
4532
4533 delete [] items;
4534
4535 // We have to update explicitly in case the current "vieport" overlaps the
4536 // new viewport we set (starting at (0,0)).
4537 bool haveToUpdate = contentsX() < visibleWidth() || contentsY() < visibleHeight();
4538 setContentsPos( 0, 0 );
4539 if ( haveToUpdate )
4540 updateContents( 0, 0, visibleWidth(), visibleHeight() );
4541}
4542
4543void QListBox::handleItemChange( QListBoxItem *old, bool shift, bool control )
4544{
4545 if ( d->selectionMode == Single ) {
4546 // nothing
4547 } else if ( d->selectionMode == Extended ) {
4548 if ( shift ) {
4549 selectRange( d->selectAnchor ? d->selectAnchor : old,
4550 d->current, FALSE, TRUE, (d->selectAnchor && !control) ? TRUE : FALSE );
4551 } else if ( !control ) {
4552 bool block = signalsBlocked();
4553 blockSignals( TRUE );
4554 selectAll( FALSE );
4555 blockSignals( block );
4556 setSelected( d->current, TRUE );
4557 }
4558 } else if ( d->selectionMode == Multi ) {
4559 if ( shift )
4560 selectRange( old, d->current, TRUE, FALSE );
4561 }
4562}
4563
4564void QListBox::selectRange( QListBoxItem *from, QListBoxItem *to, bool invert, bool includeFirst, bool clearSel )
4565{
4566 if ( !from || !to )
4567 return;
4568 if ( from == to && !includeFirst )
4569 return;
4570 QListBoxItem *i = 0;
4571 int index =0;
4572 int f_idx = -1, t_idx = -1;
4573 for ( i = d->head; i; i = i->n, index++ ) {
4574 if ( i == from )
4575 f_idx = index;
4576 if ( i == to )
4577 t_idx = index;
4578 if ( f_idx != -1 && t_idx != -1 )
4579 break;
4580 }
4581 if ( f_idx > t_idx ) {
4582 i = from;
4583 from = to;
4584 to = i;
4585 if ( !includeFirst )
4586 to = to->prev();
4587 } else {
4588 if ( !includeFirst )
4589 from = from->next();
4590 }
4591
4592 bool changed = FALSE;
4593 if ( clearSel ) {
4594 for ( i = d->head; i && i != from; i = i->n ) {
4595 if ( i->s ) {
4596 i->s = FALSE;
4597 changed = TRUE;
4598 updateItem( i );
4599 }
4600 }
4601 for ( i = to->n; i; i = i->n ) {
4602 if ( i->s ) {
4603 i->s = FALSE;
4604 changed = TRUE;
4605 updateItem( i );
4606 }
4607 }
4608 }
4609
4610 for ( i = from; i; i = i->next() ) {
4611 if ( !invert ) {
4612 if ( !i->s && i->isSelectable() ) {
4613 i->s = TRUE;
4614 changed = TRUE;
4615 updateItem( i );
4616 }
4617 } else {
4618 bool sel = !i->s;
4619 if ( (bool)i->s != sel && sel && i->isSelectable() || !sel ) {
4620 i->s = sel;
4621 changed = TRUE;
4622 updateItem( i );
4623 }
4624 }
4625 if ( i == to )
4626 break;
4627 }
4628 if ( changed ) {
4629 emit selectionChanged();
4630#if defined(QT_ACCESSIBILITY_SUPPORT)
4631 QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection );
4632#endif
4633 }
4634}
4635
4636/*! \reimp */
4637void QListBox::windowActivationChange( bool oldActive )
4638{
4639 if ( oldActive && d->scrollTimer )
4640 d->scrollTimer->stop();
4641 if ( palette().active() != palette().inactive() )
4642 viewport()->update();
4643 QScrollView::windowActivationChange( oldActive );
4644}
4645
4646int QListBoxItem::RTTI = 0;
4647
4648/*!
4649 Returns 0.
4650
4651 Make your derived classes return their own values for rtti(), and
4652 you can distinguish between listbox items. You should use values
4653 greater than 1000 preferably a large random number, to allow for
4654 extensions to this class.
4655*/
4656
4657int QListBoxItem::rtti() const
4658{
4659 return RTTI;
4660}
4661
4662#endif // QT_NO_LISTBOX
Note: See TracBrowser for help on using the repository browser.