• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • 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 "kcompletionbox.h"
00025 
00026 #include <QtCore/QEvent>
00027 #include <QtGui/QApplication>
00028 #include <QtGui/QComboBox>
00029 #include <QtGui/QStyle>
00030 #include <QtGui/QScrollBar>
00031 #include <QtGui/QKeyEvent>
00032 
00033 #include <kdebug.h>
00034 #include <kconfig.h>
00035 #include <kglobalsettings.h>
00036 
00037 class KCompletionBox::KCompletionBoxPrivate
00038 {
00039 public:
00040     QWidget *m_parent; // necessary to set the focus back
00041     QString cancelText;
00042     bool tabHandling : 1;
00043     bool down_workaround : 1;
00044     bool upwardBox : 1;
00045     bool emitSelected : 1;
00046 };
00047 
00048 KCompletionBox::KCompletionBox( QWidget *parent )
00049  :KListWidget( parent), d(new KCompletionBoxPrivate)
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     setWindowFlags(Qt::Popup); // calls setVisible, so must be done after initializations
00058 
00059     setLineWidth( 1 );
00060     setFrameStyle( QFrame::Box | QFrame::Plain );
00061 
00062     if ( parent ) {
00063         setFocusProxy( parent );
00064     } else
00065         setFocusPolicy( Qt::NoFocus );
00066 
00067     setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
00068     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00069 
00070     connect( this, SIGNAL( itemDoubleClicked( QListWidgetItem * )),
00071              SLOT( slotActivated( QListWidgetItem * )) );
00072 
00073 #ifdef __GNUC__
00074 #warning "Check if this workaround can be removed in KDE 4"
00075 #endif
00076 
00077     // grmbl, just QListBox workarounds :[ Thanks Volker.
00078     connect( this, SIGNAL( currentItemChanged( QListWidgetItem * , QListWidgetItem * )),
00079              SLOT( slotCurrentChanged() ));
00080     connect( this, SIGNAL( itemClicked( QListWidgetItem * )),
00081              SLOT( slotItemClicked( QListWidgetItem * )) );
00082 }
00083 
00084 KCompletionBox::~KCompletionBox()
00085 {
00086     d->m_parent = 0L;
00087     delete d;
00088 }
00089 
00090 QStringList KCompletionBox::items() const
00091 {
00092     QStringList list;
00093 
00094     for (int i = 0 ; i < count() ; i++)
00095     {
00096         const QListWidgetItem* currItem = item(i);
00097 
00098         list.append(currItem->text());
00099     }
00100 
00101     return list;
00102 }
00103 
00104 void KCompletionBox::slotActivated( QListWidgetItem *item )
00105 {
00106     if ( !item )
00107         return;
00108 
00109     hide();
00110     emit activated( item->text() );
00111 }
00112 
00113 bool KCompletionBox::eventFilter( QObject *o, QEvent *e )
00114 {
00115     int type = e->type();
00116 
00117     if ((o==this) && (type==QEvent::KeyPress)) {
00118     //not sure if this is a work around for a Qt bug or a fix for changed qt behaviour, at least it makes the kcombobox work again for me
00119     QApplication::sendEvent(d->m_parent,e);
00120     return true;
00121     }
00122 
00123     if ( o == d->m_parent ) {
00124         if ( isVisible() ) {
00125             if ( type == QEvent::KeyPress ) {
00126                 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00127                 switch ( ev->key() ) {
00128                     case Qt::Key_Backtab:
00129                         if ( d->tabHandling && (ev->modifiers() == Qt::NoButton ||
00130                              (ev->modifiers() & Qt::ShiftModifier)) ) {
00131                             up();
00132                             ev->accept();
00133                             return true;
00134                         }
00135                         break;
00136                     case Qt::Key_Tab:
00137                         if ( d->tabHandling && (ev->modifiers() == Qt::NoButton) ) {
00138                             down(); // Only on TAB!!
00139                             ev->accept();
00140                             return true;
00141                         }
00142                         break;
00143                     case Qt::Key_Down:
00144                         down();
00145                         ev->accept();
00146                         return true;
00147                     case Qt::Key_Up:
00148                         // If there is no selected item and we've popped up above
00149                         // our parent, select the first item when they press up.
00150                         if ( !selectedItems().isEmpty() ||
00151                              mapToGlobal( QPoint( 0, 0 ) ).y() >
00152                              d->m_parent->mapToGlobal( QPoint( 0, 0 ) ).y() )
00153                             up();
00154                         else
00155                             down();
00156                         ev->accept();
00157                         return true;
00158                     case Qt::Key_PageUp:
00159                         pageUp();
00160                         ev->accept();
00161                         return true;
00162                     case Qt::Key_PageDown:
00163                         pageDown();
00164                         ev->accept();
00165                         return true;
00166                     case Qt::Key_Escape:
00167                         canceled();
00168                         ev->accept();
00169                         return true;
00170                     case Qt::Key_Enter:
00171                     case Qt::Key_Return:
00172                         if ( ev->modifiers() & Qt::ShiftModifier ) {
00173                             hide();
00174                             ev->accept();  // Consume the Enter event
00175                             return true;
00176                         }
00177                         break;
00178                     case Qt::Key_End:
00179                         if ( ev->modifiers() & Qt::ControlModifier )
00180                         {
00181                             end();
00182                             ev->accept();
00183                             return true;
00184                         }
00185                     case Qt::Key_Home:
00186                         if ( ev->modifiers() & Qt::ControlModifier )
00187                         {
00188                             home();
00189                             ev->accept();
00190                             return true;
00191                         }
00192                     default:
00193                         break;
00194                 }
00195             }
00196             else if ( type == QEvent::ShortcutOverride ) {
00197                 // Override any accelerators that match
00198                 // the key sequences we use here...
00199                 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00200                 switch ( ev->key() ) {
00201                     case Qt::Key_Down:
00202                     case Qt::Key_Up:
00203                     case Qt::Key_PageUp:
00204                     case Qt::Key_PageDown:
00205                     case Qt::Key_Escape:
00206                     case Qt::Key_Enter:
00207                     case Qt::Key_Return:
00208                       ev->accept();
00209                       return true;
00210                       break;
00211                     case Qt::Key_Tab:
00212                     case Qt::Key_Backtab:
00213                         if ( ev->modifiers() == Qt::NoButton ||
00214                             (ev->modifiers() & Qt::ShiftModifier))
00215                         {
00216                             ev->accept();
00217                             return true;
00218                         }
00219                         break;
00220                     case Qt::Key_Home:
00221                     case Qt::Key_End:
00222                         if ( ev->modifiers() & Qt::ControlModifier )
00223                         {
00224                             ev->accept();
00225                             return true;
00226                         }
00227                         break;
00228                     default:
00229                         break;
00230                 }
00231             }
00232 
00233             // parent gets a click -> we hide
00234             else if ( type == QEvent::Resize ||
00235                       type == QEvent::Close || type == QEvent::Hide ) {
00236                 hide();
00237             }
00238             
00239             else if ( type == QEvent::FocusOut ) {
00240                   QFocusEvent* event = static_cast<QFocusEvent*>( e );
00241                   if (event->reason() != Qt::PopupFocusReason)
00242                     hide();
00243             }
00244             
00245         }
00246     }
00247 
00248     // any mouse-click on something else than "this" makes us hide
00249     else if ( type == QEvent::MouseButtonPress ) {
00250         QMouseEvent *ev = static_cast<QMouseEvent *>( e );
00251         if ( !rect().contains( ev->pos() )) // this widget
00252             hide();
00253 
00254         if ( !d->emitSelected && currentItem() && !qobject_cast<QScrollBar*>(o) )
00255         {
00256           Q_ASSERT( currentItem() );
00257 
00258           emit currentTextChanged( currentItem()->text() );
00259           hide();
00260           ev->accept();  // Consume the mouse click event...
00261           return true;
00262         }
00263     }
00264 
00265     return KListWidget::eventFilter( o, e );
00266 }
00267 
00268 
00269 void KCompletionBox::popup()
00270 {
00271     if ( count() == 0 )
00272         hide();
00273     else {
00274         //TODO KDE 4 - Port: ensureCurrentVisible();
00275         bool block = signalsBlocked();
00276         blockSignals( true );
00277         setCurrentItem( 0 );
00278         blockSignals( block );
00279         clearSelection();
00280         if ( !isVisible() )
00281             show();
00282         else if ( size().height() != sizeHint().height() )
00283             sizeAndPosition();
00284     }
00285 }
00286 
00287 void KCompletionBox::sizeAndPosition()
00288 {
00289     int currentGeom = height();
00290     QPoint currentPos = pos();
00291     QRect geom = calculateGeometry();
00292     resize( geom.size() );
00293 
00294     int x = currentPos.x(), y = currentPos.y();
00295     if ( d->m_parent ) {
00296       if ( !isVisible() ) {
00297         QRect screenSize = KGlobalSettings::desktopGeometry(d->m_parent);
00298 
00299         QPoint orig = globalPositionHint();
00300         x = orig.x() + geom.x();
00301         y = orig.y() + geom.y();
00302 
00303         if ( x + width() > screenSize.right() )
00304             x = screenSize.right() - width();
00305         if (y + height() > screenSize.bottom() ) {
00306             y = y - height() - d->m_parent->height();
00307             d->upwardBox = true;
00308         }
00309       }
00310       else {
00311         // Are we above our parent? If so we must keep bottom edge anchored.
00312         if (d->upwardBox)
00313           y += (currentGeom-height());
00314       }
00315       move( x, y);
00316     }
00317 }
00318 
00319 QPoint KCompletionBox::globalPositionHint() const
00320 {
00321     if (!d->m_parent)
00322         return QPoint();
00323     return d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
00324 }
00325 
00326 void KCompletionBox::setVisible( bool visible )
00327 {
00328     if (visible) {
00329         d->upwardBox = false;
00330         if ( d->m_parent ) {
00331             sizeAndPosition();
00332             qApp->installEventFilter( this );
00333         }
00334 
00335         // ### we shouldn't need to call this, but without this, the scrollbars
00336         // are pretty b0rked.
00337         //triggerUpdate( true );
00338 
00339         // Workaround for I'm not sure whose bug - if this KCompletionBox' parent
00340         // is in a layout, that layout will detect inserting new child (posted
00341         // ChildInserted event), and will trigger relayout (post LayoutHint event).
00342         // QWidget::show() sends also posted ChildInserted events for the parent,
00343         // and later all LayoutHint events, which causes layout updating.
00344         // The problem is, KCompletionBox::eventFilter() detects resizing
00345         // of the parent, and calls hide() - and this hide() happen in the middle
00346         // of show(), causing inconsistent state. I'll try to submit a Qt patch too.
00347         qApp->sendPostedEvents();
00348     } else {
00349         if ( d->m_parent )
00350             qApp->removeEventFilter( this );
00351         d->cancelText.clear();
00352     }
00353   
00354     KListWidget::setVisible(visible);
00355 }
00356 
00357 QRect KCompletionBox::calculateGeometry() const
00358 {
00359     if (count() == 0)
00360         return QRect();
00361 
00362     int x = 0, y = 0;
00363 
00364     Q_ASSERT( visualItemRect(item(0)).isValid() );
00365 
00366     int ih = visualItemRect(item(0)).height();
00367     int h = qMin( 15 * ih, (int) count() * ih ) + 2*frameWidth();
00368 
00369     int w = (d->m_parent) ? d->m_parent->width() : KListWidget::minimumSizeHint().width();
00370     w = qMax( KListWidget::minimumSizeHint().width(), w );
00371 
00372   //### M.O.: Qt4 doesn't actually honor SC_ComboBoxListBoxPopup ???
00373 #if 0
00374     //If we're inside a combox, Qt by default makes the dropdown
00375     // as wide as the combo, and gives the style a chance
00376     // to adjust it. Do that here as well, for consistency
00377     const QObject* combo;
00378     if ( d->m_parent && (combo = d->m_parent->parent() ) &&
00379         qobject_cast<QComboBox*>(combo) )
00380     {
00381         const QComboBox* cb = static_cast<const QComboBox*>(combo);
00382 
00383         //Expand to the combo width
00384         w = qMax( w, cb->width() );
00385 
00386         QPoint parentCorner = d->m_parent->mapToGlobal(QPoint(0, 0));
00387         QPoint comboCorner  = cb->mapToGlobal(QPoint(0, 0));
00388 
00389         //We need to adjust our horizontal position to also be WRT to the combo
00390         x += comboCorner.x() -  parentCorner.x();
00391 
00392         //The same with vertical one
00393         y += cb->height() - d->m_parent->height() +
00394              comboCorner.y() - parentCorner.y();
00395 
00396         //Ask the style to refine this a bit
00397         QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox,
00398                                     cb, QStyle::SC_ComboBoxListBoxPopup,
00399                                     QStyleOption(x, y, w, h));
00400         //QCommonStyle returns QRect() by default, so this is what we get if the
00401         //style doesn't implement this
00402         if (!styleAdj.isNull())
00403             return styleAdj;
00404 
00405     }
00406 #endif
00407     return QRect(x, y, w, h);
00408 }
00409 
00410 QSize KCompletionBox::sizeHint() const
00411 {
00412     return calculateGeometry().size();
00413 }
00414 
00415 void KCompletionBox::down()
00416 {
00417     int i = currentRow();
00418 
00419     if ( i == 0 && d->down_workaround ) {
00420         d->down_workaround = false;
00421         setCurrentRow( 0 );
00422         item(0)->setSelected(true);
00423         emit currentTextChanged( currentItem()->text() );
00424     }
00425 
00426     else if ( i < (int) count() - 1 )
00427         setCurrentRow( i + 1 );
00428 }
00429 
00430 void KCompletionBox::up()
00431 {
00432     if ( currentItem() && row(currentItem()) > 0 )
00433         setCurrentItem( item(row(currentItem()) - 1) );
00434 }
00435 
00436 void KCompletionBox::pageDown()
00437 {
00438     //int i = currentItem() + numItemsVisible();
00439     //i = i > (int)count() - 1 ? (int)count() - 1 : i;
00440     //setCurrentRow( i );
00441     moveCursor(QAbstractItemView::MovePageDown , Qt::NoModifier);
00442 }
00443 
00444 void KCompletionBox::pageUp()
00445 {
00446     //int i = currentItem() - numItemsVisible();
00447     //i = i < 0 ? 0 : i;
00448     //setCurrentRow( i );
00449     
00450     moveCursor(QAbstractItemView::MovePageUp , Qt::NoModifier);
00451 }
00452 
00453 void KCompletionBox::home()
00454 {
00455     setCurrentItem( 0 );
00456 }
00457 
00458 void KCompletionBox::end()
00459 {
00460     setCurrentRow( count() -1 );
00461 }
00462 
00463 void KCompletionBox::setTabHandling( bool enable )
00464 {
00465     d->tabHandling = enable;
00466 }
00467 
00468 bool KCompletionBox::isTabHandling() const
00469 {
00470     return d->tabHandling;
00471 }
00472 
00473 void KCompletionBox::setCancelledText( const QString& text )
00474 {
00475     d->cancelText = text;
00476 }
00477 
00478 QString KCompletionBox::cancelledText() const
00479 {
00480     return d->cancelText;
00481 }
00482 
00483 void KCompletionBox::canceled()
00484 {
00485     if ( !d->cancelText.isNull() )
00486         emit userCancelled( d->cancelText );
00487     if ( isVisible() )
00488         hide();
00489 }
00490 
00491 class KCompletionBoxItem : public QListWidgetItem
00492 {
00493 public:
00494     //Returns true if dirty.
00495     bool reuse( const QString& newText )
00496     {
00497         if ( text() == newText )
00498             return false;
00499         setText( newText );
00500         return true;
00501     }
00502 };
00503 
00504 
00505 void KCompletionBox::insertItems( const QStringList& items, int index )
00506 {
00507     bool block = signalsBlocked();
00508     blockSignals( true );
00509     KListWidget::insertItems( index, items );
00510     blockSignals( block );
00511     d->down_workaround = true;
00512 }
00513 
00514 void KCompletionBox::setItems( const QStringList& items )
00515 {
00516     bool block = signalsBlocked();
00517     blockSignals( true );
00518 
00519     int rowIndex = 0;
00520 
00521     if ( rowIndex >= count() ) {
00522         addItems( items );
00523     }
00524     else {
00525         //Keep track of whether we need to change anything,
00526         //so we can avoid a repaint for identical updates,
00527         //to reduce flicker
00528         bool dirty = false;
00529 
00530         QStringList::ConstIterator it = items.constBegin();
00531         const QStringList::ConstIterator itEnd = items.constEnd();
00532 
00533         for ( ; it != itEnd; ++it) {
00534             if ( rowIndex < count() ) {
00535                 const bool changed = ((KCompletionBoxItem*)item(rowIndex))->reuse( *it );
00536                 dirty = dirty || changed;
00537                 
00538                 rowIndex++;
00539             }
00540             else {
00541                 dirty = true;
00542                 //Inserting an item is a way of making this dirty
00543                 addItem( *it );
00544             }
00545         }
00546 
00547         //If there is an unused item, mark as dirty -> less items now
00548         if ( rowIndex < count() ) {
00549             dirty = true;
00550         }
00551 
00552         // remove unused items with an index >= rowIndex
00553         for ( ; rowIndex < count() ; ) {
00554             QListWidgetItem* item = takeItem(rowIndex);
00555             
00556             Q_ASSERT(item);
00557 
00558             delete item;
00559         }
00560         
00561         //TODO KDE4 : Port me
00562         //if (dirty)
00563         //    triggerUpdate( false );
00564     }
00565 
00566     if ( isVisible() && size().height() != sizeHint().height() )
00567         sizeAndPosition();
00568 
00569     blockSignals( block );
00570     d->down_workaround = true;
00571 }
00572 
00573 void KCompletionBox::slotSetCurrentItem( QListWidgetItem *i )
00574 {
00575     setCurrentItem( i ); // grrr
00576 }
00577 
00578 void KCompletionBox::slotCurrentChanged()
00579 {
00580     if (currentItem())
00581       emit currentTextChanged(currentItem()->text());
00582     d->down_workaround = false;
00583 }
00584 
00585 void KCompletionBox::slotItemClicked( QListWidgetItem *item )
00586 {
00587     if ( item )
00588     {
00589         if ( d->down_workaround ) {
00590             d->down_workaround = false;
00591             emit currentTextChanged( item->text() );
00592         }
00593 
00594         hide();
00595         emit currentTextChanged( item->text() );
00596         emit activated( item->text() );
00597     }
00598 }
00599 
00600 void KCompletionBox::setActivateOnSelect(bool state)
00601 {
00602     d->emitSelected = state;
00603 }
00604 
00605 bool KCompletionBox::activateOnSelect() const
00606 {
00607     return d->emitSelected;
00608 }
00609 
00610 #include "kcompletionbox.moc"

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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