• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kdeui

kcompletionbox.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00020    Boston, MA 02110-1301, 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"

kdeui

Skip menu "kdeui"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal