picking up branches/tmp/sh/qt4-port/, merging it with trunk
[duncan/yast2-qt4.git] / src / QY2ListView.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       QY2ListView.cc
14
15   Author:     Stefan Hundhammer <sh@suse.de>
16
17   This is a pure Qt widget - it can be used independently of YaST2.
18
19 /-*/
20
21
22 #include <qpixmap.h>
23 #include <q3header.h>
24 #include <QMouseEvent>
25 #include "QY2ListView.h"
26
27
28 QY2ListView::QY2ListView( QWidget * parent )
29     : Q3ListView( parent )
30     , _mousePressedItem(0)
31     , _mousePressedCol( -1 )
32     , _mousePressedButton( Qt::NoButton )
33     , _sortByInsertionSequence( false )
34     , _nextSerial(0)
35     , _mouseButton1PressedInHeader( false )
36     , _finalSizeChangeExpected( false )
37 {
38     Q3ListView::setShowToolTips( false );
39
40 #if FIXME_TOOLTIP
41     _toolTip = new QY2ListViewToolTip( this );
42 #endif
43
44     if ( header() )
45         header()->installEventFilter( this );
46
47     connect( header(),  SIGNAL( sizeChange        ( int, int, int ) ),
48              this,      SLOT  ( columnWidthChanged( int, int, int ) ) );
49 }
50
51
52 QY2ListView::~QY2ListView()
53 {
54 #if FIXME_TOOLTIP
55     if ( _toolTip )
56         delete _toolTip;
57 #endif
58 }
59
60
61 void
62 QY2ListView::selectSomething()
63 {
64     Q3ListViewItemIterator it( this );
65
66     while ( *it )
67     {
68         QY2ListViewItem * item = dynamic_cast<QY2ListViewItem *> (*it);
69
70         if ( item && item->isSelectable() )
71         {
72             setSelected( item, true ); // emits signal, too
73             return;
74         }
75         
76         ++it;
77     }
78 }
79
80
81 void
82 QY2ListView::clear()
83 {
84     Q3ListView::clear();
85     restoreColumnWidths();
86 }
87
88
89 void
90 QY2ListView::updateItemStates()
91 {
92     Q3ListViewItemIterator it( this );
93
94     while ( *it )
95     {
96         QY2ListViewItem * item = dynamic_cast<QY2ListViewItem *> (*it);
97
98         if ( item )
99             item->updateStatus();
100         
101         ++it;
102     }
103 }
104
105
106 void
107 QY2ListView::updateItemData()
108 {
109     Q3ListViewItemIterator it( this );
110
111     while ( *it )
112     {
113         QY2ListViewItem * item = dynamic_cast<QY2ListViewItem *> (*it);
114
115         if ( item )
116             item->updateData();
117         
118         ++it;
119     }
120 }
121
122
123 QString
124 QY2ListView::toolTip( Q3ListViewItem * listViewItem, int column )
125 {
126     if ( ! listViewItem )
127         return QString::null;
128
129     QString text;
130
131 #if 0
132     text.sprintf( "Column %d:\n%s", column, (const char *) listViewItem->text( column ) );
133 #endif
134
135     // Try known item classes
136
137     QY2ListViewItem * item = dynamic_cast<QY2ListViewItem *> (listViewItem);
138
139     if ( item )
140         return item->toolTip( column );
141
142     QY2CheckListItem * checkListItem = dynamic_cast<QY2CheckListItem *> (listViewItem);
143
144     if ( checkListItem )
145         return checkListItem->toolTip( column );
146
147     return QString::null;
148 }
149
150
151 void
152 QY2ListView::saveColumnWidths()
153 {
154     _savedColumnWidth.clear();
155     _savedColumnWidth.reserve( columns() );
156
157     for ( int i = 0; i < columns(); i++ )
158     {
159         _savedColumnWidth.push_back( columnWidth(i) );
160     }
161 }
162
163
164 void
165 QY2ListView::restoreColumnWidths()
166 {
167     if ( _savedColumnWidth.size() != (unsigned) columns() )     // never manually resized
168     {
169         for ( int i = 0; i < columns(); i++ )           // use optimized column width
170             adjustColumn( i );
171     }
172     else                                                // stored settings after manual resizing
173     {
174         for ( int i = 0; i < columns(); i++ )
175         {
176             setColumnWidth( i, _savedColumnWidth[ i ] ); // restore saved column width
177         }
178     }
179 }
180
181
182 void
183 QY2ListView::contentsMousePressEvent( QMouseEvent * ev )
184 {
185     Q3ListViewItem * item = itemAt( contentsToViewport( ev->pos() ) );
186
187     if ( item && item->isEnabled() )
188     {
189         _mousePressedItem       = item;
190         _mousePressedCol        = header()->sectionAt( ev->pos().x() );
191         _mousePressedButton     = ev->button();
192     }
193     else        // invalidate last click data
194     {
195         _mousePressedItem       = 0;
196         _mousePressedCol        = -1;
197         _mousePressedButton     = -1;
198     }
199
200     // Call base class method
201     Q3ListView::contentsMousePressEvent( ev );
202 }
203
204
205 void
206 QY2ListView::contentsMouseReleaseEvent( QMouseEvent * ev )
207 {
208     Q3ListViewItem * item = itemAt( contentsToViewport( ev->pos() ) );
209
210     if ( item && item->isEnabled() && item == _mousePressedItem )
211     {
212         int col = header()->sectionAt( ev->pos().x() );
213
214         if ( item == _mousePressedItem  &&
215              col  == _mousePressedCol   &&
216              ev->button() == _mousePressedButton )
217         {
218             emit( columnClicked( ev->button(), item, col, ev->globalPos() ) );
219         }
220
221     }
222
223     // invalidate last click data
224
225     _mousePressedItem   = 0;
226     _mousePressedCol    = -1;
227     _mousePressedButton = Qt::NoButton;
228
229     // Call base class method
230     Q3ListView::contentsMouseReleaseEvent( ev );
231 }
232
233
234 void
235 QY2ListView::contentsMouseDoubleClickEvent( QMouseEvent * ev )
236 {
237     Q3ListViewItem * item = itemAt( contentsToViewport( ev->pos() ) );
238
239     if ( item && item->isEnabled() )
240     {
241         int col = header()->sectionAt( ev->pos().x() );
242         emit( columnDoubleClicked( ev->button(), (QY2ListViewItem *) item, col, ev->globalPos() ) );
243     }
244
245     // invalidate last click data
246
247     _mousePressedItem   = 0;
248     _mousePressedCol    = -1;
249     _mousePressedButton = Qt::NoButton;
250
251     // Call base class method
252     Q3ListView::contentsMouseDoubleClickEvent( ev );
253 }
254
255
256 void
257 QY2ListView::columnWidthChanged( int, int, int )
258 {
259     // Workaround for Qt bug:
260     //
261     // QHeader sends a sizeChange() signal for every size change, not only (as
262     // documented) when the user resizes a header column manually. But we only
263     // want to record the column widths if the user explicitly did that, so
264     // ignore those signals if the mouse isn't pressed. There is also one final
265     // sizeChange() signal immediately after the user releases the mouse button.
266
267     if ( _mouseButton1PressedInHeader || _finalSizeChangeExpected )
268     {
269         saveColumnWidths();
270
271         // Consume that one sizeChange() signal that is sent immediately after
272         // the mouse button is released, but make sure to reset that flag only
273         // when appropriate.
274
275         if ( ! _mouseButton1PressedInHeader )
276             _finalSizeChangeExpected = false;
277     }
278 }
279
280
281 bool
282 QY2ListView::eventFilter( QObject * obj, QEvent * event )
283 {
284     if ( event && obj && obj == header() )
285     {
286         if ( event->type() == QEvent::MouseButtonPress )
287         {
288             QMouseEvent * mouseEvent = (QMouseEvent *) event;
289
290             if ( mouseEvent->button() == 1 )
291             {
292                 _mouseButton1PressedInHeader = true;
293                 _finalSizeChangeExpected     = false;
294             }
295         }
296         else if ( event->type() == QEvent::MouseButtonRelease )
297         {
298             QMouseEvent * mouseEvent = (QMouseEvent *) event;
299
300             if ( mouseEvent->button() == 1 )
301             {
302                 _finalSizeChangeExpected     = true;
303                 _mouseButton1PressedInHeader = false;
304             }
305         }
306     }
307
308     return Q3ListView::eventFilter( obj, event );
309 }
310
311
312 QSize
313 QY2ListView::minimumSizeHint() const
314 {
315     return QSize( 0, 0 );
316 }
317
318
319 void
320 QY2ListView::setSortByInsertionSequence( bool sortByInsertionSequence )
321 {
322     _sortByInsertionSequence = sortByInsertionSequence;
323     sort();
324 }
325
326
327
328
329
330
331 QY2ListViewItem::QY2ListViewItem( QY2ListView *         parentListView,
332                                   const QString &       text )
333     : Q3ListViewItem( parentListView, text )
334 {
335     _serial = parentListView->nextSerial();
336 }
337
338
339 QY2ListViewItem::QY2ListViewItem( Q3ListViewItem *      parentItem,
340                                   const QString &       text )
341     : Q3ListViewItem( parentItem, text )
342 {
343     _serial = 0;
344
345     QY2ListView * parentListView = dynamic_cast<QY2ListView *> ( listView() );
346
347     if ( parentListView )
348         _serial = parentListView->nextSerial();
349 }
350
351
352 QY2ListViewItem::~QY2ListViewItem()
353 {
354     // NOP
355 }
356
357
358 /**
359  * Comparison function used for sorting the list.
360  * Returns:
361  * -1 if this <  other
362  *  0 if this == other
363  * +1 if this >  other
364  **/
365 int
366 QY2ListViewItem::compare( Q3ListViewItem *      otherListViewItem,
367                           int                   col,
368                           bool                  ascending ) const
369 {
370     bool sortByInsertionSequence = false;
371     QY2ListView * parentListView = dynamic_cast<QY2ListView *> (listView());
372
373     if ( parentListView )
374         sortByInsertionSequence = parentListView->sortByInsertionSequence();
375     
376     if ( sortByInsertionSequence )
377     {
378         QY2ListViewItem * other = dynamic_cast<QY2ListViewItem *> (otherListViewItem);
379
380         if ( other )
381         {
382             if ( this->serial() < other->serial() ) return -1;
383             if ( this->serial() > other->serial() ) return  1;
384             return 0;
385         }
386
387         // Still here? Try the other version: QY2CheckListItem.
388
389         QY2CheckListItem * otherCheckListItem = dynamic_cast<QY2CheckListItem *> (otherListViewItem);
390
391         if ( otherCheckListItem )
392         {
393             if ( this->serial() < otherCheckListItem->serial() ) return -1;
394             if ( this->serial() > otherCheckListItem->serial() ) return  1;
395             return 0;
396         }
397
398     }
399
400     return Q3ListViewItem::compare( otherListViewItem, col, ascending );
401 }
402
403
404 void
405 QY2ListViewItem::paintCell( QPainter *          painter,
406                             const QColorGroup & colorGroup,
407                             int                 column,
408                             int                 width,
409                             int                 alignment )
410 {
411     QColorGroup cg = colorGroup;
412
413     if ( _textColor.isValid() )         cg.setColor( QColorGroup::Text, _textColor );
414     if ( _backgroundColor.isValid() )   cg.setColor( QColorGroup::Base, _backgroundColor );
415
416     Q3ListViewItem::paintCell( painter, cg, column, width, alignment );
417 }
418
419
420
421
422
423
424 QY2CheckListItem::QY2CheckListItem( QY2ListView *               parentListView,
425                                     const QString &             text,
426                                     Q3CheckListItem::Type       type )
427     : Q3CheckListItem( parentListView, text, type )
428 {
429     _serial = parentListView->nextSerial();
430 }
431
432
433 QY2CheckListItem::QY2CheckListItem( Q3ListViewItem *            parentItem,
434                                     const QString &             text,
435                                     Q3CheckListItem::Type       type )
436     : Q3CheckListItem( parentItem, text, type )
437 {
438     _serial = 0;
439     QY2ListView * parentListView = dynamic_cast<QY2ListView *> ( listView() );
440
441     if ( parentListView )
442         _serial = parentListView->nextSerial();
443 }
444
445
446 QY2CheckListItem::QY2CheckListItem( Q3CheckListItem *           parentItem,
447                                     const QString &             text,
448                                     Q3CheckListItem::Type       type )
449     : Q3CheckListItem( parentItem, text, type )
450 {
451     _serial = 0;
452     QY2ListView * parentListView = dynamic_cast<QY2ListView *> ( listView() );
453
454     if ( parentListView )
455         _serial = parentListView->nextSerial();
456 }
457
458
459 QY2CheckListItem::~QY2CheckListItem()
460 {
461     // NOP
462 }
463
464
465 /**
466  * Comparison function used for sorting the list.
467  * Returns:
468  * -1 if this <  other
469  *  0 if this == other
470  * +1 if this >  other
471  **/
472 int
473 QY2CheckListItem::compare( Q3ListViewItem *     otherListViewItem,
474                            int                  col,
475                            bool                 ascending ) const
476 {
477     bool sortByInsertionSequence = false;
478     QY2ListView * parentListView = dynamic_cast<QY2ListView *> (listView());
479
480     if ( parentListView )
481         sortByInsertionSequence = parentListView->sortByInsertionSequence();
482     
483     if ( sortByInsertionSequence )
484     {
485         QY2CheckListItem * other = dynamic_cast<QY2CheckListItem *> (otherListViewItem);
486
487         if ( other )
488         {
489             if ( this->serial() < other->serial() ) return -1;
490             if ( this->serial() > other->serial() ) return  1;
491             return 0;
492         }
493
494
495         // Still here? Try the other version: QY2ListViewItem.
496
497         QY2ListViewItem * otherCheckListItem = dynamic_cast<QY2ListViewItem *> (otherListViewItem);
498
499         if ( otherCheckListItem )
500         {
501             if ( this->serial() < otherCheckListItem->serial() ) return -1;
502             if ( this->serial() > otherCheckListItem->serial() ) return  1;
503             return 0;
504         }
505
506     }
507
508     return Q3ListViewItem::compare( otherListViewItem, col, ascending );
509 }
510
511
512 void
513 QY2CheckListItem::paintCell( QPainter *                 painter,
514                              const QColorGroup &        colorGroup,
515                              int                        column,
516                              int                        width,
517                              int                        alignment )
518 {
519     QColorGroup cg = colorGroup;
520
521     if ( _textColor.isValid() )         cg.setColor( QColorGroup::Text, _textColor );
522     if ( _backgroundColor.isValid() )   cg.setColor( QColorGroup::Base, _backgroundColor );
523
524     Q3CheckListItem::paintCell( painter, cg, column, width, alignment );
525 }
526
527
528 #if FIXME_TOOLTIP
529 void
530 QY2ListViewToolTip::maybeTip( const QPoint & pos )
531 {
532     Q3Header *       header        = _listView->header();
533     Q3ListViewItem * item          = _listView->itemAt( pos );
534
535     if ( ! item )
536         return;
537
538     int x      = _listView->viewportToContents( pos ).x();
539     int column = header->sectionAt(x);
540     int indent = 0;
541
542     if ( column == 0 )
543     {
544         indent  =  item->depth() + ( _listView->rootIsDecorated() ? 1 : 0 );
545         indent *=  _listView->treeStepSize();
546
547         if ( pos.x() < indent )
548             column = -1;
549     }
550
551     QString text = _listView->toolTip( item, column );
552
553     if ( ! text.isEmpty() )
554     {
555         QRect rect( _listView->itemRect( item ) );
556
557         if ( column < 0 )
558         {
559             rect.setX(0);
560             rect.setWidth( indent );
561         }
562         else
563         {
564             QPoint topLeft( header->sectionPos( column ), 0 );
565             topLeft = _listView->contentsToViewport( topLeft );
566             rect.setX( topLeft.x() );
567             rect.setWidth( header->sectionSize( column ) );
568         }
569
570         tip( rect, text );
571     }
572 }
573
574 #endif
575
576
577 #include "QY2ListView.moc"