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"