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