/* This file Copyright (C) 2003 Michael 'Mickey' Lauer is part of the Copyright (C) 2000,2001 Carsten Pfeiffer Opie Project Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> Copyright (C) 2000,2001 Dawit Alemayehu =. .=l. Originally part of the KDE Project            .>+-=  _;:,     .>    :=|. This program is free software; you can .> <`_,   >  .   <= redistribute it and/or modify it under :`=1 )Y*s>-.--   : the terms of the GNU Library General Public .="- .-=="i,     .._ License as published by the Free Software  - .   .-<_>     .<> Foundation; either version 2 of the License,      ._= =}       : or (at your option) any later version.     .%`+i>       _;_.     .i_,=:_.      -`: PARTICULAR PURPOSE. See the GNU ..}^=.=       =       ; Library General Public License for more ++=   -.     .`     .: details.  :     =  ...= . :.=-  -.   .:....=;==+<; You should have received a copy of the GNU   -_. . .   )=.  = Library General Public License along with     --        :-=` this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #define OListBox QListBox class OCompletionBox::OCompletionBoxPrivate { public: QWidget *m_parent; // necessary to set the focus back QString cancelText; bool tabHandling; bool down_workaround; }; OCompletionBox::OCompletionBox( QWidget *parent, const char *name ) :OListBox( parent, name, WType_Popup ) { d = new OCompletionBoxPrivate; d->m_parent = parent; d->tabHandling = true; d->down_workaround = false; setColumnMode( 1 ); setLineWidth( 1 ); setFrameStyle( QFrame::Box | QFrame::Plain ); if ( parent ) setFocusProxy( parent ); else setFocusPolicy( NoFocus ); setVScrollBarMode( Auto ); setHScrollBarMode( AlwaysOff ); connect( this, SIGNAL( doubleClicked(QListBoxItem*)), SLOT( slotActivated(QListBoxItem*)) ); // grmbl, just QListBox workarounds :[ Thanks Volker. connect( this, SIGNAL( currentChanged(QListBoxItem*)), SLOT( slotCurrentChanged() )); connect( this, SIGNAL( clicked(QListBoxItem*)), SLOT( slotItemClicked(QListBoxItem*)) ); } OCompletionBox::~OCompletionBox() { d->m_parent = 0L; delete d; } QStringList OCompletionBox::items() const { QStringList list; for ( uint i = 0; i < count(); i++ ) { list.append( text( i ) ); } return list; } void OCompletionBox::slotActivated( QListBoxItem *item ) { if ( !item ) return; hide(); emit activated( item->text() ); } bool OCompletionBox::eventFilter( QObject *o, QEvent *e ) { int type = e->type(); if ( o == d->m_parent ) { if ( isVisible() ) { if ( type == QEvent::KeyPress ) { QKeyEvent *ev = static_cast( e ); switch ( ev->key() ) { case Key_BackTab: if ( d->tabHandling ) { up(); ev->accept(); return true; } break; case Key_Tab: if ( d->tabHandling ) { down(); // Only on TAB!! ev->accept(); return true; } break; case Key_Down: down(); ev->accept(); return true; case Key_Up: up(); ev->accept(); return true; case Key_Prior: pageUp(); ev->accept(); return true; case Key_Next: pageDown(); ev->accept(); return true; case Key_Escape: cancelled(); ev->accept(); return true; case Key_Enter: case Key_Return: if ( ev->state() & ShiftButton ) { hide(); ev->accept(); // Consume the Enter event return true; } break; default: break; } } else if ( type == QEvent::AccelOverride ) { // Override any acceleartors that match // the key sequences we use here... QKeyEvent *ev = static_cast( e ); switch ( ev->key() ) { case Key_Tab: case Key_BackTab: case Key_Down: case Key_Up: case Key_Prior: case Key_Next: case Key_Escape: case Key_Enter: case Key_Return: ev->accept(); return true; break; default: break; } } // parent loses focus or gets a click -> we hide else if ( type == QEvent::FocusOut || type == QEvent::Resize || type == QEvent::Close || type == QEvent::Hide || type == QEvent::Move ) { hide(); } else if ( type == QEvent::Move ) move( d->m_parent->mapToGlobal(QPoint(0, d->m_parent->height()))); else if ( type == QEvent::Resize ) resize( sizeHint() ); } } // any mouse-click on something else than "this" makes us hide else if ( type == QEvent::MouseButtonPress ) { QMouseEvent *ev = static_cast( e ); if ( !rect().contains( ev->pos() )) // this widget hide(); } return OListBox::eventFilter( o, e ); } void OCompletionBox::popup() { if ( count() == 0 ) hide(); else { ensureCurrentVisible(); bool block = signalsBlocked(); blockSignals( true ); setCurrentItem( 0 ); blockSignals( block ); clearSelection(); if ( !isVisible() ) show(); else if ( size().height() < sizeHint().height() ) resize( sizeHint() ); } } void OCompletionBox::show() { resize( sizeHint() ); if ( d->m_parent ) { //QDesktopWidget *screen = QApplication::desktop(); QWidget *screen = QApplication::desktop(); QPoint orig = d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) ); int x = orig.x(); int y = orig.y(); if ( x + width() > screen->width() ) x = screen->width() - width(); if (y + height() > screen->height() ) y = y - height() - d->m_parent->height(); move( x, y); qApp->installEventFilter( this ); } // ### we shouldn't need to call this, but without this, the scrollbars // are pretty b0rked. //triggerUpdate( true ); OListBox::show(); } void OCompletionBox::hide() { if ( d->m_parent ) qApp->removeEventFilter( this ); d->cancelText = QString::null; OListBox::hide(); } QSize OCompletionBox::sizeHint() const { int ih = itemHeight(); int h = QMIN( 15 * ih, (int) count() * ih ) +1; h = QMAX( h, OListBox::minimumSizeHint().height() ); int w = (d->m_parent) ? d->m_parent->width() : OListBox::minimumSizeHint().width(); w = QMAX( OListBox::minimumSizeHint().width(), w ); return QSize( w, h ); } void OCompletionBox::down() { int i = currentItem(); if ( i == 0 && d->down_workaround ) { d->down_workaround = false; setCurrentItem( 0 ); setSelected( 0, true ); emit highlighted( currentText() ); } else if ( i < (int) count() - 1 ) setCurrentItem( i + 1 ); } void OCompletionBox::up() { if ( currentItem() > 0 ) setCurrentItem( currentItem() - 1 ); } void OCompletionBox::pageDown() { int i = currentItem() + numItemsVisible(); i = i > (int)count() - 1 ? (int)count() - 1 : i; setCurrentItem( i ); } void OCompletionBox::pageUp() { int i = currentItem() - numItemsVisible(); i = i < 0 ? 0 : i; setCurrentItem( i ); } void OCompletionBox::home() { setCurrentItem( 0 ); } void OCompletionBox::end() { setCurrentItem( count() -1 ); } void OCompletionBox::setTabHandling( bool enable ) { d->tabHandling = enable; } bool OCompletionBox::isTabHandling() const { return d->tabHandling; } void OCompletionBox::setCancelledText( const QString& text ) { d->cancelText = text; } QString OCompletionBox::cancelledText() const { return d->cancelText; } void OCompletionBox::cancelled() { if ( !d->cancelText.isNull() ) emit userCancelled( d->cancelText ); if ( isVisible() ) hide(); } class OCompletionBoxItem : public QListBoxItem { public: void reuse( const QString &text ) { setText( text ); } }; void OCompletionBox::insertItems( const QStringList& items, int index ) { bool block = signalsBlocked(); blockSignals( true ); insertStringList( items, index ); blockSignals( block ); d->down_workaround = true; } void OCompletionBox::setItems( const QStringList& items ) { bool block = signalsBlocked(); blockSignals( true ); QListBoxItem* item = firstItem(); if ( !item ) { insertStringList( items ); } else { for ( QStringList::ConstIterator it = items.begin(); it != items.end(); it++) { if ( item ) { ((OCompletionBoxItem*)item)->reuse( *it ); item = item->next(); } else { insertItem( new QListBoxText( *it ) ); } } QListBoxItem* tmp = item; while ( (item = tmp ) ) { tmp = item->next(); delete item; } triggerUpdate( false ); } blockSignals( block ); d->down_workaround = true; } void OCompletionBox::slotCurrentChanged() { d->down_workaround = false; } void OCompletionBox::slotItemClicked( QListBoxItem *item ) { if ( item ) { if ( d->down_workaround ) { d->down_workaround = false; emit highlighted( item->text() ); } hide(); emit activated( item->text() ); } }