• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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 // going up in the history, rotating when reaching QListBox::count()
00262 //
00263 // Note: this differs from QComboBox because "up" means ++index here,
00264 // to simulate the way shell history works (up goes to the most
00265 // recent item). In QComboBox "down" means ++index, to match the popup...
00266 //
00267 void KHistoryComboBox::rotateUp()
00268 {
00269     // save the current text in the lineedit
00270     // (This is also where this differs from standard up/down in QComboBox,
00271     // where a single keypress can make you lose your typed text)
00272     if ( d->myIterateIndex == -1 )
00273         d->myText = currentText();
00274 
00275     ++d->myIterateIndex;
00276 
00277     // skip duplicates/empty items
00278     const int last = count() - 1; // last valid index
00279     const QString currText = currentText();
00280 
00281     while ( d->myIterateIndex < last &&
00282             (currText == itemText( d->myIterateIndex ) ||
00283              itemText( d->myIterateIndex ).isEmpty()) )
00284         ++d->myIterateIndex;
00285 
00286     if ( d->myIterateIndex >= count() ) {
00287         d->myRotated = true;
00288         d->myIterateIndex = -1;
00289 
00290         // if the typed text is the same as the first item, skip the first
00291         if ( count() > 0 && d->myText == itemText(0) )
00292             d->myIterateIndex = 0;
00293 
00294         setEditText( d->myText );
00295     } else {
00296         setCurrentIndex(d->myIterateIndex);
00297     }
00298 }
00299 
00300 // going down in the history, no rotation possible. Last item will be
00301 // the text that was in the lineedit before Up was called.
00302 void KHistoryComboBox::rotateDown()
00303 {
00304     // save the current text in the lineedit
00305     if ( d->myIterateIndex == -1 )
00306         d->myText = currentText();
00307 
00308     --d->myIterateIndex;
00309 
00310     const QString currText = currentText();
00311     // skip duplicates/empty items
00312     while ( d->myIterateIndex >= 0 &&
00313             (currText == itemText( d->myIterateIndex ) ||
00314              itemText( d->myIterateIndex ).isEmpty()) )
00315         --d->myIterateIndex;
00316 
00317 
00318     if ( d->myIterateIndex < 0 ) {
00319         if ( d->myRotated && d->myIterateIndex == -2 ) {
00320             d->myRotated = false;
00321             d->myIterateIndex = count() - 1;
00322             setEditText( itemText(d->myIterateIndex) );
00323         }
00324         else { // bottom of history
00325             if ( d->myIterateIndex == -2 ) {
00326                 KNotification::event( "Textcompletion: No Match" ,
00327                                       i18n("No further items in the history."),
00328                                        QPixmap() , this, KNotification::DefaultEvent);
00329             }
00330 
00331             d->myIterateIndex = -1;
00332             if ( currentText() != d->myText )
00333                 setEditText( d->myText );
00334         }
00335     } else {
00336         setCurrentIndex(d->myIterateIndex);
00337     }
00338 }
00339 
00340 void KHistoryComboBox::keyPressEvent( QKeyEvent *e )
00341 {
00342     int event_key = e->key() | e->modifiers();
00343 
00344     if ( KStandardShortcut::rotateUp().contains(event_key) )
00345         rotateUp();
00346     else if ( KStandardShortcut::rotateDown().contains(event_key) )
00347         rotateDown();
00348     else
00349         KComboBox::keyPressEvent( e );
00350 }
00351 
00352 void KHistoryComboBox::wheelEvent( QWheelEvent *ev )
00353 {
00354     // Pass to poppable listbox if it's up
00355     QAbstractItemView* const iv = view();
00356     if ( iv && iv->isVisible() )
00357     {
00358         QApplication::sendEvent( iv, ev );
00359         return;
00360     }
00361     // Otherwise make it change the text without emitting activated
00362     if ( ev->delta() > 0 ) {
00363         rotateUp();
00364     } else {
00365         rotateDown();
00366     }
00367     ev->accept();
00368 }
00369 
00370 void KHistoryComboBox::slotReset()
00371 {
00372     d->myIterateIndex = -1;
00373     d->myRotated = false;
00374 }
00375 
00376 
00377 void KHistoryComboBox::setPixmapProvider( KPixmapProvider *prov )
00378 {
00379     if ( d->myPixProvider == prov )
00380         return;
00381 
00382     delete d->myPixProvider;
00383     d->myPixProvider = prov;
00384 
00385     // re-insert all the items with/without pixmap
00386     // I would prefer to use changeItem(), but that doesn't honor the pixmap
00387     // when using an editable combobox (what we do)
00388     if ( count() > 0 ) {
00389         QStringList items( historyItems() );
00390         clear();
00391         insertItems( items );
00392     }
00393 }
00394 
00395 void KHistoryComboBox::insertItems( const QStringList& items )
00396 {
00397     QStringList::ConstIterator it = items.constBegin();
00398     const QStringList::ConstIterator itEnd = items.constEnd();
00399 
00400     while ( it != itEnd ) {
00401         const QString item = *it;
00402         if ( !item.isEmpty() ) { // only insert non-empty items
00403             if ( d->myPixProvider )
00404                 addItem( d->myPixProvider->pixmapFor(item, KIconLoader::SizeSmall),
00405                             item );
00406             else
00407                 addItem( item );
00408         }
00409         ++it;
00410     }
00411 }
00412 
00413 void KHistoryComboBox::slotClear()
00414 {
00415     clearHistory();
00416     emit cleared();
00417 }
00418 
00419 void KHistoryComboBox::slotSimulateActivated( const QString& text )
00420 {
00421     /* With the insertion policy NoInsert, which we use by default,
00422        Qt doesn't emit activated on typed text if the item is not already there,
00423        which is perhaps reasonable. Generate the signal ourselves if that's the case.
00424     */
00425     if ((insertPolicy() == NoInsert && findText(text, Qt::MatchFixedString|Qt::MatchCaseSensitive) == -1)) {
00426         emit activated(text);
00427     }
00428 
00429     /*
00430        Qt also doesn't emit it if the box is full, and policy is not
00431        InsertAtCurrent
00432     */
00433     else if (insertPolicy() != InsertAtCurrent && count() >= maxCount()) {
00434         emit activated(text);
00435     }
00436 }
00437 
00438 KPixmapProvider *KHistoryComboBox::pixmapProvider() const
00439 {
00440   return d->myPixProvider;
00441 }
00442 
00443 void KHistoryComboBox::reset()
00444 {
00445   slotReset();
00446 }
00447 
00448 #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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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