]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/pkg/YQPkgConflictDialog.cc
more porting and make files use qt3support one by one
[duncan/yast2-qt4.git] / src / pkg / YQPkgConflictDialog.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       YQPkgConflictDialog.cc
14
15   Author:     Stefan Hundhammer <sh@suse.de>
16
17   Textdomain "packages-qt"
18
19 /-*/
20
21 #define QT3_SUPPORT 1
22
23 #define y2log_component "qt-pkg"
24 #include <ycp/y2log.h>
25
26 #include <zypp/ZYppFactory.h>
27 #include <zypp/Resolver.h>
28
29 #include <QLabel>
30 #include <QKeyEvent>
31 #include <QLayout>
32 #include <QMenu>
33 #include <QPushButton>
34 #include <QDateTime>
35 #include <QPainter>
36 #include <QMessageBox>
37 #include <QDesktopWidget>
38 #include <QPixmap>
39 #include <QBoxLayout>
40
41 #include "YQPkgConflictDialog.h"
42 #include "YQPkgConflictList.h"
43 #include "YQDialog.h"
44
45 #include "QY2LayoutUtils.h"
46 #include "YQUI.h"
47 #include "YQi18n.h"
48 #include "utf8.h"
49
50
51 #define SPACING                 6       // between subwidgets
52 #define MARGIN                  4       // around the widget
53
54
55 // The busy dialog ("Checking Dependencies") will only be shown if solving
56 // (on average) takes longer than this many seconds. The first one will be
57 // shown in any case.
58
59 #define SUPPRESS_BUSY_DIALOG_SECONDS    1.5
60
61
62 YQPkgConflictDialog::YQPkgConflictDialog( QWidget * parent )
63     : QDialog( parent )
64 {
65     _solveCount         = 0;
66     _totalSolveTime     = 0.0;
67
68
69     // Set the dialog title.
70     //
71     // "Dependency conflict" is already used as the conflict list header just
72     // some pixels below that, so don't use this twice. This dialog title may
73     // or may not be visible, depending on whether or not there is a window
74     // manager running (and configured to show any dialog titles).
75
76     setCaption( _( "Warning" ) );
77
78     // Enable dialog resizing even without window manager
79     setSizeGripEnabled( true );
80
81
82     // Layout for the dialog (can't simply insert a QVbox)
83
84     QVBoxLayout * layout = new QVBoxLayout( this, MARGIN, SPACING );
85     Q_CHECK_PTR( layout );
86
87     // Conflict list
88
89     _conflictList = new YQPkgConflictList( this );
90     Q_CHECK_PTR( _conflictList );
91     layout->addWidget( _conflictList );
92     layout->addSpacing(8);
93
94     connect( _conflictList, SIGNAL( updatePackages() ),
95              this,          SIGNAL( updatePackages() ) );
96
97
98     // Button box
99     QHBoxLayout * buttonBox = new QHBoxLayout(this);
100     Q_CHECK_PTR( buttonBox );
101     buttonBox->setSpacing( SPACING );
102     buttonBox->setMargin ( MARGIN  );
103     layout->addLayout( buttonBox );
104
105     // "OK" button
106
107     QPushButton * button = new QPushButton( _( "&OK -- Try Again" ), this);
108     buttonBox->addWidget(button);
109     Q_CHECK_PTR( button );
110     button->setDefault( true );
111
112     connect( button, SIGNAL( clicked() ),
113              this,   SLOT  ( solveAndShowConflicts() ) );
114
115     // FIXME addHStretch( buttonBox );
116
117
118     // "Expert" menu button
119
120     button = new QPushButton( _( "&Expert" ), this );
121     buttonBox->addWidget(button);
122
123     Q_CHECK_PTR( button );
124
125     //FIXME addHStretch( buttonBox );
126
127
128     // "Expert" menu
129
130     _expertMenu = new QMenu( button );
131     Q_CHECK_PTR( _expertMenu );
132     button->setPopup( _expertMenu );
133
134     _expertMenu->insertItem( _( "&Save This List to a File..." ),
135                              _conflictList, SLOT( askSaveToFile() ) );
136
137
138     // "Cancel" button
139
140     button = new QPushButton( _( "&Cancel" ), this);
141     buttonBox->addWidget(button);
142     Q_CHECK_PTR( button );
143
144     connect( button, SIGNAL( clicked() ),
145              this,   SLOT  ( reject()  ) );
146
147
148     // Busy popup
149
150     _busyPopup = new QLabel( "   " + _( "Checking Dependencies..." ) + "   ", parent, 0
151 #ifdef FIXME
152                              , WStyle_Customize | WStyle_DialogBorder | WStyle_Dialog | WStyle_Title
153 #endif
154                              );
155     Q_CHECK_PTR( _busyPopup );
156
157     _busyPopup->setCaption( "" );
158     _busyPopup->resize( _busyPopup->sizeHint() );
159     YQDialog::center( _busyPopup, parent );
160
161
162     // Here comes a real nasty hack.
163     //
164     // The busy popup is needed to indicate that the application is (you
165     // guessed right) busy. But as long as it is busy, it doesn't process X
166     // events, either, and I didn't manage to convince Qt to please paint this
167     // popup before the solver's calculations (which take quite a while) start
168     // - all combinations of show(), repaint(), XSync(), XFlush(),
169     // processEvents() etc. failed.
170     //
171     // So, let's do it the hard way: Give this popup a background pixmap into
172     // which we render the text to display. The X server draws background
173     // pixmaps immediately, so we don't have to wait until the X server, the
174     // window manager and this application are finished negotiating all their
175     // various events.
176
177     // Create a pixmap. Make it large enough so it isn't replicated (i.e. the
178     // text is displayed several times) if some window manager chooses not to
179     // honor the size hints (KDM for example uses double the height we
180     // request).
181
182     QSize size = _busyPopup->sizeHint();
183     QPixmap pixmap( 3 * size.width(), 3 * size.height() );
184
185     // Clear the pixmap with the widget's normal background color.
186     pixmap.fill( _busyPopup->paletteBackgroundColor() );
187
188     // Render the text - aligned top and left because otherwise it will of
189     // course be centered inside the pixmap which is usually much larger than
190     // the popup, thus the text would be cut off.
191     QPainter painter( &pixmap );
192     painter.drawText( pixmap.rect(), Qt::AlignLeft | Qt::AlignTop, _busyPopup->text() );
193     painter.end();
194
195     _busyPopup->setPaletteBackgroundPixmap( pixmap );
196
197     // If the application manages to render the true contents of the label we
198     // just misused so badly, the real label will interfere with the background
199     // pixmap with (maybe) a few pixels offset (bug #25647). Fast or
200     // multiprocessor machines tend to have this problem.
201     // So let's get rid of the label text and solely rely on the background
202     // pixmap.
203     _busyPopup->setText( "" );
204
205     // Make sure the newly emptied text doesn't cause the busy dialog to be
206     // resized to nil (or a window manager dependent minimum size).
207     _busyPopup->setFixedSize( _busyPopup->size() );
208 }
209
210
211 YQPkgConflictDialog::~YQPkgConflictDialog()
212 {
213     // NOP
214 }
215
216
217 QSize
218 YQPkgConflictDialog::sizeHint() const
219 {
220     return limitToScreenSize( this, 550, 450 );
221 }
222
223
224 int
225 YQPkgConflictDialog::solveAndShowConflicts()
226 {
227     prepareSolving();
228
229     y2debug( "Solving..." );
230     QTime solveTime;
231     solveTime.start();
232
233     // Solve.
234
235     bool success = zypp::getZYpp()->resolver()->resolvePool();
236
237     _totalSolveTime += solveTime.elapsed() / 1000.0;
238
239     y2debug( "Solving done in %f s - average: %f s",
240              solveTime.elapsed() / 1000.0, averageSolveTime() );
241
242     return processSolverResult( success );
243 }
244
245
246 int
247 YQPkgConflictDialog::verifySystem()
248 {
249     prepareSolving();
250
251     y2debug( "Verifying system..." );
252     QTime solveTime;
253     solveTime.start();
254
255     bool success = zypp::getZYpp()->resolver()->verifySystem( true ); // considerNewHardware
256
257     y2debug( "System verified in %f s", solveTime.elapsed() / 1000.0 );
258
259     return processSolverResult( success );
260 }
261
262
263 void
264 YQPkgConflictDialog::prepareSolving()
265 {
266     Q_CHECK_PTR( _conflictList );
267     YQUI::ui()->busyCursor();
268
269     if ( isVisible() )
270     {
271         // This is not only the starting point for all the dependency solving
272         // magic, it is also used internally when clicking the "OK - Try again"
273         // button. Thus, before doing anything else, check if the conflict list
274         // still contains anything, and if so, apply any conflict resolutions
275         // the user selected - but only if this dialog is already visible.
276
277         _conflictList->applyResolutions();
278     }
279
280
281     // Initialize for next round of solving.
282     _conflictList->clear();
283
284     if ( _solveCount++ == 0 || averageSolveTime() > SUPPRESS_BUSY_DIALOG_SECONDS )
285     {
286         YQDialog::center( _busyPopup, parentWidget() );
287         _busyPopup->show();
288
289         // No _busyPopup->repaint() - that doesn't help anyway: Qt doesn't do
290         // any actual painting until the window is mapped. We just rely on the
291         // background pixmap we provided in the constructor.
292
293         // Make sure show() gets processed - usually, a window manager catches
294         // the show() (XMap) events, positions and maybe resizes the window and
295         // only then sends off an event that makes the window appear. This
296         // event needs to be processed.
297         qApp->processEvents();
298     }
299 }
300
301
302 int
303 YQPkgConflictDialog::processSolverResult( bool success )
304 {
305     if ( _busyPopup->isVisible() )
306         _busyPopup->hide();
307
308     // Package states may have changed: The solver may have set packages to
309     // autoInstall or autoUpdate. Make those changes known.
310     emit updatePackages();
311
312     YQUI::ui()->normalCursor();
313     int result = QDialog::Accepted;
314
315     if ( success )      // Solving went without any complaints?
316     {
317         result = QDialog::Accepted;
318
319         if ( isVisible() )
320             accept();   // Pop down the dialog.
321     }
322     else                // There were solving problems.
323     {
324         y2debug( "Dependency conflict!" );
325         YQUI::ui()->busyCursor();
326
327         _conflictList->fill( zypp::getZYpp()->resolver()->problems() );
328         YQUI::ui()->normalCursor();
329
330         if ( ! isVisible() )
331         {
332             // Pop up the dialog and run a local event loop.
333             result = exec();
334         }
335     }
336
337     return result;      // QDialog::Accepted or QDialog::Rejected
338 }
339
340
341 void
342 YQPkgConflictDialog::resetIgnoredDependencyProblems()
343 {
344     zypp::getZYpp()->resolver()->undo();
345 }
346
347
348 double
349 YQPkgConflictDialog::averageSolveTime() const
350 {
351     if ( _solveCount < 1 )
352         return 0.0;
353
354     return _totalSolveTime / _solveCount;
355 }
356
357
358 void
359 YQPkgConflictDialog::askCreateSolverTestCase()
360 {
361     QString testCaseDir = "/var/log/YaST2/solverTestcase";
362     // Heading for popup dialog
363     QString heading = QString( "<h2>%1</h2>" ).arg( _( "Create Dependency Resolver Test Case" ) );
364
365     QString msg =
366         _( "<p>Use this to generate extensive logs to help tracking down bugs in the dependency resolver. "
367            "The logs will be stored in directory <br><tt>%1</tt></p>" ).arg( testCaseDir );
368
369     int button_no = QMessageBox::information( 0,                        // parent
370                                               _( "Solver Test Case" ),  // caption
371                                               heading + msg,
372                                               _( "C&ontinue" ),         // button #0
373                                               _( "&Cancel" ) );         // button #1
374
375     if ( button_no == 1 )       // Cancel
376         return;
377
378     y2milestone( "Generating solver test case START" );
379     bool success = zypp::getZYpp()->resolver()->createSolverTestcase( testCaseDir.ascii() );
380     y2milestone( "Generating solver test case END" );
381
382     if ( success )
383     {
384         msg =
385             _( "<p>Dependency resolver test case written to <br><tt>%1</tt></p>"
386                "<p>Prepare <tt>y2logs.tgz tar</tt> archive to attach to Bugzilla?</p>" ).arg( testCaseDir ),
387         button_no = QMessageBox::question( 0,                           // parent
388                                            _( "Success" ),              // caption
389                                            msg,
390                                            QMessageBox::Yes    | QMessageBox::Default,
391                                            QMessageBox::No,
392                                            QMessageBox::Cancel | QMessageBox::Escape );
393
394         if ( button_no & QMessageBox::Yes ) // really binary (not logical) '&' - QMessageBox::Default is still in there
395             YQUI::ui()->askSaveLogs();
396     }
397     else // no success
398     {
399         QMessageBox::warning( 0,                                        // parent
400                               _( "Error" ),                             // caption
401                               _( "<p><b>Error</b> creating dependency resolver test case</p>"
402                                  "<p>Please check disk space and permissions for <tt>%1</tt></p>" ).arg( testCaseDir ),
403                               QMessageBox::Ok | QMessageBox::Default,
404                               QMessageBox::NoButton,
405                               QMessageBox::NoButton );
406     }
407 }
408
409 void
410 YQPkgConflictDialog::keyPressEvent( QKeyEvent * event )
411 {
412     if ( event &&  event->key() == Qt::Key_Print )
413     {
414         YQUI::ui()->makeScreenShot( "" );
415         return;
416     }
417     QWidget::keyPressEvent( event );
418 }
419
420
421
422 #include "YQPkgConflictDialog.moc"