libyui-qt  2.43.5
/usr/src/RPM/BUILD/libyui-qt-2.43.5/src/YQSelectionBox.cc
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"
 All Classes Functions Variables