2008-09-24 20:32:01 +01:00
/***************************************************************************
* Copyright ( C ) 2008 by Volker Lanz < vl @ fidra . de > *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the *
* Free Software Foundation , Inc . , *
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-02-11 18:47:51 +00:00
# include "gui/applyprogressdialog.h"
2008-09-24 20:32:01 +01:00
2010-02-11 18:47:51 +00:00
# include "gui/applyprogressdialogwidget.h"
# include "gui/applyprogressdetailswidget.h"
2008-09-24 20:32:01 +01:00
# include "core/operationrunner.h"
# include "ops/operation.h"
# include "jobs/job.h"
# include "util/report.h"
2010-04-28 16:21:07 +01:00
# include "util/htmlreport.h"
2008-09-24 20:32:01 +01:00
# include <QCloseEvent>
# include <QTime>
# include <QFont>
# include <QKeyEvent>
# include <QFile>
# include <kapplication.h>
# include <kdebug.h>
# include <kmessagebox.h>
# include <kfiledialog.h>
# include <krun.h>
# include <ktemporaryfile.h>
# include <kaboutdata.h>
# include <ktextedit.h>
2010-04-11 17:12:16 +01:00
# include <kio/netaccess.h>
# include <kio/jobuidelegate.h>
# include <kio/copyjob.h>
2008-09-24 20:32:01 +01:00
2010-02-11 18:47:51 +00:00
const QString ApplyProgressDialog : : m_TimeFormat = " hh:mm:ss " ;
2008-09-24 20:32:01 +01:00
2009-05-06 06:43:08 +01:00
static QWidget * mainWindow ( QWidget * w )
{
while ( w & & w - > parentWidget ( ) )
w = w - > parentWidget ( ) ;
return w ;
}
2008-09-24 20:32:01 +01:00
/** Creates a new ProgressDialog
@ param parent pointer to the parent widget
@ param orunner the OperationRunner whose progress this dialog is showing
*/
2010-02-11 18:47:51 +00:00
ApplyProgressDialog : : ApplyProgressDialog ( QWidget * parent , OperationRunner & orunner ) :
2008-09-24 20:32:01 +01:00
KDialog ( parent ) ,
2010-02-11 18:47:51 +00:00
m_ProgressDialogWidget ( new ApplyProgressDialogWidget ( this ) ) ,
m_ProgressDetailsWidget ( new ApplyProgressDetailsWidget ( this ) ) ,
2008-09-24 20:32:01 +01:00
m_OperationRunner ( orunner ) ,
m_Report ( NULL ) ,
2009-05-06 06:43:08 +01:00
m_SavedParentTitle ( mainWindow ( this ) - > windowTitle ( ) ) ,
2008-09-24 20:32:01 +01:00
m_Timer ( this ) ,
m_Time ( ) ,
m_CurrentOpItem ( NULL ) ,
2009-11-09 13:51:08 +00:00
m_CurrentJobItem ( NULL ) ,
m_LastReportUpdate ( 0 )
2008-09-24 20:32:01 +01:00
{
setMainWidget ( & dialogWidget ( ) ) ;
setDetailsWidget ( & detailsWidget ( ) ) ;
showButtonSeparator ( true ) ;
2010-02-13 12:31:29 +00:00
setAttribute ( Qt : : WA_ShowModal , true ) ;
2008-09-24 20:32:01 +01:00
setButtons ( KDialog : : Ok | KDialog : : Cancel | KDialog : : Details ) ;
dialogWidget ( ) . treeTasks ( ) . setColumnWidth ( 0 , width ( ) * 0.8 ) ;
2010-05-01 14:31:27 +01:00
detailsWidget ( ) . buttonBrowser ( ) . setIcon ( KIcon ( " document-open " ) ) ;
detailsWidget ( ) . buttonSave ( ) . setIcon ( KIcon ( " document-save " ) ) ;
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
setupConnections ( ) ;
2010-02-21 17:38:24 +00:00
restoreDialogSize ( KConfigGroup ( KGlobal : : config ( ) , " applyProgressDialog " ) ) ;
2008-09-24 20:32:01 +01:00
}
/** Destroys a ProgressDialog */
2010-02-11 18:47:51 +00:00
ApplyProgressDialog : : ~ ApplyProgressDialog ( )
2008-09-24 20:32:01 +01:00
{
2010-02-21 17:38:24 +00:00
KConfigGroup kcg ( KGlobal : : config ( ) , " applyProgressDialog " ) ;
2008-09-24 20:32:01 +01:00
saveDialogSize ( kcg ) ;
delete m_Report ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : setupConnections ( )
2008-09-24 20:32:01 +01:00
{
connect ( & operationRunner ( ) , SIGNAL ( progressSub ( int ) ) , & dialogWidget ( ) . progressSub ( ) , SLOT ( setValue ( int ) ) ) ;
connect ( & operationRunner ( ) , SIGNAL ( finished ( ) ) , SLOT ( onAllOpsFinished ( ) ) ) ;
connect ( & operationRunner ( ) , SIGNAL ( cancelled ( ) ) , SLOT ( onAllOpsCancelled ( ) ) ) ;
connect ( & operationRunner ( ) , SIGNAL ( error ( ) ) , SLOT ( onAllOpsError ( ) ) ) ;
connect ( & operationRunner ( ) , SIGNAL ( opStarted ( int , Operation * ) ) , SLOT ( onOpStarted ( int , Operation * ) ) ) ;
connect ( & operationRunner ( ) , SIGNAL ( opFinished ( int , Operation * ) ) , SLOT ( onOpFinished ( int , Operation * ) ) ) ;
connect ( & timer ( ) , SIGNAL ( timeout ( ) ) , SLOT ( onSecondElapsed ( ) ) ) ;
connect ( & detailsWidget ( ) . buttonSave ( ) , SIGNAL ( clicked ( ) ) , SLOT ( saveReport ( ) ) ) ;
connect ( & detailsWidget ( ) . buttonBrowser ( ) , SIGNAL ( clicked ( ) ) , SLOT ( browserReport ( ) ) ) ;
}
/** Shows the dialog */
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : show ( )
2008-09-24 20:32:01 +01:00
{
setStatus ( i18nc ( " @info:progress " , " Setting up... " ) ) ;
resetReport ( ) ;
dialogWidget ( ) . progressTotal ( ) . setRange ( 0 , operationRunner ( ) . numJobs ( ) ) ;
dialogWidget ( ) . progressTotal ( ) . setValue ( 0 ) ;
dialogWidget ( ) . treeTasks ( ) . clear ( ) ;
showButton ( KDialog : : Ok , false ) ;
showButton ( KDialog : : Cancel , true ) ;
timer ( ) . start ( 1000 ) ;
time ( ) . start ( ) ;
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
setLastReportUpdate ( 0 ) ;
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
onSecondElapsed ( ) ; // resets the total time output label
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
KDialog : : show ( ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : resetReport ( )
2008-09-24 20:32:01 +01:00
{
delete m_Report ;
m_Report = new Report ( NULL ) ;
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
detailsWidget ( ) . editReport ( ) . clear ( ) ;
detailsWidget ( ) . editReport ( ) . setCursorWidth ( 0 ) ;
detailsWidget ( ) . buttonSave ( ) . setEnabled ( false ) ;
detailsWidget ( ) . buttonBrowser ( ) . setEnabled ( false ) ;
connect ( & report ( ) , SIGNAL ( outputChanged ( ) ) , SLOT ( updateReport ( ) ) ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : closeEvent ( QCloseEvent * e )
2008-09-24 20:32:01 +01:00
{
e - > ignore ( ) ;
slotButtonClicked ( operationRunner ( ) . isRunning ( ) ? KDialog : : Cancel : KDialog : : Ok ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : slotButtonClicked ( int button )
2008-09-24 20:32:01 +01:00
{
if ( button = = KDialog : : Details )
{
KDialog : : slotButtonClicked ( button ) ;
updateReport ( true ) ;
return ;
}
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
if ( button = = KDialog : : Cancel & & operationRunner ( ) . isRunning ( ) )
{
// only cancel once
if ( operationRunner ( ) . isCancelling ( ) )
return ;
KApplication : : setOverrideCursor ( QCursor ( Qt : : WaitCursor ) ) ;
enableButtonCancel ( false ) ;
setStatus ( i18nc ( " @info:progress " , " Waiting for operation to finish... " ) ) ;
repaint ( ) ;
dialogWidget ( ) . repaint ( ) ;
// suspend the runner, so it doesn't happily carry on while the user decides
// if he really wants to cancel
operationRunner ( ) . suspendMutex ( ) . lock ( ) ;
enableButtonCancel ( true ) ;
KApplication : : restoreOverrideCursor ( ) ;
2010-04-11 19:19:24 +01:00
if ( KMessageBox : : questionYesNo ( this , i18nc ( " @info " , " Do you really want to cancel? " ) , i18nc ( " @title:window " , " Cancel Running Operations " ) , KGuiItem ( i18nc ( " @action:button " , " Yes, Cancel Operations " ) , " dialog-ok " ) , KStandardGuiItem : : no ( ) ) = = KMessageBox : : Yes )
2008-09-24 20:32:01 +01:00
// in the meantime while we were showing the messagebox, the runner might have finished.
if ( operationRunner ( ) . isRunning ( ) )
operationRunner ( ) . cancel ( ) ;
operationRunner ( ) . suspendMutex ( ) . unlock ( ) ;
return ;
}
2009-05-06 06:43:08 +01:00
mainWindow ( this ) - > setWindowTitle ( savedParentTitle ( ) ) ;
2008-09-24 20:32:01 +01:00
KDialog : : accept ( ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : onAllOpsFinished ( )
2008-09-24 20:32:01 +01:00
{
allOpsDone ( i18nc ( " @info:progress " , " All operations successfully finished. " ) ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : onAllOpsCancelled ( )
2008-09-24 20:32:01 +01:00
{
allOpsDone ( i18nc ( " @info:progress " , " Operations cancelled. " ) ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : onAllOpsError ( )
2008-09-24 20:32:01 +01:00
{
allOpsDone ( i18nc ( " @info:progress " , " There were errors while applying operations. Aborted. " ) ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : allOpsDone ( const QString & msg )
2008-09-24 20:32:01 +01:00
{
dialogWidget ( ) . progressTotal ( ) . setValue ( operationRunner ( ) . numJobs ( ) ) ;
showButton ( KDialog : : Cancel , false ) ;
showButton ( KDialog : : Ok , true ) ;
detailsWidget ( ) . buttonSave ( ) . setEnabled ( true ) ;
detailsWidget ( ) . buttonBrowser ( ) . setEnabled ( true ) ;
timer ( ) . stop ( ) ;
updateReport ( true ) ;
setStatus ( msg ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : updateReport ( bool force )
2008-09-24 20:32:01 +01:00
{
// Rendering the HTML in the KTextEdit is extremely expensive. So make sure not to do that
// unnecessarily and not too often:
// (1) If the widget isn't visible, don't update.
// (2) Also don't update if the last update was n msecs ago, BUT
// (3) DO update if we're being forced to.
2008-11-21 12:28:39 +00:00
if ( force | | ( detailsWidget ( ) . isVisible ( ) & & time ( ) . elapsed ( ) - lastReportUpdate ( ) > 2000 ) )
2008-09-24 20:32:01 +01:00
{
detailsWidget ( ) . editReport ( ) . setHtml ( " <html><body> " + report ( ) . toHtml ( ) + " </body></html> " ) ;
detailsWidget ( ) . editReport ( ) . moveCursor ( QTextCursor : : End ) ;
detailsWidget ( ) . editReport ( ) . ensureCursorVisible ( ) ;
setLastReportUpdate ( time ( ) . elapsed ( ) ) ;
}
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : onOpStarted ( int num , Operation * op )
2008-09-24 20:32:01 +01:00
{
addTaskOutput ( num , * op ) ;
setStatus ( op - > description ( ) ) ;
dialogWidget ( ) . progressSub ( ) . setValue ( 0 ) ;
dialogWidget ( ) . progressSub ( ) . setRange ( 0 , op - > totalProgress ( ) ) ;
connect ( op , SIGNAL ( jobStarted ( Job * , Operation * ) ) , SLOT ( onJobStarted ( Job * , Operation * ) ) ) ;
connect ( op , SIGNAL ( jobFinished ( Job * , Operation * ) ) , SLOT ( onJobFinished ( Job * , Operation * ) ) ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : onJobStarted ( Job * job , Operation * op )
2008-09-24 20:32:01 +01:00
{
for ( qint32 i = 0 ; i < dialogWidget ( ) . treeTasks ( ) . topLevelItemCount ( ) ; i + + )
{
QTreeWidgetItem * item = dialogWidget ( ) . treeTasks ( ) . topLevelItem ( i ) ;
if ( item = = NULL | | reinterpret_cast < const Operation * > ( item - > data ( 0 , Qt : : UserRole ) . toULongLong ( ) ) ! = op )
continue ;
QTreeWidgetItem * child = new QTreeWidgetItem ( ) ;
child - > setText ( 0 , job - > description ( ) ) ;
child - > setIcon ( 0 , job - > statusIcon ( ) ) ;
child - > setText ( 1 , QTime ( 0 , 0 ) . toString ( timeFormat ( ) ) ) ;
item - > addChild ( child ) ;
dialogWidget ( ) . treeTasks ( ) . scrollToBottom ( ) ;
setCurrentJobItem ( child ) ;
break ;
}
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : onJobFinished ( Job * job , Operation * op )
2008-09-24 20:32:01 +01:00
{
if ( currentJobItem ( ) )
currentJobItem ( ) - > setIcon ( 0 , job - > statusIcon ( ) ) ;
setCurrentJobItem ( NULL ) ;
const int current = dialogWidget ( ) . progressTotal ( ) . value ( ) ;
dialogWidget ( ) . progressTotal ( ) . setValue ( current + 1 ) ;
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
setParentTitle ( op - > description ( ) ) ;
updateReport ( true ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : onOpFinished ( int num , Operation * op )
2008-09-24 20:32:01 +01:00
{
if ( currentOpItem ( ) )
{
currentOpItem ( ) - > setText ( 0 , opDesc ( num , * op ) ) ;
currentOpItem ( ) - > setIcon ( 0 , op - > statusIcon ( ) ) ;
}
setCurrentOpItem ( NULL ) ;
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
setStatus ( op - > description ( ) ) ;
dialogWidget ( ) . progressSub ( ) . setValue ( op - > totalProgress ( ) ) ;
updateReport ( true ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : setParentTitle ( const QString & s )
2008-09-24 20:32:01 +01:00
{
const int percent = dialogWidget ( ) . progressTotal ( ) . value ( ) * 100 / dialogWidget ( ) . progressTotal ( ) . maximum ( ) ;
2009-05-06 06:43:08 +01:00
mainWindow ( this ) - > setWindowTitle ( QString : : number ( percent ) + " % - " + s + " - " + savedParentTitle ( ) ) ;
2008-09-24 20:32:01 +01:00
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : setStatus ( const QString & s )
2008-09-24 20:32:01 +01:00
{
setCaption ( s ) ;
dialogWidget ( ) . status ( ) . setText ( s ) ;
setParentTitle ( s ) ;
}
2010-02-11 18:47:51 +00:00
QString ApplyProgressDialog : : opDesc ( int num , const Operation & op ) const
2008-09-24 20:32:01 +01:00
{
return i18nc ( " @info:progress " , " [%1/%2] - %3: %4 " , num , operationRunner ( ) . numOperations ( ) , op . statusText ( ) , op . description ( ) ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : addTaskOutput ( int num , const Operation & op )
2008-09-24 20:32:01 +01:00
{
QTreeWidgetItem * item = new QTreeWidgetItem ( ) ;
item - > setIcon ( 0 , op . statusIcon ( ) ) ;
item - > setText ( 0 , opDesc ( num , op ) ) ;
item - > setText ( 1 , QTime ( 0 , 0 ) . toString ( timeFormat ( ) ) ) ;
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
QFont f ;
f . setWeight ( QFont : : Bold ) ;
item - > setFont ( 0 , f ) ;
item - > setFont ( 1 , f ) ;
item - > setData ( 0 , Qt : : UserRole , reinterpret_cast < const qulonglong > ( & op ) ) ;
dialogWidget ( ) . treeTasks ( ) . addTopLevelItem ( item ) ;
dialogWidget ( ) . treeTasks ( ) . scrollToBottom ( ) ;
setCurrentOpItem ( item ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : onSecondElapsed ( )
2008-09-24 20:32:01 +01:00
{
if ( currentJobItem ( ) )
{
QTime t = QTime : : fromString ( currentJobItem ( ) - > text ( 1 ) , timeFormat ( ) ) . addSecs ( 1 ) ;
currentJobItem ( ) - > setText ( 1 , t . toString ( timeFormat ( ) ) ) ;
}
if ( currentOpItem ( ) )
{
QTime t = QTime : : fromString ( currentOpItem ( ) - > text ( 1 ) , timeFormat ( ) ) . addSecs ( 1 ) ;
currentOpItem ( ) - > setText ( 1 , t . toString ( timeFormat ( ) ) ) ; ;
}
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
const QTime outputTime = QTime ( 0 , 0 ) . addMSecs ( time ( ) . elapsed ( ) ) ;
dialogWidget ( ) . totalTime ( ) . setText ( i18nc ( " @info:progress " , " Total Time: %1 " , outputTime . toString ( timeFormat ( ) ) ) ) ;
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : keyPressEvent ( QKeyEvent * e )
2008-09-24 20:32:01 +01:00
{
e - > accept ( ) ;
switch ( e - > key ( ) )
{
case Qt : : Key_Return :
case Qt : : Key_Enter :
if ( isButtonEnabled ( KDialog : : Ok ) )
slotButtonClicked ( KDialog : : Ok ) ;
break ;
case Qt : : Key_Escape :
slotButtonClicked ( isButtonEnabled ( KDialog : : Cancel ) ? KDialog : : Cancel : KDialog : : Ok ) ;
break ;
default :
break ;
}
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : saveReport ( )
2008-09-24 20:32:01 +01:00
{
2010-04-11 17:12:16 +01:00
const KUrl url = KFileDialog : : getSaveUrl ( KUrl ( " kfiledialog://saveReport " ) ) ;
2008-09-24 20:32:01 +01:00
2010-04-11 17:12:16 +01:00
if ( url . isEmpty ( ) )
2008-09-24 20:32:01 +01:00
return ;
2008-12-27 12:01:29 +00:00
2010-04-11 17:12:16 +01:00
KTemporaryFile tempFile ;
if ( tempFile . open ( ) )
2008-09-24 20:32:01 +01:00
{
2010-05-01 14:19:50 +01:00
QTextStream s ( & tempFile ) ;
HtmlReport html ;
s < < html . header ( )
< < report ( ) . toHtml ( )
< < html . footer ( ) ;
2010-04-11 17:12:16 +01:00
tempFile . close ( ) ;
KIO : : CopyJob * job = KIO : : move ( tempFile . fileName ( ) , url , KIO : : HideProgressInfo ) ;
if ( ! KIO : : NetAccess : : synchronousRun ( job , NULL ) )
job - > ui ( ) - > showErrorMessage ( ) ;
2008-09-24 20:32:01 +01:00
}
2010-04-11 17:12:16 +01:00
else
KMessageBox : : sorry ( this , i18nc ( " @info " , " Could not create temporary file when trying to save to <filename>%1</filename>. " , url . fileName ( ) ) , i18nc ( " @title:window " , " Could Not Save Report. " ) ) ;
2008-09-24 20:32:01 +01:00
}
2010-02-11 18:47:51 +00:00
void ApplyProgressDialog : : browserReport ( )
2008-09-24 20:32:01 +01:00
{
KTemporaryFile file ;
// Make sure the temp file is created somewhere another user can read it: KRun::runUrl() will open
// the file as the logged in user, not as the user running our application.
file . setFileTemplate ( " /tmp/ " + KGlobal : : mainComponent ( ) . aboutData ( ) - > appName ( ) + " -XXXXXX.html " ) ;
file . setAutoRemove ( false ) ;
2008-12-27 12:01:29 +00:00
2008-09-24 20:32:01 +01:00
if ( file . open ( ) )
{
2010-05-01 14:19:50 +01:00
QTextStream s ( & file ) ;
HtmlReport html ;
s < < html . header ( )
< < report ( ) . toHtml ( )
< < html . footer ( ) ;
2008-09-24 20:32:01 +01:00
// set the temp file's permission for everyone to read it.
file . setPermissions ( QFile : : ReadOwner | QFile : : WriteOwner | QFile : : ReadGroup | QFile : : ReadOther ) ;
if ( ! KRun : : runUrl ( file . fileName ( ) , " text/html " , this , true ) )
2008-12-27 12:01:29 +00:00
KMessageBox : : sorry ( this , i18nc ( " @info " , " The configured external browser could not be run. Please check your settings. " ) , i18nc ( " @title:window " , " Could Not Launch Browser. " ) ) ;
2008-09-24 20:32:01 +01:00
}
else
2008-12-27 12:01:29 +00:00
KMessageBox : : sorry ( this , i18nc ( " @info " , " Could not create temporary file <filename>%1</filename> for writing. " , file . fileName ( ) ) , i18nc ( " @title:window " , " Could Not Launch Browser. " ) ) ;
2008-09-24 20:32:01 +01:00
}