summaryrefslogtreecommitdiffstats
path: root/lib/widgets/qcomboview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/widgets/qcomboview.cpp')
-rw-r--r--lib/widgets/qcomboview.cpp1505
1 files changed, 1505 insertions, 0 deletions
diff --git a/lib/widgets/qcomboview.cpp b/lib/widgets/qcomboview.cpp
new file mode 100644
index 00000000..2cab4fea
--- /dev/null
+++ b/lib/widgets/qcomboview.cpp
@@ -0,0 +1,1505 @@
+/**********************************************************************
+**
+**
+** Implementation of QComboView widget class
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+** Copyright (C) 2003 Alexander Dymo <[email protected]>
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+**********************************************************************/
+
+#include "qcomboview.h"
+#include <kdeversion.h>
+#ifndef QT_NO_COMBOBOX
+#include "qpopupmenu.h"
+#include "qlistview.h"
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qstrlist.h"
+#include "qpixmap.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "qlineedit.h"
+#include "qbitmap.h"
+#include "private/qeffects_p.h"
+#include "qstringlist.h"
+#include "qcombobox.h"
+#include "qstyle.h"
+#include "qheader.h"
+#include <limits.h>
+
+class QComboViewData
+{
+public:
+ QComboViewData( QComboView *cb ): current(0), lView( 0 ), combo( cb )
+ {
+ duplicatesEnabled = TRUE;
+ cb->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
+ }
+
+ inline QListView * listView() { return lView; }
+ void updateLinedGeometry();
+
+ void setListView( QListView *l ) { lView = l ;
+ l->setMouseTracking( TRUE );}
+
+ QListViewItem *current;
+ int maxCount;
+ int sizeLimit;
+ QComboView::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;
+
+private:
+ bool usinglView;
+ QListView *lView;
+ QComboView *combo;
+
+};
+
+void QComboViewData::updateLinedGeometry()
+{
+ if ( !ed || !combo )
+ return;
+ QRect r = QStyle::visualRect( combo->style().querySubControlMetrics(QStyle::CC_ComboBox, combo,
+ QStyle::SC_ComboBoxEditField), combo );
+
+// qWarning("updateLinedGeometry(): currentItem is %d", combo->currentItem() == 0 ? 0 : 1);
+ const QPixmap *pix = combo->currentItem() ? combo->currentItem()->pixmap(0) : 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( "QComboView::%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( "QComboView::%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 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.
+*/
+
+
+QComboView::QComboView( bool rw, QWidget *parent, const char *name )
+ : QWidget( parent, name, WResizeNoErase )
+{
+ d = new QComboViewData( this );
+ setUpListView();
+
+ 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 QTimer( this );
+
+ setFocusPolicy( StrongFocus );
+
+ d->ed = 0;
+ if ( rw )
+ setUpLineEdit();
+ setBackgroundMode( PaletteButton, PaletteBase );
+}
+
+
+
+/*!
+ Destroys the combobox.
+*/
+
+QComboView::~QComboView()
+{
+ delete d;
+}
+
+void QComboView::setDuplicatesEnabled( bool enable )
+{
+ d->duplicatesEnabled = enable;
+}
+
+bool QComboView::duplicatesEnabled() const
+{
+ return d->duplicatesEnabled;
+}
+
+int QComboView::childCount() const
+{
+ return d->listView()->childCount();
+}
+
+
+/*!
+ Removes all comboview items.
+*/
+
+void QComboView::clear()
+{
+ d->listView()->resize( 0, 0 );
+ d->listView()->clear();
+
+ d->current = 0;
+ if ( d->ed ) {
+ d->ed->setText( QString::fromLatin1("") );
+ d->updateLinedGeometry();
+ }
+ currentChanged();
+}
+
+QListViewItem *QComboView::currentItem() const
+{
+ return d->current;
+}
+
+void QComboView::setCurrentItem( QListViewItem *item )
+{
+ if ( item == d->current && !d->ed ) {
+ return;
+ }
+
+ if (!item)
+ {
+ d->current = 0;
+ if ( d->ed ) {
+// d->ed->setText( "" );
+ d->updateLinedGeometry();
+ }
+ return;
+ }
+
+ d->current = item;
+ d->completeAt = 0;
+ if ( d->ed ) {
+ d->ed->setText( item->text(0) );
+// qWarning("setCurrentItem( %s )", item->text(0).latin1());
+ d->updateLinedGeometry();
+ }
+ if ( d->listView() ) {
+ d->listView()->setCurrentItem( item );
+ } else {
+ internalHighlight( item );
+ // internalActivate( item ); ### this leads to weird behavior, as in 3.0.1
+ }
+
+ currentChanged();
+
+ d->listView()->ensureItemVisible(item);
+}
+
+bool QComboView::autoResize() const
+{
+ return d->autoresize;
+}
+
+void QComboView::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().
+*/
+QSize QComboView::sizeHint() const
+{
+ if ( isVisible() && d->sizeHint.isValid() )
+ return d->sizeHint;
+
+ constPolish();
+// int i, w;
+ QFontMetrics fm = fontMetrics();
+
+ int maxW = childCount() ? 18 : 7 * fm.width(QChar('x')) + 18;
+ int maxH = QMAX( fm.lineSpacing(), 14 ) + 2;
+
+/* for( i = 0; i < count(); i++ ) {
+ w = d->listView()->item( i )->width( d->listView() );
+ if ( w > maxW )
+ maxW = w;
+ }
+*/
+ d->sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, this,
+ QSize(maxW, maxH)).expandedTo(QApplication::globalStrut()));
+
+ return d->sizeHint;
+}
+
+
+/*!
+ \internal
+ Receives activated signals from an internal popup list and emits
+ the activated() signal.
+*/
+
+void QComboView::internalActivate( QListViewItem * item )
+{
+ if (!item)
+ {
+ d->current = 0;
+ if ( d->ed ) {
+// d->ed->setText( "" );
+ d->updateLinedGeometry();
+ }
+ return;
+ }
+ popDownListView();
+ d->poppedUp = FALSE;
+
+ d->current = item;
+
+ QString t( item->text(0) );
+ if ( d->ed ) {
+ d->ed->setText( t );
+// qWarning("internalActivate( %s )", item->text(0).latin1());
+ d->updateLinedGeometry();
+ }
+ emit activated( item );
+ emit activated( t );
+
+// item->setOpen(true);
+}
+
+/*!
+ \internal
+ Receives highlighted signals from an internal popup list and emits
+ the highlighted() signal.
+*/
+
+void QComboView::internalHighlight( QListViewItem * item )
+{
+ if (!item)
+ {
+ d->current = 0;
+ if ( d->ed ) {
+// d->ed->setText( "" );
+ d->updateLinedGeometry();
+ }
+ return;
+ }
+ emit highlighted( item );
+ QString t = item->text(0);
+ 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 QComboView::internalClickTimeout()
+{
+ d->shortClick = FALSE;
+}
+
+/*!
+ Sets the palette for both the combobox button and the combobox
+ popup list to \a palette.
+*/
+
+void QComboView::setPalette( const QPalette &palette )
+{
+ QWidget::setPalette( palette );
+ if( d ) {
+ if(d->listView())
+ d->listView()->setPalette( palette );
+ }
+}
+
+/*!
+ Sets the font for both the combobox button and the combobox popup
+ list to \a font.
+*/
+
+void QComboView::setFont( const QFont &font )
+{
+ d->sizeHint = QSize(); // invalidate size hint
+ QWidget::setFont( font );
+ d->listView()->setFont( font );
+ if (d->ed)
+ d->ed->setFont( font );
+ if ( d->autoresize )
+ adjustSize();
+}
+
+
+/*!reimp
+*/
+
+void QComboView::resizeEvent( QResizeEvent * e )
+{
+ if ( d->ed )
+ d->updateLinedGeometry();
+ d->listView()->resize( width(), d->listView()->height() );
+ QWidget::resizeEvent( e );
+}
+
+/*!reimp
+*/
+
+void QComboView::paintEvent( QPaintEvent * )
+{
+ QPainter p( this );
+ const QColorGroup & g = colorGroup();
+ p.setPen(g.text());
+
+ QStyle::SFlags flags = QStyle::Style_Default;
+ if (isEnabled())
+ flags |= QStyle::Style_Enabled;
+ if (hasFocus())
+ flags |= QStyle::Style_HasFocus;
+
+ if ( width() < 5 || height() < 5 ) {
+ qDrawShadePanel( &p, rect(), g, FALSE, 2,
+ &g.brush( QColorGroup::Button ) );
+ return;
+ }
+
+// bool reverse = QApplication::reverseLayout();
+ style().drawComplexControl( QStyle::CC_ComboBox, &p, this, rect(), g,
+ flags, QStyle::SC_All,
+ (d->arrowDown ?
+ QStyle::SC_ComboBoxArrow :
+ QStyle::SC_None ));
+
+ QRect re = style().querySubControlMetrics( QStyle::CC_ComboBox, this,
+ QStyle::SC_ComboBoxEditField );
+ re = QStyle::visualRect(re, this);
+ p.setClipRect( re );
+
+ if ( !d->ed ) {
+ QListViewItem * item = d->current;
+ if ( item ) {
+ // we calculate the QListBoxTexts height (ignoring strut)
+ int itemh = d->listView()->fontMetrics().lineSpacing() + 2;
+ p.translate( re.x(), re.y() + (re.height() - itemh)/2 );
+ item->paintCell( &p, d->listView()->colorGroup(), 0, width(), AlignLeft | AlignVCenter );
+ }
+ } else if ( d->listView() && d->listView()->currentItem( ) && d->current ) {
+ QListViewItem * item = d->current ;
+ const QPixmap *pix = item->pixmap(0);
+ 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
+*/
+
+void QComboView::mousePressEvent( QMouseEvent *e )
+{
+ if ( e->button() != LeftButton )
+ return;
+ if ( d->discardNextMousePress ) {
+ d->discardNextMousePress = FALSE;
+ return;
+ }
+ QRect arrowRect = style().querySubControlMetrics( QStyle::CC_ComboBox, this,
+ QStyle::SC_ComboBoxArrow);
+ arrowRect = QStyle::visualRect(arrowRect, 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 ( childCount() && ( !editable() || arrowRect.contains( e->pos() ) ) ) {
+ d->arrowPressed = FALSE;
+ listView()->blockSignals( TRUE );
+ qApp->sendEvent( listView(), e ); // trigger the listbox's autoscroll
+ listView()->blockSignals( FALSE );
+ popup();
+ if ( arrowRect.contains( e->pos() ) ) {
+ d->arrowPressed = TRUE;
+ d->arrowDown = TRUE;
+ repaint( FALSE );
+ }
+ QTimer::singleShot( 200, this, SLOT(internalClickTimeout()));
+ d->shortClick = TRUE;
+ }
+}
+
+/*!reimp
+*/
+
+void QComboView::mouseMoveEvent( QMouseEvent * )
+{
+}
+
+/*!reimp
+*/
+
+void QComboView::mouseReleaseEvent( QMouseEvent * )
+{
+}
+
+/*!reimp
+*/
+
+void QComboView::mouseDoubleClickEvent( QMouseEvent *e )
+{
+ mousePressEvent( e );
+}
+
+
+/*!reimp
+*/
+
+void QComboView::keyPressEvent( QKeyEvent *e )
+{
+ QListViewItem *c = currentItem();
+ if ( ( e->key() == Key_F4 && e->state() == 0 ) ||
+ ( e->key() == Key_Down && (e->state() & AltButton) ) ||
+ ( !d->ed && e->key() == Key_Space ) ) {
+ if ( childCount() ) {
+ popup();
+ }
+ return;
+ } else if ( e->key() == Key_Up ) {
+/* if ((!c) && (listView()->firstChild()))
+ setCurrentItem(listView()->firstChild());*/
+ if (c && c->itemAbove() )
+ setCurrentItem( c->itemAbove() );
+ else
+ return;
+ } else if ( e->key() == Key_Down ) {
+ if ((!c) && (listView()->firstChild()))
+ {
+ setCurrentItem(listView()->firstChild());
+ return;
+ }
+ if ( c && c->itemBelow() )
+ setCurrentItem( c->itemBelow() );
+ else
+ return;
+ } else if ( e->key() == Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) {
+ if (listView()->firstChild())
+ setCurrentItem( listView()->firstChild() );
+ else
+ return;
+ } else if ( e->key() == Key_End && ( !d->ed || !d->ed->hasFocus() ) ) {
+ if (listView()->lastItem())
+ setCurrentItem( listView()->lastItem() );
+ else
+ return;
+ } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) {
+ if ( !d->completionTimer->isActive() ) {
+ d->completeAt = 0;
+ c = completionIndex( e->text(), c->itemBelow() );
+ if ( c ) {
+ setCurrentItem( c );
+ d->completeAt = e->text().length();
+ }
+ else
+ return;
+ } 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(), listView()->firstChild() );
+ ct = e->text();
+ }
+ d->completeAt = 0;
+ if ( c ) {
+ setCurrentItem( c );
+ d->completeAt = ct.length();
+ }
+ else
+ return;
+ }
+ d->completionTimer->start( 400, TRUE );
+ } else {
+ e->ignore();
+ return;
+ }
+
+ c = currentItem();
+ if ( childCount() && c && !c->text(0).isNull() )
+ emit activated( c->text(0) );
+ emit activated( c );
+}
+
+QString QComboView::currentText() const
+{
+ if ( d->ed )
+ return d->ed->text();
+ else if ( d->current )
+ return currentItem()->text(0);
+ else
+ return QString::null;
+}
+
+/*!reimp
+*/
+
+void QComboView::focusInEvent( QFocusEvent * e )
+{
+ QWidget::focusInEvent( e );
+ d->completeNow = FALSE;
+ d->completeAt = 0;
+
+ emit focusGranted();
+}
+
+/*!reimp
+*/
+
+void QComboView::focusOutEvent( QFocusEvent * e )
+{
+ QWidget::focusOutEvent( e );
+ d->completeNow = FALSE;
+ d->completeAt = 0;
+
+ emit focusLost();
+}
+
+/*!reimp
+*/
+
+void QComboView::wheelEvent( QWheelEvent *e )
+{
+ if ( d->poppedUp ) {
+ QApplication::sendEvent( d->listView(), e );
+ } else {
+ if ( e->delta() > 0 ) {
+ QListViewItem *c = currentItem();
+ if ( c && c->itemAbove() ) {
+ setCurrentItem( c->itemAbove() );
+ emit activated( currentItem() );
+ emit activated( currentText() );
+ }
+ } else {
+ QListViewItem *c = currentItem();
+ if ( c && c->itemBelow() ) {
+ setCurrentItem( c->itemBelow() );
+ emit activated( currentItem() );
+ emit activated( currentText() );
+ }
+ }
+ e->accept();
+ }
+}
+
+int childCount(QListViewItem *it)
+{
+ int count = 1;
+ QListViewItem * myChild = it->firstChild();
+ while( myChild ) {
+ count += childCount(myChild);
+ myChild = myChild->nextSibling();
+ }
+ return count;
+}
+
+int childCount(QListView *lv)
+{
+ int count = 0;
+ QListViewItem * myChild = lv->firstChild();
+ while( myChild ) {
+ count += childCount(myChild);
+// count += 1;
+ myChild = myChild->nextSibling();
+ }
+ return count;
+}
+
+/*!
+ \internal
+ Calculates the listbox height needed to contain all items, or as
+ many as the list box is supposed to contain.
+*/
+static int listHeight( QListView *l, int /*sl*/ )
+{
+/* if ( l->childCount() > 0 )
+ return QMIN( l->childCount(), (uint)sl) * l->firstChild()->height();
+ else*/
+
+ int prefH = 0;
+ int ch = childCount(l);
+ ch = QMIN(ch, 10);
+ if (l->firstChild())
+ {
+ prefH = ch * l->firstChild()->height();
+ }
+ else
+ prefH = l->sizeHint().height();
+
+ if (l->header()->isVisible())
+ prefH += l->header()->sizeHint().height();
+
+// return prefH < l->sizeHint().height() ? prefH : l->sizeHint().height();
+
+ return prefH+2;
+}
+
+/*!
+ Pops up the combobox popup list.
+
+ If the list is empty, no items appear.
+*/
+
+void QComboView::popup()
+{
+ if ( !childCount() )
+ return;
+
+ // Send all listbox events to eventFilter():
+ QListView* lb = d->listView();
+ lb->triggerUpdate( );
+ lb->installEventFilter( this );
+ lb->viewport()->installEventFilter( this );
+ d->mouseWasInsidePopup = FALSE;
+// int w = lb->variableWidth() ? lb->sizeHint().width() : width();
+ int w = width();
+ int h = listHeight( lb, d->sizeLimit );
+ QRect screen = QApplication::desktop()->availableGeometry( const_cast<QComboView*>(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();
+ QRect rect =
+ style().querySubControlMetrics( QStyle::CC_ComboBox, this,
+ QStyle::SC_ComboBoxListBoxPopup,
+ QStyleOption( x, y, w, h ) );
+ if ( rect.isNull() )
+ rect.setRect( x, y, w, h );
+ lb->setGeometry( rect );
+
+ lb->raise();
+ bool block = lb->signalsBlocked();
+ lb->blockSignals( TRUE );
+ QListViewItem *currentLBItem = d->current ;
+ lb->setCurrentItem( currentLBItem );
+ // 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(QScrollView::Auto);
+
+//#ifndef QT_NO_EFFECTS
+/* if ( QApplication::isEffectEnabled( UI_AnimateCombo ) ) {
+ if ( lb->y() < mapToGlobal(QPoint(0,0)).y() )
+ qScrollEffect( lb, QEffects::UpScroll );
+ else
+ qScrollEffect( lb );
+ } else*/
+//#endif
+ lb->show();
+ d->poppedUp = TRUE;
+}
+
+
+/*!
+ reimp
+*/
+void QComboView::updateMask()
+{
+ QBitmap bm( size() );
+ bm.fill( color0 );
+
+ {
+ QPainter p( &bm, this );
+ style().drawComplexControlMask(QStyle::CC_ComboBox, &p, this, rect());
+ }
+
+ setMask( bm );
+}
+
+/*!
+ \internal
+ Pops down (removes) the combobox popup list box.
+*/
+void QComboView::popDownListView()
+{
+ d->listView()->removeEventFilter( this );
+ d->listView()->viewport()->removeEventFilter( this );
+ d->listView()->hide();
+ d->listView()->setCurrentItem( d->current );
+ if ( d->arrowDown ) {
+ d->arrowDown = FALSE;
+ repaint( FALSE );
+ }
+ d->poppedUp = FALSE;
+}
+
+
+/*!
+ \internal
+ Re-indexes the identifiers in the popup list.
+*/
+
+void QComboView::reIndex()
+{
+}
+
+/*!
+ \internal
+ Repaints the combobox.
+*/
+
+void QComboView::currentChanged()
+{
+ if ( d->autoresize )
+ adjustSize();
+ update();
+}
+
+/*! 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 QComboView::eventFilter( QObject *object, QEvent *event )
+{
+ 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() != 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 ) {
+ d->completeNow = FALSE;
+ 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 ) {
+ if ( !d->ed->text().isNull() &&
+ d->ed->cursorPosition() > d->completeAt &&
+ d->ed->cursorPosition() == (int)d->ed->text().length() ) {
+ d->completeNow = FALSE;
+ QString ct( d->ed->text() );
+ QListViewItem *i = completionIndex( ct, currentItem() );
+ if ( i ) {
+ QString it = i->text(0);
+ d->ed->validateAndSet( it, ct.length(),
+ ct.length(), it.length() );
+ }
+ }
+ }
+ } else if ( ( object == d->listView() ||
+ object == d->listView()->viewport() )) {
+ QMouseEvent *e = (QMouseEvent*)event;
+ switch( event->type() ) {
+ case QEvent::MouseMove:
+ if ( !d->mouseWasInsidePopup ) {
+// qWarning("!d->mouseWasInsidePopup");
+ QPoint pos = e->pos();
+ if ( d->listView()->rect().contains( pos ) )
+ d->mouseWasInsidePopup = TRUE;
+ // Check if arrow button should toggle
+ if ( d->arrowPressed ) {
+ QPoint comboPos;
+ comboPos = mapFromGlobal( d->listView()->mapToGlobal(pos) );
+ QRect arrowRect =
+ style().querySubControlMetrics( QStyle::CC_ComboBox, this,
+ QStyle::SC_ComboBoxArrow);
+ arrowRect = QStyle::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(QStyle::SH_ComboBox_ListMouseTracking, this)) {
+// qWarning("event filter:: emu");
+ QWidget *mouseW = QApplication::widgetAt( e->globalPos(), TRUE );
+// if ( mouseW == d->listView()->viewport() ) { //###
+ if ( mouseW == d->listView()->viewport() ) {
+ QListViewItem *sel = d->listView()->itemAt(e->pos());
+ if (sel)
+ {
+ d->listView()->setCurrentItem(sel);
+ d->listView()->setSelected(sel, true);
+ }
+ return TRUE;
+ }
+ }
+
+ break;
+ case QEvent::MouseButtonRelease:
+ if ( d->listView()->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 ) {
+ popDownListView();
+ } else {
+ d->arrowPressed = FALSE;
+ if ( d->arrowDown ) {
+ d->arrowDown = FALSE;
+ repaint( FALSE );
+ }
+ }
+ }
+ break;
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ if ( !d->listView()->rect().contains( e->pos() ) ) {
+ QPoint globalPos = d->listView()->mapToGlobal(e->pos());
+ if ( QApplication::widgetAt( globalPos, TRUE ) == this ) {
+ d->discardNextMousePress = TRUE;
+ // avoid popping up again
+ }
+ popDownListView();
+ return TRUE;
+ }
+ break;
+ case QEvent::KeyPress:
+ switch( ((QKeyEvent *)event)->key() ) {
+ case Key_Up:
+ case Key_Down:
+ if ( !(((QKeyEvent *)event)->state() & AltButton) )
+ break;
+ case Key_F4:
+ case Key_Escape:
+ if ( d->poppedUp ) {
+ popDownListView();
+ return TRUE;
+ }
+ break;
+ case Key_Enter:
+ case Key_Return:
+ // work around QDialog's enter handling
+ return FALSE;
+ default:
+ break;
+ }
+ break;
+ case QEvent::Hide:
+ popDownListView();
+ 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.
+*/
+
+QListViewItem *QComboView::completionIndex( const QString & prefix,
+ QListViewItem *startingAt ) const
+{
+ QListViewItem *start = startingAt;
+/* if ( start < 0 || start >= count() )
+ start = 0;
+ if ( start >= count() )
+ return -1;*/
+ if (!start)
+ start = listView()->firstChild();
+ if (!start)
+ return 0;
+/* if (!start->itemBelow())
+ return 0;*/
+ QString match = prefix.lower();
+ if ( match.length() < 1 )
+ return start;
+
+ QString current;
+ QListViewItem *i = start;
+ do {
+ current = i->text(0).lower();
+ if ( current.startsWith( match ) )
+ return i;
+ i = i->itemBelow();
+ if ( i )
+ i = listView()->firstChild();
+ } while ( i != start );
+ return 0;
+}
+
+
+int QComboView::sizeLimit() const
+{
+ return d ? d->sizeLimit : INT_MAX;
+}
+
+void QComboView::setSizeLimit( int lines )
+{
+ d->sizeLimit = lines;
+}
+
+
+/*int QComboView::maxCount() const
+{
+ return d ? d->maxCount : INT_MAX;
+}
+
+void QComboView::setMaxCount( int count )
+{
+ int l = this->count();
+ while( --l > count )
+ removeItem( l );
+ d->maxCount = count;
+}
+*/
+QComboView::Policy QComboView::insertionPolicy() const
+{
+ return d->p;
+}
+
+void QComboView::setInsertionPolicy( Policy policy )
+{
+ d->p = policy;
+}
+
+
+
+/*!
+ Internal slot to keep the line editor up to date.
+*/
+
+void QComboView::returnPressed()
+{
+ QString s( d->ed->text() );
+
+ if ( s.isEmpty() )
+ return;
+
+ QListViewItem *c = 0;
+ bool doInsert = TRUE;
+ if ( !d->duplicatesEnabled ) {
+ c = listView()->findItem(s, 0);
+ if ( c )
+ doInsert = FALSE;
+ }
+
+ if ( doInsert ) {
+ if ( insertionPolicy() != NoInsertion ) {
+/* int cnt = count();
+ while ( cnt >= d->maxCount ) {
+ removeItem( --cnt );
+ }*/
+ }
+
+ switch ( insertionPolicy() ) {
+ case AtCurrent:
+ if ( s != currentItem()->text(0) )
+ currentItem()->setText(0, s);
+ emit activated( currentItem() );
+ emit activated( s );
+ return;
+ case NoInsertion:
+ emit activated( s );
+ return;
+ case AtTop:
+ c = 0;
+ return;
+// break;
+ case AtBottom:
+ c = new QListViewItem(listView(), listView()->lastItem(), s);
+ break;
+ case BeforeCurrent:
+ if (currentItem() && currentItem()->itemAbove())
+ c = new QListViewItem(listView(), currentItem()->itemAbove(), s);
+ else
+ {
+ c = 0;
+ return;
+ }
+ break;
+ case AfterCurrent:
+ if (currentItem() && currentItem()->itemBelow())
+ c = new QListViewItem(listView(), currentItem()->itemBelow(), s);
+ else
+ {
+ c = 0;
+ return;
+ }
+ break;
+ }
+ }
+
+ if (c)
+ {
+ setCurrentItem( c );
+ emit activated( c );
+ emit activated( s );
+ }
+}
+
+
+/*! reimp
+*/
+
+void QComboView::setEnabled( bool enable )
+{
+ 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
+*/
+
+void QComboView::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
+*/
+
+const QValidator * QComboView::validator() const
+{
+ return d && d->ed ? d->ed->validator() : 0;
+}
+
+
+/*!
+ This slot is equivalent to setValidator( 0 ).
+*/
+
+void QComboView::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 QComboView assumes that newListBox->text(n) returns
+ non-null for 0 \<= n \< newListbox->count(). This assumption is
+ necessary because of the line edit in QComboView.
+*/
+
+void QComboView::setListView( QListView * newListView )
+{
+ clear();
+
+ delete d->listView();
+
+ newListView->reparent( this, WType_Popup, QPoint(0,0), FALSE );
+ d->setListView( newListView );
+ d->listView()->setFont( font() );
+ d->listView()->setPalette( palette() );
+/* d->listView()->setVScrollBarMode(QScrollView::AlwaysOff);
+ d->listView()->setHScrollBarMode(QScrollView::AlwaysOff);*/
+ d->listView()->setFrameStyle( QFrame::Box | QFrame::Plain );
+ d->listView()->setLineWidth( 1 );
+/* d->listView()->setRootIsDecorated( true );
+ d->listView()->setAllColumnsShowFocus(true);*/
+ d->listView()->resize( 100, 10 );
+
+ if (d->listView()->firstChild())
+ d->current = d->listView()->firstChild();
+
+// d->listView()->header()->hide();
+
+
+/* d->listView()->setFont( font() );
+ d->listView()->setPalette( palette() );
+ d->listView()->setVScrollBarMode( QScrollView::AlwaysOff );
+ d->listView()->setHScrollBarMode( QScrollView::AlwaysOff );
+ d->listView()->setFrameStyle( QFrame::Box | QFrame::Plain );
+ d->listView()->setLineWidth( 1 );
+ d->listView()->setRootIsDecorated( true );
+ d->listView()->setAllColumnsShowFocus(true);
+ d->listView()->addColumn("");
+ d->listView()->resize( 100, 10 );
+*/
+
+ connect( d->listView(), SIGNAL(returnPressed(QListViewItem*)),
+ SLOT(internalActivate(QListViewItem*)));
+ connect( d->listView(), SIGNAL(doubleClicked(QListViewItem*)),
+ SLOT(internalActivate(QListViewItem*)));
+ connect( d->listView(), SIGNAL(doubleClicked(QListViewItem*)),
+ SLOT(checkState(QListViewItem*)));
+ connect( d->listView(), SIGNAL(currentChanged(QListViewItem*)),
+ SLOT(internalHighlight(QListViewItem*)));
+ connect( d->listView(), SIGNAL(selectionChanged(QListViewItem*)),
+ SLOT(internalHighlight(QListViewItem*)));
+}
+
+
+/*!
+ Returns the current list box, or 0 if there is no list box.
+ (QComboView can use QPopupMenu instead of QListBox.) Provided to
+ match setlistView().
+
+ \sa setlistView()
+*/
+
+QListView * QComboView::listView() const
+{
+ return d ? d->listView() : 0;
+}
+
+/*!
+ Returns the line edit, or 0 if there is no line edit.
+
+ Only editable listboxes have a line editor.
+*/
+QLineEdit* QComboView::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 QComboView::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 QComboView::setEditText( const QString &newText )
+{
+ if ( d && d->ed ) {
+ d->updateLinedGeometry();
+ d->ed->setText( newText );
+ }
+}
+
+void QComboView::setAutoCompletion( bool enable )
+{
+ d->useCompletion = enable;
+ d->completeNow = FALSE;
+}
+
+
+bool QComboView::autoCompletion() const
+{
+ return d->useCompletion;
+}
+
+/*!reimp
+ */
+void QComboView::styleChange( QStyle& s )
+{
+ d->sizeHint = QSize(); // invalidate size hint...
+ if ( d->ed )
+ d->updateLinedGeometry();
+ QWidget::styleChange( s );
+}
+
+bool QComboView::editable() const
+{
+ return d->ed != 0;
+}
+
+void QComboView::setEditable( bool y )
+{
+ if ( y == editable() )
+ return;
+ if ( y ) {
+ setUpListView();
+ setUpLineEdit();
+ d->ed->show();
+ if ( currentItem() )
+ setEditText( currentText() );
+ } else {
+ delete d->ed;
+ d->ed = 0;
+ }
+
+ setFocusPolicy( StrongFocus );
+ updateGeometry();
+ update();
+}
+
+
+void QComboView::setUpListView()
+{
+ d->setListView( new QListView( this, "in-combo", WType_Popup ) );
+
+ d->listView()->setFont( font() );
+ d->listView()->setPalette( palette() );
+/* d->listView()->setVScrollBarMode( QScrollView::AlwaysOff );
+ d->listView()->setHScrollBarMode( QScrollView::AlwaysOff );*/
+ d->listView()->setFrameStyle( QFrame::Box | QFrame::Plain );
+ d->listView()->setLineWidth( 1 );
+ d->listView()->setRootIsDecorated( false );
+ d->listView()->setAllColumnsShowFocus(true);
+ d->listView()->addColumn("");
+ d->listView()->resize( 100, 10 );
+ d->listView()->setResizeMode(QListView::LastColumn);
+
+ if (d->listView()->firstChild())
+ d->current = d->listView()->firstChild();
+
+ d->listView()->header()->hide();
+
+ connect( d->listView(), SIGNAL(returnPressed(QListViewItem*)),
+ SLOT(internalActivate(QListViewItem*)));
+ connect( d->listView(), SIGNAL(doubleClicked(QListViewItem*)),
+ SLOT(internalActivate(QListViewItem*)));
+ connect( d->listView(), SIGNAL(doubleClicked(QListViewItem*)),
+ SLOT(checkState(QListViewItem*)));
+ connect( d->listView(), SIGNAL(currentChanged(QListViewItem*)),
+ SLOT(internalHighlight(QListViewItem*)));
+ connect( d->listView(), SIGNAL(selectionChanged(QListViewItem*)),
+ SLOT(internalHighlight(QListViewItem*)));
+}
+
+
+void QComboView::setUpLineEdit()
+{
+ if ( !d->ed )
+ setLineEdit( new QLineEdit( this, "combo edit" ) );
+}
+
+/*!
+ Sets the line edit to use \a edit instead of the current line edit.
+*/
+
+void QComboView::setLineEdit( QLineEdit *edit )
+{
+ if ( !edit ) {
+#if defined(QT_CHECK_NULL)
+ Q_ASSERT( edit != 0 );
+#endif
+ return;
+ }
+
+ edit->setText( currentText() );
+ if ( d->ed ) {
+ int start = 0, end = 0;
+ d->ed->getSelection( &start, &end );
+ edit->setSelection( start, end );
+ edit->setCursorPosition( d->ed->cursorPosition() );
+ edit->setEdited( d->ed->edited() );
+ delete d->ed;
+ }
+
+ d->ed = edit;
+
+ if ( edit->parent() != this ) {
+ edit->reparent( this, QPoint(0,0), FALSE );
+ edit->setFont( font() );
+ }
+
+ connect (edit, SIGNAL( textChanged( const QString& ) ),
+ this, SIGNAL( textChanged( const QString& ) ) );
+ connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) );
+
+ edit->setFrame( FALSE );
+ d->updateLinedGeometry();
+ edit->installEventFilter( this );
+ setFocusProxy( edit );
+ setFocusPolicy( StrongFocus );
+
+ setUpListView();
+
+ if ( isVisible() )
+ edit->show();
+
+ updateGeometry();
+ update();
+}
+
+void QComboView::setCurrentText( const QString& txt )
+{
+ QListViewItem *i;
+ i = listView()->findItem(txt, 0);
+ if ( i )
+ setCurrentItem( i );
+ else if ( d->ed )
+ d->ed->setText( txt );
+ else if (currentItem())
+ currentItem()->setText(0, txt);
+}
+
+void QComboView::checkState( QListViewItem * item)
+{
+ item->setOpen(!item->isOpen());
+}
+
+void QComboView::setCurrentActiveItem( QListViewItem * item )
+{
+ if ( item == d->current && !d->ed ) {
+ return;
+ }
+
+ d->current = item;
+ d->completeAt = 0;
+ if ( d->ed ) {
+ d->ed->setText( item->text(0) );
+ d->ed->setCursorPosition(0);
+// qWarning("setCurrentActiveItem( %s )", item->text(0).latin1());
+ d->updateLinedGeometry();
+ }
+ if ( d->listView() ) {
+ d->listView()->setCurrentItem( item );
+ emit activated( item );
+ emit activated( item->text(0) );
+ } else {
+ internalHighlight( item );
+ internalActivate( item );
+ }
+
+ currentChanged();
+
+ d->listView()->ensureItemVisible(item);
+}
+
+#include "qcomboview.moc"
+
+#endif // QT_NO_COMBOBOX
+