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