• Skip to content
  • Skip to link menu
KDE 4.7 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 row = currentRow();
00407     const int lastRow = count() - 1;
00408     if (row < lastRow) {
00409         setCurrentRow(row + 1);
00410         return;
00411     }
00412 
00413     if (lastRow > -1) {
00414         setCurrentRow(0);
00415     }
00416 }
00417 
00418 void KCompletionBox::up()
00419 {
00420     const int row = currentRow();
00421     if (row > 0) {
00422         setCurrentRow(row - 1);
00423         return;
00424     }
00425 
00426     const int lastRow = count() - 1;
00427     if (lastRow > 0) {
00428         setCurrentRow(lastRow);
00429     }
00430 }
00431 
00432 void KCompletionBox::pageDown()
00433 {
00434     //int i = currentItem() + numItemsVisible();
00435     //i = i > (int)count() - 1 ? (int)count() - 1 : i;
00436     //setCurrentRow( i );
00437     moveCursor(QAbstractItemView::MovePageDown , Qt::NoModifier);
00438 }
00439 
00440 void KCompletionBox::pageUp()
00441 {
00442     //int i = currentItem() - numItemsVisible();
00443     //i = i < 0 ? 0 : i;
00444     //setCurrentRow( i );
00445 
00446     moveCursor(QAbstractItemView::MovePageUp , Qt::NoModifier);
00447 }
00448 
00449 void KCompletionBox::home()
00450 {
00451     setCurrentRow( 0 );
00452 }
00453 
00454 void KCompletionBox::end()
00455 {
00456     setCurrentRow( count() -1 );
00457 }
00458 
00459 void KCompletionBox::setTabHandling( bool enable )
00460 {
00461     d->tabHandling = enable;
00462 }
00463 
00464 bool KCompletionBox::isTabHandling() const
00465 {
00466     return d->tabHandling;
00467 }
00468 
00469 void KCompletionBox::setCancelledText( const QString& text )
00470 {
00471     d->cancelText = text;
00472 }
00473 
00474 QString KCompletionBox::cancelledText() const
00475 {
00476     return d->cancelText;
00477 }
00478 
00479 void KCompletionBox::canceled()
00480 {
00481     if ( !d->cancelText.isNull() )
00482         emit userCancelled( d->cancelText );
00483     if ( isVisible() )
00484         hide();
00485 }
00486 
00487 class KCompletionBoxItem : public QListWidgetItem
00488 {
00489 public:
00490     //Returns true if dirty.
00491     bool reuse( const QString& newText )
00492     {
00493         if ( text() == newText )
00494             return false;
00495         setText( newText );
00496         return true;
00497     }
00498 };
00499 
00500 
00501 void KCompletionBox::insertItems( const QStringList& items, int index )
00502 {
00503     bool block = signalsBlocked();
00504     blockSignals( true );
00505     KListWidget::insertItems( index, items );
00506     blockSignals( block );
00507     setCurrentRow(-1);
00508 }
00509 
00510 void KCompletionBox::setItems( const QStringList& items )
00511 {
00512     bool block = signalsBlocked();
00513     blockSignals( true );
00514 
00515     int rowIndex = 0;
00516 
00517     if (!count()) {
00518         addItems(items);
00519     } else {
00520         // Keep track of whether we need to change anything,
00521         // so we can avoid a repaint for identical updates,
00522         // to reduce flicker
00523         bool dirty = false;
00524 
00525         QStringList::ConstIterator it = items.constBegin();
00526         const QStringList::ConstIterator itEnd = items.constEnd();
00527 
00528         for ( ; it != itEnd; ++it) {
00529             if ( rowIndex < count() ) {
00530                 const bool changed = ((KCompletionBoxItem*)item(rowIndex))->reuse( *it );
00531                 dirty = dirty || changed;
00532             } else {
00533                 dirty = true;
00534                 // Inserting an item is a way of making this dirty
00535                 addItem(*it);
00536             }
00537             rowIndex++;
00538         }
00539 
00540         // If there is an unused item, mark as dirty -> less items now
00541         if (rowIndex < count()) {
00542             dirty = true;
00543         }
00544 
00545         // remove unused items with an index >= rowIndex
00546         for ( ; rowIndex < count() ; ) {
00547             QListWidgetItem* item = takeItem(rowIndex);
00548             Q_ASSERT(item);
00549             delete item;
00550         }
00551 
00552         //TODO KDE4 : Port me
00553         //if (dirty)
00554         //    triggerUpdate( false );
00555     }
00556 
00557     if (isVisible() && size().height() != sizeHint().height())
00558         sizeAndPosition();
00559 
00560     blockSignals(block);
00561 }
00562 
00563 void KCompletionBox::slotItemClicked( QListWidgetItem *item )
00564 {
00565     if ( item )
00566     {
00567         hide();
00568         emit currentTextChanged( item->text() );
00569         emit activated( item->text() );
00570     }
00571 }
00572 
00573 void KCompletionBox::setActivateOnSelect(bool state)
00574 {
00575     d->emitSelected = state;
00576 }
00577 
00578 bool KCompletionBox::activateOnSelect() const
00579 {
00580     return d->emitSelected;
00581 }
00582 
00583 #include "kcompletionbox.moc"

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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