kdelibs/kdeui/jobs/kwidgetjobtracker.cpp
Ivailo Monev 1d0d3598c7 kdeui: use KToolInvocation::startServiceForUrl() instead of spawning kde-open process in KWidgetJobTracker
kde-open does exactly the same - it calls
KToolInvocation::startServiceForUrl() with the URL as argument

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2024-05-31 05:10:06 +03:00

678 lines
20 KiB
C++

/* This file is part of the KDE project
Copyright (C) 2000 Matej Koss <koss@miesto.sk>
Copyright (C) 2007 Kevin Ottens <ervin@kde.org>
Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
Copyright (C) 2009 Shaun Reich <shaun.reich@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kwidgetjobtracker.h"
#include "kwidgetjobtracker_p.h"
#include <QTimer>
#include <QLabel>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QMenu>
#include <QEvent>
#include <kurl.h>
#include <kpushbutton.h>
#include <ksqueezedtextlabel.h>
#include <kguiitem.h>
#include <kiconloader.h>
#include <kdialog.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <klocale.h>
#include <kwindowsystem.h>
#include <kseparator.h>
#include <ktoolinvocation.h>
void KWidgetJobTracker::Private::_k_showProgressWidget()
{
if (progressWidgetsToBeShown.isEmpty()) {
return;
}
KJob *job = progressWidgetsToBeShown.dequeue();
// If the job has been unregistered before reaching this point, widget will
// return 0.
QWidget *widget = q->widget(job);
if (widget) {
widget->show();
}
}
KWidgetJobTracker::KWidgetJobTracker(QWidget *parent)
: KAbstractWidgetJobTracker(parent),
d(new Private(parent, this))
{
}
KWidgetJobTracker::~KWidgetJobTracker()
{
delete d;
}
QWidget *KWidgetJobTracker::widget(KJob *job)
{
return d->progressWidget.value(job, 0);
}
bool KWidgetJobTracker::registerJob(KJob *job)
{
Private::ProgressWidget *vi = new Private::ProgressWidget(job, this, d->parent);
vi->jobRegistered = true;
vi->setAttribute(Qt::WA_DeleteOnClose);
d->progressWidget.insert(job, vi);
d->progressWidgetsToBeShown.enqueue(job);
KAbstractWidgetJobTracker::registerJob(job);
QTimer::singleShot(500, this, SLOT(_k_showProgressWidget()));
return true;
}
void KWidgetJobTracker::unregisterJob(KJob *job)
{
KAbstractWidgetJobTracker::unregisterJob(job);
d->progressWidgetsToBeShown.removeAll(job);
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->jobRegistered = false;
pWidget->deref();
}
bool KWidgetJobTracker::keepOpen(KJob *job) const
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return false;
}
return pWidget->keepOpenCheck->isChecked();
}
void KWidgetJobTracker::setStopOnClose(KJob *job, bool stopOnClose)
{
if (!d->progressWidget.contains(job)) {
return;
}
d->progressWidget[job]->stopOnClose = stopOnClose;
}
bool KWidgetJobTracker::stopOnClose(KJob *job) const
{
if (!d->progressWidget.contains(job)) {
kWarning() << "not found widget for job " << job << ". This method will return a "
"hardcoded value";
return true;
}
return d->progressWidget[job]->stopOnClose;
}
void KWidgetJobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich)
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->infoMessage(plain, rich);
}
void KWidgetJobTracker::description(KJob *job, const QString &title,
const QPair<QString, QString> &field1,
const QPair<QString, QString> &field2)
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->description(title, field1, field2);
}
void KWidgetJobTracker::totalAmount(KJob *job, KJob::Unit unit, qulonglong amount)
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->totalAmount(unit, amount);
}
void KWidgetJobTracker::processedAmount(KJob *job, KJob::Unit unit, qulonglong amount)
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->processedAmount(unit, amount);
}
void KWidgetJobTracker::percent(KJob *job, unsigned long percent)
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->percent(percent);
}
void KWidgetJobTracker::speed(KJob *job, unsigned long value)
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->speed(value);
}
void KWidgetJobTracker::suspended(KJob *job)
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->suspended();
}
void KWidgetJobTracker::resumed(KJob *job)
{
KWidgetJobTracker::Private::ProgressWidget *pWidget = d->progressWidget.value(job, 0);
if (!pWidget) {
return;
}
pWidget->resumed();
}
void KWidgetJobTracker::Private::ProgressWidget::ref()
{
++refCount;
}
void KWidgetJobTracker::Private::ProgressWidget::deref()
{
if (refCount) {
--refCount;
}
if (!refCount) {
if (!keepOpenCheck->isChecked()) {
closeNow();
} else {
percent(100);
cancelClose->setGuiItem(KStandardGuiItem::close());
openFile->setEnabled(true);
if (!totalSizeKnown || totalSize < processedSize)
totalSize = processedSize;
processedAmount(KJob::Bytes, totalSize);
keepOpenCheck->setEnabled(false);
pauseButton->setEnabled(false);
if (startTime.isValid()) {
qint64 s = startTime.elapsed();
if (!s) {
s = 1;
}
speedLabel->setText(i18n("%1/s (done)",
KGlobal::locale()->formatByteSize(1000 * totalSize / s)));
}
}
}
}
void KWidgetJobTracker::Private::ProgressWidget::closeNow()
{
close();
// It might happen the next scenario:
// - Start a job which opens a progress widget. Keep it open. Address job is 0xdeadbeef
// - Start a new job, which is given address 0xdeadbeef. A new window is opened.
// This one will take much longer to complete. The key 0xdeadbeef on the widget map now
// stores the new widget address.
// - Close the first progress widget that was opened (and has already finished) while the
// last one is still running. We remove its reference on the map. Wrong.
// For that reason we have to check if the map stores the widget as the current one.
// ereslibre
if (tracker->d->progressWidget[job] == this) {
tracker->d->progressWidget.remove(job);
tracker->d->progressWidgetsToBeShown.removeAll(job);
}
}
bool KWidgetJobTracker::Private::ProgressWidget::eventFilter(QObject *watched, QEvent *event)
{
// Handle context menu events for the source/dest labels here, so that we are ref()ed while the
// menu is exec()ed, to avoid a crash if the job finishes meanwhile. #159621.
if ((watched == sourceEdit || watched == destEdit) && event->type() == QEvent::ContextMenu) {
ref();
watched->event(event);
deref();
return true;
}
return QWidget::eventFilter(watched, event);
}
void KWidgetJobTracker::Private::ProgressWidget::infoMessage(const QString &plain, const QString &/*rich*/)
{
speedLabel->setText(plain);
speedLabel->setAlignment(speedLabel->alignment() & ~Qt::TextWordWrap);
}
void KWidgetJobTracker::Private::ProgressWidget::description(const QString &title,
const QPair<QString, QString> &field1,
const QPair<QString, QString> &field2)
{
setWindowTitle(title);
caption = title;
sourceInvite->setText(i18nc("%1 is the label, we add a ':' to it", "%1:", field1.first));
sourceEdit->setText(field1.second);
if (field2.first.isEmpty()) {
setDestVisible(false);
} else {
setDestVisible(true);
checkDestination(KUrl(field2.second));
destInvite->setText(i18nc("%1 is the label, we add a ':' to it", "%1:", field2.first));
destEdit->setText(field2.second);
}
}
void KWidgetJobTracker::Private::ProgressWidget::totalAmount(KJob::Unit unit, qulonglong amount)
{
switch(unit)
{
case KJob::Bytes:
totalSizeKnown = true;
// size is measured in bytes
if (totalSize == amount)
return;
totalSize = amount;
if (!startTime.isValid())
startTime.start();
break;
case KJob::Files:
if (totalFiles == amount)
return;
totalFiles = amount;
showTotals();
break;
case KJob::Directories:
if (totalDirs == amount)
return;
totalDirs = amount;
showTotals();
break;
}
}
void KWidgetJobTracker::Private::ProgressWidget::processedAmount(KJob::Unit unit, qulonglong amount)
{
QString tmp;
switch(unit) {
case KJob::Bytes: {
if (processedSize == amount) {
return;
}
processedSize = amount;
if (totalSizeKnown) {
tmp = i18np( "%2 of %3 complete", "%2 of %3 complete",
amount,
KGlobal::locale()->formatByteSize(amount),
KGlobal::locale()->formatByteSize(totalSize));
} else {
tmp = KGlobal::locale()->formatByteSize(amount);
}
sizeLabel->setText(tmp);
if (!totalSizeKnown) {
// update jumping progressbar
progressBar->setValue(amount);
}
break;
}
case KJob::Directories: {
if (processedDirs == amount) {
return;
}
processedDirs = amount;
tmp = i18np("%2 / %1 folder", "%2 / %1 folders", totalDirs, processedDirs);
tmp += " ";
tmp += i18np("%2 / %1 file", "%2 / %1 files", totalFiles, processedFiles);
progressLabel->setText(tmp);
break;
}
case KJob::Files: {
if (processedFiles == amount) {
return;
}
processedFiles = amount;
if (totalDirs > 1) {
tmp = i18np("%2 / %1 folder", "%2 / %1 folders", totalDirs, processedDirs);
tmp += " ";
}
tmp += i18np("%2 / %1 file", "%2 / %1 files", totalFiles, processedFiles);
progressLabel->setText(tmp);
}
}
}
void KWidgetJobTracker::Private::ProgressWidget::percent(unsigned long percent)
{
QString title = caption + " (";
if (totalSizeKnown) {
title += i18n("%1% of %2", percent,
KGlobal::locale()->formatByteSize(totalSize));
} else if (totalFiles) {
title += i18np("%2% of 1 file", "%2% of %1 files", totalFiles, percent);
} else {
title += i18n("%1%", percent);
}
title += ')';
progressBar->setMaximum(100);
progressBar->setValue(percent);
setWindowTitle(title);
}
void KWidgetJobTracker::Private::ProgressWidget::speed(unsigned long value)
{
if (value == 0) {
speedLabel->setText(i18n("Stalled"));
} else {
const QString speedStr = KGlobal::locale()->formatByteSize(value);
if (totalSizeKnown) {
const int remaining = 1000*(totalSize - processedSize)/value;
speedLabel->setText(i18np("%2/s (%3 remaining)", "%2/s (%3 remaining)", remaining, speedStr,
KGlobal::locale()->formatDuration(remaining)));
} else { // total size is not known (#24228)
speedLabel->setText(i18nc("speed in bytes per second", "%1/s", speedStr));
}
}
}
void KWidgetJobTracker::Private::ProgressWidget::suspended()
{
pauseButton->setText(i18n("&Resume"));
suspendedProperty = true;
}
void KWidgetJobTracker::Private::ProgressWidget::resumed()
{
pauseButton->setText(i18n("&Pause"));
suspendedProperty = false;
}
void KWidgetJobTracker::Private::ProgressWidget::closeEvent(QCloseEvent *event)
{
if (jobRegistered && tracker->stopOnClose(job)) {
tracker->slotStop(job);
}
QWidget::closeEvent(event);
}
void KWidgetJobTracker::Private::ProgressWidget::init()
{
// Set a useful icon for this window!
KWindowSystem::setIcons( winId(),
KIconLoader::global()->loadIcon("document-save", KIconLoader::NoGroup, 32),
KIconLoader::global()->loadIcon("document-save", KIconLoader::NoGroup, 16));
QVBoxLayout *topLayout = new QVBoxLayout(this);
QGridLayout *grid = new QGridLayout();
topLayout->addLayout(grid);
grid->addItem(new QSpacerItem(KDialog::spacingHint(),0),0,1);
// filenames or action name
sourceInvite = new QLabel(i18nc("The source url of a job", "Source:"), this);
grid->addWidget(sourceInvite, 0, 0);
sourceEdit = new KSqueezedTextLabel(this);
sourceEdit->setTextInteractionFlags(Qt::TextSelectableByMouse);
sourceEdit->installEventFilter(this);
grid->addWidget(sourceEdit, 0, 2);
destInvite = new QLabel(i18nc("The destination url of a job", "Destination:"), this);
grid->addWidget(destInvite, 1, 0);
destEdit = new KSqueezedTextLabel(this);
destEdit->setTextInteractionFlags(Qt::TextSelectableByMouse);
destEdit->installEventFilter(this);
grid->addWidget(destEdit, 1, 2);
QHBoxLayout *progressHBox = new QHBoxLayout();
topLayout->addLayout(progressHBox);
progressBar = new QProgressBar(this);
progressBar->setMaximum(0); // want a jumping progress bar if percent is not emitted
progressHBox->addWidget(progressBar);
suspendedProperty = false;
// processed info
QHBoxLayout *hBox = new QHBoxLayout();
topLayout->addLayout(hBox);
arrowButton = new KPushButton(this);
arrowButton->setMaximumSize(QSize(32,25));
arrowButton->setIcon(KIcon("arrow-down"));
arrowButton->setToolTip(i18n("Click this to expand the dialog, to show details"));
arrowState = Qt::DownArrow;
connect(arrowButton, SIGNAL(clicked()), this, SLOT(_k_arrowToggled()));
hBox->addWidget(arrowButton);
hBox->addStretch(1);
KSeparator *separator1 = new KSeparator(Qt::Horizontal, this);
topLayout->addWidget(separator1);
sizeLabel = new QLabel(this);
hBox->addWidget(sizeLabel, 0, Qt::AlignLeft);
resumeLabel = new QLabel(this);
hBox->addWidget(resumeLabel);
pauseButton = new KPushButton(i18n("&Pause"), this);
connect(pauseButton, SIGNAL(clicked()), this, SLOT(_k_pauseResumeClicked()));
hBox->addWidget(pauseButton);
hBox = new QHBoxLayout();
topLayout->addLayout(hBox);
speedLabel = new QLabel(this);
hBox->addWidget(speedLabel, 1);
speedLabel->hide();
hBox = new QHBoxLayout();
topLayout->addLayout(hBox);
progressLabel = new QLabel(this);
progressLabel->setAlignment(Qt::AlignLeft);
hBox->addWidget(progressLabel);
progressLabel->hide();
keepOpenCheck = new QCheckBox(i18n("&Keep this window open after transfer is complete"), this);
connect(keepOpenCheck, SIGNAL(toggled(bool)), this, SLOT(_k_keepOpenToggled(bool)));
topLayout->addWidget(keepOpenCheck);
keepOpenCheck->hide();
hBox = new QHBoxLayout();
topLayout->addLayout(hBox);
openFile = new KPushButton(i18n("Open &File"), this);
connect(openFile, SIGNAL(clicked()), this, SLOT(_k_openFile()));
hBox->addWidget(openFile);
openFile->setEnabled(false);
openFile->hide();
openLocation = new KPushButton(i18n("Open &Destination"), this);
connect(openLocation, SIGNAL(clicked()), this, SLOT(_k_openLocation()));
hBox->addWidget(openLocation);
openLocation->hide();
hBox->addStretch(1);
cancelClose = new KPushButton(KStandardGuiItem::cancel(), this);
connect(cancelClose, SIGNAL(clicked()), this, SLOT(_k_stop()));
hBox->addWidget(cancelClose);
resize(sizeHint());
setMaximumHeight(sizeHint().height());
setWindowTitle(i18n("Progress Dialog"));
}
void KWidgetJobTracker::Private::ProgressWidget::showTotals()
{
// Show the totals in the progress label, if we still haven't
// processed anything. This is useful when the stat'ing phase
// of CopyJob takes a long time (e.g. over networks).
if (processedFiles == 0 && processedDirs == 0) {
QString tmps;
if (totalDirs > 1) {
// that we have a singular to translate looks weired but is only logical
tmps = i18np("%1 folder", "%1 folders", totalDirs) + " ";
}
tmps += i18np("%1 file", "%1 files", totalFiles);
progressLabel->setText( tmps );
}
}
void KWidgetJobTracker::Private::ProgressWidget::setDestVisible(bool visible)
{
// We can't hide the destInvite/destEdit labels,
// because it screws up the QGridLayout.
if (visible) {
destInvite->show();
destEdit->show();
} else {
destInvite->hide();
destEdit->hide();
destInvite->setText( QString() );
destEdit->setText( QString() );
}
setMaximumHeight(sizeHint().height());
}
void KWidgetJobTracker::Private::ProgressWidget::checkDestination(const KUrl &dest)
{
bool ok = true;
if (dest.isLocalFile()) {
QString path = dest.toLocalFile( KUrl::RemoveTrailingSlash );
const QStringList tmpDirs = KGlobal::dirs()->resourceDirs( "tmp" );
for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) {
if (path.contains(*it)) {
// it's in the tmp resource
ok = false;
}
}
}
if (ok) {
openFile->show();
openLocation->show();
keepOpenCheck->show();
setMaximumHeight(sizeHint().height());
location=dest;
}
}
void KWidgetJobTracker::Private::ProgressWidget::_k_keepOpenToggled(bool keepOpen)
{
if (keepOpen) {
KGlobal::ref();
} else {
KGlobal::deref();
}
}
void KWidgetJobTracker::Private::ProgressWidget::_k_openFile()
{
KToolInvocation::self()->startServiceForUrl(location.url());
}
void KWidgetJobTracker::Private::ProgressWidget::_k_openLocation()
{
KUrl dirLocation(location);
dirLocation.setFileName(QString());
KToolInvocation::self()->startServiceForUrl(dirLocation.url());
}
void KWidgetJobTracker::Private::ProgressWidget::_k_pauseResumeClicked()
{
if (jobRegistered && !suspendedProperty) {
tracker->slotSuspend(job);
} else if (jobRegistered) {
tracker->slotResume(job);
}
}
void KWidgetJobTracker::Private::ProgressWidget::_k_stop()
{
if (jobRegistered) {
tracker->slotStop(job);
}
closeNow();
}
void KWidgetJobTracker::Private::ProgressWidget::_k_arrowToggled()
{
if (arrowState == Qt::DownArrow) {
//The arrow is in the down position, dialog is collapsed, expand it and change icon.
progressLabel->show();
speedLabel->show();
arrowButton->setIcon(KIcon("arrow-up"));
arrowButton->setToolTip(i18n("Click this to collapse the dialog, to hide details"));
arrowState = Qt::UpArrow;
} else {
//Collapse the dialog
progressLabel->hide();
speedLabel->hide();
arrowButton->setIcon(KIcon("arrow-down"));
arrowButton->setToolTip(i18n("Click this to expand the dialog, to show details"));
arrowState = Qt::DownArrow;
}
setMaximumHeight(sizeHint().height());
}
#include "moc_kwidgetjobtracker.cpp"
#include "moc_kwidgetjobtracker_p.cpp"