Logo Search packages:      
Sourcecode: qt4-x11 version File versions  Download package

q3combobox.cpp

/****************************************************************************
**
** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
**
** This file is part of the Qt3Support module of the Qt 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 Qt Foundation. In
** addition, as a special exception, Trolltech gives you certain
** additional rights. These rights are described in the Trolltech GPL
** Exception version 1.1, which can be found at
** http://www.trolltech.com/products/qt/gplexception/ and in the file
** GPL_EXCEPTION.txt in this package.
**
** 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.
**
** In addition, as a special exception, Trolltech, as the sole
** copyright holder for Qt Designer, grants users of the Qt/Eclipse
** Integration plug-in the right for the Qt/Eclipse Integration to
** link to functionality provided by Qt Designer and its related
** libraries.
**
** 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 expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "q3combobox.h"
#ifndef QT_NO_COMBOBOX
#include "qpainter.h"
#include "qdrawutil.h"
#include "qpixmap.h"
#include "qtimer.h"
#include "qapplication.h"
#include "qlineedit.h"
#include "qbitmap.h"
#include "qstringlist.h"
#include "qstyle.h"
#include "qevent.h"
#include "qmenu.h"
#include "qmenudata.h"
#include "qstyleoption.h"
#include "qdesktopwidget.h"
#include "q3popupmenu.h"
#include "q3listbox.h"
#include "q3strlist.h"
#include "q3frame.h"
#include <limits.h>
#include <qdebug.h>
#ifndef QT_NO_EFFECTS
# include <private/qeffects_p.h>
#endif
#if defined(QT_ACCESSIBILITY_SUPPORT)
#include "qaccessible.h"
#endif

/*!
    \class Q3ComboBox q3combobox.h
    \brief The Q3ComboBox widget is a combined button and popup list.
    \since 4.1
    \compat

    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.

    Q3ComboBox 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. Q3ComboBox provides both.

    Q3ComboBox provides two different constructors. The simplest
    constructor creates an "old-style" combobox in Motif (or Aqua)
    style:
    \code
        Q3ComboBox *c = new Q3ComboBox(this, tr("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
        Q3ComboBox *c1 = new Q3ComboBox(false, this, tr("read-only combobox"));
        Q3ComboBox *c2 = new Q3ComboBox(true, this, tr("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
    QString 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 QValidator; 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().

    Depending on the style, Q3ComboBox will use a list box or a
    popup menu to display the list of items. See setListBox() for
    more information.

    \sa QComboBox, QLineEdit, QSpinBox
        {GUI Design Handbook}{GUI Design Handbook: Combo Box, Drop-Down List Box}
*/


/*!
    \enum Q3ComboBox::Policy

    This enum specifies what the Q3ComboBox 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.

    \omitvalue NoInsert
    \omitvalue InsertAtTop
    \omitvalue InsertAtCurrent
    \omitvalue InsertAtBottom
    \omitvalue InsertAfterCurrent
    \omitvalue InsertBeforeCurrent
*/


/*!
    \fn void Q3ComboBox::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
    \fn void Q3ComboBox::activated( const QString &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 Q3ComboBox::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
    \fn void Q3ComboBox::highlighted( const QString &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 Q3ComboBox::textChanged( const QString &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 Q3ComboBox::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 Q3ComboBox::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 Q3ComboBox::count
    \brief the number of items in the combobox
*/

/*!
    \property Q3ComboBox::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 Q3ComboBox::currentText
    \brief the text of the combobox's current item
*/

/*!
    \property Q3ComboBox::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 Q3ComboBox::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 its appearance
    to a 2.0 style Motif combobox is it is set to be editable.
*/

/*!
    \property Q3ComboBox::insertionPolicy
    \brief the position of the items inserted by the user

    The default insertion policy is \c AtBottom. See \l Policy.
*/

/*!
    \property Q3ComboBox::maxCount
    \brief the maximum number of items allowed in the combobox
*/

/*!
    \property Q3ComboBox::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 scroll bar is added.
*/

class Q3ComboBoxPopup : public Q3PopupMenu
{
public:
    Q3ComboBoxPopup( QWidget *parent=0, const char *name=0 )
      : Q3PopupMenu( parent, name )
    {
    }

    int itemHeight( int index )
    {
      return Q3PopupMenu::itemHeight( index );
    }
};

static inline QString escapedComboString(const QString &str)
{
    QString stringToReturn = str;
    return stringToReturn.replace(QLatin1Char('&'), QLatin1String("&&"));
}

