source: vendor/trolltech/current/src/widgets/qcombobox.cpp

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

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 59.4 KB
Line 
1/**********************************************************************
2** $Id: qcombobox.cpp 2 2005-11-16 15:49:26Z 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 if ( count() && ( !editable() || arrowRect.contains( e->pos() ) ) ) {
1342 d->arrowPressed = FALSE;
1343
1344 if ( d->usingListBox() ) {
1345 listBox()->blockSignals( TRUE );
1346 qApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll
1347 listBox()->setCurrentItem(d->current);
1348 listBox()->blockSignals( FALSE );
1349 popup();
1350 if ( arrowRect.contains( e->pos() ) ) {
1351 d->arrowPressed = TRUE;
1352 d->arrowDown = TRUE;
1353 repaint( FALSE );
1354 }
1355 } else {
1356 popup();
1357 }
1358 QTimer::singleShot( 200, this, SLOT(internalClickTimeout()));
1359 d->shortClick = TRUE;
1360 }
1361}
1362
1363/*!\reimp
1364*/
1365
1366void QComboBox::mouseMoveEvent( QMouseEvent * )
1367{
1368}
1369
1370/*!\reimp
1371*/
1372
1373void QComboBox::mouseReleaseEvent( QMouseEvent * )
1374{
1375}
1376
1377/*!\reimp
1378*/
1379
1380void QComboBox::mouseDoubleClickEvent( QMouseEvent *e )
1381{
1382 mousePressEvent( e );
1383}
1384
1385
1386/*!\reimp
1387*/
1388
1389void QComboBox::keyPressEvent( QKeyEvent *e )
1390{
1391 int c = currentItem();
1392 if ( ( e->key() == Key_F4 && e->state() == 0 ) ||
1393 ( e->key() == Key_Down && (e->state() & AltButton) ) ||
1394 ( !d->ed && e->key() == Key_Space ) ) {
1395 if ( count() ) {
1396 if ( !d->usingListBox() )
1397 d->popup()->setActiveItem( this->d->current );
1398 popup();
1399 }
1400 return;
1401 } else if ( d->usingListBox() && e->key() == Key_Up ) {
1402 if ( c > 0 )
1403 setCurrentItem( c-1 );
1404 } else if ( d->usingListBox() && e->key() == Key_Down ) {
1405 if ( ++c < count() )
1406 setCurrentItem( c );
1407 } else if ( d->usingListBox() && e->key() == Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) {
1408 setCurrentItem( 0 );
1409 } else if ( d->usingListBox() && e->key() == Key_End && ( !d->ed || !d->ed->hasFocus() ) ) {
1410 setCurrentItem( count()-1 );
1411 } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) {
1412 if ( !d->completionTimer->isActive() ) {
1413 d->completeAt = 0;
1414 c = completionIndex( e->text(), ++c );
1415 if ( c >= 0 ) {
1416 setCurrentItem( c );
1417 d->completeAt = e->text().length();
1418 }
1419 } else {
1420 d->completionTimer->stop();
1421 QString ct = currentText().left( d->completeAt ) + e->text();
1422 c = completionIndex( ct, c );
1423 if ( c < 0 && d->completeAt > 0 ) {
1424 c = completionIndex( e->text(), 0 );
1425 ct = e->text();
1426 }
1427 d->completeAt = 0;
1428 if ( c >= 0 ) {
1429 setCurrentItem( c );
1430 d->completeAt = ct.length();
1431 }
1432 }
1433 d->completionTimer->start( 400, TRUE );
1434 } else {
1435 e->ignore();
1436 return;
1437 }
1438
1439 c = currentItem();
1440 if ( count() && !text( c ).isNull() )
1441 emit activated( text( c ) );
1442 emit activated( c );
1443}
1444
1445
1446/*!\reimp
1447*/
1448
1449void QComboBox::focusInEvent( QFocusEvent * e )
1450{
1451 QWidget::focusInEvent( e );
1452 d->completeNow = FALSE;
1453 d->completeAt = 0;
1454}
1455
1456/*!\reimp
1457*/
1458
1459void QComboBox::focusOutEvent( QFocusEvent * e )
1460{
1461 QWidget::focusOutEvent( e );
1462 d->completeNow = FALSE;
1463 d->completeAt = 0;
1464}
1465
1466/*!\reimp
1467*/
1468#ifndef QT_NO_WHEELEVENT
1469void QComboBox::wheelEvent( QWheelEvent *e )
1470{
1471 if ( d->poppedUp ) {
1472 if ( d->usingListBox() ) {
1473 QApplication::sendEvent( d->listBox(), e );
1474 }
1475 } else {
1476 if ( e->delta() > 0 ) {
1477 int c = currentItem();
1478 if ( c > 0 ) {
1479 setCurrentItem( c-1 );
1480 emit activated( currentItem() );
1481 emit activated( currentText() );
1482 }
1483 } else {
1484 int c = currentItem();
1485 if ( ++c < count() ) {
1486 setCurrentItem( c );
1487 emit activated( currentItem() );
1488 emit activated( currentText() );
1489 }
1490 }
1491 e->accept();
1492 }
1493}
1494#endif
1495
1496/*!
1497 \internal
1498 Calculates the listbox height needed to contain all items, or as
1499 many as the list box is supposed to contain.
1500*/
1501static int listHeight( QListBox *l, int sl )
1502{
1503 if ( l->count() > 0 )
1504 return QMIN( l->count(), (uint)sl) * l->item( 0 )->height(l);
1505 else
1506 return l->sizeHint().height();
1507}
1508
1509
1510/*!
1511 Pops up the combobox popup list.
1512
1513 If the list is empty, no items appear.
1514*/
1515
1516void QComboBox::popup()
1517{
1518 if ( !count() )
1519 return;
1520
1521 if( !d->usingListBox() || style().styleHint(QStyle::SH_ComboBox_Popup, this) ) {
1522 if(d->usingListBox()) {
1523 if(!d->popup()) {
1524 QComboBoxPopup *p = new QComboBoxPopup( this, "in-combo" );
1525 d->setPopupMenu( p, FALSE );
1526 p->setFont( font() );
1527 connect( p, SIGNAL(activated(int)), SLOT(internalActivate(int)) );
1528 connect( p, SIGNAL(highlighted(int)), SLOT(internalHighlight(int)) );
1529 }
1530 d->popup()->clear();
1531 for(unsigned int i = 0; i < d->listBox()->count(); i++) {
1532 QListBoxItem *item = d->listBox()->item(i);
1533 if(item->rtti() == QListBoxText::RTTI) {
1534 d->popup()->insertItem(item->text(), i, i);
1535 } else if(item->rtti() == QListBoxPixmap::RTTI) {
1536 if(item->pixmap())
1537 d->popup()->insertItem(QIconSet(*item->pixmap()), item->text(), i, i);
1538 else
1539 d->popup()->insertItem(item->text(), i, i);
1540 } else {
1541 d->popup()->insertItem(new QComboBoxPopupItem(item), i, i);
1542 }
1543 }
1544 }
1545 d->popup()->installEventFilter( this );
1546 if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this))
1547 d->popup()->setItemChecked(this->d->current, TRUE);
1548 d->popup()->popup( mapToGlobal( QPoint(0,0) ), this->d->current );
1549 update();
1550 } else {
1551 // Send all listbox events to eventFilter():
1552 QListBox* lb = d->listBox();
1553 lb->triggerUpdate( TRUE );
1554 lb->installEventFilter( this );
1555 d->mouseWasInsidePopup = FALSE;
1556 int w = lb->variableWidth() ? lb->sizeHint().width() : width();
1557 int h = listHeight( lb, d->sizeLimit ) + 2;
1558 QRect screen = QApplication::desktop()->availableGeometry( this );
1559
1560 int sx = screen.x(); // screen pos
1561 int sy = screen.y();
1562 int sw = screen.width(); // screen width
1563 int sh = screen.height(); // screen height
1564 QPoint pos = mapToGlobal( QPoint(0,height()) );
1565 // ## Similar code is in QPopupMenu
1566 int x = pos.x();
1567 int y = pos.y();
1568
1569 // the complete widget must be visible
1570 if ( x + w > sx + sw )
1571 x = sx+sw - w;
1572 if ( x < sx )
1573 x = sx;
1574 if (y + h > sy+sh && y - h - height() >= 0 )
1575 y = y - h - height();
1576
1577 QRect rect =
1578 style().querySubControlMetrics( QStyle::CC_ComboBox, this,
1579 QStyle::SC_ComboBoxListBoxPopup,
1580 QStyleOption( x, y, w, h ) );
1581 // work around older styles that don't implement the combobox
1582 // listbox popup subcontrol
1583 if ( rect.isNull() )
1584 rect.setRect( x, y, w, h );
1585 lb->setGeometry( rect );
1586
1587 lb->raise();
1588 bool block = lb->signalsBlocked();
1589 lb->blockSignals( TRUE );
1590 QListBoxItem* currentLBItem = 0;
1591 if ( editable() && currentText() != text( currentItem() ) )
1592 currentLBItem = lb->findItem( currentText() );
1593
1594 currentLBItem = currentLBItem ? currentLBItem : lb->item( d->current );
1595
1596 lb->setCurrentItem( currentLBItem );
1597 lb->setContentsPos( lb->contentsX(),
1598 lb->viewportToContents( lb->itemRect( currentLBItem ).topLeft() ).y() );
1599
1600 // set the current item to also be the selected item if it isn't already
1601 if ( currentLBItem && currentLBItem->isSelectable() && !currentLBItem->isSelected() )
1602 lb->setSelected( currentLBItem, TRUE );
1603 lb->blockSignals( block );
1604 lb->setVScrollBarMode(QScrollView::Auto);
1605
1606#ifndef QT_NO_EFFECTS
1607 if ( QApplication::isEffectEnabled( UI_AnimateCombo ) ) {
1608 if ( lb->y() < mapToGlobal(QPoint(0,0)).y() )
1609 qScrollEffect( lb, QEffects::UpScroll );
1610 else
1611 qScrollEffect( lb );
1612 } else
1613#endif
1614 lb->show();
1615 }
1616 d->poppedUp = TRUE;
1617}
1618
1619
1620/*!
1621 \reimp
1622*/
1623void QComboBox::updateMask()
1624{
1625 QBitmap bm( size() );
1626 bm.fill( color0 );
1627
1628 {
1629 QPainter p( &bm, this );
1630 style().drawComplexControlMask(QStyle::CC_ComboBox, &p, this, rect());
1631 }
1632
1633 setMask( bm );
1634}
1635
1636/*!
1637 \internal
1638 Pops down (removes) the combobox popup list box.
1639*/
1640void QComboBox::popDownListBox()
1641{
1642 Q_ASSERT( d->usingListBox() );
1643 d->listBox()->removeEventFilter( this );
1644 d->listBox()->viewport()->removeEventFilter( this );
1645 d->listBox()->hide();
1646 d->listBox()->setCurrentItem( d->current );
1647 if ( d->arrowDown ) {
1648 d->arrowDown = FALSE;
1649 repaint( FALSE );
1650 }
1651 d->poppedUp = FALSE;
1652}
1653
1654
1655/*!
1656 \internal
1657 Re-indexes the identifiers in the popup list.
1658*/
1659
1660void QComboBox::reIndex()
1661{
1662 if ( !d->usingListBox() ) {
1663 int cnt = count();
1664 while ( cnt-- )
1665 d->popup()->setId( cnt, cnt );
1666 }
1667}
1668
1669/*!
1670 \internal
1671 Repaints the combobox.
1672*/
1673
1674void QComboBox::currentChanged()
1675{
1676 if ( d->autoresize )
1677 adjustSize();
1678 update();
1679
1680#if defined(QT_ACCESSIBILITY_SUPPORT)
1681 QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
1682#endif
1683}
1684
1685/*! \reimp
1686
1687 \internal
1688
1689 The event filter steals events from the popup or listbox when they
1690 are popped up. It makes the popup stay up after a short click in
1691 motif style. In windows style it toggles the arrow button of the
1692 combobox field, and activates an item and takes down the listbox
1693 when the mouse button is released.
1694*/
1695
1696bool QComboBox::eventFilter( QObject *object, QEvent *event )
1697{
1698 if ( !event )
1699 return TRUE;
1700 else if ( object == d->ed ) {
1701 if ( event->type() == QEvent::KeyPress ) {
1702 bool isAccepted = ( (QKeyEvent*)event )->isAccepted();
1703 keyPressEvent( (QKeyEvent *)event );
1704 if ( ((QKeyEvent *)event)->isAccepted() ) {
1705 d->completeNow = FALSE;
1706 return TRUE;
1707 } else if ( ((QKeyEvent *)event)->key() != Key_End ) {
1708 d->completeNow = TRUE;
1709 d->completeAt = d->ed->cursorPosition();
1710 }
1711 if ( isAccepted )
1712 ( (QKeyEvent*)event )->accept();
1713 else
1714 ( (QKeyEvent*)event )->ignore();
1715 } else if ( event->type() == QEvent::KeyRelease ) {
1716 keyReleaseEvent( (QKeyEvent *)event );
1717 return ((QKeyEvent *)event)->isAccepted();
1718 } else if ( event->type() == QEvent::FocusIn ) {
1719 focusInEvent( (QFocusEvent *)event );
1720 } else if ( event->type() == QEvent::FocusOut ) {
1721 focusOutEvent( (QFocusEvent *)event );
1722 } else if ( d->useCompletion && d->completeNow ) {
1723 d->completeNow = FALSE;
1724 if ( !d->ed->text().isNull() &&
1725 d->ed->cursorPosition() > d->completeAt &&
1726 d->ed->cursorPosition() == (int)d->ed->text().length() ) {
1727 QString ct( d->ed->text() );
1728 int i = completionIndex( ct, currentItem() );
1729 if ( i > -1 ) {
1730 QString it = text( i );
1731 d->ed->validateAndSet( it, ct.length(),
1732 ct.length(), it.length() );
1733 d->current = i;
1734 // ### sets current item without emitting signals. This is to
1735 // make sure the right item is current if you change current with
1736 // wheel/up/down. While typing current is not valid anyway. Fix properly
1737 // in 4.0.
1738 }
1739 }
1740 }
1741 } else if ( d->usingListBox() && ( object == d->listBox() ||
1742 object == d->listBox()->viewport() )) {
1743 QMouseEvent *e = (QMouseEvent*)event;
1744 switch( event->type() ) {
1745 case QEvent::MouseMove:
1746 if ( !d->mouseWasInsidePopup ) {
1747 QPoint pos = e->pos();
1748 if ( d->listBox()->rect().contains( pos ) )
1749 d->mouseWasInsidePopup = TRUE;
1750 // Check if arrow button should toggle
1751 if ( d->arrowPressed ) {
1752 QPoint comboPos;
1753 comboPos = mapFromGlobal( d->listBox()->mapToGlobal(pos) );
1754 QRect arrowRect =
1755 style().querySubControlMetrics( QStyle::CC_ComboBox, this,
1756 QStyle::SC_ComboBoxArrow);
1757 arrowRect = QStyle::visualRect(arrowRect, this);
1758 if ( arrowRect.contains( comboPos ) ) {
1759 if ( !d->arrowDown ) {
1760 d->arrowDown = TRUE;
1761 repaint( FALSE );
1762 }
1763 } else {
1764 if ( d->arrowDown ) {
1765 d->arrowDown = FALSE;
1766 repaint( FALSE );
1767 }
1768 }
1769 }
1770 } else if ((e->state() & ( RightButton | LeftButton | MidButton ) ) == 0 &&
1771 style().styleHint(QStyle::SH_ComboBox_ListMouseTracking, this)) {
1772 QWidget *mouseW = QApplication::widgetAt( e->globalPos(), TRUE );
1773 if ( mouseW == d->listBox()->viewport() ) { //###
1774 QMouseEvent m( QEvent::MouseMove, e->pos(), e->globalPos(),
1775 LeftButton, LeftButton );
1776 QApplication::sendEvent( object, &m ); //### Evil
1777 return TRUE;
1778 }
1779 }
1780
1781 break;
1782 case QEvent::MouseButtonRelease:
1783 if ( d->listBox()->rect().contains( e->pos() ) ) {
1784 QMouseEvent tmp( QEvent::MouseButtonDblClick,
1785 e->pos(), e->button(), e->state() ) ;
1786 // will hide popup
1787 QApplication::sendEvent( object, &tmp );
1788 return TRUE;
1789 } else {
1790 if ( d->mouseWasInsidePopup ) {
1791 popDownListBox();
1792 } else {
1793 d->arrowPressed = FALSE;
1794 if ( d->arrowDown ) {
1795 d->arrowDown = FALSE;
1796 repaint( FALSE );
1797 }
1798 }
1799 }
1800 break;
1801 case QEvent::MouseButtonDblClick:
1802 case QEvent::MouseButtonPress:
1803 if ( !d->listBox()->rect().contains( e->pos() ) ) {
1804 QPoint globalPos = d->listBox()->mapToGlobal(e->pos());
1805 if ( QApplication::widgetAt( globalPos, TRUE ) == this ) {
1806 d->discardNextMousePress = TRUE;
1807 // avoid popping up again
1808 }
1809 popDownListBox();
1810 return TRUE;
1811 }
1812 break;
1813 case QEvent::KeyPress:
1814 switch( ((QKeyEvent *)event)->key() ) {
1815 case Key_Up:
1816 case Key_Down:
1817 if ( !(((QKeyEvent *)event)->state() & AltButton) )
1818 break;
1819 case Key_F4:
1820 case Key_Escape:
1821 if ( d->poppedUp ) {
1822 popDownListBox();
1823 return TRUE;
1824 }
1825 break;
1826 case Key_Enter:
1827 case Key_Return:
1828 // work around QDialog's enter handling
1829 return FALSE;
1830 default:
1831 break;
1832 }
1833 break;
1834 case QEvent::Hide:
1835 popDownListBox();
1836 break;
1837 default:
1838 break;
1839 }
1840 } else if ( (!d->usingListBox() || style().styleHint(QStyle::SH_ComboBox_Popup, this)) &&
1841 object == d->popup() ) {
1842 QMouseEvent *e = (QMouseEvent*)event;
1843 switch ( event->type() ) {
1844 case QEvent::MouseButtonRelease:
1845 if ( d->shortClick ) {
1846 QMouseEvent tmp( QEvent::MouseMove,
1847 e->pos(), e->button(), e->state() ) ;
1848 // highlight item, but don't pop down:
1849 QApplication::sendEvent( object, &tmp );
1850 return TRUE;
1851 }
1852 break;
1853 case QEvent::MouseButtonDblClick:
1854 case QEvent::MouseButtonPress:
1855 if ( !d->popup()->rect().contains( e->pos() ) ) {
1856 // remove filter, event will take down popup:
1857 d->popup()->removeEventFilter( this );
1858 // ### uglehack!
1859 // call internalHighlight so the highlighed signal
1860 // will be emitted at least as often as necessary.
1861 // it may be called more often than necessary
1862 internalHighlight( d->current );
1863 }
1864 break;
1865 case QEvent::Hide:
1866 d->poppedUp = FALSE;
1867 break;
1868 default:
1869 break;
1870 }
1871 }
1872 return QWidget::eventFilter( object, event );
1873}
1874
1875
1876/*!
1877 Returns the index of the first item \e after \a startingAt of
1878 which \a prefix is a case-insensitive prefix. Returns -1 if no
1879 items start with \a prefix.
1880*/
1881
1882int QComboBox::completionIndex( const QString & prefix,
1883 int startingAt = 0 ) const
1884{
1885 int start = startingAt;
1886 if ( start < 0 || start >= count() )
1887 start = 0;
1888 if ( start >= count() )
1889 return -1;
1890 QString match = prefix.lower();
1891 if ( match.length() < 1 )
1892 return start;
1893
1894 QString current;
1895 int i = start;
1896 do {
1897 current = text( i ).lower();
1898 if ( current.startsWith( match ) )
1899 return i;
1900 i++;
1901 if ( i == count() )
1902 i = 0;
1903 } while ( i != start );
1904 return -1;
1905}
1906
1907
1908int QComboBox::sizeLimit() const
1909{
1910 return d ? d->sizeLimit : INT_MAX;
1911}
1912
1913void QComboBox::setSizeLimit( int lines )
1914{
1915 d->sizeLimit = lines;
1916}
1917
1918
1919int QComboBox::maxCount() const
1920{
1921 return d ? d->maxCount : INT_MAX;
1922}
1923
1924void QComboBox::setMaxCount( int count )
1925{
1926 int l = this->count();
1927 while( --l > count )
1928 removeItem( l );
1929 d->maxCount = count;
1930}
1931
1932QComboBox::Policy QComboBox::insertionPolicy() const
1933{
1934 return d->p;
1935}
1936
1937void QComboBox::setInsertionPolicy( Policy policy )
1938{
1939 d->p = policy;
1940}
1941
1942
1943
1944/*!
1945 Internal slot to keep the line editor up to date.
1946*/
1947
1948void QComboBox::returnPressed()
1949{
1950 QString s( d->ed->text() );
1951
1952 if ( s.isEmpty() )
1953 return;
1954
1955 int c = 0;
1956 bool doInsert = TRUE;
1957 if ( !d->duplicatesEnabled ) {
1958 for ( int i = 0; i < count(); ++i ) {
1959 if ( s == text( i ) ) {
1960 doInsert = FALSE;
1961 c = i;
1962 break;
1963 }
1964 }
1965 }
1966
1967 if ( doInsert ) {
1968 if ( insertionPolicy() != NoInsertion ) {
1969 int cnt = count();
1970 while ( cnt >= d->maxCount ) {
1971 removeItem( --cnt );
1972 }
1973 }
1974
1975 switch ( insertionPolicy() ) {
1976 case AtCurrent:
1977 if (count() == 0)
1978 insertItem(s);
1979 else if ( s != text( currentItem() ) )
1980 changeItem( s, currentItem() );
1981 emit activated( currentItem() );
1982 emit activated( s );
1983 return;
1984 case NoInsertion:
1985 emit activated( s );
1986 return;
1987 case AtTop:
1988 c = 0;
1989 break;
1990 case AtBottom:
1991 c = count();
1992 break;
1993 case BeforeCurrent:
1994 c = currentItem();
1995 break;
1996 case AfterCurrent:
1997 c = count() == 0 ? 0 : currentItem() + 1;
1998 break;
1999 }
2000 insertItem( s, c );
2001 }
2002
2003 setCurrentItem( c );
2004 emit activated( c );
2005 emit activated( s );
2006}
2007
2008
2009/*! \reimp
2010*/
2011
2012void QComboBox::setEnabled( bool enable )
2013{
2014 if ( !enable ) {
2015 if ( d->usingListBox() ) {
2016 popDownListBox();
2017 } else {
2018 d->popup()->removeEventFilter( this );
2019 d->popup()->close();
2020 d->poppedUp = FALSE;
2021 }
2022 }
2023 QWidget::setEnabled( enable );
2024}
2025
2026
2027
2028/*!
2029 Applies the validator \a v to the combobox so that only text which
2030 is valid according to \a v is accepted.
2031
2032 This function does nothing if the combobox is not editable.
2033
2034 \sa validator() clearValidator() QValidator
2035*/
2036
2037void QComboBox::setValidator( const QValidator * v )
2038{
2039 if ( d && d->ed )
2040 d->ed->setValidator( v );
2041}
2042
2043
2044/*!
2045 Returns the validator which constrains editing for this combobox
2046 if there is one; otherwise returns 0.
2047
2048 \sa setValidator() clearValidator() QValidator
2049*/
2050
2051const QValidator * QComboBox::validator() const
2052{
2053 return d && d->ed ? d->ed->validator() : 0;
2054}
2055
2056
2057/*!
2058 This slot is equivalent to setValidator( 0 ).
2059*/
2060
2061void QComboBox::clearValidator()
2062{
2063 if ( d && d->ed )
2064 d->ed->setValidator( 0 );
2065}
2066
2067
2068/*!
2069 Sets the combobox to use \a newListBox instead of the current list
2070 box or popup. As a side effect, it clears the combobox of its
2071 current contents.
2072
2073 \warning QComboBox assumes that newListBox->text(n) returns
2074 non-null for 0 \<= n \< newListbox->count(). This assumption is
2075 necessary because of the line edit in QComboBox.
2076*/
2077
2078void QComboBox::setListBox( QListBox * newListBox )
2079{
2080 clear();
2081
2082 if ( d->usingListBox() )
2083 delete d->listBox();
2084 else
2085 delete d->popup();
2086
2087 newListBox->reparent( this, WType_Popup, QPoint(0,0), FALSE );
2088 d->setListBox( newListBox );
2089 d->listBox()->setFont( font() );
2090 d->listBox()->setPalette( palette() );
2091 d->listBox()->setVScrollBarMode(QScrollView::AlwaysOff);
2092 d->listBox()->setHScrollBarMode(QScrollView::AlwaysOff);
2093 d->listBox()->setFrameStyle( QFrame::Box | QFrame::Plain );
2094 d->listBox()->setLineWidth( 1 );
2095 d->listBox()->resize( 100, 10 );
2096
2097 connect( d->listBox(), SIGNAL(selected(int)),
2098 SLOT(internalActivate(int)) );
2099 connect( d->listBox(), SIGNAL(highlighted(int)),
2100 SLOT(internalHighlight(int)));
2101}
2102
2103
2104/*!
2105 Returns the current list box, or 0 if there is no list box.
2106 (QComboBox can use QPopupMenu instead of QListBox.) Provided to
2107 match setListBox().
2108
2109 \sa setListBox()
2110*/
2111
2112QListBox * QComboBox::listBox() const
2113{
2114 return d && d->usingListBox() ? d->listBox() : 0;
2115}
2116
2117/*!
2118 Returns the line edit, or 0 if there is no line edit.
2119
2120 Only editable listboxes have a line editor.
2121*/
2122QLineEdit* QComboBox::lineEdit() const
2123{
2124 return d->ed;
2125}
2126
2127
2128
2129/*!
2130 Clears the line edit without changing the combobox's contents.
2131 Does nothing if the combobox isn't editable.
2132
2133 This is particularly useful when using a combobox as a line edit
2134 with history. For example you can connect the combobox's
2135 activated() signal to clearEdit() in order to present the user
2136 with a new, empty line as soon as Enter is pressed.
2137
2138 \sa setEditText()
2139*/
2140
2141void QComboBox::clearEdit()
2142{
2143 if ( d && d->ed )
2144 d->ed->clear();
2145}
2146
2147
2148/*!
2149 Sets the text in the line edit to \a newText without changing the
2150 combobox's contents. Does nothing if the combobox isn't editable.
2151
2152 This is useful e.g. for providing a good starting point for the
2153 user's editing and entering the change in the combobox only when
2154 the user presses Enter.
2155
2156 \sa clearEdit() insertItem()
2157*/
2158
2159void QComboBox::setEditText( const QString &newText )
2160{
2161 if ( d && d->ed ) {
2162 d->updateLinedGeometry();
2163 d->ed->setText( newText );
2164 }
2165}
2166
2167void QComboBox::setAutoCompletion( bool enable )
2168{
2169 d->useCompletion = enable;
2170 d->completeNow = FALSE;
2171}
2172
2173
2174bool QComboBox::autoCompletion() const
2175{
2176 return d->useCompletion;
2177}
2178
2179/*!\reimp
2180 */
2181void QComboBox::styleChange( QStyle& s )
2182{
2183 d->sizeHint = QSize(); // invalidate size hint...
2184 if ( d->ed )
2185 d->updateLinedGeometry();
2186 QWidget::styleChange( s );
2187}
2188
2189bool QComboBox::editable() const
2190{
2191 return d->ed != 0;
2192}
2193
2194void QComboBox::setEditable( bool y )
2195{
2196 if ( y == editable() )
2197 return;
2198 if ( y ) {
2199 if ( !d->usingListBox() )
2200 setUpListBox();
2201 setUpLineEdit();
2202 d->ed->show();
2203 if ( currentItem() )
2204 setEditText( currentText() );
2205 } else {
2206 delete d->ed;
2207 d->ed = 0;
2208 }
2209
2210 setFocusPolicy( StrongFocus );
2211 updateGeometry();
2212 update();
2213}
2214
2215
2216void QComboBox::setUpListBox()
2217{
2218 d->setListBox( new QListBox( this, "in-combo", WType_Popup ) );
2219 d->listBox()->setFont( font() );
2220 d->listBox()->setPalette( palette() );
2221 d->listBox()->setVScrollBarMode( QListBox::AlwaysOff );
2222 d->listBox()->setHScrollBarMode( QListBox::AlwaysOff );
2223 d->listBox()->setFrameStyle( QFrame::Box | QFrame::Plain );
2224 d->listBox()->setLineWidth( 1 );
2225 d->listBox()->resize( 100, 10 );
2226
2227 connect( d->listBox(), SIGNAL(selected(int)),
2228 SLOT(internalActivate(int)) );
2229 connect( d->listBox(), SIGNAL(highlighted(int)),
2230 SLOT(internalHighlight(int)));
2231}
2232
2233
2234void QComboBox::setUpLineEdit()
2235{
2236 if ( !d->ed )
2237 setLineEdit( new QLineEdit( this, "combo edit" ) );
2238}
2239
2240/*!
2241 Sets the line edit to use \a edit instead of the current line edit.
2242*/
2243
2244void QComboBox::setLineEdit( QLineEdit *edit )
2245{
2246 if ( !edit ) {
2247#if defined(QT_CHECK_NULL)
2248 Q_ASSERT( edit != 0 );
2249#endif
2250 return;
2251 }
2252
2253 edit->setText( currentText() );
2254 delete d->ed;
2255 d->ed = edit;
2256
2257 if ( edit->parent() != this )
2258 edit->reparent( this, QPoint(0,0), FALSE );
2259
2260 connect (edit, SIGNAL( textChanged(const QString&) ),
2261 this, SIGNAL( textChanged(const QString&) ) );
2262 connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) );
2263
2264 edit->setFrame( FALSE );
2265 d->updateLinedGeometry();
2266 edit->installEventFilter( this );
2267 setFocusProxy( edit );
2268 setFocusPolicy( StrongFocus );
2269 setInputMethodEnabled( TRUE );
2270
2271 if ( !d->usingListBox() )
2272 setUpListBox();
2273
2274 if ( isVisible() )
2275 edit->show();
2276
2277 updateGeometry();
2278 update();
2279}
2280
2281/*!
2282 \reimp
2283*/
2284void QComboBox::hide()
2285{
2286 QWidget::hide();
2287
2288 if (listBox())
2289 listBox()->hide();
2290 else if (d->popup())
2291 d->popup()->hide();
2292}
2293
2294#endif // QT_NO_COMBOBOX
Note: See TracBrowser for help on using the repository browser.