• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.11.5 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • widgets
khistorycombobox.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2 
3  Copyright (c) 2000,2001 Dawit Alemayehu <adawit@kde.org>
4  Copyright (c) 2000,2001 Carsten Pfeiffer <pfeiffer@kde.org>
5  Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public
9  License (LGPL) as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
22 
23 #include "khistorycombobox.h"
24 
25 #include <QtGui/QAbstractItemView>
26 #include <QtGui/QApplication>
27 #include <QtGui/QKeyEvent>
28 #include <QtGui/QMenu>
29 #include <QtGui/QWheelEvent>
30 
31 #include <klocale.h>
32 #include <knotification.h>
33 #include <kpixmapprovider.h>
34 #include <kstandardshortcut.h>
35 #include <kicontheme.h>
36 #include <kicon.h>
37 
38 #include <kdebug.h>
39 
40 class KHistoryComboBox::Private
41 {
42 public:
43  Private(KHistoryComboBox *q): q(q), myPixProvider(0) {}
44 
45  KHistoryComboBox *q;
46 
50  int myIterateIndex;
51 
55  QString myText;
56 
61  bool myRotated;
62  KPixmapProvider *myPixProvider;
63 };
64 
65 // we are always read-write
66 KHistoryComboBox::KHistoryComboBox( QWidget *parent )
67  : KComboBox( true, parent ), d(new Private(this))
68 {
69  init( true ); // using completion
70 }
71 
72 // we are always read-write
73 KHistoryComboBox::KHistoryComboBox( bool useCompletion,
74  QWidget *parent )
75  : KComboBox( true, parent ), d(new Private(this))
76 {
77  init( useCompletion );
78 }
79 
80 void KHistoryComboBox::init( bool useCompletion )
81 {
82  // Set a default history size to something reasonable, Qt sets it to INT_MAX by default
83  setMaxCount( 50 );
84 
85  if ( useCompletion )
86  completionObject()->setOrder( KCompletion::Weighted );
87 
88  setInsertPolicy( NoInsert );
89  d->myIterateIndex = -1;
90  d->myRotated = false;
91  d->myPixProvider = 0L;
92 
93  // obey HISTCONTROL setting
94  QByteArray histControl = qgetenv("HISTCONTROL");
95  if ( histControl == "ignoredups" || histControl == "ignoreboth" )
96  setDuplicatesEnabled( false );
97 
98  connect(this, SIGNAL(aboutToShowContextMenu(QMenu*)), SLOT(addContextMenuItems(QMenu*)));
99  connect(this, SIGNAL(activated(int)), SLOT(slotReset()));
100  connect(this, SIGNAL(returnPressed(QString)), SLOT(slotReset()));
101  // We want slotSimulateActivated to be called _after_ QComboBoxPrivate::_q_returnPressed
102  // otherwise there's a risk of emitting activated twice (slotSimulateActivated will find
103  // the item, after some app's slotActivated inserted the item into the combo).
104  connect(this, SIGNAL(returnPressed(QString)), SLOT(slotSimulateActivated(QString)), Qt::QueuedConnection);
105 }
106 
107 KHistoryComboBox::~KHistoryComboBox()
108 {
109  delete d->myPixProvider;
110  delete d;
111 }
112 
113 void KHistoryComboBox::setHistoryItems( const QStringList &items )
114 {
115  setHistoryItems(items, false);
116 }
117 
118 void KHistoryComboBox::setHistoryItems( const QStringList &items,
119  bool setCompletionList )
120 {
121  QStringList insertingItems = items;
122  KComboBox::clear();
123 
124  // limit to maxCount()
125  const int itemCount = insertingItems.count();
126  const int toRemove = itemCount - maxCount();
127 
128  if (toRemove >= itemCount) {
129  insertingItems.clear();
130  } else {
131  for (int i = 0; i < toRemove; ++i)
132  insertingItems.pop_front();
133  }
134 
135  insertItems( insertingItems );
136 
137  if ( setCompletionList && useCompletion() ) {
138  // we don't have any weighting information here ;(
139  KCompletion *comp = completionObject();
140  comp->setOrder( KCompletion::Insertion );
141  comp->setItems( insertingItems );
142  comp->setOrder( KCompletion::Weighted );
143  }
144 
145  clearEditText();
146 }
147 
148 QStringList KHistoryComboBox::historyItems() const
149 {
150  QStringList list;
151  const int itemCount = count();
152  for ( int i = 0; i < itemCount; ++i )
153  list.append( itemText( i ) );
154 
155  return list;
156 }
157 
158 bool KHistoryComboBox::useCompletion() const
159 {
160  return compObj();
161 }
162 
163 void KHistoryComboBox::clearHistory()
164 {
165  const QString temp = currentText();
166  KComboBox::clear();
167  if ( useCompletion() )
168  completionObject()->clear();
169  setEditText( temp );
170 }
171 
172 void KHistoryComboBox::addContextMenuItems( QMenu* menu )
173 {
174  if ( menu )
175  {
176  menu->addSeparator();
177  QAction* clearHistory = menu->addAction( KIcon("edit-clear-history"), i18n("Clear &History"), this, SLOT(slotClear()));
178  if (!count())
179  clearHistory->setEnabled(false);
180  }
181 }
182 
183 void KHistoryComboBox::addToHistory( const QString& item )
184 {
185  if ( item.isEmpty() || (count() > 0 && item == itemText(0) )) {
186  return;
187  }
188 
189  bool wasCurrent = false;
190  // remove all existing items before adding
191  if ( !duplicatesEnabled() ) {
192  int i = 0;
193  int itemCount = count();
194  while ( i < itemCount ) {
195  if ( itemText( i ) == item ) {
196  if ( !wasCurrent )
197  wasCurrent = ( i == currentIndex() );
198  removeItem( i );
199  --itemCount;
200  } else {
201  ++i;
202  }
203  }
204  }
205 
206  // now add the item
207  if ( d->myPixProvider )
208  insertItem( 0, d->myPixProvider->pixmapFor(item, iconSize().height()), item);
209  else
210  insertItem( 0, item );
211 
212  if ( wasCurrent )
213  setCurrentIndex( 0 );
214 
215  const bool useComp = useCompletion();
216 
217  const int last = count() - 1; // last valid index
218  const int mc = maxCount();
219  const int stopAt = qMax(mc, 0);
220 
221  for (int rmIndex = last; rmIndex >= stopAt; --rmIndex) {
222  // remove the last item, as long as we are longer than maxCount()
223  // remove the removed item from the completionObject if it isn't
224  // anymore available at all in the combobox.
225  const QString rmItem = itemText( rmIndex );
226  removeItem( rmIndex );
227  if ( useComp && !contains( rmItem ) )
228  completionObject()->removeItem( rmItem );
229  }
230 
231  if ( useComp )
232  completionObject()->addItem( item );
233 }
234 
235 bool KHistoryComboBox::removeFromHistory( const QString& item )
236 {
237  if ( item.isEmpty() )
238  return false;
239 
240  bool removed = false;
241  const QString temp = currentText();
242  int i = 0;
243  int itemCount = count();
244  while ( i < itemCount ) {
245  if ( item == itemText( i ) ) {
246  removed = true;
247  removeItem( i );
248  --itemCount;
249  } else {
250  ++i;
251  }
252  }
253 
254  if ( removed && useCompletion() )
255  completionObject()->removeItem( item );
256 
257  setEditText( temp );
258  return removed;
259 }
260 
261 // going up in the history, rotating when reaching QListBox::count()
262 //
263 // Note: this differs from QComboBox because "up" means ++index here,
264 // to simulate the way shell history works (up goes to the most
265 // recent item). In QComboBox "down" means ++index, to match the popup...
266 //
267 void KHistoryComboBox::rotateUp()
268 {
269  // save the current text in the lineedit
270  // (This is also where this differs from standard up/down in QComboBox,
271  // where a single keypress can make you lose your typed text)
272  if ( d->myIterateIndex == -1 )
273  d->myText = currentText();
274 
275  ++d->myIterateIndex;
276 
277  // skip duplicates/empty items
278  const int last = count() - 1; // last valid index
279  const QString currText = currentText();
280 
281  while ( d->myIterateIndex < last &&
282  (currText == itemText( d->myIterateIndex ) ||
283  itemText( d->myIterateIndex ).isEmpty()) )
284  ++d->myIterateIndex;
285 
286  if ( d->myIterateIndex >= count() ) {
287  d->myRotated = true;
288  d->myIterateIndex = -1;
289 
290  // if the typed text is the same as the first item, skip the first
291  if ( count() > 0 && d->myText == itemText(0) )
292  d->myIterateIndex = 0;
293 
294  setEditText( d->myText );
295  } else {
296  setCurrentIndex(d->myIterateIndex);
297  }
298 }
299 
300 // going down in the history, no rotation possible. Last item will be
301 // the text that was in the lineedit before Up was called.
302 void KHistoryComboBox::rotateDown()
303 {
304  // save the current text in the lineedit
305  if ( d->myIterateIndex == -1 )
306  d->myText = currentText();
307 
308  --d->myIterateIndex;
309 
310  const QString currText = currentText();
311  // skip duplicates/empty items
312  while ( d->myIterateIndex >= 0 &&
313  (currText == itemText( d->myIterateIndex ) ||
314  itemText( d->myIterateIndex ).isEmpty()) )
315  --d->myIterateIndex;
316 
317 
318  if ( d->myIterateIndex < 0 ) {
319  if ( d->myRotated && d->myIterateIndex == -2 ) {
320  d->myRotated = false;
321  d->myIterateIndex = count() - 1;
322  setEditText( itemText(d->myIterateIndex) );
323  }
324  else { // bottom of history
325  if ( d->myIterateIndex == -2 ) {
326  KNotification::event( "Textcompletion: No Match" ,
327  i18n("No further items in the history."),
328  QPixmap() , this, KNotification::DefaultEvent);
329  }
330 
331  d->myIterateIndex = -1;
332  if ( currentText() != d->myText )
333  setEditText( d->myText );
334  }
335  } else {
336  setCurrentIndex(d->myIterateIndex);
337  }
338 }
339 
340 void KHistoryComboBox::keyPressEvent( QKeyEvent *e )
341 {
342  int event_key = e->key() | e->modifiers();
343 
344  if ( KStandardShortcut::rotateUp().contains(event_key) )
345  rotateUp();
346  else if ( KStandardShortcut::rotateDown().contains(event_key) )
347  rotateDown();
348  else
349  KComboBox::keyPressEvent( e );
350 }
351 
352 void KHistoryComboBox::wheelEvent( QWheelEvent *ev )
353 {
354  // Pass to poppable listbox if it's up
355  QAbstractItemView* const iv = view();
356  if ( iv && iv->isVisible() )
357  {
358  QApplication::sendEvent( iv, ev );
359  return;
360  }
361  // Otherwise make it change the text without emitting activated
362  if ( ev->delta() > 0 ) {
363  rotateUp();
364  } else {
365  rotateDown();
366  }
367  ev->accept();
368 }
369 
370 void KHistoryComboBox::slotReset()
371 {
372  d->myIterateIndex = -1;
373  d->myRotated = false;
374 }
375 
376 
377 void KHistoryComboBox::setPixmapProvider( KPixmapProvider *prov )
378 {
379  if ( d->myPixProvider == prov )
380  return;
381 
382  delete d->myPixProvider;
383  d->myPixProvider = prov;
384 
385  // re-insert all the items with/without pixmap
386  // I would prefer to use changeItem(), but that doesn't honor the pixmap
387  // when using an editable combobox (what we do)
388  if ( count() > 0 ) {
389  QStringList items( historyItems() );
390  clear();
391  insertItems( items );
392  }
393 }
394 
395 void KHistoryComboBox::insertItems( const QStringList& items )
396 {
397  QStringList::ConstIterator it = items.constBegin();
398  const QStringList::ConstIterator itEnd = items.constEnd();
399 
400  while ( it != itEnd ) {
401  const QString item = *it;
402  if ( !item.isEmpty() ) { // only insert non-empty items
403  if ( d->myPixProvider )
404  addItem( d->myPixProvider->pixmapFor(item, iconSize().height()),
405  item );
406  else
407  addItem( item );
408  }
409  ++it;
410  }
411 }
412 
413 void KHistoryComboBox::slotClear()
414 {
415  clearHistory();
416  emit cleared();
417 }
418 
419 void KHistoryComboBox::slotSimulateActivated( const QString& text )
420 {
421  /* With the insertion policy NoInsert, which we use by default,
422  Qt doesn't emit activated on typed text if the item is not already there,
423  which is perhaps reasonable. Generate the signal ourselves if that's the case.
424  */
425  if ((insertPolicy() == NoInsert && findText(text, Qt::MatchFixedString|Qt::MatchCaseSensitive) == -1)) {
426  emit activated(text);
427  }
428 
429  /*
430  Qt also doesn't emit it if the box is full, and policy is not
431  InsertAtCurrent
432  */
433  else if (insertPolicy() != InsertAtCurrent && count() >= maxCount()) {
434  emit activated(text);
435  }
436 }
437 
438 KPixmapProvider *KHistoryComboBox::pixmapProvider() const
439 {
440  return d->myPixProvider;
441 }
442 
443 void KHistoryComboBox::reset()
444 {
445  slotReset();
446 }
447 
448 #include "khistorycombobox.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Sep 23 2014 09:57:46 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

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

kdelibs-4.11.5 API Reference

Skip menu "kdelibs-4.11.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal