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