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

KDEUI

  • kdeui
  • widgets
kcompletionbox.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2 
3  Copyright (c) 2000,2001,2002 Carsten Pfeiffer <pfeiffer@kde.org>
4  Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de>
5  Copyright (c) 2000,2001,2002,2003,2004 Dawit Alemayehu <adawit@kde.org>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library 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  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library 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 
24 #include "kcompletionbox.h"
25 #include "klineedit.h"
26 
27 #include <QtCore/QEvent>
28 #include <QtGui/QApplication>
29 #include <QtGui/QComboBox>
30 #include <QtGui/QStyle>
31 #include <QtGui/QScrollBar>
32 #include <QtGui/QKeyEvent>
33 
34 #include <kdebug.h>
35 #include <kconfig.h>
36 #include <kglobalsettings.h>
37 
38 class KCompletionBox::KCompletionBoxPrivate
39 {
40 public:
41  QWidget *m_parent; // necessary to set the focus back
42  QString cancelText;
43  bool tabHandling : 1;
44  bool upwardBox : 1;
45  bool emitSelected : 1;
46 };
47 
48 KCompletionBox::KCompletionBox( QWidget *parent )
49  :KListWidget( parent), d(new KCompletionBoxPrivate)
50 {
51  d->m_parent = parent;
52  d->tabHandling = true;
53  d->upwardBox = false;
54  d->emitSelected = true;
55 
56  setWindowFlags( Qt::ToolTip ); // calls setVisible, so must be done after initializations
57 
58  setLineWidth( 1 );
59  setFrameStyle( QFrame::Box | QFrame::Plain );
60 
61  setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
62  setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
63 
64  connect( this, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
65  SLOT(slotActivated(QListWidgetItem*)) );
66  connect( this, SIGNAL(itemClicked(QListWidgetItem*)),
67  SLOT(slotItemClicked(QListWidgetItem*)) );
68 }
69 
70 KCompletionBox::~KCompletionBox()
71 {
72  d->m_parent = 0L;
73  delete d;
74 }
75 
76 QStringList KCompletionBox::items() const
77 {
78  QStringList list;
79 
80  for (int i = 0 ; i < count() ; i++)
81  {
82  const QListWidgetItem* currItem = item(i);
83 
84  list.append(currItem->text());
85  }
86 
87  return list;
88 }
89 
90 void KCompletionBox::slotActivated( QListWidgetItem *item )
91 {
92  if ( !item )
93  return;
94 
95  hide();
96  emit activated( item->text() );
97 }
98 
99 bool KCompletionBox::eventFilter( QObject *o, QEvent *e )
100 {
101  int type = e->type();
102  QWidget *wid = qobject_cast<QWidget*>(o);
103 
104  if (o == this) {
105  return false;
106  }
107 
108  if (wid && wid == d->m_parent &&
109  (type == QEvent::Move || type == QEvent::Resize)) {
110  sizeAndPosition();
111  return false;
112  }
113 
114  if (wid && (wid->windowFlags() & Qt::Window) &&
115  type == QEvent::Move && wid == d->m_parent->window()) {
116  hide();
117  return false;
118  }
119 
120  if (type == QEvent::MouseButtonPress && (wid && !isAncestorOf(wid))) {
121  if (!d->emitSelected && currentItem() && !qobject_cast<QScrollBar*>(o)) {
122  Q_ASSERT(currentItem());
123  emit currentTextChanged(currentItem()->text() );
124  }
125  hide();
126  e->accept();
127  return true;
128  }
129 
130  if (wid && wid->isAncestorOf(d->m_parent) && isVisible()) {
131  if ( type == QEvent::KeyPress ) {
132  QKeyEvent *ev = static_cast<QKeyEvent *>( e );
133  switch ( ev->key() ) {
134  case Qt::Key_Backtab:
135  if ( d->tabHandling && (ev->modifiers() == Qt::NoButton ||
136  (ev->modifiers() & Qt::ShiftModifier)) ) {
137  up();
138  ev->accept();
139  return true;
140  }
141  break;
142  case Qt::Key_Tab:
143  if ( d->tabHandling && (ev->modifiers() == Qt::NoButton) ) {
144  down();
145  // #65877: Key_Tab should complete using the first
146  // (or selected) item, and then offer completions again
147  if (count() == 1) {
148  KLineEdit* parent = qobject_cast<KLineEdit*>(d->m_parent);
149  if (parent) {
150  parent->doCompletion(currentItem()->text());
151  } else {
152  hide();
153  }
154  }
155  ev->accept();
156  return true;
157  }
158  break;
159  case Qt::Key_Down:
160  down();
161  ev->accept();
162  return true;
163  case Qt::Key_Up:
164  // If there is no selected item and we've popped up above
165  // our parent, select the first item when they press up.
166  if ( !selectedItems().isEmpty() ||
167  mapToGlobal( QPoint( 0, 0 ) ).y() >
168  d->m_parent->mapToGlobal( QPoint( 0, 0 ) ).y() )
169  up();
170  else
171  down();
172  ev->accept();
173  return true;
174  case Qt::Key_PageUp:
175  pageUp();
176  ev->accept();
177  return true;
178  case Qt::Key_PageDown:
179  pageDown();
180  ev->accept();
181  return true;
182  case Qt::Key_Escape:
183  canceled();
184  ev->accept();
185  return true;
186  case Qt::Key_Enter:
187  case Qt::Key_Return:
188  if ( ev->modifiers() & Qt::ShiftModifier ) {
189  hide();
190  ev->accept(); // Consume the Enter event
191  return true;
192  }
193  break;
194  case Qt::Key_End:
195  if ( ev->modifiers() & Qt::ControlModifier )
196  {
197  end();
198  ev->accept();
199  return true;
200  }
201  break;
202  case Qt::Key_Home:
203  if ( ev->modifiers() & Qt::ControlModifier )
204  {
205  home();
206  ev->accept();
207  return true;
208  }
209  default:
210  break;
211  }
212  } else if ( type == QEvent::ShortcutOverride ) {
213  // Override any accelerators that match
214  // the key sequences we use here...
215  QKeyEvent *ev = static_cast<QKeyEvent *>( e );
216  switch ( ev->key() ) {
217  case Qt::Key_Down:
218  case Qt::Key_Up:
219  case Qt::Key_PageUp:
220  case Qt::Key_PageDown:
221  case Qt::Key_Escape:
222  case Qt::Key_Enter:
223  case Qt::Key_Return:
224  ev->accept();
225  return true;
226  case Qt::Key_Tab:
227  case Qt::Key_Backtab:
228  if ( ev->modifiers() == Qt::NoButton ||
229  (ev->modifiers() & Qt::ShiftModifier))
230  {
231  ev->accept();
232  return true;
233  }
234  break;
235  case Qt::Key_Home:
236  case Qt::Key_End:
237  if ( ev->modifiers() & Qt::ControlModifier )
238  {
239  ev->accept();
240  return true;
241  }
242  break;
243  default:
244  break;
245  }
246  } else if ( type == QEvent::FocusOut ) {
247  QFocusEvent* event = static_cast<QFocusEvent*>( e );
248  if (event->reason() != Qt::PopupFocusReason
249 #ifdef Q_WS_WIN
250  && (event->reason() != Qt::ActiveWindowFocusReason || QApplication::activeWindow() != this)
251 #endif
252  )
253  hide();
254  }
255  }
256 
257  return KListWidget::eventFilter( o, e );
258 }
259 
260 void KCompletionBox::popup()
261 {
262  if ( count() == 0 )
263  hide();
264  else {
265  bool block = signalsBlocked();
266  blockSignals( true );
267  setCurrentRow( -1 );
268  blockSignals( block );
269  clearSelection();
270  if ( !isVisible() )
271  show();
272  else if ( size().height() != sizeHint().height() )
273  sizeAndPosition();
274  }
275 }
276 
277 void KCompletionBox::sizeAndPosition()
278 {
279  int currentGeom = height();
280  QPoint currentPos = pos();
281  QRect geom = calculateGeometry();
282  resize( geom.size() );
283 
284  int x = currentPos.x(), y = currentPos.y();
285  if ( d->m_parent ) {
286  if ( !isVisible() ) {
287  QPoint orig = globalPositionHint();
288  QRect screenSize = KGlobalSettings::desktopGeometry(orig);
289 
290  x = orig.x() + geom.x();
291  y = orig.y() + geom.y();
292 
293  if ( x + width() > screenSize.right() )
294  x = screenSize.right() - width();
295  if (y + height() > screenSize.bottom() ) {
296  y = y - height() - d->m_parent->height();
297  d->upwardBox = true;
298  }
299  }
300  else {
301  // Are we above our parent? If so we must keep bottom edge anchored.
302  if (d->upwardBox)
303  y += (currentGeom-height());
304  }
305  move( x, y);
306  }
307 }
308 
309 QPoint KCompletionBox::globalPositionHint() const
310 {
311  if (!d->m_parent)
312  return QPoint();
313  return d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
314 }
315 
316 void KCompletionBox::setVisible( bool visible )
317 {
318  if (visible) {
319  d->upwardBox = false;
320  if ( d->m_parent ) {
321  sizeAndPosition();
322  qApp->installEventFilter( this );
323  }
324 
325  // ### we shouldn't need to call this, but without this, the scrollbars
326  // are pretty b0rked.
327  //triggerUpdate( true );
328 
329  // Workaround for I'm not sure whose bug - if this KCompletionBox' parent
330  // is in a layout, that layout will detect inserting new child (posted
331  // ChildInserted event), and will trigger relayout (post LayoutHint event).
332  // QWidget::show() sends also posted ChildInserted events for the parent,
333  // and later all LayoutHint events, which causes layout updating.
334  // The problem is, KCompletionBox::eventFilter() detects resizing
335  // of the parent, and calls hide() - and this hide() happen in the middle
336  // of show(), causing inconsistent state. I'll try to submit a Qt patch too.
337  qApp->sendPostedEvents();
338  } else {
339  if ( d->m_parent )
340  qApp->removeEventFilter( this );
341  d->cancelText.clear();
342  }
343 
344  KListWidget::setVisible(visible);
345 }
346 
347 QRect KCompletionBox::calculateGeometry() const
348 {
349  QRect visualRect;
350  if (count() == 0 || !(visualRect = visualItemRect(item(0))).isValid())
351  return QRect();
352 
353  int x = 0, y = 0;
354  int ih = visualRect.height();
355  int h = qMin( 15 * ih, (int) count() * ih ) + 2*frameWidth();
356 
357  int w = (d->m_parent) ? d->m_parent->width() : KListWidget::minimumSizeHint().width();
358  w = qMax( KListWidget::minimumSizeHint().width(), w );
359 
360  //### M.O.: Qt4 doesn't actually honor SC_ComboBoxListBoxPopup ???
361 #if 0
362  //If we're inside a combox, Qt by default makes the dropdown
363  // as wide as the combo, and gives the style a chance
364  // to adjust it. Do that here as well, for consistency
365  const QObject* combo;
366  if ( d->m_parent && (combo = d->m_parent->parent() ) &&
367  qobject_cast<QComboBox*>(combo) )
368  {
369  const QComboBox* cb = static_cast<const QComboBox*>(combo);
370 
371  //Expand to the combo width
372  w = qMax( w, cb->width() );
373 
374  QPoint parentCorner = d->m_parent->mapToGlobal(QPoint(0, 0));
375  QPoint comboCorner = cb->mapToGlobal(QPoint(0, 0));
376 
377  //We need to adjust our horizontal position to also be WRT to the combo
378  x += comboCorner.x() - parentCorner.x();
379 
380  //The same with vertical one
381  y += cb->height() - d->m_parent->height() +
382  comboCorner.y() - parentCorner.y();
383 
384  //Ask the style to refine this a bit
385  QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox,
386  cb, QStyle::SC_ComboBoxListBoxPopup,
387  QStyleOption(x, y, w, h));
388  //QCommonStyle returns QRect() by default, so this is what we get if the
389  //style doesn't implement this
390  if (!styleAdj.isNull())
391  return styleAdj;
392 
393  }
394 #endif
395  return QRect(x, y, w, h);
396 }
397 
398 QSize KCompletionBox::sizeHint() const
399 {
400  return calculateGeometry().size();
401 }
402 
403 void KCompletionBox::down()
404 {
405  const int row = currentRow();
406  const int lastRow = count() - 1;
407  if (row < lastRow) {
408  setCurrentRow(row + 1);
409  return;
410  }
411 
412  if (lastRow > -1) {
413  setCurrentRow(0);
414  }
415 }
416 
417 void KCompletionBox::up()
418 {
419  const int row = currentRow();
420  if (row > 0) {
421  setCurrentRow(row - 1);
422  return;
423  }
424 
425  const int lastRow = count() - 1;
426  if (lastRow > 0) {
427  setCurrentRow(lastRow);
428  }
429 }
430 
431 void KCompletionBox::pageDown()
432 {
433  //int i = currentItem() + numItemsVisible();
434  //i = i > (int)count() - 1 ? (int)count() - 1 : i;
435  //setCurrentRow( i );
436  moveCursor(QAbstractItemView::MovePageDown , Qt::NoModifier);
437 }
438 
439 void KCompletionBox::pageUp()
440 {
441  //int i = currentItem() - numItemsVisible();
442  //i = i < 0 ? 0 : i;
443  //setCurrentRow( i );
444 
445  moveCursor(QAbstractItemView::MovePageUp , Qt::NoModifier);
446 }
447 
448 void KCompletionBox::home()
449 {
450  setCurrentRow( 0 );
451 }
452 
453 void KCompletionBox::end()
454 {
455  setCurrentRow( count() -1 );
456 }
457 
458 void KCompletionBox::setTabHandling( bool enable )
459 {
460  d->tabHandling = enable;
461 }
462 
463 bool KCompletionBox::isTabHandling() const
464 {
465  return d->tabHandling;
466 }
467 
468 void KCompletionBox::setCancelledText( const QString& text )
469 {
470  d->cancelText = text;
471 }
472 
473 QString KCompletionBox::cancelledText() const
474 {
475  return d->cancelText;
476 }
477 
478 void KCompletionBox::canceled()
479 {
480  if ( !d->cancelText.isNull() )
481  emit userCancelled( d->cancelText );
482  if ( isVisible() )
483  hide();
484 }
485 
486 class KCompletionBoxItem : public QListWidgetItem
487 {
488 public:
489  //Returns true if dirty.
490  bool reuse( const QString& newText )
491  {
492  if ( text() == newText )
493  return false;
494  setText( newText );
495  return true;
496  }
497 };
498 
499 
500 void KCompletionBox::insertItems( const QStringList& items, int index )
501 {
502  bool block = signalsBlocked();
503  blockSignals( true );
504  KListWidget::insertItems( index, items );
505  blockSignals( block );
506  setCurrentRow(-1);
507 }
508 
509 void KCompletionBox::setItems( const QStringList& items )
510 {
511  bool block = signalsBlocked();
512  blockSignals( true );
513 
514  int rowIndex = 0;
515 
516  if (!count()) {
517  addItems(items);
518  } else {
519  // Keep track of whether we need to change anything,
520  // so we can avoid a repaint for identical updates,
521  // to reduce flicker
522  bool dirty = false;
523 
524  QStringList::ConstIterator it = items.constBegin();
525  const QStringList::ConstIterator itEnd = items.constEnd();
526 
527  for ( ; it != itEnd; ++it) {
528  if ( rowIndex < count() ) {
529  const bool changed = ((KCompletionBoxItem*)item(rowIndex))->reuse( *it );
530  dirty = dirty || changed;
531  } else {
532  dirty = true;
533  // Inserting an item is a way of making this dirty
534  addItem(*it);
535  }
536  rowIndex++;
537  }
538 
539  // If there is an unused item, mark as dirty -> less items now
540  if (rowIndex < count()) {
541  dirty = true;
542  }
543 
544  // remove unused items with an index >= rowIndex
545  for ( ; rowIndex < count() ; ) {
546  QListWidgetItem* item = takeItem(rowIndex);
547  Q_ASSERT(item);
548  delete item;
549  }
550 
551  //TODO KDE4 : Port me
552  //if (dirty)
553  // triggerUpdate( false );
554  }
555 
556  if (isVisible() && size().height() != sizeHint().height())
557  sizeAndPosition();
558 
559  blockSignals(block);
560 }
561 
562 void KCompletionBox::slotItemClicked( QListWidgetItem *item )
563 {
564  if ( item )
565  {
566  hide();
567  emit currentTextChanged( item->text() );
568  emit activated( item->text() );
569  }
570 }
571 
572 void KCompletionBox::setActivateOnSelect(bool state)
573 {
574  d->emitSelected = state;
575 }
576 
577 bool KCompletionBox::activateOnSelect() const
578 {
579  return d->emitSelected;
580 }
581 
582 #include "kcompletionbox.moc"
KCompletionBox::down
void down()
Moves the selection one line down or select the first item if nothing is selected yet...
Definition: kcompletionbox.cpp:403
KCompletionBox::~KCompletionBox
~KCompletionBox()
Destroys the box.
Definition: kcompletionbox.cpp:70
KCompletionBox::calculateGeometry
QRect calculateGeometry() const
This calculates the size of the dropdown and the relative position of the top left corner with respec...
Definition: kcompletionbox.cpp:347
KCompletionBox::activateOnSelect
bool activateOnSelect() const
KLineEdit::doCompletion
void doCompletion(const QString &txt)
Do completion now.
Definition: klineedit.cpp:1855
kdebug.h
KCompletionBox::KCompletionBox
KCompletionBox(QWidget *parent=0)
Constructs a KCompletionBox.
Definition: kcompletionbox.cpp:48
KCompletionBox::up
void up()
Moves the selection one line up or select the first item if nothing is selected yet.
Definition: kcompletionbox.cpp:417
KCompletionBox::setCancelledText
void setCancelledText(const QString &txt)
Sets the text to be emitted if the user chooses not to pick from the available matches.
Definition: kcompletionbox.cpp:468
KCompletionBox::cancelledText
QString cancelledText() const
kglobalsettings.h
kcompletionbox.h
KCompletionBox::pageUp
void pageUp()
Moves the selection one page up.
Definition: kcompletionbox.cpp:439
kconfig.h
QWidget
KCompletionBox::items
QStringList items() const
Returns a list of all items currently in the box.
Definition: kcompletionbox.cpp:76
KGlobalSettings::desktopGeometry
static QRect desktopGeometry(const QPoint &point)
This function returns the desktop geometry for an application that needs to set the geometry of a wid...
Definition: kglobalsettings.cpp:732
QString
KCompletionBox::setActivateOnSelect
void setActivateOnSelect(bool state)
Set whether or not the selected signal should be emitted when an item is selected.
Definition: kcompletionbox.cpp:572
KCompletionBox::setVisible
virtual void setVisible(bool visible)
Re-implemented for internal reasons.
Definition: kcompletionbox.cpp:316
KCompletionBox::popup
virtual void popup()
Adjusts the size of the box to fit the width of the parent given in the constructor and pops it up at...
Definition: kcompletionbox.cpp:260
QObject
KCompletionBox::globalPositionHint
virtual QPoint globalPositionHint() const
The preferred global coordinate at which the completion box&#39;s top left corner should be positioned...
Definition: kcompletionbox.cpp:309
KCompletionBox::insertItems
void insertItems(const QStringList &items, int index=-1)
Inserts items into the box.
Definition: kcompletionbox.cpp:500
QStringList
QComboBox
KCompletionBox::end
void end()
Moves the selection down to the last item.
Definition: kcompletionbox.cpp:453
KCompletionBox::pageDown
void pageDown()
Moves the selection one page down.
Definition: kcompletionbox.cpp:431
KCompletionBox::setTabHandling
void setTabHandling(bool enable)
Makes this widget (when visible) capture Tab-key events to traverse the items in the dropdown list (T...
Definition: kcompletionbox.cpp:458
KCompletionBox::home
void home()
Moves the selection up to the first item.
Definition: kcompletionbox.cpp:448
KCompletionBox::eventFilter
virtual bool eventFilter(QObject *, QEvent *)
Reimplemented from KListWidget to get events from the viewport (to hide this widget on mouse-click...
Definition: kcompletionbox.cpp:99
KCompletionBox::activated
void activated(const QString &)
Emitted when an item was selected, contains the text of the selected item.
KCompletionBox::isTabHandling
bool isTabHandling() const
KLineEdit
An enhanced QLineEdit widget for inputting text.
Definition: klineedit.h:149
QPoint
KCompletionBox::sizeHint
virtual QSize sizeHint() const
Definition: kcompletionbox.cpp:398
QRect
KCompletionBox::sizeAndPosition
void sizeAndPosition()
This properly sizes and positions the listbox.
Definition: kcompletionbox.cpp:277
KCompletionBox::userCancelled
void userCancelled(const QString &)
Emitted whenever the user chooses to ignore the available selections and close the this box...
QSize
klineedit.h
KListWidget
A variant of QListWidget that honors KDE&#39;s system-wide settings.
Definition: klistwidget.h:40
KCompletionBox::setItems
void setItems(const QStringList &items)
Clears the box and inserts items.
Definition: kcompletionbox.cpp:509
KCompletionBox::slotActivated
virtual void slotActivated(QListWidgetItem *)
Called when an item was activated.
Definition: kcompletionbox.cpp:90
This file is part of the KDE documentation.
Documentation copyright © 1996-2017 The KDE developers.
Generated on Sat Feb 25 2017 06:45:28 by doxygen 1.8.5 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.10.5 API Reference

Skip menu "kdelibs-4.10.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