]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/pkg/YQPkgObjList.cc
picking up branches/tmp/sh/qt4-port/, merging it with trunk
[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, Q3ListViewItem *, int, const QPoint & ) ),
71              this,      SLOT  ( pkgObjClicked           ( int, Q3ListViewItem *, int, const QPoint & ) ) );
72
73     connect( this,      SIGNAL( columnDoubleClicked     ( int, Q3ListViewItem *, int, const QPoint & ) ),
74              this,      SLOT  ( pkgObjClicked           ( int, Q3ListViewItem *, int, const QPoint & ) ) );
75
76     connect( this,      SIGNAL( selectionChanged        ( Q3ListViewItem * ) ),
77              this,      SLOT  ( selectionChangedInternal( Q3ListViewItem * ) ) );
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                              Q3ListViewItem *   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( Q3ListViewItem * 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     Q3ListViewItem * 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     Q3ListViewItemIterator 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     Q3ListViewItem * 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()
521 {
522     updateActions( dynamic_cast<YQPkgObjListItem *> ( selectedItem() ) );
523 }
524
525
526 void
527 YQPkgObjList::updateActions( YQPkgObjListItem * item )
528 {
529     if ( item )
530     {
531         ZyppSel selectable = item->selectable();
532
533         if ( selectable->hasInstalledObj() )
534         {
535             actionSetCurrentInstall->setEnabled( false );
536             actionSetCurrentDontInstall->setEnabled( false );
537             actionSetCurrentTaboo->setEnabled( false );
538             actionSetCurrentProtected->setEnabled( true );
539
540             actionSetCurrentKeepInstalled->setEnabled( true );
541             actionSetCurrentDelete->setEnabled( true );
542             actionSetCurrentUpdate->setEnabled( selectable->hasCandidateObj() );
543         }
544         else
545         {
546             actionSetCurrentInstall->setEnabled( selectable->hasCandidateObj() );
547             actionSetCurrentDontInstall->setEnabled( true );
548             actionSetCurrentTaboo->setEnabled( true );
549             actionSetCurrentProtected->setEnabled( false );
550
551             actionSetCurrentKeepInstalled->setEnabled( false );
552             actionSetCurrentDelete->setEnabled( false );
553             actionSetCurrentUpdate->setEnabled( false );
554         }
555     }
556     else        // ! item
557     {
558         actionSetCurrentInstall->setEnabled( false );
559         actionSetCurrentDontInstall->setEnabled( false );
560         actionSetCurrentTaboo->setEnabled( false );
561
562         actionSetCurrentKeepInstalled->setEnabled( false );
563         actionSetCurrentDelete->setEnabled( false );
564         actionSetCurrentUpdate->setEnabled( false );
565         actionSetCurrentProtected->setEnabled( false );
566     }
567 }
568
569
570 void
571 YQPkgObjList::keyPressEvent( QKeyEvent * event )
572 {
573     if ( event )
574     {
575         unsigned special_combo = ( Qt::ControlButton | Qt::ShiftButton | Qt::AltButton );
576
577         if ( ( event->state() & special_combo ) == special_combo )
578         {
579             if ( event->key() == Qt::Key_Q )
580             {
581                 _debug= ! _debug;
582                 y2milestone( "Debug mode %s", _debug ? "on" : "off" );
583             }
584
585         }
586
587         Q3ListViewItem * selectedListViewItem = selectedItem();
588
589         if ( selectedListViewItem )
590         {
591             YQPkgObjListItem * item = dynamic_cast<YQPkgObjListItem *> (selectedListViewItem);
592
593             if ( item )
594             {
595                 bool installed = item->selectable()->hasInstalledObj();
596                 ZyppStatus status = item->status();
597
598                 switch( event->ascii() )
599                 {
600                     case Qt::Key_Space:         // Cycle
601                         item->cycleStatus();
602                         event->accept();
603                         return;
604
605                     case '+':   // Grab everything - install or update
606
607                         if ( installed )
608                         {
609                             ZyppStatus newStatus = S_KeepInstalled;
610
611                             if ( item->candidateIsNewer() )
612                                 newStatus = S_Update;
613
614                             setCurrentStatus( newStatus );
615                         }
616                         else
617                             setCurrentStatus( S_Install );
618                         selectNextItem();
619                         event->accept();
620                         return;
621
622                     case '-':   // Get rid of everything - don't install or delete
623                         setCurrentStatus( installed ? S_Del : S_NoInst );
624                         selectNextItem();
625                         event->accept();
626                         return;
627
628                     case '!':   // Taboo
629
630                         if ( ! installed )
631                             setCurrentStatus( S_Taboo );
632                         selectNextItem();
633                         event->accept();
634                         return;
635
636                     case '*':   // Protected
637
638                         if ( installed )
639                             setCurrentStatus( S_Protected );
640                         selectNextItem();
641                         event->accept();
642                         return;
643
644                     case '>':   // Update what is worth to be updated
645
646                         if ( installed && item->candidateIsNewer() )
647                             setCurrentStatus( S_Update );
648                         selectNextItem();
649                         event->accept();
650                         return;
651
652                     case '<':   // Revert update
653
654                         if ( status == S_Update ||
655                              status == S_AutoUpdate )
656                         {
657                             setCurrentStatus( S_KeepInstalled );
658                         }
659                         selectNextItem();
660                         event->accept();
661                         return;
662
663                     case 'b':
664                     case 'B':   // Toggle debugIsBroken flag
665
666                         if ( _debug )
667                         {
668                             item->toggleDebugIsBroken();
669                             item->setStatusIcon();
670                         }
671                         event->accept();
672                         break;
673
674                     case 's':
675                     case 'S':   // Toggle debugIsSatisfied flag
676
677                         if ( _debug )
678                         {
679                             item->toggleDebugIsSatisfied();
680                             item->setStatusIcon();
681                         }
682                         event->accept();
683                         break;
684                 }
685             }
686         }
687     }
688
689     Q3ListView::keyPressEvent( event );
690 }
691
692
693 void
694 YQPkgObjList::message( const QString & text )
695 {
696     QY2ListViewItem * item = new QY2ListViewItem( this );
697     Q_CHECK_PTR( item );
698
699     item->setText( nameCol() >= 0 ? nameCol() : 0, text );
700     item->setBackgroundColor( QColor( 0xE0, 0xE0, 0xF8 ) );
701 }
702
703
704 void
705 YQPkgObjList::addExcludeRule( YQPkgObjList::ExcludeRule * rule )
706 {
707     _excludeRules.push_back( rule );
708 }
709
710
711 void
712 YQPkgObjList::applyExcludeRules()
713 {
714     // y2debug( "Applying exclude rules" );
715     Q3ListViewItemIterator listView_it( this );
716
717     while ( *listView_it )
718     {
719         Q3ListViewItem * current_item = *listView_it;
720
721         // Advance iterator now so it remains valid even if there are changes
722         // to the QListView, e.g., if the current item is excluded and thus
723         // removed from the QListView
724         ++listView_it;
725
726         applyExcludeRules( current_item );
727     }
728
729     ExcludedItems::iterator excluded_it = _excludedItems->begin();
730
731     while ( excluded_it != _excludedItems->end() )
732     {
733         Q3ListViewItem * current_item = (*excluded_it).first;
734
735         // Advance iterator now so it remains valid even if there are changes
736         // to the excluded items, e.g., if the current item is un-excluded and thus
737         // removed from the excluded items
738         ++excluded_it;
739
740         applyExcludeRules( current_item );
741     }
742
743     logExcludeStatistics();
744 }
745
746
747 void
748 YQPkgObjList::logExcludeStatistics()
749 {
750     if ( _excludedItems->size() > 0 )
751     {
752         y2milestone( "%d packages excluded", _excludedItems->size() );
753
754         for ( ExcludeRuleList::iterator rule_it = _excludeRules.begin();
755               rule_it != _excludeRules.end();
756               ++rule_it )
757         {
758             ExcludeRule * rule = *rule_it;
759
760             if ( rule->isEnabled() )
761             {
762                 y2milestone( "Active exclude rule: \"%s\"",
763                              rule->regexp().pattern().ascii() );
764             }
765         }
766     }
767 }
768
769
770 void
771 YQPkgObjList::applyExcludeRules( Q3ListViewItem * listViewItem )
772 {
773     YQPkgObjListItem * item = dynamic_cast<YQPkgObjListItem *>( listViewItem );
774
775     if ( item )
776     {
777         bool exclude = false;
778         ExcludeRule * matchingRule = 0;
779
780         for ( ExcludeRuleList::iterator rule_it = _excludeRules.begin();
781               rule_it != _excludeRules.end() && ! exclude;
782               ++rule_it )
783         {
784             ExcludeRule * rule = *rule_it;
785
786             if ( rule->match( item ) )
787             {
788                 exclude = true;
789                 matchingRule = rule;
790             }
791         }
792
793         if ( exclude != item->isExcluded() )    // change exclude status?
794         {
795             this->exclude( item, exclude );
796
797 #if VERBOSE_EXCLUDE_RULES
798             if ( exclude )
799             {
800                 y2debug( "Rule %s matches: Excluding %s",
801                          matchingRule->regexp().pattern().ascii(),
802                          item->zyppObj()->name().c_str() );
803             }
804             else
805             {
806                 y2debug( "Un-excluding %s", item->zyppObj()->name().c_str() );
807             }
808 #endif
809         }
810     }
811 }
812
813
814 void
815 YQPkgObjList::exclude( YQPkgObjListItem * item, bool exclude )
816 {
817     if ( exclude == item->isExcluded() )
818         return;
819
820     item->setExcluded( exclude );
821
822     if ( exclude )
823     {
824         Q3ListViewItem * parentItem = item->parent();
825
826         if ( parentItem )
827             parentItem->takeItem( item );
828         else
829             Q3ListView::takeItem( item );
830
831         _excludedItems->add( item, parentItem );
832     }
833     else // un-exclude
834     {
835         if ( _excludedItems->contains( item ) )
836         {
837             Q3ListViewItem * oldParent = _excludedItems->oldParentItem( item );
838             _excludedItems->remove( item );
839
840             if ( oldParent )
841                 oldParent->insertItem( item );
842             else
843                 Q3ListView::insertItem( item );
844         }
845     }
846 }
847
848
849
850
851 YQPkgObjListItem::YQPkgObjListItem( YQPkgObjList * pkgObjList,
852                                     ZyppSel selectable,
853                                     ZyppObj zyppObj )
854     : QY2ListViewItem( pkgObjList )
855     , _pkgObjList( pkgObjList )
856     , _selectable( selectable )
857     , _zyppObj( zyppObj )
858     , _editable( true )
859     , _excluded( false )
860 {
861     init();
862 }
863
864
865 YQPkgObjListItem::YQPkgObjListItem( YQPkgObjList *      pkgObjList,
866                                     QY2ListViewItem *   parent,
867                                     ZyppSel             selectable,
868                                     ZyppObj             zyppObj )
869     : QY2ListViewItem( parent )
870     , _pkgObjList( pkgObjList )
871     , _selectable( selectable )
872     , _zyppObj( zyppObj )
873     , _editable( true )
874     , _excluded( false )
875 {
876     init();
877 }
878
879
880 YQPkgObjListItem::~YQPkgObjListItem()
881 {
882     // NOP
883 }
884
885
886 void
887 YQPkgObjListItem::init()
888 {
889     if ( _zyppObj == 0 && _selectable )
890         _zyppObj = _selectable->theObj();
891
892     _debugIsBroken      = false;
893     _debugIsSatisfied   = false;
894     _candidateIsNewer   = false;
895     _installedIsNewer   = false;
896
897     const ZyppObj candidate = selectable()->candidateObj();
898     const ZyppObj installed = selectable()->installedObj();
899
900     if ( candidate && installed )
901     {
902         if ( candidate->edition() < installed->edition() )
903             _installedIsNewer = true;
904         else if ( installed->edition() < candidate->edition() )
905             _candidateIsNewer = true;
906     }
907
908     if ( nameCol()    >= 0 )    setText( nameCol(),     zyppObj()->name()       );
909     if ( summaryCol() >= 0 )    setText( summaryCol(),  zyppObj()->summary()    );
910
911     if ( sizeCol()    >= 0 )
912     {
913         zypp::ByteCount size = zyppObj()->size();
914
915         if ( size > 0L )
916             setText( sizeCol(), size.asString() + "  " );
917     }
918
919     if ( instVersionCol() >= 0 )
920     {
921         if ( selectable()->hasInstalledObj() )
922              setText( instVersionCol(), installed->edition() );
923
924         if ( zyppObj() != selectable()->installedObj() &&
925              zyppObj() != selectable()->candidateObj()   )
926         {
927             setText( versionCol(), zyppObj()->edition() );
928         }
929         else if ( selectable()->hasCandidateObj() )
930         {
931             setText( versionCol(), candidate->edition() );
932         }
933     }
934     else
935     {
936         setText( versionCol(),  zyppObj()->edition() );
937     }
938
939     setStatusIcon();
940 }
941
942
943 void
944 YQPkgObjListItem::updateData()
945 {
946     init();
947 }
948
949
950 void
951 YQPkgObjListItem::setText( int column, const string text )
952 {
953     Q3ListViewItem::setText( column, fromUTF8( text.c_str() ) );
954 }
955
956
957 void
958 YQPkgObjListItem::setText( int column, const zypp::Edition & edition )
959 {
960     setText( column, edition.asString() );
961 }
962
963
964 ZyppStatus
965 YQPkgObjListItem::status() const
966 {
967     if ( ! selectable() )
968     {
969         y2error( "No selectable" );
970         return S_NoInst;
971     }
972
973     return selectable()->status();
974 }
975
976
977 bool
978 YQPkgObjListItem::bySelection() const
979 {
980     zypp::ResStatus::TransactByValue modifiedBy = selectable()->modifiedBy();
981
982     return ( modifiedBy == zypp::ResStatus::APPL_LOW ||
983              modifiedBy == zypp::ResStatus::APPL_HIGH  );
984 }
985
986
987 void
988 YQPkgObjListItem::setStatus( ZyppStatus newStatus, bool sendSignals )
989 {
990     ZyppStatus oldStatus = selectable()->status();
991     selectable()->set_status( newStatus );
992
993     if ( oldStatus != selectable()->status() )
994     {
995         applyChanges();
996
997         if ( sendSignals )
998         {
999             _pkgObjList->updateItemStates();
1000             _pkgObjList->sendUpdatePackages();
1001         }
1002     }
1003
1004     setStatusIcon();
1005 }
1006
1007
1008 void
1009 YQPkgObjListItem::solveResolvableCollections()
1010 {
1011 #if EXTRA_SOLVE_COLLECTIONS
1012     zypp::Resolver_Ptr resolver = zypp::getZYpp()->resolver();
1013
1014     resolver->transactReset( zypp::ResStatus::APPL_LOW );
1015
1016     resolver->transactResKind( zypp::ResTraits<zypp::Product  >::kind );
1017     resolver->transactResKind( zypp::ResTraits<zypp::Selection>::kind );
1018     resolver->transactResKind( zypp::ResTraits<zypp::Pattern  >::kind );
1019     resolver->transactResKind( zypp::ResTraits<zypp::Language >::kind );
1020     resolver->transactResKind( zypp::ResTraits<zypp::Patch    >::kind );
1021     resolver->transactResKind( zypp::ResTraits<zypp::Atom     >::kind );
1022 #endif
1023 }
1024
1025
1026
1027 void
1028 YQPkgObjListItem::updateStatus()
1029 {
1030     setStatusIcon();
1031 }
1032
1033
1034 void
1035 YQPkgObjListItem::setStatusIcon()
1036 {
1037     if ( statusCol() >= 0 )
1038     {
1039         bool enabled = editable() && _pkgObjList->editable();
1040         setPixmap( statusCol(), _pkgObjList->statusIcon( status(), enabled, bySelection() ) );
1041     }
1042
1043
1044     if ( brokenIconCol() >= 0 )
1045     {
1046         // Reset this icon now - it might be the same column as satisfiedIconCol()
1047         setPixmap( brokenIconCol(), QPixmap() );
1048     }
1049
1050     if ( satisfiedIconCol() >= 0 )
1051     {
1052         // Set special icon for zyppObjs that are not marked as installed,
1053         // but satisfied anyway (e.g. for patches or patterns where the user
1054         // selected all required packages manually)
1055
1056         setPixmap( satisfiedIconCol(), isSatisfied() ? YQIconPool::pkgSatisfied() : QPixmap() );
1057     }
1058
1059     if ( brokenIconCol() >= 0 )
1060     {
1061         // Set special icon for zyppObjs that are installed, but broken
1062         // (dependencies no longer satisfied, e.g. for patches or patterns)
1063
1064         if ( isBroken() )
1065         {
1066             setPixmap( brokenIconCol(), YQIconPool::warningSign() );
1067
1068             y2warning( "Broken object: %s - %s",
1069                        _selectable->theObj()->name().c_str(),
1070                        _selectable->theObj()->summary().c_str() );
1071         }
1072     }
1073
1074 }
1075
1076
1077 bool
1078 YQPkgObjListItem::isSatisfied() const
1079 {
1080     if ( _debugIsSatisfied )
1081         return true;
1082
1083     if ( _selectable->hasInstalledObj() )
1084         return false;
1085
1086     return _selectable->candidatePoolItem().status().isSatisfied();
1087 }
1088
1089
1090 bool YQPkgObjListItem::isBroken() const
1091 {
1092     if ( _debugIsBroken )
1093         return true;
1094
1095     if ( ! _selectable->hasInstalledObj() )
1096         return false;           // can't be broken if not installed
1097
1098     switch ( status() )
1099     {
1100         case S_KeepInstalled:
1101         case S_Protected:
1102
1103             return _selectable->installedPoolItem().status().isIncomplete();
1104
1105         case S_Update:          // will be fixed by updating
1106         case S_AutoUpdate:
1107         case S_Del:             // will no longer be relevant after deleting
1108         case S_AutoDel:
1109
1110             return false;
1111
1112         case S_NoInst:          // should not happen - no installed obj
1113         case S_Install:
1114         case S_AutoInstall:
1115         case S_Taboo:
1116
1117             y2error( "Expected uninstalled zyppObj" );
1118             return false;
1119     }
1120
1121     y2error( "Should never get here" );
1122     return false;
1123 }
1124
1125
1126 void
1127 YQPkgObjListItem::cycleStatus()
1128 {
1129     if ( ! _editable || ! _pkgObjList->editable() )
1130         return;
1131
1132     ZyppStatus oldStatus = status();
1133     ZyppStatus newStatus = oldStatus;
1134
1135     if ( selectable()->hasInstalledObj() )
1136     {
1137         switch ( oldStatus )
1138         {
1139             case S_Protected:
1140                 newStatus = selectable()->hasCandidateObj() ?
1141                     S_KeepInstalled: S_NoInst;
1142                 break;
1143
1144             case S_KeepInstalled:
1145                 newStatus = selectable()->hasCandidateObj() ?
1146                     S_Update : S_Del;
1147                 break;
1148
1149             case S_Update:
1150                 newStatus = S_Del;
1151                 break;
1152
1153             case S_Del:
1154                 newStatus = S_KeepInstalled;
1155                 break;
1156
1157             default:
1158                 newStatus = S_KeepInstalled;
1159                 break;
1160         }
1161     }
1162     else        // Pkg not installed
1163     {
1164         switch ( oldStatus )
1165         {
1166             case S_NoInst:
1167                 if ( selectable()->hasCandidateObj() )
1168                 {
1169                     newStatus = S_Install;
1170                 }
1171                 else
1172                 {
1173                     y2warning( "No candidate for %s", selectable()->theObj()->name().c_str() );
1174                     newStatus = S_NoInst;
1175                 }
1176                 break;
1177
1178             case S_AutoInstall:
1179                 newStatus =  S_Taboo;
1180                 break;
1181
1182             default:
1183                 newStatus = S_NoInst;
1184                 break;
1185         }
1186     }
1187
1188     if ( oldStatus != newStatus )
1189     {
1190         setStatus( newStatus );
1191
1192         if ( showLicenseAgreement() )
1193         {
1194             showNotifyTexts( newStatus );
1195         }
1196         else // License not confirmed?
1197         {
1198             // Status is now S_Taboo or S_Del - update status icon
1199             setStatusIcon();
1200         }
1201
1202         _pkgObjList->sendStatusChanged();
1203     }
1204 }
1205
1206
1207 void
1208 YQPkgObjListItem::showNotifyTexts( ZyppStatus status )
1209 {
1210     string text;
1211
1212     switch ( status )
1213     {
1214         case S_Install:
1215             if ( selectable()->hasCandidateObj() )
1216                 text = selectable()->candidateObj()->insnotify();
1217             break;
1218
1219         case S_NoInst:
1220         case S_Del:
1221         case S_Taboo:
1222             if ( selectable()->hasCandidateObj() )
1223                 text = selectable()->candidateObj()->delnotify();
1224             break;
1225
1226         default: break;
1227     }
1228
1229     if ( ! text.empty() )
1230     {
1231         y2debug( "Showing notify text" );
1232         YQPkgTextDialog::showText( _pkgObjList, selectable(), text );
1233     }
1234 }
1235
1236
1237 bool
1238 YQPkgObjListItem::showLicenseAgreement()
1239 {
1240     return showLicenseAgreement( selectable() );
1241 }
1242
1243
1244 bool
1245 YQPkgObjListItem::showLicenseAgreement( ZyppSel sel )
1246 {
1247     string licenseText;
1248
1249     switch ( sel->status() )
1250     {
1251         case S_Install:
1252         case S_AutoInstall:
1253         case S_Update:
1254         case S_AutoUpdate:
1255
1256             if ( sel->hasLicenceConfirmed() )
1257                 return true;
1258
1259             if ( sel->candidateObj() )
1260                 licenseText = sel->candidateObj()->licenseToConfirm();
1261             break;
1262
1263         default: return true;
1264     }
1265
1266     if ( licenseText.empty() )
1267         return true;
1268
1269     y2debug( "Showing license agreement for %s", sel->name().c_str() );
1270     bool confirmed = YQPkgTextDialog::confirmText( 0, sel, licenseText );
1271
1272     if ( confirmed )
1273     {
1274         y2milestone( "User confirmed license agreement for %s", sel->name().c_str() );
1275         sel->setLicenceConfirmed( true );
1276     }
1277     else
1278     {
1279         // The user rejected the license agreement -
1280         // make sure the package gets unselected.
1281
1282         switch ( sel->status() )
1283         {
1284             case S_Install:
1285             case S_AutoInstall:
1286
1287                 y2warning( "User rejected license agreement for %s - setting to TABOO",
1288                            sel->name().c_str() );
1289
1290                 sel->set_status( S_Taboo );
1291                 break;
1292
1293
1294             case S_Update:
1295             case S_AutoUpdate:
1296
1297                 y2warning( "User rejected license agreement for %s  - setting to PROTECTED",
1298                            sel->name().c_str() );
1299
1300                 sel->set_status( S_Protected );
1301                 // S_Keep wouldn't be good enough: The next solver run might
1302                 // set it to S_AutoUpdate again
1303                 break;
1304
1305             default: break;
1306         }
1307     }
1308
1309     return confirmed;
1310 }
1311
1312
1313 QString
1314 YQPkgObjListItem::toolTip( int col )
1315 {
1316     if ( col == statusCol() )
1317     {
1318         QString tip = _pkgObjList->statusText( status() );
1319
1320         switch ( status() )
1321         {
1322             case S_AutoDel:
1323             case S_AutoInstall:
1324             case S_AutoUpdate:
1325
1326                 if ( bySelection() )
1327                     // Translators: Additional hint what caused an auto-status
1328                     tip += "\n" + _( "(by a software selection)" );
1329                 else
1330                     tip += "\n" + _( "(by dependencies)" );
1331
1332                 break;
1333
1334             default:
1335                 break;
1336         }
1337
1338         return tip;
1339     }
1340
1341     if ( col == brokenIconCol() )
1342     {
1343         if ( isBroken() )
1344             // Translators: tool tip for patches / patterns that are installed,
1345             // but whose dependencies are broken (no longer satisfied)
1346             return _( "Dependencies broken" );
1347     }
1348
1349     // don't use "else if" here, it might be the same colum as another one!
1350
1351     if ( col == satisfiedIconCol() )
1352     {
1353         if ( isSatisfied() )
1354             // Translators: tool tip for patches / patterns that are not installed,
1355             // but whose dependencies are satisfied
1356             return _( "All dependencies satisfied" );
1357     }
1358
1359     return QString::null;
1360 }
1361
1362
1363 /**
1364  * Comparison function used for sorting the list.
1365  * Returns:
1366  * -1 if this <  other
1367  *  0 if this == other
1368  * +1 if this >  other
1369  **/
1370 int
1371 YQPkgObjListItem::compare( Q3ListViewItem *     otherListViewItem,
1372                            int                  col,
1373                            bool                 ascending ) const
1374 {
1375     YQPkgObjListItem * other = dynamic_cast<YQPkgObjListItem *> (otherListViewItem);
1376
1377     if ( other )
1378     {
1379         if ( col == sizeCol() )
1380         {
1381             // Numeric sort by size
1382
1383             if ( this->zyppObj()->size() < other->zyppObj()->size() ) return -1;
1384             if ( this->zyppObj()->size() > other->zyppObj()->size() ) return 1;
1385             return 0;
1386         }
1387         else if ( col == statusCol() )
1388         {
1389             // Sorting by status depends on the numeric value of the
1390             // ZyppStatus enum, thus it is important to insert new
1391             // package states there where they make most sense. We want to show
1392             // dangerous or noteworthy states first - e.g., "taboo" which should
1393             // seldeom occur, but when it does, it is important.
1394
1395             if ( this->status() < other->status() ) return -1;
1396             if ( this->status() > other->status() ) return 1;
1397             return 0;
1398         }
1399         else if ( col == instVersionCol() ||
1400                   col == versionCol() )
1401         {
1402             // Sorting by version numbers doesn't make too much sense, so let's
1403             // sort by package relation:
1404             // - Installed newer than candidate (red)
1405             // - Candidate newer than installed (blue) - worthwhile updating
1406             // - Installed
1407             // - Not installed, but candidate available
1408             //
1409             // Within these categories, sort versions by ASCII - OK, it's
1410             // pretty random, but predictable.
1411
1412             int thisPoints  = this->versionPoints();
1413             int otherPoints = other->versionPoints();
1414
1415             if ( thisPoints > otherPoints ) return -1;
1416             if ( thisPoints < otherPoints ) return  1;
1417             return QY2ListViewItem::compare( otherListViewItem, col, ascending );
1418         }
1419     }
1420
1421     // Fallback: Use parent class method
1422     return QY2ListViewItem::compare( otherListViewItem, col, ascending );
1423 }
1424
1425
1426 int
1427 YQPkgObjListItem::versionPoints() const
1428 {
1429     int points = 0;
1430
1431     if ( installedIsNewer() )                   points += 1000;
1432     if ( candidateIsNewer() )                   points += 100;
1433     if ( selectable()->hasInstalledObj() )      points += 10;
1434     if ( selectable()->hasCandidateObj() )      points += 1;
1435
1436     return points;
1437 }
1438
1439
1440 void
1441 YQPkgObjListItem::setExcluded( bool excl )
1442 {
1443     _excluded = excl;
1444 }
1445
1446
1447
1448
1449 YQPkgObjList::ExcludeRule::ExcludeRule( YQPkgObjList *  parent,
1450                                         const QRegExp & regexp,
1451                                         int             column )
1452     : _parent( parent )
1453     , _regexp( regexp )
1454     , _column( column )
1455     , _enabled( true )
1456 {
1457     _parent->addExcludeRule( this );
1458 }
1459
1460
1461 void
1462 YQPkgObjList::ExcludeRule::enable( bool enable )
1463 {
1464     _enabled = enable;
1465
1466 #if VERBOSE_EXCLUDE_RULES
1467     y2debug( "%s exclude rule %s",
1468              enable ? "Enabling" : "Disabling",
1469              _regexp.pattern().ascii() );
1470 #endif
1471 }
1472
1473
1474 void
1475 YQPkgObjList::ExcludeRule::setRegexp( const QRegExp & regexp )
1476 {
1477     _regexp = regexp;
1478 }
1479
1480
1481 void
1482 YQPkgObjList::ExcludeRule::setColumn( int column )
1483 {
1484     _column = column;
1485 }
1486
1487
1488 bool
1489 YQPkgObjList::ExcludeRule::match( Q3ListViewItem * item )
1490 {
1491     if ( ! _enabled )
1492         return false;
1493
1494     QString text = item->text( _column );
1495
1496     if ( text.isEmpty() )
1497         return false;
1498
1499     return _regexp.exactMatch( text );
1500 }
1501
1502
1503
1504
1505
1506
1507 YQPkgObjList::ExcludedItems::ExcludedItems( YQPkgObjList * parent )
1508     : _pkgObjList( parent )
1509 {
1510 }
1511
1512
1513 YQPkgObjList::ExcludedItems::~ExcludedItems()
1514 {
1515     clear();
1516 }
1517
1518
1519 void YQPkgObjList::ExcludedItems::add( Q3ListViewItem * item, Q3ListViewItem * oldParent )
1520 {
1521     _excludeMap.insert( ItemPair( item, oldParent ) );
1522 }
1523
1524
1525 void YQPkgObjList::ExcludedItems::remove( Q3ListViewItem * item )
1526 {
1527     ItemMap::iterator it = _excludeMap.find( item );
1528
1529     if ( it != _excludeMap.end() )
1530     {
1531         _excludeMap.erase( it );
1532     }
1533 }
1534
1535
1536 void YQPkgObjList::ExcludedItems::clear()
1537 {
1538     for ( ItemMap::iterator it = _excludeMap.begin();
1539           it != _excludeMap.end();
1540           ++it )
1541     {
1542         delete it->first;
1543     }
1544
1545     _excludeMap.clear();
1546 }
1547
1548
1549 bool YQPkgObjList::ExcludedItems::contains( Q3ListViewItem * item )
1550 {
1551     return ( _excludeMap.find( item ) != _excludeMap.end() );
1552 }
1553
1554
1555 Q3ListViewItem * YQPkgObjList::ExcludedItems::oldParentItem( Q3ListViewItem * item )
1556 {
1557     ItemMap::iterator it = _excludeMap.find( item );
1558
1559     if ( it == _excludeMap.end() )
1560         return 0;
1561
1562     return it->second;
1563 }
1564
1565
1566
1567 #include "YQPkgObjList.moc"