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