KDEUI
kcompletionbox.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "kcompletionbox.h"
00025 #include "klineedit.h"
00026
00027 #include <QtCore/QEvent>
00028 #include <QtGui/QApplication>
00029 #include <QtGui/QComboBox>
00030 #include <QtGui/QStyle>
00031 #include <QtGui/QScrollBar>
00032 #include <QtGui/QKeyEvent>
00033
00034 #include <kdebug.h>
00035 #include <kconfig.h>
00036 #include <kglobalsettings.h>
00037
00038 class KCompletionBox::KCompletionBoxPrivate
00039 {
00040 public:
00041 QWidget *m_parent;
00042 QString cancelText;
00043 bool tabHandling : 1;
00044 bool down_workaround : 1;
00045 bool upwardBox : 1;
00046 bool emitSelected : 1;
00047 };
00048
00049 KCompletionBox::KCompletionBox( QWidget *parent )
00050 :KListWidget( parent), d(new KCompletionBoxPrivate)
00051 {
00052 d->m_parent = parent;
00053 d->tabHandling = true;
00054 d->down_workaround = false;
00055 d->upwardBox = false;
00056 d->emitSelected = true;
00057
00058 setWindowFlags( Qt::ToolTip );
00059
00060 setLineWidth( 1 );
00061 setFrameStyle( QFrame::Box | QFrame::Plain );
00062
00063 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
00064 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00065
00066 connect( this, SIGNAL( itemDoubleClicked( QListWidgetItem * )),
00067 SLOT( slotActivated( QListWidgetItem * )) );
00068
00069 #ifdef __GNUC__
00070 #warning "Check if this workaround can be removed in KDE 4"
00071 #endif
00072
00073
00074 connect( this, SIGNAL( currentItemChanged( QListWidgetItem * , QListWidgetItem * )),
00075 SLOT( slotCurrentChanged() ));
00076 connect( this, SIGNAL( itemClicked( QListWidgetItem * )),
00077 SLOT( slotItemClicked( QListWidgetItem * )) );
00078 }
00079
00080 KCompletionBox::~KCompletionBox()
00081 {
00082 d->m_parent = 0L;
00083 delete d;
00084 }
00085
00086 QStringList KCompletionBox::items() const
00087 {
00088 QStringList list;
00089
00090 for (int i = 0 ; i < count() ; i++)
00091 {
00092 const QListWidgetItem* currItem = item(i);
00093
00094 list.append(currItem->text());
00095 }
00096
00097 return list;
00098 }
00099
00100 void KCompletionBox::slotActivated( QListWidgetItem *item )
00101 {
00102 if ( !item )
00103 return;
00104
00105 hide();
00106 emit activated( item->text() );
00107 }
00108
00109 bool KCompletionBox::eventFilter( QObject *o, QEvent *e )
00110 {
00111 int type = e->type();
00112 QWidget *wid = qobject_cast<QWidget*>(o);
00113
00114 if (o == this) {
00115 return false;
00116 }
00117
00118 if (wid && wid == d->m_parent &&
00119 (type == QEvent::Move || type == QEvent::Resize)) {
00120 hide();
00121 return false;
00122 }
00123
00124 if (wid && (wid->windowFlags() & Qt::Window) &&
00125 type == QEvent::Move && wid == d->m_parent->window()) {
00126 hide();
00127 return false;
00128 }
00129
00130 if (type == QEvent::MouseButtonPress && (wid && !isAncestorOf(wid))) {
00131 if (!d->emitSelected && currentItem() && !qobject_cast<QScrollBar*>(o)) {
00132 Q_ASSERT(currentItem());
00133 emit currentTextChanged(currentItem()->text() );
00134 }
00135 hide();
00136 e->accept();
00137 return true;
00138 }
00139
00140 if (wid && wid->isAncestorOf(d->m_parent) && isVisible()) {
00141 if ( type == QEvent::KeyPress ) {
00142 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00143 switch ( ev->key() ) {
00144 case Qt::Key_Backtab:
00145 if ( d->tabHandling && (ev->modifiers() == Qt::NoButton ||
00146 (ev->modifiers() & Qt::ShiftModifier)) ) {
00147 up();
00148 ev->accept();
00149 return true;
00150 }
00151 break;
00152 case Qt::Key_Tab:
00153 if ( d->tabHandling && (ev->modifiers() == Qt::NoButton) ) {
00154 down();
00155
00156
00157 if (count() == 1) {
00158 KLineEdit* parent = qobject_cast<KLineEdit*>(d->m_parent);
00159 if (parent) {
00160 parent->doCompletion(currentItem()->text());
00161 } else {
00162 hide();
00163 }
00164 }
00165 ev->accept();
00166 return true;
00167 }
00168 break;
00169 case Qt::Key_Down:
00170 down();
00171 ev->accept();
00172 return true;
00173 case Qt::Key_Up:
00174
00175
00176 if ( !selectedItems().isEmpty() ||
00177 mapToGlobal( QPoint( 0, 0 ) ).y() >
00178 d->m_parent->mapToGlobal( QPoint( 0, 0 ) ).y() )
00179 up();
00180 else
00181 down();
00182 ev->accept();
00183 return true;
00184 case Qt::Key_PageUp:
00185 pageUp();
00186 ev->accept();
00187 return true;
00188 case Qt::Key_PageDown:
00189 pageDown();
00190 ev->accept();
00191 return true;
00192 case Qt::Key_Escape:
00193 canceled();
00194 ev->accept();
00195 return true;
00196 case Qt::Key_Enter:
00197 case Qt::Key_Return:
00198 if ( ev->modifiers() & Qt::ShiftModifier ) {
00199 hide();
00200 ev->accept();
00201 return true;
00202 }
00203 break;
00204 case Qt::Key_End:
00205 if ( ev->modifiers() & Qt::ControlModifier )
00206 {
00207 end();
00208 ev->accept();
00209 return true;
00210 }
00211 break;
00212 case Qt::Key_Home:
00213 if ( ev->modifiers() & Qt::ControlModifier )
00214 {
00215 home();
00216 ev->accept();
00217 return true;
00218 }
00219 default:
00220 break;
00221 }
00222 } else if ( type == QEvent::ShortcutOverride ) {
00223
00224
00225 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00226 switch ( ev->key() ) {
00227 case Qt::Key_Down:
00228 case Qt::Key_Up:
00229 case Qt::Key_PageUp:
00230 case Qt::Key_PageDown:
00231 case Qt::Key_Escape:
00232 case Qt::Key_Enter:
00233 case Qt::Key_Return:
00234 ev->accept();
00235 return true;
00236 break;
00237 case Qt::Key_Tab:
00238 case Qt::Key_Backtab:
00239 if ( ev->modifiers() == Qt::NoButton ||
00240 (ev->modifiers() & Qt::ShiftModifier))
00241 {
00242 ev->accept();
00243 return true;
00244 }
00245 break;
00246 case Qt::Key_Home:
00247 case Qt::Key_End:
00248 if ( ev->modifiers() & Qt::ControlModifier )
00249 {
00250 ev->accept();
00251 return true;
00252 }
00253 break;
00254 default:
00255 break;
00256 }
00257 } else if ( type == QEvent::FocusOut ) {
00258 QFocusEvent* event = static_cast<QFocusEvent*>( e );
00259 if (event->reason() != Qt::PopupFocusReason
00260 #ifdef Q_WS_WIN
00261 && (event->reason() != Qt::ActiveWindowFocusReason || QApplication::activeWindow() != this)
00262 #endif
00263 )
00264 hide();
00265 }
00266 }
00267
00268 return KListWidget::eventFilter( o, e );
00269 }
00270
00271 void KCompletionBox::popup()
00272 {
00273 if ( count() == 0 )
00274 hide();
00275 else {
00276
00277 bool block = signalsBlocked();
00278 blockSignals( true );
00279 setCurrentItem( 0 );
00280 blockSignals( block );
00281 clearSelection();
00282 if ( !isVisible() )
00283 show();
00284 else if ( size().height() != sizeHint().height() )
00285 sizeAndPosition();
00286 }
00287 }
00288
00289 void KCompletionBox::sizeAndPosition()
00290 {
00291 int currentGeom = height();
00292 QPoint currentPos = pos();
00293 QRect geom = calculateGeometry();
00294 resize( geom.size() );
00295
00296 int x = currentPos.x(), y = currentPos.y();
00297 if ( d->m_parent ) {
00298 if ( !isVisible() ) {
00299 QRect screenSize = KGlobalSettings::desktopGeometry(d->m_parent);
00300
00301 QPoint orig = globalPositionHint();
00302 x = orig.x() + geom.x();
00303 y = orig.y() + geom.y();
00304
00305 if ( x + width() > screenSize.right() )
00306 x = screenSize.right() - width();
00307 if (y + height() > screenSize.bottom() ) {
00308 y = y - height() - d->m_parent->height();
00309 d->upwardBox = true;
00310 }
00311 }
00312 else {
00313
00314 if (d->upwardBox)
00315 y += (currentGeom-height());
00316 }
00317 move( x, y);
00318 }
00319 }
00320
00321 QPoint KCompletionBox::globalPositionHint() const
00322 {
00323 if (!d->m_parent)
00324 return QPoint();
00325 return d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
00326 }
00327
00328 void KCompletionBox::setVisible( bool visible )
00329 {
00330 if (visible) {
00331 d->upwardBox = false;
00332 if ( d->m_parent ) {
00333 sizeAndPosition();
00334 qApp->installEventFilter( this );
00335 }
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349 qApp->sendPostedEvents();
00350 } else {
00351 if ( d->m_parent )
00352 qApp->removeEventFilter( this );
00353 d->cancelText.clear();
00354 }
00355
00356 KListWidget::setVisible(visible);
00357 }
00358
00359 QRect KCompletionBox::calculateGeometry() const
00360 {
00361 QRect visualRect;
00362 if (count() == 0 || !(visualRect = visualItemRect(item(0))).isValid())
00363 return QRect();
00364
00365 int x = 0, y = 0;
00366 int ih = visualRect.height();
00367 int h = qMin( 15 * ih, (int) count() * ih ) + 2*frameWidth();
00368
00369 int w = (d->m_parent) ? d->m_parent->width() : KListWidget::minimumSizeHint().width();
00370 w = qMax( KListWidget::minimumSizeHint().width(), w );
00371
00372
00373 #if 0
00374
00375
00376
00377 const QObject* combo;
00378 if ( d->m_parent && (combo = d->m_parent->parent() ) &&
00379 qobject_cast<QComboBox*>(combo) )
00380 {
00381 const QComboBox* cb = static_cast<const QComboBox*>(combo);
00382
00383
00384 w = qMax( w, cb->width() );
00385
00386 QPoint parentCorner = d->m_parent->mapToGlobal(QPoint(0, 0));
00387 QPoint comboCorner = cb->mapToGlobal(QPoint(0, 0));
00388
00389
00390 x += comboCorner.x() - parentCorner.x();
00391
00392
00393 y += cb->height() - d->m_parent->height() +
00394 comboCorner.y() - parentCorner.y();
00395
00396
00397 QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox,
00398 cb, QStyle::SC_ComboBoxListBoxPopup,
00399 QStyleOption(x, y, w, h));
00400
00401
00402 if (!styleAdj.isNull())
00403 return styleAdj;
00404
00405 }
00406 #endif
00407 return QRect(x, y, w, h);
00408 }
00409
00410 QSize KCompletionBox::sizeHint() const
00411 {
00412 return calculateGeometry().size();
00413 }
00414
00415 void KCompletionBox::down()
00416 {
00417 int i = currentRow();
00418
00419 if ( i == 0 && d->down_workaround ) {
00420 d->down_workaround = false;
00421 setCurrentRow( 0 );
00422 item(0)->setSelected(true);
00423 emit currentTextChanged( currentItem()->text() );
00424 }
00425
00426 else if ( i < (int) count() - 1 )
00427 setCurrentRow( i + 1 );
00428 }
00429
00430 void KCompletionBox::up()
00431 {
00432 if ( currentItem() && row(currentItem()) > 0 )
00433 setCurrentItem( item(row(currentItem()) - 1) );
00434 }
00435
00436 void KCompletionBox::pageDown()
00437 {
00438
00439
00440
00441 moveCursor(QAbstractItemView::MovePageDown , Qt::NoModifier);
00442 }
00443
00444 void KCompletionBox::pageUp()
00445 {
00446
00447
00448
00449
00450 moveCursor(QAbstractItemView::MovePageUp , Qt::NoModifier);
00451 }
00452
00453 void KCompletionBox::home()
00454 {
00455 setCurrentItem( 0 );
00456 }
00457
00458 void KCompletionBox::end()
00459 {
00460 setCurrentRow( count() -1 );
00461 }
00462
00463 void KCompletionBox::setTabHandling( bool enable )
00464 {
00465 d->tabHandling = enable;
00466 }
00467
00468 bool KCompletionBox::isTabHandling() const
00469 {
00470 return d->tabHandling;
00471 }
00472
00473 void KCompletionBox::setCancelledText( const QString& text )
00474 {
00475 d->cancelText = text;
00476 }
00477
00478 QString KCompletionBox::cancelledText() const
00479 {
00480 return d->cancelText;
00481 }
00482
00483 void KCompletionBox::canceled()
00484 {
00485 if ( !d->cancelText.isNull() )
00486 emit userCancelled( d->cancelText );
00487 if ( isVisible() )
00488 hide();
00489 }
00490
00491 class KCompletionBoxItem : public QListWidgetItem
00492 {
00493 public:
00494
00495 bool reuse( const QString& newText )
00496 {
00497 if ( text() == newText )
00498 return false;
00499 setText( newText );
00500 return true;
00501 }
00502 };
00503
00504
00505 void KCompletionBox::insertItems( const QStringList& items, int index )
00506 {
00507 bool block = signalsBlocked();
00508 blockSignals( true );
00509 KListWidget::insertItems( index, items );
00510 blockSignals( block );
00511 d->down_workaround = true;
00512 }
00513
00514 void KCompletionBox::setItems( const QStringList& items )
00515 {
00516 bool block = signalsBlocked();
00517 blockSignals( true );
00518
00519 int rowIndex = 0;
00520
00521 if (!count()) {
00522 addItems(items);
00523 } else {
00524
00525
00526
00527 bool dirty = false;
00528
00529 QStringList::ConstIterator it = items.constBegin();
00530 const QStringList::ConstIterator itEnd = items.constEnd();
00531
00532 for ( ; it != itEnd; ++it) {
00533 if ( rowIndex < count() ) {
00534 const bool changed = ((KCompletionBoxItem*)item(rowIndex))->reuse( *it );
00535 dirty = dirty || changed;
00536 } else {
00537 dirty = true;
00538
00539 addItem(*it);
00540 }
00541 rowIndex++;
00542 }
00543
00544
00545 if (rowIndex < count()) {
00546 dirty = true;
00547 }
00548
00549
00550 for ( ; rowIndex < count() ; ) {
00551 QListWidgetItem* item = takeItem(rowIndex);
00552 Q_ASSERT(item);
00553 delete item;
00554 }
00555
00556
00557
00558
00559 }
00560
00561 if (isVisible() && size().height() != sizeHint().height())
00562 sizeAndPosition();
00563
00564 blockSignals(block);
00565 d->down_workaround = true;
00566 }
00567
00568 void KCompletionBox::slotCurrentChanged()
00569 {
00570 if (currentItem())
00571 emit currentTextChanged(currentItem()->text());
00572 d->down_workaround = false;
00573 }
00574
00575 void KCompletionBox::slotItemClicked( QListWidgetItem *item )
00576 {
00577 if ( item )
00578 {
00579 if ( d->down_workaround ) {
00580 d->down_workaround = false;
00581 emit currentTextChanged( item->text() );
00582 }
00583
00584 hide();
00585 emit currentTextChanged( item->text() );
00586 emit activated( item->text() );
00587 }
00588 }
00589
00590 void KCompletionBox::setActivateOnSelect(bool state)
00591 {
00592 d->emitSelected = state;
00593 }
00594
00595 bool KCompletionBox::activateOnSelect() const
00596 {
00597 return d->emitSelected;
00598 }
00599
00600 #include "kcompletionbox.moc"