]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/pkg/YQPkgPatchList.cc
various fixes
[duncan/yast2-qt4.git] / src / pkg / YQPkgPatchList.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       YQPkgPatchList.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
24 #include <QMenu>
25 #include <QAction>
26 #include <QEvent>
27 #include <zypp/ui/PatchContents.h>
28 #include <set>
29
30 #include "YQi18n.h"
31 #include "utf8.h"
32
33 #include "YQPkgPatchList.h"
34 #include "YQPkgTextDialog.h"
35
36
37 #define VERBOSE_PATCH_LIST      1
38
39
40 typedef zypp::ui::PatchContents                 ZyppPatchContents;
41 typedef zypp::ui::PatchContents::const_iterator ZyppPatchContentsIterator;
42
43 using std::list;
44 using std::set;
45
46
47 YQPkgPatchList::YQPkgPatchList( QWidget * parent )
48     : YQPkgObjList( parent )
49 {
50     y2debug( "Creating patch list" );
51
52     _filterCriteria = RelevantPatches;
53
54     int numCol = 0;
55
56     QStringList headers;
57
58     headers << "";                      _statusCol      = numCol++;
59     headers <<  _( "Patch"      );      _nameCol        = numCol++;
60     headers << _( "Summary"     );      _summaryCol     = numCol++;
61     headers << _( "Category"    );      _categoryCol    = numCol++;
62     headers << _( "Size"        );      _sizeCol        = numCol++;
63     headers << _( "Version"     );      _versionCol     = numCol++;
64
65     // Can use the same colum for "broken" and "satisfied":
66     // Both states are mutually exclusive
67
68     _satisfiedIconCol   = _summaryCol;
69     _brokenIconCol      = _summaryCol;
70
71     setHeaderLabels(headers);
72
73     setAllColumnsShowFocus( true );
74     //FIXME setColumnAlignment( sizeCol(), Qt::AlignRight );
75
76     connect( this,      SIGNAL( currentItemChanged      ( QTreeWidgetItem * ) ),
77              this,      SLOT  ( filter()                                    ) );
78
79     sortItems( categoryCol(), Qt::AscendingOrder );
80     fillList();
81
82     y2debug( "Creating patch list done" );
83 }
84
85
86 YQPkgPatchList::~YQPkgPatchList()
87 {
88     // NOP
89 }
90
91
92 void
93 YQPkgPatchList::polish()
94 {
95     // Delayed initialization after widget is fully created etc.
96
97     // Only now send currentItemChanged() signal so attached details views also
98     // display something if their showDetailsIfVisible() slot is connected to
99     // currentItemChanged() signals.
100     selectSomething();
101 }
102
103
104 void
105 YQPkgPatchList::setFilterCriteria( FilterCriteria filterCriteria )
106 {
107     _filterCriteria = filterCriteria;
108 }
109
110
111 void
112 YQPkgPatchList::fillList()
113 {
114     clear();
115     y2debug( "Filling patch list" );
116
117     for ( ZyppPoolIterator it = zyppPatchesBegin();
118           it != zyppPatchesEnd();
119           ++it )
120     {
121         ZyppSel   selectable = *it;
122         ZyppPatch zyppPatch = tryCastToZyppPatch( selectable->theObj() );
123
124         if ( zyppPatch )
125         {
126             bool displayPatch = false;
127
128             switch ( _filterCriteria )
129             {
130                 case RelevantPatches:   // needed + broken + satisfied (but not installed)
131
132                     if ( selectable->hasInstalledObj() ) // installed?
133                     {
134                         if ( selectable->installedPoolItem().status().isIncomplete() ) // patch broken?
135                         {
136                             // The patch is broken: It had been installed, but the user somehow
137                             // downgraded individual packages belonging to the patch to older versions.
138
139                             displayPatch = true;
140
141                             y2warning( "Installed patch is broken: %s - %s",
142                                        zyppPatch->name().c_str(),
143                                        zyppPatch->summary().c_str() );
144                         }
145                     }
146                     else // not installed
147                     {
148                         if ( selectable->hasCandidateObj() &&
149                              selectable->candidatePoolItem().status().isSatisfied() )
150                         {
151                             // This is a pretty exotic case, but still it might happen:
152                             //
153                             // The patch itelf is not installed, but it is satisfied because the
154                             // user updated all the packages belonging to the patch to the versions
155                             // the patch requires. All that is missing now is to get the patch meta
156                             // data onto the system. So let's display the patch to give the user
157                             // a chance to install it (if he so chooses).
158
159                             displayPatch = true;
160
161                             y2milestone( "Patch satisfied, but not installed yet: %s - %s",
162                                          zyppPatch->name().c_str(),
163                                          zyppPatch->summary().c_str() );
164                         }
165                     }
166
167                     if ( selectable->hasCandidateObj() )        // candidate available?
168                     {
169                         // The most common case: There is a candidate patch, i.e. one that could be
170                         // installed, but either no version of that patch is installed or there is a
171                         // newer one to which the patch could be updated.
172
173                         if ( selectable->candidatePoolItem().status().isNeeded() ) // patch really needed?
174                         {
175                             // Patches are needed if any of the packages that belong to the patch
176                             // are installed on the system.
177
178                             displayPatch = true;
179                         }
180                         else
181                         {
182                             // None of the packages that belong to the patch is installed on the system.
183
184                             y2debug( "Patch not needed: %s - %s",
185                                      zyppPatch->name().c_str(),
186                                      zyppPatch->summary().c_str() );
187                         }
188                     }
189                     break;
190
191
192                 case RelevantAndInstalledPatches:       // needed + broken + installed
193
194                     if ( selectable->hasInstalledObj() ) // installed?
195                     {
196                         displayPatch = true;
197                     }
198                     else // not installed - display only if needed
199                     {
200                         zypp::ResStatus candidateStatus = selectable->candidatePoolItem().status();
201
202                         if ( candidateStatus.isNeeded() ||
203                              candidateStatus.isSatisfied() )
204                         {
205                             displayPatch = true;
206                         }
207                         else
208                         {
209                             y2milestone( "Patch not needed: %s - %s",
210                                          zyppPatch->name().c_str(),
211                                          zyppPatch->summary().c_str() );
212                         }
213                     }
214                     break;
215
216
217                 case AllPatches:
218                     displayPatch = true;
219                     break;
220
221                 // Intentionally omitting "default" so the compiler
222                 // can catch unhandled enum values
223             }
224
225             if ( displayPatch )
226             {
227 #if VERBOSE_PATCH_LIST
228                 y2debug( "Displaying patch %s - %s", zyppPatch->name().c_str(), zyppPatch->summary().c_str() );
229 #endif
230                 addPatchItem( *it, zyppPatch);
231             }
232         }
233         else
234         {
235             y2error( "Found non-patch selectable" );
236         }
237     }
238
239 #if FIXME
240     if ( ! firstChild() )
241         message( _( "No patches available." ) );
242 #endif
243
244     y2debug( "patch list filled" );
245 }
246
247
248
249 void
250 YQPkgPatchList::message( const QString & text )
251 {
252     QY2ListViewItem * item = new QY2ListViewItem( this );
253     Q_CHECK_PTR( item );
254
255     item->setText( 1, text );
256     item->setBackgroundColor( QColor( 0xE0, 0xE0, 0xF8 ) );
257 }
258
259
260
261 void
262 YQPkgPatchList::filterIfVisible()
263 {
264     if ( isVisible() )
265         filter();
266 }
267
268
269 void
270 YQPkgPatchList::filter()
271 {
272     emit filterStart();
273     std::set<ZyppSel> patchSelectables;
274
275     if ( selection() )
276     {
277         ZyppPatch patch = selection()->zyppPatch();
278
279         if ( patch )
280         {
281             ZyppPatchContents patchContents( patch );
282
283             for ( ZyppPatchContentsIterator it = patchContents.begin();
284                   it != patchContents.end();
285                   ++it )
286             {
287                 ZyppPkg pkg = tryCastToZyppPkg( *it );
288
289                 if ( pkg )
290                 {
291                     y2debug( "Found patch pkg: %s arch: %s", (*it)->name().c_str(), (*it)->arch().asString().c_str() );
292
293                     ZyppSel sel = _selMapper.findZyppSel( pkg );
294
295                     if ( sel )
296                     {
297                         if ( contains( patchSelectables, sel ) )
298                         {
299                             y2milestone( "Suppressing duplicate selectable %s-%s arch: %s",
300                                          (*it)->name().c_str(),
301                                          (*it)->edition().asString().c_str(),
302                                          (*it)->arch().asString().c_str() );
303                         }
304                         else
305                         {
306                             patchSelectables.insert( sel );
307                             emit filterMatch( sel, pkg );
308                         }
309                     }
310                     else
311                         y2error( "No selectable for pkg %s",  (*it)->name().c_str() );
312                 }
313                 else // No ZyppPkg - some other kind of object (script, message)
314                 {
315                     if ( zypp::isKind<zypp::Script> ( *it ) )
316                     {
317                         y2debug( "Found patch script %s", (*it)->name().c_str() );
318                         emit filterMatch( _( "Script" ),  fromUTF8( (*it)->name() ), -1 );
319                     }
320                     else if ( zypp::isKind<zypp::Message> ( *it ) )
321                     {
322                         y2debug( "Found patch message %s (ignoring)", (*it)->name().c_str() );
323                     }
324                     else
325                     {
326                         y2error( "Found unknown object of kind %s in patch: %s-%s arch: %s",
327                                  (*it)->kind().asString().c_str(),
328                                  (*it)->name().c_str(),
329                                  (*it)->edition().asString().c_str(),
330                                  (*it)->arch().asString().c_str() );
331                     }
332                 }
333             }
334         }
335     }
336
337     emit filterFinished();
338 }
339
340
341 void
342 YQPkgPatchList::addPatchItem( ZyppSel   selectable,
343                               ZyppPatch zyppPatch )
344 {
345     if ( ! selectable )
346     {
347         y2error( "NULL ZyppSel!" );
348         return;
349     }
350
351     YQPkgPatchListItem * item = new YQPkgPatchListItem( this, selectable, zyppPatch );
352     applyExcludeRules( item );
353 }
354
355
356 YQPkgPatchListItem *
357 YQPkgPatchList::selection() const
358 {
359 #if FIXME
360     QTreeWidgetItem * item = selectedItem();
361
362     if ( ! item )
363         return 0;
364
365     return dynamic_cast<YQPkgPatchListItem *> (item);
366 #else
367     return 0;
368 #endif
369 }
370
371
372 void
373 YQPkgPatchList::createNotInstalledContextMenu()
374 {
375     _notInstalledContextMenu = new QMenu( this );
376     Q_CHECK_PTR( _notInstalledContextMenu );
377
378     _notInstalledContextMenu->addAction(actionSetCurrentInstall);
379     _notInstalledContextMenu->addAction(actionSetCurrentDontInstall);
380     _notInstalledContextMenu->addAction(actionSetCurrentTaboo);
381
382     addAllInListSubMenu( _notInstalledContextMenu );
383 }
384
385
386 void
387 YQPkgPatchList::createInstalledContextMenu()
388 {
389     _installedContextMenu = new QMenu( this );
390     Q_CHECK_PTR( _installedContextMenu );
391
392     _installedContextMenu->addAction(actionSetCurrentKeepInstalled);
393
394 #if ENABLE_DELETING_PATCHES
395     _installedContextMenu->addAction(actionSetCurrentDelete);
396 #endif
397
398     _installedContextMenu->addAction(actionSetCurrentUpdate);
399     _installedContextMenu->addAction(actionSetCurrentProtected);
400
401     addAllInListSubMenu( _installedContextMenu );
402 }
403
404
405 QMenu *
406 YQPkgPatchList::addAllInListSubMenu( QMenu * menu )
407 {
408     QMenu * submenu = new QMenu( menu );
409     Q_CHECK_PTR( submenu );
410
411     submenu->addAction(actionSetListInstall);
412     submenu->addAction(actionSetListDontInstall);
413     submenu->addAction(actionSetListKeepInstalled);
414
415 #if ENABLE_DELETING_PATCHES
416     submenu->addAction(actionSetListDelete);
417 #endif
418
419     submenu->addAction(actionSetListUpdate);
420     submenu->addAction(actionSetListUpdateForce);
421     submenu->addAction(actionSetListTaboo);
422     submenu->addAction(actionSetListProtected);
423
424     QAction *action = menu->addMenu(submenu);
425     action->setText(_( "&All in This List" ));
426
427     return submenu;
428 }
429
430
431 void
432 YQPkgPatchList::keyPressEvent( QKeyEvent * event )
433 {
434     if ( event )
435     {
436 #if ! ENABLE_DELETING_PATCHES
437         if ( event->ascii() == '-' )
438         {
439             QTreeWidgetItem * selectedListViewItem = currentItem();
440
441             if ( selectedListViewItem )
442             {
443                 YQPkgPatchListItem * item = dynamic_cast<YQPkgPatchListItem *> (selectedListViewItem);
444
445                 if ( item && item->selectable()->hasInstalledObj() )
446                 {
447                     y2warning( "Deleting patches is not supported" );
448                     return;
449                 }
450             }
451         }
452 #endif
453     }
454
455     YQPkgObjList::keyPressEvent( event );
456 }
457
458
459
460
461 YQPkgPatchListItem::YQPkgPatchListItem( YQPkgPatchList *        patchList,
462                                         ZyppSel                 selectable,
463                                         ZyppPatch               zyppPatch )
464     : YQPkgObjListItem( patchList, selectable, zyppPatch )
465     , _patchList( patchList )
466     , _zyppPatch( zyppPatch )
467 {
468     if ( ! _zyppPatch )
469         _zyppPatch = tryCastToZyppPatch( selectable->theObj() );
470
471     if ( ! _zyppPatch )
472         return;
473
474     setStatusIcon();
475     _patchCategory = patchCategory( _zyppPatch->category() );
476
477     if ( categoryCol() > -1 )
478         setText( categoryCol(), asString( _patchCategory ) );
479
480     if ( summaryCol() > -1 && _zyppPatch->summary().empty() )
481         setText( summaryCol(), _zyppPatch->name() );            // use name as fallback
482
483     switch ( _patchCategory )
484     {
485         case YQPkgYaSTPatch:            setTextColor( QColor( 0, 0, 0xC0 ) );   break;  // medium blue
486         case YQPkgSecurityPatch:        setTextColor( Qt::red );                break;
487         case YQPkgRecommendedPatch:     setTextColor( QColor( 0, 0, 0xC0 ) );   break;  // medium blue
488         case YQPkgOptionalPatch:        break;
489         case YQPkgDocumentPatch:        break;
490         case YQPkgUnknownPatchCategory: break;
491     }
492 }
493
494
495 YQPkgPatchListItem::~YQPkgPatchListItem()
496 {
497     // NOP
498 }
499
500
501 YQPkgPatchCategory
502 YQPkgPatchListItem::patchCategory( const string & category )
503 {
504     return patchCategory( fromUTF8( category ) );
505 }
506
507
508 YQPkgPatchCategory
509 YQPkgPatchListItem::patchCategory( QString category )
510 {
511     category = category.toLower();
512
513     if ( category == "yast"             ) return YQPkgYaSTPatch;
514     if ( category == "security"         ) return YQPkgSecurityPatch;
515     if ( category == "recommended"      ) return YQPkgRecommendedPatch;
516     if ( category == "optional"         ) return YQPkgOptionalPatch;
517     if ( category == "document"         ) return YQPkgDocumentPatch;
518
519     y2warning( "Unknown patch category \"%s\"", qPrintable(category) );
520     return YQPkgUnknownPatchCategory;
521 }
522
523
524 QString
525 YQPkgPatchListItem::asString( YQPkgPatchCategory category )
526 {
527     switch ( category )
528     {
529         // Translators: These are patch categories
530         case YQPkgYaSTPatch:            return _( "YaST"        );
531         case YQPkgSecurityPatch:        return _( "security"    );
532         case YQPkgRecommendedPatch:     return _( "recommended" );
533         case YQPkgOptionalPatch:        return _( "optional"    );
534         case YQPkgDocumentPatch:        return _( "document"    );
535         case YQPkgUnknownPatchCategory: return "";
536     }
537
538     return "";
539 }
540
541
542 void
543 YQPkgPatchListItem::cycleStatus()
544 {
545     YQPkgObjListItem::cycleStatus();
546
547 #if ! ENABLE_DELETING_PATCHES
548     if ( status() == S_Del )    // Can't delete patches
549         setStatus( S_KeepInstalled );
550 #endif
551 }
552
553
554 QString
555 YQPkgPatchListItem::toolTip( int col )
556 {
557     QString text;
558
559     if ( col == statusCol() )
560     {
561         text = YQPkgObjListItem::toolTip( col );
562     }
563     else
564     {
565         if (  ( col == brokenIconCol()    && isBroken()    ) ||
566               ( col == satisfiedIconCol() && isSatisfied() )   )
567         {
568             text = YQPkgObjListItem::toolTip( col );
569         }
570         else
571         {
572             text = fromUTF8( zyppPatch()->category() );
573
574             if ( ! text.isEmpty() )
575                 text += "\n";
576
577             text += fromUTF8( zyppPatch()->size().asString().c_str() );
578         }
579     }
580
581     return text;
582 }
583
584
585 void
586 YQPkgPatchListItem::applyChanges()
587 {
588     solveResolvableCollections();
589 }
590
591
592 bool YQPkgPatchListItem::operator< ( const QTreeWidgetItem & otherListViewItem ) const
593 {
594     const YQPkgPatchListItem * other = dynamic_cast<const YQPkgPatchListItem *> (&otherListViewItem);
595     int col = treeWidget()->sortColumn();
596     if ( other )
597     {
598         if ( col == categoryCol() )
599         {
600             return ( this->patchCategory() < other->patchCategory() );
601         }
602     }
603     return YQPkgObjListItem::operator<( otherListViewItem );
604 }
605
606
607
608 #include "YQPkgPatchList.moc"