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 }
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 }
00097 
00098 
00099 bool KComboBox::contains( const QString& _text ) const
00100 {
00101     if ( _text.isEmpty() )
00102         return false;
00103 
00104     for (int i = 0; i < count(); i++ )
00105     {
00106         if ( text(i) == _text )
00107             return true;
00108     }
00109     return false;
00110 }
00111 
00112 void KComboBox::setAutoCompletion( bool autocomplete )
00113 {
00114     if ( d->klineEdit )
00115     {
00116         if ( autocomplete )
00117         {
00118             d->klineEdit->setCompletionMode( KGlobalSettings::CompletionAuto );
00119             setCompletionMode( KGlobalSettings::CompletionAuto );
00120         }
00121         else
00122         {
00123             d->klineEdit->setCompletionMode( KGlobalSettings::completionMode() );
00124             setCompletionMode( KGlobalSettings::completionMode() );
00125         }
00126     }
00127 }
00128 
00129 void KComboBox::setContextMenuEnabled( bool showMenu )
00130 {
00131     if( d->klineEdit )
00132         d->klineEdit->setContextMenuEnabled( showMenu );
00133 }
00134 
00135 
00136 void KComboBox::setURLDropsEnabled( bool enable )
00137 {
00138     if ( d->klineEdit )
00139         d->klineEdit->setURLDropsEnabled( enable );
00140 }
00141 
00142 bool KComboBox::isURLDropsEnabled() const
00143 {
00144     return d->klineEdit && d->klineEdit->isURLDropsEnabled();
00145 }
00146 
00147 
00148 void KComboBox::setCompletedText( const QString& text, bool marked )
00149 {
00150     if ( d->klineEdit )
00151         d->klineEdit->setCompletedText( text, marked );
00152 }
00153 
00154 void KComboBox::setCompletedText( const QString& text )
00155 {
00156     if ( d->klineEdit )
00157         d->klineEdit->setCompletedText( text );
00158 }
00159 
00160 void KComboBox::makeCompletion( const QString& text )
00161 {
00162     if( d->klineEdit )
00163         d->klineEdit->makeCompletion( text );
00164 
00165     else // read-only combo completion
00166     {
00167         if( text.isNull() || !listBox() )
00168             return;
00169 
00170         int index = listBox()->index( listBox()->findItem( text ) );
00171         if( index >= 0 )
00172             setCurrentItem( index );
00173     }
00174 }
00175 
00176 void KComboBox::rotateText( KCompletionBase::KeyBindingType type )
00177 {
00178     if ( d->klineEdit )
00179         d->klineEdit->rotateText( type );
00180 }
00181 
00182 // not needed anymore
00183 bool KComboBox::eventFilter( QObject* o, QEvent* ev )
00184 {
00185     return QComboBox::eventFilter( o, ev );
00186 }
00187 
00188 void KComboBox::setTrapReturnKey( bool grab )
00189 {
00190     if ( d->klineEdit )
00191         d->klineEdit->setTrapReturnKey( grab );
00192     else
00193         qWarning("KComboBox::setTrapReturnKey not supported with a non-KLineEdit.");
00194 }
00195 
00196 bool KComboBox::trapReturnKey() const
00197 {
00198     return d->klineEdit && d->klineEdit->trapReturnKey();
00199 }
00200 
00201 
00202 void KComboBox::setEditURL( const KURL& url )
00203 {
00204     QComboBox::setEditText( url.prettyURL() );
00205 }
00206 
00207 void KComboBox::insertURL( const KURL& url, int index )
00208 {
00209     QComboBox::insertItem( url.prettyURL(), index );
00210 }
00211 
00212 void KComboBox::insertURL( const QPixmap& pixmap, const KURL& url, int index )
00213 {
00214     QComboBox::insertItem( pixmap, url.prettyURL(), index );
00215 }
00216 
00217 void KComboBox::changeURL( const KURL& url, int index )
00218 {
00219     QComboBox::changeItem( url.prettyURL(), index );
00220 }
00221 
00222 void KComboBox::changeURL( const QPixmap& pixmap, const KURL& url, int index )
00223 {
00224     QComboBox::changeItem( pixmap, url.prettyURL(), index );
00225 }
00226 
00227 void KComboBox::setCompletedItems( const QStringList& items )
00228 {
00229     if ( d->klineEdit )
00230         d->klineEdit->setCompletedItems( items );
00231 }
00232 
00233 KCompletionBox * KComboBox::completionBox( bool create )
00234 {
00235     if ( d->klineEdit )
00236         return d->klineEdit->completionBox( create );
00237     return 0;
00238 }
00239 
00240 // QWidget::create() turns off mouse-Tracking which would break auto-hiding
00241 void KComboBox::create( WId id, bool initializeWindow, bool destroyOldWindow )
00242 {
00243     QComboBox::create( id, initializeWindow, destroyOldWindow );
00244     KCursor::setAutoHideCursor( lineEdit(), true, true );
00245 }
00246 
00247 void KComboBox::wheelEvent( QWheelEvent *ev )
00248 {
00249     // Not necessary anymore
00250     QComboBox::wheelEvent( ev );
00251 }
00252 
00253 void KComboBox::setLineEdit( QLineEdit *edit )
00254 {
00255     if ( !editable() && edit &&
00256          qstrcmp( edit->className(), "QLineEdit" ) == 0 )
00257     {
00258         // uic generates code that creates a read-only KComboBox and then
00259         // calls combo->setEditable( true ), which causes QComboBox to set up
00260         // a dumb QLineEdit instead of our nice KLineEdit.
00261         // As some KComboBox features rely on the KLineEdit, we reject
00262         // this order here.
00263         delete edit;
00264         edit = new KLineEdit( this, "combo edit" );
00265     }
00266 
00267     QComboBox::setLineEdit( edit );
00268     d->klineEdit = dynamic_cast<KLineEdit*>( edit );
00269     setDelegate( d->klineEdit );
00270 
00271     // Connect the returnPressed signal for both Q[K]LineEdits'
00272     if (edit)
00273         connect( edit, SIGNAL( returnPressed() ), SIGNAL( returnPressed() ));
00274 
00275     if ( d->klineEdit )
00276     {
00277         // someone calling KComboBox::setEditable( false ) destroys our
00278         // lineedit without us noticing. And KCompletionBase::delegate would
00279         // be a dangling pointer then, so prevent that. Note: only do this
00280         // when it is a KLineEdit!
00281         connect( edit, SIGNAL( destroyed() ), SLOT( lineEditDeleted() ));
00282 
00283         connect( d->klineEdit, SIGNAL( returnPressed( const QString& )),
00284                  SIGNAL( returnPressed( const QString& ) ));
00285 
00286         connect( d->klineEdit, SIGNAL( completion( const QString& )),
00287                  SIGNAL( completion( const QString& )) );
00288 
00289         connect( d->klineEdit, SIGNAL( substringCompletion( const QString& )),
00290                  SIGNAL( substringCompletion( const QString& )) );
00291 
00292         connect( d->klineEdit,
00293                  SIGNAL( textRotation( KCompletionBase::KeyBindingType )),
00294                  SIGNAL( textRotation( KCompletionBase::KeyBindingType )) );
00295 
00296         connect( d->klineEdit,
00297                  SIGNAL( completionModeChanged( KGlobalSettings::Completion )),
00298                  SIGNAL( completionModeChanged( KGlobalSettings::Completion)));
00299 
00300         connect( d->klineEdit,
00301                  SIGNAL( aboutToShowContextMenu( QPopupMenu * )),
00302                  SIGNAL( aboutToShowContextMenu( QPopupMenu * )) );
00303 
00304         connect( d->klineEdit,
00305                  SIGNAL( completionBoxActivated( const QString& )),
00306                  SIGNAL( activated( const QString& )) );
00307     }
00308 }
00309 
00310 void KComboBox::setCurrentItem( const QString& item, bool insert, int index )
00311 {
00312     int sel = -1;
00313 
00314     for (int i = 0; i < count(); ++i)
00315     {
00316         if (text(i) == item)
00317         {
00318             sel = i;
00319             break;
00320         }
00321     }
00322 
00323     if (sel == -1 && insert)
00324     {
00325         insertItem(item, index);
00326         if (index >= 0)
00327             sel = index;
00328         else
00329             sel = count() - 1;
00330     }
00331     setCurrentItem(sel);
00332 }
00333 
00334 void KComboBox::lineEditDeleted()
00335 {
00336     // yes, we need those ugly casts due to the multiple inheritance
00337     // sender() is guaranteed to be a KLineEdit (see the connect() to the
00338     // destroyed() signal
00339     const KCompletionBase *base = static_cast<const KCompletionBase*>( static_cast<const KLineEdit*>( sender() ));
00340 
00341     // is it our delegate, that is destroyed?
00342     if ( base == delegate() )
00343         setDelegate( 0L );
00344 }
00345 
00346 
00347 // *********************************************************************
00348 // *********************************************************************
00349 
00350 
00351 // we are always read-write
00352 KHistoryCombo::KHistoryCombo( QWidget *parent, const char *name )
00353     : KComboBox( true, parent, name )
00354 {
00355     init( true ); // using completion
00356 }
00357 
00358 // we are always read-write
00359 KHistoryCombo::KHistoryCombo( bool useCompletion,
00360                               QWidget *parent, const char *name )
00361     : KComboBox( true, parent, name )
00362 {
00363     init( useCompletion );
00364 }
00365 
00366 void KHistoryCombo::init( bool useCompletion )
00367 {
00368     if ( useCompletion )
00369         completionObject()->setOrder( KCompletion::Weighted );
00370 
00371     setInsertionPolicy( NoInsertion );
00372     myIterateIndex = -1;
00373     myRotated = false;
00374     myPixProvider = 0L;
00375 
00376     // obey HISTCONTROL setting
00377     QCString histControl = getenv("HISTCONTROL");
00378     if ( histControl == "ignoredups" || histControl == "ignoreboth" )
00379         setDuplicatesEnabled( false );
00380 
00381     connect( this, SIGNAL(aboutToShowContextMenu(QPopupMenu*)),
00382              SLOT(addContextMenuItems(QPopupMenu*)) );
00383     connect( this, SIGNAL( activated(int) ), SLOT( slotReset() ));
00384     connect( this, SIGNAL( returnPressed(const QString&) ), SLOT(slotReset()));
00385 }
00386 
00387 KHistoryCombo::~KHistoryCombo()
00388 {
00389     delete myPixProvider;
00390 }
00391 
00392 void KHistoryCombo::setHistoryItems( QStringList items,
00393                                      bool setCompletionList )
00394 {
00395     KComboBox::clear();
00396 
00397     // limit to maxCount()
00398     while ( (int) items.count() > maxCount() && !items.isEmpty() )
00399         items.remove( items.begin() );
00400 
00401     insertItems( items );
00402 
00403     if ( setCompletionList && useCompletion() ) {
00404         // we don't have any weighting information here ;(
00405         KCompletion *comp = completionObject();
00406         comp->setOrder( KCompletion::Insertion );
00407         comp->setItems( items );
00408         comp->setOrder( KCompletion::Weighted );
00409     }
00410 
00411     clearEdit();
00412 }
00413 
00414 QStringList KHistoryCombo::historyItems() const
00415 {
00416     QStringList list;
00417     for ( int i = 0; i < count(); i++ )
00418         list.append( text( i ) );
00419 
00420     return list;
00421 }
00422 
00423 void KHistoryCombo::clearHistory()
00424 {
00425     QString temp = currentText();
00426     KComboBox::clear();
00427     if ( useCompletion() )
00428         completionObject()->clear();
00429     setEditText( temp );
00430 }
00431 
00432 void KHistoryCombo::addContextMenuItems( QPopupMenu* menu )
00433 {
00434     if ( menu )
00435     {
00436         menu->insertSeparator();
00437         int id = menu->insertItem( SmallIcon("history_clear"), i18n("Clear &History"), this, SLOT( slotClear()));
00438         if (!count())
00439            menu->setItemEnabled(id, false);
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 ( count() > 0 && 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( winId(), 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     KKey event_key( e );
00578 
00579     // going up in the history, rotating when reaching QListBox::count()
00580     if ( KKey(KStdAccel::rotateUp().keyCodeQt()) == event_key )
00581         rotateUp();
00582 
00583     // going down in the history, no rotation possible. Last item will be
00584     // the text that was in the lineedit before Up was called.
00585     else if ( KKey(KStdAccel::rotateDown().keyCodeQt()) == event_key )
00586         rotateDown();
00587     else
00588         KComboBox::keyPressEvent( e );
00589 }
00590 
00591 void KHistoryCombo::wheelEvent( QWheelEvent *ev )
00592 {
00593     // Pass to poppable listbox if it's up
00594     QListBox *lb = listBox();
00595     if ( lb && lb->isVisible() )
00596     {
00597         QApplication::sendEvent( lb, ev );
00598         return;
00599     }
00600     // Otherwise make it change the text without emitting activated
00601     if ( ev->delta() > 0 ) {
00602         rotateUp();
00603     } else {
00604         rotateDown();
00605     }
00606     ev->accept();
00607 }
00608 
00609 void KHistoryCombo::slotReset()
00610 {
00611     myIterateIndex = -1;
00612     myRotated = false;
00613 }
00614 
00615 
00616 void KHistoryCombo::setPixmapProvider( KPixmapProvider *prov )
00617 {
00618     if ( myPixProvider == prov )
00619         return;
00620 
00621     delete myPixProvider;
00622     myPixProvider = prov;
00623 
00624     // re-insert all the items with/without pixmap
00625     // I would prefer to use changeItem(), but that doesn't honor the pixmap
00626     // when using an editable combobox (what we do)
00627     if ( count() > 0 ) {
00628         QStringList items( historyItems() );
00629         clear();
00630         insertItems( items );
00631     }
00632 }
00633 
00634 void KHistoryCombo::insertItems( const QStringList& items )
00635 {
00636     QStringList::ConstIterator it = items.begin();
00637     QString item;
00638     while ( it != items.end() ) {
00639         item = *it;
00640         if ( !item.isEmpty() ) { // only insert non-empty items
00641             if ( myPixProvider )
00642                 insertItem( myPixProvider->pixmapFor(item, KIcon::SizeSmall),
00643                             item );
00644             else
00645                 insertItem( item );
00646         }
00647         ++it;
00648     }
00649 }
00650 
00651 void KHistoryCombo::slotClear()
00652 {
00653     clearHistory();
00654     emit cleared();
00655 }
00656 
00657 void KComboBox::virtual_hook( int id, void* data )
00658 { KCompletionBase::virtual_hook( id, data ); }
00659 
00660 void KHistoryCombo::virtual_hook( int id, void* data )
00661 { KComboBox::virtual_hook( id, data ); }
00662 
00663 #include "kcombobox.moc"
KDE Logo
This file is part of the documentation for kdeui Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Sep 30 05:16:47 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003