1 /*---------------------------------------------------------------------\
3 | __ __ ____ _____ ____ |
4 | \ \ / /_ _/ ___|_ _|___ \ |
5 | \ V / _` \___ \ | | __) | |
6 | | | (_| |___) || | / __/ |
7 | |_|\__,_|____/ |_| |_____| |
11 \----------------------------------------------------------------------/
13 File: YQPkgConflictDialog.cc
15 Author: Stefan Hundhammer <sh@suse.de>
17 Textdomain "packages-qt"
21 #define y2log_component "qt-pkg"
22 #include <ycp/y2log.h>
24 #include <zypp/ZYppFactory.h>
25 #include <zypp/Resolver.h>
31 #include <QPushButton>
34 #include <QMessageBox>
35 #include <QDesktopWidget>
39 #include "YQPkgConflictDialog.h"
40 #include "YQPkgConflictList.h"
43 #include "QY2LayoutUtils.h"
49 #define SPACING 6 // between subwidgets
50 #define MARGIN 4 // around the widget
53 // The busy dialog ("Checking Dependencies") will only be shown if solving
54 // (on average) takes longer than this many seconds. The first one will be
57 #define SUPPRESS_BUSY_DIALOG_SECONDS 1.5
60 YQPkgConflictDialog::YQPkgConflictDialog( QWidget * parent )
64 _totalSolveTime = 0.0;
67 // Set the dialog title.
69 // "Dependency conflict" is already used as the conflict list header just
70 // some pixels below that, so don't use this twice. This dialog title may
71 // or may not be visible, depending on whether or not there is a window
72 // manager running (and configured to show any dialog titles).
74 setWindowTitle( _( "Warning" ) );
76 // Enable dialog resizing even without window manager
77 setSizeGripEnabled( true );
80 // Layout for the dialog (can't simply insert a QVbox)
82 QVBoxLayout * layout = new QVBoxLayout( this );
83 layout->setMargin(MARGIN);
84 layout->setSpacing(SPACING);
86 Q_CHECK_PTR( layout );
90 _conflictList = new YQPkgConflictList( this );
91 Q_CHECK_PTR( _conflictList );
92 layout->addWidget( _conflictList );
93 layout->addSpacing(8);
95 connect( _conflictList, SIGNAL( updatePackages() ),
96 this, SIGNAL( updatePackages() ) );
100 QHBoxLayout * buttonBox = new QHBoxLayout();
101 Q_CHECK_PTR( buttonBox );
102 buttonBox->setSpacing( SPACING );
103 buttonBox->setMargin ( MARGIN );
104 layout->addLayout( buttonBox );
108 QPushButton * button = new QPushButton( _( "&OK -- Try Again" ), this);
109 buttonBox->addWidget(button);
110 Q_CHECK_PTR( button );
111 button->setDefault( true );
113 connect( button, SIGNAL( clicked() ),
114 this, SLOT ( solveAndShowConflicts() ) );
116 // FIXME addHStretch( buttonBox );
119 // "Expert" menu button
121 button = new QPushButton( _( "&Expert" ), this );
122 buttonBox->addWidget(button);
124 Q_CHECK_PTR( button );
126 //FIXME addHStretch( buttonBox );
131 _expertMenu = new QMenu( button );
132 Q_CHECK_PTR( _expertMenu );
133 button->setMenu( _expertMenu );
135 _expertMenu->addAction( _( "&Save This List to a File..." ),
136 _conflictList, SLOT( askSaveToFile() ) );
141 button = new QPushButton( _( "&Cancel" ), this);
142 buttonBox->addWidget(button);
143 Q_CHECK_PTR( button );
145 connect( button, SIGNAL( clicked() ),
146 this, SLOT ( reject() ) );
151 _busyPopup = new QLabel( " " + _( "Checking Dependencies..." ) + " ", parent, 0
153 , WStyle_Customize | WStyle_DialogBorder | WStyle_Dialog | WStyle_Title
156 Q_CHECK_PTR( _busyPopup );
158 _busyPopup->setWindowTitle( "" );
159 _busyPopup->resize( _busyPopup->sizeHint() );
160 YQDialog::center( _busyPopup, parent );
163 // Here comes a real nasty hack.
165 // The busy popup is needed to indicate that the application is (you
166 // guessed right) busy. But as long as it is busy, it doesn't process X
167 // events, either, and I didn't manage to convince Qt to please paint this
168 // popup before the solver's calculations (which take quite a while) start
169 // - all combinations of show(), repaint(), XSync(), XFlush(),
170 // processEvents() etc. failed.
172 // So, let's do it the hard way: Give this popup a background pixmap into
173 // which we render the text to display. The X server draws background
174 // pixmaps immediately, so we don't have to wait until the X server, the
175 // window manager and this application are finished negotiating all their
178 // Create a pixmap. Make it large enough so it isn't replicated (i.e. the
179 // text is displayed several times) if some window manager chooses not to
180 // honor the size hints (KDM for example uses double the height we
183 QSize size = _busyPopup->sizeHint();
184 QPixmap pixmap( 3 * size.width(), 3 * size.height() );
186 // Clear the pixmap with the widget's normal background color.
187 //FIXME pixmap.fill( _busyPopup->paletteBackgroundColor() );
189 // Render the text - aligned top and left because otherwise it will of
190 // course be centered inside the pixmap which is usually much larger than
191 // the popup, thus the text would be cut off.
192 QPainter painter( &pixmap );
193 painter.drawText( pixmap.rect(), Qt::AlignLeft | Qt::AlignTop, _busyPopup->text() );
196 //FIXME _busyPopup->setPaletteBackgroundPixmap( pixmap );
198 // If the application manages to render the true contents of the label we
199 // just misused so badly, the real label will interfere with the background
200 // pixmap with (maybe) a few pixels offset (bug #25647). Fast or
201 // multiprocessor machines tend to have this problem.
202 // So let's get rid of the label text and solely rely on the background
204 _busyPopup->setText( "" );
206 // Make sure the newly emptied text doesn't cause the busy dialog to be
207 // resized to nil (or a window manager dependent minimum size).
208 _busyPopup->setFixedSize( _busyPopup->size() );
212 YQPkgConflictDialog::~YQPkgConflictDialog()
219 YQPkgConflictDialog::sizeHint() const
221 return limitToScreenSize( this, 550, 450 );
226 YQPkgConflictDialog::solveAndShowConflicts()
230 y2debug( "Solving..." );
236 bool success = zypp::getZYpp()->resolver()->resolvePool();
238 _totalSolveTime += solveTime.elapsed() / 1000.0;
240 y2debug( "Solving done in %f s - average: %f s",
241 solveTime.elapsed() / 1000.0, averageSolveTime() );
243 return processSolverResult( success );
248 YQPkgConflictDialog::verifySystem()
252 y2debug( "Verifying system..." );
256 bool success = zypp::getZYpp()->resolver()->verifySystem( true ); // considerNewHardware
258 y2debug( "System verified in %f s", solveTime.elapsed() / 1000.0 );
260 return processSolverResult( success );
265 YQPkgConflictDialog::prepareSolving()
267 Q_CHECK_PTR( _conflictList );
268 YQUI::ui()->busyCursor();
272 // This is not only the starting point for all the dependency solving
273 // magic, it is also used internally when clicking the "OK - Try again"
274 // button. Thus, before doing anything else, check if the conflict list
275 // still contains anything, and if so, apply any conflict resolutions
276 // the user selected - but only if this dialog is already visible.
278 _conflictList->applyResolutions();
282 // Initialize for next round of solving.
283 _conflictList->clear();
285 if ( _solveCount++ == 0 || averageSolveTime() > SUPPRESS_BUSY_DIALOG_SECONDS )
287 YQDialog::center( _busyPopup, parentWidget() );
290 // No _busyPopup->repaint() - that doesn't help anyway: Qt doesn't do
291 // any actual painting until the window is mapped. We just rely on the
292 // background pixmap we provided in the constructor.
294 // Make sure show() gets processed - usually, a window manager catches
295 // the show() (XMap) events, positions and maybe resizes the window and
296 // only then sends off an event that makes the window appear. This
297 // event needs to be processed.
298 qApp->processEvents();
304 YQPkgConflictDialog::processSolverResult( bool success )
306 if ( _busyPopup->isVisible() )
309 // Package states may have changed: The solver may have set packages to
310 // autoInstall or autoUpdate. Make those changes known.
311 emit updatePackages();
313 YQUI::ui()->normalCursor();
314 int result = QDialog::Accepted;
316 if ( success ) // Solving went without any complaints?
318 result = QDialog::Accepted;
321 accept(); // Pop down the dialog.
323 else // There were solving problems.
325 y2debug( "Dependency conflict!" );
326 YQUI::ui()->busyCursor();
328 _conflictList->fill( zypp::getZYpp()->resolver()->problems() );
329 YQUI::ui()->normalCursor();
333 // Pop up the dialog and run a local event loop.
338 return result; // QDialog::Accepted or QDialog::Rejected
343 YQPkgConflictDialog::resetIgnoredDependencyProblems()
345 zypp::getZYpp()->resolver()->undo();
350 YQPkgConflictDialog::averageSolveTime() const
352 if ( _solveCount < 1 )
355 return _totalSolveTime / _solveCount;
360 YQPkgConflictDialog::askCreateSolverTestCase()
362 QString testCaseDir = "/var/log/YaST2/solverTestcase";
363 // Heading for popup dialog
364 QString heading = QString( "<h2>%1</h2>" ).arg( _( "Create Dependency Resolver Test Case" ) );
367 _( "<p>Use this to generate extensive logs to help tracking down bugs in the dependency resolver. "
368 "The logs will be stored in directory <br><tt>%1</tt></p>" ).arg( testCaseDir );
370 int button_no = QMessageBox::information( 0, // parent
371 _( "Solver Test Case" ), // caption
373 _( "C&ontinue" ), // button #0
374 _( "&Cancel" ) ); // button #1
376 if ( button_no == 1 ) // Cancel
379 y2milestone( "Generating solver test case START" );
380 bool success = zypp::getZYpp()->resolver()->createSolverTestcase( qPrintable(testCaseDir) );
381 y2milestone( "Generating solver test case END" );
386 _( "<p>Dependency resolver test case written to <br><tt>%1</tt></p>"
387 "<p>Prepare <tt>y2logs.tgz tar</tt> archive to attach to Bugzilla?</p>" ).arg( testCaseDir ),
388 button_no = QMessageBox::question( 0, // parent
389 _( "Success" ), // caption
391 QMessageBox::Yes | QMessageBox::Default,
393 QMessageBox::Cancel | QMessageBox::Escape );
395 if ( button_no & QMessageBox::Yes ) // really binary (not logical) '&' - QMessageBox::Default is still in there
396 YQUI::ui()->askSaveLogs();
400 QMessageBox::warning( 0, // parent
401 _( "Error" ), // caption
402 _( "<p><b>Error</b> creating dependency resolver test case</p>"
403 "<p>Please check disk space and permissions for <tt>%1</tt></p>" ).arg( testCaseDir ),
404 QMessageBox::Ok | QMessageBox::Default,
405 QMessageBox::NoButton,
406 QMessageBox::NoButton );
411 YQPkgConflictDialog::keyPressEvent( QKeyEvent * event )
413 if ( event && event->key() == Qt::Key_Print )
415 YQUI::ui()->makeScreenShot( "" );
418 QWidget::keyPressEvent( event );
423 #include "YQPkgConflictDialog.moc"