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