libyui-qt  2.53.0
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 <QKeyEvent>
30 #include <QVBoxLayout>
31 #define YUILogComponent "qt-ui"
32 #include <yui/YUILog.h>
33 
34 #include "utf8.h"
35 #include <yui/YEvent.h>
36 #include "YQUI.h"
37 #include "YQApplication.h"
38 #include "YQSelectionBox.h"
39 #include "YQSignalBlocker.h"
40 #include "YQDialog.h"
41 #include <yui/YUIException.h>
42 #include "YQWidgetCaption.h"
43 
44 #define VERBOSE_SELECTION 1
45 
46 #define DEFAULT_VISIBLE_LINES 5
47 #define SHRINKABLE_VISIBLE_LINES 2
48 
49 using std::string;
50 using std::endl;
51 
52 
53 YQSelectionBox::YQSelectionBox( YWidget * parent, const 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 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  QIcon icon;
130 
131  if ( item->hasIconName() )
132  {
133  icon = icon = YQUI::ui()->loadIcon( item->iconName() );
134  }
135 
136  if ( icon.isNull() )
137  {
138  _qt_listWidget->addItem( fromUTF8( item->label() ) );
139  }
140  else
141  {
142  QListWidgetItem *i = new QListWidgetItem( _qt_listWidget );
143  i->setData(Qt::DisplayRole, fromUTF8( item->label() ) );
144  i->setData(Qt::DecorationRole, icon );
145  _qt_listWidget->addItem( i );
146  }
147 
148  if ( item->selected() )
149  {
150  YQSignalBlocker sigBlocker( _qt_listWidget );
151  _qt_listWidget->setCurrentItem( _qt_listWidget->item( item->index() ) );
152  }
153 
154  if ( ! batchMode )
155  {
156  _qt_listWidget->scrollToItem( _qt_listWidget->currentItem(),
157  QAbstractItemView::EnsureVisible );
158  }
159 }
160 
161 
162 void YQSelectionBox::selectItem( YItem * item, bool selected )
163 {
164  YQSignalBlocker sigBlocker( _qt_listWidget );
165 
166  YSelectionBox::selectItem( item, selected );
167  _qt_listWidget->setCurrentRow( selected ? item->index() : -1 );
168 }
169 
170 
171 void YQSelectionBox::selectItem( int index )
172 {
173  YSelectionBox::deselectAllItems();
174  YItem * item = YSelectionBox::itemAt( index );
175 
176  if ( item )
177  {
178 #ifdef VERBOSE_SELECTION
179  yuiDebug() << this << ": Selecting item \"" << item->label() << "\"" << endl;
180 #endif
181 
182  item->setSelected( true );
183  }
184  else
185  YUI_THROW( YUIException( "Can't find selected item" ) );
186 }
187 
188 
190 {
191  YSelectionBox::deselectAllItems();
192  _qt_listWidget->clearSelection();
193  _qt_listWidget->setCurrentRow( -1 );
194 
195  if ( _qt_listWidget->currentRow() > -1 )
196  {
197  // Some item is selected after all; the Qt documtation says this
198  // happens if the QListBox is in single selection mode (which it is)
199  // and has the keyboard focus. setCurrentRow( -1 ) does the trick for
200  // now, but who knows how this might change in future Qt versions.
201  //
202  // Synchronize internal "selected" flags with what the QListBox
203  // displays. This has a small performance penalty because it calls
204  // YSelectionBox::deselectAllItems() again which again iterates over
205  // all items.
206 
207  int index = _qt_listWidget->row( _qt_listWidget->currentItem() );
208  selectItem( index );
209  }
210 }
211 
212 
214 {
215  YQSignalBlocker sigBlocker( _qt_listWidget );
216 
217  _qt_listWidget->clear();
218  YSelectionBox::deleteAllItems();
219 }
220 
221 
222 
224 {
225  int hintWidth = !_caption->isHidden() ?
226  _caption->sizeHint().width() + frameWidth() : 0;
227 
228  return std::max( 80, hintWidth );
229 }
230 
231 
233 {
234  int hintHeight = !_caption->isHidden() ? _caption->sizeHint().height() : 0;
235  int visibleLines = shrinkable() ? SHRINKABLE_VISIBLE_LINES : DEFAULT_VISIBLE_LINES;
236  hintHeight += visibleLines * _qt_listWidget->fontMetrics().lineSpacing();
237  hintHeight += _qt_listWidget->frameWidth() * 2;
238 
239  return std::max( 80, hintHeight );
240 }
241 
242 
243 void YQSelectionBox::setSize( int newWidth, int newHeight )
244 {
245  resize( newWidth, newHeight );
246 }
247 
248 
249 void YQSelectionBox::setEnabled( bool enabled )
250 {
251  _caption->setEnabled( enabled );
252  _qt_listWidget->setEnabled( enabled );
253  //FIXME needed? _qt_listWidget->triggerUpdate( true );
254  YWidget::setEnabled( enabled );
255 }
256 
257 
259 {
260  _qt_listWidget->setFocus();
261 
262  return true;
263 }
264 
265 
266 bool YQSelectionBox::eventFilter( QObject * obj, QEvent * ev )
267 {
268  if ( ev->type() == QEvent::KeyPress )
269  {
270  QKeyEvent * event = ( QKeyEvent * ) ev;
271 
272  if ( ( event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter ) &&
273  ( (event->modifiers() & Qt::NoModifier) || (event->modifiers() & Qt::KeypadModifier) ) )
274  {
275  YQDialog * dia = (YQDialog *) findDialog();
276 
277  if ( dia )
278  {
279  ( void ) dia->activateDefaultButton();
280  return true;
281  }
282  }
283  }
284  else if ( ev->type() == QEvent::MouseButtonRelease )
285  {
286  QMouseEvent * mouseEvent = dynamic_cast<QMouseEvent *> (ev);
287 
288  if ( mouseEvent && mouseEvent->button() == Qt::RightButton )
289  {
290  yuiMilestone() << "Right click in selecton box detected" << endl;
292  }
293  }
294  else if ( ev->type() == QEvent::ContextMenu )
295  {
296  QContextMenuEvent * contextMenuEvent = dynamic_cast<QContextMenuEvent *> (ev);
297 
298  YQUI::yqApp()->setContextMenuPos( contextMenuEvent->globalPos() );
299  if ( notifyContextMenu() )
300  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::ContextMenuActivated ) );
301  }
302 
303  return QWidget::eventFilter( obj, ev );
304 }
305 
306 
308 {
309  QList<QListWidgetItem *> items = _qt_listWidget->selectedItems();
310 
311  if ( ! items.empty() )
312  {
313  selectItem( _qt_listWidget->row( items.first() ) );
314  }
315  else
316  {
317  // Qt thinks it has to outsmart libyui: It might not select anything.
318  // So let's get our old selection back. Tit for tat.
319 
320  if ( hasItems() && hasSelectedItem() )
321  YQSelectionBox::selectItem( YSelectionWidget::selectedItem(), true );
322  }
323 
324  if ( notify() )
325  {
326  if ( immediateMode() )
328  else
329  {
330  if ( ! YQUI::ui()->eventsBlocked() )
331  {
332  // Delayed event delivery - only if events are to be delivered
333  // right now.
334  //
335  // An event block that is in effect right now may or may not
336  // affect events after the timer delay is expired.
337  //
338  // This may create nasty side effects such as bug #32510: When
339  // an item is initially selected, that initial selection event
340  // gets through even though (!) events are blocked during
341  // widget creation.
342 
343  returnDelayed();
344  }
345  }
346  }
347 }
348 
349 
350 void YQSelectionBox::slotActivated( QListWidgetItem * qItem )
351 {
352  selectItem( _qt_listWidget->row( qItem ) );
353 
354  if ( notify() )
355  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::Activated ) );
356 }
357 
358 
360 {
361  if ( YQUI::ui()->eventPendingFor( this ) )
362  {
363  YWidgetEvent * event = dynamic_cast<YWidgetEvent *> ( YQUI::ui()->pendingEvent() );
364 
365  if ( event && event->reason() != YEvent::SelectionChanged )
366  {
367  // Avoid overwriting a (more important) Activated event with a
368  // SelectionChanged event
369 
370  yuiDebug() << "Not overwriting more important event" << endl;
371 
372  return;
373  }
374  }
375 
376 
377  yuiDebug() << "Sending SelectionChanged event for " << this << endl;
378  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::SelectionChanged ) );
379 }
380 
381 
383 {
384  yuiDebug() << "Starting selbox timer" << endl;
385  _timer.setSingleShot( true );
386  _timer.start( 250 ); // millisec
387 }
388 
YQSignalBlocker
Helper class to block Qt signals for QWidgets or QObjects as long as this object exists.
Definition: YQSignalBlocker.h:37
YQSelectionBox::eventFilter
virtual bool eventFilter(QObject *obj, QEvent *ev)
Event filter.
Definition: YQSelectionBox.cc:266
YQSelectionBox::preferredWidth
virtual int preferredWidth()
Preferred width of the widget.
Definition: YQSelectionBox.cc:223
YQSelectionBox::setKeyboardFocus
virtual bool setKeyboardFocus()
Accept the keyboard focus.
Definition: YQSelectionBox.cc:258
YQSelectionBox::YQSelectionBox
YQSelectionBox(YWidget *parent, const std::string &label)
Constructor.
Definition: YQSelectionBox.cc:53
YQSelectionBox::returnDelayed
void returnDelayed()
Return after some millseconds delay - collect multiple events.
Definition: YQSelectionBox.cc:382
YQUI::sendEvent
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:480
YQSelectionBox::setSize
virtual void setSize(int newWidth, int newHeight)
Set the new size of the widget.
Definition: YQSelectionBox.cc:243
YQDialog
Definition: YQDialog.h:43
YQApplication::maybeLeftHandedUser
void maybeLeftHandedUser()
A mouse click with the wrong mouse button was detected - e.g., a right click on a push button.
Definition: YQApplication.cc:642
YQSelectionBox::setEnabled
virtual void setEnabled(bool enabled)
Set enabled/disabled state.
Definition: YQSelectionBox.cc:249
YQSelectionBox::addItems
virtual void addItems(const YItemCollection &itemCollection)
Add multiple items.
Definition: YQSelectionBox.cc:104
YQWidgetCaption::setText
virtual void setText(const std::string &newText)
Change the text and handle visibility: If the new text is empty, hide this widget.
Definition: YQWidgetCaption.cc:59
YQUI::ui
static YQUI * ui()
Access the global Qt-UI.
Definition: YQUI.h:83
YQSelectionBox::deleteAllItems
virtual void deleteAllItems()
Delete all items.
Definition: YQSelectionBox.cc:213
YQSelectionBox::~YQSelectionBox
virtual ~YQSelectionBox()
Destructor.
Definition: YQSelectionBox.cc:91
YQSelectionBox::slotSelectionChanged
void slotSelectionChanged()
Notification that an item has been selected.
Definition: YQSelectionBox.cc:307
YQUI::pendingEvent
YEvent * pendingEvent() const
Returns the last event that isn't processed yet or 0 if there is none.
Definition: YQUI.h:150
YQUI::yqApp
static YQApplication * yqApp()
Return the global YApplication object as YQApplication.
Definition: YQUI.cc:268
YQSelectionBox::slotActivated
void slotActivated(QListWidgetItem *item)
Notification that an item has been activated (double clicked).
Definition: YQSelectionBox.cc:350
YQSelectionBox::selectItem
virtual void selectItem(YItem *item, bool selected=true)
Select or deselect an item.
Definition: YQSelectionBox.cc:162
YQWidgetCaption
Helper class for captions (labels) above a widget: Takes care of hiding itself when its text is empty...
Definition: YQWidgetCaption.h:39
YQSelectionBox::setLabel
virtual void setLabel(const std::string &label)
Change the label text.
Definition: YQSelectionBox.cc:97
YQSelectionBox::preferredHeight
virtual int preferredHeight()
Preferred height of the widget.
Definition: YQSelectionBox.cc:232
YQSelectionBox::deselectAllItems
virtual void deselectAllItems()
Deselect all items.
Definition: YQSelectionBox.cc:189
YQSelectionBox::addItem
virtual void addItem(YItem *item)
Add an item.
Definition: YQSelectionBox.cc:119
YQDialog::activateDefaultButton
bool activateDefaultButton(bool warn=true)
Activate (i.e.
Definition: YQDialog.cc:534
YQApplication::setContextMenuPos
virtual void setContextMenuPos(QPoint contextMenuPos)
Sets the position of the context menu (in gloabl coordinates)
Definition: YQApplication.cc:773
YQSelectionBox::returnImmediately
void returnImmediately()
Return immediately.
Definition: YQSelectionBox.cc:359
YQUI::loadIcon
QIcon loadIcon(const string &iconName) const
Load an icon.
Definition: YQUI.cc:708