libyui-qt  2.42.4
 All Classes Functions Variables
YQSelectionBox.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: YQSelectionBox.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23 /-*/
24 
25 #include <QString>
26 #include <QLabel>
27 #include <QListWidget>
28 #include <qnamespace.h>
29 #include <QPixmap>
30 #include <QKeyEvent>
31 #include <QVBoxLayout>
32 #define YUILogComponent "qt-ui"
33 #include <yui/YUILog.h>
34 
35 using std::max;
36 
37 #include "utf8.h"
38 #include <yui/YEvent.h>
39 #include "YQUI.h"
40 #include "YQApplication.h"
41 #include "YQSelectionBox.h"
42 #include "YQSignalBlocker.h"
43 #include "YQDialog.h"
44 #include <yui/YUIException.h>
45 #include "YQWidgetCaption.h"
46 
47 #define VERBOSE_SELECTION 1
48 
49 #define DEFAULT_VISIBLE_LINES 5
50 #define SHRINKABLE_VISIBLE_LINES 2
51 
52 
53 YQSelectionBox::YQSelectionBox( YWidget * parent, const std::string & label )
54  : QFrame( (QWidget *) parent->widgetRep() )
55  , YSelectionBox( parent, label )
56 {
57  setWidgetRep( this );
58 
59  QVBoxLayout* layout = new QVBoxLayout( this );
60  setLayout( layout );
61 
62  layout->setSpacing( YQWidgetSpacing );
63  layout->setMargin ( YQWidgetMargin );
64 
65  _caption = new YQWidgetCaption( this, label );
66  YUI_CHECK_NEW( _caption );
67  layout->addWidget( _caption );
68 
69  _qt_listWidget = new QListWidget( this );
70  YUI_CHECK_NEW( _qt_listWidget );
71  layout->addWidget( _qt_listWidget );
72 
73  _qt_listWidget->installEventFilter( this );
74  //FIXME _qt_listWidget->setVariableHeight( false );
75  _qt_listWidget->setSizePolicy( QSizePolicy( QSizePolicy::Expanding,
76  QSizePolicy::Expanding ) );
77  //FIXME _qt_listWidget->setTopItem(0);
78  _caption->setBuddy( _qt_listWidget );
79 
80  connect( _qt_listWidget, SIGNAL( itemSelectionChanged() ),
81  this, SLOT ( slotSelectionChanged() ) );
82 
83  connect( _qt_listWidget, SIGNAL( itemDoubleClicked( QListWidgetItem * ) ),
84  this, SLOT ( slotActivated( QListWidgetItem * ) ) );
85 
86  connect( &_timer, SIGNAL( timeout() ),
87  this, SLOT ( returnImmediately() ) );
88 }
89 
90 
92 {
93  // NOP
94 }
95 
96 
97 void YQSelectionBox::setLabel( const std::string & label )
98 {
99  _caption->setText( label );
100  YSelectionBox::setLabel( label );
101 }
102 
103 
104 void YQSelectionBox::addItems( const YItemCollection & itemCollection )
105 {
106  for ( YItemConstIterator it = itemCollection.begin();
107  it != itemCollection.end();
108  ++it )
109  {
110  addItem( *it,
111  true ); // batchMode
112  }
113 
114  _qt_listWidget->scrollToItem( _qt_listWidget->currentItem(),
115  QAbstractItemView::EnsureVisible );
116 }
117 
118 
119 void YQSelectionBox::addItem( YItem * item )
120 {
121  addItem( item,
122  false ); // batchMode
123 }
124 
125 
126 void YQSelectionBox::addItem( YItem * item, bool batchMode )
127 {
128  YSelectionBox::addItem( item );
129  QPixmap icon;
130 
131  if ( item->hasIconName() )
132  {
133  string iconName = iconFullPath( item );
134  icon = QPixmap( iconName.c_str() );
135 
136  if ( icon.isNull() )
137  yuiWarning() << "Can't load icon " << iconName << std::endl;
138  }
139 
140  if ( icon.isNull() )
141  {
142  _qt_listWidget->addItem( fromUTF8( item->label() ) );
143  }
144  else
145  {
146  QListWidgetItem *i = new QListWidgetItem( _qt_listWidget );
147  i->setData(Qt::DisplayRole, fromUTF8( item->label() ) );
148  i->setData(Qt::DecorationRole, icon );
149  _qt_listWidget->addItem( i );
150  }
151 
152  if ( item->selected() )
153  {
154  YQSignalBlocker sigBlocker( _qt_listWidget );
155  _qt_listWidget->setCurrentItem( _qt_listWidget->item( item->index() ) );
156  }
157 
158  if ( ! batchMode )
159  {
160  _qt_listWidget->scrollToItem( _qt_listWidget->currentItem(),
161  QAbstractItemView::EnsureVisible );
162  }
163 }
164 
165 
166 void YQSelectionBox::selectItem( YItem * item, bool selected )
167 {
168  YQSignalBlocker sigBlocker( _qt_listWidget );
169 
170  YSelectionBox::selectItem( item, selected );
171  _qt_listWidget->setCurrentRow( selected ? item->index() : -1 );
172 }
173 
174 
175 void YQSelectionBox::selectItem( int index )
176 {
177  YSelectionBox::deselectAllItems();
178  YItem * item = YSelectionBox::itemAt( index );
179 
180  if ( item )
181  {
182 #ifdef VERBOSE_SELECTION
183  yuiDebug() << this << ": Selecting item \"" << item->label() << "\"" << std::endl;
184 #endif
185 
186  item->setSelected( true );
187  }
188  else
189  YUI_THROW( YUIException( "Can't find selected item" ) );
190 }
191 
192 
194 {
195  YSelectionBox::deselectAllItems();
196  _qt_listWidget->clearSelection();
197  _qt_listWidget->setCurrentRow( -1 );
198 
199  if ( _qt_listWidget->currentRow() > -1 )
200  {
201  // Some item is selected after all; the Qt documtation says this
202  // happens if the QListBox is in single selection mode (which it is)
203  // and has the keyboard focus. setCurrentRow( -1 ) does the trick for
204  // now, but who knows how this might change in future Qt versions.
205  //
206  // Synchronize internal "selected" flags with what the QListBox
207  // displays. This has a small performance penalty because it calls
208  // YSelectionBox::deselectAllItems() again which again iterates over
209  // all items.
210 
211  int index = _qt_listWidget->row( _qt_listWidget->currentItem() );
212  selectItem( index );
213  }
214 }
215 
216 
218 {
219  YQSignalBlocker sigBlocker( _qt_listWidget );
220 
221  _qt_listWidget->clear();
222  YSelectionBox::deleteAllItems();
223 }
224 
225 
226 
228 {
229  int hintWidth = !_caption->isHidden() ?
230  _caption->sizeHint().width() + frameWidth() : 0;
231 
232  return max( 80, hintWidth );
233 }
234 
235 
237 {
238  int hintHeight = !_caption->isHidden() ? _caption->sizeHint().height() : 0;
239  int visibleLines = shrinkable() ? SHRINKABLE_VISIBLE_LINES : DEFAULT_VISIBLE_LINES;
240  hintHeight += visibleLines * _qt_listWidget->fontMetrics().lineSpacing();
241  hintHeight += _qt_listWidget->frameWidth() * 2;
242 
243  return max( 80, hintHeight );
244 }
245 
246 
247 void YQSelectionBox::setSize( int newWidth, int newHeight )
248 {
249  resize( newWidth, newHeight );
250 }
251 
252 
253 void YQSelectionBox::setEnabled( bool enabled )
254 {
255  _caption->setEnabled( enabled );
256  _qt_listWidget->setEnabled( enabled );
257  //FIXME needed? _qt_listWidget->triggerUpdate( true );
258  YWidget::setEnabled( enabled );
259 }
260 
261 
263 {
264  _qt_listWidget->setFocus();
265 
266  return true;
267 }
268 
269 
270 bool YQSelectionBox::eventFilter( QObject * obj, QEvent * ev )
271 {
272  if ( ev->type() == QEvent::KeyPress )
273  {
274  QKeyEvent * event = ( QKeyEvent * ) ev;
275 
276  if ( ( event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter ) &&
277  ( (event->modifiers() & Qt::NoModifier) || (event->modifiers() & Qt::KeypadModifier) ) )
278  {
279  YQDialog * dia = (YQDialog *) findDialog();
280 
281  if ( dia )
282  {
283  ( void ) dia->activateDefaultButton();
284  return true;
285  }
286  }
287  }
288  else if ( ev->type() == QEvent::MouseButtonRelease )
289  {
290  QMouseEvent * mouseEvent = dynamic_cast<QMouseEvent *> (ev);
291 
292  if ( mouseEvent && mouseEvent->button() == Qt::RightButton )
293  {
294  yuiMilestone() << "Right click in selecton box detected" << std::endl;
296  }
297  }
298  else if ( ev->type() == QEvent::ContextMenu )
299  {
300  QContextMenuEvent * contextMenuEvent = dynamic_cast<QContextMenuEvent *> (ev);
301 
302  YQUI::yqApp()->setContextMenuPos( contextMenuEvent->globalPos() );
303  if ( notifyContextMenu() )
304  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::ContextMenuActivated ) );
305  }
306 
307  return QWidget::eventFilter( obj, ev );
308 }
309 
310 
312 {
313  QList<QListWidgetItem *> items = _qt_listWidget->selectedItems();
314 
315  if ( ! items.empty() )
316  {
317  selectItem( _qt_listWidget->row( items.first() ) );
318  }
319  else
320  {
321  // Qt thinks it has to outsmart libyui: It might not select anything.
322  // So let's get our old selection back. Tit for tat.
323 
324  if ( hasItems() && hasSelectedItem() )
325  YQSelectionBox::selectItem( YSelectionWidget::selectedItem(), true );
326  }
327 
328  if ( notify() )
329  {
330  if ( immediateMode() )
332  else
333  {
334  if ( ! YQUI::ui()->eventsBlocked() )
335  {
336  // Delayed event delivery - only if events are to be delivered
337  // right now.
338  //
339  // An event block that is in effect right now may or may not
340  // affect events after the timer delay is expired.
341  //
342  // This may create nasty side effects such as bug #32510: When
343  // an item is initially selected, that initial selection event
344  // gets through even though (!) events are blocked during
345  // widget creation.
346 
347  returnDelayed();
348  }
349  }
350  }
351 }
352 
353 
354 void YQSelectionBox::slotActivated( QListWidgetItem * qItem )
355 {
356  selectItem( _qt_listWidget->row( qItem ) );
357 
358  if ( notify() )
359  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::Activated ) );
360 }
361 
362 
364 {
365  if ( YQUI::ui()->eventPendingFor( this ) )
366  {
367  YWidgetEvent * event = dynamic_cast<YWidgetEvent *> ( YQUI::ui()->pendingEvent() );
368 
369  if ( event && event->reason() != YEvent::SelectionChanged )
370  {
371  // Avoid overwriting a (more important) Activated event with a
372  // SelectionChanged event
373 
374  yuiDebug() << "Not overwriting more important event" << std::endl;
375 
376  return;
377  }
378  }
379 
380 
381  yuiDebug() << "Sending SelectionChanged event for " << this << std::endl;
382  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::SelectionChanged ) );
383 }
384 
385 
387 {
388  yuiDebug() << "Starting selbox timer" << std::endl;
389  _timer.setSingleShot( true );
390  _timer.start( 250 ); // millisec
391 }
392 
393 
394 
395 #include "YQSelectionBox.moc"