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