]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/pkg/YQPkgObjList.cc
don't create QObjects in ycp thread - make timeouts work
[duncan/yast2-qt4.git] / src / pkg / YQPkgObjList.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       YQPkgObjList.cc
14
15   Author:     Stefan Hundhammer <sh@suse.de>
16
17   Textdomain "packages-qt"
18
19 /-*/
20
21 #define y2log_component "qt-pkg"
22 #include <ycp/y2log.h>
23 #include <QPixmap>
24 #include <QHeaderView>
25 #include <QMenu>
26 #include <QAction>
27 //Added by qt3to4:
28 #include <qevent.h>
29
30 #include "utf8.h"
31
32 #include "YQPkgObjList.h"
33 #include "YQPkgTextDialog.h"
34 #include "YQi18n.h"
35 #include "YQIconPool.h"
36 #include "YQUI.h"
37
38 using std::list;
39 using std::string;
40
41
42 #define VERBOSE_EXCLUDE_RULES   0
43 #define EXTRA_SOLVE_COLLECTIONS 0
44
45
46 YQPkgObjList::YQPkgObjList( QWidget * parent )
47     : QY2ListView( parent )
48     , _editable( true )
49     , _installedContextMenu(0)
50     , _notInstalledContextMenu(0),
51     actionSetCurrentInstall(0L),
52     actionSetCurrentDontInstall(0L),
53     actionSetCurrentKeepInstalled(0L),
54     actionSetCurrentDelete(0L),
55     actionSetCurrentUpdate(0L),
56     actionSetCurrentTaboo(0L),
57     actionSetCurrentProtected(0L),
58     actionSetListInstall(0L),
59     actionSetListDontInstall(0L),
60     actionSetListKeepInstalled(0L),
61     actionSetListDelete(0L),
62     actionSetListUpdate(0L),
63     actionSetListUpdateForce(0L),
64     actionSetListTaboo(0L),
65     actionSetListProtected(0L)
66 {
67     // This class does not add any columns. This is the main reason why this is
68     // an abstract base class: It doesn't know which columns are desired and in
69     // what order.
70
71     _statusCol          = -42;
72     _nameCol            = -42;
73     _versionCol         = -42;
74     _instVersionCol     = -42;
75     _summaryCol         = -42;
76     _sizeCol            = -42;
77     _brokenIconCol      = -42;
78     _satisfiedIconCol   = -42;
79     _debug              = false;
80
81     _excludedItems = new YQPkgObjList::ExcludedItems( this );
82
83     createActions();
84
85     connect( this,      SIGNAL( columnClicked           ( int, QTreeWidgetItem *, int, const QPoint & ) ),
86              this,      SLOT  ( pkgObjClicked           ( int, QTreeWidgetItem *, int, const QPoint & ) ) );
87
88     connect( this,      SIGNAL( columnDoubleClicked     ( int, QTreeWidgetItem *, int, const QPoint & ) ),
89              this,      SLOT  ( pkgObjClicked           ( int, QTreeWidgetItem *, int, const QPoint & ) ) );
90
91     connect( this,      SIGNAL( currentItemChanged      ( QTreeWidgetItem *, QTreeWidgetItem * ) ),
92              this,      SLOT  ( currentItemChangedInternal( QTreeWidgetItem * ) ) );
93 }
94
95
96 YQPkgObjList::~YQPkgObjList()
97 {
98     if ( _excludedItems )
99         delete _excludedItems;
100 }
101
102
103 void
104 YQPkgObjList::addPkgObjItem( ZyppSel selectable, ZyppObj zyppObj )
105 {
106     if ( ! selectable )
107     {
108         y2error( "Null zypp::ui::Selectable!" );
109         return;
110     }
111
112     YQPkgObjListItem * item = new YQPkgObjListItem( this, selectable, zyppObj );
113     applyExcludeRules( item );
114 }
115
116
117 void
118 YQPkgObjList::addPassiveItem( const QString &   name,
119                               const QString &   summary,
120                               FSize             size )
121 {
122     QY2ListViewItem * item = new QY2ListViewItem( this, QString::null );
123
124     if ( item )
125     {
126         if ( nameCol()    >= 0 && ! name.isEmpty()    ) item->setText( nameCol(),       name    );
127         if ( summaryCol() >= 0 && ! summary.isEmpty() ) item->setText( summaryCol(),    summary );
128         if ( sizeCol()    >= 0 && size > 0L           )
129         {
130             QString sizeStr = size.form().c_str();
131             sizeStr += "  ";
132             item->setText( sizeCol(), sizeStr );
133         }
134     }
135 }
136
137
138 void
139 YQPkgObjList::pkgObjClicked( int                button,
140                              QTreeWidgetItem *  listViewItem,
141                              int                col,
142                              const QPoint &     pos )
143 {
144     YQPkgObjListItem * item = dynamic_cast<YQPkgObjListItem *> (listViewItem);
145
146     if ( item )
147     {
148         if ( button == Qt::LeftButton )
149         {
150             if ( col == statusCol() )
151                 // || col == nameCol() )
152             {
153                 if ( editable() && item->editable() )
154                     item->cycleStatus();
155             }
156         }
157         else if ( button == Qt::RightButton )
158         {
159             if ( editable() && item->editable() )
160             {
161                 updateActions( item );
162
163                 QMenu * contextMenu =
164                     item->selectable()->hasInstalledObj() ?
165                     installedContextMenu() : notInstalledContextMenu();
166
167                 if ( contextMenu )
168                     contextMenu->popup( pos );
169             }
170         }
171     }
172 }
173
174
175 void
176 YQPkgObjList::currentItemChangedInternal( QTreeWidgetItem * listViewItem )
177 {
178     YQPkgObjListItem * item = dynamic_cast<YQPkgObjListItem *> (listViewItem);
179
180     emit currentItemChanged( item ? item->selectable() : ZyppSel() );
181 }
182
183
184 void
185 YQPkgObjList::clear()
186 {
187     emit currentItemChanged( ZyppSel() );
188
189     _excludedItems->clear();
190     QY2ListView::clear();
191 }
192
193
194 QPixmap
195 YQPkgObjList::statusIcon( ZyppStatus status, bool enabled, bool bySelection )
196 {
197     QPixmap icon = YQIconPool::pkgNoInst();
198
199     if ( enabled )
200     {
201         switch ( status )
202         {
203             case S_Del:                 icon = YQIconPool::pkgDel();            break;
204             case S_Install:             icon = YQIconPool::pkgInstall();        break;
205             case S_KeepInstalled:       icon = YQIconPool::pkgKeepInstalled();  break;
206             case S_NoInst:              icon = YQIconPool::pkgNoInst();         break;
207             case S_Protected:           icon = YQIconPool::pkgProtected();      break;
208             case S_Taboo:               icon = YQIconPool::pkgTaboo();          break;
209             case S_Update:              icon = YQIconPool::pkgUpdate();         break;
210
211             case S_AutoDel:             icon = bySelection ?
212                                             YQIconPool::pkgSelAutoDel() :
213                                             YQIconPool::pkgAutoDel();           break;
214
215             case S_AutoInstall:         icon = bySelection ?
216                                             YQIconPool::pkgSelAutoInstall() :
217                                             YQIconPool::pkgAutoInstall();       break;
218
219             case S_AutoUpdate:          icon = bySelection ?
220                                             YQIconPool::pkgSelAutoUpdate() :
221                                              YQIconPool::pkgAutoUpdate();       break;
222
223
224                 // Intentionally omitting 'default' branch so the compiler can
225                 // catch unhandled enum states
226         }
227     }
228     else
229     {
230         switch ( status )
231         {
232             case S_Del:                 icon = YQIconPool::disabledPkgDel();            break;
233             case S_Install:             icon = YQIconPool::disabledPkgInstall();        break;
234             case S_KeepInstalled:       icon = YQIconPool::disabledPkgKeepInstalled();  break;
235             case S_NoInst:              icon = YQIconPool::disabledPkgNoInst();         break;
236             case S_Protected:           icon = YQIconPool::disabledPkgProtected();      break;
237             case S_Taboo:               icon = YQIconPool::disabledPkgTaboo();          break;
238             case S_Update:              icon = YQIconPool::disabledPkgUpdate();         break;
239
240             case S_AutoDel:             icon = bySelection ?
241                                             YQIconPool::disabledPkgSelAutoDel() :
242                                             YQIconPool::disabledPkgAutoDel();           break;
243
244             case S_AutoInstall:         icon = bySelection ?
245                                             YQIconPool::disabledPkgSelAutoInstall() :
246                                             YQIconPool::disabledPkgAutoInstall();       break;
247
248             case S_AutoUpdate:          icon = bySelection ?
249                                             YQIconPool::disabledPkgSelAutoUpdate() :
250                                             YQIconPool::disabledPkgAutoUpdate();        break;
251
252                 // Intentionally omitting 'default' branch so the compiler can
253                 // catch unhandled enum states
254         }
255     }
256
257     return icon;
258 }
259
260
261 QString
262 YQPkgObjList::statusText( ZyppStatus status ) const
263 {
264     switch ( status )
265     {
266         case S_AutoDel:         return _( "Autodelete"                  );
267         case S_AutoInstall:     return _( "Autoinstall"                 );
268         case S_AutoUpdate:      return _( "Autoupdate"                  );
269         case S_Del:             return _( "Delete"                      );
270         case S_Install:         return _( "Install"                     );
271         case S_KeepInstalled:   return _( "Keep"                        );
272         case S_NoInst:          return _( "Do Not Install"              );
273         case S_Protected:       return _( "Protected -- Do Not Modify"  );
274         case S_Taboo:           return _( "Taboo -- Never Install"      );
275         case S_Update:          return _( "Update"                      );
276     }
277
278     return QString::null;
279 }
280
281
282 void
283 YQPkgObjList::setCurrentStatus( ZyppStatus newStatus, bool doSelectNextItem )
284 {
285     QTreeWidgetItem * listViewItem = currentItem();
286
287     if ( ! listViewItem )
288         return;
289
290     YQPkgObjListItem * item = dynamic_cast<YQPkgObjListItem *> (listViewItem);
291
292     if ( item && item->editable() && _editable )
293     {
294         if ( newStatus != item->status() )
295         {
296             item->setStatus( newStatus );
297
298             if ( item->showLicenseAgreement() )
299             {
300                 item->showNotifyTexts( newStatus );
301             }
302             else // License not confirmed?
303             {
304                 // Status is now S_Taboo or S_Del - update status icon
305                 item->setStatusIcon();
306             }
307
308             emit statusChanged();
309         }
310     }
311
312     if ( doSelectNextItem )
313         selectNextItem();
314 }
315
316
317 void
318 YQPkgObjList::setAllItemStatus( ZyppStatus newStatus, bool force )
319 {
320     if ( ! _editable )
321         return;
322
323     YQUI::ui()->busyCursor();
324     QTreeWidgetItemIterator it( this );
325
326     while ( *it )
327     {
328         YQPkgObjListItem * item = dynamic_cast<YQPkgObjListItem *> (*it);
329
330         if ( item && item->editable() && newStatus != item->status() )
331         {
332             if ( newStatus == S_Update )
333             {
334                 if ( item->candidateIsNewer() || force )
335                     item->setStatus( newStatus,
336                                      false );   // sendSignals
337             }
338             else
339             {
340                 item->setStatus( newStatus,
341                                  false );       // sendSignals
342             }
343         }
344
345         ++it;
346     }
347
348     emit updateItemStates();
349     emit updatePackages();
350
351     YQUI::ui()->normalCursor();
352     emit statusChanged();
353 }
354
355
356 void
357 YQPkgObjList::selectNextItem()
358 {
359     QTreeWidgetItemIterator it(this);
360     QTreeWidgetItem * item;
361
362     while ( item = *it)
363     {
364         ++it;
365         //item->setSelected( false );                   // Doesn't emit signals
366         scrollToItem( *it );    // Scroll if necessary
367         setCurrentItem( *it );  // Emits signals
368         
369     }
370 }
371
372
373 void
374 YQPkgObjList::createActions()
375 {
376     actionSetCurrentInstall             = createAction( S_Install,              "[+]"           );
377     actionSetCurrentDontInstall         = createAction( S_NoInst,               "[-]"           );
378     actionSetCurrentKeepInstalled       = createAction( S_KeepInstalled,        "[<], [-]"      );
379     actionSetCurrentDelete              = createAction( S_Del,                  "[-]"           );
380     actionSetCurrentUpdate              = createAction( S_Update,               "[>], [+]"      );
381     actionSetCurrentTaboo               = createAction( S_Taboo,                "[!]"           );
382     actionSetCurrentProtected           = createAction( S_Protected,            "[*]"           );
383
384     actionSetListInstall                = createAction( S_Install,              "", true );
385     actionSetListDontInstall            = createAction( S_NoInst,               "", true );
386     actionSetListKeepInstalled          = createAction( S_KeepInstalled,        "", true );
387     actionSetListDelete                 = createAction( S_Del,                  "", true );
388     actionSetListProtected              = createAction( S_Protected,            "", true );
389
390     actionSetListUpdate                 = createAction( _( "Update if newer version available" ),
391                                                         statusIcon( S_Update, true ),
392                                                         statusIcon( S_Update, false ),
393                                                         "",
394                                                         true );
395
396     actionSetListUpdateForce            = createAction( _( "Update unconditionally" ),
397                                                         statusIcon( S_Update, true ),
398                                                         statusIcon( S_Update, false ),
399                                                         "",
400                                                         true );
401
402     actionSetListTaboo                  = createAction( S_Taboo,                "", true );
403
404     connect( actionSetCurrentInstall,        SIGNAL( activated() ), this, SLOT( setCurrentInstall()       ) );
405     connect( actionSetCurrentDontInstall,    SIGNAL( activated() ), this, SLOT( setCurrentDontInstall()   ) );
406     connect( actionSetCurrentKeepInstalled,  SIGNAL( activated() ), this, SLOT( setCurrentKeepInstalled() ) );
407     connect( actionSetCurrentDelete,         SIGNAL( activated() ), this, SLOT( setCurrentDelete()        ) );
408     connect( actionSetCurrentUpdate,         SIGNAL( activated() ), this, SLOT( setCurrentUpdate()        ) );
409     connect( actionSetCurrentTaboo,          SIGNAL( activated() ), this, SLOT( setCurrentTaboo()         ) );
410     connect( actionSetCurrentProtected,      SIGNAL( activated() ), this, SLOT( setCurrentProtected()     ) );
411
412     connect( actionSetListInstall,           SIGNAL( activated() ), this, SLOT( setListInstall()          ) );
413     connect( actionSetListDontInstall,       SIGNAL( activated() ), this, SLOT( setListDontInstall()      ) );
414     connect( actionSetListKeepInstalled,     SIGNAL( activated() ), this, SLOT( setListKeepInstalled()    ) );
415     connect( actionSetListDelete,            SIGNAL( activated() ), this, SLOT( setListDelete()           ) );
416     connect( actionSetListUpdate,            SIGNAL( activated() ), this, SLOT( setListUpdate()           ) );
417     connect( actionSetListUpdateForce,       SIGNAL( activated() ), this, SLOT( setListUpdateForce()      ) );
418     connect( actionSetListTaboo,             SIGNAL( activated() ), this, SLOT( setListTaboo()            ) );
419     connect( actionSetListProtected,         SIGNAL( activated() ), this, SLOT( setListProtected()        ) );
420 }
421
422
423
424 QAction *
425 YQPkgObjList::createAction( ZyppStatus status, const QString & key, bool enabled )
426 {
427     return createAction( statusText( status ),
428                          statusIcon( status, true ),
429                          statusIcon( status, false ),
430                          key,
431                          enabled );
432 }
433
434
435 QAction *
436 YQPkgObjList::createAction( const QString &     text,
437                             const QPixmap &     icon,
438                             const QPixmap &     insensitiveIcon,
439                             const QString &     key,
440                             bool                enabled )
441 {
442     QString label = text;
443
444     if ( ! key.isEmpty() )
445         label += "\t" + key;
446
447
448     QIcon iconSet ( icon );
449
450     if ( ! insensitiveIcon.isNull() )
451     {
452         iconSet.addPixmap( insensitiveIcon,
453                            QIcon::Disabled );
454     }
455
456     QAction * action = new QAction( label,      // text
457                                     this );     // parent
458     Q_CHECK_PTR( action );
459     action->setEnabled( enabled );
460     action->setIcon( iconSet );
461
462     return action;
463 }
464
465
466 void
467 YQPkgObjList::createNotInstalledContextMenu()
468 {
469     _notInstalledContextMenu = new QMenu( this );
470     Q_CHECK_PTR( _notInstalledContextMenu );
471
472     _notInstalledContextMenu->addAction(actionSetCurrentInstall);
473     _notInstalledContextMenu->addAction(actionSetCurrentDontInstall);
474     _notInstalledContextMenu->addAction(actionSetCurrentTaboo);
475
476     addAllInListSubMenu( _notInstalledContextMenu );
477 }
478
479
480 void
481 YQPkgObjList::createInstalledContextMenu()
482 {
483     _installedContextMenu = new QMenu( this );
484     Q_CHECK_PTR( _installedContextMenu );
485
486     _installedContextMenu->addAction(actionSetCurrentKeepInstalled);
487     _installedContextMenu->addAction(actionSetCurrentDelete);
488     _installedContextMenu->addAction(actionSetCurrentUpdate);
489
490     addAllInListSubMenu( _installedContextMenu );
491 }
492
493
494 QMenu *
495 YQPkgObjList::addAllInListSubMenu( QMenu * menu )
496 {
497     QMenu * submenu = new QMenu( menu );
498     Q_CHECK_PTR( submenu );
499
500     submenu->addAction(actionSetListInstall);
501     submenu->addAction(actionSetListDontInstall);
502     submenu->addAction(actionSetListKeepInstalled);
503     submenu->addAction(actionSetListDelete);
504     submenu->addAction(actionSetListUpdate);
505     submenu->addAction(actionSetListUpdateForce);
506     submenu->addAction(actionSetListTaboo);
507
508     QAction *action = menu->addMenu( submenu );
509     action->setText(_( "&All in This List" ));
510
511     return submenu;
512 }
513
514
515 QMenu *
516 YQPkgObjList::notInstalledContextMenu()
517 {
518     if ( ! _notInstalledContextMenu )
519         createNotInstalledContextMenu();
520
521     return _notInstalledContextMenu;
522 }
523
524
525 QMenu *
526 YQPkgObjList::installedContextMenu()
527 {
528     if ( ! _installedContextMenu )
529         createInstalledContextMenu();
530
531     return _installedContextMenu;
532 }
533
534
535 void
536 YQPkgObjList::updateActions( YQPkgObjListItem * item )
537 {
538     if ( !item)
539       item = dynamic_cast<YQPkgObjListItem *> ( currentItem() );
540
541     if ( item )
542     {
543         ZyppSel selectable = item->selectable();
544
545         if ( selectable->hasInstalledObj() )
546         {
547             actionSetCurrentInstall->setEnabled( false );
548             actionSetCurrentDontInstall->setEnabled( false );
549             actionSetCurrentTaboo->setEnabled( false );
550             actionSetCurrentProtected->setEnabled( true );
551
552             actionSetCurrentKeepInstalled->setEnabled( true );
553             actionSetCurrentDelete->setEnabled( true );
554             actionSetCurrentUpdate->setEnabled( selectable->hasCandidateObj() );
555         }
556         else
557         {
558             actionSetCurrentInstall->setEnabled( selectable->hasCandidateObj() );
559             actionSetCurrentDontInstall->setEnabled( true );
560             actionSetCurrentTaboo->setEnabled( true );
561             actionSetCurrentProtected->setEnabled( false );
562
563             actionSetCurrentKeepInstalled->setEnabled( false );
564             actionSetCurrentDelete->setEnabled( false );
565             actionSetCurrentUpdate->setEnabled( false );
566         }
567     }
568     else        // ! item
569     {
570         actionSetCurrentInstall->setEnabled( false );
571         actionSetCurrentDontInstall->setEnabled( false );
572         actionSetCurrentTaboo->setEnabled( false );
573
574         actionSetCurrentKeepInstalled->setEnabled( false );
575         actionSetCurrentDelete->setEnabled( false );
576         actionSetCurrentUpdate->setEnabled( false );
577         actionSetCurrentProtected->setEnabled( false );
578     }
579 }
580
581
582 void
583 YQPkgObjList::keyPressEvent( QKeyEvent * event )
584 {
585     if ( event )
586     {
587         int special_combo = ( Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier );
588
589         if ( ( event->modifiers() & special_combo ) == special_combo )
590         {
591             if ( event->key() == Qt::Key_Q )
592             {
593                 _debug= ! _debug;
594                 y2milestone( "Debug mode %s", _debug ? "on" : "off" );
595             }
596
597         }
598         QTreeWidgetItem * selectedListViewItem = currentItem();
599
600         if ( selectedListViewItem )
601         {
602             YQPkgObjListItem * item = dynamic_cast<YQPkgObjListItem *> (selectedListViewItem);
603
604             if ( item )
605             {
606                 bool installed = item->selectable()->hasInstalledObj();
607                 ZyppStatus status = item->status();
608
609                 switch( event->key() )
610                 {
611                     case Qt::Key_Space:         // Cycle
612                         item->cycleStatus();
613                         event->accept();
614                         return;
615
616                     case Qt::Key_Plus:  // Grab everything - install or update
617
618                         if ( installed )
619                         {
620                             ZyppStatus newStatus = S_KeepInstalled;
621
622                             if ( item->candidateIsNewer() )
623                                 newStatus = S_Update;
624
625                             setCurrentStatus( newStatus );
626                         }
627                         else
628                             setCurrentStatus( S_Install );
629                         selectNextItem();
630                         event->accept();
631                         return;
632
633                     case Qt::Key_Minus: // Get rid of everything - don't install or delete
634                         setCurrentStatus( installed ? S_Del : S_NoInst );
635                         selectNextItem();
636                         event->accept();
637                         return;
638
639                     case Qt::Key_Exclam:        // Taboo
640
641                         if ( ! installed )
642                             setCurrentStatus( S_Taboo );
643                         selectNextItem();
644                         event->accept();
645                         return;
646
647                     case Qt::Key_Asterisk:      // Protected
648
649                         if ( installed )
650                             setCurrentStatus( S_Protected );
651                         selectNextItem();
652                         event->accept();
653                         return;
654
655                     case Qt::Key_Greater:       // Update what is worth to be updated
656
657                         if ( installed && item->candidateIsNewer() )
658                             setCurrentStatus( S_Update );
659                         selectNextItem();
660                         event->accept();
661                         return;
662
663                     case Qt::Key_Less:  // Revert update
664
665                         if ( status == S_Update ||
666                              status == S_AutoUpdate )
667                         {
668                             setCurrentStatus( S_KeepInstalled );
669                         }
670                         selectNextItem();
671                         event->accept();
672                         return;
673
674                     case Qt::Key_B:     // Toggle debugIsBroken flag
675
676                         if ( _debug )
677                         {
678                             item->toggleDebugIsBroken();
679                             item->setStatusIcon();
680                         }
681                         event->accept();
682                         break;
683
684                     case Qt::Key_S:     // Toggle debugIsSatisfied flag
685
686                         if ( _debug )
687                         {
688                             item->toggleDebugIsSatisfied();
689                             item->setStatusIcon();
690                         }
691                         event->accept();
692                         break;
693                 }
694             }
695         }
696     }
697     QY2ListView::keyPressEvent( event );
698 }
699
700
701 void
702 YQPkgObjList::message( const QString & text )
703 {
704     QY2ListViewItem * item = new QY2ListViewItem( this );
705     Q_CHECK_PTR( item );
706
707     item->setText( nameCol() >= 0 ? nameCol() : 0, text );
708     item->setBackgroundColor( QColor( 0xE0, 0xE0, 0xF8 ) );
709 }
710
711
712 void
713 YQPkgObjList::addExcludeRule( YQPkgObjList::ExcludeRule * rule )
714 {
715     _excludeRules.push_back( rule );
716 }
717
718
719 void
720 YQPkgObjList::applyExcludeRules()
721 {
722     // y2debug( "Applying exclude rules" );
723     QTreeWidgetItemIterator listView_it( this );
724
725     while ( *listView_it )
726     {
727         QTreeWidgetItem * current_item = *listView_it;
728
729         // Advance iterator now so it remains valid even if there are changes
730         // to the QListView, e.g., if the current item is excluded and thus
731         // removed from the QListView
732         ++listView_it;
733
734         applyExcludeRules( current_item );
735     }
736
737     ExcludedItems::iterator excluded_it = _excludedItems->begin();
738
739     while ( excluded_it != _excludedItems->end() )
740     {
741         QTreeWidgetItem * current_item = (*excluded_it).first;
742
743         // Advance iterator now so it remains valid even if there are changes
744         // to the excluded items, e.g., if the current item is un-excluded and thus
745         // removed from the excluded items
746         ++excluded_it;
747
748         applyExcludeRules( current_item );
749     }
750
751     logExcludeStatistics();
752 }
753
754
755 void
756 YQPkgObjList::logExcludeStatistics()
757 {
758     if ( _excludedItems->size() > 0 )
759     {
760         y2milestone( "%d packages excluded", _excludedItems->size() );
761
762         for ( ExcludeRuleList::iterator rule_it = _excludeRules.begin();
763               rule_it != _excludeRules.end();
764               ++rule_it )
765         {
766             ExcludeRule * rule = *rule_it;
767
768             if ( rule->isEnabled() )
769             {
770                 y2milestone( "Active exclude rule: \"%s\"",
771                              qPrintable(rule->regexp().pattern()) );
772             }
773         }
774     }
775 }
776
777
778 void
779 YQPkgObjList::applyExcludeRules( QTreeWidgetItem * listViewItem )
780 {
781     YQPkgObjListItem * item = dynamic_cast<YQPkgObjListItem *>( listViewItem );
782
783     if ( item )
784     {
785         bool exclude = false;
786         ExcludeRule * matchingRule = 0;
787
788         for ( ExcludeRuleList::iterator rule_it = _excludeRules.begin();
789               rule_it != _excludeRules.end() && ! exclude;
790               ++rule_it )
791         {
792             ExcludeRule * rule = *rule_it;
793
794             if ( rule->match( item ) )
795             {
796                 exclude = true;
797                 matchingRule = rule;
798             }
799         }
800
801         if ( exclude != item->isExcluded() )    // change exclude status?
802         {
803             this->exclude( item, exclude );
804
805 #if VERBOSE_EXCLUDE_RULES
806             if ( exclude )
807             {
808                 y2debug( "Rule %s matches: Excluding %s",
809                          matchingRule->regexp().pattern().ascii(),
810                          item->zyppObj()->name().c_str() );
811             }
812             else
813             {
814                 y2debug( "Un-excluding %s", item->zyppObj()->name().c_str() );
815             }
816 #endif
817         }
818     }
819 }
820
821
822 void
823 YQPkgObjList::exclude( YQPkgObjListItem * item, bool exclude )
824 {
825 #if FIXME
826     if ( exclude == item->isExcluded() )
827         return;
828
829     item->setExcluded( exclude );
830
831     if ( exclude )
832     {
833         QTreeWidgetItem * parentItem = item->parent();
834
835         if ( parentItem )
836             parentItem->takeItem( item );
837         else
838             QTreeWidget::takeItem( item );
839
840         _excludedItems->add( item, parentItem );
841     }
842     else // un-exclude
843     {
844         if ( _excludedItems->contains( item ) )
845         {
846             QTreeWidgetItem * oldParent = _excludedItems->oldParentItem( item );
847             _excludedItems->remove( item );
848
849             if ( oldParent )
850                 oldParent->insertItem( item );
851             else
852                 QTreeWidget::insertItem( item );
853         }
854     }
855 #endif
856 }
857
858
859
860
861 YQPkgObjListItem::YQPkgObjListItem( YQPkgObjList * pkgObjList,
862                                     ZyppSel selectable,
863                                     ZyppObj zyppObj )
864     : QY2ListViewItem( pkgObjList )
865     , _pkgObjList( pkgObjList )
866     , _selectable( selectable )
867     , _zyppObj( zyppObj )
868     , _editable( true )
869     , _excluded( false )
870 {
871     init();
872 }
873
874
875 YQPkgObjListItem::YQPkgObjListItem( YQPkgObjList *      pkgObjList,
876                                     QY2ListViewItem *   parent,
877                                     ZyppSel             selectable,
878                                     ZyppObj             zyppObj )
879     : QY2ListViewItem( parent )
880     , _pkgObjList( pkgObjList )
881     , _selectable( selectable )
882     , _zyppObj( zyppObj )
883     , _editable( true )
884     , _excluded( false )
885 {
886     init();
887 }
888
889
890 YQPkgObjListItem::~YQPkgObjListItem()
891 {
892     // NOP
893 }
894
895
896 void
897 YQPkgObjListItem::init()
898 {
899     if ( _zyppObj == 0 && _selectable )
900         _zyppObj = _selectable->theObj();
901
902     _debugIsBroken      = false;
903     _debugIsSatisfied   = false;
904     _candidateIsNewer   = false;
905     _installedIsNewer   = false;
906
907     const ZyppObj candidate = selectable()->candidateObj();
908     const ZyppObj installed = selectable()->installedObj();
909
910     if ( candidate && installed )
911     {
912         if ( candidate->edition() < installed->edition() )
913             _installedIsNewer = true;
914         else if ( installed->edition() < candidate->edition() )
915             _candidateIsNewer = true;
916     }
917
918     if ( nameCol()    >= 0 )    setText( nameCol(),     zyppObj()->name()       );
919     if ( summaryCol() >= 0 )    setText( summaryCol(),  zyppObj()->summary()    );
920
921     if ( sizeCol()    >= 0 )
922     {
923         zypp::ByteCount size = zyppObj()->size();
924
925         if ( size > 0L )
926             setText( sizeCol(), size.asString() + "  " );
927     }
928
929     if ( instVersionCol() >= 0 )
930     {
931         if ( selectable()->hasInstalledObj() )
932              setText( instVersionCol(), installed->edition() );
933
934         if ( zyppObj() != selectable()->installedObj() &&
935              zyppObj() != selectable()->candidateObj()   )
936         {
937             setText( versionCol(), zyppObj()->edition() );
938         }
939         else if ( selectable()->hasCandidateObj() )
940         {
941             setText( versionCol(), candidate->edition() );
942         }
943     }
944     else
945     {
946         setText( versionCol(),  zyppObj()->edition() );
947     }
948
949     setStatusIcon();
950 }
951
952
953 void
954 YQPkgObjListItem::updateData()
955 {
956     init();
957 }
958
959
960 void
961 YQPkgObjListItem::setText( int column, const string text )
962 {
963     QTreeWidgetItem::setText( column, fromUTF8( text.c_str() ) );
964 }
965
966
967 void
968 YQPkgObjListItem::setText( int column, const zypp::Edition & edition )
969 {
970     setText( column, edition.asString() );
971 }
972
973
974 ZyppStatus
975 YQPkgObjListItem::status() const
976 {
977     if ( ! selectable() )
978     {
979         y2error( "No selectable" );
980         return S_NoInst;
981     }
982
983     return selectable()->status();
984 }
985
986
987 bool
988 YQPkgObjListItem::bySelection() const
989 {
990     zypp::ResStatus::TransactByValue modifiedBy = selectable()->modifiedBy();
991
992     return ( modifiedBy == zypp::ResStatus::APPL_LOW ||
993              modifiedBy == zypp::ResStatus::APPL_HIGH  );
994 }
995
996
997 void
998 YQPkgObjListItem::setStatus( ZyppStatus newStatus, bool sendSignals )
999 {
1000     ZyppStatus oldStatus = selectable()->status();
1001     selectable()->set_status( newStatus );
1002
1003     if ( oldStatus != selectable()->status() )
1004     {
1005         applyChanges();
1006
1007         if ( sendSignals )
1008         {
1009             _pkgObjList->updateItemStates();
1010             _pkgObjList->sendUpdatePackages();
1011         }
1012     }
1013
1014     setStatusIcon();
1015 }
1016
1017
1018 void
1019 YQPkgObjListItem::solveResolvableCollections()
1020 {
1021 #if EXTRA_SOLVE_COLLECTIONS
1022     zypp::Resolver_Ptr resolver = zypp::getZYpp()->resolver();
1023
1024     resolver->transactReset( zypp::ResStatus::APPL_LOW );
1025
1026     resolver->transactResKind( zypp::ResTraits<zypp::Product  >::kind );
1027     resolver->transactResKind( zypp::ResTraits<zypp::Selection>::kind );
1028     resolver->transactResKind( zypp::ResTraits<zypp::Pattern  >::kind );
1029     resolver->transactResKind( zypp::ResTraits<zypp::Language >::kind );
1030     resolver->transactResKind( zypp::ResTraits<zypp::Patch    >::kind );
1031     resolver->transactResKind( zypp::ResTraits<zypp::Atom     >::kind );
1032 #endif
1033 }
1034
1035
1036
1037 void
1038 YQPkgObjListItem::updateStatus()
1039 {
1040     setStatusIcon();
1041 }
1042
1043
1044 void
1045 YQPkgObjListItem::setStatusIcon()
1046 {
1047     if ( statusCol() >= 0 )
1048     {
1049         bool enabled = editable() && _pkgObjList->editable();
1050         setData( statusCol(), Qt::DecorationRole, _pkgObjList->statusIcon( status(), enabled, bySelection() ) );
1051     }
1052
1053
1054     if ( brokenIconCol() >= 0 )
1055     {
1056         // Reset this icon now - it might be the same column as satisfiedIconCol()
1057         setData( brokenIconCol(), Qt::DecorationRole, QPixmap() );
1058     }
1059
1060     if ( satisfiedIconCol() >= 0 )
1061     {
1062         // Set special icon for zyppObjs that are not marked as installed,
1063         // but satisfied anyway (e.g. for patches or patterns where the user
1064         // selected all required packages manually)
1065
1066         setData( satisfiedIconCol(), Qt::DecorationRole, isSatisfied() ? YQIconPool::pkgSatisfied() : QPixmap() );
1067     }
1068
1069     if ( brokenIconCol() >= 0 )
1070     {
1071         // Set special icon for zyppObjs that are installed, but broken
1072         // (dependencies no longer satisfied, e.g. for patches or patterns)
1073
1074         if ( isBroken() )
1075         {
1076             setData( brokenIconCol(), Qt::DecorationRole, YQIconPool::warningSign() );
1077             y2warning( "Broken object: %s - %s",
1078                        _selectable->theObj()->name().c_str(),
1079                        _selectable->theObj()->summary().c_str() );
1080         }
1081     }
1082 }
1083
1084
1085 bool
1086 YQPkgObjListItem::isSatisfied() const
1087 {
1088     if ( _debugIsSatisfied )
1089         return true;
1090
1091     if ( _selectable->hasInstalledObj() )
1092         return false;
1093
1094     return _selectable->candidatePoolItem().status().isSatisfied();
1095 }
1096
1097
1098 bool YQPkgObjListItem::isBroken() const
1099 {
1100     if ( _debugIsBroken )
1101         return true;
1102
1103     if ( ! _selectable->hasInstalledObj() )
1104         return false;           // can't be broken if not installed
1105
1106     switch ( status() )
1107     {
1108         case S_KeepInstalled:
1109         case S_Protected:
1110
1111             return _selectable->installedPoolItem().status().isIncomplete();
1112
1113         case S_Update:          // will be fixed by updating
1114         case S_AutoUpdate:
1115         case S_Del:             // will no longer be relevant after deleting
1116         case S_AutoDel:
1117
1118             return false;
1119
1120         case S_NoInst:          // should not happen - no installed obj
1121         case S_Install:
1122         case S_AutoInstall:
1123         case S_Taboo:
1124
1125             y2error( "Expected uninstalled zyppObj" );
1126             return false;
1127     }
1128
1129     y2error( "Should never get here" );
1130     return false;
1131 }
1132
1133
1134 void
1135 YQPkgObjListItem::cycleStatus()
1136 {
1137     if ( ! _editable || ! _pkgObjList->editable() )
1138         return;
1139
1140     ZyppStatus oldStatus = status();
1141     ZyppStatus newStatus = oldStatus;
1142
1143     if ( selectable()->hasInstalledObj() )
1144     {
1145         switch ( oldStatus )
1146         {
1147             case S_Protected:
1148                 newStatus = selectable()->hasCandidateObj() ?
1149                     S_KeepInstalled: S_NoInst;
1150                 break;
1151
1152             case S_KeepInstalled:
1153                 newStatus = selectable()->hasCandidateObj() ?
1154                     S_Update : S_Del;
1155                 break;
1156
1157             case S_Update:
1158                 newStatus = S_Del;
1159                 break;
1160
1161             case S_Del:
1162                 newStatus = S_KeepInstalled;
1163                 break;
1164
1165             default:
1166                 newStatus = S_KeepInstalled;
1167                 break;
1168         }
1169     }
1170     else        // Pkg not installed
1171     {
1172         switch ( oldStatus )
1173         {
1174             case S_NoInst:
1175                 if ( selectable()->hasCandidateObj() )
1176                 {
1177                     newStatus = S_Install;
1178                 }
1179                 else
1180                 {
1181                     y2warning( "No candidate for %s", selectable()->theObj()->name().c_str() );
1182                     newStatus = S_NoInst;
1183                 }
1184                 break;
1185
1186             case S_AutoInstall:
1187                 newStatus =  S_Taboo;
1188                 break;
1189
1190             default:
1191                 newStatus = S_NoInst;
1192                 break;
1193         }
1194     }
1195
1196     if ( oldStatus != newStatus )
1197     {
1198         setStatus( newStatus );
1199
1200         if ( showLicenseAgreement() )
1201         {
1202             showNotifyTexts( newStatus );
1203         }
1204         else // License not confirmed?
1205         {
1206             // Status is now S_Taboo or S_Del - update status icon
1207             setStatusIcon();
1208         }
1209
1210         _pkgObjList->sendStatusChanged();
1211     }
1212 }
1213
1214
1215 void
1216 YQPkgObjListItem::showNotifyTexts( ZyppStatus status )
1217 {
1218     string text;
1219
1220     switch ( status )
1221     {
1222         case S_Install:
1223             if ( selectable()->hasCandidateObj() )
1224                 text = selectable()->candidateObj()->insnotify();
1225             break;
1226
1227         case S_NoInst:
1228         case S_Del:
1229         case S_Taboo:
1230             if ( selectable()->hasCandidateObj() )
1231                 text = selectable()->candidateObj()->delnotify();
1232             break;
1233
1234         default: break;
1235     }
1236
1237     if ( ! text.empty() )
1238     {
1239         y2debug( "Showing notify text" );
1240         YQPkgTextDialog::showText( _pkgObjList, selectable(), text );
1241     }
1242 }
1243
1244
1245 bool
1246 YQPkgObjListItem::showLicenseAgreement()
1247 {
1248     return showLicenseAgreement( selectable() );
1249 }
1250
1251
1252 bool
1253 YQPkgObjListItem::showLicenseAgreement( ZyppSel sel )
1254 {
1255     string licenseText;
1256
1257     switch ( sel->status() )
1258     {
1259         case S_Install:
1260         case S_AutoInstall:
1261         case S_Update:
1262         case S_AutoUpdate:
1263
1264             if ( sel->hasLicenceConfirmed() )
1265                 return true;
1266
1267             if ( sel->candidateObj() )
1268                 licenseText = sel->candidateObj()->licenseToConfirm();
1269             break;
1270
1271         default: return true;
1272     }
1273
1274     if ( licenseText.empty() )
1275         return true;
1276
1277     y2debug( "Showing license agreement for %s", sel->name().c_str() );
1278     bool confirmed = YQPkgTextDialog::confirmText( 0, sel, licenseText );
1279
1280     if ( confirmed )
1281     {
1282         y2milestone( "User confirmed license agreement for %s", sel->name().c_str() );
1283         sel->setLicenceConfirmed( true );
1284     }
1285     else
1286     {
1287         // The user rejected the license agreement -
1288         // make sure the package gets unselected.
1289
1290         switch ( sel->status() )
1291         {
1292             case S_Install:
1293             case S_AutoInstall:
1294
1295                 y2warning( "User rejected license agreement for %s - setting to TABOO",
1296                            sel->name().c_str() );
1297
1298                 sel->set_status( S_Taboo );
1299                 break;
1300
1301
1302             case S_Update:
1303             case S_AutoUpdate:
1304
1305                 y2warning( "User rejected license agreement for %s  - setting to PROTECTED",
1306                            sel->name().c_str() );
1307
1308                 sel->set_status( S_Protected );
1309                 // S_Keep wouldn't be good enough: The next solver run might
1310                 // set it to S_AutoUpdate again
1311                 break;
1312
1313             default: break;
1314         }
1315     }
1316
1317     return confirmed;
1318 }
1319
1320
1321 QString
1322 YQPkgObjListItem::toolTip( int col )
1323 {
1324     if ( col == statusCol() )
1325     {
1326         QString tip = _pkgObjList->statusText( status() );
1327
1328         switch ( status() )
1329         {
1330             case S_AutoDel:
1331             case S_AutoInstall:
1332             case S_AutoUpdate:
1333
1334                 if ( bySelection() )
1335                     // Translators: Additional hint what caused an auto-status
1336                     tip += "\n" + _( "(by a software selection)" );
1337                 else
1338                     tip += "\n" + _( "(by dependencies)" );
1339
1340                 break;
1341
1342             default:
1343                 break;
1344         }
1345
1346         return tip;
1347     }
1348
1349     if ( col == brokenIconCol() )
1350     {
1351         if ( isBroken() )
1352             // Translators: tool tip for patches / patterns that are installed,
1353             // but whose dependencies are broken (no longer satisfied)
1354             return _( "Dependencies broken" );
1355     }
1356
1357     // don't use "else if" here, it might be the same colum as another one!
1358
1359     if ( col == satisfiedIconCol() )
1360     {
1361         if ( isSatisfied() )
1362             // Translators: tool tip for patches / patterns that are not installed,
1363             // but whose dependencies are satisfied
1364             return _( "All dependencies satisfied" );
1365     }
1366
1367     return QString::null;
1368 }
1369
1370
1371
1372 bool YQPkgObjListItem::operator<( const QTreeWidgetItem & otherListViewItem ) const
1373 {
1374     const YQPkgObjListItem * other = dynamic_cast<const YQPkgObjListItem *> (&otherListViewItem);
1375     int col = treeWidget()->sortColumn();
1376
1377     if ( other )
1378     {
1379         if ( col == sizeCol() )
1380         {
1381             // Numeric sort by size
1382     
1383             return ( this->zyppObj()->size() < other->zyppObj()->size() );
1384         }
1385         else if ( col == statusCol() )
1386         {
1387             // Sorting by status depends on the numeric value of the
1388             // ZyppStatus enum, thus it is important to insert new
1389             // package states there where they make most sense. We want to show
1390             // dangerous or noteworthy states first - e.g., "taboo" which should
1391             // seldeom occur, but when it does, it is important.
1392
1393             return ( this->status() < other->status() );
1394         }
1395         else if ( col == instVersionCol() ||
1396                   col == versionCol() )
1397         {
1398             // Sorting by version numbers doesn't make too much sense, so let's
1399             // sort by package relation:
1400             // - Installed newer than candidate (red)
1401             // - Candidate newer than installed (blue) - worthwhile updating
1402             // - Installed
1403             // - Not installed, but candidate available
1404             //
1405             // Within these categories, sort versions by ASCII - OK, it's
1406             // pretty random, but predictable.
1407
1408             int thisPoints  = this->versionPoints();
1409             int otherPoints = other->versionPoints();
1410
1411             return ( thisPoints < otherPoints );
1412             return QY2ListViewItem::operator<( otherListViewItem );
1413         }
1414     }
1415
1416     // Fallback: Use parent class method
1417     return QY2ListViewItem::operator<( otherListViewItem );
1418 }
1419
1420
1421 int
1422 YQPkgObjListItem::versionPoints() const
1423 {
1424     int points = 0;
1425
1426     if ( installedIsNewer() )                   points += 1000;
1427     if ( candidateIsNewer() )                   points += 100;
1428     if ( selectable()->hasInstalledObj() )      points += 10;
1429     if ( selectable()->hasCandidateObj() )      points += 1;
1430
1431     return points;
1432 }
1433
1434
1435 void
1436 YQPkgObjListItem::setExcluded( bool excl )
1437 {
1438     _excluded = excl;
1439 }
1440
1441
1442
1443
1444 YQPkgObjList::ExcludeRule::ExcludeRule( YQPkgObjList *  parent,
1445                                         const QRegExp & regexp,
1446                                         int             column )
1447     : _parent( parent )
1448     , _regexp( regexp )
1449     , _column( column )
1450     , _enabled( true )
1451 {
1452     _parent->addExcludeRule( this );
1453 }
1454
1455
1456 void
1457 YQPkgObjList::ExcludeRule::enable( bool enable )
1458 {
1459     _enabled = enable;
1460
1461 #if VERBOSE_EXCLUDE_RULES
1462     y2debug( "%s exclude rule %s",
1463              enable ? "Enabling" : "Disabling",
1464              _regexp.pattern().ascii() );
1465 #endif
1466 }
1467
1468
1469 void
1470 YQPkgObjList::ExcludeRule::setRegexp( const QRegExp & regexp )
1471 {
1472     _regexp = regexp;
1473 }
1474
1475
1476 void
1477 YQPkgObjList::ExcludeRule::setColumn( int column )
1478 {
1479     _column = column;
1480 }
1481
1482
1483 bool
1484 YQPkgObjList::ExcludeRule::match( QTreeWidgetItem * item )
1485 {
1486     if ( ! _enabled )
1487         return false;
1488
1489     QString text = item->text( _column );
1490
1491     if ( text.isEmpty() )
1492         return false;
1493
1494     return _regexp.exactMatch( text );
1495 }
1496
1497
1498
1499
1500
1501
1502 YQPkgObjList::ExcludedItems::ExcludedItems( YQPkgObjList * parent )
1503     : _pkgObjList( parent )
1504 {
1505 }
1506
1507
1508 YQPkgObjList::ExcludedItems::~ExcludedItems()
1509 {
1510     clear();
1511 }
1512
1513
1514 void YQPkgObjList::ExcludedItems::add( QTreeWidgetItem * item, QTreeWidgetItem * oldParent )
1515 {
1516     _excludeMap.insert( ItemPair( item, oldParent ) );
1517 }
1518
1519
1520 void YQPkgObjList::ExcludedItems::remove( QTreeWidgetItem * item )
1521 {
1522     ItemMap::iterator it = _excludeMap.find( item );
1523
1524     if ( it != _excludeMap.end() )
1525     {
1526         _excludeMap.erase( it );
1527     }
1528 }
1529
1530
1531 void YQPkgObjList::ExcludedItems::clear()
1532 {
1533     for ( ItemMap::iterator it = _excludeMap.begin();
1534           it != _excludeMap.end();
1535           ++it )
1536     {
1537         delete it->first;
1538     }
1539
1540     _excludeMap.clear();
1541 }
1542
1543
1544 bool YQPkgObjList::ExcludedItems::contains( QTreeWidgetItem * item )
1545 {
1546     return ( _excludeMap.find( item ) != _excludeMap.end() );
1547 }
1548
1549
1550 QTreeWidgetItem * YQPkgObjList::ExcludedItems::oldParentItem( QTreeWidgetItem * item )
1551 {
1552     ItemMap::iterator it = _excludeMap.find( item );
1553
1554     if ( it == _excludeMap.end() )
1555         return 0;
1556
1557     return it->second;
1558 }
1559
1560
1561
1562 #include "YQPkgObjList.moc"