source: trunk/src/widgets/qcombobox.cpp@ 174

Last change on this file since 174 was 174, checked in by dmik, 18 years ago

Styles: Implemented the first version of the Warp4 style (contributed by Cornelis Bockemuehl).

  • Property svn:keywords set to Id
File size: 59.7 KB
Line 
1/**********************************************************************
2** $Id: qcombobox.cpp 174 2007-11-06 22:27:57Z dmik $
3**
4** Implementation of QComboBox widget class
5**
6** Created : 940426
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the widgets module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qcombobox.h"
39#ifndef QT_NO_COMBOBOX
40#include "qpopupmenu.h"
41#include "qlistbox.h"
42#include "qpainter.h"
43#include "qdrawutil.h"
44#include "qstrlist.h"
45#include "qpixmap.h"
46#include "qtimer.h"
47#include "qapplication.h"
48#include "qlineedit.h"
49#include "qbitmap.h"
50#include "qeffects_p.h"
51#include "qstringlist.h"
52#include "qcombobox.h"
53#include "qstyle.h"
54#include <limits.h>
55#if defined(QT_ACCESSIBILITY_SUPPORT)
56#include "qaccessible.h"
57#endif
58
59/*!
60 \class QComboBox qcombobox.h
61 \brief The QComboBox widget is a combined button and popup list.
62
63 \ingroup basic
64 \mainclass
65
66 A combobox is a selection widget which displays the current item
67 and can pop up a list of items. A combobox may be editable in
68 which case the user can enter arbitrary strings.
69
70 Comboboxes provide a means of showing the user's current choice
71 out of a list of options in a way that takes up the minimum amount
72 of screen space.
73
74 QComboBox supports three different display styles: Aqua/Motif 1.x,
75 Motif 2.0 and Windows. In Motif 1.x, a combobox was called
76 XmOptionMenu. In Motif 2.0, OSF introduced an improved combobox
77 and named that XmComboBox. QComboBox provides both.
78
79 QComboBox provides two different constructors. The simplest
80 constructor creates an "old-style" combobox in Motif (or Aqua)
81 style:
82 \code
83 QComboBox *c = new QComboBox( this, "read-only combobox" );
84 \endcode
85
86 The other constructor creates a new-style combobox in Motif style,
87 and can create both read-only and editable comboboxes:
88 \code
89 QComboBox *c1 = new QComboBox( FALSE, this, "read-only combobox" );
90 QComboBox *c2 = new QComboBox( TRUE, this, "editable combobox" );
91 \endcode
92
93 New-style comboboxes use a list box in both Motif and Windows
94 styles, and both the content size and the on-screen size of the
95 list box can be limited with sizeLimit() and setMaxCount()
96 respectively. Old-style comboboxes use a popup in Aqua and Motif
97 style, and that popup will happily grow larger than the desktop if
98 you put enough data into it.
99
100 The two constructors create identical-looking comboboxes in
101 Windows style.
102
103 Comboboxes can contain pixmaps as well as strings; the
104 insertItem() and changeItem() functions are suitably overloaded.
105 For editable comboboxes, the function clearEdit() is provided,
106 to clear the displayed string without changing the combobox's
107 contents.
108
109 A combobox emits two signals, activated() and highlighted(), when
110 a new item has been activated (selected) or highlighted (made
111 current). Both signals exist in two versions, one with a \c
112 QString argument and one with an \c int argument. If the user
113 highlights or activates a pixmap, only the \c int signals are
114 emitted. Whenever the text of an editable combobox is changed the
115 textChanged() signal is emitted.
116
117 When the user enters a new string in an editable combobox, the
118 widget may or may not insert it, and it can insert it in several
119 locations. The default policy is is \c AtBottom but you can change
120 this using setInsertionPolicy().
121
122 It is possible to constrain the input to an editable combobox
123 using QValidator; see setValidator(). By default, any input is
124 accepted.
125
126 If the combobox is not editable then it has a default
127 focusPolicy() of \c TabFocus, i.e. it will not grab focus if
128 clicked. This differs from both Windows and Motif. If the combobox
129 is editable then it has a default focusPolicy() of \c StrongFocus,
130 i.e. it will grab focus if clicked.
131
132 A combobox can be populated using the insert functions,
133 insertStringList() and insertItem() for example. Items can be
134 changed with changeItem(). An item can be removed with
135 removeItem() and all items can be removed with clear(). The text
136 of the current item is returned by currentText(), and the text of
137 a numbered item is returned with text(). The current item can be
138 set with setCurrentItem() or setCurrentText(). The number of items
139 in the combobox is returned by count(); the maximum number of
140 items can be set with setMaxCount(). You can allow editing using
141 setEditable(). For editable comboboxes you can set auto-completion
142 using setAutoCompletion() and whether or not the user can add
143 duplicates is set with setDuplicatesEnabled().
144
145 <img src="qcombo1-m.png">(Motif 1, read-only)<br clear=all>
146 <img src="qcombo2-m.png">(Motif 2, editable)<br clear=all>
147 <img src="qcombo3-m.png">(Motif 2, read-only)<br clear=all>
148 <img src="qcombo1-w.png">(Windows style)
149
150 \sa QLineEdit QListBox QSpinBox QRadioButton QButtonGroup
151 \link guibooks.html#fowler GUI Design Handbook: Combo Box,\endlink
152 \link guibooks.html#fowler GUI Design Handbook: Drop-Down List Box.\endlink
153*/
154
155
156/*!
157 \enum QComboBox::Policy
158
159 This enum specifies what the QComboBox should do when a new string
160 is entered by the user.
161
162 \value NoInsertion the string will not be inserted into the
163 combobox.
164
165 \value AtTop insert the string as the first item in the combobox.
166
167 \value AtCurrent replace the previously selected item with the
168 string the user has entered.
169
170 \value AtBottom insert the string as the last item in the
171 combobox.
172
173 \value AfterCurrent insert the string after the previously
174 selected item.
175
176 \value BeforeCurrent insert the string before the previously
177 selected item.
178
179 activated() is always emitted when the string is entered.
180
181 If inserting the new string would cause the combobox to breach its
182 content size limit, the item at the other end of the list is
183 deleted. The definition of "other end" is
184 implementation-dependent.
185*/
186
187
188/*!
189 \fn void QComboBox::activated( int index )
190
191 This signal is emitted when a new item has been activated
192 (selected). The \a index is the position of the item in the
193 combobox.
194
195 This signal is not emitted if the item is changed
196 programmatically, e.g. using setCurrentItem().
197*/
198
199/*!
200 \overload void QComboBox::activated( const QString &string )
201
202 This signal is emitted when a new item has been activated
203 (selected). \a string is the selected string.
204
205 You can also use the activated(int) signal, but be aware that its
206 argument is meaningful only for selected strings, not for user
207 entered strings.
208*/
209
210/*!
211 \fn void QComboBox::highlighted( int index )
212
213 This signal is emitted when a new item has been set to be the
214 current item. The \a index is the position of the item in the
215 combobox.
216
217 This signal is not emitted if the item is changed
218 programmatically, e.g. using setCurrentItem().
219*/
220
221/*!
222 \overload void QComboBox::highlighted( const QString &string )
223
224 This signal is emitted when a new item has been set to be the
225 current item. \a string is the item's text.
226
227 You can also use the highlighted(int) signal.
228*/
229
230/*!
231 \fn void QComboBox::textChanged( const QString &string )
232
233 This signal is used for editable comboboxes. It is emitted
234 whenever the contents of the text entry field changes. \a string
235 contains the new text.
236*/
237
238/*!
239 \property QComboBox::autoCompletion
240 \brief whether auto-completion is enabled
241
242 This property can only be set for editable comboboxes, for
243 non-editable comboboxes it has no effect. It is FALSE by default.
244*/
245
246/*!
247 \property QComboBox::autoMask
248 \brief whether the combobox is automatically masked
249
250 \sa QWidget::setAutoMask()
251*/
252
253/*! \property QComboBox::autoResize
254 \brief whether auto resize is enabled
255 \obsolete
256
257 If this property is set to TRUE then the combobox will resize itself
258 whenever its contents change. The default is FALSE.
259*/
260
261/*!
262 \property QComboBox::count
263 \brief the number of items in the combobox
264*/
265
266/*!
267 \property QComboBox::currentItem
268 \brief the index of the current item in the combobox
269
270 Note that the activated() and highlighted() signals are only
271 emitted when the user changes the current item, not when it is
272 changed programmatically.
273*/
274
275/*!
276 \property QComboBox::currentText
277 \brief the text of the combobox's current item
278*/
279
280/*!
281 \property QComboBox::duplicatesEnabled
282 \brief whether duplicates are allowed
283
284 If the combobox is editable and the user enters some text in the
285 combobox's lineedit and presses Enter (and the insertionPolicy()
286 is not \c NoInsertion), then what happens is this:
287 \list
288 \i If the text is not already in the list, the text is inserted.
289 \i If the text is in the list and this property is TRUE (the
290 default), the text is inserted.
291 \i If the text is in the list and this property is FALSE, the text
292 is \e not inserted; instead the item which has matching text becomes
293 the current item.
294 \endlist
295
296 This property only affects user-interaction. You can use
297 insertItem() to insert duplicates if you wish regardless of this
298 setting.
299*/
300
301/*!
302 \property QComboBox::editable
303 \brief whether the combobox is editable
304
305 This property's default is FALSE. Note that the combobox will be
306 cleared if this property is set to TRUE for a 1.x Motif style
307 combobox. To avoid this, use setEditable() before inserting any
308 items. Also note that the 1.x version of Motif didn't have any
309 editable comboboxes, so the combobox will change it's appearance
310 to a 2.0 style Motif combobox is it is set to be editable.
311*/
312
313/*!
314 \property QComboBox::insertionPolicy
315 \brief the position of the items inserted by the user
316
317 The default insertion policy is \c AtBottom. See \l Policy.
318*/
319
320/*!
321 \property QComboBox::maxCount
322 \brief the maximum number of items allowed in the combobox
323*/
324
325/*!
326 \property QComboBox::sizeLimit
327 \brief the maximum on-screen size of the combobox.
328
329 This is disregarded in Motif 1.x style. The default limit is ten
330 lines. If the number of items in the combobox is or grows larger
331 than lines, a scrollbar is added.
332*/
333
334class QComboBoxPopup : public QPopupMenu
335{
336public:
337 QComboBoxPopup( QWidget *parent=0, const char *name=0 )
338 : QPopupMenu( parent, name )
339 {
340 }
341
342 int itemHeight( int index )
343 {
344 return QPopupMenu::itemHeight( index );
345 }
346
347};
348
349class QComboBoxPopupItem : public QCustomMenuItem
350{
351 QListBoxItem *li;
352 QSize sc; // Size cache optimization
353public:
354 QComboBoxPopupItem(QListBoxItem *i) : QCustomMenuItem(), li(i), sc(0, 0) { }
355 virtual bool fullSpan() const { return TRUE; }
356 virtual void paint( QPainter*, const QColorGroup&, bool, bool, int, int, int, int);
357 virtual QSize sizeHint() { if (sc.isNull()) sc = QSize(li->width(li->listBox()), QMAX(25, li->height(li->listBox()))); return sc; }
358};
359void QComboBoxPopupItem::paint( QPainter* p, const QColorGroup&, bool,
360 bool, int x, int y, int, int)
361{
362 p->save();
363 p->translate(x, y + ((sizeHint().height() / 2) - (li->height(li->listBox()) / 2)));
364 li->paint(p);
365 p->restore();
366}
367
368
369class QComboBoxData
370{
371public:
372 QComboBoxData( QComboBox *cb ): ed( 0 ), usingLBox( FALSE ), pop( 0 ), lBox( 0 ), combo( cb )
373 {
374 duplicatesEnabled = TRUE;
375 cb->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
376 }
377
378 inline bool usingListBox() { return usingLBox; }
379 inline QListBox * listBox() { return lBox; }
380 inline QComboBoxPopup * popup() { return pop; }
381 void updateLinedGeometry();
382
383 void setListBox( QListBox *l ) { lBox = l ; usingLBox = TRUE;
384 l->setMouseTracking( TRUE );}
385
386 void setPopupMenu( QComboBoxPopup * pm, bool isPopup=TRUE )
387 { pop = pm; if(isPopup) usingLBox = FALSE; }
388
389 int current;
390 int maxCount;
391 int sizeLimit;
392 QComboBox::Policy p;
393 bool autoresize;
394 bool poppedUp;
395 bool mouseWasInsidePopup;
396 bool arrowPressed;
397 bool arrowDown;
398 bool discardNextMousePress;
399 bool shortClick;
400 bool useCompletion;
401 bool completeNow;
402 int completeAt;
403 bool duplicatesEnabled;
404 int fullHeight, currHeight;
405
406 QLineEdit * ed; // /bin/ed rules!
407 QTimer *completionTimer;
408
409 QSize sizeHint;
410
411private:
412 bool usingLBox;
413 QComboBoxPopup *pop;
414 QListBox *lBox;
415 QComboBox *combo;
416
417};
418
419void QComboBoxData::updateLinedGeometry()
420{
421 if ( !ed || !combo )
422 return;
423 QRect r = QStyle::visualRect( combo->style().querySubControlMetrics(QStyle::CC_ComboBox, combo,
424 QStyle::SC_ComboBoxEditField), combo );
425
426 const QPixmap *pix = current < combo->count() ? combo->pixmap( current ) : 0;
427 if ( pix && pix->width() < r.width() )
428 r.setLeft( r.left() + pix->width() + 4 );
429 if ( r != ed->geometry() )
430 ed->setGeometry( r );
431}
432
433static inline bool checkInsertIndex( const char *method, const char * name,
434 int count, int *index)
435{
436 bool range_err = (*index > count);
437#if defined(QT_CHECK_RANGE)
438 if ( range_err )
439 qWarning( "QComboBox::%s: (%s) Index %d out of range",
440 method, name ? name : "<no name>", *index );
441#else
442 Q_UNUSED( method )
443 Q_UNUSED( name )
444#endif
445 if ( *index < 0 ) // append
446 *index = count;
447 return !range_err;
448}
449
450
451static inline bool checkIndex( const char *method, const char * name,
452 int count, int index )
453{
454 bool range_err = (index >= count);
455#if defined(QT_CHECK_RANGE)
456 if ( range_err )
457 qWarning( "QComboBox::%s: (%s) Index %i out of range",
458 method, name ? name : "<no name>", index );
459#else
460 Q_UNUSED( method )
461 Q_UNUSED( name )
462#endif
463 return !range_err;
464}
465
466
467
468/*!
469 Constructs a combobox widget with parent \a parent called \a name.
470
471 This constructor creates a popup list if the program uses Motif
472 (or Aqua) look and feel; this is compatible with Motif 1.x and
473 Aqua.
474
475 Note: If you use this constructor to create your QComboBox, then
476 the pixmap() function will always return 0. To workaround this,
477 use the other constructor.
478
479*/
480
481
482
483QComboBox::QComboBox( QWidget *parent, const char *name )
484 : QWidget( parent, name, WNoAutoErase )
485{
486 d = new QComboBoxData( this );
487 if ( style().styleHint(QStyle::SH_ComboBox_Popup, this) ||
488 style().styleHint(QStyle::SH_GUIStyle) == Qt::MotifStyle ) {
489 d->setPopupMenu( new QComboBoxPopup( this, "in-combo" ) );
490 d->popup()->setFont( font() );
491 connect( d->popup(), SIGNAL(activated(int)),
492 SLOT(internalActivate(int)) );
493 connect( d->popup(), SIGNAL(highlighted(int)),
494 SLOT(internalHighlight(int)) );
495 } else {
496 setUpListBox();
497 }
498 d->ed = 0;
499 d->current = 0;
500 d->maxCount = INT_MAX;
501 d->sizeLimit = 10;
502 d->p = AtBottom;
503 d->autoresize = FALSE;
504 d->poppedUp = FALSE;
505 d->arrowDown = FALSE;
506 d->arrowPressed = FALSE;
507 d->discardNextMousePress = FALSE;
508 d->shortClick = FALSE;
509 d->useCompletion = FALSE;
510 d->completeAt = 0;
511 d->completeNow = FALSE;
512 d->completionTimer = new QTimer( this );
513
514 setFocusPolicy( TabFocus );
515 setBackgroundMode( PaletteButton );
516}
517
518
519/*!
520 Constructs a combobox with a maximum size and either Motif 2.0 or
521 Windows look and feel.
522
523 The input field can be edited if \a rw is TRUE, otherwise the user
524 may only choose one of the items in the combobox.
525
526 The \a parent and \a name arguments are passed on to the QWidget
527 constructor.
528*/
529
530
531QComboBox::QComboBox( bool rw, QWidget *parent, const char *name )
532 : QWidget( parent, name, WNoAutoErase )
533{
534 d = new QComboBoxData( this );
535 setUpListBox();
536
537 if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this))
538 d->popup()->setItemChecked(d->current, FALSE);
539 d->current = 0;
540 d->maxCount = INT_MAX;
541 setSizeLimit(10);
542 d->p = AtBottom;
543 d->autoresize = FALSE;
544 d->poppedUp = FALSE;
545 d->arrowDown = FALSE;
546 d->discardNextMousePress = FALSE;
547 d->shortClick = FALSE;
548 d->useCompletion = FALSE;
549 d->completeAt = 0;
550 d->completeNow = FALSE;
551 d->completionTimer = new QTimer( this );
552
553 setFocusPolicy( StrongFocus );
554
555 d->ed = 0;
556 if ( rw )
557 setUpLineEdit();
558 setBackgroundMode( PaletteButton, PaletteBase );
559}
560
561
562
563/*!
564 Destroys the combobox.
565*/
566
567QComboBox::~QComboBox()
568{
569 delete d;
570}
571
572void QComboBox::setDuplicatesEnabled( bool enable )
573{
574 d->duplicatesEnabled = enable;
575}
576
577bool QComboBox::duplicatesEnabled() const
578{
579 return d->duplicatesEnabled;
580}
581
582int QComboBox::count() const
583{
584 if ( d->usingListBox() )
585 return d->listBox()->count();
586 else
587 return d->popup()->count();
588}
589
590
591/*!
592 \overload
593
594 Inserts the \a list of strings at position \a index in the
595 combobox.
596
597 This is only for compatibility since it does not support Unicode
598 strings. See insertStringList().
599*/
600
601void QComboBox::insertStrList( const QStrList &list, int index )
602{
603 insertStrList( &list, index );
604}
605
606/*!
607 \overload
608
609 Inserts the \a list of strings at position \a index in the
610 combobox.
611
612 This is only for compatibility since it does not support Unicode
613 strings. See insertStringList().
614*/
615
616void QComboBox::insertStrList( const QStrList *list, int index )
617{
618 if ( !list ) {
619#if defined(QT_CHECK_NULL)
620 Q_ASSERT( list != 0 );
621#endif
622 return;
623 }
624 QStrListIterator it( *list );
625 const char* tmp;
626 if ( index < 0 )
627 index = count();
628 while ( (tmp=it.current()) ) {
629 ++it;
630 if ( d->usingListBox() )
631 d->listBox()->insertItem( QString::fromLatin1(tmp), index );
632 else
633 d->popup()->insertItem( QString::fromLatin1(tmp), index, index );
634 if ( index++ == d->current && d->current < count() ) {
635 if ( d->ed ) {
636 d->ed->setText( text( d->current ) );
637 d->updateLinedGeometry();
638 } else
639 update();
640 currentChanged();
641 }
642 }
643 if ( index != count() )
644 reIndex();
645}
646
647/*!
648 Inserts the \a list of strings at position \a index in the
649 combobox.
650*/
651
652void QComboBox::insertStringList( const QStringList &list, int index )
653{
654 QStringList::ConstIterator it = list.begin();
655 if ( index < 0 )
656 index = count();
657 while ( it != list.end() ) {
658 if ( d->usingListBox() )
659 d->listBox()->insertItem( *it, index );
660 else
661 d->popup()->insertItem( *it, index, index );
662 if ( index++ == d->current && d->current < count() ) {
663 if ( d->ed ) {
664 d->ed->setText( text( d->current ) );
665 d->updateLinedGeometry();
666 } else
667 update();
668 currentChanged();
669 }
670 ++it;
671 }
672 if ( index != count() )
673 reIndex();
674}
675
676/*!
677 Inserts the array of char * \a strings at position \a index in the
678 combobox.
679
680 The \a numStrings argument is the number of strings. If \a
681 numStrings is -1 (default), the \a strings array must be
682 terminated with 0.
683
684 Example:
685 \code
686 static const char* items[] = { "red", "green", "blue", 0 };
687 combo->insertStrList( items );
688 \endcode
689
690 \sa insertStringList()
691*/
692
693void QComboBox::insertStrList( const char **strings, int numStrings, int index)
694{
695 if ( !strings ) {
696#if defined(QT_CHECK_NULL)
697 Q_ASSERT( strings != 0 );
698#endif
699 return;
700 }
701 if ( index < 0 )
702 index = count();
703 int i = 0;
704 while ( (numStrings<0 && strings[i]!=0) || i<numStrings ) {
705 if ( d->usingListBox() )
706 d->listBox()->insertItem( QString::fromLatin1(strings[i]), index );
707 else
708 d->popup()->insertItem( QString::fromLatin1(strings[i]), index, index );
709 i++;
710 if ( index++ == d->current && d->current < count() ) {
711 if ( d->ed ) {
712 d->ed->setText( text( d->current ) );
713 d->updateLinedGeometry();
714 } else
715 update();
716 currentChanged();
717 }
718 }
719 if ( index != count() )
720 reIndex();
721}
722
723
724/*!
725 Inserts a text item with text \a t, at position \a index. The item
726 will be appended if \a index is negative.
727*/
728
729void QComboBox::insertItem( const QString &t, int index )
730{
731 int cnt = count();
732 if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) )
733 return;
734 if ( d->usingListBox() )
735 d->listBox()->insertItem( t, index );
736 else
737 d->popup()->insertItem( t, index, index );
738 if ( index != cnt )
739 reIndex();
740 if ( index == d->current && d->current < count() ) {
741 if ( d->ed ) {
742 d->ed->setText( text( d->current ) );
743 d->updateLinedGeometry();
744 } else
745 update();
746 }
747 if ( index == d->current )
748 currentChanged();
749}
750
751/*!
752 \overload
753
754 Inserts a \a pixmap item at position \a index. The item will be
755 appended if \a index is negative.
756*/
757
758void QComboBox::insertItem( const QPixmap &pixmap, int index )
759{
760 int cnt = count();
761 if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) )
762 return;
763 if ( d->usingListBox() )
764 d->listBox()->insertItem( pixmap, index );
765 else
766 d->popup()->insertItem( pixmap, index, index );
767 if ( index != cnt )
768 reIndex();
769 if ( index == d->current && d->current < count() ) {
770 if ( d->ed ) {
771 d->ed->setText( text( d->current ) );
772 d->updateLinedGeometry();
773 } else
774 update();
775 }
776 if ( index == d->current )
777 currentChanged();
778}
779
780/*!
781 \overload
782
783 Inserts a \a pixmap item with additional text \a text at position
784 \a index. The item will be appended if \a index is negative.
785*/
786
787void QComboBox::insertItem( const QPixmap &pixmap, const QString& text, int index )
788{
789 int cnt = count();
790 if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) )
791 return;
792 if ( d->usingListBox() )
793 d->listBox()->insertItem( pixmap, text, index );
794 else
795 d->popup()->insertItem( pixmap, text, index, index );
796 if ( index != cnt )
797 reIndex();
798 if ( index == d->current && d->current < count() ) {
799 if ( d->ed ) {
800 d->ed->setText( this->text( d->current ) );
801 d->updateLinedGeometry();
802 } else
803 update();
804 }
805 if ( index == d->current )
806 currentChanged();
807}
808
809
810/*!
811 Removes the item at position \a index.
812*/
813
814void QComboBox::removeItem( int index )
815{
816 int cnt = count();
817 if ( !checkIndex( "removeItem", name(), cnt, index ) )
818 return;
819 if ( d->usingListBox() ) {
820 if ( style().styleHint(QStyle::SH_ComboBox_Popup, this) && d->popup() )
821 d->popup()->removeItemAt( index );
822 d->listBox()->removeItem( index );
823 } else {
824 d->popup()->removeItemAt( index );
825 }
826 if ( index != cnt-1 )
827 reIndex();
828 if ( index == d->current ) {
829 if ( d->ed ) {
830 QString s = QString::fromLatin1("");
831 if (d->current < cnt - 1)
832 s = text( d->current );
833 d->ed->setText( s );
834 d->updateLinedGeometry();
835 }
836 else {
837 if ( d->usingListBox() ) {
838 d->current = d->listBox()->currentItem();
839 } else {
840 if (d->current > count()-1 && d->current > 0)
841 d->current--;
842 }
843 update();
844 }
845 currentChanged();
846 }
847 else {
848 if ( !d->ed ) {
849 if (d->current < cnt - 1)
850 setCurrentItem( d->current );
851 else
852 setCurrentItem( d->current - 1 );
853 }
854 }
855
856}
857
858
859/*!
860 Removes all combobox items.
861*/
862
863void QComboBox::clear()
864{
865 if ( d->usingListBox() ) {
866 if ( style().styleHint(QStyle::SH_ComboBox_Popup, this) && d->popup() )
867 d->popup()->clear();
868 d->listBox()->resize( 0, 0 );
869 d->listBox()->clear();
870 } else {
871 d->popup()->clear();
872 }
873
874 if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this))
875 d->popup()->setItemChecked(d->current, FALSE);
876 d->current = 0;
877 if ( d->ed ) {
878 d->ed->setText( QString::fromLatin1("") );
879 d->updateLinedGeometry();
880 }
881 currentChanged();
882}
883
884
885QString QComboBox::currentText() const
886{
887 if ( d->ed )
888 return d->ed->text();
889 else if ( d->current < count() )
890 return text( currentItem() );
891 else
892 return QString::null;
893}
894
895void QComboBox::setCurrentText( const QString& txt )
896{
897 int i;
898 for ( i = 0; i < count(); i++)
899 if ( text( i ) == txt )
900 break;
901 if ( i < count() )
902 setCurrentItem( i );
903 else if ( d->ed )
904 d->ed->setText( txt );
905 else
906 changeItem( txt, currentItem() );
907}
908
909
910/*!
911 Returns the text item at position \a index, or QString::null if
912 the item is not a string.
913
914 \sa currentText()
915*/
916
917QString QComboBox::text( int index ) const
918{
919 if ( !checkIndex( "text", name(), count(), index ) )
920 return QString::null;
921 if ( d->usingListBox() )
922 return d->listBox()->text( index );
923 else
924 return d->popup()->text( index );
925}
926
927/*!
928 Returns the pixmap item at position \a index, or 0 if the item is
929 not a pixmap.
930*/
931
932const QPixmap *QComboBox::pixmap( int index ) const
933{
934 if ( !checkIndex( "pixmap", name(), count(), index ) )
935 return 0;
936 if ( d->usingListBox() )
937 return d->listBox()->pixmap( index );
938 else
939 return d->popup()->pixmap( index );
940}
941
942/*!
943 Replaces the item at position \a index with the text \a t.
944*/
945
946void QComboBox::changeItem( const QString &t, int index )
947{
948 if ( !checkIndex( "changeItem", name(), count(), index ) )
949 return;
950 if ( d->usingListBox() )
951 d->listBox()->changeItem( t, index );
952 else
953 d->popup()->changeItem( t, index );
954 if ( index == d->current ) {
955 if ( d->ed ) {
956 d->ed->setText( text( d->current ) );
957 d->updateLinedGeometry();
958 } else
959 update();
960 }
961}
962
963/*!
964 \overload
965
966 Replaces the item at position \a index with the pixmap \a im,
967 unless the combobox is editable.
968
969 \sa insertItem()
970*/
971
972void QComboBox::changeItem( const QPixmap &im, int index )
973{
974 if ( !checkIndex( "changeItem", name(), count(), index ) )
975 return;
976 if ( d->usingListBox() )
977 d->listBox()->changeItem( im, index );
978 else
979 d->popup()->changeItem( im, index );
980 if ( index == d->current )
981 update();
982}
983
984/*!
985 \overload
986
987 Replaces the item at position \a index with the pixmap \a im and
988 the text \a t.
989
990 \sa insertItem()
991*/
992
993void QComboBox::changeItem( const QPixmap &im, const QString &t, int index )
994{
995 if ( !checkIndex( "changeItem", name(), count(), index ) )
996 return;
997 if ( d->usingListBox() )
998 d->listBox()->changeItem( im, t, index );
999 else
1000 d->popup()->changeItem( im, t, index );
1001 if ( index == d->current )
1002 update();
1003}
1004
1005
1006int QComboBox::currentItem() const
1007{
1008 return d->current;
1009}
1010
1011void QComboBox::setCurrentItem( int index )
1012{
1013 if ( index == d->current && !d->ed ) {
1014 return;
1015 }
1016 if ( !checkIndex( "setCurrentItem", name(), count(), index ) ) {
1017 return;
1018 }
1019
1020 if ( d->usingListBox() && !( listBox()->item(index) && listBox()->item(index)->isSelectable() ) )
1021 return;
1022
1023 if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this))
1024 d->popup()->setItemChecked(d->current, FALSE);
1025 d->current = index;
1026 d->completeAt = 0;
1027 if ( d->ed ) {
1028 d->ed->setText( text( index ) );
1029 d->updateLinedGeometry();
1030 }
1031 // ### We want to keep ListBox's currentItem in sync, even if NOT popuped...
1032 if ( d->usingListBox() && d->listBox() ) {
1033 d->listBox()->setCurrentItem( index );
1034 } else {
1035 internalHighlight( index );
1036 // internalActivate( index ); ### this leads to weird behavior, as in 3.0.1
1037 }
1038
1039 currentChanged();
1040}
1041
1042bool QComboBox::autoResize() const
1043{
1044 return d->autoresize;
1045}
1046
1047void QComboBox::setAutoResize( bool enable )
1048{
1049 if ( (bool)d->autoresize != enable ) {
1050 d->autoresize = enable;
1051 if ( enable )
1052 adjustSize();
1053 }
1054}
1055
1056
1057/*!
1058 \reimp
1059
1060 This implementation caches the size hint to avoid resizing when
1061 the contents change dynamically. To invalidate the cached value
1062 call setFont().
1063*/
1064QSize QComboBox::sizeHint() const
1065{
1066 if ( isVisible() && d->sizeHint.isValid() )
1067 return d->sizeHint;
1068
1069 constPolish();
1070 int i, w;
1071 QFontMetrics fm = fontMetrics();
1072
1073 int maxW = count() ? 18 : 7 * fm.width(QChar('x')) + 18;
1074 int maxH = QMAX( fm.lineSpacing(), 14 ) + 2;
1075
1076 if ( !d->usingListBox() ) {
1077 w = d->popup()->sizeHint().width() - 2* d->popup()->frameWidth();
1078 if ( w > maxW )
1079 maxW = w;
1080 } else {
1081 for( i = 0; i < count(); i++ ) {
1082 w = d->listBox()->item( i )->width( d->listBox() );
1083 if ( w > maxW )
1084 maxW = w;
1085 }
1086 }
1087
1088 d->sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, this,
1089 QSize(maxW, maxH)).
1090 expandedTo(QApplication::globalStrut()));
1091
1092 return d->sizeHint;
1093}
1094
1095
1096/*!
1097 \internal
1098 Receives activated signals from an internal popup list and emits
1099 the activated() signal.
1100*/
1101
1102void QComboBox::internalActivate( int index )
1103{
1104 if ( d->current != index ) {
1105 if ( !d->usingListBox() || listBox()->item( index )->isSelectable() ) {
1106 if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this))
1107 d->popup()->setItemChecked(d->current, FALSE);
1108 d->current = index;
1109 currentChanged();
1110 }
1111 }
1112 if ( d->usingListBox() )
1113 popDownListBox();
1114 else
1115 d->popup()->removeEventFilter( this );
1116 d->poppedUp = FALSE;
1117
1118 QString t( text( index ) );
1119 if ( d->ed ) {
1120 d->ed->setText( t );
1121 d->updateLinedGeometry();
1122 }
1123 emit activated( index );
1124 emit activated( t );
1125}
1126
1127/*!
1128 \internal
1129 Receives highlighted signals from an internal popup list and emits
1130 the highlighted() signal.
1131*/
1132
1133void QComboBox::internalHighlight( int index )
1134{
1135 emit highlighted( index );
1136 QString t = text( index );
1137 if ( !t.isNull() )
1138 emit highlighted( t );
1139}
1140
1141/*!
1142 \internal
1143 Receives timeouts after a click. Used to decide if a Motif style
1144 popup should stay up or not after a click.
1145*/
1146void QComboBox::internalClickTimeout()
1147{
1148 d->shortClick = FALSE;
1149}
1150
1151/*!
1152 Sets the palette for both the combobox button and the combobox
1153 popup list to \a palette.
1154*/
1155
1156void QComboBox::setPalette( const QPalette &palette )
1157{
1158 QWidget::setPalette( palette );
1159 if ( d->listBox() )
1160 d->listBox()->setPalette( palette );
1161 if ( d->popup() )
1162 d->popup()->setPalette( palette );
1163}
1164
1165/*!
1166 Sets the font for both the combobox button and the combobox popup
1167 list to \a font.
1168*/
1169
1170void QComboBox::setFont( const QFont &font )
1171{
1172 d->sizeHint = QSize(); // invalidate size hint
1173 QWidget::setFont( font );
1174 if ( d->usingListBox() )
1175 d->listBox()->setFont( font );
1176 else
1177 d->popup()->setFont( font );
1178 if (d->ed)
1179 d->ed->setFont( font );
1180 if ( d->autoresize )
1181 adjustSize();
1182}
1183
1184
1185/*!\reimp
1186*/
1187
1188void QComboBox::resizeEvent( QResizeEvent * e )
1189{
1190 if ( d->ed )
1191 d->updateLinedGeometry();
1192 if ( d->listBox() )
1193 d->listBox()->resize( width(), d->listBox()->height() );
1194 QWidget::resizeEvent( e );
1195}
1196
1197/*!\reimp
1198*/
1199
1200void QComboBox::paintEvent( QPaintEvent * )
1201{
1202 QPainter p( this );
1203 const QColorGroup & g = colorGroup();
1204 p.setPen(g.text());
1205
1206 QStyle::SFlags flags = QStyle::Style_Default;
1207 if (isEnabled())
1208 flags |= QStyle::Style_Enabled;
1209 if (hasFocus())
1210 flags |= QStyle::Style_HasFocus;
1211
1212 if ( width() < 5 || height() < 5 ) {
1213 qDrawShadePanel( &p, rect(), g, FALSE, 2,
1214 &g.brush( QColorGroup::Button ) );
1215 return;
1216 }
1217
1218 bool reverse = QApplication::reverseLayout();
1219 if ( !d->usingListBox() &&
1220 style().styleHint(QStyle::SH_GUIStyle) == Qt::MotifStyle) { // motif 1.x style
1221 int dist, buttonH, buttonW;
1222 dist = 8;
1223 buttonH = 7;
1224 buttonW = 11;
1225 int xPos;
1226 int x0;
1227 int w = width() - dist - buttonW - 1;
1228 if ( reverse ) {
1229 xPos = dist + 1;
1230 x0 = xPos + 4;
1231 } else {
1232 xPos = w;
1233 x0 = 4;
1234 }
1235 qDrawShadePanel( &p, rect(), g, FALSE,
1236 style().pixelMetric(QStyle::PM_DefaultFrameWidth, this),
1237 &g.brush( QColorGroup::Button ) );
1238 qDrawShadePanel( &p, xPos, (height() - buttonH)/2,
1239 buttonW, buttonH, g, FALSE,
1240 style().pixelMetric(QStyle::PM_DefaultFrameWidth, this) );
1241 QRect clip( x0, 2, w - 2 - 4 - 5, height() - 4 );
1242 QString str = d->popup()->text( this->d->current );
1243 if ( !str.isNull() ) {
1244 p.drawText( clip, AlignCenter | SingleLine, str );
1245 }
1246
1247 QPixmap *pix = d->popup()->pixmap( this->d->current );
1248 QIconSet *iconSet = d->popup()->iconSet( this->d->current );
1249 if (pix || iconSet) {
1250 QPixmap pm = ( pix ? *pix : iconSet->pixmap() );
1251 p.setClipRect( clip );
1252 p.drawPixmap( 4, (height()-pm.height())/2, pm );
1253 p.setClipping( FALSE );
1254 }
1255
1256 if ( hasFocus() )
1257 p.drawRect( xPos - 5, 4, width() - xPos + 1 , height() - 8 );
1258 } else if(!d->usingListBox()) {
1259 style().drawComplexControl( QStyle::CC_ComboBox, &p, this, rect(), g,
1260 flags, (uint)QStyle::SC_All,
1261 (d->arrowDown ?
1262 QStyle::SC_ComboBoxArrow :
1263 QStyle::SC_None ));
1264
1265 QRect re = style().querySubControlMetrics( QStyle::CC_ComboBox, this,
1266 QStyle::SC_ComboBoxEditField );
1267 re = QStyle::visualRect(re, this);
1268 p.setClipRect( re );
1269
1270 QString str = d->popup()->text( this->d->current );
1271 QPixmap *pix = d->popup()->pixmap( this->d->current );
1272 if ( !str.isNull() ) {
1273 p.save();
1274 p.setFont(font());
1275 QFontMetrics fm(font());
1276 int x = re.x(), y = re.y() + fm.ascent();
1277 if( pix )
1278 x += pix->width() + 5;
1279 p.drawText( x, y, str );
1280 p.restore();
1281 }
1282 if ( pix ) {
1283 p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(),
1284 colorGroup().brush( QColorGroup::Base ) );
1285 p.drawPixmap( re.x() + 2, re.y() +
1286 ( re.height() - pix->height() ) / 2, *pix );
1287 }
1288 } else {
1289 style().drawComplexControl( QStyle::CC_ComboBox, &p, this, rect(), g,
1290 flags, (uint)QStyle::SC_All,
1291 (d->arrowDown ?
1292 QStyle::SC_ComboBoxArrow :
1293 QStyle::SC_None ));
1294
1295 QRect re = style().querySubControlMetrics( QStyle::CC_ComboBox, this,
1296 QStyle::SC_ComboBoxEditField );
1297 re = QStyle::visualRect(re, this);
1298 p.setClipRect( re );
1299
1300 if ( !d->ed ) {
1301 QListBoxItem * item = d->listBox()->item( d->current );
1302 if ( item ) {
1303 int itemh = item->height( d->listBox() );
1304 p.translate( re.x(), re.y() + (re.height() - itemh)/2 );
1305 item->paint( &p );
1306 }
1307 } else if ( d->listBox() && d->listBox()->item( d->current ) ) {
1308 QListBoxItem * item = d->listBox()->item( d->current );
1309 const QPixmap *pix = item->pixmap();
1310 if ( pix ) {
1311 p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(),
1312 colorGroup().brush( QColorGroup::Base ) );
1313 p.drawPixmap( re.x() + 2, re.y() +
1314 ( re.height() - pix->height() ) / 2, *pix );
1315 }
1316 }
1317 p.setClipping( FALSE );
1318 }
1319}
1320
1321
1322/*!\reimp
1323*/
1324
1325void QComboBox::mousePressEvent( QMouseEvent *e )
1326{
1327 if ( e->button() != LeftButton )
1328 return;
1329 if ( d->discardNextMousePress ) {
1330 d->discardNextMousePress = FALSE;
1331 return;
1332 }
1333 QRect arrowRect = style().querySubControlMetrics( QStyle::CC_ComboBox, this,
1334 QStyle::SC_ComboBoxArrow);
1335 arrowRect = QStyle::visualRect(arrowRect, this);
1336
1337 // Correction for motif style, where arrow is smaller
1338 // and thus has a rect that doesn't fit the button.
1339 arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) );
1340
1341 bool dropDown;
1342 int gs = style().styleHint(QStyle::SH_GUIStyle);
1343 if ( gs == QStyle::PMStyle )
1344 dropDown = count() && arrowRect.contains( e->pos() );
1345 else
1346 dropDown = count() && ( !editable() || arrowRect.contains( e->pos() ) );
1347
1348 if ( dropDown ) {
1349 d->arrowPressed = FALSE;
1350
1351 if ( d->usingListBox() ) {
1352 listBox()->blockSignals( TRUE );
1353 qApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll
1354 listBox()->setCurrentItem(d->current);
1355 listBox()->blockSignals( FALSE );
1356 popup();
1357 if ( arrowRect.contains( e->pos() ) ) {
1358 d->arrowPressed = TRUE;
1359 d->arrowDown = TRUE;
1360 repaint( FALSE );
1361 }
1362 } else {
1363 popup();
1364 }
1365 QTimer::singleShot( 200, this, SLOT(internalClickTimeout()));
1366 d->shortClick = TRUE;
1367 }
1368 else if ( gs == QStyle::PMStyle ) {
1369 setFocus();
1370 }
1371}
1372
1373/*!\reimp
1374*/
1375
1376void QComboBox::mouseMoveEvent( QMouseEvent * )
1377{
1378}
1379
1380/*!\reimp
1381*/
1382
1383void QComboBox::mouseReleaseEvent( QMouseEvent * )
1384{
1385}
1386
1387/*!\reimp
1388*/
1389
1390void QComboBox::mouseDoubleClickEvent( QMouseEvent *e )
1391{
1392 mousePressEvent( e );
1393}
1394
1395
1396/*!\reimp
1397*/
1398
1399void QComboBox::keyPressEvent( QKeyEvent *e )
1400{
1401 int c = currentItem();
1402 if ( ( e->key() == Key_F4 && e->state() == 0 ) ||
1403 ( e->key() == Key_Down && (e->state() & AltButton) ) ||
1404 ( !d->ed && e->key() == Key_Space ) ) {
1405 if ( count() ) {
1406 if ( !d->usingListBox() )
1407 d->popup()->setActiveItem( this->d->current );
1408 popup();
1409 }
1410 return;
1411 } else if ( d->usingListBox() && e->key() == Key_Up ) {
1412 if ( c > 0 )
1413 setCurrentItem( c-1 );
1414 } else if ( d->usingListBox() && e->key() == Key_Down ) {
1415 if ( ++c < count() )
1416 setCurrentItem( c );
1417 } else if ( d->usingListBox() && e->key() == Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) {
1418 setCurrentItem( 0 );
1419 } else if ( d->usingListBox() && e->key() == Key_End && ( !d->ed || !d->ed->hasFocus() ) ) {
1420 setCurrentItem( count()-1 );
1421 } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) {
1422 if ( !d->completionTimer->isActive() ) {
1423 d->completeAt = 0;
1424 c = completionIndex( e->text(), ++c );
1425 if ( c >= 0 ) {
1426 setCurrentItem( c );
1427 d->completeAt = e->text().length();
1428 }
1429 } else {
1430 d->completionTimer->stop();
1431 QString ct = currentText().left( d->completeAt ) + e->text();
1432 c = completionIndex( ct, c );
1433 if ( c < 0 && d->completeAt > 0 ) {
1434 c = completionIndex( e->text(), 0 );
1435 ct = e->text();
1436 }
1437 d->completeAt = 0;
1438 if ( c >= 0 ) {
1439 setCurrentItem( c );
1440 d->completeAt = ct.length();
1441 }
1442 }
1443 d->completionTimer->start( 400, TRUE );
1444 } else {
1445 e->ignore();
1446 return;
1447 }
1448
1449 c = currentItem();
1450 if ( count() && !text( c ).isNull() )
1451 emit activated( text( c ) );
1452 emit activated( c );
1453}
1454
1455
1456/*!\reimp
1457*/
1458
1459void QComboBox::focusInEvent( QFocusEvent * e )
1460{
1461 QWidget::focusInEvent( e );
1462 d->completeNow = FALSE;
1463 d->completeAt = 0;
1464}
1465
1466/*!\reimp
1467*/
1468
1469void QComboBox::focusOutEvent( QFocusEvent * e )
1470{
1471 QWidget::focusOutEvent( e );
1472 d->completeNow = FALSE;
1473 d->completeAt = 0;
1474}
1475
1476/*!\reimp
1477*/
1478#ifndef QT_NO_WHEELEVENT
1479void QComboBox::wheelEvent( QWheelEvent *e )
1480{
1481 if ( d->poppedUp ) {
1482 if ( d->usingListBox() ) {
1483 QApplication::sendEvent( d->listBox(), e );
1484 }
1485 } else {
1486 if ( e->delta() > 0 ) {
1487 int c = currentItem();
1488 if ( c > 0 ) {
1489 setCurrentItem( c-1 );
1490 emit activated( currentItem() );
1491 emit activated( currentText() );
1492 }
1493 } else {
1494 int c = currentItem();
1495 if ( ++c < count() ) {
1496 setCurrentItem( c );
1497 emit activated( currentItem() );
1498 emit activated( currentText() );
1499 }
1500 }
1501 e->accept();
1502 }
1503}
1504#endif
1505
1506/*!
1507 \internal
1508 Calculates the listbox height needed to contain all items, or as
1509 many as the list box is supposed to contain.
1510*/
1511static int listHeight( QListBox *l, int sl )
1512{
1513 if ( l->count() > 0 )
1514 return QMIN( l->count(), (uint)sl) * l->item( 0 )->height(l);
1515 else
1516 return l->sizeHint().height();
1517}
1518
1519
1520/*!
1521 Pops up the combobox popup list.
1522
1523 If the list is empty, no items appear.
1524*/
1525
1526void QComboBox::popup()
1527{
1528 if ( !count() )
1529 return;
1530
1531 if( !d->usingListBox() || style().styleHint(QStyle::SH_ComboBox_Popup, this) ) {
1532 if(d->usingListBox()) {
1533 if(!d->popup()) {
1534 QComboBoxPopup *p = new QComboBoxPopup( this, "in-combo" );
1535 d->setPopupMenu( p, FALSE );
1536 p->setFont( font() );
1537 connect( p, SIGNAL(activated(int)), SLOT(internalActivate(int)) );
1538 connect( p, SIGNAL(highlighted(int)), SLOT(internalHighlight(int)) );
1539 }
1540 d->popup()->clear();
1541 for(unsigned int i = 0; i < d->listBox()->count(); i++) {
1542 QListBoxItem *item = d->listBox()->item(i);
1543 if(item->rtti() == QListBoxText::RTTI) {
1544 d->popup()->insertItem(item->text(), i, i);
1545 } else if(item->rtti() == QListBoxPixmap::RTTI) {
1546 if(item->pixmap())
1547 d->popup()->insertItem(QIconSet(*item->pixmap()), item->text(), i, i);
1548 else
1549 d->popup()->insertItem(item->text(), i, i);
1550 } else {
1551 d->popup()->insertItem(new QComboBoxPopupItem(item), i, i);
1552 }
1553 }
1554 }
1555 d->popup()->installEventFilter( this );
1556 if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this))
1557 d->popup()->setItemChecked(this->d->current, TRUE);
1558 d->popup()->popup( mapToGlobal( QPoint(0,0) ), this->d->current );
1559 update();
1560 } else {
1561 // Send all listbox events to eventFilter():
1562 QListBox* lb = d->listBox();
1563 lb->triggerUpdate( TRUE );
1564 lb->installEventFilter( this );
1565 d->mouseWasInsidePopup = FALSE;
1566 int w = lb->variableWidth() ? lb->sizeHint().width() : width();
1567 int h = listHeight( lb, d->sizeLimit ) + 2;
1568 QRect screen = QApplication::desktop()->availableGeometry( this );
1569
1570 int sx = screen.x(); // screen pos
1571 int sy = screen.y();
1572 int sw = screen.width(); // screen width
1573 int sh = screen.height(); // screen height
1574 QPoint pos = mapToGlobal( QPoint(0,height()) );
1575 // ## Similar code is in QPopupMenu
1576 int x = pos.x();
1577 int y = pos.y();
1578
1579 // the complete widget must be visible
1580 if ( x + w > sx + sw )
1581 x = sx+sw - w;
1582 if ( x < sx )
1583 x = sx;
1584 if (y + h > sy+sh && y - h - height() >= 0 )
1585 y = y - h - height();
1586
1587 QRect rect =
1588 style().querySubControlMetrics( QStyle::CC_ComboBox, this,
1589 QStyle::SC_ComboBoxListBoxPopup,
1590 QStyleOption( x, y, w, h ) );
1591 // work around older styles that don't implement the combobox
1592 // listbox popup subcontrol
1593 if ( rect.isNull() )
1594 rect.setRect( x, y, w, h );
1595 lb->setGeometry( rect );
1596
1597 lb->raise();
1598 bool block = lb->signalsBlocked();
1599 lb->blockSignals( TRUE );
1600 QListBoxItem* currentLBItem = 0;
1601 if ( editable() && currentText() != text( currentItem() ) )
1602 currentLBItem = lb->findItem( currentText() );
1603
1604 currentLBItem = currentLBItem ? currentLBItem : lb->item( d->current );
1605
1606 lb->setCurrentItem( currentLBItem );
1607 lb->setContentsPos( lb->contentsX(),
1608 lb->viewportToContents( lb->itemRect( currentLBItem ).topLeft() ).y() );
1609
1610 // set the current item to also be the selected item if it isn't already
1611 if ( currentLBItem && currentLBItem->isSelectable() && !currentLBItem->isSelected() )
1612 lb->setSelected( currentLBItem, TRUE );
1613 lb->blockSignals( block );
1614 lb->setVScrollBarMode(QScrollView::Auto);
1615
1616#ifndef QT_NO_EFFECTS
1617 if ( QApplication::isEffectEnabled( UI_AnimateCombo ) ) {
1618 if ( lb->y() < mapToGlobal(QPoint(0,0)).y() )
1619 qScrollEffect( lb, QEffects::UpScroll );
1620 else
1621 qScrollEffect( lb );
1622 } else
1623#endif
1624 lb->show();
1625 }
1626 d->poppedUp = TRUE;
1627}
1628
1629
1630/*!
1631 \reimp
1632*/
1633void QComboBox::updateMask()
1634{
1635 QBitmap bm( size() );
1636 bm.fill( color0 );
1637
1638 {
1639 QPainter p( &bm, this );
1640 style().drawComplexControlMask(QStyle::CC_ComboBox, &p, this, rect());
1641 }
1642
1643 setMask( bm );
1644}
1645
1646/*!
1647 \internal
1648 Pops down (removes) the combobox popup list box.
1649*/
1650void QComboBox::popDownListBox()
1651{
1652 Q_ASSERT( d->usingListBox() );
1653 d->listBox()->removeEventFilter( this );
1654 d->listBox()->viewport()->removeEventFilter( this );
1655 d->listBox()->hide();
1656 d->listBox()->setCurrentItem( d->current );
1657 if ( d->arrowDown ) {
1658 d->arrowDown = FALSE;
1659 repaint( FALSE );
1660 }
1661 d->poppedUp = FALSE;
1662}
1663
1664
1665/*!
1666 \internal
1667 Re-indexes the identifiers in the popup list.
1668*/
1669
1670void QComboBox::reIndex()
1671{
1672 if ( !d->usingListBox() ) {
1673 int cnt = count();
1674 while ( cnt-- )
1675 d->popup()->setId( cnt, cnt );
1676 }
1677}
1678
1679/*!
1680 \internal
1681 Repaints the combobox.
1682*/
1683
1684void QComboBox::currentChanged()
1685{
1686 if ( d->autoresize )
1687 adjustSize();
1688 update();
1689
1690#if defined(QT_ACCESSIBILITY_SUPPORT)
1691 QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
1692#endif
1693}
1694
1695/*! \reimp
1696
1697 \internal
1698
1699 The event filter steals events from the popup or listbox when they
1700 are popped up. It makes the popup stay up after a short click in
1701 motif style. In windows style it toggles the arrow button of the
1702 combobox field, and activates an item and takes down the listbox
1703 when the mouse button is released.
1704*/
1705
1706bool QComboBox::eventFilter( QObject *object, QEvent *event )
1707{
1708 if ( !event )
1709 return TRUE;
1710 else if ( object == d->ed ) {
1711 if ( event->type() == QEvent::KeyPress ) {
1712 bool isAccepted = ( (QKeyEvent*)event )->isAccepted();
1713 keyPressEvent( (QKeyEvent *)event );
1714 if ( ((QKeyEvent *)event)->isAccepted() ) {
1715 d->completeNow = FALSE;
1716 return TRUE;
1717 } else if ( ((QKeyEvent *)event)->key() != Key_End ) {
1718 d->completeNow = TRUE;
1719 d->completeAt = d->ed->cursorPosition();
1720 }
1721 if ( isAccepted )
1722 ( (QKeyEvent*)event )->accept();
1723 else
1724 ( (QKeyEvent*)event )->ignore();
1725 } else if ( event->type() == QEvent::KeyRelease ) {
1726 keyReleaseEvent( (QKeyEvent *)event );
1727 return ((QKeyEvent *)event)->isAccepted();
1728 } else if ( event->type() == QEvent::FocusIn ) {
1729 focusInEvent( (QFocusEvent *)event );
1730 } else if ( event->type() == QEvent::FocusOut ) {
1731 focusOutEvent( (QFocusEvent *)event );
1732 } else if ( d->useCompletion && d->completeNow ) {
1733 d->completeNow = FALSE;
1734 if ( !d->ed->text().isNull() &&
1735 d->ed->cursorPosition() > d->completeAt &&
1736 d->ed->cursorPosition() == (int)d->ed->text().length() ) {
1737 QString ct( d->ed->text() );
1738 int i = completionIndex( ct, currentItem() );
1739 if ( i > -1 ) {
1740 QString it = text( i );
1741 d->ed->validateAndSet( it, ct.length(),
1742 ct.length(), it.length() );
1743 d->current = i;
1744 // ### sets current item without emitting signals. This is to
1745 // make sure the right item is current if you change current with
1746 // wheel/up/down. While typing current is not valid anyway. Fix properly
1747 // in 4.0.
1748 }
1749 }
1750 }
1751 } else if ( d->usingListBox() && ( object == d->listBox() ||
1752 object == d->listBox()->viewport() )) {
1753 QMouseEvent *e = (QMouseEvent*)event;
1754 switch( event->type() ) {
1755 case QEvent::MouseMove:
1756 if ( !d->mouseWasInsidePopup ) {
1757 QPoint pos = e->pos();
1758 if ( d->listBox()->rect().contains( pos ) )
1759 d->mouseWasInsidePopup = TRUE;
1760 // Check if arrow button should toggle
1761 if ( d->arrowPressed ) {
1762 QPoint comboPos;
1763 comboPos = mapFromGlobal( d->listBox()->mapToGlobal(pos) );
1764 QRect arrowRect =
1765 style().querySubControlMetrics( QStyle::CC_ComboBox, this,
1766 QStyle::SC_ComboBoxArrow);
1767 arrowRect = QStyle::visualRect(arrowRect, this);
1768 if ( arrowRect.contains( comboPos ) ) {
1769 if ( !d->arrowDown ) {
1770 d->arrowDown = TRUE;
1771 repaint( FALSE );
1772 }
1773 } else {
1774 if ( d->arrowDown ) {
1775 d->arrowDown = FALSE;
1776 repaint( FALSE );
1777 }
1778 }
1779 }
1780 } else if ((e->state() & ( RightButton | LeftButton | MidButton ) ) == 0 &&
1781 style().styleHint(QStyle::SH_ComboBox_ListMouseTracking, this)) {
1782 QWidget *mouseW = QApplication::widgetAt( e->globalPos(), TRUE );
1783 if ( mouseW == d->listBox()->viewport() ) { //###
1784 QMouseEvent m( QEvent::MouseMove, e->pos(), e->globalPos(),
1785 LeftButton, LeftButton );
1786 QApplication::sendEvent( object, &m ); //### Evil
1787 return TRUE;
1788 }
1789 }
1790
1791 break;
1792 case QEvent::MouseButtonRelease:
1793 if ( d->listBox()->rect().contains( e->pos() ) ) {
1794 QMouseEvent tmp( QEvent::MouseButtonDblClick,
1795 e->pos(), e->button(), e->state() ) ;
1796 // will hide popup
1797 QApplication::sendEvent( object, &tmp );
1798 return TRUE;
1799 } else {
1800 if ( d->mouseWasInsidePopup ) {
1801 popDownListBox();
1802 } else {
1803 d->arrowPressed = FALSE;
1804 if ( d->arrowDown ) {
1805 d->arrowDown = FALSE;
1806 repaint( FALSE );
1807 }
1808 }
1809 }
1810 break;
1811 case QEvent::MouseButtonDblClick:
1812 case QEvent::MouseButtonPress:
1813 if ( !d->listBox()->rect().contains( e->pos() ) ) {
1814 QPoint globalPos = d->listBox()->mapToGlobal(e->pos());
1815 if ( QApplication::widgetAt( globalPos, TRUE ) == this ) {
1816 d->discardNextMousePress = TRUE;
1817 // avoid popping up again
1818 }
1819 popDownListBox();
1820 return TRUE;
1821 }
1822 break;
1823 case QEvent::KeyPress:
1824 switch( ((QKeyEvent *)event)->key() ) {
1825 case Key_Up:
1826 case Key_Down:
1827 if ( !(((QKeyEvent *)event)->state() & AltButton) )
1828 break;
1829 case Key_F4:
1830 case Key_Escape:
1831 if ( d->poppedUp ) {
1832 popDownListBox();
1833 return TRUE;
1834 }
1835 break;
1836 case Key_Enter:
1837 case Key_Return:
1838 // work around QDialog's enter handling
1839 return FALSE;
1840 default:
1841 break;
1842 }
1843 break;
1844 case QEvent::Hide:
1845 popDownListBox();
1846 break;
1847 default:
1848 break;
1849 }
1850 } else if ( (!d->usingListBox() || style().styleHint(QStyle::SH_ComboBox_Popup, this)) &&
1851 object == d->popup() ) {
1852 QMouseEvent *e = (QMouseEvent*)event;
1853 switch ( event->type() ) {
1854 case QEvent::MouseButtonRelease:
1855 if ( d->shortClick ) {
1856 QMouseEvent tmp( QEvent::MouseMove,
1857 e->pos(), e->button(), e->state() ) ;
1858 // highlight item, but don't pop down:
1859 QApplication::sendEvent( object, &tmp );
1860 return TRUE;
1861 }
1862 break;
1863 case QEvent::MouseButtonDblClick:
1864 case QEvent::MouseButtonPress:
1865 if ( !d->popup()->rect().contains( e->pos() ) ) {
1866 // remove filter, event will take down popup:
1867 d->popup()->removeEventFilter( this );
1868 // ### uglehack!
1869 // call internalHighlight so the highlighed signal
1870 // will be emitted at least as often as necessary.
1871 // it may be called more often than necessary
1872 internalHighlight( d->current );
1873 }
1874 break;
1875 case QEvent::Hide:
1876 d->poppedUp = FALSE;
1877 break;
1878 default:
1879 break;
1880 }
1881 }
1882 return QWidget::eventFilter( object, event );
1883}
1884
1885
1886/*!
1887 Returns the index of the first item \e after \a startingAt of
1888 which \a prefix is a case-insensitive prefix. Returns -1 if no
1889 items start with \a prefix.
1890*/
1891
1892int QComboBox::completionIndex( const QString & prefix,
1893 int startingAt = 0 ) const
1894{
1895 int start = startingAt;
1896 if ( start < 0 || start >= count() )
1897 start = 0;
1898 if ( start >= count() )
1899 return -1;
1900 QString match = prefix.lower();
1901 if ( match.length() < 1 )
1902 return start;
1903
1904 QString current;
1905 int i = start;
1906 do {
1907 current = text( i ).lower();
1908 if ( current.startsWith( match ) )
1909 return i;
1910 i++;
1911 if ( i == count() )
1912 i = 0;
1913 } while ( i != start );
1914 return -1;
1915}
1916
1917
1918int QComboBox::sizeLimit() const
1919{
1920 return d ? d->sizeLimit : INT_MAX;
1921}
1922
1923void QComboBox::setSizeLimit( int lines )
1924{
1925 d->sizeLimit = lines;
1926}
1927
1928
1929int QComboBox::maxCount() const
1930{
1931 return d ? d->maxCount : INT_MAX;
1932}
1933
1934void QComboBox::setMaxCount( int count )
1935{
1936 int l = this->count();
1937 while( --l > count )
1938 removeItem( l );
1939 d->maxCount = count;
1940}
1941
1942QComboBox::Policy QComboBox::insertionPolicy() const
1943{
1944 return d->p;
1945}
1946
1947void QComboBox::setInsertionPolicy( Policy policy )
1948{
1949 d->p = policy;
1950}
1951
1952
1953
1954/*!
1955 Internal slot to keep the line editor up to date.
1956*/
1957
1958void QComboBox::returnPressed()
1959{
1960 QString s( d->ed->text() );
1961
1962 if ( s.isEmpty() )
1963 return;
1964
1965 int c = 0;
1966 bool doInsert = TRUE;
1967 if ( !d->duplicatesEnabled ) {
1968 for ( int i = 0; i < count(); ++i ) {
1969 if ( s == text( i ) ) {
1970 doInsert = FALSE;
1971 c = i;
1972 break;
1973 }
1974 }
1975 }
1976
1977 if ( doInsert ) {
1978 if ( insertionPolicy() != NoInsertion ) {
1979 int cnt = count();
1980 while ( cnt >= d->maxCount ) {
1981 removeItem( --cnt );
1982 }
1983 }
1984
1985 switch ( insertionPolicy() ) {
1986 case AtCurrent:
1987 if (count() == 0)
1988 insertItem(s);
1989 else if ( s != text( currentItem() ) )
1990 changeItem( s, currentItem() );
1991 emit activated( currentItem() );
1992 emit activated( s );
1993 return;
1994 case NoInsertion:
1995 emit activated( s );
1996 return;
1997 case AtTop:
1998 c = 0;
1999 break;
2000 case AtBottom:
2001 c = count();
2002 break;
2003 case BeforeCurrent:
2004 c = currentItem();
2005 break;
2006 case AfterCurrent:
2007 c = count() == 0 ? 0 : currentItem() + 1;
2008 break;
2009 }
2010 insertItem( s, c );
2011 }
2012
2013 setCurrentItem( c );
2014 emit activated( c );
2015 emit activated( s );
2016}
2017
2018
2019/*! \reimp
2020*/
2021
2022void QComboBox::setEnabled( bool enable )
2023{
2024 if ( !enable ) {
2025 if ( d->usingListBox() ) {
2026 popDownListBox();
2027 } else {
2028 d->popup()->removeEventFilter( this );
2029 d->popup()->close();
2030 d->poppedUp = FALSE;
2031 }
2032 }
2033 QWidget::setEnabled( enable );
2034}
2035
2036
2037
2038/*!
2039 Applies the validator \a v to the combobox so that only text which
2040 is valid according to \a v is accepted.
2041
2042 This function does nothing if the combobox is not editable.
2043
2044 \sa validator() clearValidator() QValidator
2045*/
2046
2047void QComboBox::setValidator( const QValidator * v )
2048{
2049 if ( d && d->ed )
2050 d->ed->setValidator( v );
2051}
2052
2053
2054/*!
2055 Returns the validator which constrains editing for this combobox
2056 if there is one; otherwise returns 0.
2057
2058 \sa setValidator() clearValidator() QValidator
2059*/
2060
2061const QValidator * QComboBox::validator() const
2062{
2063 return d && d->ed ? d->ed->validator() : 0;
2064}
2065
2066
2067/*!
2068 This slot is equivalent to setValidator( 0 ).
2069*/
2070
2071void QComboBox::clearValidator()
2072{
2073 if ( d && d->ed )
2074 d->ed->setValidator( 0 );
2075}
2076
2077
2078/*!
2079 Sets the combobox to use \a newListBox instead of the current list
2080 box or popup. As a side effect, it clears the combobox of its
2081 current contents.
2082
2083 \warning QComboBox assumes that newListBox->text(n) returns
2084 non-null for 0 \<= n \< newListbox->count(). This assumption is
2085 necessary because of the line edit in QComboBox.
2086*/
2087
2088void QComboBox::setListBox( QListBox * newListBox )
2089{
2090 clear();
2091
2092 if ( d->usingListBox() )
2093 delete d->listBox();
2094 else
2095 delete d->popup();
2096
2097 newListBox->reparent( this, WType_Popup, QPoint(0,0), FALSE );
2098 d->setListBox( newListBox );
2099 d->listBox()->setFont( font() );
2100 d->listBox()->setPalette( palette() );
2101 d->listBox()->setVScrollBarMode(QScrollView::AlwaysOff);
2102 d->listBox()->setHScrollBarMode(QScrollView::AlwaysOff);
2103 d->listBox()->setFrameStyle( QFrame::Box | QFrame::Plain );
2104 d->listBox()->setLineWidth( 1 );
2105 d->listBox()->resize( 100, 10 );
2106
2107 connect( d->listBox(), SIGNAL(selected(int)),
2108 SLOT(internalActivate(int)) );
2109 connect( d->listBox(), SIGNAL(highlighted(int)),
2110 SLOT(internalHighlight(int)));
2111}
2112
2113
2114/*!
2115 Returns the current list box, or 0 if there is no list box.
2116 (QComboBox can use QPopupMenu instead of QListBox.) Provided to
2117 match setListBox().
2118
2119 \sa setListBox()
2120*/
2121
2122QListBox * QComboBox::listBox() const
2123{
2124 return d && d->usingListBox() ? d->listBox() : 0;
2125}
2126
2127/*!
2128 Returns the line edit, or 0 if there is no line edit.
2129
2130 Only editable listboxes have a line editor.
2131*/
2132QLineEdit* QComboBox::lineEdit() const
2133{
2134 return d->ed;
2135}
2136
2137
2138
2139/*!
2140 Clears the line edit without changing the combobox's contents.
2141 Does nothing if the combobox isn't editable.
2142
2143 This is particularly useful when using a combobox as a line edit
2144 with history. For example you can connect the combobox's
2145 activated() signal to clearEdit() in order to present the user
2146 with a new, empty line as soon as Enter is pressed.
2147
2148 \sa setEditText()
2149*/
2150
2151void QComboBox::clearEdit()
2152{
2153 if ( d && d->ed )
2154 d->ed->clear();
2155}
2156
2157
2158/*!
2159 Sets the text in the line edit to \a newText without changing the
2160 combobox's contents. Does nothing if the combobox isn't editable.
2161
2162 This is useful e.g. for providing a good starting point for the
2163 user's editing and entering the change in the combobox only when
2164 the user presses Enter.
2165
2166 \sa clearEdit() insertItem()
2167*/
2168
2169void QComboBox::setEditText( const QString &newText )
2170{
2171 if ( d && d->ed ) {
2172 d->updateLinedGeometry();
2173 d->ed->setText( newText );
2174 }
2175}
2176
2177void QComboBox::setAutoCompletion( bool enable )
2178{
2179 d->useCompletion = enable;
2180 d->completeNow = FALSE;
2181}
2182
2183
2184bool QComboBox::autoCompletion() const
2185{
2186 return d->useCompletion;
2187}
2188
2189/*!\reimp
2190 */
2191void QComboBox::styleChange( QStyle& s )
2192{
2193 d->sizeHint = QSize(); // invalidate size hint...
2194 if ( d->ed )
2195 d->updateLinedGeometry();
2196 QWidget::styleChange( s );
2197}
2198
2199bool QComboBox::editable() const
2200{
2201 return d->ed != 0;
2202}
2203
2204void QComboBox::setEditable( bool y )
2205{
2206 if ( y == editable() )
2207 return;
2208 if ( y ) {
2209 if ( !d->usingListBox() )
2210 setUpListBox();
2211 setUpLineEdit();
2212 d->ed->show();
2213 if ( currentItem() )
2214 setEditText( currentText() );
2215 } else {
2216 delete d->ed;
2217 d->ed = 0;
2218 }
2219
2220 setFocusPolicy( StrongFocus );
2221 updateGeometry();
2222 update();
2223}
2224
2225
2226void QComboBox::setUpListBox()
2227{
2228 d->setListBox( new QListBox( this, "in-combo", WType_Popup ) );
2229 d->listBox()->setFont( font() );
2230 d->listBox()->setPalette( palette() );
2231 d->listBox()->setVScrollBarMode( QListBox::AlwaysOff );
2232 d->listBox()->setHScrollBarMode( QListBox::AlwaysOff );
2233 d->listBox()->setFrameStyle( QFrame::Box | QFrame::Plain );
2234 d->listBox()->setLineWidth( 1 );
2235 d->listBox()->resize( 100, 10 );
2236
2237 connect( d->listBox(), SIGNAL(selected(int)),
2238 SLOT(internalActivate(int)) );
2239 connect( d->listBox(), SIGNAL(highlighted(int)),
2240 SLOT(internalHighlight(int)));
2241}
2242
2243
2244void QComboBox::setUpLineEdit()
2245{
2246 if ( !d->ed )
2247 setLineEdit( new QLineEdit( this, "combo edit" ) );
2248}
2249
2250/*!
2251 Sets the line edit to use \a edit instead of the current line edit.
2252*/
2253
2254void QComboBox::setLineEdit( QLineEdit *edit )
2255{
2256 if ( !edit ) {
2257#if defined(QT_CHECK_NULL)
2258 Q_ASSERT( edit != 0 );
2259#endif
2260 return;
2261 }
2262
2263 edit->setText( currentText() );
2264 delete d->ed;
2265 d->ed = edit;
2266
2267 if ( edit->parent() != this )
2268 edit->reparent( this, QPoint(0,0), FALSE );
2269
2270 connect (edit, SIGNAL( textChanged(const QString&) ),
2271 this, SIGNAL( textChanged(const QString&) ) );
2272 connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) );
2273
2274 edit->setFrame( FALSE );
2275 d->updateLinedGeometry();
2276 edit->installEventFilter( this );
2277 setFocusProxy( edit );
2278 setFocusPolicy( StrongFocus );
2279 setInputMethodEnabled( TRUE );
2280
2281 if ( !d->usingListBox() )
2282 setUpListBox();
2283
2284 if ( isVisible() )
2285 edit->show();
2286
2287 updateGeometry();
2288 update();
2289}
2290
2291/*!
2292 \reimp
2293*/
2294void QComboBox::hide()
2295{
2296 QWidget::hide();
2297
2298 if (listBox())
2299 listBox()->hide();
2300 else if (d->popup())
2301 d->popup()->hide();
2302}
2303
2304#endif // QT_NO_COMBOBOX
Note: See TracBrowser for help on using the repository browser.