kdeui: rework job tracking

just watch me do it without a dedicated D-Bus service and dataengine

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2024-04-07 23:35:15 +03:00
parent 35e6fb9b0c
commit 8293d08153
15 changed files with 269 additions and 858 deletions

View file

@ -309,7 +309,7 @@ install(
KWidgetItemDelegate KWidgetItemDelegate
KWidgetJobTracker KWidgetJobTracker
KDynamicJobTracker KDynamicJobTracker
KUiServerJobTracker KPlasmaJobTracker
KPixmapSequence KPixmapSequence
KPixmapSequenceWidget KPixmapSequenceWidget
KPixmapSequenceOverlayPainter KPixmapSequenceOverlayPainter

View file

@ -0,0 +1 @@
#include "../kplasmajobtracker.h"

View file

@ -1 +0,0 @@
#include "../kuiserverjobtracker.h"

View file

@ -132,7 +132,7 @@ set(kdeui_LIB_SRCS
jobs/kdialogjobuidelegate.cpp jobs/kdialogjobuidelegate.cpp
jobs/kabstractwidgetjobtracker.cpp jobs/kabstractwidgetjobtracker.cpp
jobs/kwidgetjobtracker.cpp jobs/kwidgetjobtracker.cpp
jobs/kuiserverjobtracker.cpp jobs/kplasmajobtracker.cpp
jobs/kdynamicjobtracker.cpp jobs/kdynamicjobtracker.cpp
kernel/kapplication.cpp kernel/kapplication.cpp
kernel/kuniqueapplication.cpp kernel/kuniqueapplication.cpp
@ -250,13 +250,6 @@ if (X11_Xkb_FOUND AND X11_Xkbfile_FOUND)
) )
endif() endif()
qt4_add_dbus_interfaces(kdeui_LIB_SRCS jobs/org.kde.JobViewServer.xml )
qt4_add_dbus_interface(kdeui_LIB_SRCS
jobs/org.kde.JobView.xml
jobviewiface
)
set(kglobalaccel_xml shortcuts/org.kde.KGlobalAccel.xml) set(kglobalaccel_xml shortcuts/org.kde.KGlobalAccel.xml)
set_source_files_properties(${kglobalaccel_xml} PROPERTIES INCLUDE "kglobalshortcutinfo_p.h") set_source_files_properties(${kglobalaccel_xml} PROPERTIES INCLUDE "kglobalshortcutinfo_p.h")
qt4_add_dbus_interface(kdeui_LIB_SRCS ${kglobalaccel_xml} kglobalaccel_interface ) qt4_add_dbus_interface(kdeui_LIB_SRCS ${kglobalaccel_xml} kglobalaccel_interface )
@ -407,7 +400,7 @@ install(
jobs/kdialogjobuidelegate.h jobs/kdialogjobuidelegate.h
jobs/kabstractwidgetjobtracker.h jobs/kabstractwidgetjobtracker.h
jobs/kwidgetjobtracker.h jobs/kwidgetjobtracker.h
jobs/kuiserverjobtracker.h jobs/kplasmajobtracker.h
jobs/kdynamicjobtracker.h jobs/kdynamicjobtracker.h
kernel/kapplication.h kernel/kapplication.h
kernel/kuniqueapplication.h kernel/kuniqueapplication.h
@ -518,10 +511,3 @@ install(
PROGRAMS preparetips PROGRAMS preparetips
DESTINATION ${KDE4_BIN_INSTALL_DIR} DESTINATION ${KDE4_BIN_INSTALL_DIR}
) )
install(
FILES
jobs/org.kde.JobViewServer.xml
jobs/org.kde.JobView.xml
DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR}
)

View file

