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
00026 #include <QtCore/QEvent>
00027 #include <QtGui/QApplication>
00028 #include <QtGui/QComboBox>
00029 #include <QtGui/QStyle>
00030 #include <QtGui/QScrollBar>
00031 #include <QtGui/QKeyEvent>
00032
00033 #include <kdebug.h>
00034 #include <kconfig.h>
00035 #include <kglobalsettings.h>
00036
00037 class KCompletionBox::KCompletionBoxPrivate
00038 {
00039 public:
00040 QWidget *m_parent;
00041 QString cancelText;
00042 bool tabHandling : 1;
00043 bool down_workaround : 1;
00044 bool upwardBox : 1;
00045 bool emitSelected : 1;
00046 };
00047
00048 KCompletionBox::KCompletionBox( QWidget *parent )
00049 :KListWidget( parent), d(new KCompletionBoxPrivate)
00050 {
00051 d->m_parent = parent;
00052 d->tabHandling = true;
00053 d->down_workaround = false;
00054 d->upwardBox = false;
00055 d->emitSelected = true;
00056
00057 setWindowFlags(Qt::Popup);
00058
00059 setLineWidth( 1 );
00060 setFrameStyle( QFrame::Box | QFrame::Plain );
00061
00062 if ( parent ) {
00063 setFocusProxy( parent );
00064 } else
00065 setFocusPolicy( Qt::NoFocus );
00066
00067 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
00068 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00069
00070 connect( this, SIGNAL( itemDoubleClicked( QListWidgetItem * )),
00071 SLOT( slotActivated( QListWidgetItem * )) );
00072
00073 #ifdef __GNUC__
00074 #warning "Check if this workaround can be removed in KDE 4"
00075 #endif
00076
00077
00078 connect( this, SIGNAL( currentItemChanged( QListWidgetItem * , QListWidgetItem * )),
00079 SLOT( slotCurrentChanged() ));
00080 connect( this, SIGNAL( itemClicked( QListWidgetItem * )),
00081 SLOT( slotItemClicked( QListWidgetItem * )) );
00082 }
00083
00084 KCompletionBox::~KCompletionBox()
00085 {
00086 d->m_parent = 0L;
00087 delete d;
00088 }
00089
00090 QStringList KCompletionBox::items() const
00091 {
00092 QStringList list;
00093
00094 for (int i = 0 ; i < count() ; i++)
00095 {
00096 const QListWidgetItem* currItem = item(i);
00097
00098 list.append(currItem->text());
00099 }
00100
00101 return list;
00102 }
00103
00104 void KCompletionBox::slotActivated( QListWidgetItem *item )
00105 {
00106 if ( !item )
00107 return;
00108
00109 hide();
00110 emit activated( item->text() );
00111 }
00112
00113 bool KCompletionBox::eventFilter( QObject *o, QEvent *e )
00114 {
00115 int type = e->type();
00116
00117 if ((o==this) && (type==QEvent::KeyPress)) {
00118
00119 QApplication::sendEvent(d->m_parent,e);
00120 return true;
00121 }
00122
00123 if ( o == d->m_parent ) {
00124 if ( isVisible() ) {
00125 if ( type == QEvent::KeyPress ) {
00126 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00127 switch ( ev->key() ) {
00128 case Qt::Key_Backtab:
00129 if ( d->tabHandling && (ev->modifiers() == Qt::NoButton ||
00130 (ev->modifiers() & Qt::ShiftModifier)) ) {
00131 up();
00132 ev->accept();
00133 return true;
00134 }
00135 break;
00136 case Qt::Key_Tab:
00137 if ( d->tabHandling && (ev->modifiers() == Qt::NoButton) ) {
00138 down();
00139 ev->accept();
00140 return true;
00141 }
00142 break;
00143 case Qt::Key_Down:
00144 down();
00145 ev->accept();
00146 return true;
00147 case Qt::Key_Up:
00148
00149
00150 if ( !selectedItems().isEmpty() ||
00151 mapToGlobal( QPoint( 0, 0 ) ).y() >
00152 d->m_parent->mapToGlobal( QPoint( 0, 0 ) ).y() )
00153 up();
00154 else
00155 down();
00156 ev->accept();
00157 return true;
00158 case Qt::Key_PageUp:
00159 pageUp();
00160 ev->accept();
00161 return true;
00162 case Qt::Key_PageDown:
00163 pageDown();
00164 ev->accept();
00165 return true;
00166 case Qt::Key_Escape:
00167 canceled();
00168 ev->accept();
00169 return true;
00170 case Qt::Key_Enter:
00171 case Qt::Key_Return:
00172 if ( ev->modifiers() & Qt::ShiftModifier ) {
00173 hide();
00174 ev->accept();
00175 return true;
00176 }
00177 break;
00178 case Qt::Key_End:
00179 if ( ev->modifiers() & Qt::ControlModifier )
00180 {
00181 end();
00182 ev->accept();
00183 return true;
00184 }
00185 case Qt::Key_Home:
00186 if ( ev->modifiers() & Qt::ControlModifier )
00187 {
00188 home();
00189 ev->accept();
00190 return true;
00191 }
00192 default:
00193 break;
00194 }
00195 }
00196 else if ( type == QEvent::ShortcutOverride ) {
00197
00198
00199 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00200 switch ( ev->key() ) {
00201 case Qt::Key_Down:
00202 case Qt::Key_Up:
00203 case Qt::Key_PageUp:
00204 case Qt::Key_PageDown:
00205 case Qt::Key_Escape:
00206 case Qt::Key_Enter:
00207 case Qt::Key_Return:
00208 ev->accept();
00209 return true;
00210 break;
00211 case Qt::Key_Tab:
00212 case Qt::Key_Backtab:
00213 if ( ev->modifiers() == Qt::NoButton ||
00214 (ev->modifiers() & Qt::ShiftModifier))
00215 {
00216 ev->accept();
00217 return true;
00218 }
00219 break;
00220 case Qt::Key_Home:
00221 case Qt::Key_End:
00222 if ( ev->modifiers() & Qt::ControlModifier )
00223 {
00224 ev->accept();
00225 return true;
00226 }
00227 break;
00228 default:
00229 break;
00230 }
00231 }
00232
00233
00234 else if ( type == QEvent::Resize ||
00235 type == QEvent::Close || type == QEvent::Hide ) {
00236 hide();
00237 }
00238
00239 else if ( type == QEvent::FocusOut ) {
00240 QFocusEvent* event = static_cast<QFocusEvent*>( e );
00241 if (event->reason() != Qt::PopupFocusReason)
00242 hide();
00243 }
00244
00245 }
00246 }
00247
00248
00249 else if ( type == QEvent::MouseButtonPress ) {
00250 QMouseEvent *ev = static_cast<QMouseEvent *>( e );
00251 if ( !rect().contains( ev->pos() ))
00252 hide();
00253
00254 if ( !d->emitSelected && currentItem() && !qobject_cast<QScrollBar*>(o) )
00255 {
00256 Q_ASSERT( currentItem() );
00257
00258 emit currentTextChanged( currentItem()->text() );
00259 hide();
00260 ev->accept();
00261 return true;
00262 }
00263 }
00264
00265 return KListWidget::eventFilter( o, e );
00266 }
00267
00268
00269 void KCompletionBox::popup()
00270 {
00271 if ( count() == 0 )
00272 hide();
00273 else {
00274
00275 bool block = signalsBlocked();
00276 blockSignals( true );
00277 setCurrentItem( 0 );
00278 blockSignals( block );
00279 clearSelection();
00280 if ( !isVisible() )
00281 show();
00282 else if ( size().height() != sizeHint().height() )
00283 sizeAndPosition();
00284 }
00285 }
00286
00287 void KCompletionBox::sizeAndPosition()
00288 {
00289 int currentGeom = height();
00290 QPoint currentPos = pos();
00291 QRect geom = calculateGeometry();
00292 resize( geom.size() );
00293
00294 int x = currentPos.x(), y = currentPos.y();
00295 if ( d->m_parent ) {
00296 if ( !isVisible() ) {
00297 QRect screenSize = KGlobalSettings::desktopGeometry(d->m_parent);
00298
00299 QPoint orig = globalPositionHint();
00300 x = orig.x() + geom.x();
00301 y = orig.y() + geom.y();
00302
00303 if ( x + width() > screenSize.right() )
00304 x = screenSize.right() - width();
00305 if (y + height() > screenSize.bottom() ) {
00306 y = y - height() - d->m_parent->height();
00307 d->upwardBox = true;
00308 }
00309 }
00310 else {
00311
00312 if (d->upwardBox)
00313 y += (currentGeom-height());
00314 }
00315 move( x, y);
00316 }
00317 }
00318
00319 QPoint KCompletionBox::globalPositionHint() const
00320 {
00321 if (!d->m_parent)
00322 return QPoint();
00323 return d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
00324 }
00325
00326 void KCompletionBox::setVisible( bool visible )
00327 {
00328 if (visible) {
00329 d->upwardBox = false;
00330 if ( d->m_parent ) {
00331 sizeAndPosition();
00332 qApp->installEventFilter( this );
00333 }
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347 qApp->sendPostedEvents();
00348 } else {
00349 if ( d->m_parent )
00350 qApp->removeEventFilter( this );
00351 d->cancelText.clear();
00352 }
00353
00354 KListWidget::setVisible(visible);
00355 }
00356
00357 QRect KCompletionBox::calculateGeometry() const
00358 {
00359 if (count() == 0)
00360 return QRect();
00361
00362 int x = 0, y = 0;
00363
00364 Q_ASSERT( visualItemRect(item(0)).isValid() );
00365
00366 int ih = visualItemRect(item(0)).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 ( rowIndex >= count() ) {
00522 addItems( items );
00523 }
00524 else {
00525
00526
00527
00528 bool dirty = false;
00529
00530 QStringList::ConstIterator it = items.constBegin();
00531 const QStringList::ConstIterator itEnd = items.constEnd();
00532
00533 for ( ; it != itEnd; ++it) {
00534 if ( rowIndex < count() ) {
00535 const bool changed = ((KCompletionBoxItem*)item(rowIndex))->reuse( *it );
00536 dirty = dirty || changed;
00537
00538 rowIndex++;
00539 }
00540 else {
00541 dirty = true;
00542
00543 addItem( *it );
00544 }
00545 }
00546
00547
00548 if ( rowIndex < count() ) {
00549 dirty = true;
00550 }
00551
00552
00553 for ( ; rowIndex < count() ; ) {
00554 QListWidgetItem* item = takeItem(rowIndex);
00555
00556 Q_ASSERT(item);
00557
00558 delete item;
00559 }
00560
00561
00562
00563
00564 }
00565
00566 if ( isVisible() && size().height() != sizeHint().height() )
00567 sizeAndPosition();
00568
00569 blockSignals( block );
00570 d->down_workaround = true;
00571 }
00572
00573 void KCompletionBox::slotSetCurrentItem( QListWidgetItem *i )
00574 {
00575 setCurrentItem( i );
00576 }
00577
00578 void KCompletionBox::slotCurrentChanged()
00579 {
00580 if (currentItem())
00581 emit currentTextChanged(currentItem()->text());
00582 d->down_workaround = false;
00583 }
00584
00585 void KCompletionBox::slotItemClicked( QListWidgetItem *item )
00586 {
00587 if ( item )
00588 {
00589 if ( d->down_workaround ) {
00590 d->down_workaround = false;
00591 emit currentTextChanged( item->text() );
00592 }
00593
00594 hide();
00595 emit currentTextChanged( item->text() );
00596 emit activated( item->text() );
00597 }
00598 }
00599
00600 void KCompletionBox::setActivateOnSelect(bool state)
00601 {
00602 d->emitSelected = state;
00603 }
00604
00605 bool KCompletionBox::activateOnSelect() const
00606 {
00607 return d->emitSelected;
00608 }
00609
00610 #include "kcompletionbox.moc"