libyui-qt  2.52.4
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 
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:268
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.
YEvent * pendingEvent() const
Returns the last event that isn&#39;t processed yet or 0 if there is none.
Definition: YQUI.h:150
virtual void addItem(YItem *item)
Add an item.
YQSelectionBox(YWidget *parent, const std::string &label)
Constructor.
QIcon loadIcon(const string &iconName) const
Load an icon.
Definition: YQUI.cc:708
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:480
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:534
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.
static YQUI * ui()
Access the global Qt-UI.
Definition: YQUI.h:83
void returnImmediately()
Return immediately.