]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/pkg/YQPackageSelector.cc
88f2e2f094c84ee536be5c3e3330e58922b3674a
[duncan/yast2-qt4.git] / src / pkg / YQPackageSelector.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       YQPackageSelector.cc
14   See also:   YQPackageSelectorHelp.cc
15
16   Author:     Stefan Hundhammer <sh@suse.de>
17
18   Textdomain "packages-qt"
19
20 /-*/
21
22 #define CHECK_DEPENDENCIES_ON_STARTUP                   1
23 #define DEPENDENCY_FEEDBACK_IF_OK                       1
24 #define AUTO_CHECK_DEPENDENCIES_DEFAULT                 true
25 #define ALWAYS_SHOW_PATCHES_VIEW_IF_PATCHES_AVAILABLE   0
26 #define GLOBAL_UPDATE_CONFIRMATION_THRESHOLD            20
27
28 #include <fstream>
29 #include <boost/bind.hpp>
30
31 #include <QHBoxLayout>
32 #include <QVBoxLayout>
33 #include <QAction>
34 #include <QShortcut>
35 #include <QApplication>
36 #include <QCheckBox>
37 #include <QDialog>
38 #include <QFileDialog>
39 #include <QLabel>
40 #include <QMap>
41 #include <QMenuBar>
42 #include <QMessageBox>
43 #include <QPushButton>
44 #include <QSplitter>
45 #include <QTabWidget>
46 #include <QTimer>
47 #include <QMenu>
48
49 #define y2log_component "qt-pkg"
50 #include <ycp/y2log.h>
51
52 #include "QY2LayoutUtils.h"
53
54 #include "YQPackageSelector.h"
55 #include "YQPkgChangeLogView.h"
56 #include "YQPkgChangesDialog.h"
57 #include "YQPkgConflictDialog.h"
58 #include "YQPkgConflictList.h"
59 #include "YQPkgDependenciesView.h"
60 #include "YQPkgDescriptionView.h"
61 #include "YQPkgDiskUsageList.h"
62 #include "YQPkgDiskUsageWarningDialog.h"
63 #include "YQPkgFileListView.h"
64 #include "YQPkgRepoFilterView.h"
65 #include "YQPkgRepoList.h"
66 #include "YQPkgLangList.h"
67 #include "YQPkgList.h"
68 #include "YQPkgPatchFilterView.h"
69 #include "YQPkgPatchList.h"
70 #include "YQPkgPatternList.h"
71 #include "YQPkgProductDialog.h"
72 #include "YQPkgRpmGroupTagsFilterView.h"
73 #include "YQPkgSearchFilterView.h"
74 #include "YQPkgSelList.h"
75 #include "YQPkgStatusFilterView.h"
76 #include "YQPkgTechnicalDetailsView.h"
77 #include "YQPkgTextDialog.h"
78 #include "YQPkgUpdateProblemFilterView.h"
79 #include "YQPkgVersionsView.h"
80 #include "zypp/SysContent.h"
81
82 #include "QY2ComboTabWidget.h"
83 #include "YQDialog.h"
84 #include "utf8.h"
85 #include "YQUI.h"
86 #include "YEvent.h"
87 #include "YQi18n.h"
88
89
90 using std::max;
91 using std::string;
92 using std::map;
93 using std::pair;
94
95 #define SPACING                         6
96 #define MARGIN                          4
97 #define DEFAULT_EXPORT_FILE_NAME        "user-packages.xml"
98 #define FAST_SOLVER                     1
99
100
101
102 YQPackageSelector::YQPackageSelector( YWidget *         parent,
103                                       long              modeFlags )
104     : YQPackageSelectorBase( parent, modeFlags )
105 {
106     _showChangesDialog          = true;
107     _autoDependenciesCheckBox   = 0;
108     _detailsViews               = 0;
109     _diskUsageList              = 0;
110     _filters                    = 0;
111     _repoFilterView             = 0;
112     _langList                   = 0;
113     _patternList                = 0;
114     _pkgChangeLogView           = 0;
115     _pkgDependenciesView        = 0;
116     _pkgDescriptionView         = 0;
117     _pkgFileListView            = 0;
118     _pkgList                    = 0;
119     _pkgTechnicalDetailsView    = 0;
120     _pkgVersionsView            = 0;
121     _rpmGroupTagsFilterView     = 0;
122     _searchFilterView           = 0;
123     _selList                    = 0;
124     _statusFilterView           = 0;
125     _updateProblemFilterView    = 0;
126     _patchFilterView            = 0;
127     _patchList                  = 0;
128     _excludeDevelPkgs           = 0;
129     _excludeDebugInfoPkgs       = 0;
130
131
132     if ( onlineUpdateMode() )   y2milestone( "Online update mode" );
133     if ( updateMode() )         y2milestone( "Update mode" );
134
135
136     basicLayout();
137     addMenus();         // Only after all widgets are created!
138     makeConnections();
139     emit loadData();
140
141     if ( _pkgList )
142         _pkgList->clear();
143
144     if ( _patchFilterView && onlineUpdateMode() )
145     {
146         if ( _filters && _patchFilterView && _patchList )
147         {
148             _filters->showPage( _patchFilterView );
149             _patchList->filter();
150         }
151     }
152     else if ( _repoFilterView && repoMode() )
153     {
154         if ( YQPkgRepoList::countEnabledRepositories() > 1 )
155         {
156             _filters->showPage( _repoFilterView );
157             _repoFilterView->filter();
158         }
159         else if ( _searchFilterView )
160         {
161             y2milestone( "No multiple repositories - falling back to search mode" );
162             _filters->showPage( _searchFilterView );
163             _searchFilterView->filter();
164             QTimer::singleShot( 0, _searchFilterView, SLOT( setFocus() ) );
165         }
166     }
167     else if ( _updateProblemFilterView )
168     {
169         _filters->showPage( _updateProblemFilterView );
170         _updateProblemFilterView->filter();
171
172     }
173     else if ( searchMode() && _searchFilterView )
174     {
175         _filters->showPage( _searchFilterView );
176         _searchFilterView->filter();
177         QTimer::singleShot( 0, _searchFilterView, SLOT( setFocus() ) );
178     }
179     else if ( summaryMode() && _statusFilterView )
180     {
181         _filters->showPage( _statusFilterView );
182         _statusFilterView->filter();
183     }
184     else if ( _patternList )
185     {
186         _filters->showPage( _patternList );
187         _patternList->filter();
188     }
189     else if ( _selList )
190     {
191         _filters->showPage( _selList );
192         _selList->filter();
193     }
194
195
196     if ( _diskUsageList )
197         _diskUsageList->updateDiskUsage();
198
199     y2milestone( "PackageSelector init done" );
200
201
202 #if CHECK_DEPENDENCIES_ON_STARTUP
203
204     if ( ! testMode() && ! onlineUpdateMode() )
205     {
206         // Fire up the first dependency check in the main loop.
207         // Don't do this right away - wait until all initializations are finished.
208         QTimer::singleShot( 0, this, SLOT( resolveDependencies() ) );
209   
210     }
211 #endif
212 }
213
214
215 void
216 YQPackageSelector::basicLayout()
217 {
218     QVBoxLayout *layout = new QVBoxLayout(this);
219     setLayout(layout);
220     layoutMenuBar(this);
221
222     QSplitter * outer_splitter = new QSplitter( Qt::Horizontal, this );
223     Q_CHECK_PTR( outer_splitter );
224
225     layout->addWidget(outer_splitter);
226
227     QWidget * left_pane  = layoutLeftPane ( outer_splitter );
228
229     QWidget * right_pane = layoutRightPane( outer_splitter );
230
231     outer_splitter->setResizeMode( left_pane,  QSplitter::KeepSize );
232     outer_splitter->setResizeMode( right_pane, QSplitter::Stretch );
233 }
234
235
236 QWidget *
237 YQPackageSelector::layoutLeftPane( QWidget *parent )
238 {
239     QSplitter * splitter = new QSplitter( Qt::Vertical, parent );
240     Q_CHECK_PTR( splitter );
241     splitter->setMargin( MARGIN );
242
243     QWidget * upper_vbox = new QWidget( splitter );
244     QVBoxLayout *layout = new QVBoxLayout(upper_vbox);
245     upper_vbox->setLayout(layout);
246     
247     Q_CHECK_PTR( upper_vbox );
248     layoutFilters( upper_vbox );
249     addVSpacing( upper_vbox, MARGIN );
250
251     QWidget * lower_vbox = new QWidget( splitter );
252     layout = new QVBoxLayout(lower_vbox);
253     lower_vbox->setLayout(layout);
254
255     addVSpacing( lower_vbox, MARGIN );
256     _diskUsageList = new YQPkgDiskUsageList( lower_vbox );
257     Q_CHECK_PTR( _diskUsageList );
258     layout->addWidget(_diskUsageList);
259
260     splitter->setResizeMode( upper_vbox, QSplitter::Stretch );
261     splitter->setResizeMode( lower_vbox, QSplitter::KeepSize );
262
263     return splitter;
264 }
265
266
267 void
268 YQPackageSelector::layoutFilters( QWidget *parent )
269 {
270     _filters = new QY2ComboTabWidget( _( "Fi&lter:" ), parent );
271     Q_CHECK_PTR( _filters );
272     parent->layout()->addWidget(_filters);
273
274     //
275     // Update problem view
276     //
277
278     if ( updateMode() )
279     {
280         if ( YQPkgUpdateProblemFilterView::haveProblematicPackages()
281              || testMode() )
282         {
283             _updateProblemFilterView = new YQPkgUpdateProblemFilterView( parent);
284             Q_CHECK_PTR( _updateProblemFilterView );
285             _filters->addPage( _( "Update Problems" ), _updateProblemFilterView );
286         }
287     }
288
289
290     //
291     // Patches view
292     //
293
294     if ( onlineUpdateMode()
295 #if ALWAYS_SHOW_PATCHES_VIEW_IF_PATCHES_AVAILABLE
296          || ! zyppPool().empty<zypp::Patch>()
297 #endif
298          )
299         addPatchFilterView();
300
301
302     //
303     // Patterns view
304     //
305
306     if ( ! zyppPool().empty<zypp::Pattern>() || testMode() )
307     {
308         _patternList = new YQPkgPatternList( parent, true );
309         Q_CHECK_PTR( _patternList );
310         _filters->addPage( _( "Patterns" ), _patternList );
311
312         connect( _patternList,          SIGNAL( statusChanged()                 ),
313                  this,                  SLOT  ( autoResolveDependencies()       ) );
314
315         connect( this,                  SIGNAL( refresh()                       ),
316                  _patternList,          SLOT  ( updateItemStates()              ) );
317
318         if ( _pkgConflictDialog )
319         {
320             connect( _pkgConflictDialog, SIGNAL( updatePackages()               ),
321                      _patternList,       SLOT  ( updateItemStates()             ) );
322         }
323     }
324
325
326     //
327     // Selections view
328     //
329
330     if ( ! zyppPool().empty<zypp::Selection>() || testMode() )
331     {
332
333         _selList = new YQPkgSelList( parent, true );
334         Q_CHECK_PTR( _selList );
335         _filters->addPage( _( "Selections" ), _selList );
336
337         connect( _selList,              SIGNAL( statusChanged()                 ),
338                  this,                  SLOT  ( autoResolveDependencies()       ) );
339
340         connect( this,                  SIGNAL( refresh()                       ),
341                  _selList,               SLOT  ( updateItemStates()             ) );
342
343         if ( _pkgConflictDialog )
344         {
345             connect( _pkgConflictDialog, SIGNAL( updatePackages()               ),
346                      _selList,           SLOT  ( updateItemStates()             ) );
347         }
348     }
349
350
351     //
352     // RPM group tags view
353     //
354
355     _rpmGroupTagsFilterView = new YQPkgRpmGroupTagsFilterView( parent );
356     Q_CHECK_PTR( _rpmGroupTagsFilterView );
357     _filters->addPage( _( "Package Groups" ), _rpmGroupTagsFilterView );
358
359     connect( this,                      SIGNAL( loadData() ),
360              _rpmGroupTagsFilterView,   SLOT  ( filter()   ) );
361
362
363     //
364     // Languages view
365     //
366
367     _langList = new YQPkgLangList( parent );
368     Q_CHECK_PTR( _langList );
369
370     _filters->addPage( _( "Languages" ), _langList );
371     _langList->setSizePolicy( QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ) ); // hor/vert
372
373     connect( _langList,         SIGNAL( statusChanged()                 ),
374              this,              SLOT  ( autoResolveDependencies()       ) );
375
376     connect( this,              SIGNAL( refresh()                       ),
377              _langList,         SLOT  ( updateItemStates()              ) );
378
379
380     //
381     // Repository view
382     //
383
384     _repoFilterView = new YQPkgRepoFilterView( parent );
385     Q_CHECK_PTR( _repoFilterView );
386     _filters->addPage( _( "Repositories" ), _repoFilterView );
387
388
389     //
390     // Package search view
391     //
392
393     _searchFilterView = new YQPkgSearchFilterView( parent );
394     Q_CHECK_PTR( _searchFilterView );
395     _filters->addPage( _( "Search" ), _searchFilterView );
396
397
398     //
399     // Status change view
400     //
401
402     _statusFilterView = new YQPkgStatusFilterView( parent );
403     Q_CHECK_PTR( _statusFilterView );
404     _filters->addPage( _( "Installation Summary" ), _statusFilterView );
405
406
407 #if 0
408     // DEBUG
409
410     _filters->addPage( _( "Keywords"   ), new QLabel( "Keywords\nfilter\n\nfor future use", 0 ) );
411     _filters->addPage( _( "MIME Types" ), new QLabel( "MIME Types\nfilter\n\nfor future use" , 0 ) );
412 #endif
413
414 }
415
416
417 QWidget *
418 YQPackageSelector::layoutRightPane( QWidget *parent )
419 {
420     QWidget * right_pane_vbox = new QWidget( parent );
421     
422     QVBoxLayout *layout = new QVBoxLayout(right_pane_vbox);
423     right_pane_vbox->setLayout(layout);
424
425     Q_CHECK_PTR( right_pane_vbox );
426
427     layout->setMargin( MARGIN );
428     
429
430     QSplitter * splitter = new QSplitter( Qt::Vertical, right_pane_vbox );
431     Q_CHECK_PTR( splitter );
432     layout->addWidget(splitter);
433
434     Q_CHECK_PTR( splitter );
435     layoutPkgList( splitter );
436
437     addVSpacing( splitter, MARGIN );
438
439     layoutDetailsViews( splitter );
440
441     layoutButtons( splitter );
442
443     return right_pane_vbox;
444 }
445
446
447 void
448 YQPackageSelector::layoutPkgList( QWidget *parent )
449 {
450     _pkgList= new YQPkgList( parent );
451     Q_CHECK_PTR( _pkgList );
452
453     connect( _pkgList,  SIGNAL( statusChanged()           ),
454              this,      SLOT  ( autoResolveDependencies() ) );
455 }
456
457
458 void
459 YQPackageSelector::layoutDetailsViews( QWidget *parent )
460 {
461     bool haveInstalledPkgs = YQPkgList::haveInstalledPkgs();
462
463     QWidget * details_vbox = new QWidget( parent );
464     Q_CHECK_PTR( details_vbox );
465
466     QVBoxLayout *layout = new QVBoxLayout(details_vbox);
467     details_vbox->setLayout(layout);
468     //details_vbox->setMinimumSize( 0, 0 );
469
470     addVSpacing( details_vbox, 8 );
471
472     _detailsViews = new QTabWidget( details_vbox );
473     layout->addWidget(_detailsViews);
474
475     Q_CHECK_PTR( _detailsViews );
476     _detailsViews->setMargin( MARGIN );
477
478     // _detailsViews->setTabPosition( QTabWidget::Bottom );
479
480
481     //
482     // Description
483     //
484
485     _pkgDescriptionView = new YQPkgDescriptionView( _detailsViews );
486     Q_CHECK_PTR( _pkgDescriptionView );
487
488     _detailsViews->addTab( _pkgDescriptionView, _( "D&escription" ) );
489     _detailsViews->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); // hor/vert
490
491     connect( _pkgList,                  SIGNAL( selectionChanged    ( ZyppSel ) ),
492              _pkgDescriptionView,       SLOT  ( showDetailsIfVisible( ZyppSel ) ) );
493
494     //
495     // Technical details
496     //
497
498     _pkgTechnicalDetailsView = new YQPkgTechnicalDetailsView( _detailsViews );
499     Q_CHECK_PTR( _pkgTechnicalDetailsView );
500
501     _detailsViews->addTab( _pkgTechnicalDetailsView, _( "&Technical Data" ) );
502
503     connect( _pkgList,                  SIGNAL( selectionChanged    ( ZyppSel ) ),
504              _pkgTechnicalDetailsView,  SLOT  ( showDetailsIfVisible( ZyppSel ) ) );
505
506
507     //
508     // Dependencies
509     //
510
511     _pkgDependenciesView = new YQPkgDependenciesView( _detailsViews );
512     Q_CHECK_PTR( _pkgDependenciesView );
513
514     _detailsViews->addTab( _pkgDependenciesView, _( "Dependencies" ) );
515     _detailsViews->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); // hor/vert
516
517     connect( _pkgList,                  SIGNAL( selectionChanged    ( ZyppSel ) ),
518              _pkgDependenciesView,      SLOT  ( showDetailsIfVisible( ZyppSel ) ) );
519
520
521
522     //
523     // Versions
524     //
525
526     _pkgVersionsView = new YQPkgVersionsView( _detailsViews,
527                                               true );   // userCanSwitchVersions
528     Q_CHECK_PTR( _pkgVersionsView );
529
530     _detailsViews->addTab( _pkgVersionsView, _( "&Versions" ) );
531
532     connect( _pkgList,          SIGNAL( selectionChanged    ( ZyppSel ) ),
533              _pkgVersionsView,  SLOT  ( showDetailsIfVisible( ZyppSel ) ) );
534
535
536     //
537     // File List
538     //
539
540     if ( haveInstalledPkgs )    // file list information is only available for installed pkgs
541     {
542         _pkgFileListView = new YQPkgFileListView( _detailsViews );
543         Q_CHECK_PTR( _pkgFileListView );
544
545         _detailsViews->addTab( _pkgFileListView, _( "File List" ) );
546         _detailsViews->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); // hor/vert
547
548         connect( _pkgList,              SIGNAL( selectionChanged    ( ZyppSel ) ),
549                  _pkgFileListView,      SLOT  ( showDetailsIfVisible( ZyppSel ) ) );
550     }
551
552
553     //
554     // Change Log
555     //
556
557     if ( haveInstalledPkgs )    // change log information is only available for installed pkgs
558     {
559         _pkgChangeLogView = new YQPkgChangeLogView( _detailsViews );
560         Q_CHECK_PTR( _pkgChangeLogView );
561
562         _detailsViews->addTab( _pkgChangeLogView, _( "Change Log" ) );
563         _detailsViews->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); // hor/vert
564
565         connect( _pkgList,              SIGNAL( selectionChanged    ( ZyppSel ) ),
566                  _pkgChangeLogView,     SLOT  ( showDetailsIfVisible( ZyppSel ) ) );
567     }
568 }
569
570
571 void
572 YQPackageSelector::layoutButtons( QWidget *parent )
573 {
574     QWidget * button_box = new QWidget( parent );
575     Q_CHECK_PTR( button_box );
576
577     QHBoxLayout *layout = new QHBoxLayout(button_box);
578     button_box->setLayout(layout);
579
580     layout->setSpacing( SPACING );
581
582     // Button: Dependency check
583     // Translators: Please keep this short!
584     _checkDependenciesButton = new QPushButton( _( "Chec&k" ), button_box );
585     layout->addWidget(_checkDependenciesButton);
586
587     Q_CHECK_PTR( _checkDependenciesButton );
588     _checkDependenciesButton->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); // hor/vert
589     _normalButtonBackground = _checkDependenciesButton->paletteBackgroundColor();
590
591     connect( _checkDependenciesButton,  SIGNAL( clicked() ),
592              this,                      SLOT  ( manualResolvePackageDependencies() ) );
593
594
595     // Checkbox: Automatically check dependencies for every package status change?
596     // Translators: Please keep this short!
597     _autoDependenciesCheckBox = new QCheckBox( _( "A&utocheck" ), button_box );
598     Q_CHECK_PTR( _autoDependenciesCheckBox );
599     layout->addWidget(_autoDependenciesCheckBox);
600
601     _autoDependenciesCheckBox->setChecked( AUTO_CHECK_DEPENDENCIES_DEFAULT );
602
603     addHStretch( button_box );
604
605     QPushButton * cancel_button = new QPushButton( _( "&Cancel" ), button_box );
606     Q_CHECK_PTR( cancel_button );
607     layout->addWidget(cancel_button);
608
609     cancel_button->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); // hor/vert
610
611     connect( cancel_button, SIGNAL( clicked() ),
612              this,          SLOT  ( reject()   ) );
613
614
615     QPushButton * accept_button = new QPushButton( _( "&Accept" ), button_box );
616     Q_CHECK_PTR( accept_button );
617     layout->addWidget(accept_button);
618     accept_button->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); // hor/vert
619
620     connect( accept_button, SIGNAL( clicked() ),
621              this,          SLOT  ( accept()   ) );
622
623     button_box->setFixedHeight( button_box->sizeHint().height() );
624 }
625
626
627 void
628 YQPackageSelector::layoutMenuBar( QWidget *parent )
629 {
630     _menuBar = new QMenuBar( parent );
631     Q_CHECK_PTR( _menuBar );
632
633     parent->layout()->addWidget(_menuBar);
634
635     _fileMenu           = 0;
636     _viewMenu           = 0;
637     _pkgMenu            = 0;
638     _patchMenu          = 0;
639     _extrasMenu         = 0;
640     _helpMenu           = 0;
641
642 }
643
644
645 void
646 YQPackageSelector::addMenus()
647 {
648     //
649     // File menu
650     //
651
652     _fileMenu = new QMenu( _menuBar );
653     Q_CHECK_PTR( _fileMenu );
654     _menuBar->insertItem( _( "&File" ), _fileMenu );
655
656     _fileMenu->insertItem( _( "&Import..." ),   this, SLOT( pkgImport() ) );
657     _fileMenu->insertItem( _( "&Export..." ),   this, SLOT( pkgExport() ) );
658
659     _fileMenu->insertSeparator();
660
661     _fileMenu->insertItem( _( "E&xit -- Discard Changes" ), this, SLOT( reject() ) );
662     _fileMenu->insertItem( _( "&Quit -- Save Changes"    ), this, SLOT( accept() ) );
663
664
665     if ( _pkgList )
666     {
667         //
668         // View menu
669         //
670
671         _viewMenu = new QMenu( _menuBar );
672         Q_CHECK_PTR( _viewMenu );
673         _menuBar->insertItem( _( "&View" ), _viewMenu );
674
675         // Translators: This is about packages ending in "-devel", so don't translate that "-devel"!
676         _viewShowDevelID = _viewMenu->insertItem( _( "Show -de&vel Packages" ),
677                                                        this, SLOT( pkgExcludeRulesChanged( int ) ), Qt::Key_F7 );
678         _viewMenu->setItemChecked( _viewShowDevelID, true );
679         _excludeDevelPkgs = new YQPkgObjList::ExcludeRule( _pkgList, QRegExp( ".*-devel(-\\d+bit)?$" ), _pkgList->nameCol() );
680         Q_CHECK_PTR( _excludeDevelPkgs );
681         _excludeDevelPkgs->enable( false );
682
683         // Translators: This is about packages ending in "-debuginfo", so don't translate that "-debuginfo"!
684         _viewShowDebugInfoID = _viewMenu->insertItem( _( "Show -&debuginfo Packages" ),
685                                                        this, SLOT( pkgExcludeRulesChanged( int ) ), Qt::Key_F8 );
686         _viewMenu->setItemChecked( _viewShowDebugInfoID, true );
687         _excludeDebugInfoPkgs = new YQPkgObjList::ExcludeRule( _pkgList, QRegExp( ".*-debuginfo$" ), _pkgList->nameCol() );
688         Q_CHECK_PTR( _excludeDebugInfoPkgs );
689         _excludeDebugInfoPkgs->enable( false );
690
691
692         //
693         // Package menu
694         //
695
696         _pkgMenu = new QMenu( _menuBar );
697         Q_CHECK_PTR( _pkgMenu );
698         _menuBar->insertItem( _( "&Package" ), _pkgMenu );
699
700         _pkgList->actionSetCurrentInstall->addTo( _pkgMenu );
701         _pkgList->actionSetCurrentDontInstall->addTo( _pkgMenu );
702         _pkgList->actionSetCurrentKeepInstalled->addTo( _pkgMenu );
703         _pkgList->actionSetCurrentDelete->addTo( _pkgMenu );
704         _pkgList->actionSetCurrentUpdate->addTo( _pkgMenu );
705         _pkgList->actionSetCurrentTaboo->addTo( _pkgMenu );
706         _pkgList->actionSetCurrentProtected->addTo( _pkgMenu );
707
708         _pkgMenu->insertSeparator();
709
710         _pkgList->actionInstallSourceRpm->addTo( _pkgMenu );
711         _pkgList->actionDontInstallSourceRpm->addTo( _pkgMenu );
712
713         _pkgMenu->insertSeparator();
714         QMenu * submenu = _pkgList->addAllInListSubMenu( _pkgMenu );
715         Q_CHECK_PTR( submenu );
716
717         submenu->insertSeparator();
718         _pkgList->actionInstallListSourceRpms->addTo( submenu );
719         _pkgList->actionDontInstallListSourceRpms->addTo( submenu );
720
721
722         //
723         // Submenu for all packages
724         //
725
726         submenu = new QMenu( _pkgMenu );
727         Q_CHECK_PTR( submenu );
728
729         // Translators: Unlike the "all in this list" submenu, this submenu
730         // refers to all packages globally, not only to those that are
731         // currently visible in the packages list.
732         _pkgMenu->insertItem( _( "All Packages" ), submenu );
733
734         submenu->insertItem( _( "Update if newer version available" ),
735                              this, SLOT( globalUpdatePkg() ) );
736
737         submenu->insertItem( _( "Update unconditionally" ),
738                              this, SLOT( globalUpdatePkgForce() ) );
739     }
740
741
742     if ( _patchList )
743     {
744         //
745         // Patch menu
746         //
747
748         _patchMenu = new QMenu( _menuBar );
749         Q_CHECK_PTR( _patchMenu );
750         _menuBar->insertItem( _( "&Patch" ), _patchMenu );
751
752         _patchList->actionSetCurrentInstall->addTo( _patchMenu );
753         _patchList->actionSetCurrentDontInstall->addTo( _patchMenu );
754         _patchList->actionSetCurrentKeepInstalled->addTo( _patchMenu );
755 #if ENABLE_DELETING_PATCHES
756         _patchList->actionSetCurrentDelete->addTo( _patchMenu );
757 #endif
758         _patchList->actionSetCurrentUpdate->addTo( _patchMenu );
759         _patchList->actionSetCurrentTaboo->addTo( _patchMenu );
760
761         _patchMenu->insertSeparator();
762         _patchList->addAllInListSubMenu( _patchMenu );
763     }
764
765
766     //
767     // Extras menu
768     //
769
770     _extrasMenu = new QMenu( _menuBar );
771     Q_CHECK_PTR( _extrasMenu );
772     _menuBar->insertItem( _( "&Extras" ), _extrasMenu );
773
774     _extrasMenu->insertItem( _( "Show &Products"                  ), this, SLOT( showProducts()    ) );
775     _extrasMenu->insertItem( _( "Show &Automatic Package Changes" ), this, SLOT( showAutoPkgList() ), Qt::CTRL + Qt::Key_A );
776     _extrasMenu->insertItem( _( "&Verify System"                  ), this, SLOT( verifySystem()    ) );
777
778     _extrasMenu->insertSeparator();
779
780     // Translators: This is about packages ending in "-devel", so don't translate that "-devel"!
781     _extrasMenu->insertItem( _( "Install All Matching -&devel Packages" ), this, SLOT( installDevelPkgs() ) );
782
783     // Translators: This is about packages ending in "-debuginfo", so don't translate that "-debuginfo"!
784     _extrasMenu->insertItem( _( "Install All Matching -de&buginfo Packages" ), this, SLOT( installDebugInfoPkgs() ) );
785
786     _extrasMenu->insertSeparator();
787
788     if ( _pkgConflictDialog )
789         _extrasMenu->insertItem( _( "Generate Dependency Resolver &Test Case" ),
790                                     _pkgConflictDialog, SLOT( askCreateSolverTestCase() ) );
791
792     if ( _actionResetIgnoredDependencyProblems )
793         _actionResetIgnoredDependencyProblems->addTo( _extrasMenu );
794
795
796 #ifdef FIXME
797     if ( _patchList )
798         _patchList->actionShowRawPatchInfo->addTo( _extrasMenu );
799 #endif
800
801
802     //
803     // Help menu
804     //
805
806     _helpMenu = new QMenu( _menuBar );
807     Q_CHECK_PTR( _helpMenu );
808     _menuBar->insertSeparator();
809     _menuBar->insertItem( _( "&Help" ), _helpMenu );
810
811     // Note: The help functions and their texts are moved out
812     // to a separate source file YQPackageSelectorHelp.cc
813
814     // Menu entry for help overview
815     _helpMenu->insertItem( _( "&Overview" ), this, SLOT( help()         ), Qt::Key_F1 );
816
817     // Menu entry for help about used symbols ( icons )
818     _helpMenu->insertItem( _( "&Symbols" ), this, SLOT( symbolHelp()    ), Qt::SHIFT + Qt::Key_F1 );
819
820     // Menu entry for keyboard help
821     _helpMenu->insertItem( _( "&Keys" ), this, SLOT( keyboardHelp() )                 );
822 }
823
824
825 void
826 YQPackageSelector::connectFilter( QWidget * filter,
827                                   QWidget * pkgList,
828                                   bool hasUpdateSignal )
829 {
830     if ( ! filter  )    return;
831     if ( ! pkgList )    return;
832
833     if ( _filters )
834     {
835         connect( _filters,      SIGNAL( currentChanged(QWidget *) ),
836                  filter,        SLOT  ( filterIfVisible()            ) );
837     }
838
839     connect( this,      SIGNAL( refresh()         ),
840              filter,    SLOT  ( filterIfVisible() ) );
841
842     connect( filter,    SIGNAL( filterStart()   ),
843              pkgList,   SLOT  ( clear()         ) );
844
845     connect( filter,    SIGNAL( filterMatch( ZyppSel, ZyppPkg ) ),
846              pkgList,   SLOT  ( addPkgItem ( ZyppSel, ZyppPkg ) ) );
847
848     connect( filter,    SIGNAL( filterFinished()  ),
849              pkgList,   SLOT  ( selectSomething() ) );
850
851     connect( filter,    SIGNAL( filterFinished()       ),
852              pkgList,   SLOT  ( logExcludeStatistics() ) );
853
854
855     if ( hasUpdateSignal )
856     {
857         connect( filter,                SIGNAL( updatePackages()   ),
858                  pkgList,               SLOT  ( updateItemStates() ) );
859
860         if ( _diskUsageList )
861         {
862             connect( filter,            SIGNAL( updatePackages()  ),
863                      _diskUsageList,    SLOT  ( updateDiskUsage() ) );
864         }
865     }
866 }
867
868
869 void
870 YQPackageSelector::makeConnections()
871 {
872     connect( this, SIGNAL( resolvingStarted()   ),
873              this, SLOT  ( animateCheckButton() ) );
874
875     connect( this, SIGNAL( resolvingFinished()  ),
876              this, SLOT  ( restoreCheckButton() ) );
877
878     connectFilter( _updateProblemFilterView,    _pkgList, false );
879     connectFilter( _patternList,                _pkgList );
880     connectFilter( _selList,                    _pkgList );
881     connectFilter( _repoFilterView,             _pkgList, false );
882     connectFilter( _rpmGroupTagsFilterView,     _pkgList, false );
883     connectFilter( _langList,                   _pkgList );
884     connectFilter( _statusFilterView,           _pkgList, false );
885     connectFilter( _searchFilterView,           _pkgList, false );
886
887     if ( _searchFilterView && _pkgList )
888     {
889         connect( _searchFilterView,     SIGNAL( message( const QString & ) ),
890                  _pkgList,              SLOT  ( message( const QString & ) ) );
891     }
892
893     if ( _repoFilterView && _pkgList )
894     {
895         connect( _repoFilterView,       SIGNAL( filterNearMatch  ( ZyppSel, ZyppPkg ) ),
896                  _pkgList,              SLOT  ( addPkgItemDimmed ( ZyppSel, ZyppPkg ) ) );
897     }
898
899     if ( _pkgList && _diskUsageList )
900     {
901
902         connect( _pkgList,              SIGNAL( statusChanged()   ),
903                  _diskUsageList,        SLOT  ( updateDiskUsage() ) );
904     }
905
906     connectPatchList();
907
908
909
910     //
911     // Connect package conflict dialog
912     //
913
914     if ( _pkgConflictDialog )
915     {
916         if (_pkgList )
917         {
918             connect( _pkgConflictDialog,        SIGNAL( updatePackages()   ),
919                      _pkgList,                  SLOT  ( updateItemStates() ) );
920         }
921
922         if ( _patternList )
923         {
924             connect( _pkgConflictDialog,        SIGNAL( updatePackages()   ),
925                      _patternList,              SLOT  ( updateItemStates() ) );
926         }
927
928         if ( _selList )
929         {
930             connect( _pkgConflictDialog,        SIGNAL( updatePackages()   ),
931                      _selList,                  SLOT  ( updateItemStates() ) );
932         }
933
934         if ( _diskUsageList )
935         {
936             connect( _pkgConflictDialog,        SIGNAL( updatePackages()   ),
937                      _diskUsageList,            SLOT  ( updateDiskUsage()  ) );
938         }
939     }
940
941
942     //
943     // Connect package versions view
944     //
945
946     if ( _pkgVersionsView && _pkgList )
947     {
948         connect( _pkgVersionsView,      SIGNAL( candidateChanged( ZyppObj ) ),
949                  _pkgList,              SLOT  ( updateItemData()    ) );
950     }
951
952
953     //
954     // Hotkey to enable "patches" filter view on the fly
955     //
956
957     QShortcut * accel = new QShortcut( Qt::Key_F2, this, SLOT( hotkeyInsertPatchFilterView() ) );
958     Q_CHECK_PTR( accel );
959
960     //
961     // Update actions just before opening menus
962     //
963
964     if ( _pkgMenu && _pkgList )
965     {
966         connect( _pkgMenu,      SIGNAL( aboutToShow()   ),
967                  _pkgList,      SLOT  ( updateActions() ) );
968     }
969
970     if ( _patchMenu && _patchList )
971     {
972         connect( _patchMenu,    SIGNAL( aboutToShow()   ),
973                  _patchList,    SLOT  ( updateActions() ) );
974     }
975 }
976
977
978 void
979 YQPackageSelector::animateCheckButton()
980 {
981     if ( _checkDependenciesButton )
982     {
983         _checkDependenciesButton->setPaletteBackgroundColor( QColor( 0xE0, 0xE0, 0xF8 ) );
984         _checkDependenciesButton->repaint();
985     }
986 }
987
988
989 void
990 YQPackageSelector::restoreCheckButton()
991 {
992     if ( _checkDependenciesButton )
993         _checkDependenciesButton->setPaletteBackgroundColor( _normalButtonBackground );
994 }
995
996
997 void
998 YQPackageSelector::autoResolveDependencies()
999 {
1000     if ( _autoDependenciesCheckBox && ! _autoDependenciesCheckBox->isChecked() )
1001         return;
1002
1003     resolveDependencies();
1004 }
1005
1006
1007 int
1008 YQPackageSelector::manualResolvePackageDependencies()
1009 {
1010     if ( ! _pkgConflictDialog )
1011     {
1012         y2error( "No package conflict dialog existing" );
1013         return QDialog::Accepted;
1014     }
1015
1016     YQUI::ui()->busyCursor();
1017     int result = _pkgConflictDialog->solveAndShowConflicts();
1018     YQUI::ui()->normalCursor();
1019
1020 #if DEPENDENCY_FEEDBACK_IF_OK
1021
1022     if ( result == QDialog::Accepted )
1023     {
1024         QMessageBox::information( this, "",
1025                                   _( "All package dependencies are OK." ),
1026                                   QMessageBox::Ok );
1027     }
1028 #endif
1029
1030     return result;
1031 }
1032
1033
1034 void
1035 YQPackageSelector::addPatchFilterView()
1036 {
1037     if ( ! _patchFilterView )
1038     {
1039         _patchFilterView = new YQPkgPatchFilterView( this );
1040         Q_CHECK_PTR( _patchFilterView );
1041         _filters->addPage( _( "Patches" ), _patchFilterView );
1042
1043         _patchList = _patchFilterView->patchList();
1044         Q_CHECK_PTR( _patchList );
1045
1046         connectPatchList();
1047     }
1048 }
1049
1050
1051 void
1052 YQPackageSelector::hotkeyInsertPatchFilterView()
1053 {
1054     if ( ! _patchFilterView )
1055     {
1056         y2milestone( "Activating patches filter view" );
1057
1058         addPatchFilterView();
1059         connectPatchList();
1060
1061         _filters->showPage( _patchFilterView );
1062         _pkgList->clear();
1063         _patchList->filter();
1064     }
1065 }
1066
1067
1068 void
1069 YQPackageSelector::connectPatchList()
1070 {
1071     if ( _pkgList && _patchList )
1072     {
1073         connectFilter( _patchList, _pkgList );
1074
1075         connect( _patchList, SIGNAL( filterMatch   ( const QString &, const QString &, FSize ) ),
1076                  _pkgList,   SLOT  ( addPassiveItem( const QString &, const QString &, FSize ) ) );
1077
1078         connect( _patchList,            SIGNAL( statusChanged()                 ),
1079                  this,                  SLOT  ( autoResolveDependencies()       ) );
1080
1081         if ( _pkgConflictDialog )
1082         {
1083             connect( _pkgConflictDialog,SIGNAL( updatePackages()                ),
1084                      _patchList,        SLOT  ( updateItemStates()              ) );
1085         }
1086
1087         connect( this,                  SIGNAL( refresh()                       ),
1088                  _patchList,            SLOT  ( updateItemStates()              ) );
1089
1090     }
1091 }
1092
1093
1094 void
1095 YQPackageSelector::pkgExport()
1096 {
1097     QString filename = YQUI::ui()->askForSaveFileName( QString( DEFAULT_EXPORT_FILE_NAME ),     // startsWith
1098                                                        QString( "*.xml;;*" ),                   // filter
1099                                                        _( "Save Package List" ) );
1100
1101     if ( ! filename.isEmpty() )
1102     {
1103         zypp::syscontent::Writer writer;
1104         const zypp::ResPool & pool = zypp::getZYpp()->pool();
1105
1106         // The ZYPP obfuscated C++ contest proudly presents:
1107
1108         for_each( pool.begin(), pool.end(),
1109                   boost::bind( &zypp::syscontent::Writer::addIf,
1110                                boost::ref( writer ),
1111                                _1 ) );
1112         // Yuck. What a mess.
1113         //
1114         // Does anybody seriously believe this kind of thing is easier to read,
1115         // let alone use? Get real. This is an argument in favour of all C++
1116         // haters. And it's one that is really hard to counter.
1117         //
1118         // -sh 2006-12-13
1119
1120         try
1121         {
1122             std::ofstream exportFile( toUTF8( filename ).c_str() );
1123             exportFile.exceptions( std::ios_base::badbit | std::ios_base::failbit );
1124             exportFile << writer;
1125
1126             y2milestone( "Package list exported to %s", (const char *) filename );
1127         }
1128         catch ( std::exception & exception )
1129         {
1130             y2warning( "Error exporting package list to %s", (const char *) filename );
1131
1132             // The export might have left over a partially written file.
1133             // Try to delete it. Don't care if it doesn't exist and unlink() fails.
1134             (void) unlink( (const char *) filename );
1135
1136             // Post error popup
1137             QMessageBox::warning( this,                                         // parent
1138                                   _( "Error" ),                                 // caption
1139                                   _( "Error exporting package list to %1" ).arg( filename ),
1140                                   QMessageBox::Ok | QMessageBox::Default,       // button0
1141                                   Qt::NoButton,                 // button1
1142                                   Qt::NoButton );                       // button2
1143         }
1144     }
1145 }
1146
1147
1148 void
1149 YQPackageSelector::pkgImport()
1150 {
1151     QString filename =  QFileDialog::getOpenFileName( this, _( "Load Package List" ), DEFAULT_EXPORT_FILE_NAME,         // startsWi
1152     "*.xml+;;*"// filter
1153     );
1154
1155     if ( ! filename.isEmpty() )
1156     {
1157         y2milestone( "Importing package list from %s", (const char *) filename );
1158
1159         try
1160         {
1161             std::ifstream importFile( toUTF8( filename ).c_str() );
1162             zypp::syscontent::Reader reader( importFile );
1163
1164             //
1165             // Put reader contents into maps
1166             //
1167
1168             typedef zypp::syscontent::Reader::Entry     ZyppReaderEntry;
1169             typedef std::pair<string, ZyppReaderEntry>  ImportMapPair;
1170
1171             map<string, ZyppReaderEntry> importPkg;
1172             map<string, ZyppReaderEntry> importPatterns;
1173
1174             for ( zypp::syscontent::Reader::const_iterator it = reader.begin();
1175                   it != reader.end();
1176                   ++ it )
1177             {
1178                 string kind = it->kind();
1179
1180                 if      ( kind == "package" )   importPkg.insert     ( ImportMapPair( it->name(), *it ) );
1181                 else if ( kind == "pattern" )   importPatterns.insert( ImportMapPair( it->name(), *it ) );
1182             }
1183
1184             y2debug( "Found %zu packages and %zu patterns in %s",
1185                      importPkg.size(),
1186                      importPatterns.size(),
1187                      (const char *) filename );
1188
1189
1190             //
1191             // Set status of all patterns and packages according to import map
1192             //
1193
1194             for ( ZyppPoolIterator it = zyppPatternsBegin();
1195                   it != zyppPatternsEnd();
1196                   ++it )
1197             {
1198                 ZyppSel selectable = *it;
1199                 importSelectable( *it, importPatterns.find( selectable->name() ) != importPatterns.end(), "pattern" );
1200             }
1201
1202             for ( ZyppPoolIterator it = zyppPkgBegin();
1203                   it != zyppPkgEnd();
1204                   ++it )
1205             {
1206                 ZyppSel selectable = *it;
1207                 importSelectable( *it, importPkg.find( selectable->name() ) != importPkg.end(), "package" );
1208             }
1209
1210
1211             //
1212             // Display result
1213             //
1214
1215             emit refresh();
1216
1217             if ( _statusFilterView )
1218             {
1219                 // Switch to "Installation Summary" filter view
1220
1221                 _filters->showPage( _statusFilterView );
1222                 _statusFilterView->filter();
1223             }
1224
1225         }
1226         catch ( const zypp::Exception & exception )
1227         {
1228             y2warning( "Error reading package list from %s", (const char *) filename );
1229
1230             // Post error popup
1231             QMessageBox::warning( this,                                         // parent
1232                                   _( "Error" ),                                 // caption
1233                                   _( "Error loading package list from %1" ).arg( filename ),
1234                                   QMessageBox::Ok | QMessageBox::Default,       // button0
1235                                   QMessageBox::NoButton,                        // button1
1236                                   QMessageBox::NoButton );                      // button2
1237         }
1238     }
1239 }
1240
1241
1242 void
1243 YQPackageSelector::importSelectable( ZyppSel            selectable,
1244                                      bool               isWanted,
1245                                      const char *       kind )
1246 {
1247     ZyppStatus oldStatus = selectable->status();
1248     ZyppStatus newStatus = oldStatus;
1249
1250     if ( isWanted )
1251     {
1252         //
1253         // Make sure this selectable does not get installed
1254         //
1255
1256         switch ( oldStatus )
1257         {
1258             case S_Install:
1259             case S_AutoInstall:
1260             case S_KeepInstalled:
1261             case S_Protected:
1262             case S_Update:
1263             case S_AutoUpdate:
1264                 newStatus = oldStatus;
1265                 break;
1266
1267             case S_Del:
1268             case S_AutoDel:
1269                 newStatus = S_KeepInstalled;
1270                 y2debug( "Keeping %s %s", kind, selectable->name().c_str() );
1271                 break;
1272
1273             case S_NoInst:
1274             case S_Taboo:
1275
1276                 if ( selectable->hasCandidateObj() )
1277                 {
1278                     newStatus = S_Install;
1279                     y2debug( "Adding %s %s", kind, selectable->name().c_str() );
1280                 }
1281                 else
1282                 {
1283                     y2debug( "Can't add %s %s: No candidate",
1284                              kind, selectable->name().c_str() );
1285                 }
1286                 break;
1287         }
1288     }
1289     else // ! isWanted
1290     {
1291         //
1292         // Make sure this selectable does not get installed
1293         //
1294
1295         switch ( oldStatus )
1296         {
1297             case S_Install:
1298             case S_AutoInstall:
1299             case S_KeepInstalled:
1300             case S_Protected:
1301             case S_Update:
1302             case S_AutoUpdate:
1303                 newStatus = S_Del;
1304                 y2debug( "Deleting %s %s", kind, selectable->name().c_str() );
1305                 break;
1306
1307             case S_Del:
1308             case S_AutoDel:
1309             case S_NoInst:
1310             case S_Taboo:
1311                 newStatus = oldStatus;
1312                 break;
1313         }
1314     }
1315
1316     if ( oldStatus != newStatus )
1317         selectable->set_status( newStatus );
1318 }
1319
1320
1321 void
1322 YQPackageSelector::globalUpdatePkg( bool force )
1323 {
1324     if ( ! _pkgList )
1325         return;
1326
1327     int count = _pkgList->globalSetPkgStatus( S_Update, force,
1328                                               true ); // countOnly
1329     y2milestone( "%d pkgs found for update", count );
1330
1331     if ( count >= GLOBAL_UPDATE_CONFIRMATION_THRESHOLD )
1332     {
1333         if ( QMessageBox::question( this, "",   // caption
1334                                     // Translators: %1 is the number of affected packages
1335                                     _( "%1 packages will be updated" ).arg( count ),
1336                                     _( "&Continue" ), _( "C&ancel" ),
1337                                     0,          // defaultButtonNumber (from 0)
1338                                     1 )         // escapeButtonNumber
1339              == 1 )     // "Cancel"?
1340         {
1341             return;
1342         }
1343     }
1344
1345     (void) _pkgList->globalSetPkgStatus( S_Update, force,
1346                                          false ); // countOnly
1347
1348     if ( _statusFilterView )
1349     {
1350         _filters->showPage( _statusFilterView );
1351         _statusFilterView->clear();
1352         _statusFilterView->showTransactions();
1353         _statusFilterView->filter();
1354     }
1355 }
1356
1357
1358 void
1359 YQPackageSelector::showProducts()
1360 {
1361     YQPkgProductDialog::showProductDialog();
1362 }
1363
1364 void
1365 YQPackageSelector::installDevelPkgs()
1366 {
1367     installSubPkgs( "-devel" );
1368 }
1369
1370
1371 void
1372 YQPackageSelector::installDebugInfoPkgs()
1373 {
1374     installSubPkgs( "-debuginfo" );
1375 }
1376
1377
1378 void
1379 YQPackageSelector::pkgExcludeRulesChanged( int menuItemID )
1380 {
1381     if ( _viewMenu && _pkgList )
1382     {
1383         _viewMenu->setItemChecked( menuItemID, ! _viewMenu->isItemChecked( menuItemID ) );
1384
1385         if ( _excludeDevelPkgs )
1386             _excludeDevelPkgs->enable( ! _viewMenu->isItemChecked( _viewShowDevelID ) );
1387
1388         if ( _excludeDebugInfoPkgs )
1389             _excludeDebugInfoPkgs->enable( ! _viewMenu->isItemChecked( _viewShowDebugInfoID ) );
1390
1391         _pkgList->applyExcludeRules();
1392     }
1393 }
1394
1395
1396 void
1397 YQPackageSelector::installSubPkgs( const QString suffix )
1398 {
1399     // Find all matching packages and put them into a QMap
1400
1401     QMap<QString, ZyppSel> subPkgs;
1402
1403     for ( ZyppPoolIterator it = zyppPkgBegin();
1404           it != zyppPkgEnd();
1405           ++it )
1406     {
1407         QString name = (*it)->name().c_str();
1408
1409         if ( name.endsWith( suffix ) )
1410         {
1411             subPkgs[ name ] = *it;
1412
1413             y2debug( "Found subpackage: %s", (const char *) name );
1414         }
1415     }
1416
1417
1418     // Now go through all packages and look if there is a corresponding subpackage in the QMap
1419
1420     for ( ZyppPoolIterator it = zyppPkgBegin();
1421           it != zyppPkgEnd();
1422           ++it )
1423     {
1424         QString name = (*it)->name().c_str();
1425
1426         if ( subPkgs.contains( name + suffix ) )
1427         {
1428             QString subPkgName( name + suffix );
1429             ZyppSel subPkg = subPkgs[ subPkgName ];
1430
1431             switch ( (*it)->status() )
1432             {
1433                 case S_AutoDel:
1434                 case S_NoInst:
1435                 case S_Protected:
1436                 case S_Taboo:
1437                 case S_Del:
1438                     // Don't install the subpackage
1439                     y2milestone( "Ignoring unwanted subpackage %s", (const char *) subPkgName );
1440                     break;
1441
1442                 case S_AutoInstall:
1443                 case S_Install:
1444                 case S_KeepInstalled:
1445
1446                     // Install the subpackage, but don't try to update it
1447
1448                     if ( ! subPkg->installedObj() )
1449                     {
1450                         subPkg->set_status( S_Install );
1451                         y2milestone( "Installing subpackage %s", (const char *) subPkgName );
1452                     }
1453                     break;
1454
1455
1456                 case S_Update:
1457                 case S_AutoUpdate:
1458
1459                     // Install or update the subpackage
1460
1461                     if ( ! subPkg->installedObj() )
1462                     {
1463                         subPkg->set_status( S_Install );
1464                         y2milestone( "Installing subpackage %s", (const char *) subPkgName );
1465                     }
1466                     else
1467                     {
1468                         subPkg->set_status( S_Update );
1469                         y2milestone( "Updating subpackage %s", (const char *) subPkgName );
1470                     }
1471                     break;
1472
1473                     // Intentionally omitting 'default' branch so the compiler can
1474                     // catch unhandled enum states
1475             }
1476         }
1477     }
1478
1479
1480     if ( _filters && _statusFilterView )
1481     {
1482         _filters->showPage( _statusFilterView );
1483         _statusFilterView->filter();
1484     }
1485
1486     YQPkgChangesDialog::showChangesDialog( _( "Added Subpackages:" ),
1487                                            QRegExp( ".*" + suffix + "$" ),
1488                                            _( "&OK" ),
1489                                            QString::null,       // rejectButtonLabel
1490                                            true );              // showIfEmpty
1491 }
1492
1493
1494 #include "YQPackageSelector.moc"