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