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 if ( parentWidget() ) {
00288 QFocusEvent focusEvent( QEvent::FocusIn );
00289 QApplication::sendEvent( parentWidget(), &focusEvent );
00290 }
00291 }
00292
00293 void KCompletionBox::sizeAndPosition()
00294 {
00295 int currentGeom = height();
00296 QPoint currentPos = pos();
00297 QRect geom = calculateGeometry();
00298 resize( geom.size() );
00299
00300 int x = currentPos.x(), y = currentPos.y();
00301 if ( d->m_parent ) {
00302 if ( !isVisible() ) {
00303 QRect screenSize = KGlobalSettings::desktopGeometry(d->m_parent);
00304
00305 QPoint orig = globalPositionHint();
00306 x = orig.x() + geom.x();
00307 y = orig.y() + geom.y();
00308
00309 if ( x + width() > screenSize.right() )
00310 x = screenSize.right() - width();
00311 if (y + height() > screenSize.bottom() ) {
00312 y = y - height() - d->m_parent->height();
00313 d->upwardBox = true;
00314 }
00315 }
00316 else {
00317
00318 if (d->upwardBox)
00319 y += (currentGeom-height());
00320 }
00321 move( x, y);
00322 }
00323 }
00324
00325 QPoint KCompletionBox::globalPositionHint() const
00326 {
00327 if (!d->m_parent)
00328 return QPoint();
00329 return d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
00330 }
00331
00332 void KCompletionBox::setVisible( bool visible )
00333 {
00334 if (visible) {
00335 d->upwardBox = false;
00336 if ( d->m_parent ) {
00337 sizeAndPosition();
00338 qApp->installEventFilter( this );
00339 }
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353 qApp->sendPostedEvents();
00354 } else {
00355 if ( d->m_parent )
00356 qApp->removeEventFilter( this );
00357 d->cancelText.clear();
00358 }
00359
00360 KListWidget::setVisible(visible);
00361 }
00362
00363 QRect KCompletionBox::calculateGeometry() const
00364 {
00365 if (count() == 0)
00366 return QRect();
00367
00368 int x = 0, y = 0;
00369
00370 Q_ASSERT( visualItemRect(item(0)).isValid() );
00371
00372 int ih = visualItemRect(item(0)).height();
00373 int h = qMin( 15 * ih, (int) count() * ih ) + 2*frameWidth();
00374
00375 int w = (d->m_parent) ? d->m_parent->width() : KListWidget::minimumSizeHint().width();
00376 w = qMax( KListWidget::minimumSizeHint().width(), w );
00377
00378
00379 #if 0
00380
00381
00382
00383 const QObject* combo;
00384 if ( d->m_parent && (combo = d->m_parent->parent() ) &&
00385 qobject_cast<QComboBox*>(combo) )
00386 {
00387 const QComboBox* cb = static_cast<const QComboBox*>(combo);
00388
00389
00390 w = qMax( w, cb->width() );
00391
00392 QPoint parentCorner = d->m_parent->mapToGlobal(QPoint(0, 0));
00393 QPoint comboCorner = cb->mapToGlobal(QPoint(0, 0));
00394
00395
00396 x += comboCorner.x() - parentCorner.x();
00397
00398
00399 y += cb->height() - d->m_parent->height() +
00400 comboCorner.y() - parentCorner.y();
00401
00402
00403 QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox,
00404 cb, QStyle::SC_ComboBoxListBoxPopup,
00405 QStyleOption(x, y, w, h));
00406
00407
00408 if (!styleAdj.isNull())
00409 return styleAdj;
00410
00411 }
00412 #endif
00413 return QRect(x, y, w, h);
00414 }
00415
00416 QSize KCompletionBox::sizeHint() const
00417 {
00418 return calculateGeometry().size();
00419 }
00420
00421 void KCompletionBox::down()
00422 {
00423 int i = currentRow();
00424
00425 if ( i == 0 && d->down_workaround ) {
00426 d->down_workaround = false;
00427 setCurrentRow( 0 );
00428 item(0)->setSelected(true);
00429 emit currentTextChanged( currentItem()->text() );
00430 }
00431
00432 else if ( i < (int) count() - 1 )
00433 setCurrentRow( i + 1 );
00434 }
00435
00436 void KCompletionBox::up()
00437 {
00438 if ( currentItem() && row(currentItem()) > 0 )
00439 setCurrentItem( item(row(currentItem()) - 1) );
00440 }
00441
00442 void KCompletionBox::pageDown()
00443 {
00444
00445
00446
00447 moveCursor(QAbstractItemView::MovePageDown , Qt::NoModifier);
00448 }
00449
00450 void KCompletionBox::pageUp()
00451 {
00452
00453
00454
00455
00456 moveCursor(QAbstractItemView::MovePageUp , Qt::NoModifier);
00457 }
00458
00459 void KCompletionBox::home()
00460 {
00461 setCurrentItem( 0 );
00462 }
00463
00464 void KCompletionBox::end()
00465 {
00466 setCurrentRow( count() -1 );
00467 }
00468
00469 void KCompletionBox::setTabHandling( bool enable )
00470 {
00471 d->tabHandling = enable;
00472 }
00473
00474 bool KCompletionBox::isTabHandling() const
00475 {
00476 return d->tabHandling;
00477 }
00478
00479 void KCompletionBox::setCancelledText( const QString& text )
00480 {
00481 d->cancelText = text;
00482 }
00483
00484 QString KCompletionBox::cancelledText() const
00485 {
00486 return d->cancelText;
00487 }
00488
00489 void KCompletionBox::canceled()
00490 {
00491 if ( !d->cancelText.isNull() )
00492 emit userCancelled( d->cancelText );
00493 if ( isVisible() )
00494 hide();
00495 }
00496
00497 class KCompletionBoxItem : public QListWidgetItem
00498 {
00499 public:
00500
00501 bool reuse( const QString& newText )
00502 {
00503 if ( text() == newText )
00504 return false;
00505 setText( newText );
00506 return true;
00507 }
00508 };
00509
00510
00511 void KCompletionBox::insertItems( const QStringList& items, int index )
00512 {
00513 bool block = signalsBlocked();
00514 blockSignals( true );
00515 KListWidget::insertItems( index, items );
00516 blockSignals( block );
00517 d->down_workaround = true;
00518 }
00519
00520 void KCompletionBox::setItems( const QStringList& items )
00521 {
00522 bool block = signalsBlocked();
00523 blockSignals( true );
00524
00525 int rowIndex = 0;
00526
00527 if ( rowIndex >= count() ) {
00528 addItems( items );
00529 }
00530 else {
00531
00532
00533
00534 bool dirty = false;
00535
00536 QStringList::ConstIterator it = items.constBegin();
00537 const QStringList::ConstIterator itEnd = items.constEnd();
00538
00539 for ( ; it != itEnd; ++it) {
00540 if ( rowIndex < count() ) {
00541 const bool changed = ((KCompletionBoxItem*)item(rowIndex))->reuse( *it );
00542 dirty = dirty || changed;
00543
00544 rowIndex++;
00545 }
00546 else {
00547 dirty = true;
00548
00549 addItem( *it );
00550 }
00551 }
00552
00553
00554 if ( rowIndex < count() ) {
00555 dirty = true;
00556 }
00557
00558
00559 for ( ; rowIndex < count() ; ) {
00560 QListWidgetItem* item = takeItem(rowIndex);
00561
00562 Q_ASSERT(item);
00563
00564 delete item;
00565 }
00566
00567
00568
00569
00570 }
00571
00572 if ( isVisible() && size().height() != sizeHint().height() )
00573 sizeAndPosition();
00574
00575 blockSignals( block );
00576 d->down_workaround = true;
00577 }
00578
00579 void KCompletionBox::slotSetCurrentItem( QListWidgetItem *i )
00580 {
00581 setCurrentItem( i );
00582 }
00583
00584 void KCompletionBox::slotCurrentChanged()
00585 {
00586 if (currentItem())
00587 emit currentTextChanged(currentItem()->text());
00588 d->down_workaround = false;
00589 }
00590
00591 void KCompletionBox::slotItemClicked( QListWidgetItem *item )
00592 {
00593 if ( item )
00594 {
00595 if ( d->down_workaround ) {
00596 d->down_workaround = false;
00597 emit currentTextChanged( item->text() );
00598 }
00599
00600 hide();
00601 emit currentTextChanged( item->text() );
00602 emit activated( item->text() );
00603 }
00604 }
00605
00606 void KCompletionBox::setActivateOnSelect(bool state)
00607 {
00608 d->emitSelected = state;
00609 }
00610
00611 bool KCompletionBox::activateOnSelect() const
00612 {
00613 return d->emitSelected;
00614 }
00615
00616 #include "kcompletionbox.moc"