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