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