diff options
Diffstat (limited to 'src/widgets/qcombobox.cpp')
-rw-r--r-- | src/widgets/qcombobox.cpp | 2334 |
1 files changed, 2334 insertions, 0 deletions
diff --git a/src/widgets/qcombobox.cpp b/src/widgets/qcombobox.cpp new file mode 100644 index 000000000..8923192af --- /dev/null +++ b/src/widgets/qcombobox.cpp @@ -0,0 +1,2334 @@ +/********************************************************************** +** +** Implementation of TQComboBox widget class +** +** Created : 940426 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qcombobox.h" +#ifndef QT_NO_COMBOBOX +#include "qpopupmenu.h" +#include "qlistbox.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include "qstrlist.h" +#include "qpixmap.h" +#include "qtimer.h" +#include "qapplication.h" +#include "qlineedit.h" +#include "qbitmap.h" +#include "qeffects_p.h" +#include "qstringlist.h" +#include "qcombobox.h" +#include "qstyle.h" +#include <limits.h> +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +/*! + \class TQComboBox qcombobox.h + \brief The TQComboBox widget is a combined button and popup list. + + \ingroup basic + \mainclass + + A combobox is a selection widget which displays the current item + and can pop up a list of items. A combobox may be editable in + which case the user can enter arbitrary strings. + + Comboboxes provide a means of showing the user's current choice + out of a list of options in a way that takes up the minimum amount + of screen space. + + TQComboBox supports three different display styles: Aqua/Motif 1.x, + Motif 2.0 and Windows. In Motif 1.x, a combobox was called + XmOptionMenu. In Motif 2.0, OSF introduced an improved combobox + and named that XmComboBox. TQComboBox provides both. + + TQComboBox provides two different constructors. The simplest + constructor creates an "old-style" combobox in Motif (or Aqua) + style: + \code + TQComboBox *c = new TQComboBox( this, "read-only combobox" ); + \endcode + + The other constructor creates a new-style combobox in Motif style, + and can create both read-only and editable comboboxes: + \code + TQComboBox *c1 = new TQComboBox( FALSE, this, "read-only combobox" ); + TQComboBox *c2 = new TQComboBox( TRUE, this, "editable combobox" ); + \endcode + + New-style comboboxes use a list box in both Motif and Windows + styles, and both the content size and the on-screen size of the + list box can be limited with sizeLimit() and setMaxCount() + respectively. Old-style comboboxes use a popup in Aqua and Motif + style, and that popup will happily grow larger than the desktop if + you put enough data into it. + + The two constructors create identical-looking comboboxes in + Windows style. + + Comboboxes can contain pixmaps as well as strings; the + insertItem() and changeItem() functions are suitably overloaded. + For editable comboboxes, the function clearEdit() is provided, + to clear the displayed string without changing the combobox's + contents. + + A combobox emits two signals, activated() and highlighted(), when + a new item has been activated (selected) or highlighted (made + current). Both signals exist in two versions, one with a \c + TQString argument and one with an \c int argument. If the user + highlights or activates a pixmap, only the \c int signals are + emitted. Whenever the text of an editable combobox is changed the + textChanged() signal is emitted. + + When the user enters a new string in an editable combobox, the + widget may or may not insert it, and it can insert it in several + locations. The default policy is is \c AtBottom but you can change + this using setInsertionPolicy(). + + It is possible to constrain the input to an editable combobox + using TQValidator; see setValidator(). By default, any input is + accepted. + + If the combobox is not editable then it has a default + focusPolicy() of \c TabFocus, i.e. it will not grab focus if + clicked. This differs from both Windows and Motif. If the combobox + is editable then it has a default focusPolicy() of \c StrongFocus, + i.e. it will grab focus if clicked. + + A combobox can be populated using the insert functions, + insertStringList() and insertItem() for example. Items can be + changed with changeItem(). An item can be removed with + removeItem() and all items can be removed with clear(). The text + of the current item is returned by currentText(), and the text of + a numbered item is returned with text(). The current item can be + set with setCurrentItem() or setCurrentText(). The number of items + in the combobox is returned by count(); the maximum number of + items can be set with setMaxCount(). You can allow editing using + setEditable(). For editable comboboxes you can set auto-completion + using setAutoCompletion() and whether or not the user can add + duplicates is set with setDuplicatesEnabled(). + + <img src="qcombo1-m.png">(Motif 1, read-only)<br clear=all> + <img src="qcombo2-m.png">(Motif 2, editable)<br clear=all> + <img src="qcombo3-m.png">(Motif 2, read-only)<br clear=all> + <img src="qcombo1-w.png">(Windows style) + + Depending on the style, TQComboBox will use a TQListBox or a + TQPopupMenu to display the list of items. See setListBox() for + more information. + + \sa TQLineEdit TQListBox TQSpinBox TQRadioButton TQButtonGroup + \link guibooks.html#fowler GUI Design Handbook: Combo Box,\endlink + \link guibooks.html#fowler GUI Design Handbook: Drop-Down List Box.\endlink +*/ + + +/*! + \enum TQComboBox::Policy + + This enum specifies what the TQComboBox should do when a new string + is entered by the user. + + \value NoInsertion the string will not be inserted into the + combobox. + + \value AtTop insert the string as the first item in the combobox. + + \value AtCurrent replace the previously selected item with the + string the user has entered. + + \value AtBottom insert the string as the last item in the + combobox. + + \value AfterCurrent insert the string after the previously + selected item. + + \value BeforeCurrent insert the string before the previously + selected item. + + activated() is always emitted when the string is entered. + + If inserting the new string would cause the combobox to breach its + content size limit, the item at the other end of the list is + deleted. The definition of "other end" is + implementation-dependent. +*/ + + +/*! + \fn void TQComboBox::activated( int index ) + + This signal is emitted when a new item has been activated + (selected). The \a index is the position of the item in the + combobox. + + This signal is not emitted if the item is changed + programmatically, e.g. using setCurrentItem(). +*/ + +/*! + \overload void TQComboBox::activated( const TQString &string ) + + This signal is emitted when a new item has been activated + (selected). \a string is the selected string. + + You can also use the activated(int) signal, but be aware that its + argument is meaningful only for selected strings, not for user + entered strings. +*/ + +/*! + \fn void TQComboBox::highlighted( int index ) + + This signal is emitted when a new item has been set to be the + current item. The \a index is the position of the item in the + combobox. + + This signal is not emitted if the item is changed + programmatically, e.g. using setCurrentItem(). +*/ + +/*! + \overload void TQComboBox::highlighted( const TQString &string ) + + This signal is emitted when a new item has been set to be the + current item. \a string is the item's text. + + You can also use the highlighted(int) signal. +*/ + +/*! + \fn void TQComboBox::textChanged( const TQString &string ) + + This signal is used for editable comboboxes. It is emitted + whenever the contents of the text entry field changes. \a string + contains the new text. +*/ + +/*! + \property TQComboBox::autoCompletion + \brief whether auto-completion is enabled + + This property can only be set for editable comboboxes, for + non-editable comboboxes it has no effect. It is FALSE by default. +*/ + +/*! + \property TQComboBox::autoMask + \brief whether the combobox is automatically masked + + \sa TQWidget::setAutoMask() +*/ + +/*! \property TQComboBox::autoResize + \brief whether auto resize is enabled + \obsolete + + If this property is set to TRUE then the combobox will resize itself + whenever its contents change. The default is FALSE. +*/ + +/*! + \property TQComboBox::count + \brief the number of items in the combobox +*/ + +/*! + \property TQComboBox::currentItem + \brief the index of the current item in the combobox + + Note that the activated() and highlighted() signals are only + emitted when the user changes the current item, not when it is + changed programmatically. +*/ + +/*! + \property TQComboBox::currentText + \brief the text of the combobox's current item +*/ + +/*! + \property TQComboBox::duplicatesEnabled + \brief whether duplicates are allowed + + If the combobox is editable and the user enters some text in the + combobox's lineedit and presses Enter (and the insertionPolicy() + is not \c NoInsertion), then what happens is this: + \list + \i If the text is not already in the list, the text is inserted. + \i If the text is in the list and this property is TRUE (the + default), the text is inserted. + \i If the text is in the list and this property is FALSE, the text + is \e not inserted; instead the item which has matching text becomes + the current item. + \endlist + + This property only affects user-interaction. You can use + insertItem() to insert duplicates if you wish regardless of this + setting. +*/ + +/*! + \property TQComboBox::editable + \brief whether the combobox is editable + + This property's default is FALSE. Note that the combobox will be + cleared if this property is set to TRUE for a 1.x Motif style + combobox. To avoid this, use setEditable() before inserting any + items. Also note that the 1.x version of Motif didn't have any + editable comboboxes, so the combobox will change it's appearance + to a 2.0 style Motif combobox is it is set to be editable. +*/ + +/*! + \property TQComboBox::insertionPolicy + \brief the position of the items inserted by the user + + The default insertion policy is \c AtBottom. See \l Policy. +*/ + +/*! + \property TQComboBox::maxCount + \brief the maximum number of items allowed in the combobox +*/ + +/*! + \property TQComboBox::sizeLimit + \brief the maximum on-screen size of the combobox. + + This property is ignored for both Motif 1.x style and non-editable + comboboxes in Mac style. The default limit is ten + lines. If the number of items in the combobox is or grows larger + than lines, a scrollbar is added. +*/ + +class TQComboBoxPopup : public TQPopupMenu +{ +public: + TQComboBoxPopup( TQWidget *parent=0, const char *name=0 ) + : TQPopupMenu( parent, name ) + { + } + + int itemHeight( int index ) + { + return TQPopupMenu::itemHeight( index ); + } +}; + +static inline TQString escapedComboString(const TQString &str) +{ + TQString stringToReturn = str; + return stringToReturn.replace('&', "&&"); +} + +class TQComboBoxPopupItem : public TQCustomMenuItem +{ + TQListBoxItem *li; + TQSize sc; // Size cache optimization +public: + TQComboBoxPopupItem(TQListBoxItem *i) : TQCustomMenuItem(), li(i), sc(0, 0) { } + virtual bool fullSpan() const { return TRUE; } + virtual void paint( TQPainter*, const TQColorGroup&, bool, bool, int, int, int, int); + virtual TQSize sizeHint() { if (sc.isNull()) sc = TQSize(li->width(li->listBox()), TQMAX(25, li->height(li->listBox()))); return sc; } +}; +void TQComboBoxPopupItem::paint( TQPainter* p, const TQColorGroup&, bool, + bool, int x, int y, int, int) +{ + p->save(); + p->translate(x, y + ((sizeHint().height() / 2) - (li->height(li->listBox()) / 2))); + li->paint(p); + p->restore(); +} + + +class TQComboBoxData +{ +public: + TQComboBoxData( TQComboBox *cb ): ed( 0 ), usingLBox( FALSE ), pop( 0 ), lBox( 0 ), combo( cb ) + { + duplicatesEnabled = TRUE; + cb->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) ); + } + + inline bool usingListBox() { return usingLBox; } + inline TQListBox * listBox() { return lBox; } + inline TQComboBoxPopup * popup() { return pop; } + void updateLinedGeometry(); + void setListBox( TQListBox *l ); + void setPopupMenu( TQComboBoxPopup * pm, bool isPopup=TRUE ); + + int current; + int maxCount; + int sizeLimit; + TQComboBox::Policy p; + bool autoresize; + bool poppedUp; + bool mouseWasInsidePopup; + bool arrowPressed; + bool arrowDown; + bool discardNextMousePress; + bool shortClick; + bool useCompletion; + bool completeNow; + int completeAt; + bool duplicatesEnabled; + int fullHeight, currHeight; + + TQLineEdit * ed; // /bin/ed rules! + TQTimer *completionTimer; + + TQSize sizeHint; + +private: + bool usingLBox; + TQComboBoxPopup *pop; + TQListBox *lBox; + TQComboBox *combo; + +}; + +void TQComboBoxData::updateLinedGeometry() +{ + if ( !ed || !combo ) + return; + TQRect r = TQStyle::visualRect( combo->style().querySubControlMetrics(TQStyle::CC_ComboBox, combo, + TQStyle::SC_ComboBoxEditField), combo ); + + const TQPixmap *pix = current < combo->count() ? combo->pixmap( current ) : 0; + if ( pix && pix->width() < r.width() ) + r.setLeft( r.left() + pix->width() + 4 ); + if ( r != ed->geometry() ) + ed->setGeometry( r ); +} + +void TQComboBoxData::setListBox( TQListBox *l ) +{ + lBox = l; + usingLBox = TRUE; + l->setMouseTracking( TRUE ); +#ifdef Q_WS_X11 + l->x11SetWindowType( TQWidget::X11WindowTypeCombo ); + l->x11SetWindowTransient( combo->topLevelWidget()); +#endif +} + +void TQComboBoxData::setPopupMenu( TQComboBoxPopup * pm, bool isPopup ) +{ + pop = pm; + if(isPopup) + usingLBox = FALSE; +#ifdef Q_WS_X11 + if( pm ) { + pm->x11SetWindowType( TQWidget::X11WindowTypeCombo ); + pm->x11SetWindowTransient( combo->topLevelWidget()); + } +#endif +} + +static inline bool checkInsertIndex( const char *method, const char * name, + int count, int *index) +{ + bool range_err = (*index > count); +#if defined(QT_CHECK_RANGE) + if ( range_err ) + qWarning( "TQComboBox::%s: (%s) Index %d out of range", + method, name ? name : "<no name>", *index ); +#else + Q_UNUSED( method ) + Q_UNUSED( name ) +#endif + if ( *index < 0 ) // append + *index = count; + return !range_err; +} + + +static inline bool checkIndex( const char *method, const char * name, + int count, int index ) +{ + bool range_err = (index >= count); +#if defined(QT_CHECK_RANGE) + if ( range_err ) + qWarning( "TQComboBox::%s: (%s) Index %i out of range", + method, name ? name : "<no name>", index ); +#else + Q_UNUSED( method ) + Q_UNUSED( name ) +#endif + return !range_err; +} + + + +/*! + Constructs a combobox widget with parent \a parent called \a name. + + This constructor creates a popup list if the program uses Motif + (or Aqua) look and feel; this is compatible with Motif 1.x and + Aqua. + + Note: If you use this constructor to create your TQComboBox, then + the pixmap() function will always return 0. To workaround this, + use the other constructor. + +*/ + + + +TQComboBox::TQComboBox( TQWidget *parent, const char *name ) + : TQWidget( parent, name, WNoAutoErase ) +{ + d = new TQComboBoxData( this ); + if ( style().styleHint(TQStyle::SH_ComboBox_Popup, this) || + style().styleHint(TQStyle::SH_GUIStyle) == TQt::MotifStyle ) { + d->setPopupMenu( new TQComboBoxPopup( this, "in-combo" ) ); + d->popup()->setFont( font() ); + connect( d->popup(), SIGNAL(activated(int)), + SLOT(internalActivate(int)) ); + connect( d->popup(), SIGNAL(highlighted(int)), + SLOT(internalHighlight(int)) ); + } else { + setUpListBox(); + } + d->ed = 0; + d->current = 0; + d->maxCount = INT_MAX; + d->sizeLimit = 10; + d->p = AtBottom; + d->autoresize = FALSE; + d->poppedUp = FALSE; + d->arrowDown = FALSE; + d->arrowPressed = FALSE; + d->discardNextMousePress = FALSE; + d->shortClick = FALSE; + d->useCompletion = FALSE; + d->completeAt = 0; + d->completeNow = FALSE; + d->completionTimer = new TQTimer( this ); + + setFocusPolicy( TabFocus ); + setBackgroundMode( PaletteButton ); +} + + +/*! + Constructs a combobox with a maximum size and either Motif 2.0 or + Windows look and feel. + + The input field can be edited if \a rw is TRUE, otherwise the user + may only choose one of the items in the combobox. + + The \a parent and \a name arguments are passed on to the TQWidget + constructor. +*/ + + +TQComboBox::TQComboBox( bool rw, TQWidget *parent, const char *name ) + : TQWidget( parent, name, WNoAutoErase ) +{ + d = new TQComboBoxData( this ); + setUpListBox(); + + if(d->popup() && style().styleHint(TQStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = 0; + d->maxCount = INT_MAX; + setSizeLimit(10); + d->p = AtBottom; + d->autoresize = FALSE; + d->poppedUp = FALSE; + d->arrowDown = FALSE; + d->discardNextMousePress = FALSE; + d->shortClick = FALSE; + d->useCompletion = FALSE; + d->completeAt = 0; + d->completeNow = FALSE; + d->completionTimer = new TQTimer( this ); + + setFocusPolicy( StrongFocus ); + + d->ed = 0; + if ( rw ) + setUpLineEdit(); + setBackgroundMode( PaletteButton, PaletteBase ); +} + + + +/*! + Destroys the combobox. +*/ + +TQComboBox::~TQComboBox() +{ + delete d; +} + +void TQComboBox::setDuplicatesEnabled( bool enable ) +{ + d->duplicatesEnabled = enable; +} + +bool TQComboBox::duplicatesEnabled() const +{ + return d->duplicatesEnabled; +} + +int TQComboBox::count() const +{ + if ( d->usingListBox() ) + return d->listBox()->count(); + else + return d->popup()->count(); +} + + +/*! + \overload + + Inserts the \a list of strings at position \a index in the + combobox. + + This is only for compatibility since it does not support Unicode + strings. See insertStringList(). +*/ + +void TQComboBox::insertStrList( const TQStrList &list, int index ) +{ + insertStrList( &list, index ); +} + +/*! + \overload + + Inserts the \a list of strings at position \a index in the + combobox. + + This is only for compatibility since it does not support Unicode + strings. See insertStringList(). +*/ + +void TQComboBox::insertStrList( const TQStrList *list, int index ) +{ + if ( !list ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( list != 0 ); +#endif + return; + } + TQStrListIterator it( *list ); + const char* tmp; + if ( index < 0 ) + index = count(); + while ( (tmp=it.current()) ) { + ++it; + if ( d->usingListBox() ) + d->listBox()->insertItem( TQString::fromLatin1(tmp), index ); + else + d->popup()->insertItem( escapedComboString(TQString::fromLatin1(tmp)), index, index ); + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + } + if ( index != count() ) + reIndex(); +} + +/*! + Inserts the \a list of strings at position \a index in the + combobox. +*/ + +void TQComboBox::insertStringList( const TQStringList &list, int index ) +{ + TQStringList::ConstIterator it = list.begin(); + if ( index < 0 ) + index = count(); + while ( it != list.end() ) { + if ( d->usingListBox() ) + d->listBox()->insertItem( *it, index ); + else + d->popup()->insertItem( escapedComboString(*it), index, index ); + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + ++it; + } + if ( index != count() ) + reIndex(); +} + +/*! + Inserts the array of char * \a strings at position \a index in the + combobox. + + The \a numStrings argument is the number of strings. If \a + numStrings is -1 (default), the \a strings array must be + terminated with 0. + + Example: + \code + static const char* items[] = { "red", "green", "blue", 0 }; + combo->insertStrList( items ); + \endcode + + \sa insertStringList() +*/ + +void TQComboBox::insertStrList( const char **strings, int numStrings, int index) +{ + if ( !strings ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( strings != 0 ); +#endif + return; + } + if ( index < 0 ) + index = count(); + int i = 0; + while ( (numStrings<0 && strings[i]!=0) || i<numStrings ) { + if ( d->usingListBox() ) + d->listBox()->insertItem( TQString::fromLatin1(strings[i]), index ); + else + d->popup()->insertItem( escapedComboString(TQString::fromLatin1(strings[i])), index, index ); + i++; + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + } + if ( index != count() ) + reIndex(); +} + + +/*! + Inserts a text item with text \a t, at position \a index. The item + will be appended if \a index is negative. +*/ + +void TQComboBox::insertItem( const TQString &t, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( t, index ); + else + d->popup()->insertItem( escapedComboString(t), index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + +/*! + \overload + + Inserts a \a pixmap item at position \a index. The item will be + appended if \a index is negative. +*/ + +void TQComboBox::insertItem( const TQPixmap &pixmap, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( pixmap, index ); + else + d->popup()->insertItem( pixmap, index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + +/*! + \overload + + Inserts a \a pixmap item with additional text \a text at position + \a index. The item will be appended if \a index is negative. +*/ + +void TQComboBox::insertItem( const TQPixmap &pixmap, const TQString& text, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( pixmap, text, index ); + else + d->popup()->insertItem( pixmap, escapedComboString(text), index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( this->text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + + +/*! + Removes the item at position \a index. +*/ + +void TQComboBox::removeItem( int index ) +{ + int cnt = count(); + if ( !checkIndex( "removeItem", name(), cnt, index ) ) + return; + if ( d->usingListBox() ) { + if ( style().styleHint(TQStyle::SH_ComboBox_Popup, this) && d->popup() ) + d->popup()->removeItemAt( index ); + d->listBox()->removeItem( index ); + } else { + d->popup()->removeItemAt( index ); + } + if ( index != cnt-1 ) + reIndex(); + if ( index == d->current ) { + if ( d->ed ) { + TQString s = TQString::fromLatin1(""); + if (d->current < cnt - 1) + s = text( d->current ); + d->ed->setText( s ); + d->updateLinedGeometry(); + } + else { + if ( d->usingListBox() ) { + d->current = d->listBox()->currentItem(); + } else { + if (d->current > count()-1 && d->current > 0) + d->current--; + } + update(); + } + currentChanged(); + } + else { + if ( !d->ed ) { + if (d->current < cnt - 1) + setCurrentItem( d->current ); + else + setCurrentItem( d->current - 1 ); + } + } + +} + + +/*! + Removes all combobox items. +*/ + +void TQComboBox::clear() +{ + if ( d->usingListBox() ) { + if ( style().styleHint(TQStyle::SH_ComboBox_Popup, this) && d->popup() ) + d->popup()->clear(); + d->listBox()->resize( 0, 0 ); + d->listBox()->clear(); + } else { + d->popup()->clear(); + } + + if(d->popup() && style().styleHint(TQStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = 0; + if ( d->ed ) { + d->ed->setText( TQString::fromLatin1("") ); + d->updateLinedGeometry(); + } + currentChanged(); +} + + +TQString TQComboBox::currentText() const +{ + if ( d->ed ) + return d->ed->text(); + else if ( d->current < count() ) + return text( currentItem() ); + else + return TQString::null; +} + +void TQComboBox::setCurrentText( const TQString& txt ) +{ + int i; + for ( i = 0; i < count(); i++) + if ( text( i ) == txt ) + break; + if ( i < count() ) + setCurrentItem( i ); + else if ( d->ed ) + d->ed->setText( txt ); + else + changeItem( txt, currentItem() ); +} + + +/*! + Returns the text item at position \a index, or TQString::null if + the item is not a string. + + \sa currentText() +*/ + +TQString TQComboBox::text( int index ) const +{ + if ( !checkIndex( "text", name(), count(), index ) ) + return TQString::null; + if ( d->usingListBox() ) { + return d->listBox()->text( index ); + } else { + TQString retText = d->popup()->text(index); + retText.replace("&&", "&"); + return retText; + } +} + +/*! + Returns the pixmap item at position \a index, or 0 if the item is + not a pixmap. +*/ + +const TQPixmap *TQComboBox::pixmap( int index ) const +{ + if ( !checkIndex( "pixmap", name(), count(), index ) ) + return 0; + if ( d->usingListBox() ) + return d->listBox()->pixmap( index ); + else + return d->popup()->pixmap( index ); +} + +/*! + Replaces the item at position \a index with the text \a t. +*/ + +void TQComboBox::changeItem( const TQString &t, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( t, index ); + else + d->popup()->changeItem( t, index ); + if ( index == d->current ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } +} + +/*! + \overload + + Replaces the item at position \a index with the pixmap \a im, + unless the combobox is editable. + + \sa insertItem() +*/ + +void TQComboBox::changeItem( const TQPixmap &im, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( im, index ); + else + d->popup()->changeItem( im, index ); + if ( index == d->current ) + update(); +} + +/*! + \overload + + Replaces the item at position \a index with the pixmap \a im and + the text \a t. + + \sa insertItem() +*/ + +void TQComboBox::changeItem( const TQPixmap &im, const TQString &t, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( im, t, index ); + else + d->popup()->changeItem( im, t, index ); + if ( index == d->current ) + update(); +} + + +int TQComboBox::currentItem() const +{ + return d->current; +} + +void TQComboBox::setCurrentItem( int index ) +{ + if ( index == d->current && !d->ed ) { + return; + } + if ( !checkIndex( "setCurrentItem", name(), count(), index ) ) { + return; + } + + if ( d->usingListBox() && !( listBox()->item(index) && listBox()->item(index)->isSelectable() ) ) + return; + + if(d->popup() && style().styleHint(TQStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = index; + d->completeAt = 0; + if ( d->ed ) { + d->ed->setText( text( index ) ); + d->updateLinedGeometry(); + } + // ### We want to keep ListBox's currentItem in sync, even if NOT popuped... + if ( d->usingListBox() && d->listBox() ) { + d->listBox()->setCurrentItem( index ); + } else { + internalHighlight( index ); + // internalActivate( index ); ### this leads to weird behavior, as in 3.0.1 + } + + currentChanged(); +} + +bool TQComboBox::autoResize() const +{ + return d->autoresize; +} + +void TQComboBox::setAutoResize( bool enable ) +{ + if ( (bool)d->autoresize != enable ) { + d->autoresize = enable; + if ( enable ) + adjustSize(); + } +} + + +/*! + \reimp + + This implementation caches the size hint to avoid resizing when + the contents change dynamically. To invalidate the cached value + call setFont(). +*/ +TQSize TQComboBox::sizeHint() const +{ + if ( isVisible() && d->sizeHint.isValid() ) + return d->sizeHint; + + constPolish(); + int i, w; + TQFontMetrics fm = fontMetrics(); + + int maxW = count() ? 18 : 7 * fm.width(TQChar('x')) + 18; + int maxH = TQMAX( fm.lineSpacing(), 14 ) + 2; + + if ( !d->usingListBox() ) { + w = d->popup()->sizeHint().width() - 2* d->popup()->frameWidth(); + if ( w > maxW ) + maxW = w; + } else { + for( i = 0; i < count(); i++ ) { + w = d->listBox()->item( i )->width( d->listBox() ); + if ( w > maxW ) + maxW = w; + } + } + + d->sizeHint = (style().sizeFromContents(TQStyle::CT_ComboBox, this, + TQSize(maxW, maxH)). + expandedTo(TQApplication::globalStrut())); + + return d->sizeHint; +} + + +/*! + \internal + Receives activated signals from an internal popup list and emits + the activated() signal. +*/ + +void TQComboBox::internalActivate( int index ) +{ + if ( d->current != index ) { + if ( !d->usingListBox() || listBox()->item( index )->isSelectable() ) { + if(d->popup() && style().styleHint(TQStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = index; + currentChanged(); + } + } + if ( d->usingListBox() ) + popDownListBox(); + else + d->popup()->removeEventFilter( this ); + d->poppedUp = FALSE; + + TQString t( text( index ) ); + if ( d->ed ) { + d->ed->setText( t ); + d->updateLinedGeometry(); + } + emit activated( index ); + emit activated( t ); +} + +/*! + \internal + Receives highlighted signals from an internal popup list and emits + the highlighted() signal. +*/ + +void TQComboBox::internalHighlight( int index ) +{ + emit highlighted( index ); + TQString t = text( index ); + if ( !t.isNull() ) + emit highlighted( t ); +} + +/*! + \internal + Receives timeouts after a click. Used to decide if a Motif style + popup should stay up or not after a click. +*/ +void TQComboBox::internalClickTimeout() +{ + d->shortClick = FALSE; +} + +/*! + Sets the palette for both the combobox button and the combobox + popup list to \a palette. +*/ + +void TQComboBox::setPalette( const TQPalette &palette ) +{ + TQWidget::setPalette( palette ); + if ( d->listBox() ) + d->listBox()->setPalette( palette ); + if ( d->popup() ) + d->popup()->setPalette( palette ); +} + +/*! + Sets the font for both the combobox button and the combobox popup + list to \a font. +*/ + +void TQComboBox::setFont( const TQFont &font ) +{ + d->sizeHint = TQSize(); // invalidate size hint + TQWidget::setFont( font ); + if ( d->usingListBox() ) + d->listBox()->setFont( font ); + else + d->popup()->setFont( font ); + if (d->ed) + d->ed->setFont( font ); + if ( d->autoresize ) + adjustSize(); +} + + +/*!\reimp +*/ + +void TQComboBox::resizeEvent( TQResizeEvent * e ) +{ + if ( d->ed ) + d->updateLinedGeometry(); + if ( d->listBox() ) + d->listBox()->resize( width(), d->listBox()->height() ); + TQWidget::resizeEvent( e ); +} + +/*!\reimp +*/ + +void TQComboBox::paintEvent( TQPaintEvent * ) +{ + TQPainter p( this ); + const TQColorGroup & g = colorGroup(); + p.setPen(g.text()); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + + if ( width() < 5 || height() < 5 ) { + qDrawShadePanel( &p, rect(), g, FALSE, 2, + &g.brush( TQColorGroup::Button ) ); + return; + } + + bool reverse = TQApplication::reverseLayout(); + if ( !d->usingListBox() && + style().styleHint(TQStyle::SH_GUIStyle) == TQt::MotifStyle) { // motif 1.x style + int dist, buttonH, buttonW; + dist = 8; + buttonH = 7; + buttonW = 11; + int xPos; + int x0; + int w = width() - dist - buttonW - 1; + if ( reverse ) { + xPos = dist + 1; + x0 = xPos + 4; + } else { + xPos = w; + x0 = 4; + } + qDrawShadePanel( &p, rect(), g, FALSE, + style().pixelMetric(TQStyle::PM_DefaultFrameWidth, this), + &g.brush( TQColorGroup::Button ) ); + qDrawShadePanel( &p, xPos, (height() - buttonH)/2, + buttonW, buttonH, g, FALSE, + style().pixelMetric(TQStyle::PM_DefaultFrameWidth, this) ); + TQRect clip( x0, 2, w - 2 - 4 - 5, height() - 4 ); + TQString str = d->popup()->text( this->d->current ); + if ( !str.isNull() ) { + p.drawText( clip, AlignCenter | SingleLine, str ); + } + + TQPixmap *pix = d->popup()->pixmap( this->d->current ); + TQIconSet *iconSet = d->popup()->iconSet( this->d->current ); + if (pix || iconSet) { + TQPixmap pm = ( pix ? *pix : iconSet->pixmap() ); + p.setClipRect( clip ); + p.drawPixmap( 4, (height()-pm.height())/2, pm ); + p.setClipping( FALSE ); + } + + if ( hasFocus() ) + p.drawRect( xPos - 5, 4, width() - xPos + 1 , height() - 8 ); + } else if(!d->usingListBox()) { + style().drawComplexControl( TQStyle::CC_ComboBox, &p, this, rect(), g, + flags, (uint)TQStyle::SC_All, + (d->arrowDown ? + TQStyle::SC_ComboBoxArrow : + TQStyle::SC_None )); + + TQRect re = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxEditField ); + re = TQStyle::visualRect(re, this); + p.setClipRect( re ); + + TQString str = d->popup()->text( this->d->current ); + TQPixmap *pix = d->popup()->pixmap( this->d->current ); + if ( !str.isNull() ) { + p.save(); + p.setFont(font()); + TQFontMetrics fm(font()); + int x = re.x(), y = re.y() + fm.ascent(); + if( pix ) + x += pix->width() + 5; + p.drawText( x, y, str ); + p.restore(); + } + if ( pix ) { + p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(), + colorGroup().brush( TQColorGroup::Base ) ); + p.drawPixmap( re.x() + 2, re.y() + + ( re.height() - pix->height() ) / 2, *pix ); + } + } else { + style().drawComplexControl( TQStyle::CC_ComboBox, &p, this, rect(), g, + flags, (uint)TQStyle::SC_All, + (d->arrowDown ? + TQStyle::SC_ComboBoxArrow : + TQStyle::SC_None )); + + TQRect re = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxEditField ); + re = TQStyle::visualRect(re, this); + p.setClipRect( re ); + + if ( !d->ed ) { + TQListBoxItem * item = d->listBox()->item( d->current ); + if ( item ) { + int itemh = item->height( d->listBox() ); + p.translate( re.x(), re.y() + (re.height() - itemh)/2 ); + item->paint( &p ); + } + } else if ( d->listBox() && d->listBox()->item( d->current ) ) { + TQListBoxItem * item = d->listBox()->item( d->current ); + const TQPixmap *pix = item->pixmap(); + if ( pix ) { + p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(), + colorGroup().brush( TQColorGroup::Base ) ); + p.drawPixmap( re.x() + 2, re.y() + + ( re.height() - pix->height() ) / 2, *pix ); + } + } + p.setClipping( FALSE ); + } +} + + +/*!\reimp +*/ + +void TQComboBox::mousePressEvent( TQMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + if ( d->discardNextMousePress ) { + d->discardNextMousePress = FALSE; + return; + } + TQRect arrowRect = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxArrow); + arrowRect = TQStyle::visualRect(arrowRect, this); + + // Correction for motif style, where arrow is smaller + // and thus has a rect that doesn't fit the button. + arrowRect.setHeight( TQMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); + + if ( count() && ( !editable() || arrowRect.contains( e->pos() ) ) ) { + d->arrowPressed = FALSE; + + if ( d->usingListBox() ) { + listBox()->blockSignals( TRUE ); + qApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll + listBox()->setCurrentItem(d->current); + listBox()->blockSignals( FALSE ); + popup(); + if ( arrowRect.contains( e->pos() ) ) { + d->arrowPressed = TRUE; + d->arrowDown = TRUE; + repaint( FALSE ); + } + } else { + popup(); + } + TQTimer::singleShot( 200, this, SLOT(internalClickTimeout())); + d->shortClick = TRUE; + } +} + +/*!\reimp +*/ + +void TQComboBox::mouseMoveEvent( TQMouseEvent * ) +{ +} + +/*!\reimp +*/ + +void TQComboBox::mouseReleaseEvent( TQMouseEvent * ) +{ +} + +/*!\reimp +*/ + +void TQComboBox::mouseDoubleClickEvent( TQMouseEvent *e ) +{ + mousePressEvent( e ); +} + + +/*!\reimp +*/ + +void TQComboBox::keyPressEvent( TQKeyEvent *e ) +{ + int c = currentItem(); + if ( ( e->key() == Key_F4 && e->state() == 0 ) || + ( e->key() == Key_Down && (e->state() & AltButton) ) || + ( !d->ed && e->key() == Key_Space ) ) { + if ( count() ) { + if ( !d->usingListBox() ) + d->popup()->setActiveItem( this->d->current ); + popup(); + } + return; + } else if ( d->usingListBox() && e->key() == Key_Up ) { + if ( c > 0 ) + setCurrentItem( c-1 ); + } else if ( d->usingListBox() && e->key() == Key_Down ) { + if ( ++c < count() ) + setCurrentItem( c ); + } else if ( d->usingListBox() && e->key() == Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) { + setCurrentItem( 0 ); + } else if ( d->usingListBox() && e->key() == Key_End && ( !d->ed || !d->ed->hasFocus() ) ) { + setCurrentItem( count()-1 ); + } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) { + if ( !d->completionTimer->isActive() ) { + d->completeAt = 0; + c = completionIndex( e->text(), ++c ); + if ( c >= 0 ) { + setCurrentItem( c ); + d->completeAt = e->text().length(); + } + } else { + d->completionTimer->stop(); + TQString ct = currentText().left( d->completeAt ) + e->text(); + c = completionIndex( ct, c ); + if ( c < 0 && d->completeAt > 0 ) { + c = completionIndex( e->text(), 0 ); + ct = e->text(); + } + d->completeAt = 0; + if ( c >= 0 ) { + setCurrentItem( c ); + d->completeAt = ct.length(); + } + } + d->completionTimer->start( 400, TRUE ); + } else { + e->ignore(); + return; + } + + c = currentItem(); + if ( count() && !text( c ).isNull() ) + emit activated( text( c ) ); + emit activated( c ); +} + + +/*!\reimp +*/ + +void TQComboBox::focusInEvent( TQFocusEvent * e ) +{ + TQWidget::focusInEvent( e ); + d->completeNow = FALSE; + d->completeAt = 0; +} + +/*!\reimp +*/ + +void TQComboBox::focusOutEvent( TQFocusEvent * e ) +{ + TQWidget::focusOutEvent( e ); + d->completeNow = FALSE; + d->completeAt = 0; +} + +/*!\reimp +*/ +#ifndef QT_NO_WHEELEVENT +void TQComboBox::wheelEvent( TQWheelEvent *e ) +{ + if ( d->poppedUp ) { + if ( d->usingListBox() ) { + TQApplication::sendEvent( d->listBox(), e ); + } + } else { + if ( e->delta() > 0 ) { + int c = currentItem(); + if ( c > 0 ) { + setCurrentItem( c-1 ); + emit activated( currentItem() ); + emit activated( currentText() ); + } + } else { + int c = currentItem(); + if ( ++c < count() ) { + setCurrentItem( c ); + emit activated( currentItem() ); + emit activated( currentText() ); + } + } + e->accept(); + } +} +#endif + +/*! + \internal + Calculates the listbox height needed to contain all items, or as + many as the list box is supposed to contain. +*/ +static int listHeight( TQListBox *l, int sl ) +{ + if ( l->count() > 0 ) + return TQMIN( l->count(), (uint)sl) * l->item( 0 )->height(l); + else + return l->sizeHint().height(); +} + + +/*! + Pops up the combobox popup list. + + If the list is empty, no items appear. +*/ + +void TQComboBox::popup() +{ + if ( !count() || d->poppedUp ) + return; + + if( !d->usingListBox() || style().styleHint(TQStyle::SH_ComboBox_Popup, this) ) { + if(d->usingListBox()) { + if(!d->popup()) { + TQComboBoxPopup *p = new TQComboBoxPopup( this, "in-combo" ); + d->setPopupMenu( p, FALSE ); + p->setFont( font() ); + connect( p, SIGNAL(activated(int)), SLOT(internalActivate(int)) ); + connect( p, SIGNAL(highlighted(int)), SLOT(internalHighlight(int)) ); + } + d->popup()->clear(); + for(unsigned int i = 0; i < d->listBox()->count(); i++) { + TQListBoxItem *item = d->listBox()->item(i); + if(item->rtti() == TQListBoxText::RTTI) { + d->popup()->insertItem(escapedComboString(item->text()), i, i); + } else if(item->rtti() == TQListBoxPixmap::RTTI) { + if(item->pixmap()) + d->popup()->insertItem(TQIconSet(*item->pixmap()), escapedComboString(item->text()), i, i); + else + d->popup()->insertItem(escapedComboString(item->text()), i, i); + } else { + d->popup()->insertItem(new TQComboBoxPopupItem(item), i, i); + } + } + } + d->popup()->installEventFilter( this ); + if(d->popup() && style().styleHint(TQStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(this->d->current, TRUE); + d->popup()->popup( mapToGlobal( TQPoint(0,0) ), this->d->current ); + update(); + } else { + // Send all listbox events to eventFilter(): + TQListBox* lb = d->listBox(); + lb->triggerUpdate( TRUE ); + lb->installEventFilter( this ); + d->mouseWasInsidePopup = FALSE; + int w = lb->variableWidth() ? lb->sizeHint().width() : width(); + int h = listHeight( lb, d->sizeLimit ) + 2; + TQRect screen = TQApplication::desktop()->availableGeometry( this ); + + int sx = screen.x(); // screen pos + int sy = screen.y(); + int sw = screen.width(); // screen width + int sh = screen.height(); // screen height + TQPoint pos = mapToGlobal( TQPoint(0,height()) ); + // ## Similar code is in TQPopupMenu + int x = pos.x(); + int y = pos.y(); + + // the complete widget must be visible + if ( x + w > sx + sw ) + x = sx+sw - w; + if ( x < sx ) + x = sx; + if (y + h > sy+sh && y - h - height() >= 0 ) + y = y - h - height(); + + TQRect rect = + style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxListBoxPopup, + TQStyleOption( x, y, w, h ) ); + // work around older styles that don't implement the combobox + // listbox popup subcontrol + if ( rect.isNull() ) + rect.setRect( x, y, w, h ); + lb->setGeometry( rect ); + + lb->raise(); + bool block = lb->signalsBlocked(); + lb->blockSignals( TRUE ); + TQListBoxItem* currentLBItem = 0; + if ( editable() && currentText() != text( currentItem() ) ) + currentLBItem = lb->findItem( currentText() ); + + currentLBItem = currentLBItem ? currentLBItem : lb->item( d->current ); + + lb->setCurrentItem( currentLBItem ); + lb->setContentsPos( lb->contentsX(), + lb->viewportToContents( lb->itemRect( currentLBItem ).topLeft() ).y() ); + + // set the current item to also be the selected item if it isn't already + if ( currentLBItem && currentLBItem->isSelectable() && !currentLBItem->isSelected() ) + lb->setSelected( currentLBItem, TRUE ); + lb->blockSignals( block ); + lb->setVScrollBarMode(TQScrollView::Auto); + +#ifndef QT_NO_EFFECTS + if ( TQApplication::isEffectEnabled( UI_AnimateCombo ) ) { + if ( lb->y() < mapToGlobal(TQPoint(0,0)).y() ) + qScrollEffect( lb, TQEffects::UpScroll ); + else + qScrollEffect( lb ); + } else +#endif + lb->show(); + } + d->poppedUp = TRUE; +} + + +/*! + \reimp +*/ +void TQComboBox::updateMask() +{ + TQBitmap bm( size() ); + bm.fill( color0 ); + + { + TQPainter p( &bm, this ); + style().drawComplexControlMask(TQStyle::CC_ComboBox, &p, this, rect()); + } + + setMask( bm ); +} + +/*! + \internal + Pops down (removes) the combobox popup list box. +*/ +void TQComboBox::popDownListBox() +{ + Q_ASSERT( d->usingListBox() ); + d->listBox()->removeEventFilter( this ); + d->listBox()->viewport()->removeEventFilter( this ); + d->listBox()->hide(); + d->listBox()->setCurrentItem( d->current ); + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + d->poppedUp = FALSE; +} + + +/*! + \internal + Re-indexes the identifiers in the popup list. +*/ + +void TQComboBox::reIndex() +{ + if ( !d->usingListBox() ) { + int cnt = count(); + while ( cnt-- ) + d->popup()->setId( cnt, cnt ); + } +} + +/*! + \internal + Repaints the combobox. +*/ + +void TQComboBox::currentChanged() +{ + if ( d->autoresize ) + adjustSize(); + update(); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif +} + +/*! \reimp + + \internal + + The event filter steals events from the popup or listbox when they + are popped up. It makes the popup stay up after a short click in + motif style. In windows style it toggles the arrow button of the + combobox field, and activates an item and takes down the listbox + when the mouse button is released. +*/ + +bool TQComboBox::eventFilter( TQObject *object, TQEvent *event ) +{ + if ( !event ) + return TRUE; + else if ( object == d->ed ) { + if ( event->type() == TQEvent::KeyPress ) { + bool isAccepted = ( (TQKeyEvent*)event )->isAccepted(); + keyPressEvent( (TQKeyEvent *)event ); + if ( ((TQKeyEvent *)event)->isAccepted() ) { + d->completeNow = FALSE; + return TRUE; + } else if ( ((TQKeyEvent *)event)->key() != Key_End ) { + d->completeNow = TRUE; + d->completeAt = d->ed->cursorPosition(); + } + if ( isAccepted ) + ( (TQKeyEvent*)event )->accept(); + else + ( (TQKeyEvent*)event )->ignore(); + } else if ( event->type() == TQEvent::KeyRelease ) { + keyReleaseEvent( (TQKeyEvent *)event ); + return ((TQKeyEvent *)event)->isAccepted(); + } else if ( event->type() == TQEvent::FocusIn ) { + focusInEvent( (TQFocusEvent *)event ); + } else if ( event->type() == TQEvent::FocusOut ) { + focusOutEvent( (TQFocusEvent *)event ); + } else if ( d->useCompletion && d->completeNow ) { + d->completeNow = FALSE; + if ( !d->ed->text().isNull() && + d->ed->cursorPosition() > d->completeAt && + d->ed->cursorPosition() == (int)d->ed->text().length() ) { + TQString ct( d->ed->text() ); + int i = completionIndex( ct, currentItem() ); + if ( i > -1 ) { + TQString it = text( i ); + d->ed->validateAndSet( it, ct.length(), + ct.length(), it.length() ); + d->current = i; + // ### sets current item without emitting signals. This is to + // make sure the right item is current if you change current with + // wheel/up/down. While typing current is not valid anyway. Fix properly + // in 4.0. + } + } + } + } else if ( d->usingListBox() && ( object == d->listBox() || + object == d->listBox()->viewport() )) { + TQMouseEvent *e = (TQMouseEvent*)event; + switch( event->type() ) { + case TQEvent::MouseMove: + if ( !d->mouseWasInsidePopup ) { + TQPoint pos = e->pos(); + if ( d->listBox()->rect().contains( pos ) ) + d->mouseWasInsidePopup = TRUE; + // Check if arrow button should toggle + if ( d->arrowPressed ) { + TQPoint comboPos; + comboPos = mapFromGlobal( d->listBox()->mapToGlobal(pos) ); + TQRect arrowRect = + style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxArrow); + arrowRect = TQStyle::visualRect(arrowRect, this); + if ( arrowRect.contains( comboPos ) ) { + if ( !d->arrowDown ) { + d->arrowDown = TRUE; + repaint( FALSE ); + } + } else { + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + } + } + } else if ((e->state() & ( RightButton | LeftButton | MidButton ) ) == 0 && + style().styleHint(TQStyle::SH_ComboBox_ListMouseTracking, this)) { + TQWidget *mouseW = TQApplication::widgetAt( e->globalPos(), TRUE ); + if ( mouseW == d->listBox()->viewport() ) { //### + TQMouseEvent m( TQEvent::MouseMove, e->pos(), e->globalPos(), + LeftButton, LeftButton ); + TQApplication::sendEvent( object, &m ); //### Evil + return TRUE; + } + } + + break; + case TQEvent::MouseButtonRelease: + if ( d->listBox()->rect().contains( e->pos() ) ) { + TQMouseEvent tmp( TQEvent::MouseButtonDblClick, + e->pos(), e->button(), e->state() ) ; + // will hide popup + TQApplication::sendEvent( object, &tmp ); + return TRUE; + } else { + if ( d->mouseWasInsidePopup ) { + popDownListBox(); + } else { + d->arrowPressed = FALSE; + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + } + } + break; + case TQEvent::MouseButtonDblClick: + case TQEvent::MouseButtonPress: + if ( !d->listBox()->rect().contains( e->pos() ) ) { + TQPoint globalPos = d->listBox()->mapToGlobal(e->pos()); + if ( TQApplication::widgetAt( globalPos, TRUE ) == this ) { + d->discardNextMousePress = TRUE; + // avoid popping up again + } + popDownListBox(); + return TRUE; + } + break; + case TQEvent::KeyPress: + switch( ((TQKeyEvent *)event)->key() ) { + case Key_Up: + case Key_Down: + if ( !(((TQKeyEvent *)event)->state() & AltButton) ) + break; + case Key_F4: + case Key_Escape: + if ( d->poppedUp ) { + popDownListBox(); + return TRUE; + } + break; + case Key_Enter: + case Key_Return: + // work around TQDialog's enter handling + return FALSE; + default: + break; + } + break; + case TQEvent::Hide: + popDownListBox(); + break; + default: + break; + } + } else if ( (!d->usingListBox() || style().styleHint(TQStyle::SH_ComboBox_Popup, this)) && + object == d->popup() ) { + TQMouseEvent *e = (TQMouseEvent*)event; + switch ( event->type() ) { + case TQEvent::MouseButtonRelease: + if ( d->shortClick ) { + TQMouseEvent tmp( TQEvent::MouseMove, + e->pos(), e->button(), e->state() ) ; + // highlight item, but don't pop down: + TQApplication::sendEvent( object, &tmp ); + return TRUE; + } + break; + case TQEvent::MouseButtonDblClick: + case TQEvent::MouseButtonPress: + if ( !d->popup()->rect().contains( e->pos() ) ) { + d->poppedUp = FALSE; + d->arrowDown = FALSE; + // remove filter, event will take down popup: + d->popup()->removeEventFilter( this ); + // ### uglehack! + // call internalHighlight so the highlighed signal + // will be emitted at least as often as necessary. + // it may be called more often than necessary + internalHighlight( d->current ); + } + break; + case TQEvent::Hide: + d->poppedUp = FALSE; + break; + default: + break; + } + } + return TQWidget::eventFilter( object, event ); +} + + +/*! + Returns the index of the first item \e after \a startingAt of + which \a prefix is a case-insensitive prefix. Returns -1 if no + items start with \a prefix. +*/ + +int TQComboBox::completionIndex( const TQString & prefix, + int startingAt = 0 ) const +{ + int start = startingAt; + if ( start < 0 || start >= count() ) + start = 0; + if ( start >= count() ) + return -1; + TQString match = prefix.lower(); + if ( match.length() < 1 ) + return start; + + TQString current; + int i = start; + do { + current = text( i ).lower(); + if ( current.startsWith( match ) ) + return i; + i++; + if ( i == count() ) + i = 0; + } while ( i != start ); + return -1; +} + + +int TQComboBox::sizeLimit() const +{ + return d ? d->sizeLimit : INT_MAX; +} + +void TQComboBox::setSizeLimit( int lines ) +{ + d->sizeLimit = lines; +} + + +int TQComboBox::maxCount() const +{ + return d ? d->maxCount : INT_MAX; +} + +void TQComboBox::setMaxCount( int count ) +{ + int l = this->count(); + while( --l > count ) + removeItem( l ); + d->maxCount = count; +} + +TQComboBox::Policy TQComboBox::insertionPolicy() const +{ + return d->p; +} + +void TQComboBox::setInsertionPolicy( Policy policy ) +{ + d->p = policy; +} + + + +/*! + Internal slot to keep the line editor up to date. +*/ + +void TQComboBox::returnPressed() +{ + TQString s( d->ed->text() ); + + if ( s.isEmpty() ) + return; + + int c = 0; + bool doInsert = TRUE; + if ( !d->duplicatesEnabled ) { + for ( int i = 0; i < count(); ++i ) { + if ( s == text( i ) ) { + doInsert = FALSE; + c = i; + break; + } + } + } + + if ( doInsert ) { + if ( insertionPolicy() != NoInsertion ) { + int cnt = count(); + while ( cnt >= d->maxCount ) { + removeItem( --cnt ); + } + } + + switch ( insertionPolicy() ) { + case AtCurrent: + if (count() == 0) + insertItem(s); + else if ( s != text( currentItem() ) ) + changeItem( s, currentItem() ); + emit activated( currentItem() ); + emit activated( s ); + return; + case NoInsertion: + emit activated( s ); + return; + case AtTop: + c = 0; + break; + case AtBottom: + c = count(); + break; + case BeforeCurrent: + c = currentItem(); + break; + case AfterCurrent: + c = count() == 0 ? 0 : currentItem() + 1; + break; + } + insertItem( s, c ); + } + + setCurrentItem( c ); + emit activated( c ); + emit activated( s ); +} + + +/*! \reimp +*/ + +void TQComboBox::setEnabled( bool enable ) +{ + if ( !enable ) { + if ( d->usingListBox() ) { + popDownListBox(); + } else { + d->popup()->removeEventFilter( this ); + d->popup()->close(); + d->poppedUp = FALSE; + } + } + TQWidget::setEnabled( enable ); +} + + + +/*! + Applies the validator \a v to the combobox so that only text which + is valid according to \a v is accepted. + + This function does nothing if the combobox is not editable. + + \sa validator() clearValidator() TQValidator +*/ + +void TQComboBox::setValidator( const TQValidator * v ) +{ + if ( d && d->ed ) + d->ed->setValidator( v ); +} + + +/*! + Returns the validator which constrains editing for this combobox + if there is one; otherwise returns 0. + + \sa setValidator() clearValidator() TQValidator +*/ + +const TQValidator * TQComboBox::validator() const +{ + return d && d->ed ? d->ed->validator() : 0; +} + + +/*! + This slot is equivalent to setValidator( 0 ). +*/ + +void TQComboBox::clearValidator() +{ + if ( d && d->ed ) + d->ed->setValidator( 0 ); +} + + +/*! + Sets the combobox to use \a newListBox instead of the current list + box or popup. As a side effect, it clears the combobox of its + current contents. + + \warning TQComboBox assumes that newListBox->text(n) returns + non-null for 0 \<= n \< newListbox->count(). This assumption is + necessary because of the line edit in TQComboBox. +*/ + +void TQComboBox::setListBox( TQListBox * newListBox ) +{ + clear(); + + if ( d->usingListBox() ) { + delete d->listBox(); + } else { + delete d->popup(); + d->setPopupMenu(0, FALSE); + } + + newListBox->reparent( this, WType_Popup, TQPoint(0,0), FALSE ); + d->setListBox( newListBox ); + d->listBox()->setFont( font() ); + d->listBox()->setPalette( palette() ); + d->listBox()->setVScrollBarMode(TQScrollView::AlwaysOff); + d->listBox()->setHScrollBarMode(TQScrollView::AlwaysOff); + d->listBox()->setFrameStyle( TQFrame::Box | TQFrame::Plain ); + d->listBox()->setLineWidth( 1 ); + d->listBox()->resize( 100, 10 ); + + connect( d->listBox(), SIGNAL(selected(int)), + SLOT(internalActivate(int)) ); + connect( d->listBox(), SIGNAL(highlighted(int)), + SLOT(internalHighlight(int))); +} + + +/*! + Returns the current list box, or 0 if there is no list box. + (TQComboBox can use TQPopupMenu instead of TQListBox.) Provided to + match setListBox(). + + \sa setListBox() +*/ + +TQListBox * TQComboBox::listBox() const +{ + return d && d->usingListBox() ? d->listBox() : 0; +} + +/*! + Returns the line edit, or 0 if there is no line edit. + + Only editable listboxes have a line editor. +*/ +TQLineEdit* TQComboBox::lineEdit() const +{ + return d->ed; +} + + + +/*! + Clears the line edit without changing the combobox's contents. + Does nothing if the combobox isn't editable. + + This is particularly useful when using a combobox as a line edit + with history. For example you can connect the combobox's + activated() signal to clearEdit() in order to present the user + with a new, empty line as soon as Enter is pressed. + + \sa setEditText() +*/ + +void TQComboBox::clearEdit() +{ + if ( d && d->ed ) + d->ed->clear(); +} + + +/*! + Sets the text in the line edit to \a newText without changing the + combobox's contents. Does nothing if the combobox isn't editable. + + This is useful e.g. for providing a good starting point for the + user's editing and entering the change in the combobox only when + the user presses Enter. + + \sa clearEdit() insertItem() +*/ + +void TQComboBox::setEditText( const TQString &newText ) +{ + if ( d && d->ed ) { + d->updateLinedGeometry(); + d->ed->setText( newText ); + } +} + +void TQComboBox::setAutoCompletion( bool enable ) +{ + d->useCompletion = enable; + d->completeNow = FALSE; +} + + +bool TQComboBox::autoCompletion() const +{ + return d->useCompletion; +} + +/*!\reimp + */ +void TQComboBox::styleChange( TQStyle& s ) +{ + d->sizeHint = TQSize(); // invalidate size hint... + if ( d->ed ) + d->updateLinedGeometry(); + TQWidget::styleChange( s ); +} + +bool TQComboBox::editable() const +{ + return d->ed != 0; +} + +void TQComboBox::setEditable( bool y ) +{ + if ( y == editable() ) + return; + if ( y ) { + if ( !d->usingListBox() ) + setUpListBox(); + setUpLineEdit(); + d->ed->show(); + if ( currentItem() ) + setEditText( currentText() ); + } else { + delete d->ed; + d->ed = 0; + } + + setFocusPolicy( StrongFocus ); + updateGeometry(); + update(); +} + + +void TQComboBox::setUpListBox() +{ + d->setListBox( new TQListBox( this, "in-combo", WType_Popup ) ); + d->listBox()->setFont( font() ); + d->listBox()->setPalette( palette() ); + d->listBox()->setVScrollBarMode( TQListBox::AlwaysOff ); + d->listBox()->setHScrollBarMode( TQListBox::AlwaysOff ); + d->listBox()->setFrameStyle( TQFrame::Box | TQFrame::Plain ); + d->listBox()->setLineWidth( 1 ); + d->listBox()->resize( 100, 10 ); + + connect( d->listBox(), SIGNAL(selected(int)), + SLOT(internalActivate(int)) ); + connect( d->listBox(), SIGNAL(highlighted(int)), + SLOT(internalHighlight(int))); +} + + +void TQComboBox::setUpLineEdit() +{ + if ( !d->ed ) + setLineEdit( new TQLineEdit( this, "combo edit" ) ); +} + +/*! + Sets the line edit to use \a edit instead of the current line edit. +*/ + +void TQComboBox::setLineEdit( TQLineEdit *edit ) +{ + if ( !edit ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( edit != 0 ); +#endif + return; + } + + edit->setText( currentText() ); + delete d->ed; + d->ed = edit; + + if ( edit->parent() != this ) + edit->reparent( this, TQPoint(0,0), FALSE ); + + connect (edit, SIGNAL( textChanged(const TQString&) ), + this, SIGNAL( textChanged(const TQString&) ) ); + connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) ); + + edit->setFrame( FALSE ); + d->updateLinedGeometry(); + edit->installEventFilter( this ); + setFocusProxy( edit ); + setFocusPolicy( StrongFocus ); + setInputMethodEnabled( TRUE ); + + if ( !d->usingListBox() ) + setUpListBox(); + + if ( isVisible() ) + edit->show(); + + updateGeometry(); + update(); +} + +/*! + \reimp +*/ +void TQComboBox::hide() +{ + TQWidget::hide(); + + if (listBox()) + listBox()->hide(); + else if (d->popup()) + d->popup()->hide(); +} + +#endif // QT_NO_COMBOBOX |