kdeui Library API Documentation

kcompletionbox.cpp

00001 /* This file is part of the KDE libraries 00002 00003 Copyright (c) 2000,2001,2002 Carsten Pfeiffer <pfeiffer@kde.org> 00004 Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de> 00005 Copyright (c) 2000,2001 Dawit Alemayehu <adawit@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License (LGPL) as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00020 Boston, MA 02111-1307, USA. 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; // necessary to set the focus back 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 // grmbl, just QListBox workarounds :[ Thanks Volker. 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(); // Only on TAB!! 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 // If there is no selected item and we've popped up above 00132 // our parent, select the first item when they press up. 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(); // Consume the Enter event 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 // Override any acceleartors that match 00183 // the key sequences we use here... 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 // parent loses focus or gets a click -> we hide 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 // any mouse-click on something else than "this" makes us hide 00228 else if ( type == QEvent::MouseButtonPress ) { 00229 QMouseEvent *ev = static_cast<QMouseEvent *>( e ); 00230 if ( !rect().contains( ev->pos() )) // this widget 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 // Are we above our parent? If so we must keep bottom edge anchored. 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 // ### we shouldn't need to call this, but without this, the scrollbars 00297 // are pretty b0rked. 00298 //triggerUpdate( true ); 00299 00300 // Workaround for I'm not sure whose bug - if this KCompletionBox' parent 00301 // is in a layout, that layout will detect inserting new child (posted 00302 // ChildInserted event), and will trigger relayout (post LayoutHint event). 00303 // QWidget::show() sends also posted ChildInserted events for the parent, 00304 // and later all LayoutHint events, which causes layout updating. 00305 // The problem is, KCompletionBox::eventFilter() detects resizing 00306 // of the parent, and calls hide() - and this hide() happen in the middle 00307 // of show(), causing inconsistent state. I'll try to submit a Qt patch too. 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 //If we're inside a combox, Qt by default makes the dropdown 00330 // as wide as the combo, and gives the style a chance 00331 // to adjust it. Do that here as well, for consistency 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 //Expand to the combo width 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 //We need to adjust our horizontal position to also be WRT to the combo 00345 x += comboCorner.x() - parentCorner.x(); 00346 00347 //The same with vertical one 00348 y += cb->height() - d->m_parent->height() + 00349 comboCorner.y() - parentCorner.y(); 00350 00351 //Ask the style to refine this a bit 00352 QRect styleAdj = style().querySubControlMetrics(QStyle::CC_ComboBox, 00353 cb, QStyle::SC_ComboBoxListBoxPopup, 00354 QStyleOption(x, y, w, h)); 00355 //QCommonStyle returns QRect() by default, so this is what we get if the 00356 //style doesn't implement this 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 //Returns true if dirty. 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 //Keep track of whether we need to change anything, 00476 //so we can avoid a repaint for identical updates, 00477 //to reduce flicker 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 //Inserting an item is a way of making this dirty 00488 insertItem( new QListBoxText( *it ) ); 00489 } 00490 } 00491 00492 //If there is an unused item, mark as dirty -> less items now 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"
KDE Logo
This file is part of the documentation for kdeui Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:43:27 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003