• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

khistorycombobox.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00020    Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include "khistorycombobox.h"
00024 
00025 #include <QtGui/QAbstractItemView>
00026 #include <QtGui/QApplication>
00027 #include <QtGui/QKeyEvent>
00028 #include <QtGui/QLineEdit>
00029 #include <QtGui/QMenu>
00030 #include <QtGui/QWheelEvent>
00031 
00032 #include <klocale.h>
00033 #include <knotification.h>
00034 #include <kpixmapprovider.h>
00035 #include <kstandardshortcut.h>
00036 #include <kicontheme.h>
00037 #include <kicon.h>
00038 
00039 #include <kdebug.h>
00040 
00041 class KHistoryComboBox::Private
00042 {
00043 public:
00044     Private(KHistoryComboBox *q): q(q), myPixProvider(0) {}
00045 
00046     KHistoryComboBox *q;
00047 
00051     int myIterateIndex;
00052 
00056     QString myText;
00057 
00062     bool myRotated;
00063     KPixmapProvider *myPixProvider;
00064 };
00065 
00066 // we are always read-write
00067 KHistoryComboBox::KHistoryComboBox( QWidget *parent )
00068      : KComboBox( true, parent ), d(new Private(this))
00069 {
00070     init( true ); // using completion
00071 }
00072 
00073 // we are always read-write
00074 KHistoryComboBox::KHistoryComboBox( bool useCompletion,
00075                               QWidget *parent )
00076     : KComboBox( true, parent ), d(new Private(this))
00077 {
00078     init( useCompletion );
00079 }
00080 
00081 void KHistoryComboBox::init( bool useCompletion )
00082 {
00083     // Set a default history size to something reasonable, Qt sets it to INT_MAX by default
00084     setMaxCount( 50 );
00085 
00086     if ( useCompletion )
00087         completionObject()->setOrder( KCompletion::Weighted );
00088 
00089     setInsertPolicy( NoInsert );
00090     d->myIterateIndex = -1;
00091     d->myRotated = false;
00092     d->myPixProvider = 0L;
00093 
00094     // obey HISTCONTROL setting
00095     QByteArray histControl = qgetenv("HISTCONTROL");
00096     if ( histControl == "ignoredups" || histControl == "ignoreboth" )
00097         setDuplicatesEnabled( false );
00098 
00099     connect(this, SIGNAL(aboutToShowContextMenu(QMenu*)), SLOT(addContextMenuItems(QMenu*)));
00100     connect(this, SIGNAL(activated(int)), SLOT(slotReset()));
00101     connect(this, SIGNAL(returnPressed(QString)), SLOT(slotReset()));
00102     // We want slotSimulateActivated to be called _after_ QComboBoxPrivate::_q_returnPressed
00103     // otherwise there's a risk of emitting activated twice (slotSimulateActivated will find
00104     // the item, after some app's slotActivated inserted the item into the combo).
00105     connect(this, SIGNAL(returnPressed(QString)), SLOT(slotSimulateActivated(QString)), Qt::QueuedConnection);
00106 }
00107 
00108 KHistoryComboBox::~KHistoryComboBox()
00109 {
00110     delete d->myPixProvider;
00111     delete d;
00112 }
00113 
00114 void KHistoryComboBox::setHistoryItems( const QStringList &items )
00115 {
00116     setHistoryItems(items, false);
00117 }
00118 
00119 void KHistoryComboBox::setHistoryItems( const QStringList &items,
00120                                      bool setCompletionList )
00121 {
00122     QStringList insertingItems = items;
00123     KComboBox::clear();
00124 
00125     // limit to maxCount()
00126     const int itemCount = insertingItems.count();
00127     const int toRemove = itemCount - maxCount();
00128 
00129     if (toRemove >= itemCount) {
00130         insertingItems.clear();
00131     } else {
00132         for (int i = 0; i < toRemove; ++i)
00133             insertingItems.pop_front();
00134     }
00135 
00136     insertItems( insertingItems );
00137 
00138     if ( setCompletionList && useCompletion() ) {
00139         // we don't have any weighting information here ;(
00140         KCompletion *comp = completionObject();
00141         comp->setOrder( KCompletion::Insertion );
00142         comp->setItems( insertingItems );
00143         comp->setOrder( KCompletion::Weighted );
00144     }
00145 
00146     clearEditText();
00147 }
00148 
00149 QStringList KHistoryComboBox::historyItems() const
00150 {
00151     QStringList list;
00152     const int itemCount = count();
00153     for ( int i = 0; i < itemCount; ++i )
00154         list.append( itemText( i ) );
00155 
00156     return list;
00157 }
00158 
00159 bool KHistoryComboBox::useCompletion() const
00160 {
00161     return compObj();
00162 }
00163 
00164 void KHistoryComboBox::clearHistory()
00165 {
00166     const QString temp = currentText();
00167     KComboBox::clear();
00168     if ( useCompletion() )
00169         completionObject()->clear();
00170     setEditText( temp );
00171 }
00172 
00173 void KHistoryComboBox::addContextMenuItems( QMenu* menu )
00174 {
00175     if ( menu )
00176     {
00177         menu->addSeparator();
00178         QAction* clearHistory = menu->addAction( KIcon("edit-clear-history"), i18n("Clear &History"), this, SLOT( slotClear()));
00179         if (!count())
00180            clearHistory->setEnabled(false);
00181     }
00182 }
00183 
00184 void KHistoryComboBox::addToHistory( const QString& item )
00185 {
00186     if ( item.isEmpty() || (count() > 0 && item == itemText(0) )) {
00187         return;
00188     }
00189 
00190     bool wasCurrent = false;
00191     // remove all existing items before adding
00192     if ( !duplicatesEnabled() ) {
00193         int i = 0;
00194         int itemCount = count();
00195         while ( i < itemCount ) {
00196             if ( itemText( i ) == item ) {
00197                 if ( !wasCurrent )
00198                   wasCurrent = ( i == currentIndex() );
00199                 removeItem( i );
00200                 --itemCount;
00201             } else {
00202                 ++i;
00203             }
00204         }
00205     }
00206 
00207     // now add the item
00208     if ( d->myPixProvider )
00209         insertItem( 0, d->myPixProvider->pixmapFor(item, KIconLoader::SizeSmall), item);
00210     else
00211         insertItem( 0, item );
00212 
00213     if ( wasCurrent )
00214         setCurrentIndex( 0 );
00215 
00216     const bool useComp = useCompletion();
00217 
00218     const int last = count() - 1; // last valid index
00219     const int mc = maxCount();
00220     const int stopAt = qMax(mc, 0);
00221 
00222     for (int rmIndex = last; rmIndex >= stopAt; --rmIndex) {
00223         // remove the last item, as long as we are longer than maxCount()
00224         // remove the removed item from the completionObject if it isn't
00225         // anymore available at all in the combobox.
00226         const QString rmItem = itemText( rmIndex );
00227         removeItem( rmIndex );
00228         if ( useComp && !contains( rmItem ) )
00229             completionObject()->removeItem( rmItem );
00230     }
00231 
00232     if ( useComp )
00233         completionObject()->addItem( item );
00234 }
00235 
00236 bool KHistoryComboBox::removeFromHistory( const QString& item )
00237 {
00238     if ( item.isEmpty() )
00239         return false;
00240 
00241     bool removed = false;
00242     const QString temp = currentText();
00243     int i = 0;
00244     int itemCount = count();
00245     while ( i < itemCount ) {
00246         if ( item == itemText( i ) ) {
00247             removed = true;
00248             removeItem( i );
00249             --itemCount;
00250         } else {
00251             ++i;
00252         }
00253     }
00254 
00255     if ( removed && useCompletion() )
00256         completionObject()->removeItem( item );
00257 
00258     setEditText( temp );
00259     return removed;
00260 }
00261 
00262 void KHistoryComboBox::rotateUp()
00263 {
00264     // save the current text in the lineedit
00265     if ( d->myIterateIndex == -1 )
00266         d->myText = currentText();
00267 
00268     ++d->myIterateIndex;
00269 
00270     // skip duplicates/empty items
00271     const int last = count() - 1; // last valid index
00272     const QString currText = currentText();
00273 
00274     while ( d->myIterateIndex < last &&
00275             (currText == itemText( d->myIterateIndex ) ||
00276              itemText( d->myIterateIndex ).isEmpty()) )
00277         ++d->myIterateIndex;
00278 
00279     if ( d->myIterateIndex >= count() ) {
00280         d->myRotated = true;
00281         d->myIterateIndex = -1;
00282 
00283         // if the typed text is the same as the first item, skip the first
00284         if ( count() > 0 && d->myText == itemText(0) )
00285             d->myIterateIndex = 0;
00286 
00287         setEditText( d->myText );
00288     }
00289     else
00290         setEditText( itemText( d->myIterateIndex ));
00291 }
00292 
00293 void KHistoryComboBox::rotateDown()
00294 {
00295     // save the current text in the lineedit
00296     if ( d->myIterateIndex == -1 )
00297         d->myText = currentText();
00298 
00299     --d->myIterateIndex;
00300 
00301     const QString currText = currentText();
00302     // skip duplicates/empty items
00303     while ( d->myIterateIndex >= 0 &&
00304             (currText == itemText( d->myIterateIndex ) ||
00305              itemText( d->myIterateIndex ).isEmpty()) )
00306         --d->myIterateIndex;
00307 
00308 
00309     if ( d->myIterateIndex < 0 ) {
00310         if ( d->myRotated && d->myIterateIndex == -2 ) {
00311             d->myRotated = false;
00312             d->myIterateIndex = count() - 1;
00313             setEditText( itemText(d->myIterateIndex) );
00314         }
00315         else { // bottom of history
00316             if ( d->myIterateIndex == -2 ) {
00317                 KNotification::event( KNotification::Notification ,
00318                                       i18n("No further item in the history."),
00319                                        QPixmap() , this);
00320             }
00321 
00322             d->myIterateIndex = -1;
00323             if ( currentText() != d->myText )
00324                 setEditText( d->myText );
00325         }
00326     }
00327     else
00328         setEditText( itemText( d->myIterateIndex ));
00329 
00330 }
00331 
00332 void KHistoryComboBox::keyPressEvent( QKeyEvent *e )
00333 {
00334     int event_key = e->key() | e->modifiers();
00335 
00336     // going up in the history, rotating when reaching QListBox::count()
00337     if ( KStandardShortcut::rotateUp().contains(event_key) )
00338         rotateUp();
00339 
00340     // going down in the history, no rotation possible. Last item will be
00341     // the text that was in the lineedit before Up was called.
00342     else if ( KStandardShortcut::rotateDown().contains(event_key) )
00343         rotateDown();
00344     else
00345         KComboBox::keyPressEvent( e );
00346 }
00347 
00348 void KHistoryComboBox::wheelEvent( QWheelEvent *ev )
00349 {
00350     // Pass to poppable listbox if it's up
00351     QAbstractItemView* const iv = view();
00352     if ( iv && iv->isVisible() )
00353     {
00354         QApplication::sendEvent( iv, ev );
00355         return;
00356     }
00357     // Otherwise make it change the text without emitting activated
00358     if ( ev->delta() > 0 ) {
00359         rotateUp();
00360     } else {
00361         rotateDown();
00362     }
00363     ev->accept();
00364 }
00365 
00366 void KHistoryComboBox::slotReset()
00367 {
00368     d->myIterateIndex = -1;
00369     d->myRotated = false;
00370 }
00371 
00372 
00373 void KHistoryComboBox::setPixmapProvider( KPixmapProvider *prov )
00374 {
00375     if ( d->myPixProvider == prov )
00376         return;
00377 
00378     delete d->myPixProvider;
00379     d->myPixProvider = prov;
00380 
00381     // re-insert all the items with/without pixmap
00382     // I would prefer to use changeItem(), but that doesn't honor the pixmap
00383     // when using an editable combobox (what we do)
00384     if ( count() > 0 ) {
00385         QStringList items( historyItems() );
00386         clear();
00387         insertItems( items );
00388     }
00389 }
00390 
00391 void KHistoryComboBox::insertItems( const QStringList& items )
00392 {
00393     QStringList::ConstIterator it = items.constBegin();
00394     const QStringList::ConstIterator itEnd = items.constEnd();
00395 
00396     while ( it != itEnd ) {
00397         const QString item = *it;
00398         if ( !item.isEmpty() ) { // only insert non-empty items
00399             if ( d->myPixProvider )
00400                 addItem( d->myPixProvider->pixmapFor(item, KIconLoader::SizeSmall),
00401                             item );
00402             else
00403                 addItem( item );
00404         }
00405         ++it;
00406     }
00407 }
00408 
00409 void KHistoryComboBox::slotClear()
00410 {
00411     clearHistory();
00412     emit cleared();
00413 }
00414 
00415 void KHistoryComboBox::slotSimulateActivated( const QString& text )
00416 {
00417     /* With the insertion policy NoInsert, which we use by default,
00418        Qt doesn't emit activated on typed text if the item is not already there,
00419        which is perhaps reasonable. Generate the signal ourselves if that's the case.
00420     */
00421     if ((insertPolicy() == NoInsert && findText(text, Qt::MatchFixedString|Qt::MatchCaseSensitive) == -1)) {
00422         emit activated(text);
00423     }
00424 
00425     /*
00426        Qt also doesn't emit it if the box is full, and policy is not
00427        InsertAtCurrent
00428     */
00429     else if (insertPolicy() != InsertAtCurrent && count() >= maxCount()) {
00430         emit activated(text);
00431     }
00432 }
00433 
00434 KPixmapProvider *KHistoryComboBox::pixmapProvider() const
00435 {
00436   return d->myPixProvider;
00437 }
00438 
00439 void KHistoryComboBox::reset()
00440 {
00441   slotReset();
00442 }
00443 
00444 #include "khistorycombobox.moc"

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal