f408333743a0615a09306aac2f30ca3e97cdf341
[duncan/yast2-qt4.git] / src / YQSelectionBox.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       YQSelectionBox.cc
14
15   Author:     Stefan Hundhammer <sh@suse.de>
16
17 /-*/
18
19 #include <QString>
20 #include <QLabel>
21 #include <QListWidget>
22 #include <qnamespace.h>
23 #include <QPixmap>
24 #include <QEvent>
25 #include <QVBoxLayout>
26 #define y2log_component "qt-ui"
27 #include <ycp/y2log.h>
28
29 using std::max;
30
31 #include "utf8.h"
32 #include "YEvent.h"
33 #include "YQUI.h"
34 #include "YQSelectionBox.h"
35 #include "YQSignalBlocker.h"
36 #include "YQDialog.h"
37 #include "YUIException.h"
38 #include "YQWidgetCaption.h"
39
40 #define VERBOSE_SELECTION               1
41
42 #define DEFAULT_VISIBLE_LINES           5
43 #define SHRINKABLE_VISIBLE_LINES        2
44
45
46 YQSelectionBox::YQSelectionBox( YWidget * parent, const string & label )
47     : QFrame( (QWidget *) parent->widgetRep() )
48     , YSelectionBox( parent, label )
49 {
50     setWidgetRep( this );
51
52     QVBoxLayout* layout = new QVBoxLayout( this );
53     setLayout( layout );
54
55     layout->setSpacing( YQWidgetSpacing );
56     layout->setMargin ( YQWidgetMargin  );
57
58     _caption = new YQWidgetCaption( this, label );
59     YUI_CHECK_NEW( _caption );
60     layout->addWidget( _caption );
61
62     _qt_listBox = new QListWidget( this );
63     YUI_CHECK_NEW( _qt_listBox );
64     layout->addWidget( _qt_listBox );
65
66     _qt_listBox->installEventFilter( this );
67     //FIXME _qt_listBox->setVariableHeight( false );
68     _qt_listBox->setSizePolicy( QSizePolicy( QSizePolicy::Expanding,
69                                              QSizePolicy::Expanding ) );
70     //FIXME _qt_listBox->setTopItem(0);
71     _caption->setBuddy( _qt_listBox );
72
73     connect( _qt_listBox,       SIGNAL( highlighted ( int ) ),
74              this,              SLOT  ( slotSelected( int ) ) );
75
76     connect( _qt_listBox,       SIGNAL( doubleClicked( QListWidgetItem * ) ),
77              this,              SLOT  ( slotActivated( QListWidgetItem * ) ) );
78
79     connect( &_timer,           SIGNAL( timeout()           ),
80              this,              SLOT  ( returnImmediately() ) );
81 }
82
83
84 YQSelectionBox::~YQSelectionBox()
85 {
86     // NOP
87 }
88
89
90 void YQSelectionBox::setLabel( const string & label )
91 {
92     _caption->setText( label );
93     YSelectionBox::setLabel( label );
94 }
95
96
97 void YQSelectionBox::addItem( YItem * item )
98 {
99     YSelectionBox::addItem( item );
100     QPixmap icon;
101
102     if ( item->hasIconName() )
103     {
104         string iconName = iconFullPath( item );
105         icon = QPixmap( iconName.c_str() );
106
107         if ( icon.isNull() )
108             y2warning( "Can't load icon %s", iconName.c_str() );
109     }
110
111     if ( icon.isNull() )
112     {
113       _qt_listBox->addItem( fromUTF8( item->label() ) );
114     }
115     else
116     {
117       QListWidgetItem *i = new QListWidgetItem(_qt_listBox);
118       i->setData(Qt::DisplayRole, fromUTF8( item->label() ) );
119       i->setData(Qt::DecorationRole, icon );
120       _qt_listBox->addItem( i );
121     }
122
123     if ( item->selected() )
124     {
125         YQSignalBlocker sigBlocker( _qt_listBox );
126         _qt_listBox->setCurrentItem( _qt_listBox->item( item->index() ) );
127     }
128 }
129
130
131 void YQSelectionBox::selectItem( YItem * item, bool selected )
132 {
133     YQSignalBlocker sigBlocker( _qt_listBox );
134
135     YSelectionBox::selectItem( item, selected );
136     _qt_listBox->setCurrentRow( selected ? item->index() : -1 );
137 }
138
139
140 void YQSelectionBox::selectItem( int index )
141 {
142     YSelectionBox::deselectAllItems();
143     YItem * item = YSelectionBox::itemAt( index );
144
145     if ( item )
146     {
147 #ifdef VERBOSE_SELECTION
148         y2debug( "%s \"%s\": Selecting item \"%s\"",
149                  widgetClass(),
150                  debugLabel().c_str(),
151                  item->label().c_str() );
152 #endif
153
154         item->setSelected( true );
155     }
156     else
157         YUI_THROW( YUIException( "Can't find selected item" ) );
158 }
159
160
161 void YQSelectionBox::deselectAllItems()
162 {
163     YSelectionBox::deselectAllItems();
164     _qt_listBox->clearSelection();
165
166     if ( _qt_listBox->currentRow() > -1 )
167     {
168         // Some item is selected after all; the Qt documtation says this
169         // happens if the QListBox is in single selection mode (which it is)
170         // and has the keyboard focus.
171         //
172         // Synchronize internal "selected" flags with what the QListBox
173         // displays. This has a small performance penalty because it calls
174         // YSelectionBox::deselectAllItems() again which again iterates over
175         // all items.
176         selectItem( _qt_listBox->row(_qt_listBox->currentItem()) );
177     }
178 }
179
180
181 void YQSelectionBox::deleteAllItems()
182 {
183     YQSignalBlocker sigBlocker( _qt_listBox );
184
185     _qt_listBox->clear();
186     YSelectionBox::deleteAllItems();
187 }
188
189
190
191 int YQSelectionBox::preferredWidth()
192 {
193     int hintWidth = !_caption->isHidden() ?
194         _caption->sizeHint().width() + frameWidth() : 0;
195
196     return max( 80, hintWidth );
197 }
198
199
200 int YQSelectionBox::preferredHeight()
201 {
202     int hintHeight       = !_caption->isHidden() ? _caption->sizeHint().height() : 0;
203     int visibleLines     = shrinkable() ? SHRINKABLE_VISIBLE_LINES : DEFAULT_VISIBLE_LINES;
204     hintHeight          += visibleLines * _qt_listBox->fontMetrics().lineSpacing();
205     hintHeight          += _qt_listBox->frameWidth() * 2;
206
207     return max( 80, hintHeight );
208 }
209
210
211 void YQSelectionBox::setSize( int newWidth, int newHeight )
212 {
213     resize( newWidth, newHeight );
214 }
215
216
217 void YQSelectionBox::setEnabled( bool enabled )
218 {
219     _caption->setEnabled( enabled );
220     _qt_listBox->setEnabled( enabled );
221     //FIXME needed? _qt_listBox->triggerUpdate( true );
222     YWidget::setEnabled( enabled );
223 }
224
225
226 bool YQSelectionBox::setKeyboardFocus()
227 {
228     _qt_listBox->setFocus();
229
230     return true;
231 }
232
233
234 bool YQSelectionBox::eventFilter( QObject * obj, QEvent * ev )
235 {
236     if ( ev->type() == QEvent::KeyPress )
237     {
238         QKeyEvent * event = ( QKeyEvent * ) ev;
239
240         if ( ( event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter ) &&
241              ( (event->modifiers() & Qt::NoModifier) || (event->modifiers() & Qt::KeypadModifier) ) )
242         {
243             YQDialog * dia = (YQDialog *) findDialog();
244
245             if ( dia )
246             {
247                 ( void ) dia->activateDefaultButton();
248                 return true;
249             }
250         }
251     }
252     else if ( ev->type() == QEvent::MouseButtonRelease )
253     {
254         QMouseEvent * mouseEvent = dynamic_cast<QMouseEvent *> (ev);
255
256         if ( mouseEvent && mouseEvent->button() == Qt::RightButton )
257         {
258             y2milestone( "Right click in selecton box detected" );
259             YQUI::ui()->maybeLeftHandedUser();
260         }
261     }
262
263     return QWidget::eventFilter( obj, ev );
264 }
265
266
267 void YQSelectionBox::slotSelected( int index )
268 {
269     selectItem( index );
270
271     if ( notify() )
272     {
273         if ( immediateMode() )
274             returnImmediately();
275         else
276         {
277             if ( ! YQUI::ui()->eventsBlocked() )
278             {
279                 // Delayed event delivery - only if events are to be delivered
280                 // right now.
281                 //
282                 // An event block that is in effect right now may or may not
283                 // affect events after the timer delay is expired.
284                 //
285                 // This may create nasty side effects such as bug #32510: When
286                 // an item is initially selected, that initial selection event
287                 // gets through even though (!) events are blocked during
288                 // widget creation.
289
290                 returnDelayed();
291             }
292         }
293     }
294 }
295
296
297 void YQSelectionBox::slotActivated( QListWidgetItem * qItem )
298 {
299     selectItem( _qt_listBox->row( qItem ) );
300
301     if ( notify() )
302         YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::Activated ) );
303 }
304
305
306 void YQSelectionBox::returnImmediately()
307 {
308     if ( ! YQUI::ui()->eventPendingFor( this ) )
309     {
310         // Avoid overwriting a (more important) Activated event with a
311         // SelectionChanged event
312
313         y2debug( "sending selbox event" );
314         YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::SelectionChanged ) );
315     }
316 }
317
318
319 void YQSelectionBox::returnDelayed()
320 {
321     y2debug( "Starting selbox timer" );
322     _timer.setSingleShot( true );
323     _timer.start( 250 ); // millisec
324 }
325
326
327
328 #include "YQSelectionBox.moc"