@ -20,7 +20,7 @@
#include "kdynamicjobtracker.h" #include "kdynamicjobtracker.h"
#include <kuiserverjobtracker.h> #include <kplasmajobtracker.h>
#include <kwidgetjobtracker.h> #include <kwidgetjobtracker.h>
#include <kjobtrackerinterface.h> #include <kjobtrackerinterface.h>
#include <kdebug.h> #include <kdebug.h>
@ -32,7 +32,7 @@
struct AllTrackers struct AllTrackers
{ {
KUiServerJobTracker *kuiserverTracker; KPlasmaJobTracker *plasmaTracker;
KWidgetJobTracker *widgetTracker; KWidgetJobTracker *widgetTracker;
}; };
@ -40,18 +40,18 @@ class KDynamicJobTracker::Private
{ {
public: public:
Private() Private()
: kuiserverTracker(nullptr), : plasmaTracker(nullptr),
widgetTracker(nullptr) widgetTracker(nullptr)
{ {
} }
~Private() ~Private()
{ {
delete kuiserverTracker; delete plasmaTracker;
delete widgetTracker; delete widgetTracker;
} }
KUiServerJobTracker *kuiserverTracker; KPlasmaJobTracker *plasmaTracker;
KWidgetJobTracker *widgetTracker; KWidgetJobTracker *widgetTracker;
QMap<KJob*, AllTrackers> trackers; QMap<KJob*, AllTrackers> trackers;
}; };
@ -69,19 +69,16 @@ KDynamicJobTracker::~KDynamicJobTracker()
void KDynamicJobTracker::registerJob(KJob *job) void KDynamicJobTracker::registerJob(KJob *job)
{ {
if (!d->kuiserverTracker) { if (!d->plasmaTracker) {
d->kuiserverTracker = new KUiServerJobTracker(); d->plasmaTracker = new KPlasmaJobTracker();
} }
d->trackers[job].kuiserverTracker = d->kuiserverTracker; d->trackers[job].plasmaTracker = d->plasmaTracker;
d->trackers[job].kuiserverTracker->registerJob(job); d->trackers[job].plasmaTracker->registerJob(job);
QDBusInterface interface("org.kde.kuiserver", "/JobViewServer", "", QDBusInterface interface("org.kde.plasma-desktop", "/JobTracker", "org.kde.JobTracker", QDBusConnection::sessionBus(), this);
QDBusConnection::sessionBus(), this); if (!interface.isValid()) {
QDBusReply<bool> reply = interface.call("requiresJobTracker"); // create a widget tracker in addition to KPlasmaJobTracker.
if (reply.isValid() && reply.value()) {
//create a widget tracker in addition to kuiservertracker.
if (!d->widgetTracker) { if (!d->widgetTracker) {
d->widgetTracker = new KWidgetJobTracker(); d->widgetTracker = new KWidgetJobTracker();
} }
@ -89,21 +86,21 @@ void KDynamicJobTracker::registerJob(KJob *job)
d->trackers[job].widgetTracker->registerJob(job); d->trackers[job].widgetTracker->registerJob(job);
} }
Q_ASSERT(d->trackers[job].kuiserverTracker || d->trackers[job].widgetTracker); Q_ASSERT(d->trackers[job].plasmaTracker || d->trackers[job].widgetTracker);
} }
void KDynamicJobTracker::unregisterJob(KJob *job) void KDynamicJobTracker::unregisterJob(KJob *job)
{ {
KUiServerJobTracker *kuiserverTracker = d->trackers[job].kuiserverTracker; KPlasmaJobTracker *plasmaTracker = d->trackers[job].plasmaTracker;
KWidgetJobTracker *widgetTracker = d->trackers[job].widgetTracker; KWidgetJobTracker *widgetTracker = d->trackers[job].widgetTracker;
if (!(widgetTracker || kuiserverTracker)) { if (!(widgetTracker || plasmaTracker)) {
kWarning() << "Tried to unregister a kio job that hasn't been registered."; kWarning() << "Tried to unregister a kio job that hasn't been registered.";
return; return;
} }
if (kuiserverTracker) { if (plasmaTracker) {
kuiserverTracker->unregisterJob(job); plasmaTracker->unregisterJob(job);
} }
if (widgetTracker) { if (widgetTracker) {
widgetTracker->unregisterJob(job); widgetTracker->unregisterJob(job);

View file

@ -25,14 +25,12 @@
/** /**
* This class implements a simple job tracker which registers any job to the KWidgetJobTracker if a * This class implements a simple job tracker which registers any job to the KWidgetJobTracker if a
* kuiserver isn't available on the DBus, or to the KUiServerJobTracker, if a kuiserver is * plasma isn't available on the DBus, or to the KPlasmaJobTracker, if a plasma is available. This
* available. This way, we have the old dialogs as fallback when the user doesn't use a kuiserver * way, we have the old dialogs as fallback when the user doesn't use a plasma applet.
* applet or application.
*/ */
class KDEUI_EXPORT KDynamicJobTracker : public KJobTrackerInterface class KDEUI_EXPORT KDynamicJobTracker : public KJobTrackerInterface
{ {
Q_OBJECT Q_OBJECT
public: public:
/** /**
* Creates a new KDynamicJobTracker * Creates a new KDynamicJobTracker
@ -48,8 +46,7 @@ public:
public Q_SLOTS: public Q_SLOTS:
/** /**
* Register a new job in this tracker. This call will get forwarded to either KWidgetJobTracker * Register a new job in this tracker.
* or KUiServerJobTracker, depending on the availability of the Kuiserver.
* *
* @param job the job to register * @param job the job to register
*/ */
@ -67,4 +64,4 @@ private:
Private *const d; Private *const d;
}; };
#endif #endif // KDYNAMICJOBTRACKER_H

View file

@ -0,0 +1,230 @@
/*
This file is part of the KDE libraries
Copyright (C) 2024 Ivailo Monev <xakepa10@gmail.com>
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 "kplasmajobtracker.h"
#include <QDBusInterface>
#include <QDBusConnectionInterface>
#include <kglobal.h>
#include <kcomponentdata.h>
#include <kaboutdata.h>
#include <kjob.h>
#include <kdebug.h>
static QString kJobID(const KJob *job)
{
return QString::number(quintptr(job), 16);
}
class KPlasmaJobTrackerPrivate
{
public:
KPlasmaJobTrackerPrivate();
void _k_slotStopRequested(const QString &name);
QMap<KJob*, QVariantMap> jobs;
QDBusInterface interface;
};
KPlasmaJobTrackerPrivate::KPlasmaJobTrackerPrivate()
: interface("org.kde.plasma-desktop", "/JobTracker", "org.kde.JobTracker", QDBusConnection::sessionBus())
{
QDBusConnectionInterface* sessionIface = QDBusConnection::sessionBus().interface();
if (!sessionIface->isServiceRegistered("org.kde.plasma-desktop")) {
kError() << "The service org.kde.plasma-desktop is still not registered";
} else {
kDebug() << "Plasma job tracker registered";
}
}
void KPlasmaJobTrackerPrivate::_k_slotStopRequested(const QString &name)
{
kDebug() << "job stop requested" << name;
foreach (KJob* job, jobs.keys()) {
const QString jobid = kJobID(job);
if (jobid == name) {
job->kill(KJob::EmitResult);
break;
}
}
}
KPlasmaJobTracker::KPlasmaJobTracker(QObject *parent)
: KJobTrackerInterface(parent),
d(new KPlasmaJobTrackerPrivate())
{
if (d->interface.isValid()) {
connect(
&d->interface, SIGNAL(stopRequested(QString)),
this, SLOT(_k_slotStopRequested(QString))
);
}
}
KPlasmaJobTracker::~KPlasmaJobTracker()
{
delete d;
}
void KPlasmaJobTracker::registerJob(KJob *job)
{
if (d->jobs.contains(job)) {
kWarning() << "atempting to register the same job twice" << job;
return;
}
const KComponentData componentData = KGlobal::mainComponent();
QString appName = job->property("appName").toString();
if (appName.isEmpty()) {
appName = componentData.aboutData()->programName();
}
QString appIconName = job->property("appIconName").toString();
if (appIconName.isEmpty()) {
appIconName = componentData.aboutData()->programIconName();
}
if (appIconName.isEmpty()) {
appIconName = componentData.aboutData()->appName();
}
const QString destUrl = job->property("destUrl").toString();
const QString jobid = kJobID(job);
QVariantMap jobdata;
jobdata.insert("infoMessage", QString());
jobdata.insert("appName", appName); // currently not used
jobdata.insert("appIconName", appIconName);
jobdata.insert("labelName0", QString());
jobdata.insert("labelName1", QString());
jobdata.insert("label0", QString());
jobdata.insert("label1", QString());
jobdata.insert("destUrl", destUrl);
jobdata.insert("error", QString());
jobdata.insert("percentage", 0);
jobdata.insert("state", "running");
jobdata.insert("killable", bool(job->capabilities() & KJob::Killable));
d->jobs.insert(job, jobdata);
d->interface.call("addJob", jobid);
d->interface.call("updateJob", jobid, jobdata);
kDebug() << "registerd job" << jobid << jobdata;
KJobTrackerInterface::registerJob(job);
}
void KPlasmaJobTracker::unregisterJob(KJob *job)
{
KJobTrackerInterface::unregisterJob(job);
if (!d->jobs.contains(job)) {
return;
}
// both finished() and unregistrJob will be called, either does it
kDebug() << "unregisterd job" << kJobID(job);
finished(job);
d->jobs.remove(job);
}
void KPlasmaJobTracker::finished(KJob *job)
{
if (!d->jobs.contains(job)) {
return;
}
const QString jobid = kJobID(job);
QVariantMap jobdata = d->jobs.value(job);
jobdata.insert("state", "stopped");
d->interface.call("updateJob", jobid, jobdata);
kDebug() << "job finished" << jobid;
d->jobs.remove(job);
}
void KPlasmaJobTracker::suspended(KJob *job)
{
if (!d->jobs.contains(job)) {
return;
}
const QString jobid = kJobID(job);
QVariantMap jobdata = d->jobs.value(job);
jobdata.insert("state", "suspended");
d->interface.call("updateJob", jobid, jobdata);
kDebug() << "job suspended" << jobid;
}
void KPlasmaJobTracker::resumed(KJob *job)
{
if (!d->jobs.contains(job)) {
return;
}
const QString jobid = kJobID(job);
QVariantMap jobdata = d->jobs.value(job);
jobdata.insert("state", "running");
d->interface.call("updateJob", jobid, jobdata);
kDebug() << "job resumed" << jobid;
}
void KPlasmaJobTracker::description(KJob *job, const QString &title,
const QPair<QString, QString> &field1,
const QPair<QString, QString> &field2)
{
if (!d->jobs.contains(job)) {
return;
}
const QString jobid = kJobID(job);
QVariantMap jobdata = d->jobs.value(job);
jobdata.insert("labelName0", field1.first);
jobdata.insert("label0", field1.second);
jobdata.insert("labelName1", field2.first);
jobdata.insert("label1", field2.second);
d->interface.call("updateJob", jobid, jobdata);
kDebug() << "job description" << jobid << field1 << field2;
}
void KPlasmaJobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich)
{
Q_UNUSED(rich);
if (!d->jobs.contains(job)) {
return;
}
const QString jobid = kJobID(job);
QVariantMap jobdata = d->jobs.value(job);
jobdata.insert("infoMessage", plain);
d->interface.call("updateJob", jobid, jobdata);
kDebug() << "job info message" << jobid << plain << rich;
}
void KPlasmaJobTracker::percent(KJob *job, unsigned long percent)
{
if (!d->jobs.contains(job)) {
return;
}
const QString jobid = kJobID(job);
QVariantMap jobdata = d->jobs.value(job);
jobdata.insert("percent", qulonglong(percent));
d->interface.call("updateJob", jobid, jobdata);
kDebug() << "job percent" << jobid << percent;
}
#include "moc_kplasmajobtracker.cpp"

View file

@ -1,9 +1,10 @@
/* This file is part of the KDE project /*
Copyright (C) 2007 Kevin Ottens <ervin@kde.org> This file is part of the KDE libraries
Copyright (C) 2024 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation. License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
@ -16,52 +17,30 @@
Boston, MA 02110-1301, USA. Boston, MA 02110-1301, USA.
*/ */
#ifndef KUISERVERJOBTRACKER_H #ifndef KPLASMAJOBTRACKER_H
#define KUISERVERJOBTRACKER_H #define KPLASMAJOBTRACKER_H
#include <kdeui_export.h> #include <kdeui_export.h>
#include <kjobtrackerinterface.h> #include <kjobtrackerinterface.h>
class KJob; class KJob;
class KPlasmaJobTrackerPrivate;
/** /**
* The interface to implement to track the progresses of a job. * The interface to implement to track the progresses of a job.
*/ */
class KDEUI_EXPORT KUiServerJobTracker : public KJobTrackerInterface class KDEUI_EXPORT KPlasmaJobTracker : public KJobTrackerInterface
{ {
Q_OBJECT Q_OBJECT
public: public:
/** KPlasmaJobTracker(QObject *parent = nullptr);
* Creates a new KJobTrackerInterface virtual ~KPlasmaJobTracker();
*
* @param parent the parent object
*/
KUiServerJobTracker(QObject *parent=0);
/**
* Destroys a KJobTrackerInterface
*/
virtual ~KUiServerJobTracker();
/**
* Register a new job in this tracker.
*
* @param job the job to register
*/
virtual void registerJob(KJob *job); virtual void registerJob(KJob *job);
/**
* Unregister a job from this tracker.
*
* @param job the job to unregister
*/
virtual void unregisterJob(KJob *job); virtual void unregisterJob(KJob *job);
protected Q_SLOTS: protected Q_SLOTS:
/**
* The following slots are inherited from KJobTrackerInterface.
*/
virtual void finished(KJob *job); virtual void finished(KJob *job);
virtual void suspended(KJob *job); virtual void suspended(KJob *job);
virtual void resumed(KJob *job); virtual void resumed(KJob *job);
@ -69,16 +48,12 @@ protected Q_SLOTS:
const QPair<QString, QString> &field1, const QPair<QString, QString> &field1,
const QPair<QString, QString> &field2); const QPair<QString, QString> &field2);
virtual void infoMessage(KJob *job, const QString &plain, const QString &rich); virtual void infoMessage(KJob *job, const QString &plain, const QString &rich);
virtual void totalAmount(KJob *job, KJob::Unit unit, qulonglong amount);
virtual void processedAmount(KJob *job, KJob::Unit unit, qulonglong amount);
virtual void percent(KJob *job, unsigned long percent); virtual void percent(KJob *job, unsigned long percent);
virtual void speed(KJob *job, unsigned long value);
private: private:
class Private; KPlasmaJobTrackerPrivate *const d;
Private *const d;
Q_PRIVATE_SLOT(d, void _k_killJob()) Q_PRIVATE_SLOT(d, void _k_slotStopRequested(const QString &name))
}; };
#endif #endif // KPLASMAJOBTRACKER_H

View file

@ -1,333 +0,0 @@
/* This file is part of the KDE project
Copyright (C) 2008 Rafael Fernández López <ereslibre@kde.org>
Copyright (C) 2007 Kevin Ottens <ervin@kde.org>
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 "kuiserverjobtracker.h"
#include "kuiserverjobtracker_p.h"
#include "jobviewiface.h"
#include <klocale.h>
#include <kdebug.h>
#include <ktoolinvocation.h>
#include <kcomponentdata.h>
#include <kaboutdata.h>
#include <kjob.h>
K_GLOBAL_STATIC(KSharedUiServerProxy, serverProxy)
class KUiServerJobTracker::Private
{
public:
Private(KUiServerJobTracker *parent)
: q(parent)
{
}
KUiServerJobTracker *const q;
void _k_killJob();
QHash<KJob*, org::kde::JobView*> progressJobView;
};
void KUiServerJobTracker::Private::_k_killJob()
{
org::kde::JobView *jobView = qobject_cast<org::kde::JobView*>(q->sender());
if (jobView) {
KJob *job = progressJobView.key(jobView);
if (job)
job->kill(KJob::EmitResult);
}
}
KUiServerJobTracker::KUiServerJobTracker(QObject *parent)
: KJobTrackerInterface(parent),
d(new Private(this))
{
}
KUiServerJobTracker::~KUiServerJobTracker()
{
if (!d->progressJobView.isEmpty()) {
qWarning() << "A KUiServerJobTracker instance contains"
<< d->progressJobView.size() << "stalled jobs";
}
delete d;
}
void KUiServerJobTracker::registerJob(KJob *job)
{
// Already registered job?
if (d->progressJobView.contains(job)) {
return;
}
KComponentData componentData = KGlobal::mainComponent();
QString appName = job->property("appName").toString();
if (appName.isEmpty()) {
appName = componentData.aboutData()->programName();
}
QString appIconName = job->property("appIconName").toString();
if (appIconName.isEmpty()) {
appIconName = componentData.aboutData()->programIconName();
}
if (appIconName.isEmpty()) {
appIconName = componentData.aboutData()->appName();
}
QWeakPointer<KJob> jobWatch = job;
QDBusReply<QDBusObjectPath> reply = serverProxy->uiserver().requestView(appName,
appIconName,
job->capabilities());
// If we got a valid reply, register the interface for later usage.
if (reply.isValid()) {
org::kde::JobView *jobView = new org::kde::JobView("org.kde.JobViewServer",
reply.value().path(),
QDBusConnection::sessionBus());
if (!jobWatch) {
//kDebug() << "deleted out from under us when asking the server proxy for the view";
jobView->terminate(QString());
delete jobView;
return;
}
QObject::connect(jobView, SIGNAL(cancelRequested()), this,
SLOT(_k_killJob()));
QObject::connect(jobView, SIGNAL(suspendRequested()), job,
SLOT(suspend()));
QObject::connect(jobView, SIGNAL(resumeRequested()), job,
SLOT(resume()));
QVariant destUrl = job->property("destUrl");
if (destUrl.isValid()) {
jobView->setDestUrl(destUrl.toString());
}
if (!jobWatch) {
//kDebug() << "deleted out from under us when creating the dbus interface";
jobView->terminate(QString());
delete jobView;
return;
}
d->progressJobView.insert(job, jobView);
} else if (!jobWatch) {
qWarning() << "Uh-oh...KUiServerJobTracker was trying to forward a job, but it was deleted from under us."
<< "kuiserver *may* have a stranded job. we can't do anything about it because the returned objectPath is invalid.";
return;
}
KJobTrackerInterface::registerJob(job);
}
void KUiServerJobTracker::unregisterJob(KJob *job)
{
KJobTrackerInterface::unregisterJob(job);
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView.take(job);
if (job->error()) {
jobView->terminate(job->errorText());
} else {
jobView->terminate(QString());
}
delete jobView;
}
void KUiServerJobTracker::finished(KJob *job)
{
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView.take(job);
if (job->error()) {
jobView->terminate(job->errorText());
} else {
jobView->terminate(QString());
}
}
void KUiServerJobTracker::suspended(KJob *job)
{
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView[job];
jobView->setSuspended(true);
}
void KUiServerJobTracker::resumed(KJob *job)
{
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView[job];
jobView->setSuspended(false);
}
void KUiServerJobTracker::description(KJob *job, const QString &title,
const QPair<QString, QString> &field1,
const QPair<QString, QString> &field2)
{
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView[job];
jobView->setInfoMessage(title);
if (field1.first.isNull() || field1.second.isNull()) {
jobView->clearDescriptionField(0);
} else {
jobView->setDescriptionField(0, field1.first, field1.second);
}
if (field2.first.isNull() || field2.second.isNull()) {
jobView->clearDescriptionField(1);
} else {
jobView->setDescriptionField(1, field2.first, field2.second);
}
}
void KUiServerJobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich)
{
Q_UNUSED(rich)
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView[job];
jobView->setInfoMessage(plain);
}
void KUiServerJobTracker::totalAmount(KJob *job, KJob::Unit unit, qulonglong amount)
{
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView[job];
switch (unit) {
case KJob::Bytes:
jobView->setTotalAmount(amount, "bytes");
break;
case KJob::Files:
jobView->setTotalAmount(amount, "files");
break;
case KJob::Directories:
jobView->setTotalAmount(amount, "dirs");
break;
default:
break;
}
}
void KUiServerJobTracker::processedAmount(KJob *job, KJob::Unit unit, qulonglong amount)
{
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView[job];
switch (unit) {
case KJob::Bytes:
jobView->setProcessedAmount(amount, "bytes");
break;
case KJob::Files:
jobView->setProcessedAmount(amount, "files");
break;
case KJob::Directories:
jobView->setProcessedAmount(amount, "dirs");
break;
default:
break;
}
}
void KUiServerJobTracker::percent(KJob *job, unsigned long percent)
{
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView[job];
jobView->setPercent(percent);
}
void KUiServerJobTracker::speed(KJob *job, unsigned long value)
{
if (!d->progressJobView.contains(job)) {
return;
}
org::kde::JobView *jobView = d->progressJobView[job];
jobView->setSpeed(value);
}
KSharedUiServerProxy::KSharedUiServerProxy()
: m_uiserver("org.kde.JobViewServer", "/JobViewServer", QDBusConnection::sessionBus())
{
QDBusConnectionInterface* sessionIface = QDBusConnection::sessionBus().interface();
if (!sessionIface->isServiceRegistered("org.kde.JobViewServer")) {
QDBusReply<void> sessionReply = sessionIface->startService("org.kde.kuiserver");
if (!sessionReply.isValid()) {
kError() << "Couldn't start kuiserver service" << sessionReply.error();
}
}
if (!sessionIface->isServiceRegistered("org.kde.JobViewServer"))
kError() << "The service org.kde.JobViewServer is still not registered";
else
kDebug() << "kuiserver registered";
}
KSharedUiServerProxy::~KSharedUiServerProxy()
{
}
org::kde::JobViewServer &KSharedUiServerProxy::uiserver()
{
return m_uiserver;
}
#include "moc_kuiserverjobtracker.cpp"
#include "moc_kuiserverjobtracker_p.cpp"

View file

@ -1,48 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2006-2008 Rafael Fernández López <ereslibre@kde.org>
Kevin Ottens <ervin@kde.org>
Copyright (C) 2000 Matej Koss <koss@miesto.sk>
David Faure <faure@kde.org>
Stephan Kulow <coolo@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef KUISERVERJOBTRACKER_P_H
#define KUISERVERJOBTRACKER_P_H
#include <QtCore/QMap>
#include <kglobal.h>
#include "jobviewserverinterface.h"
#include "jobviewiface.h"
class KSharedUiServerProxy : public QObject
{
Q_OBJECT
public:
KSharedUiServerProxy();
~KSharedUiServerProxy();
org::kde::JobViewServer &uiserver();
private:
org::kde::JobViewServer m_uiserver;
};
#endif

View file

@ -556,7 +556,7 @@ void KWidgetJobTracker::Private::ProgressWidget::init()
resize(sizeHint()); resize(sizeHint());
setMaximumHeight(sizeHint().height()); setMaximumHeight(sizeHint().height());
setWindowTitle(i18n("Progress Dialog")); // show something better than kuiserver setWindowTitle(i18n("Progress Dialog"));
} }
void KWidgetJobTracker::Private::ProgressWidget::showTotals() void KWidgetJobTracker::Private::ProgressWidget::showTotals()

View file

@ -1,65 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.JobView">
<method name="terminate">
<arg name="errorMessage" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setSuspended">
<arg name="suspended" type="b" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setTotalAmount">
<arg name="amount" type="t" direction="in"/>
<arg name="unit" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setProcessedAmount">
<arg name="amount" type="t" direction="in"/>
<arg name="unit" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setPercent">
<arg name="percent" type="u" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setSpeed">
<arg name="bytesPerSecond" type="t" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setInfoMessage">
<arg name="message" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setDescriptionField">
<arg name="number" type="u" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="s" direction="in"/>
<arg name="res" type="b" direction="out"/>
</method>
<method name="clearDescriptionField">
<arg name="number" type="u" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setAppName">
<arg name="message" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setAppIconName">
<arg name="message" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setCapabilities">
<arg name="number" type="i" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="setDestUrl">
<arg name="destUrl" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<signal name="suspendRequested"/>
<signal name="resumeRequested"/>
<signal name="cancelRequested"/>
</interface>
</node>

View file

@ -1,17 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.JobViewServer">
<method name="requestView">
<arg name="appName" type="s" direction="in"/>
<arg name="appIconName" type="s" direction="in"/>
<!-- 'capabilities' is used as a bit field:
0x0001 means that the user should be able to cancel the job
0x0002 means that the user should be able to suspend/resume the job
-->
<arg name="capabilities" type="i" direction="in"/>
<arg name="trackerPath" type="o" direction="out"/>
</method>
</interface>
</node>

View file

@ -1,276 +0,0 @@
DESIGN:
=======
libkio uses kioslaves (separate processes) that handle a given protocol.
Connection is the most low-level class, the one that encapsulates the pipe.
SlaveInterface is the class for transferring anything to the slave.
A slave inherits SlaveBase, which is the other half of SlaveInterface.
The scheduling is supposed to be on a two level basis. One is in the daemon
and one is in the application. The daemon one (as opposite to the holy one? :)
will determine how many slaves are ok for this app to be opened and it will
also assign tasks to actually existing slaves.
The application will still have some kind of a scheduler, but it should be
a lot simpler as it doesn't have to decide anything besides which
task goes to which pool of slaves (related to the protocol/host/user/port)
and move tasks around.
Currently a design study to name it cool is in scheduler.cpp but in the
application side. This is just to test other things like recursive jobs
and signals/slots within SlaveInterface. If someone feels brave, the scheduler
is yours!
On a second thought: at the daemon side there is no real scheduler, but a
pool of slaves. So what we need is some kind of load calculation of the
scheduler in the application and load balancing in the daemon.
A third thought: Maybe the daemon can just take care of a number of 'unused'
slaves. When an application needs a slave, it can request it from the daemon.
The application will get one, either from the pool of unused slaves,
or a new one will be created. This keeps things simple at the daemon level.
It is up to the application to give the slaves back to the daemon.
The scheduler in the application must take care not to request too many
slaves and could implement priorities.
Thought on usage:
* Typically a single slave-type is used exclusively in one application. E.g.
http slaves are used in a web-browser. POP3 slaves used in a mail program.
* Sometimes a single program can have multiple roles. E.g. konqueror is
both a web-browser and a file-manager. As a web-browser it primarily uses
http-slaves as a file-manager file-slaves.
* Selecting a link in konqueror: konqueror does a partial download of
the file to check the mimetype (right??) then the application is
started which downloads the complete file. In this case it should
be able to pass the slave which does the partial download from konqueror
to the application where it can do the complete download.
Do we need to have a hard limit on the number of slaves/host?
It seems so, because some protocols are about to fail if you
have two slaves running in parallel (e.g. POP3)
This has to be implemented in the daemon because only at daemon
level all the slaves are known. As a consequence slaves must
be returned to the daemon before connecting to another host.
(Returning the slaves back to the daemon after every job is not
strictly needed and only causes extra overhead)
Instead of actually returning the slave to the daemon, it could
be enough to ask 'recycling permission' from the daemon: the
application asks the daemon whether it is ok to use a slave for
another host. The daemon can then update its administration of
which slave is connected to which host.
The above does of course not apply to hostless protocols (like file).
(They will never change host).
Apart from a 'hard limit' on the number of slaves/host we can have
a 'soft limit'. E.g. upon connection to a HTTP 1.1 server, the web-
server tells the slave the number of parallel connections allowed.
THe simplest solution seems to be to treat 'soft limits' the same
as 'hard limits'. This means that the slave has to communicate the
'soft limit' to the daemon.
Jobs using multiple slaves.
If a job needs multiple slaves in parallel (e.g. copying a file from
a web-server to a ftp-server or browsing a tar-file on a ftp-site)
we must make sure to request the daemon for all slaves together since
otherwise there is a risk of deadlock.
(If two applications both need a 'pop3' and a 'ftp' slave for a single
job and only a single slave/host is allowed for pop3 and ftp, we must
prevent giving the single pop3 slave to application #1 and the single
ftp slave to application #2. Both applications will then wait till the
end of times till they get the other slave so that they can start the
job. (This is a quite unlikely situation, but nevertheless possible))
File Operations:
listRecursive is implemented as listDir and finding out if in the result
is a directory. If there is, another listDir job is issued. As listDir
is a readonly operation it fails when a directory isn't readable
.. but the main job goes on and discards the error, because
bIgnoreSubJobsError is true, which is what we want (David)
del is implemented as listRecursive, removing all files and removing all
empty directories. This basically means if one directory isn't readable
we don't remove it as listRecursive didn't find it. But the del will later
on try to remove it's parent directory and fail. But there are cases when
it would be possible to delete the dir in chmod the dir before. On the
other hand del("/") shouldn't list the whole file system and remove all
user owned files just to find out it can't remove everything else (this
basically means we have to take care of things we can remove before we try)
... Well, rm -rf / refuses to do anything, so we should just do the same:
use a listRecursive with bIgnoreSubJobsError = false. If anything can't
be removed, we just abort. (David)
... My concern was more that the fact we can list / doesn't mean we can
remove it. So we shouldn't remove everything we could list without checking
we can. But then the question arises how do we check whether we can remove it?
(Stephan)
... I was wrong, rm -rf /, even as a user, lists everything and removes
everything it can (don't try this at home!). I don't think we can do
better, unless we add a protocol-dependent "canDelete(path)", which is
_really_ not easy to implement, whatever protocol. (David)
Lib docu
========
mkdir: ...
rmdir: ...
chmod: ...
special: ...
stat: ...
get is implemented as TransferJob. Clients get 'data' signals with the data.
A data block of zero size indicates end of data (EOD)
put is implemented as TransferJob. Clients have to connect to the
'dataReq' signal. The slave will call you when it needs your data.
mimetype: ...
file_copy: copies a single file, either using CMD_COPY if the slave
supports that or get & put otherwise.
file_move: moves a single file, either using CMD_RENAME if the slave
supports that, CMD_COPY + del otherwise, or eventually
get & put & del.
file_delete: delete a single file.
copy: copies a file or directory, recursively if the latter
move: moves a file or directory, recursively if the latter
del: deletes a file or directory, recursively if the latter
PROGRESS DISPLAYING : [this is outdated, and describes the kde3 situation]
=====================
Taj brought up the idea of delegating all progress information to an extern
GUI daemon which could be provided in several implementations - examples
are popup dialogs (most are annoyed by them, like me :) or a kicker applet
or something completely different. This would also remove the dependency on
libkdeui (I hope).
Conclusion: kuiserver is this single GUI daemon, but the dependency on
libkdeui couldn't be removed (for many reasons, including rename/skip dialogs)
A. progress handling
---------------------
There will be two ways how the application can display progress :
1. regular apps will use NetAccess for all kio operations and will not care
about progress handling :
- NetAccess creates Job
- NetAccess creates JobObserver that will connect to the Job's signals and
pass them via dcop to the running GUI Progress Server
2. apps that want to do some handling with progress dialogs like Caitoo or
KMail :
- app creates Job
- app creates a progress dialog : this should be a ProgressBase descendant
e.g. StatusProgress or custom progress dialog
- app calls progress->setJob( job ) in order to connect job's signals with
progress dialog slots
B. customized progress dialogs
-------------------------------
This will be similar to what we had before.
- ProgressBase class that all other dialogs will inherit.
will contain an initialization method setJob( KIO::Job*) for apps of the
second class (see A.2 above), that will connect job's signals to dialog's
slots
- DefaultProgress ( former KIOSimpleProgressDialog ) that will be used for
regular progress dialogs created by GUI Progress Server
- StatusProgress ( former KIOLittleProgressDialog ) that can be used for
embedding in status bar
C. GUI Progress Server
-----------------------
This is a special progress server.
- createProgress() will either create a DefaultProgress dialog or add new entry
in a ListProgress ( an all-jobs-in-one progress dialog )
- after receiving signals from the JobObserver via DBus it will call
appropriate method of progress dialog ( either in DefaultProgress or ListProgress )
- ListProgres can be a Caitoo style dialog, kicker applet or both in one.
D. Some notes
--------------
1. most of the apps will not care at all about the progress display
2. user will be able to choose whether he wants to see separate progress
dialogs or all-in-one ListProgress dialog
3. developers can create their custom progress dialogs that inherit
ProgressBase and do any manipulation with a dialog if they use a second
approach ( see A.2 above )
Streaming [didn't work well, has been removed]
---------
1. We currently support a streaming "GET": e.g. file:/tmp/test.gz#gzip:/
works. The following should also work: file:/tmp/test.gz.gz#gzip:/#gzip:/
The current approach makes a TransferJob for gzip:/ and then adds a
subjob for "file:/tmp/test.gz.gz#gzip:/" which itself adds a subjob
for "file:/tmp/test.gz.gz".
2. This doesn't extend very well to PUT, because there the order should
basically be the other way around, but the "input" to the job as a whole
should go to the "gzip:/" job, not to the "file:/tmp/test.gz.gz."
It would probably be easier to implement such a job in the way the
current "CopyJob" is done. Have a Job and make all sub-urls sub-jobs of
this Job.
3. As a result of 1. COPY FROM an url like file:/tmp/test.gz#gzip:/ should
work. COPY TO does not, because that would require PUT.
Resuming
--------
A rough note for now, just to have this somewhere :
(PJ=put-job, GJ=get-job)
PJ can't resume:
PJ-->app: canResume(0) (emitted by dataReq)
GJ-->app: data()
PJ-->app: dataReq()
app->PJ: data()
PJ can resume but GJ can't resume:
PJ-->app: canResume(xx)
app->GJ: start job with "resume=xxx" metadata.
GJ-->app: data()
PJ-->app: dataReq()
app->PJ: data()
PJ can resume and GJ can resume:
PJ-->app: canResume(xx)
app->GJ: start job with "resume=xxx" metadata.
GJ-->app: canResume(xx)
GJ-->app: data()
PJ-->app: dataReq()
app->PJ: canResume(xx)
app->PJ: data()
So when the slave supports resume for "put" it has to check after the first
dataRequest() whether it has got a canResume() back from the app. If it did
it must resume. Otherwise it must start from 0.
Protocols
=========
Most KIO slaves (but not all) are implementing internet protocols.
In this case, the slave name matches the URI name for the protocol.
A list of such URIs can be found here, as per RFC 4395:
http://www.iana.org/assignments/uri-schemes.html

View file

@ -1,35 +0,0 @@
konq_run / krun should determine the mimetype by actually
getting the contents of the URL. It should then put the slave
on hold and tell the job-scheduler which request the
slave is currently handling. (Status: implemented in konq_run)
Now krun/konq_run should determine which client should process the
result of the request.
* When the client belongs to the same process, no action needs to be
taken. When a new job is created for the request which is on hold the
existing slave will be re-used and the request resumed.
(Status: implemented)
* When the client is an external process, the on-hold-slave should be
removed from the job-scheduler and should connect itself with
klauncher. This is hard because it must ensure that the external
program does not request the slave before it has been transfered to
klauncher. (Status: to be done)
* When a slave is on hold but not used for a certain period of time,
or, when another slave is put on hold, the slave should be killed.
(Status: almost done)
=====
The slave must emit "mimetype" during a GET before the first data is send.
It may wait with sending "mimetype" until it has enough data to
determine the mimetype, but it should not pass any data along before it has
send the mimetype.
Currently only http _always_ sends a mimetype.