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 <qapplication.h>
00025 #include <qcombobox.h>
00026 #include <qevent.h>
00027 #include <qstyle.h>
00028
00029 #include <kdebug.h>
00030 #include <kconfig.h>
00031 #include <knotifyclient.h>
00032 #include <kglobalsettings.h>
00033
00034 #include "kcompletionbox.h"
00035
00036 class KCompletionBox::KCompletionBoxPrivate
00037 {
00038 public:
00039 QWidget *m_parent;
00040 QString cancelText;
00041 bool tabHandling;
00042 bool down_workaround;
00043 bool upwardBox;
00044 bool emitSelected;
00045 };
00046
00047 KCompletionBox::KCompletionBox( QWidget *parent, const char *name )
00048 :KListBox( parent, name, WType_Popup ), d(new KCompletionBoxPrivate)
00049 {
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 setColumnMode( 1 );
00058 setLineWidth( 1 );
00059 setFrameStyle( QFrame::Box | QFrame::Plain );
00060
00061 if ( parent )
00062 setFocusProxy( parent );
00063 else
00064 setFocusPolicy( NoFocus );
00065
00066 setVScrollBarMode( Auto );
00067 setHScrollBarMode( AlwaysOff );
00068
00069 connect( this, SIGNAL( doubleClicked( QListBoxItem * )),
00070 SLOT( slotActivated( QListBoxItem * )) );
00071
00072
00073 connect( this, SIGNAL( currentChanged( QListBoxItem * )),
00074 SLOT( slotCurrentChanged() ));
00075 connect( this, SIGNAL( clicked( QListBoxItem * )),
00076 SLOT( slotItemClicked( QListBoxItem * )) );
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 const QListBoxItem* currItem = firstItem();
00090
00091 while (currItem) {
00092 list.append(currItem->text());
00093 currItem = currItem->next();
00094 }
00095
00096 return list;
00097 }
00098
00099 void KCompletionBox::slotActivated( QListBoxItem *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
00112 if ( o == d->m_parent ) {
00113 if ( isVisible() ) {
00114 if ( type == QEvent::KeyPress ) {
00115 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00116 switch ( ev->key() ) {
00117 case Key_BackTab:
00118 if ( d->tabHandling && (ev->state() == NoButton ||
00119 (ev->state() & ShiftButton)) ) {
00120 up();
00121 ev->accept();
00122 return true;
00123 }
00124 break;
00125 case Key_Tab:
00126 if ( d->tabHandling && (ev->state() == NoButton) ) {
00127 down();
00128 ev->accept();
00129 return true;
00130 }
00131 break;
00132 case Key_Down:
00133 down();
00134 ev->accept();
00135 return true;
00136 case Key_Up:
00137
00138
00139 if ( selectedItem() ||
00140 mapToGlobal( QPoint( 0, 0 ) ).y() >
00141 d->m_parent->mapToGlobal( QPoint( 0, 0 ) ).y() )
00142 up();
00143 else
00144 down();
00145 ev->accept();
00146 return true;
00147 case Key_Prior:
00148 pageUp();
00149 ev->accept();
00150 return true;
00151 case Key_Next:
00152 pageDown();
00153 ev->accept();
00154 return true;
00155 case Key_Escape:
00156 canceled();
00157 ev->accept();
00158 return true;
00159 case Key_Enter:
00160 case Key_Return:
00161 if ( ev->state() & ShiftButton ) {
00162 hide();
00163 ev->accept();
00164 return true;
00165 }
00166 break;
00167 case Key_End:
00168 if ( ev->state() & ControlButton )
00169 {
00170 end();
00171 ev->accept();
00172 return true;
00173 }
00174 case Key_Home:
00175 if ( ev->state() & ControlButton )
00176 {
00177 home();
00178 ev->accept();
00179 return true;
00180 }
00181 default:
00182 break;
00183 }
00184 }
00185 else if ( type == QEvent::AccelOverride ) {
00186
00187
00188 QKeyEvent *ev = static_cast<QKeyEvent *>( e );
00189 switch ( ev->key() ) {
00190 case Key_Down:
00191 case Key_Up:
00192 case Key_Prior:
00193 case Key_Next:
00194 case Key_Escape:
00195 case Key_Enter:
00196 case Key_Return:
00197 ev->accept();
00198 return true;
00199 break;
00200 case Key_Tab:
00201 case Key_BackTab:
00202 if ( ev->state() == NoButton ||
00203 (ev->state() & ShiftButton))
00204 {
00205 ev->accept();
00206 return true;
00207 }
00208 break;
00209 case Key_Home:
00210 case Key_End:
00211 if ( ev->state() & ControlButton )
00212 {
00213 ev->accept();
00214 return true;
00215 }
00216 break;
00217 default:
00218 break;
00219 }
00220 }
00221
00222
00223 else if ( type == QEvent::FocusOut || type == QEvent::Resize ||
00224 type == QEvent::Close || type == QEvent::Hide ||
00225 type == QEvent::Move ) {
00226 hide();
00227 }
00228 }
00229 }
00230
00231
00232 else if ( type == QEvent::MouseButtonPress ) {
00233 QMouseEvent *ev = static_cast<QMouseEvent *>( e );
00234 if ( !rect().contains( ev->pos() ))
00235 hide();
00236
00237 if ( !d->emitSelected && currentItem() && !::qt_cast<QScrollBar*>(o) )
00238 {
00239 emit highlighted( currentText() );
00240 hide();
00241 ev->accept();
00242 return true;
00243 }
00244 }
00245
00246 return KListBox::eventFilter( o, e );
00247 }
00248
00249
00250 void KCompletionBox::popup()
00251 {
00252 if ( count() == 0 )
00253 hide();
00254 else {
00255 ensureCurrentVisible();
00256 bool block = signalsBlocked();
00257 blockSignals( true );
00258 setCurrentItem( 0 );
00259 blockSignals( block );
00260 clearSelection();
00261 if ( !isVisible() )
00262 show();
00263 else if ( size().height() != sizeHint().height() )
00264 sizeAndPosition();
00265 }
00266 }
00267
00268 void KCompletionBox::sizeAndPosition()
00269 {
00270 int currentGeom = height();
00271 QPoint currentPos = pos();
00272 QRect geom = calculateGeometry();
00273 resize( geom.size() );
00274
00275 int x = currentPos.x(), y = currentPos.y();
00276 if ( d->m_parent ) {
00277 if ( !isVisible() ) {
00278 QRect screenSize = KGlobalSettings::desktopGeometry(d->m_parent);
00279
00280 QPoint orig = d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) );
00281 x = orig.x() + geom.x();
00282 y = orig.y() + geom.y();
00283
00284 if ( x + width() > screenSize.right() )
00285 x = screenSize.right() - width();
00286 if (y + height() > screenSize.bottom() ) {
00287 y = y - height() - d->m_parent->height();
00288 d->upwardBox = true;
00289 }
00290 }
00291 else {
00292
00293 if (d->upwardBox)
00294 y += (currentGeom-height());
00295 }
00296 move( x, y);
00297 }
00298 }
00299
00300 void KCompletionBox::show()
00301 {
00302 d->upwardBox = false;
00303 if ( d->m_parent ) {
00304 sizeAndPosition();
00305 qApp->installEventFilter( this );
00306 }
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320 qApp->sendPostedEvents();
00321 KListBox::show();
00322 }
00323
00324 void KCompletionBox::hide()
00325 {
00326 if ( d->m_parent )
00327 qApp->removeEventFilter( this );
00328 d->cancelText = QString::null;
00329 KListBox::hide();
00330 }
00331
00332 QRect KCompletionBox::calculateGeometry() const
00333 {
00334 int x = 0, y = 0;
00335 int ih = itemHeight();
00336 int h = QMIN( 15 * ih, (int) count() * ih ) + 2*frameWidth();
00337
00338 int w = (d->m_parent) ? d->m_parent->width() : KListBox::minimumSizeHint().width();
00339 w = QMAX( KListBox::minimumSizeHint().width(), w );
00340
00341
00342
00343
00344 const QObject* combo;
00345 if ( d->m_parent && (combo = d->m_parent->parent() ) &&
00346 combo->inherits("QComboBox") )
00347 {
00348 const QComboBox* cb = static_cast<const QComboBox*>(combo);
00349
00350
00351 w = QMAX( w, cb->width() );
00352
00353 QPoint parentCorner = d->m_parent->mapToGlobal(QPoint(0, 0));
00354 QPoint comboCorner = cb->mapToGlobal(QPoint(0, 0));
00355
00356
00357 x += comboCorner.x() - parentCorner.x();
00358
00359
00360 y += cb->height() - d->m_parent->height() +
00361 comboCorner.y() - parentCorner.y();
00362
00363
00364 QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox,
00365 cb, QStyle::SC_ComboBoxListBoxPopup,
00366 QStyleOption(x, y, w, h));
00367
00368
00369 if (!styleAdj.isNull())
00370 return styleAdj;
00371
00372 }
00373 return QRect(x, y, w, h);
00374 }
00375
00376 QSize KCompletionBox::sizeHint() const
00377 {
00378 return calculateGeometry().size();
00379 }
00380
00381 void KCompletionBox::down()
00382 {
00383 int i = currentItem();
00384
00385 if ( i == 0 && d->down_workaround ) {
00386 d->down_workaround = false;
00387 setCurrentItem( 0 );
00388 setSelected( 0, true );
00389 emit highlighted( currentText() );
00390 }
00391
00392 else if ( i < (int) count() - 1 )
00393 setCurrentItem( i + 1 );
00394 }
00395
00396 void KCompletionBox::up()
00397 {
00398 if ( currentItem() > 0 )
00399 setCurrentItem( currentItem() - 1 );
00400 }
00401
00402 void KCompletionBox::pageDown()
00403 {
00404 int i = currentItem() + numItemsVisible();
00405 i = i > (int)count() - 1 ? (int)count() - 1 : i;
00406 setCurrentItem( i );
00407 }
00408
00409 void KCompletionBox::pageUp()
00410 {
00411 int i = currentItem() - numItemsVisible();
00412 i = i < 0 ? 0 : i;
00413 setCurrentItem( i );
00414 }
00415
00416 void KCompletionBox::home()
00417 {
00418 setCurrentItem( 0 );
00419 }
00420
00421 void KCompletionBox::end()
00422 {
00423 setCurrentItem( count() -1 );
00424 }
00425
00426 void KCompletionBox::setTabHandling( bool enable )
00427 {
00428 d->tabHandling = enable;
00429 }
00430
00431 bool KCompletionBox::isTabHandling() const
00432 {
00433 return d->tabHandling;
00434 }
00435
00436 void KCompletionBox::setCancelledText( const QString& text )
00437 {
00438 d->cancelText = text;
00439 }
00440
00441 QString KCompletionBox::cancelledText() const
00442 {
00443 return d->cancelText;
00444 }
00445
00446 void KCompletionBox::canceled()
00447 {
00448 if ( !d->cancelText.isNull() )
00449 emit userCancelled( d->cancelText );
00450 if ( isVisible() )
00451 hide();
00452 }
00453
00454 class KCompletionBoxItem : public QListBoxItem
00455 {
00456 public:
00457
00458 bool reuse( const QString& newText )
00459 {
00460 if ( text() == newText )
00461 return false;
00462 setText( newText );
00463 return true;
00464 }
00465 };
00466
00467
00468 void KCompletionBox::insertItems( const QStringList& items, int index )
00469 {
00470 bool block = signalsBlocked();
00471 blockSignals( true );
00472 insertStringList( items, index );
00473 blockSignals( block );
00474 d->down_workaround = true;
00475 }
00476
00477 void KCompletionBox::setItems( const QStringList& items )
00478 {
00479 bool block = signalsBlocked();
00480 blockSignals( true );
00481
00482 QListBoxItem* item = firstItem();
00483 if ( !item ) {
00484 insertStringList( items );
00485 }
00486 else {
00487
00488
00489
00490 bool dirty = false;
00491
00492 QStringList::ConstIterator it = items.constBegin();
00493 const QStringList::ConstIterator itEnd = items.constEnd();
00494
00495 for ( ; it != itEnd; ++it) {
00496 if ( item ) {
00497 const bool changed = ((KCompletionBoxItem*)item)->reuse( *it );
00498 dirty = dirty || changed;
00499 item = item->next();
00500 }
00501 else {
00502 dirty = true;
00503
00504 insertItem( new QListBoxText( *it ) );
00505 }
00506 }
00507
00508
00509 if ( item ) {
00510 dirty = true;
00511 }
00512
00513 QListBoxItem* tmp = item;
00514 while ( (item = tmp ) ) {
00515 tmp = item->next();
00516 delete item;
00517 }
00518
00519 if (dirty)
00520 triggerUpdate( false );
00521 }
00522
00523 if ( isVisible() && size().height() != sizeHint().height() )
00524 sizeAndPosition();
00525
00526 blockSignals( block );
00527 d->down_workaround = true;
00528 }
00529
00530 void KCompletionBox::slotCurrentChanged()
00531 {
00532 d->down_workaround = false;
00533 }
00534
00535 void KCompletionBox::slotItemClicked( QListBoxItem *item )
00536 {
00537 if ( item )
00538 {
00539 if ( d->down_workaround ) {
00540 d->down_workaround = false;
00541 emit highlighted( item->text() );
00542 }
00543
00544 hide();
00545 emit activated( item->text() );
00546 }
00547 }
00548
00549 void KCompletionBox::setActivateOnSelect(bool state)
00550 {
00551 d->emitSelected = state;
00552 }
00553
00554 bool KCompletionBox::activateOnSelect() const
00555 {
00556 return d->emitSelected;
00557 }
00558
00559 void KCompletionBox::virtual_hook( int id, void* data )
00560 { KListBox::virtual_hook( id, data ); }
00561
00562 #include "kcompletionbox.moc"