source: trunk/src/qt3support/itemviews/q3listbox.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

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