• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 API Reference
  • 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             case Qt::Key_Tab:
00227             case Qt::Key_Backtab:
00228                 if ( ev->modifiers() == Qt::NoButton ||
00229                      (ev->modifiers() & Qt::ShiftModifier))
00230                 {
00231                     ev->accept();
00232                     return true;
00233                 }
00234                 break;
00235             case Qt::Key_Home:
00236             case Qt::Key_End:
00237                 if ( ev->modifiers() & Qt::ControlModifier )
00238                 {
00239                     ev->accept();
00240                     return true;
00241                 }
00242                 break;
00243             default:
00244                 break;
00245             }
00246         } else if ( type == QEvent::FocusOut ) {
00247             QFocusEvent* event = static_cast<QFocusEvent*>( e );
00248             if (event->reason() != Qt::PopupFocusReason
00249 #ifdef Q_WS_WIN
00250                 && (event->reason() != Qt::ActiveWindowFocusReason || QApplication::activeWindow() != this)
00251 #endif
00252                 )
00253                 hide();
00254         }
00255     }
00256 
00257     return KListWidget::eventFilter( o, e );
00258 }
00259 
00260 void KCompletionBox::popup()
00261 {
00262     if ( count() == 0 )
00263         hide();
00264     else {
00265         bool block = signalsBlocked();
00266         blockSignals( true );
00267         setCurrentRow( -1 );
00268         blockSignals( block );
00269         clearSelection();
00270         if ( !isVisible() )
00271             show();
00272         else if ( size().height() != sizeHint().height() )
00273             sizeAndPosition();
00274     }
00275 }
00276 
00277 void KCompletionBox::sizeAndPosition()
00278 {
00279     int currentGeom = height();
00280     QPoint currentPos = pos();
00281     QRect geom = calculateGeometry();
00282     resize( geom.size() );
00283 
00284     int x = currentPos.x(), y = currentPos.y();
00285     if ( d->m_parent ) {
00286       if ( !isVisible() ) {
00287         QPoint orig = globalPositionHint();
00288         QRect screenSize = KGlobalSettings::desktopGeometry(orig);
00289 
00290         x = orig.x() + geom.x();
00291         y = orig.y() + geom.y();
00292 
00293         if ( x + width() > screenSize.right() )
00294             x = screenSize.right() - width();
00295         if (y + height() > screenSize.bottom() ) {
00296             y = y - height() - d->m_parent->height();
00297             d->upwardBox = true;
00298         }
00299       }
00300       else {
00301         // Are we above our parent? If so we must keep bottom edge anchored.
00302         if (d->upwardBox)
00303           y += (currentGeom-height());
00304       }
00305       move( x, y);
00306     }
00307 }
00308 
00309 QPoint KCompletionBox::globalPositionHint() const
00310 {
00311     if (!d->m_parent)
00312         return QPoint();
00313     return d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
00314 }
00315 
00316 void KCompletionBox::setVisible( bool visible )
00317 {
00318     if (visible) {
00319         d->upwardBox = false;
00320         if ( d->m_parent ) {
00321             sizeAndPosition();
00322             qApp->installEventFilter( this );
00323         }
00324 
00325         // ### we shouldn't need to call this, but without this, the scrollbars
00326         // are pretty b0rked.
00327         //triggerUpdate( true );
00328 
00329         // Workaround for I'm not sure whose bug - if this KCompletionBox' parent
00330         // is in a layout, that layout will detect inserting new child (posted
00331         // ChildInserted event), and will trigger relayout (post LayoutHint event).
00332         // QWidget::show() sends also posted ChildInserted events for the parent,
00333         // and later all LayoutHint events, which causes layout updating.
00334         // The problem is, KCompletionBox::eventFilter() detects resizing
00335         // of the parent, and calls hide() - and this hide() happen in the middle
00336         // of show(), causing inconsistent state. I'll try to submit a Qt patch too.
00337         qApp->sendPostedEvents();
00338     } else {
00339         if ( d->m_parent )
00340             qApp->removeEventFilter( this );
00341         d->cancelText.clear();
00342     }
00343 
00344     KListWidget::setVisible(visible);
00345 }
00346 
00347 QRect KCompletionBox::calculateGeometry() const
00348 {
00349     QRect visualRect;
00350     if (count() == 0 || !(visualRect = visualItemRect(item(0))).isValid())
00351         return QRect();
00352 
00353     int x = 0, y = 0;
00354     int ih = visualRect.height();
00355     int h = qMin( 15 * ih, (int) count() * ih ) + 2*frameWidth();
00356 
00357     int w = (d->m_parent) ? d->m_parent->width() : KListWidget::minimumSizeHint().width();
00358     w = qMax( KListWidget::minimumSizeHint().width(), w );
00359 
00360   //### M.O.: Qt4 doesn't actually honor SC_ComboBoxListBoxPopup ???
00361 #if 0
00362     //If we're inside a combox, Qt by default makes the dropdown
00363     // as wide as the combo, and gives the style a chance
00364     // to adjust it. Do that here as well, for consistency
00365     const QObject* combo;
00366     if ( d->m_parent && (combo = d->m_parent->parent() ) &&
00367         qobject_cast<QComboBox*>(combo) )
00368     {
00369         const QComboBox* cb = static_cast<const QComboBox*>(combo);
00370 
00371         //Expand to the combo width
00372         w = qMax( w, cb->width() );
00373 
00374         QPoint parentCorner = d->m_parent->mapToGlobal(QPoint(0, 0));
00375         QPoint comboCorner  = cb->mapToGlobal(QPoint(0, 0));
00376 
00377         //We need to adjust our horizontal position to also be WRT to the combo
00378         x += comboCorner.x() -  parentCorner.x();
00379 
00380         //The same with vertical one
00381         y += cb->height() - d->m_parent->height() +
00382              comboCorner.y() - parentCorner.y();
00383 
00384         //Ask the style to refine this a bit
00385         QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox,
00386                                     cb, QStyle::SC_ComboBoxListBoxPopup,
00387                                     QStyleOption(x, y, w, h));
00388         //QCommonStyle returns QRect() by default, so this is what we get if the
00389         //style doesn't implement this
00390         if (!styleAdj.isNull())
00391             return styleAdj;
00392 
00393     }
00394 #endif
00395     return QRect(x, y, w, h);
00396 }
00397 
00398 QSize KCompletionBox::sizeHint() const
00399 {
00400     return calculateGeometry().size();
00401 }
00402 
00403 void KCompletionBox::down()
00404 {
00405     const int row = currentRow();
00406     const int lastRow = count() - 1;
00407     if (row < lastRow) {
00408         setCurrentRow(row + 1);
00409         return;
00410     }
00411 
00412     if (lastRow > -1) {
00413         setCurrentRow(0);
00414     }
00415 }
00416 
00417 void KCompletionBox::up()
00418 {
00419     const int row = currentRow();
00420     if (row > 0) {
00421         setCurrentRow(row - 1);
00422         return;
00423     }
00424 
00425     const int lastRow = count() - 1;
00426     if (lastRow > 0) {
00427         setCurrentRow(lastRow);
00428     }
00429 }
00430 
00431 void KCompletionBox::pageDown()
00432 {
00433     //int i = currentItem() + numItemsVisible();
00434     //i = i > (int)count() - 1 ? (int)count() - 1 : i;
00435     //setCurrentRow( i );
00436     moveCursor(QAbstractItemView::MovePageDown , Qt::NoModifier);
00437 }
00438 
00439 void KCompletionBox::pageUp()
00440 {
00441     //int i = currentItem() - numItemsVisible();
00442     //i = i < 0 ? 0 : i;
00443     //setCurrentRow( i );
00444 
00445     moveCursor(QAbstractItemView::MovePageUp , Qt::NoModifier);
00446 }
00447 
00448 void KCompletionBox::home()
00449 {
00450     setCurrentRow( 0 );
00451 }
00452 
00453 void KCompletionBox::end()
00454 {
00455     setCurrentRow( count() -1 );
00456 }
00457 
00458 void KCompletionBox::setTabHandling( bool enable )
00459 {
00460     d->tabHandling = enable;
00461 }
00462 
00463 bool KCompletionBox::isTabHandling() const
00464 {
00465     return d->tabHandling;
00466 }
00467 
00468 void KCompletionBox::setCancelledText( const QString& text )
00469 {
00470     d->cancelText = text;
00471 }
00472 
00473 QString KCompletionBox::cancelledText() const
00474 {
00475     return d->cancelText;
00476 }
00477 
00478 void KCompletionBox::canceled()
00479 {
00480     if ( !d->cancelText.isNull() )
00481         emit userCancelled( d->cancelText );
00482     if ( isVisible() )
00483         hide();
00484 }
00485 
00486 class KCompletionBoxItem : public QListWidgetItem
00487 {
00488 public:
00489     //Returns true if dirty.
00490     bool reuse( const QString& newText )
00491     {
00492         if ( text() == newText )
00493             return false;
00494         setText( newText );
00495         return true;
00496     }
00497 };
00498 
00499 
00500 void KCompletionBox::insertItems( const QStringList& items, int index )
00501 {
00502     bool block = signalsBlocked();
00503     blockSignals( true );
00504     KListWidget::insertItems( index, items );
00505     blockSignals( block );
00506     setCurrentRow(-1);
00507 }
00508 
00509 void KCompletionBox::setItems( const QStringList& items )
00510 {
00511     bool block = signalsBlocked();
00512     blockSignals( true );
00513 
00514     int rowIndex = 0;
00515 
00516     if (!count()) {
00517         addItems(items);
00518     } else {
00519         // Keep track of whether we need to change anything,
00520         // so we can avoid a repaint for identical updates,
00521         // to reduce flicker
00522         bool dirty = false;
00523 
00524         QStringList::ConstIterator it = items.constBegin();
00525         const QStringList::ConstIterator itEnd = items.constEnd();
00526 
00527         for ( ; it != itEnd; ++it) {
00528             if ( rowIndex < count() ) {
00529                 const bool changed = ((KCompletionBoxItem*)item(rowIndex))->reuse( *it );
00530                 dirty = dirty || changed;
00531             } else {
00532                 dirty = true;
00533                 // Inserting an item is a way of making this dirty
00534                 addItem(*it);
00535             }
00536             rowIndex++;
00537         }
00538 
00539         // If there is an unused item, mark as dirty -> less items now
00540         if (rowIndex < count()) {
00541             dirty = true;
00542         }
00543 
00544         // remove unused items with an index >= rowIndex
00545         for ( ; rowIndex < count() ; ) {
00546             QListWidgetItem* item = takeItem(rowIndex);
00547             Q_ASSERT(item);
00548             delete item;
00549         }
00550 
00551         //TODO KDE4 : Port me
00552         //if (dirty)
00553         //    triggerUpdate( false );
00554     }
00555 
00556     if (isVisible() && size().height() != sizeHint().height())
00557         sizeAndPosition();
00558 
00559     blockSignals(block);
00560 }
00561 
00562 void KCompletionBox::slotItemClicked( QListWidgetItem *item )
00563 {
00564     if ( item )
00565     {
00566         hide();
00567         emit currentTextChanged( item->text() );
00568         emit activated( item->text() );
00569     }
00570 }
00571 
00572 void KCompletionBox::setActivateOnSelect(bool state)
00573 {
00574     d->emitSelected = state;
00575 }
00576 
00577 bool KCompletionBox::activateOnSelect() const
00578 {
00579     return d->emitSelected;
00580 }
00581 
00582 #include "kcompletionbox.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:53:02 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

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-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal