libyui-qt  2.46.13
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, &pclass(_qt_listWidget)::itemSelectionChanged,
81  this, &pclass(this)::slotSelectionChanged );
82 
83  connect( _qt_listWidget, &pclass(_qt_listWidget)::itemDoubleClicked,
84  this, &pclass(this)::slotActivated );
85 
86  connect( &_timer, &pclass(&_timer)::timeout,
87  this, &pclass(this)::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"
virtual bool eventFilter(QObject *obj, QEvent *ev)
Event filter.
virtual void deleteAllItems()
Delete all items.
virtual bool setKeyboardFocus()
Accept the keyboard focus.
virtual void addItems(const YItemCollection &itemCollection)
Add multiple items.
Helper class to block Qt signals for QWidgets or QObjects as long as this object exists.
void slotActivated(QListWidgetItem *item)
Notification that an item has been activated (double clicked).
static YQApplication * yqApp()
Return the global YApplication object as YQApplication.
Definition: YQUI.cc:284
void maybeLeftHandedUser()
A mouse click with the wrong mouse button was detected - e.g., a right click on a push button...
virtual void setText(const std::string &newText)
Change the text and handle visibility: If the new text is empty, hide this widget.
virtual void addItem(YItem *item)
Add an item.
YQSelectionBox(YWidget *parent, const std::string &label)
Constructor.
virtual void setSize(int newWidth, int newHeight)
Set the new size of the widget.
virtual void setContextMenuPos(QPoint contextMenuPos)
Sets the position of the context menu (in gloabl coordinates)
void returnDelayed()
Return after some millseconds delay - collect multiple events.
virtual void deselectAllItems()
Deselect all items.
virtual int preferredWidth()
Preferred width of the widget.
void sendEvent(YEvent *event)
Widget event handlers (slots) call this when an event occured that should be the answer to a UserInpu...
Definition: YQUI.cc:486
virtual int preferredHeight()
Preferred height of the widget.
virtual ~YQSelectionBox()
Destructor.
virtual void setEnabled(bool enabled)
Set enabled/disabled state.
bool activateDefaultButton(bool warn=true)
Activate (i.e.
Definition: YQDialog.cc:529
virtual void selectItem(YItem *item, bool selected=true)
Select or deselect an item.
void slotSelectionChanged()
Notification that an item has been selected.
Helper class for captions (labels) above a widget: Takes care of hiding itself when its text is empty...
virtual void setLabel(const std::string &label)
Change the label text.
YEvent * pendingEvent() const
Returns the last event that isn't processed yet or 0 if there is none.
Definition: YQUI.h:148
static YQUI * ui()
Access the global Qt-UI.
Definition: YQUI.h:81
void returnImmediately()
Return immediately.