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 || wid->windowFlags() & Qt::Window) &&
00119 (type == QEvent::Move || type == QEvent::Resize)) {
00120 hide();
00121 return false;
00122 }
00123
00124 if (type == QEvent::MouseButtonPress && (wid && !isAncestorOf(wid))) {
00125 if (!d->emitSelected && currentItem() && !qobject_cast<QScrollBar*>(o)) {
00126 Q_ASSERT(currentItem());
00127 emit currentTextChanged(currentItem()->text() );
00128 }
00129 hide();
00130 e->accept();
00131 return true;
00132 }
00133
00134 if (wid && wid->isAncestorOf(d->m_parent) && isVisible()) {
00135 if ( type == QEvent::KeyPress ) {
00136 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00137 switch ( ev->key() ) {
00138 case Qt::Key_Backtab:
00139 if ( d->tabHandling && (ev->modifiers() == Qt::NoButton ||
00140 (ev->modifiers() & Qt::ShiftModifier)) ) {
00141 up();
00142 ev->accept();
00143 return true;
00144 }
00145 break;
00146 case Qt::Key_Tab:
00147 if ( d->tabHandling && (ev->modifiers() == Qt::NoButton) ) {
00148 down();
00149
00150
00151 if (count() == 1) {
00152 KLineEdit* parent = qobject_cast<KLineEdit*>(d->m_parent);
00153 if (parent) {
00154 parent->doCompletion(currentItem()->text());
00155 } else {
00156 hide();
00157 }
00158 }
00159 ev->accept();
00160 return true;
00161 }
00162 break;
00163 case Qt::Key_Down:
00164 down();
00165 ev->accept();
00166 return true;
00167 case Qt::Key_Up:
00168
00169
00170 if ( !selectedItems().isEmpty() ||
00171 mapToGlobal( QPoint( 0, 0 ) ).y() >
00172 d->m_parent->mapToGlobal( QPoint( 0, 0 ) ).y() )
00173 up();
00174 else
00175 down();
00176 ev->accept();
00177 return true;
00178 case Qt::Key_PageUp:
00179 pageUp();
00180 ev->accept();
00181 return true;
00182 case Qt::Key_PageDown:
00183 pageDown();
00184 ev->accept();
00185 return true;
00186 case Qt::Key_Escape:
00187 canceled();
00188 ev->accept();
00189 return true;
00190 case Qt::Key_Enter:
00191 case Qt::Key_Return:
00192 if ( ev->modifiers() & Qt::ShiftModifier ) {
00193 hide();
00194 ev->accept();
00195 return true;
00196 }
00197 break;
00198 case Qt::Key_End:
00199 if ( ev->modifiers() & Qt::ControlModifier )
00200 {
00201 end();
00202 ev->accept();
00203 return true;
00204 }
00205 break;
00206 case Qt::Key_Home:
00207 if ( ev->modifiers() & Qt::ControlModifier )
00208 {
00209 home();
00210 ev->accept();
00211 return true;
00212 }
00213 default:
00214 break;
00215 }
00216 } else if ( type == QEvent::ShortcutOverride ) {
00217
00218
00219 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00220 switch ( ev->key() ) {
00221 case Qt::Key_Down:
00222 case Qt::Key_Up:
00223 case Qt::Key_PageUp:
00224 case Qt::Key_PageDown:
00225 case Qt::Key_Escape:
00226 case Qt::Key_Enter:
00227 case Qt::Key_Return:
00228 ev->accept();
00229 return true;
00230 break;
00231 case Qt::Key_Tab:
00232 case Qt::Key_Backtab:
00233 if ( ev->modifiers() == Qt::NoButton ||
00234 (ev->modifiers() & Qt::ShiftModifier))
00235 {
00236 ev->accept();
00237 return true;
00238 }
00239 break;
00240 case Qt::Key_Home:
00241 case Qt::Key_End:
00242 if ( ev->modifiers() & Qt::ControlModifier )
00243 {
00244 ev->accept();
00245 return true;
00246 }
00247 break;
00248 default:
00249 break;
00250 }
00251 } else if ( type == QEvent::FocusOut ) {
00252 QFocusEvent* event = static_cast<QFocusEvent*>( e );
00253 if (event->reason() != Qt::PopupFocusReason
00254 #ifdef Q_WS_WIN
00255 && (event->reason() != Qt::ActiveWindowFocusReason || QApplication::activeWindow() != this)
00256 #endif
00257 )
00258 hide();
00259 }
00260 }
00261
00262 return KListWidget::eventFilter( o, e );
00263 }
00264
00265 void KCompletionBox::popup()
00266 {
00267 if ( count() == 0 )
00268 hide();
00269 else {
00270
00271 bool block = signalsBlocked();
00272 blockSignals( true );
00273 setCurrentItem( 0 );
00274 blockSignals( block );
00275 clearSelection();
00276 if ( !isVisible() )
00277 show();
00278 else if ( size().height() != sizeHint().height() )
00279 sizeAndPosition();
00280 }
00281 }
00282
00283 void KCompletionBox::sizeAndPosition()
00284 {
00285 int currentGeom = height();
00286 QPoint currentPos = pos();
00287 QRect geom = calculateGeometry();
00288 resize( geom.size() );
00289
00290 int x = currentPos.x(), y = currentPos.y();
00291 if ( d->m_parent ) {
00292 if ( !isVisible() ) {
00293 QRect screenSize = KGlobalSettings::desktopGeometry(d->m_parent);
00294
00295 QPoint orig = globalPositionHint();
00296 x = orig.x() + geom.x();
00297 y = orig.y() + geom.y();
00298
00299 if ( x + width() > screenSize.right() )
00300 x = screenSize.right() - width();
00301 if (y + height() > screenSize.bottom() ) {
00302 y = y - height() - d->m_parent->height();
00303 d->upwardBox = true;
00304 }
00305 }
00306 else {
00307
00308 if (d->upwardBox)
00309 y += (currentGeom-height());
00310 }
00311 move( x, y);
00312 }
00313 }
00314
00315 QPoint KCompletionBox::globalPositionHint() const
00316 {
00317 if (!d->m_parent)
00318 return QPoint();
00319 return d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
00320 }
00321
00322 void KCompletionBox::setVisible( bool visible )
00323 {
00324 if (visible) {
00325 d->upwardBox = false;
00326 if ( d->m_parent ) {
00327 sizeAndPosition();
00328 qApp->installEventFilter( this );
00329 }
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343 qApp->sendPostedEvents();
00344 } else {
00345 if ( d->m_parent )
00346 qApp->removeEventFilter( this );
00347 d->cancelText.clear();
00348 }
00349
00350 KListWidget::setVisible(visible);
00351 }
00352
00353 QRect KCompletionBox::calculateGeometry() const
00354 {
00355 QRect visualRect;
00356 if (count() == 0 || !(visualRect = visualItemRect(item(0))).isValid())
00357 return QRect();
00358
00359 int x = 0, y = 0;
00360 int ih = visualRect.height();
00361 int h = qMin( 15 * ih, (int) count() * ih ) + 2*frameWidth();
00362
00363 int w = (d->m_parent) ? d->m_parent->width() : KListWidget::minimumSizeHint().width();
00364 w = qMax( KListWidget::minimumSizeHint().width(), w );
00365
00366
00367 #if 0
00368
00369
00370
00371 const QObject* combo;
00372 if ( d->m_parent && (combo = d->m_parent->parent() ) &&
00373 qobject_cast<QComboBox*>(combo) )
00374 {
00375 const QComboBox* cb = static_cast<const QComboBox*>(combo);
00376
00377
00378 w = qMax( w, cb->width() );
00379
00380 QPoint parentCorner = d->m_parent->mapToGlobal(QPoint(0, 0));
00381 QPoint comboCorner = cb->mapToGlobal(QPoint(0, 0));
00382
00383
00384 x += comboCorner.x() - parentCorner.x();
00385
00386
00387 y += cb->height() - d->m_parent->height() +
00388 comboCorner.y() - parentCorner.y();
00389
00390
00391 QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox,
00392 cb, QStyle::SC_ComboBoxListBoxPopup,
00393 QStyleOption(x, y, w, h));
00394
00395
00396 if (!styleAdj.isNull())
00397 return styleAdj;
00398
00399 }
00400 #endif
00401 return QRect(x, y, w, h);
00402 }
00403
00404 QSize KCompletionBox::sizeHint() const
00405 {
00406 return calculateGeometry().size();
00407 }
00408
00409 void KCompletionBox::down()
00410 {
00411 int i = currentRow();
00412
00413 if ( i == 0 && d->down_workaround ) {
00414 d->down_workaround = false;
00415 setCurrentRow( 0 );
00416 item(0)->setSelected(true);
00417 emit currentTextChanged( currentItem()->text() );
00418 }
00419
00420 else if ( i < (int) count() - 1 )
00421 setCurrentRow( i + 1 );
00422 }
00423
00424 void KCompletionBox::up()
00425 {
00426 if ( currentItem() && row(currentItem()) > 0 )
00427 setCurrentItem( item(row(currentItem()) - 1) );
00428 }
00429
00430 void KCompletionBox::pageDown()
00431 {
00432
00433
00434
00435 moveCursor(QAbstractItemView::MovePageDown , Qt::NoModifier);
00436 }
00437
00438 void KCompletionBox::pageUp()
00439 {
00440
00441
00442
00443
00444 moveCursor(QAbstractItemView::MovePageUp , Qt::NoModifier);
00445 }
00446
00447 void KCompletionBox::home()
00448 {
00449 setCurrentItem( 0 );
00450 }
00451
00452 void KCompletionBox::end()
00453 {
00454 setCurrentRow( count() -1 );
00455 }
00456
00457 void KCompletionBox::setTabHandling( bool enable )
00458 {
00459 d->tabHandling = enable;
00460 }
00461
00462 bool KCompletionBox::isTabHandling() const
00463 {
00464 return d->tabHandling;
00465 }
00466
00467 void KCompletionBox::setCancelledText( const QString& text )
00468 {
00469 d->cancelText = text;
00470 }
00471
00472 QString KCompletionBox::cancelledText() const
00473 {
00474 return d->cancelText;
00475 }
00476
00477 void KCompletionBox::canceled()
00478 {
00479 if ( !d->cancelText.isNull() )
00480 emit userCancelled( d->cancelText );
00481 if ( isVisible() )
00482 hide();
00483 }
00484
00485 class KCompletionBoxItem : public QListWidgetItem
00486 {
00487 public:
00488
00489 bool reuse( const QString& newText )
00490 {
00491 if ( text() == newText )
00492 return false;
00493 setText( newText );
00494 return true;
00495 }
00496 };
00497
00498
00499 void KCompletionBox::insertItems( const QStringList& items, int index )
00500 {
00501 bool block = signalsBlocked();
00502 blockSignals( true );
00503 KListWidget::insertItems( index, items );
00504 blockSignals( block );
00505 d->down_workaround = true;
00506 }
00507
00508 void KCompletionBox::setItems( const QStringList& items )
00509 {
00510 bool block = signalsBlocked();
00511 blockSignals( true );
00512
00513 int rowIndex = 0;
00514
00515 if (!count()) {
00516 addItems(items);
00517 } else {
00518
00519
00520
00521 bool dirty = false;
00522
00523 QStringList::ConstIterator it = items.constBegin();
00524 const QStringList::ConstIterator itEnd = items.constEnd();
00525
00526 for ( ; it != itEnd; ++it) {
00527 if ( rowIndex < count() ) {
00528 const bool changed = ((KCompletionBoxItem*)item(rowIndex))->reuse( *it );
00529 dirty = dirty || changed;
00530 } else {
00531 dirty = true;
00532
00533 addItem(*it);
00534 }
00535 rowIndex++;
00536 }
00537
00538
00539 if (rowIndex < count()) {
00540 dirty = true;
00541 }
00542
00543
00544 for ( ; rowIndex < count() ; ) {
00545 QListWidgetItem* item = takeItem(rowIndex);
00546 Q_ASSERT(item);
00547 delete item;
00548 }
00549
00550
00551
00552
00553 }
00554
00555 if (isVisible() && size().height() != sizeHint().height())
00556 sizeAndPosition();
00557
00558 blockSignals(block);
00559 d->down_workaround = true;
00560 }
00561
00562 void KCompletionBox::slotCurrentChanged()
00563 {
00564 if (currentItem())
00565 emit currentTextChanged(currentItem()->text());
00566 d->down_workaround = false;
00567 }
00568
00569 void KCompletionBox::slotItemClicked( QListWidgetItem *item )
00570 {
00571 if ( item )
00572 {
00573 if ( d->down_workaround ) {
00574 d->down_workaround = false;
00575 emit currentTextChanged( item->text() );
00576 }
00577
00578 hide();
00579 emit currentTextChanged( item->text() );
00580 emit activated( item->text() );
00581 }
00582 }
00583
00584 void KCompletionBox::setActivateOnSelect(bool state)
00585 {
00586 d->emitSelected = state;
00587 }
00588
00589 bool KCompletionBox::activateOnSelect() const
00590 {
00591 return d->emitSelected;
00592 }
00593
00594 #include "kcompletionbox.moc"