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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:10:08 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003