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