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