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