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