| # kdeui Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kcompletionbox.cpp

    00001 /* This file is part of the KDE libraries
    00002
    00003    Copyright (c) 2000,2001,2002 Carsten Pfeiffer <pfeiffer@kde.org>
    00004    Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de>
    00005    Copyright (c) 2000,2001,2002,2003,2004 Dawit Alemayehu <adawit@kde.org>
    00006
    00007    This library is free software; you can redistribute it and/or
    00008    modify it under the terms of the GNU Library General Public
    00009    License (LGPL) as published by the Free Software Foundation; either
    00010    version 2 of the License, or (at your option) any later version.
    00011
    00012    This library is distributed in the hope that it will be useful,
    00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
    00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00015    Library General Public License for more details.
    00016
    00017    You should have received a copy of the GNU Library General Public License
    00018    along with this library; see the file COPYING.LIB.  If not, write to
    00019    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00020    Boston, MA 02111-1307, USA.
    00021 */
    00022
    00023
    00024 #include <qapplication.h>
    00025 #include <qcombobox.h>
    00026 #include <qevent.h>
    00027 #include <qstyle.h>
    00028
    00029 #include <kdebug.h>
    00030 #include <kconfig.h>
    00031 #include <knotifyclient.h>
    00032 #include <kglobalsettings.h>
    00033
    00034 #include "kcompletionbox.h"
    00035
    00036 class KCompletionBox::KCompletionBoxPrivate
    00037 {
    00038 public:
    00039     QWidget *m_parent; // necessary to set the focus back
    00040     QString cancelText;
    00041     bool tabHandling;
    00042     bool down_workaround;
    00043     bool upwardBox;
    00044     bool emitSelected;
    00045 };
    00046
    00047 KCompletionBox::KCompletionBox( QWidget *parent, const char *name )
    00048  :KListBox( parent, name, WType_Popup ), d(new KCompletionBoxPrivate)
    00049 {
    00050
    00051     d->m_parent        = parent;
    00052     d->tabHandling     = true;
    00053     d->down_workaround = false;
    00054     d->upwardBox       = false;
    00055     d->emitSelected    = true;
    00056
    00057     setColumnMode( 1 );
    00058     setLineWidth( 1 );
    00059     setFrameStyle( QFrame::Box | QFrame::Plain );
    00060
    00061     if ( parent )
    00062         setFocusProxy( parent );
    00063     else
    00064         setFocusPolicy( NoFocus );
    00065
    00066     setVScrollBarMode( Auto );
    00067     setHScrollBarMode( AlwaysOff );
    00068
    00069     connect( this, SIGNAL( doubleClicked( QListBoxItem * )),
    00070              SLOT( slotActivated( QListBoxItem * )) );
    00071
    00072     // grmbl, just QListBox workarounds :[ Thanks Volker.
    00073     connect( this, SIGNAL( currentChanged( QListBoxItem * )),
    00074              SLOT( slotCurrentChanged() ));
    00075     connect( this, SIGNAL( clicked( QListBoxItem * )),
    00076              SLOT( slotItemClicked( QListBoxItem * )) );
    00077 }
    00078
    00079 KCompletionBox::~KCompletionBox()
    00080 {
    00081     d->m_parent = 0L;
    00082     delete d;
    00083 }
    00084
    00085 QStringList KCompletionBox::items() const
    00086 {
    00087     QStringList list;
    00088
    00089     const QListBoxItem* currItem = firstItem();
    00090
    00091     while (currItem) {
    00092         list.append(currItem->text());
    00093         currItem = currItem->next();
    00094     }
    00095
    00096     return list;
    00097 }
    00098
    00099 void KCompletionBox::slotActivated( QListBoxItem *item )
    00100 {
    00101     if ( !item )
    00102         return;
    00103
    00104     hide();
    00105     emit activated( item->text() );
    00106 }
    00107
    00108 bool KCompletionBox::eventFilter( QObject *o, QEvent *e )
    00109 {
    00110     int type = e->type();
    00111
    00112     if ( o == d->m_parent ) {
    00113         if ( isVisible() ) {
    00114             if ( type == QEvent::KeyPress ) {
    00115                 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
    00116                 switch ( ev->key() ) {
    00117                     case Key_BackTab:
    00118                         if ( d->tabHandling && (ev->state() == NoButton ||
    00119                              (ev->state() & ShiftButton)) ) {
    00120                             up();
    00121                             ev->accept();
    00122                             return true;
    00123                         }
    00124                         break;
    00125                     case Key_Tab:
    00126                         if ( d->tabHandling && (ev->state() == NoButton) ) {
    00127                             down(); // Only on TAB!!
    00128                             ev->accept();
    00129                             return true;
    00130                         }
    00131                         break;
    00132                     case Key_Down:
    00133                         down();
    00134                         ev->accept();
    00135                         return true;
    00136                     case Key_Up:
    00137                         // If there is no selected item and we've popped up above
    00138                         // our parent, select the first item when they press up.
    00139                         if ( selectedItem() ||
    00140                              mapToGlobal( QPoint( 0, 0 ) ).y() >
    00141                              d->m_parent->mapToGlobal( QPoint( 0, 0 ) ).y() )
    00142                             up();
    00143                         else
    00144                             down();
    00145                         ev->accept();
    00146                         return true;
    00147                     case Key_Prior:
    00148                         pageUp();
    00149                         ev->accept();
    00150                         return true;
    00151                     case Key_Next:
    00152                         pageDown();
    00153                         ev->accept();
    00154                         return true;
    00155                     case Key_Escape:
    00156                         canceled();
    00157                         ev->accept();
    00158                         return true;
    00159                     case Key_Enter:
    00160                     case Key_Return:
    00161                         if ( ev->state() & ShiftButton ) {
    00162                             hide();
    00163                             ev->accept();  // Consume the Enter event
    00164                             return true;
    00165                         }
    00166                         break;
    00167                     case Key_End:
    00168                         if ( ev->state() & ControlButton )
    00169                         {
    00170                             end();
    00171                             ev->accept();
    00172                             return true;
    00173                         }
    00174                     case Key_Home:
    00175                         if ( ev->state() & ControlButton )
    00176                         {
    00177                             home();
    00178                             ev->accept();
    00179                             return true;
    00180                         }
    00181                     default:
    00182                         break;
    00183                 }
    00184             }
    00185             else if ( type == QEvent::AccelOverride ) {
    00186                 // Override any acceleartors that match
    00187                 // the key sequences we use here...
    00188                 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
    00189                 switch ( ev->key() ) {
    00190                     case Key_Down:
    00191                     case Key_Up:
    00192                     case Key_Prior:
    00193                     case Key_Next:
    00194                     case Key_Escape:
    00195                     case Key_Enter:
    00196                     case Key_Return:
    00197                       ev->accept();
    00198                       return true;
    00199                       break;
    00200                     case Key_Tab:
    00201                     case Key_BackTab:
    00202                         if ( ev->state() == NoButton ||
    00203                             (ev->state() & ShiftButton))
    00204                         {
    00205                             ev->accept();
    00206                             return true;
    00207                         }
    00208                         break;
    00209                     case Key_Home:
    00210                     case Key_End:
    00211                         if ( ev->state() & ControlButton )
    00212                         {
    00213                             ev->accept();
    00214                             return true;
    00215                         }
    00216                         break;
    00217                     default:
    00218                         break;
    00219                 }
    00220             }
    00221
    00222             // parent loses focus or gets a click -> we hide
    00223             else if ( type == QEvent::FocusOut || type == QEvent::Resize ||
    00224                       type == QEvent::Close || type == QEvent::Hide ||
    00225                       type == QEvent::Move ) {
    00226                 hide();
    00227             }
    00228         }
    00229     }
    00230
    00231     // any mouse-click on something else than "this" makes us hide
    00232     else if ( type == QEvent::MouseButtonPress ) {
    00233         QMouseEvent *ev = static_cast<QMouseEvent *>( e );
    00234         if ( !rect().contains( ev->pos() )) // this widget
    00235             hide();
    00236
    00237         if ( !d->emitSelected && currentItem() && !::qt_cast<QScrollBar*>(o) )
    00238         {
    00239           emit highlighted( currentText() );
    00240           hide();
    00241           ev->accept();  // Consume the mouse click event...
    00242           return true;
    00243         }
    00244     }
    00245
    00246     return KListBox::eventFilter( o, e );
    00247 }
    00248
    00249
    00250 void KCompletionBox::popup()
    00251 {
    00252     if ( count() == 0 )
    00253         hide();
    00254     else {
    00255         ensureCurrentVisible();
    00256         bool block = signalsBlocked();
    00257         blockSignals( true );
    00258         setCurrentItem( 0 );
    00259         blockSignals( block );
    00260         clearSelection();
    00261         if ( !isVisible() )
    00262             show();
    00263         else if ( size().height() != sizeHint().height() )
    00264             sizeAndPosition();
    00265     }
    00266 }
    00267
    00268 void KCompletionBox::sizeAndPosition()
    00269 {
    00270     int currentGeom = height();
    00271     QPoint currentPos = pos();
    00272     QRect geom = calculateGeometry();
    00273     resize( geom.size() );
    00274
    00275     int x = currentPos.x(), y = currentPos.y();
    00276     if ( d->m_parent ) {
    00277       if ( !isVisible() ) {
    00278         QRect screenSize = KGlobalSettings::desktopGeometry(d->m_parent);
    00279
    00280         QPoint orig = d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
    00281         x = orig.x() + geom.x();
    00282         y = orig.y() + geom.y();
    00283
    00284         if ( x + width() > screenSize.right() )
    00285             x = screenSize.right() - width();
    00286         if (y + height() > screenSize.bottom() ) {
    00287             y = y - height() - d->m_parent->height();
    00288             d->upwardBox = true;
    00289         }
    00290       }
    00291       else {
    00292         // Are we above our parent? If so we must keep bottom edge anchored.
    00293         if (d->upwardBox)
    00294           y += (currentGeom-height());
    00295       }
    00296       move( x, y);
    00297     }
    00298 }
    00299
    00300 void KCompletionBox::show()
    00301 {
    00302     d->upwardBox = false;
    00303     if ( d->m_parent ) {
    00304         sizeAndPosition();
    00305         qApp->installEventFilter( this );
    00306     }
    00307
    00308     // ### we shouldn't need to call this, but without this, the scrollbars
    00309     // are pretty b0rked.
    00310     //triggerUpdate( true );
    00311
    00312     // Workaround for I'm not sure whose bug - if this KCompletionBox' parent
    00313     // is in a layout, that layout will detect inserting new child (posted
    00314     // ChildInserted event), and will trigger relayout (post LayoutHint event).
    00315     // QWidget::show() sends also posted ChildInserted events for the parent,
    00316     // and later all LayoutHint events, which causes layout updating.
    00317     // The problem is, KCompletionBox::eventFilter() detects resizing
    00318     // of the parent, and calls hide() - and this hide() happen in the middle
    00319     // of show(), causing inconsistent state. I'll try to submit a Qt patch too.
    00320     qApp->sendPostedEvents();
    00321     KListBox::show();
    00322 }
    00323
    00324 void KCompletionBox::hide()
    00325 {
    00326     if ( d->m_parent )
    00327         qApp->removeEventFilter( this );
    00328     d->cancelText = QString::null;
    00329     KListBox::hide();
    00330 }
    00331
    00332 QRect KCompletionBox::calculateGeometry() const
    00333 {
    00334     int x = 0, y = 0;
    00335     int ih = itemHeight();
    00336     int h = QMIN( 15 * ih, (int) count() * ih ) + 2*frameWidth();
    00337
    00338     int w = (d->m_parent) ? d->m_parent->width() : KListBox::minimumSizeHint().width();
    00339     w = QMAX( KListBox::minimumSizeHint().width(), w );
    00340
    00341     //If we're inside a combox, Qt by default makes the dropdown
    00342     // as wide as the combo, and gives the style a chance
    00343     // to adjust it. Do that here as well, for consistency
    00344     const QObject* combo;
    00345     if ( d->m_parent && (combo = d->m_parent->parent() ) &&
    00346         combo->inherits("QComboBox") )
    00347     {
    00348         const QComboBox* cb = static_cast<const QComboBox*>(combo);
    00349
    00350         //Expand to the combo width
    00351         w = QMAX( w, cb->width() );
    00352
    00353         QPoint parentCorner = d->m_parent->mapToGlobal(QPoint(0, 0));
    00354         QPoint comboCorner  = cb->mapToGlobal(QPoint(0, 0));
    00355
    00356         //We need to adjust our horizontal position to also be WRT to the combo
    00357         x += comboCorner.x() -  parentCorner.x();
    00358
    00359         //The same with vertical one
    00360         y += cb->height() - d->m_parent->height() +
    00361              comboCorner.y() - parentCorner.y();
    00362
    00363         //Ask the style to refine this a bit
    00364         QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox,
    00365                                     cb, QStyle::SC_ComboBoxListBoxPopup,
    00366                                     QStyleOption(x, y, w, h));
    00367         //QCommonStyle returns QRect() by default, so this is what we get if the
    00368         //style doesn't implement this
    00369         if (!styleAdj.isNull())
    00370             return styleAdj;
    00371
    00372     }
    00373     return QRect(x, y, w, h);
    00374 }
    00375
    00376 QSize KCompletionBox::sizeHint() const
    00377 {
    00378     return calculateGeometry().size();
    00379 }
    00380
    00381 void KCompletionBox::down()
    00382 {
    00383     int i = currentItem();
    00384
    00385     if ( i == 0 && d->down_workaround ) {
    00386         d->down_workaround = false;
    00387         setCurrentItem( 0 );
    00388         setSelected( 0, true );
    00389         emit highlighted( currentText() );
    00390     }
    00391
    00392     else if ( i < (int) count() - 1 )
    00393         setCurrentItem( i + 1 );
    00394 }
    00395
    00396 void KCompletionBox::up()
    00397 {
    00398     if ( currentItem() > 0 )
    00399         setCurrentItem( currentItem() - 1 );
    00400 }
    00401
    00402 void KCompletionBox::pageDown()
    00403 {
    00404     int i = currentItem() + numItemsVisible();
    00405     i = i > (int)count() - 1 ? (int)count() - 1 : i;
    00406     setCurrentItem( i );
    00407 }
    00408
    00409 void KCompletionBox::pageUp()
    00410 {
    00411     int i = currentItem() - numItemsVisible();
    00412     i = i < 0 ? 0 : i;
    00413     setCurrentItem( i );
    00414 }
    00415
    00416 void KCompletionBox::home()
    00417 {
    00418     setCurrentItem( 0 );
    00419 }
    00420
    00421 void KCompletionBox::end()
    00422 {
    00423     setCurrentItem( count() -1 );
    00424 }
    00425
    00426 void KCompletionBox::setTabHandling( bool enable )
    00427 {
    00428     d->tabHandling = enable;
    00429 }
    00430
    00431 bool KCompletionBox::isTabHandling() const
    00432 {
    00433     return d->tabHandling;
    00434 }
    00435
    00436 void KCompletionBox::setCancelledText( const QString& text )
    00437 {
    00438     d->cancelText = text;
    00439 }
    00440
    00441 QString KCompletionBox::cancelledText() const
    00442 {
    00443     return d->cancelText;
    00444 }
    00445
    00446 void KCompletionBox::canceled()
    00447 {
    00448     if ( !d->cancelText.isNull() )
    00449         emit userCancelled( d->cancelText );
    00450     if ( isVisible() )
    00451         hide();
    00452 }
    00453
    00454 class KCompletionBoxItem : public QListBoxItem
    00455 {
    00456 public:
    00457     //Returns true if dirty.
    00458     bool reuse( const QString& newText )
    00459     {
    00460         if ( text() == newText )
    00461             return false;
    00462         setText( newText );
    00463         return true;
    00464     }
    00465 };
    00466
    00467
    00468 void KCompletionBox::insertItems( const QStringList& items, int index )
    00469 {
    00470     bool block = signalsBlocked();
    00471     blockSignals( true );
    00472     insertStringList( items, index );
    00473     blockSignals( block );
    00474     d->down_workaround = true;
    00475 }
    00476
    00477 void KCompletionBox::setItems( const QStringList& items )
    00478 {
    00479     bool block = signalsBlocked();
    00480     blockSignals( true );
    00481
    00482     QListBoxItem* item = firstItem();
    00483     if ( !item ) {
    00484         insertStringList( items );
    00485     }
    00486     else {
    00487         //Keep track of whether we need to change anything,
    00488         //so we can avoid a repaint for identical updates,
    00489         //to reduce flicker
    00490         bool dirty = false;
    00491
    00492         QStringList::ConstIterator it = items.constBegin();
    00493         const QStringList::ConstIterator itEnd = items.constEnd();
    00494
    00495         for ( ; it != itEnd; ++it) {
    00496             if ( item ) {
    00497                 const bool changed = ((KCompletionBoxItem*)item)->reuse( *it );
    00498                 dirty = dirty || changed;
    00499                 item = item->next();
    00500             }
    00501             else {
    00502                 dirty = true;
    00503                 //Inserting an item is a way of making this dirty
    00504                 insertItem( new QListBoxText( *it ) );
    00505             }
    00506         }
    00507
    00508         //If there is an unused item, mark as dirty -> less items now
    00509         if ( item ) {
    00510             dirty = true;
    00511         }
    00512
    00513         QListBoxItem* tmp = item;
    00514         while ( (item = tmp ) ) {
    00515             tmp = item->next();
    00516             delete item;
    00517         }
    00518
    00519         if (dirty)
    00520             triggerUpdate( false );
    00521     }
    00522
    00523     if ( isVisible() && size().height() != sizeHint().height() )
    00524         sizeAndPosition();
    00525
    00526     blockSignals( block );
    00527     d->down_workaround = true;
    00528 }
    00529
    00530 void KCompletionBox::slotCurrentChanged()
    00531 {
    00532     d->down_workaround = false;
    00533 }
    00534
    00535 void KCompletionBox::slotItemClicked( QListBoxItem *item )
    00536 {
    00537     if ( item )
    00538     {
    00539         if ( d->down_workaround ) {
    00540             d->down_workaround = false;
    00541             emit highlighted( item->text() );
    00542         }
    00543
    00544         hide();
    00545         emit activated( item->text() );
    00546     }
    00547 }
    00548
    00549 void KCompletionBox::setActivateOnSelect(bool state)
    00550 {
    00551     d->emitSelected = state;
    00552 }
    00553
    00554 bool KCompletionBox::activateOnSelect() const
    00555 {
    00556     return d->emitSelected;
    00557 }
    00558
    00559 void KCompletionBox::virtual_hook( int id, void* data )
    00560 { KListBox::virtual_hook( id, data ); }
    00561
    00562 #include "kcompletionbox.moc"

This file is part of the documentation for kdeui Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:20:14 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003