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

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