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