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