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