kdeui Library API Documentation

kcombobox.cpp

00001 /* This file is part of the KDE libraries
00002 
00003    Copyright (c) 2000,2001 Dawit Alemayehu <adawit@kde.org>
00004    Copyright (c) 2000,2001 Carsten Pfeiffer <pfeiffer@kde.org>
00005    Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de>
00006 
00007    This library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser 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    Lesser General Public License for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public License
00018    along with this library; see the file COPYING.LIB.  If not, write to
00019    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00020    Boston, MA 02111-1307, USA.
00021 */
00022 
00023 #include <qclipboard.h>
00024 #include <qlistbox.h>
00025 #include <qpopupmenu.h>
00026 #include <qapplication.h>
00027 
00028 #include <kcompletionbox.h>
00029 #include <kcursor.h>
00030 #include <kiconloader.h>
00031 #include <kicontheme.h>
00032 #include <klineedit.h>
00033 #include <klocale.h>
00034 #include <knotifyclient.h>
00035 #include <kpixmapprovider.h>
00036 #include <kstdaccel.h>
00037 #include <kurl.h>
00038 #include <kurldrag.h>
00039 
00040 #include <kdebug.h>
00041 
00042 #include "kcombobox.h"
00043 
00044 #include <stdlib.h> // getenv
00045 
00046 class KComboBox::KComboBoxPrivate
00047 {
00048 public:
00049     KComboBoxPrivate() : klineEdit(0L)
00050     {
00051     }
00052     ~KComboBoxPrivate()
00053     {
00054     }
00055 
00056     KLineEdit *klineEdit;
00057 };
00058 
00059 KComboBox::KComboBox( QWidget *parent, const char *name )
00060     : QComboBox( parent, name ), d(new KComboBoxPrivate)
00061 {
00062     init();
00063 }
00064 
00065 KComboBox::KComboBox( bool rw, QWidget *parent, const char *name )
00066     : QComboBox( rw, parent, name ), d(new KComboBoxPrivate)
00067 {
00068     init();
00069 
00070     if ( rw )
00071     {
00072       KLineEdit *edit = new KLineEdit( this, "combo lineedit" );
00073       setLineEdit( edit );
00074     }
00075 }
00076 
00077 KComboBox::~KComboBox()
00078 {
00079     delete d;
00080 }
00081 
00082 void KComboBox::init()
00083 {
00084     // Permanently set some parameters in the parent object.
00085     QComboBox::setAutoCompletion( false );
00086 
00087     // Enable context menu by default if widget
00088     // is editable.
00089     setContextMenuEnabled( true );
00090 }
00091 
00092 
00093 bool KComboBox::contains( const QString& _text ) const
00094 {
00095     if ( _text.isEmpty() )
00096         return false;
00097 
00098     const int itemCount = count();
00099     for (int i = 0; i < itemCount; ++i )
00100     {
00101         if ( text(i) == _text )
00102             return true;
00103     }
00104     return false;
00105 }
00106 
00107 void KComboBox::setAutoCompletion( bool autocomplete )
00108 {
00109     if ( d->klineEdit )
00110     {
00111         if ( autocomplete )
00112         {
00113             d->klineEdit->setCompletionMode( KGlobalSettings::CompletionAuto );
00114             setCompletionMode( KGlobalSettings::CompletionAuto );
00115         }
00116         else
00117         {
00118             d->klineEdit->setCompletionMode( KGlobalSettings::completionMode() );
00119             setCompletionMode( KGlobalSettings::completionMode() );
00120         }
00121     }
00122 }
00123 
00124 void KComboBox::setContextMenuEnabled( bool showMenu )
00125 {
00126     if( d->klineEdit )
00127         d->klineEdit->setContextMenuEnabled( showMenu );
00128 }
00129 
00130 
00131 void KComboBox::setURLDropsEnabled( bool enable )
00132 {
00133     if ( d->klineEdit )
00134         d->klineEdit->setURLDropsEnabled( enable );
00135 }
00136 
00137 bool KComboBox::isURLDropsEnabled() const
00138 {
00139     return d->klineEdit && d->klineEdit->isURLDropsEnabled();
00140 }
00141 
00142 
00143 void KComboBox::setCompletedText( const QString& text, bool marked )
00144 {
00145     if ( d->klineEdit )
00146         d->klineEdit->setCompletedText( text, marked );
00147 }
00148 
00149 void KComboBox::setCompletedText( const QString& text )
00150 {
00151     if ( d->klineEdit )
00152         d->klineEdit->setCompletedText( text );
00153 }
00154 
00155 void KComboBox::makeCompletion( const QString& text )
00156 {
00157     if( d->klineEdit )
00158         d->klineEdit->makeCompletion( text );
00159 
00160     else // read-only combo completion
00161     {
00162         if( text.isNull() || !listBox() )
00163             return;
00164 
00165         const int index = listBox()->index( listBox()->findItem( text ) );
00166         if( index >= 0 )
00167             setCurrentItem( index );
00168     }
00169 }
00170 
00171 void KComboBox::rotateText( KCompletionBase::KeyBindingType type )
00172 {
00173     if ( d->klineEdit )
00174         d->klineEdit->rotateText( type );
00175 }
00176 
00177 // not needed anymore
00178 bool KComboBox::eventFilter( QObject* o, QEvent* ev )
00179 {
00180     return QComboBox::eventFilter( o, ev );
00181 }
00182 
00183 void KComboBox::setTrapReturnKey( bool grab )
00184 {
00185     if ( d->klineEdit )
00186         d->klineEdit->setTrapReturnKey( grab );
00187     else
00188         qWarning("KComboBox::setTrapReturnKey not supported with a non-KLineEdit.");
00189 }
00190 
00191 bool KComboBox::trapReturnKey() const
00192 {
00193     return d->klineEdit && d->klineEdit->trapReturnKey();
00194 }
00195 
00196 
00197 void KComboBox::setEditURL( const KURL& url )
00198 {
00199     QComboBox::setEditText( url.prettyURL() );
00200 }
00201 
00202 void KComboBox::insertURL( const KURL& url, int index )
00203 {
00204     QComboBox::insertItem( url.prettyURL(), index );
00205 }
00206 
00207 void KComboBox::insertURL( const QPixmap& pixmap, const KURL& url, int index )
00208 {
00209     QComboBox::insertItem( pixmap, url.prettyURL(), index );
00210 }
00211 
00212 void KComboBox::changeURL( const KURL& url, int index )
00213 {
00214     QComboBox::changeItem( url.prettyURL(), index );
00215 }
00216 
00217 void KComboBox::changeURL( const QPixmap& pixmap, const KURL& url, int index )
00218 {
00219     QComboBox::changeItem( pixmap, url.prettyURL(), index );
00220 }
00221 
00222 void KComboBox::setCompletedItems( const QStringList& items )
00223 {
00224     if ( d->klineEdit )
00225         d->klineEdit->setCompletedItems( items );
00226 }
00227 
00228 KCompletionBox * KComboBox::completionBox( bool create )
00229 {
00230     if ( d->klineEdit )
00231         return d->klineEdit->completionBox( create );
00232     return 0;
00233 }
00234 
00235 // QWidget::create() turns off mouse-Tracking which would break auto-hiding
00236 void KComboBox::create( WId id, bool initializeWindow, bool destroyOldWindow )
00237 {
00238     QComboBox::create( id, initializeWindow, destroyOldWindow );
00239     KCursor::setAutoHideCursor( lineEdit(), true, true );
00240 }
00241 
00242 void KComboBox::wheelEvent( QWheelEvent *ev )
00243 {
00244     // Not necessary anymore
00245     QComboBox::wheelEvent( ev );
00246 }
00247 
00248 void KComboBox::setLineEdit( QLineEdit *edit )
00249 {
00250     if ( !editable() && edit &&
00251          !qstrcmp( edit->className(), "QLineEdit" ) )
00252     {
00253         // uic generates code that creates a read-only KComboBox and then
00254         // calls combo->setEditable( true ), which causes QComboBox to set up
00255         // a dumb QLineEdit instead of our nice KLineEdit.
00256         // As some KComboBox features rely on the KLineEdit, we reject
00257         // this order here.
00258         delete edit;
00259         edit = new KLineEdit( this, "combo edit" );
00260     }
00261 
00262     QComboBox::setLineEdit( edit );
00263     d->klineEdit = dynamic_cast<KLineEdit*>( edit );
00264     setDelegate( d->klineEdit );
00265 
00266     // Connect the returnPressed signal for both Q[K]LineEdits'
00267     if (edit)
00268         connect( edit, SIGNAL( returnPressed() ), SIGNAL( returnPressed() ));
00269 
00270     if ( d->klineEdit )
00271     {
00272         // someone calling KComboBox::setEditable( false ) destroys our
00273         // lineedit without us noticing. And KCompletionBase::delegate would
00274         // be a dangling pointer then, so prevent that. Note: only do this
00275         // when it is a KLineEdit!
00276         connect( edit, SIGNAL( destroyed() ), SLOT( lineEditDeleted() ));
00277 
00278         connect( d->klineEdit, SIGNAL( returnPressed( const QString& )),
00279                  SIGNAL( returnPressed( const QString& ) ));
00280 
00281         connect( d->klineEdit, SIGNAL( completion( const QString& )),
00282                  SIGNAL( completion( const QString& )) );
00283 
00284         connect( d->klineEdit, SIGNAL( substringCompletion( const QString& )),
00285                  SIGNAL( substringCompletion( const QString& )) );
00286 
00287         connect( d->klineEdit,
00288                  SIGNAL( textRotation( KCompletionBase::KeyBindingType )),
00289                  SIGNAL( textRotation( KCompletionBase::KeyBindingType )) );
00290 
00291         connect( d->klineEdit,
00292                  SIGNAL( completionModeChanged( KGlobalSettings::Completion )),
00293                  SIGNAL( completionModeChanged( KGlobalSettings::Completion)));
00294 
00295         connect( d->klineEdit,
00296                  SIGNAL( aboutToShowContextMenu( QPopupMenu * )),
00297                  SIGNAL( aboutToShowContextMenu( QPopupMenu * )) );
00298 
00299         connect( d->klineEdit,
00300                  SIGNAL( completionBoxActivated( const QString& )),
00301                  SIGNAL( activated( const QString& )) );
00302     }
00303 }
00304 
00305 void KComboBox::setCurrentItem( const QString& item, bool insert, int index )
00306 {
00307     int sel = -1;
00308 
00309     const int itemCount = count();
00310     for (int i = 0; i < itemCount; ++i)
00311     {
00312         if (text(i) == item)
00313         {
00314             sel = i;
00315             break;
00316         }
00317     }
00318 
00319     if (sel == -1 && insert)
00320     {
00321         insertItem(item, index);
00322         if (index >= 0)
00323             sel = index;
00324         else
00325             sel = count() - 1;
00326     }
00327     setCurrentItem(sel);
00328 }
00329 
00330 void KComboBox::lineEditDeleted()
00331 {
00332     // yes, we need those ugly casts due to the multiple inheritance
00333     // sender() is guaranteed to be a KLineEdit (see the connect() to the
00334     // destroyed() signal
00335     const KCompletionBase *base = static_cast<const KCompletionBase*>( static_cast<const KLineEdit*>( sender() ));
00336 
00337     // is it our delegate, that is destroyed?
00338     if ( base == delegate() )
00339         setDelegate( 0L );
00340 }
00341 
00342 
00343 // *********************************************************************
00344 // *********************************************************************
00345 
00346 
00347 // we are always read-write
00348 KHistoryCombo::KHistoryCombo( QWidget *parent, const char *name )
00349     : KComboBox( true, parent, name ), d(0)
00350 {
00351     init( true ); // using completion
00352 }
00353 
00354 // we are always read-write
00355 KHistoryCombo::KHistoryCombo( bool useCompletion,
00356                               QWidget *parent, const char *name )
00357     : KComboBox( true, parent, name ), d(0)
00358 {
00359     init( useCompletion );
00360 }
00361 
00362 void KHistoryCombo::init( bool useCompletion )
00363 {
00364     if ( useCompletion )
00365         completionObject()->setOrder( KCompletion::Weighted );
00366 
00367     setInsertionPolicy( NoInsertion );
00368     myIterateIndex = -1;
00369     myRotated = false;
00370     myPixProvider = 0L;
00371 
00372     // obey HISTCONTROL setting
00373     QCString histControl = getenv("HISTCONTROL");
00374     if ( histControl == "ignoredups" || histControl == "ignoreboth" )
00375         setDuplicatesEnabled( false );
00376 
00377     connect( this, SIGNAL(aboutToShowContextMenu(QPopupMenu*)),
00378              SLOT(addContextMenuItems(QPopupMenu*)) );
00379     connect( this, SIGNAL( activated(int) ), SLOT( slotReset() ));
00380     connect( this, SIGNAL( returnPressed(const QString&) ), SLOT(slotReset()));
00381 }
00382 
00383 KHistoryCombo::~KHistoryCombo()
00384 {
00385     delete myPixProvider;
00386 }
00387 
00388 void KHistoryCombo::setHistoryItems( QStringList items,
00389                                      bool setCompletionList )
00390 {
00391     KComboBox::clear();
00392 
00393     // limit to maxCount()
00394     const int itemCount = items.count(); 
00395     const int toRemove = itemCount - maxCount();
00396 
00397     if (toRemove >= itemCount) {
00398         items.clear();
00399     } else {
00400         for (int i = 0; i < toRemove; ++i) 
00401             items.pop_front();
00402     }
00403 
00404     insertItems( items );
00405 
00406     if ( setCompletionList && useCompletion() ) {
00407         // we don't have any weighting information here ;(
00408         KCompletion *comp = completionObject();
00409         comp->setOrder( KCompletion::Insertion );
00410         comp->setItems( items );
00411         comp->setOrder( KCompletion::Weighted );
00412     }
00413 
00414     clearEdit();
00415 }
00416 
00417 QStringList KHistoryCombo::historyItems() const
00418 {
00419     QStringList list;
00420     const int itemCount = count();
00421     for ( int i = 0; i < itemCount; ++i )
00422         list.append( text( i ) );
00423 
00424     return list;
00425 }
00426 
00427 void KHistoryCombo::clearHistory()
00428 {
00429     const QString temp = currentText();
00430     KComboBox::clear();
00431     if ( useCompletion() )
00432         completionObject()->clear();
00433     setEditText( temp );
00434 }
00435 
00436 void KHistoryCombo::addContextMenuItems( QPopupMenu* menu )
00437 {
00438     if ( menu )
00439     {
00440         menu->insertSeparator();
00441         int id = menu->insertItem( SmallIconSet("history_clear"), i18n("Clear &History"), this, SLOT( slotClear()));
00442         if (!count())
00443            menu->setItemEnabled(id, false);
00444     }
00445 }
00446 
00447 void KHistoryCombo::addToHistory( const QString& item )
00448 {
00449     if ( item.isEmpty() || (count() > 0 && item == text(0) )) {
00450         return;
00451     }
00452 
00453     bool wasCurrent = false;
00454     // remove all existing items before adding
00455     if ( !duplicatesEnabled() ) {
00456         int i = 0;
00457         int itemCount = count();
00458         while ( i < itemCount ) {
00459             if ( text( i ) == item ) {
00460                 if ( !wasCurrent )
00461                   wasCurrent = ( i == currentItem() );
00462                 removeItem( i );
00463                 --itemCount;
00464             } else {
00465                 ++i;
00466             }
00467         }
00468     }
00469 
00470     // now add the item
00471     if ( myPixProvider )
00472         insertItem( myPixProvider->pixmapFor(item, KIcon::SizeSmall), item, 0);
00473     else
00474         insertItem( item, 0 );
00475 
00476     if ( wasCurrent )
00477         setCurrentItem( 0 );
00478 
00479     const bool useComp = useCompletion();
00480 
00481     const int last = count() - 1; // last valid index
00482     const int mc = maxCount();
00483     const int stopAt = QMAX(mc, 0);
00484 
00485     for (int rmIndex = last; rmIndex >= stopAt; --rmIndex) {
00486         // remove the last item, as long as we are longer than maxCount()
00487         // remove the removed item from the completionObject if it isn't
00488         // anymore available at all in the combobox.
00489         const QString rmItem = text( rmIndex );
00490         removeItem( rmIndex );
00491         if ( useComp && !contains( rmItem ) )
00492             completionObject()->removeItem( rmItem );
00493     }
00494 
00495     if ( useComp )
00496         completionObject()->addItem( item );
00497 }
00498 
00499 bool KHistoryCombo::removeFromHistory( const QString& item )
00500 {
00501     if ( item.isEmpty() )
00502         return false;
00503 
00504     bool removed = false;
00505     const QString temp = currentText();
00506     int i = 0;
00507     int itemCount = count();
00508     while ( i < itemCount ) {
00509         if ( item == text( i ) ) {
00510             removed = true;
00511             removeItem( i );
00512             --itemCount;
00513         } else {
00514             ++i;
00515         }
00516     }
00517 
00518     if ( removed && useCompletion() )
00519         completionObject()->removeItem( item );
00520 
00521     setEditText( temp );
00522     return removed;
00523 }
00524 
00525 void KHistoryCombo::rotateUp()
00526 {
00527     // save the current text in the lineedit
00528     if ( myIterateIndex == -1 )
00529         myText = currentText();
00530 
00531     ++myIterateIndex;
00532 
00533     // skip duplicates/empty items
00534     const int last = count() - 1; // last valid index
00535     const QString currText = currentText();
00536 
00537     while ( myIterateIndex < last &&
00538             (currText == text( myIterateIndex ) ||
00539              text( myIterateIndex ).isEmpty()) )
00540         ++myIterateIndex;
00541 
00542     if ( myIterateIndex >= count() ) {
00543         myRotated = true;
00544         myIterateIndex = -1;
00545 
00546         // if the typed text is the same as the first item, skip the first
00547         if ( count() > 0 && myText == text(0) )
00548             myIterateIndex = 0;
00549 
00550         setEditText( myText );
00551     }
00552     else
00553         setEditText( text( myIterateIndex ));
00554 }
00555 
00556 void KHistoryCombo::rotateDown()
00557 {
00558     // save the current text in the lineedit
00559     if ( myIterateIndex == -1 )
00560         myText = currentText();
00561 
00562     --myIterateIndex;
00563 
00564     const QString currText = currentText();
00565     // skip duplicates/empty items
00566     while ( myIterateIndex >= 0 &&
00567             (currText == text( myIterateIndex ) ||
00568              text( myIterateIndex ).isEmpty()) )
00569         --myIterateIndex;
00570 
00571 
00572     if ( myIterateIndex < 0 ) {
00573         if ( myRotated && myIterateIndex == -2 ) {
00574             myRotated = false;
00575             myIterateIndex = count() - 1;
00576             setEditText( text(myIterateIndex) );
00577         }
00578         else { // bottom of history
00579             if ( myIterateIndex == -2 ) {
00580                 KNotifyClient::event( (int)winId(), KNotifyClient::notification,
00581                                       i18n("No further item in the history."));
00582             }
00583 
00584             myIterateIndex = -1;
00585             if ( currentText() != myText )
00586                 setEditText( myText );
00587         }
00588     }
00589     else
00590         setEditText( text( myIterateIndex ));
00591 
00592 }
00593 
00594 void KHistoryCombo::keyPressEvent( QKeyEvent *e )
00595 {
00596     KKey event_key( e );
00597 
00598     // going up in the history, rotating when reaching QListBox::count()
00599     if ( KStdAccel::rotateUp().contains(event_key) )
00600         rotateUp();
00601 
00602     // going down in the history, no rotation possible. Last item will be
00603     // the text that was in the lineedit before Up was called.
00604     else if ( KStdAccel::rotateDown().contains(event_key) )
00605         rotateDown();
00606     else
00607         KComboBox::keyPressEvent( e );
00608 }
00609 
00610 void KHistoryCombo::wheelEvent( QWheelEvent *ev )
00611 {
00612     // Pass to poppable listbox if it's up
00613     QListBox* const lb = listBox();
00614     if ( lb && lb->isVisible() )
00615     {
00616         QApplication::sendEvent( lb, ev );
00617         return;
00618     }
00619     // Otherwise make it change the text without emitting activated
00620     if ( ev->delta() > 0 ) {
00621         rotateUp();
00622     } else {
00623         rotateDown();
00624     }
00625     ev->accept();
00626 }
00627 
00628 void KHistoryCombo::slotReset()
00629 {
00630     myIterateIndex = -1;
00631     myRotated = false;
00632 }
00633 
00634 
00635 void KHistoryCombo::setPixmapProvider( KPixmapProvider *prov )
00636 {
00637     if ( myPixProvider == prov )
00638         return;
00639 
00640     delete myPixProvider;
00641     myPixProvider = prov;
00642 
00643     // re-insert all the items with/without pixmap
00644     // I would prefer to use changeItem(), but that doesn't honor the pixmap
00645     // when using an editable combobox (what we do)
00646     if ( count() > 0 ) {
00647         QStringList items( historyItems() );
00648         clear();
00649         insertItems( items );
00650     }
00651 }
00652 
00653 void KHistoryCombo::insertItems( const QStringList& items )
00654 {
00655     QStringList::ConstIterator it = items.constBegin();
00656     const QStringList::ConstIterator itEnd = items.constEnd();
00657 
00658     while ( it != itEnd ) {
00659         const QString item = *it;
00660         if ( !item.isEmpty() ) { // only insert non-empty items
00661             if ( myPixProvider )
00662                 insertItem( myPixProvider->pixmapFor(item, KIcon::SizeSmall),
00663                             item );
00664             else
00665                 insertItem( item );
00666         }
00667         ++it;
00668     }
00669 }
00670 
00671 void KHistoryCombo::slotClear()
00672 {
00673     clearHistory();
00674     emit cleared();
00675 }
00676 
00677 void KComboBox::virtual_hook( int id, void* data )
00678 { KCompletionBase::virtual_hook( id, data ); }
00679 
00680 void KHistoryCombo::virtual_hook( int id, void* data )
00681 { KComboBox::virtual_hook( id, data ); }
00682 
00683 #include "kcombobox.moc"
KDE Logo
This file is part of the documentation for kdeui Library Version 3.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Aug 2 12:04:22 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003