You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tqt3/src/widgets/qcombobox.cpp

2335 lines
59 KiB

/**********************************************************************
**
** 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 requirements 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 "ntqcombobox.h"
#ifndef QT_NO_COMBOBOX
#include "ntqpopupmenu.h"
#include "ntqlistbox.h"
#include "ntqpainter.h"
#include "ntqdrawutil.h"
#include "ntqstrlist.h"
#include "ntqpixmap.h"
#include "ntqtimer.h"
#include "ntqapplication.h"
#include "ntqlineedit.h"
#include "ntqbitmap.h"
#include "qeffects_p.h"
#include "ntqstringlist.h"
#include "ntqcombobox.h"
#include "ntqstyle.h"
#include <limits.h>
#if defined(QT_ACCESSIBILITY_SUPPORT)
#include "ntqaccessible.h"
#endif
/*!
\class TQComboBox ntqcombobox.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 );
tqApp->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