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