libyui-qt
2.43.5
|
00001 /* 00002 Copyright (C) 2000-2012 Novell, Inc 00003 This library is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU Lesser General Public License as 00005 published by the Free Software Foundation; either version 2.1 of the 00006 License, or (at your option) version 3.0 of the License. This library 00007 is distributed in the hope that it will be useful, but WITHOUT ANY 00008 WARRANTY; without even the implied warranty of MERCHANTABILITY or 00009 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00010 License for more details. You should have received a copy of the GNU 00011 Lesser General Public License along with this library; if not, write 00012 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 00013 Floor, Boston, MA 02110-1301 USA 00014 */ 00015 00016 00017 /*-/ 00018 00019 File: YQSelectionBox.cc 00020 00021 Author: Stefan Hundhammer <sh@suse.de> 00022 00023 /-*/ 00024 00025 #include <QString> 00026 #include <QLabel> 00027 #include <QListWidget> 00028 #include <qnamespace.h> 00029 #include <QPixmap> 00030 #include <QKeyEvent> 00031 #include <QVBoxLayout> 00032 #define YUILogComponent "qt-ui" 00033 #include <yui/YUILog.h> 00034 00035 using std::max; 00036 00037 #include "utf8.h" 00038 #include <yui/YEvent.h> 00039 #include "YQUI.h" 00040 #include "YQApplication.h" 00041 #include "YQSelectionBox.h" 00042 #include "YQSignalBlocker.h" 00043 #include "YQDialog.h" 00044 #include <yui/YUIException.h> 00045 #include "YQWidgetCaption.h" 00046 00047 #define VERBOSE_SELECTION 1 00048 00049 #define DEFAULT_VISIBLE_LINES 5 00050 #define SHRINKABLE_VISIBLE_LINES 2 00051 00052 00053 YQSelectionBox::YQSelectionBox( YWidget * parent, const std::string & label ) 00054 : QFrame( (QWidget *) parent->widgetRep() ) 00055 , YSelectionBox( parent, label ) 00056 { 00057 setWidgetRep( this ); 00058 00059 QVBoxLayout* layout = new QVBoxLayout( this ); 00060 setLayout( layout ); 00061 00062 layout->setSpacing( YQWidgetSpacing ); 00063 layout->setMargin ( YQWidgetMargin ); 00064 00065 _caption = new YQWidgetCaption( this, label ); 00066 YUI_CHECK_NEW( _caption ); 00067 layout->addWidget( _caption ); 00068 00069 _qt_listWidget = new QListWidget( this ); 00070 YUI_CHECK_NEW( _qt_listWidget ); 00071 layout->addWidget( _qt_listWidget ); 00072 00073 _qt_listWidget->installEventFilter( this ); 00074 //FIXME _qt_listWidget->setVariableHeight( false ); 00075 _qt_listWidget->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, 00076 QSizePolicy::Expanding ) ); 00077 //FIXME _qt_listWidget->setTopItem(0); 00078 _caption->setBuddy( _qt_listWidget ); 00079 00080 connect( _qt_listWidget, SIGNAL( itemSelectionChanged() ), 00081 this, SLOT ( slotSelectionChanged() ) ); 00082 00083 connect( _qt_listWidget, SIGNAL( itemDoubleClicked( QListWidgetItem * ) ), 00084 this, SLOT ( slotActivated( QListWidgetItem * ) ) ); 00085 00086 connect( &_timer, SIGNAL( timeout() ), 00087 this, SLOT ( returnImmediately() ) ); 00088 } 00089 00090 00091 YQSelectionBox::~YQSelectionBox() 00092 { 00093 // NOP 00094 } 00095 00096 00097 void YQSelectionBox::setLabel( const std::string & label ) 00098 { 00099 _caption->setText( label ); 00100 YSelectionBox::setLabel( label ); 00101 } 00102 00103 00104 void YQSelectionBox::addItems( const YItemCollection & itemCollection ) 00105 { 00106 for ( YItemConstIterator it = itemCollection.begin(); 00107 it != itemCollection.end(); 00108 ++it ) 00109 { 00110 addItem( *it, 00111 true ); // batchMode 00112 } 00113 00114 _qt_listWidget->scrollToItem( _qt_listWidget->currentItem(), 00115 QAbstractItemView::EnsureVisible ); 00116 } 00117 00118 00119 void YQSelectionBox::addItem( YItem * item ) 00120 { 00121 addItem( item, 00122 false ); // batchMode 00123 } 00124 00125 00126 void YQSelectionBox::addItem( YItem * item, bool batchMode ) 00127 { 00128 YSelectionBox::addItem( item ); 00129 QPixmap icon; 00130 00131 if ( item->hasIconName() ) 00132 { 00133 string iconName = iconFullPath( item ); 00134 icon = QPixmap( iconName.c_str() ); 00135 00136 if ( icon.isNull() ) 00137 yuiWarning() << "Can't load icon " << iconName << std::endl; 00138 } 00139 00140 if ( icon.isNull() ) 00141 { 00142 _qt_listWidget->addItem( fromUTF8( item->label() ) ); 00143 } 00144 else 00145 { 00146 QListWidgetItem *i = new QListWidgetItem( _qt_listWidget ); 00147 i->setData(Qt::DisplayRole, fromUTF8( item->label() ) ); 00148 i->setData(Qt::DecorationRole, icon ); 00149 _qt_listWidget->addItem( i ); 00150 } 00151 00152 if ( item->selected() ) 00153 { 00154 YQSignalBlocker sigBlocker( _qt_listWidget ); 00155 _qt_listWidget->setCurrentItem( _qt_listWidget->item( item->index() ) ); 00156 } 00157 00158 if ( ! batchMode ) 00159 { 00160 _qt_listWidget->scrollToItem( _qt_listWidget->currentItem(), 00161 QAbstractItemView::EnsureVisible ); 00162 } 00163 } 00164 00165 00166 void YQSelectionBox::selectItem( YItem * item, bool selected ) 00167 { 00168 YQSignalBlocker sigBlocker( _qt_listWidget ); 00169 00170 YSelectionBox::selectItem( item, selected ); 00171 _qt_listWidget->setCurrentRow( selected ? item->index() : -1 ); 00172 } 00173 00174 00175 void YQSelectionBox::selectItem( int index ) 00176 { 00177 YSelectionBox::deselectAllItems(); 00178 YItem * item = YSelectionBox::itemAt( index ); 00179 00180 if ( item ) 00181 { 00182 #ifdef VERBOSE_SELECTION 00183 yuiDebug() << this << ": Selecting item \"" << item->label() << "\"" << std::endl; 00184 #endif 00185 00186 item->setSelected( true ); 00187 } 00188 else 00189 YUI_THROW( YUIException( "Can't find selected item" ) ); 00190 } 00191 00192 00193 void YQSelectionBox::deselectAllItems() 00194 { 00195 YSelectionBox::deselectAllItems(); 00196 _qt_listWidget->clearSelection(); 00197 _qt_listWidget->setCurrentRow( -1 ); 00198 00199 if ( _qt_listWidget->currentRow() > -1 ) 00200 { 00201 // Some item is selected after all; the Qt documtation says this 00202 // happens if the QListBox is in single selection mode (which it is) 00203 // and has the keyboard focus. setCurrentRow( -1 ) does the trick for 00204 // now, but who knows how this might change in future Qt versions. 00205 // 00206 // Synchronize internal "selected" flags with what the QListBox 00207 // displays. This has a small performance penalty because it calls 00208 // YSelectionBox::deselectAllItems() again which again iterates over 00209 // all items. 00210 00211 int index = _qt_listWidget->row( _qt_listWidget->currentItem() ); 00212 selectItem( index ); 00213 } 00214 } 00215 00216 00217 void YQSelectionBox::deleteAllItems() 00218 { 00219 YQSignalBlocker sigBlocker( _qt_listWidget ); 00220 00221 _qt_listWidget->clear(); 00222 YSelectionBox::deleteAllItems(); 00223 } 00224 00225 00226 00227 int YQSelectionBox::preferredWidth() 00228 { 00229 int hintWidth = !_caption->isHidden() ? 00230 _caption->sizeHint().width() + frameWidth() : 0; 00231 00232 return max( 80, hintWidth ); 00233 } 00234 00235 00236 int YQSelectionBox::preferredHeight() 00237 { 00238 int hintHeight = !_caption->isHidden() ? _caption->sizeHint().height() : 0; 00239 int visibleLines = shrinkable() ? SHRINKABLE_VISIBLE_LINES : DEFAULT_VISIBLE_LINES; 00240 hintHeight += visibleLines * _qt_listWidget->fontMetrics().lineSpacing(); 00241 hintHeight += _qt_listWidget->frameWidth() * 2; 00242 00243 return max( 80, hintHeight ); 00244 } 00245 00246 00247 void YQSelectionBox::setSize( int newWidth, int newHeight ) 00248 { 00249 resize( newWidth, newHeight ); 00250 } 00251 00252 00253 void YQSelectionBox::setEnabled( bool enabled ) 00254 { 00255 _caption->setEnabled( enabled ); 00256 _qt_listWidget->setEnabled( enabled ); 00257 //FIXME needed? _qt_listWidget->triggerUpdate( true ); 00258 YWidget::setEnabled( enabled ); 00259 } 00260 00261 00262 bool YQSelectionBox::setKeyboardFocus() 00263 { 00264 _qt_listWidget->setFocus(); 00265 00266 return true; 00267 } 00268 00269 00270 bool YQSelectionBox::eventFilter( QObject * obj, QEvent * ev ) 00271 { 00272 if ( ev->type() == QEvent::KeyPress ) 00273 { 00274 QKeyEvent * event = ( QKeyEvent * ) ev; 00275 00276 if ( ( event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter ) && 00277 ( (event->modifiers() & Qt::NoModifier) || (event->modifiers() & Qt::KeypadModifier) ) ) 00278 { 00279 YQDialog * dia = (YQDialog *) findDialog(); 00280 00281 if ( dia ) 00282 { 00283 ( void ) dia->activateDefaultButton(); 00284 return true; 00285 } 00286 } 00287 } 00288 else if ( ev->type() == QEvent::MouseButtonRelease ) 00289 { 00290 QMouseEvent * mouseEvent = dynamic_cast<QMouseEvent *> (ev); 00291 00292 if ( mouseEvent && mouseEvent->button() == Qt::RightButton ) 00293 { 00294 yuiMilestone() << "Right click in selecton box detected" << std::endl; 00295 YQUI::yqApp()->maybeLeftHandedUser(); 00296 } 00297 } 00298 else if ( ev->type() == QEvent::ContextMenu ) 00299 { 00300 QContextMenuEvent * contextMenuEvent = dynamic_cast<QContextMenuEvent *> (ev); 00301 00302 YQUI::yqApp()->setContextMenuPos( contextMenuEvent->globalPos() ); 00303 if ( notifyContextMenu() ) 00304 YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::ContextMenuActivated ) ); 00305 } 00306 00307 return QWidget::eventFilter( obj, ev ); 00308 } 00309 00310 00311 void YQSelectionBox::slotSelectionChanged() 00312 { 00313 QList<QListWidgetItem *> items = _qt_listWidget->selectedItems(); 00314 00315 if ( ! items.empty() ) 00316 { 00317 selectItem( _qt_listWidget->row( items.first() ) ); 00318 } 00319 else 00320 { 00321 // Qt thinks it has to outsmart libyui: It might not select anything. 00322 // So let's get our old selection back. Tit for tat. 00323 00324 if ( hasItems() && hasSelectedItem() ) 00325 YQSelectionBox::selectItem( YSelectionWidget::selectedItem(), true ); 00326 } 00327 00328 if ( notify() ) 00329 { 00330 if ( immediateMode() ) 00331 returnImmediately(); 00332 else 00333 { 00334 if ( ! YQUI::ui()->eventsBlocked() ) 00335 { 00336 // Delayed event delivery - only if events are to be delivered 00337 // right now. 00338 // 00339 // An event block that is in effect right now may or may not 00340 // affect events after the timer delay is expired. 00341 // 00342 // This may create nasty side effects such as bug #32510: When 00343 // an item is initially selected, that initial selection event 00344 // gets through even though (!) events are blocked during 00345 // widget creation. 00346 00347 returnDelayed(); 00348 } 00349 } 00350 } 00351 } 00352 00353 00354 void YQSelectionBox::slotActivated( QListWidgetItem * qItem ) 00355 { 00356 selectItem( _qt_listWidget->row( qItem ) ); 00357 00358 if ( notify() ) 00359 YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::Activated ) ); 00360 } 00361 00362 00363 void YQSelectionBox::returnImmediately() 00364 { 00365 if ( YQUI::ui()->eventPendingFor( this ) ) 00366 { 00367 YWidgetEvent * event = dynamic_cast<YWidgetEvent *> ( YQUI::ui()->pendingEvent() ); 00368 00369 if ( event && event->reason() != YEvent::SelectionChanged ) 00370 { 00371 // Avoid overwriting a (more important) Activated event with a 00372 // SelectionChanged event 00373 00374 yuiDebug() << "Not overwriting more important event" << std::endl; 00375 00376 return; 00377 } 00378 } 00379 00380 00381 yuiDebug() << "Sending SelectionChanged event for " << this << std::endl; 00382 YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::SelectionChanged ) ); 00383 } 00384 00385 00386 void YQSelectionBox::returnDelayed() 00387 { 00388 yuiDebug() << "Starting selbox timer" << std::endl; 00389 _timer.setSingleShot( true ); 00390 _timer.start( 250 ); // millisec 00391 } 00392 00393 00394 00395 #include "YQSelectionBox.moc"