class Q3ComboBoxPopupItem : public QMenuItem
{
    Q3ListBoxItem *li;
    QSize sc; // Size cache optimization
public:
    Q3ComboBoxPopupItem(Q3ListBoxItem *i) : QMenuItem(), li(i), sc(0, 0) {  }
    virtual bool fullSpan() const { return true; }
    virtual void paint( QPainter*, const QColorGroup&, bool, bool, int, int, int, int);
    virtual QSize sizeHint() { if (sc.isNull()) sc = QSize(li->width(li->listBox()), QMAX(25, li->height(li->listBox()))); return sc; }
};
void Q3ComboBoxPopupItem::paint( QPainter* p, const QColorGroup&, 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 Q3ComboBoxData
{
public:
    Q3ComboBoxData( Q3ComboBox *cb ): current( 0 ), arrowDown(false), ed( 0 ), usingLBox( false ), pop( 0 ), lBox( 0 ), combo( cb )
    {
      duplicatesEnabled = true;
      cb->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
    }

    inline bool usingListBox()  { return usingLBox; }
    inline Q3ListBox * listBox() { return lBox; }
    inline Q3ComboBoxPopup * popup() { return pop; }
    void updateLinedGeometry();

    void setListBox( Q3ListBox *l ) { lBox = l ; usingLBox = true;
                        l->setMouseTracking( true );}

    void setPopupMenu( Q3ComboBoxPopup * pm, bool isPopup=true )
      { pop = pm; if(isPopup) usingLBox = false; }

    QStyleOptionComboBox getStyleOption() const
        {
            QStyleOptionComboBox opt;
            opt.init(combo);
            if (!combo->editable() && combo->hasFocus())
                opt.state |= QStyle::State_Selected;
            opt.subControls = QStyle::SC_All;
            if (arrowDown) {
                opt.activeSubControls = QStyle::SC_ComboBoxArrow;
                opt.state |= QStyle::State_Sunken;
            }
            opt.editable = combo->editable();
            opt.frame = 1; // ### get from style?
            if (current > -1 && current < combo->count()) {
                opt.currentText = combo->text(current);
                if (combo->pixmap(current))
                    opt.currentIcon = QIcon(*combo->pixmap(current));
            }
            opt.iconSize = QSize(22, 22); // ### need a sane value here
//             if (container && container->isVisible())
//                 opt.state |= QStyle::State_On;
            return opt;
        }

    int           current;
    int           maxCount;
    int           sizeLimit;
    Q3ComboBox::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;

    QLineEdit * ed;  // /bin/ed rules!
    QTimer *completionTimer;

    QSize sizeHint;
    QHash<int, QPixmap> popupPixmaps;

private:
    bool    usingLBox;
    Q3ComboBoxPopup *pop;
    Q3ListBox   *lBox;
    Q3ComboBox *combo;
};

void Q3ComboBoxData::updateLinedGeometry()
{
    if ( !ed || !combo )
      return;

    QStyleOptionComboBox opt = getStyleOption();
    QRect r = combo->style()->subControlRect(
        QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, combo);

    const QPixmap *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 );
}

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( "Q3ComboBox::%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( "Q3ComboBox::%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 Q3ComboBox, then
    the pixmap() function will always return 0. To workaround this,
    use the other constructor.

*/



00529 Q3ComboBox::Q3ComboBox( QWidget *parent, const char *name )
    : QWidget( parent, name, Qt::WNoAutoErase )
{
    d = new Q3ComboBoxData( this );
    QStyleOptionComboBox opt;
    opt.init(this);
    if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) ||
       style()->styleHint(QStyle::SH_GUIStyle, &opt, this) == Qt::MotifStyle ) {
      d->setPopupMenu( new Q3ComboBoxPopup( 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 QTimer( this );

    setFocusPolicy( Qt::TabFocus );
    setBackgroundMode( Qt::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 QWidget
    constructor.
*/


00579 Q3ComboBox::Q3ComboBox( bool rw, QWidget *parent, const char *name )
    : QWidget( parent, name, Qt::WNoAutoErase )
{
    d = new Q3ComboBoxData( this );
    setUpListBox();

    QStyleOptionComboBox opt = d->getStyleOption();
    if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
      d->popup()->setItemChecked(d->current, false);
    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 QTimer( this );

    setFocusPolicy( Qt::StrongFocus );

    d->ed = 0;
    if ( rw )
      setUpLineEdit();
    setBackgroundMode( Qt::PaletteButton, Qt::PaletteBase );
}



/*!
    Destroys the combobox.
*/

00615 Q3ComboBox::~Q3ComboBox()
{
    delete d;
}

void Q3ComboBox::setDuplicatesEnabled( bool enable )
{
   d->duplicatesEnabled = enable;
}

bool Q3ComboBox::duplicatesEnabled() const
{
    return d->duplicatesEnabled;
}

int Q3ComboBox::count() const
{
    if ( d->usingListBox() )
      return d->listBox()->count();
    else if (d->popup())
      return d->popup()->count();
    else
        return 0;
}


/*!
    \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().
*/

00651 void Q3ComboBox::insertStrList( const Q3StrList &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().
*/

00666 void Q3ComboBox::insertStrList( const Q3StrList *list, int index )
{
    if ( !list ) {
#if defined(QT_CHECK_NULL)
      Q_ASSERT( list != 0 );
#endif
      return;
    }
    Q3StrListIterator it( *list );
    const char* tmp;
    if ( index < 0 )
      index = count();
    while ( (tmp=it.current()) ) {
      ++it;
      if ( d->usingListBox() )
          d->listBox()->insertItem( QString::fromLatin1(tmp), index );
      else
          d->popup()->insertItem( escapedComboString(QString::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.
*/

00702 void Q3ComboBox::insertStringList( const QStringList &list, int index )
{
    QStringList::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()
*/

00743 void Q3ComboBox::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( QString::fromLatin1(strings[i]), index );
      else
          d->popup()->insertItem( escapedComboString(QString::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.
*/

00779 void Q3ComboBox::insertItem( const QString &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.
*/

00808 void Q3ComboBox::insertItem( const QPixmap &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.
*/

00837 void Q3ComboBox::insertItem( const QPixmap &pixmap, const QString& 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.
*/

00864 void Q3ComboBox::removeItem( int index )
{
    int cnt = count();
    if ( !checkIndex( "removeItem", name(), cnt, index ) )
      return;
    if ( d->usingListBox() ) {
        QStyleOptionComboBox opt = d->getStyleOption();
      if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, 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 ) {
          QString s = QString::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.
*/

00914 void Q3ComboBox::clear()
{
    QStyleOptionComboBox opt = d->getStyleOption();
    if ( d->usingListBox() ) {
      if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) && d->popup() )
          d->popup()->clear();
      d->listBox()->resize( 0, 0 );
      d->listBox()->clear();
    } else {
      d->popup()->clear();
    }

    if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
      d->popup()->setItemChecked(d->current, false);
    d->current = 0;
    if ( d->ed ) {
      d->ed->setText( QString::fromLatin1("") );
      d->updateLinedGeometry();
    }
    currentChanged();
}


QString Q3ComboBox::currentText() const
{
    if ( d->ed )
      return d->ed->text();
    else if ( d->current < count() )
      return text( currentItem() );
    else
      return QString::null;
}

void Q3ComboBox::setCurrentText( const QString& 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 QString::null if
    the item is not a string.

    \sa currentText()
*/

00969 QString Q3ComboBox::text( int index ) const
{
    if ( !checkIndex( "text", name(), count(), index ) )
      return QString::null;
    if ( d->usingListBox() ) {
      return d->listBox()->text( index );
    } else {
        QString retText = d->popup()->text(index);
        retText.replace(QLatin1String("&&"), QString(QLatin1Char('&')));
      return retText;
    }
}

/*!
    Returns the pixmap item at position \a index, or 0 if the item is
    not a pixmap.
*/

00987 const QPixmap *Q3ComboBox::pixmap( int index ) const
{
    if ( !checkIndex( "pixmap", name(), count(), index ) )
      return 0;

    if (d->usingListBox()) {
        return d->listBox()->pixmap( index );
    } else {
        d->popupPixmaps[index] = d->popup()->pixmap(index);
        return d->popupPixmaps[index].isNull() ? 0 : &d->popupPixmaps[index];
    }
}

/*!
    Replaces the item at position \a index with the text \a t.
*/

01004 void Q3ComboBox::changeItem( const QString &t, int index )
{
    if ( !checkIndex( "changeItem", name(), count(), index ) )
      return;
    if ( d->usingListBox() )
      d->listBox()->changeItem( t, index );
    else
      d->popup()->changeItem(index, t);
    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()
*/

01030 void Q3ComboBox::changeItem( const QPixmap &im, int index )
{
    if ( !checkIndex( "changeItem", name(), count(), index ) )
      return;
    if ( d->usingListBox() )
      d->listBox()->changeItem( im, index );
    else
      d->popup()->changeItem(index, im);
    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()
*/

01051 void Q3ComboBox::changeItem( const QPixmap &im, const QString &t, int index )
{
    if ( !checkIndex( "changeItem", name(), count(), index ) )
      return;
    if ( d->usingListBox() )
      d->listBox()->changeItem( im, t, index );
    else
      d->popup()->changeItem(index, im, t);
    if ( index == d->current )
      update();
}


int Q3ComboBox::currentItem() const
{
    return d->current;
}

void Q3ComboBox::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;

    QStyleOptionComboBox opt = d->getStyleOption();
    if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, 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();
}

/*!
    Returns true if auto-resize is enabled; otherwise returns false.

    \sa autoResize
*/

bool Q3ComboBox::autoResize() const
{
    return d->autoresize;
}

/*!
    If \a enable is true, enable auto-resize; disable it otherwise.

    \sa autoResize
*/

01118 void Q3ComboBox::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().
*/
01135 QSize Q3ComboBox::sizeHint() const
{
    if ( isVisible() && d->sizeHint.isValid() )
      return d->sizeHint;

    constPolish();
    int i, w;
    QFontMetrics fm = fontMetrics();

    int maxW = count() ? 18 : 7 * fm.width(QLatin1Char('x')) + 18;
    int maxH = QMAX( 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;
      }
    }

    QStyleOptionComboBox opt = d->getStyleOption();
    d->sizeHint = (style()->sizeFromContents(QStyle::CT_ComboBox, &opt, QSize(maxW, maxH), this).
                   expandedTo(QApplication::globalStrut()));

    return d->sizeHint;
}


/*!
  \internal
  Receives activated signals from an internal popup list and emits
  the activated() signal.
*/

01173 void Q3ComboBox::internalActivate( int index )
{
    QStyleOptionComboBox opt = d->getStyleOption();
    if ( d->current != index ) {
      if ( !d->usingListBox() || listBox()->item( index )->isSelectable() ) {
          if (d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
            d->popup()->setItemChecked(d->current, false);
          d->current = index;
          currentChanged();
      }
    }
    if ( d->usingListBox() )
      popDownListBox();
    else
      d->popup()->removeEventFilter( this );
    d->poppedUp = false;

    QString 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.
*/

01205 void Q3ComboBox::internalHighlight( int index )
{
    emit highlighted( index );
    QString 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.
*/
01218 void Q3ComboBox::internalClickTimeout()
{
    d->shortClick = false;
}

/*!
    Sets the palette for both the combobox button and the combobox
    popup list to \a palette.
*/

01228 void Q3ComboBox::setPalette( const QPalette &palette )
{
    QWidget::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.
*/

01242 void Q3ComboBox::setFont( const QFont &font )
{
    d->sizeHint = QSize();          // invalidate size hint
    QWidget::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
*/

01260 void Q3ComboBox::resizeEvent( QResizeEvent * e )
{
    if ( d->ed )
      d->updateLinedGeometry();
    if ( d->listBox() )
      d->listBox()->resize( width(), d->listBox()->height() );
    QWidget::resizeEvent( e );
}

/*!\reimp
*/

01272 void Q3ComboBox::paintEvent( QPaintEvent * )
{
    QPainter p( this );
    const QColorGroup & g = colorGroup();
    p.setPen(g.text());

    if ( width() < 5 || height() < 5 ) {
      qDrawShadePanel( &p, rect(), g, false, 2,
                   &g.brush( QColorGroup::Button ) );
      return;
    }

    QStyleOptionComboBox opt = d->getStyleOption();
    bool reverse = QApplication::reverseLayout();
    if ( !d->usingListBox() &&
       style()->styleHint(QStyle::SH_GUIStyle) == Qt::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(QStyle::PM_DefaultFrameWidth, &opt, this),
                   &g.brush( QColorGroup::Button ) );
      qDrawShadePanel( &p, xPos, (height() - buttonH)/2,
                   buttonW, buttonH, g, false,
                   style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this) );
      QRect clip( x0, 2, w - 2 - 4 - 5, height() - 4 );
      QString str = d->popup()->text( this->d->current );
      if ( !str.isNull() ) {
          p.drawText( clip, Qt::AlignCenter | Qt::TextSingleLine, str );
      }

      QPixmap pix = d->popup()->pixmap( this->d->current );
      QIcon iconSet = d->popup()->iconSet( this->d->current );
      if (!pix.isNull() || !iconSet.isNull()) {
          QPixmap pm = ( !pix.isNull() ? 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(QStyle::CC_ComboBox, &opt, &p, this);
        QRect re = style()->subControlRect(QStyle::CC_ComboBox, &opt,
                                           QStyle::SC_ComboBoxEditField, this);
      p.setClipRect( re );

      QString str = d->popup()->text( this->d->current );
      QPixmap pix = d->popup()->pixmap( this->d->current );
      if ( !str.isNull() ) {
          p.save();
          p.setFont(font());
          QFontMetrics fm(font());
          int x = re.x(), y = re.y() + fm.ascent();
            x += pix.width() + 5;
          p.drawText( x, y, str );
          p.restore();
      }
      if (!pix.isNull()) {
          p.fillRect(re.x(), re.y(), pix.width() + 4, re.height(),
                        colorGroup().brush(QColorGroup::Base));
          p.drawPixmap(re.x() + 2, re.y() + (re.height() - pix.height()) / 2, pix);
      }
    } else {
      style()->drawComplexControl(QStyle::CC_ComboBox, &opt, &p, this);
      QRect re = style()->subControlRect(QStyle::CC_ComboBox, &opt,
                                            QStyle::SC_ComboBoxEditField, this);
      p.setClipRect(re);

      if ( !d->ed ) {
          Q3ListBoxItem * 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 ) ) {
          Q3ListBoxItem * item = d->listBox()->item( d->current );
          const QPixmap *pix = item->pixmap();
          if ( pix ) {
            p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(),
                      colorGroup().brush( QColorGroup::Base ) );
            p.drawPixmap( re.x() + 2, re.y() +
                        ( re.height() - pix->height() ) / 2, *pix );
          }
      }
      p.setClipping( false );
    }
}


/*!\reimp
*/

01378 void Q3ComboBox::mousePressEvent( QMouseEvent *e )
{
    if ( e->button() != Qt::LeftButton )
      return;
    if ( d->discardNextMousePress ) {
      d->discardNextMousePress = false;
      return;
    }

    QStyleOptionComboBox opt = d->getStyleOption();
    QRect arrowRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow
                                               , this);

    // Correction for motif style, where arrow is smaller
    // and thus has a rect that doesn't fit the button.
    arrowRect.setHeight( QMAX(  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();
      }
      QTimer::singleShot( 200, this, SLOT(internalClickTimeout()));
      d->shortClick = true;
    }
}

/*!\reimp
*/

01420 void Q3ComboBox::mouseMoveEvent( QMouseEvent * )
{
}

/*!\reimp
*/

01427 void Q3ComboBox::mouseReleaseEvent( QMouseEvent * )
{
}

/*!\reimp
*/

01434 void Q3ComboBox::mouseDoubleClickEvent( QMouseEvent *e )
{
    mousePressEvent( e );
}


/*!\reimp
*/

01443 void Q3ComboBox::keyPressEvent( QKeyEvent *e )
{
    int c = currentItem();
    if ( ( e->key() == Qt::Key_F4 && e->state() == 0 ) ||
       ( e->key() == Qt::Key_Down && (e->state() & Qt::AltModifier) ) ||
       ( !d->ed && e->key() == Qt::Key_Space ) ) {
      if ( count() ) {
          if ( !d->usingListBox() )
            d->popup()->setActiveItem( this->d->current );
          popup();
      }
      return;
    } else if ( d->usingListBox() && e->key() == Qt::Key_Up ) {
      if ( c > 0 )
          setCurrentItem( c-1 );
    } else if ( d->usingListBox() && e->key() == Qt::Key_Down ) {
      if ( ++c < count() )
          setCurrentItem( c );
    } else if ( d->usingListBox() && e->key() == Qt::Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) {
      setCurrentItem( 0 );
    } else if ( d->usingListBox() && e->key() == Qt::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();
          QString 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
*/

01503 void Q3ComboBox::focusInEvent( QFocusEvent * e )
{
    QWidget::focusInEvent( e );
    d->completeNow = false;
    d->completeAt = 0;
}

/*!\reimp
*/

01513 void Q3ComboBox::focusOutEvent( QFocusEvent * e )
{
    QWidget::focusOutEvent( e );
    d->completeNow = false;
    d->completeAt = 0;
}

/*!\reimp
*/
#ifndef QT_NO_WHEELEVENT
01523 void Q3ComboBox::wheelEvent( QWheelEvent *e )
{
    if ( d->poppedUp ) {
      if ( d->usingListBox() ) {
          QApplication::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( Q3ListBox *l, int sl )
{
    if ( l->count() > 0 )
      return QMIN( 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.
*/

01570 void Q3ComboBox::popup()
{
    if ( !count() || d->poppedUp )
      return;

    QStyleOptionComboBox opt = d->getStyleOption();
    if( !d->usingListBox() || style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) ) {
      if(d->usingListBox()) {
          if(!d->popup()) {
            Q3ComboBoxPopup *p = new Q3ComboBoxPopup( 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++) {
            Q3ListBoxItem *item = d->listBox()->item(i);
            if(item->rtti() == Q3ListBoxText::RTTI) {
                d->popup()->insertItem(escapedComboString(item->text()), i, i);
            } else if(item->rtti() == Q3ListBoxPixmap::RTTI) {
                if(item->pixmap())
                  d->popup()->insertItem(QIcon(*item->pixmap()), escapedComboString(item->text()), i, i);
                else
                  d->popup()->insertItem(escapedComboString(item->text()), i, i);
            } else {
                d->popup()->insertItem(new Q3ComboBoxPopupItem(item), i, i);
            }
          }
      }
      d->popup()->installEventFilter( this );
      if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
          d->popup()->setItemChecked(this->d->current, true);
      d->popup()->popup( mapToGlobal( QPoint(0,0) ), this->d->current );
      update();
    } else {
      // Send all listbox events to eventFilter():
      Q3ListBox* 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;
      QRect screen = QApplication::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
      QPoint pos = mapToGlobal( QPoint(0,height()) );
      // ## Similar code is in QPopupMenu
      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();

        opt.rect = QRect(x, y, w, h);
            QRect rect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
                                             QStyle::SC_ComboBoxListBoxPopup, this);

      // 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 );
      Q3ListBoxItem* 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(Q3ScrollView::Auto);

#ifndef QT_NO_EFFECTS
      if ( QApplication::isEffectEnabled( Qt::UI_AnimateCombo ) ) {
          if ( lb->y() < mapToGlobal(QPoint(0,0)).y() )
            qScrollEffect( lb, QEffects::UpScroll );
          else
            qScrollEffect( lb );
      } else
#endif
          lb->show();
    }
    d->poppedUp = true;
}


/*!
  Updates the widget mask.

  \sa QWidget::setMask()
*/
01680 void Q3ComboBox::updateMask()
{
    QBitmap bm( size() );
    bm.fill( Qt::color0 );

    QStyleOptionComboBox opt = d->getStyleOption();
    {
      QPainter p(&bm);
        p.initFrom(this);
        p.fillRect(opt.rect, Qt::color1); // qcommonstyle old drawComplexControl implementation
    }

    setMask( bm );
}

/*!
  \internal
  Pops down (removes) the combobox popup list box.
*/
01699 void Q3ComboBox::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.
*/

01719 void Q3ComboBox::reIndex()
{
    if ( !d->usingListBox() ) {
      int cnt = count();
      while ( cnt-- )
          d->popup()->setId( cnt, cnt );
    }
}

/*!
  \internal
  Repaints the combobox.
*/

01733 void Q3ComboBox::currentChanged()
{
    if ( d->autoresize )
      adjustSize();
    update();

#if defined(QT_ACCESSIBILITY_SUPPORT)
    QAccessible::updateAccessibility( this, 0, QAccessible::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.
*/

01755 bool Q3ComboBox::eventFilter( QObject *object, QEvent *event )
{
    QStyleOptionComboBox opt = d->getStyleOption();
    if ( !event )
      return true;
    else if ( object == d->ed ) {
      if ( event->type() == QEvent::KeyPress ) {
          bool isAccepted = ( (QKeyEvent*)event )->isAccepted();
          keyPressEvent( (QKeyEvent *)event );
          if ( ((QKeyEvent *)event)->isAccepted() ) {
            d->completeNow = false;
            return true;
          } else if ( ((QKeyEvent *)event)->key() != Qt::Key_End ) {
            d->completeNow = true;
            d->completeAt = d->ed->cursorPosition();
          }
          if ( isAccepted )
            ( (QKeyEvent*)event )->accept();
          else
            ( (QKeyEvent*)event )->ignore();
      } else if ( event->type() == QEvent::KeyRelease ) {
          keyReleaseEvent( (QKeyEvent *)event );
          return ((QKeyEvent *)event)->isAccepted();
      } else if ( event->type() == QEvent::FocusIn ) {
          focusInEvent( (QFocusEvent *)event );
      } else if ( event->type() == QEvent::FocusOut ) {
          focusOutEvent( (QFocusEvent *)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() ) {
            QString ct( d->ed->text() );
            int i = completionIndex( ct, currentItem() );
            if ( i > -1 ) {
                QString 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() )) {
      QMouseEvent *e = (QMouseEvent*)event;
      switch( event->type() ) {
      case QEvent::MouseMove:
          if ( !d->mouseWasInsidePopup  ) {
            QPoint pos = e->pos();
            if ( d->listBox()->rect().contains( pos ) )
                d->mouseWasInsidePopup = true;
            // Check if arrow button should toggle
            if ( d->arrowPressed ) {
                QPoint comboPos;
                comboPos = mapFromGlobal( d->listBox()->mapToGlobal(pos) );
                QRect arrowRect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
                                                              QStyle::SC_ComboBoxArrow, 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() & ( Qt::RightButton | Qt::LeftButton | Qt::MidButton ) ) == 0 &&
                   style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking, &opt, this)) {
            QWidget *mouseW = QApplication::widgetAt( e->globalPos(), true );
            if ( mouseW == d->listBox()->viewport() ) { //###
                QMouseEvent m( QEvent::MouseMove, e->pos(), e->globalPos(),
                           Qt::NoButton, Qt::LeftButton );
                QApplication::sendEvent( object, &m ); //### Evil
                return true;
            }
          }

          break;
      case QEvent::MouseButtonRelease:
          if ( d->listBox()->rect().contains( e->pos() ) ) {
            QMouseEvent tmp( QEvent::MouseButtonDblClick,
                         e->pos(), e->button(), e->state() ) ;
            // will hide popup
            QApplication::sendEvent( object, &tmp );
            return true;
          } else {
            if ( d->mouseWasInsidePopup ) {
                popDownListBox();
            } else {
                d->arrowPressed = false;
                if ( d->arrowDown  ) {
                  d->arrowDown = false;
                  repaint( false );
                }
            }
          }
          break;
      case QEvent::MouseButtonDblClick:
      case QEvent::MouseButtonPress:
          if ( !d->listBox()->rect().contains( e->pos() ) ) {
            QPoint globalPos = d->listBox()->mapToGlobal(e->pos());
            if ( QApplication::widgetAt( globalPos, true ) == this ) {
                d->discardNextMousePress = true;
                // avoid popping up again
            }
            popDownListBox();
            return true;
          }
          break;
      case QEvent::KeyPress:
          switch( ((QKeyEvent *)event)->key() ) {
          case Qt::Key_Up:
          case Qt::Key_Down:
            if ( !(((QKeyEvent *)event)->state() & Qt::AltModifier) )
                break;
          case Qt::Key_F4:
          case Qt::Key_Escape:
            if ( d->poppedUp ) {
                popDownListBox();
                return true;
            }
            break;
          case Qt::Key_Enter:
          case Qt::Key_Return:
            // work around QDialog's enter handling
            return false;
          default:
            break;
          }
          break;
      case QEvent::Hide:
          popDownListBox();
          break;
      default:
          break;
      }
    } else if ( (!d->usingListBox() || style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) &&
            object == d->popup() ) {
      QMouseEvent *e = (QMouseEvent*)event;
      switch ( event->type() ) {
      case QEvent::MouseButtonRelease:
          if ( d->shortClick ) {
            QMouseEvent tmp( QEvent::MouseMove,
                         e->pos(), e->button(), e->state() ) ;
            // highlight item, but don't pop down:
            QApplication::sendEvent( object, &tmp );
            return true;
          }
          break;
      case QEvent::MouseButtonDblClick:
      case QEvent::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 QEvent::Hide:
          d->poppedUp = false;
          break;
      default:
          break;
      }
    }
    return QWidget::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.
*/

01942 int Q3ComboBox::completionIndex( const QString & prefix,
                        int startingAt = 0 ) const
{
    int start = startingAt;
    if ( start < 0 || start >= count() )
      start = 0;
    if ( start >= count() )
      return -1;
    QString match = prefix.lower();
    if ( match.length() < 1 )
      return start;

    QString 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 Q3ComboBox::sizeLimit() const
{
    return d ? d->sizeLimit : INT_MAX;
}

void Q3ComboBox::setSizeLimit( int lines )
{
    d->sizeLimit = lines;
}


int Q3ComboBox::maxCount() const
{
    return d ? d->maxCount : INT_MAX;
}

void Q3ComboBox::setMaxCount( int count )
{
    int l = this->count();
    while( --l > count )
      removeItem( l );
    d->maxCount = count;
}

Q3ComboBox::Policy Q3ComboBox::insertionPolicy() const
{
    return d->p;
}

void Q3ComboBox::setInsertionPolicy( Policy policy )
{
    d->p = policy;
}



/*!
  Internal slot to keep the line editor up to date.
*/

02007 void Q3ComboBox::returnPressed()
{
    QString 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() != NoInsert ) {
          int cnt = count();
          while ( cnt >= d->maxCount ) {
            removeItem( --cnt );
          }
      }

      switch ( insertionPolicy() ) {
      case InsertAtCurrent:
          if (count() == 0)
            insertItem(s);
          else if ( s != text( currentItem() ) )
            changeItem( s, currentItem() );
          emit activated( currentItem() );
          emit activated( s );
          return;
      case NoInsert:
          emit activated( s );
          return;
      case InsertAtTop:
          c = 0;
          break;
      case InsertAtBottom:
          c = count();
          break;
      case InsertBeforeCurrent:
          c = currentItem();
          break;
      case InsertAfterCurrent:
          c = count() == 0 ? 0 : currentItem() + 1;
          break;
      }
      insertItem( s, c );
    }

    setCurrentItem( c );
    emit activated( c );
    emit activated( s );
}


/*!
  Enables the combobox if \a enable is true; otherwise disables it.

  \sa QWidget::enabled
*/

02074 void Q3ComboBox::setEnabled( bool enable )
{
    if ( !enable ) {
      if ( d->usingListBox() ) {
          popDownListBox();
      } else {
          d->popup()->removeEventFilter( this );
          d->popup()->close();
          d->poppedUp = false;
      }
    }
    QWidget::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() QValidator
*/

02099 void Q3ComboBox::setValidator( const QValidator * 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() QValidator
*/

02113 const QValidator * Q3ComboBox::validator() const
{
    return d && d->ed ? d->ed->validator() : 0;
}


/*!
    This slot is equivalent to setValidator( 0 ).
*/

02123 void Q3ComboBox::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 Q3ComboBox assumes that newListBox->text(n) returns
    non-null for 0 <= n < newListbox->count(). This assumption is
    necessary because of the line edit in Q3ComboBox.
*/

02140 void Q3ComboBox::setListBox( Q3ListBox * newListBox )
{
    clear();

    if ( d->usingListBox() ) {
      delete d->listBox();
    } else {
      delete d->popup();
        d->setPopupMenu(0, false);
    }

    newListBox->reparent( this, Qt::WType_Popup, QPoint(0,0), false );
    d->setListBox( newListBox );
    d->listBox()->setFont( font() );
    d->listBox()->setPalette( palette() );
    d->listBox()->setVScrollBarMode(Q3ScrollView::AlwaysOff);
    d->listBox()->setHScrollBarMode(Q3ScrollView::AlwaysOff);
    d->listBox()->setFrameStyle( Q3Frame::Box | Q3Frame::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.
    (Q3ComboBox can use QPopupMenu instead of QListBox.) Provided to
    match setListBox().

    \sa setListBox()
*/

02176 Q3ListBox * Q3ComboBox::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.
*/
02186 QLineEdit* Q3ComboBox::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()
*/

02205 void Q3ComboBox::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()
*/

02223 void Q3ComboBox::setEditText( const QString &newText )
{
    if ( d && d->ed ) {
      d->updateLinedGeometry();
      d->ed->setText( newText );
    }
}

void Q3ComboBox::setAutoCompletion( bool enable )
{
    d->useCompletion = enable;
    d->completeNow = false;
}


bool Q3ComboBox::autoCompletion() const
{
    return d->useCompletion;
}

/*!\reimp
 */
02245 void Q3ComboBox::styleChange( QStyle& s )
{
    d->sizeHint = QSize();          // invalidate size hint...
    if ( d->ed )
      d->updateLinedGeometry();
    QWidget::styleChange( s );
}

bool Q3ComboBox::editable() const
{
    return d->ed != 0;
}

void Q3ComboBox::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(Qt::StrongFocus);
    updateGeometry();
    update();
}


void Q3ComboBox::setUpListBox()
{
    d->setListBox( new Q3ListBox( this, "in-combo", Qt::WType_Popup ) );
    d->listBox()->setFont( font() );
    d->listBox()->setPalette( palette() );
    d->listBox()->setVScrollBarMode( Q3ListBox::AlwaysOff );
    d->listBox()->setHScrollBarMode( Q3ListBox::AlwaysOff );
    d->listBox()->setFrameStyle( Q3Frame::Box | Q3Frame::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 Q3ComboBox::setUpLineEdit()
{
    if ( !d->ed )
      setLineEdit( new QLineEdit( this, "combo edit" ) );
}

/*!
    Sets the line edit to use \a edit instead of the current line edit.
*/

02308 void Q3ComboBox::setLineEdit( QLineEdit *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, QPoint(0,0), false );

    connect (edit, SIGNAL(textChanged(QString)),
           this, SIGNAL(textChanged(QString)) );
    connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) );

    edit->setFrame( false );
    d->updateLinedGeometry();
    edit->installEventFilter( this );
    setFocusProxy( edit );
    setFocusPolicy(Qt::StrongFocus);
    setInputMethodEnabled( true );

    if ( !d->usingListBox() )
      setUpListBox();

    if ( isVisible() )
      edit->show();

    updateGeometry();
    update();
}

/*!
  Hides the combobox.

  \sa QWidget::hide()
*/
02350 void Q3ComboBox::hide()
{
    QWidget::hide();

    if (listBox())
      listBox()->hide();
    else if (d->popup())
      d->popup()->hide();
}

#endif // QT_NO_COMBOBOX

Generated by  Doxygen 1.6.0   Back to index