1 /*
2     SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "kjobcreator.h"
8 
9 #include <QApplication>
10 #include <QIcon>
11 #include <QPushButton>
12 
13 #include <KUiServerJobTracker>
14 #include <KUiServerV2JobTracker>
15 #include <KWidgetJobTracker>
16 
17 #include <limits>
18 
TestJob(QObject * parent)19 TestJob::TestJob(QObject *parent)
20     : KJob(parent)
21 {
22 }
23 
start()24 void TestJob::start()
25 {
26     m_started = true;
27 }
28 
started() const29 bool TestJob::started() const
30 {
31     return m_started;
32 }
33 
finish()34 void TestJob::finish()
35 {
36     emitResult();
37 }
38 
setPercentage(int percent)39 void TestJob::setPercentage(int percent)
40 {
41     setPercent(percent);
42 }
43 
setProcessedMiB(double bytes)44 void TestJob::setProcessedMiB(double bytes)
45 {
46     setProcessedAmount(KJob::Bytes, bytes * 1024 * 1024);
47 }
48 
setTotalMiB(double bytes)49 void TestJob::setTotalMiB(double bytes)
50 {
51     setTotalAmount(KJob::Bytes, bytes * 1024 * 1024);
52 }
53 
setProcessedFiles(int files)54 void TestJob::setProcessedFiles(int files)
55 {
56     setProcessedAmount(KJob::Files, files);
57 }
58 
setTotalFiles(int files)59 void TestJob::setTotalFiles(int files)
60 {
61     setTotalAmount(KJob::Files, files);
62 }
63 
setProcessedDirectories(int directories)64 void TestJob::setProcessedDirectories(int directories)
65 {
66     setProcessedAmount(KJob::Directories, directories);
67 }
68 
setTotalDirectories(int directories)69 void TestJob::setTotalDirectories(int directories)
70 {
71     setTotalAmount(KJob::Directories, directories);
72 }
73 
setProcessedItems(int items)74 void TestJob::setProcessedItems(int items)
75 {
76     setProcessedAmount(KJob::Items, items);
77 }
78 
setTotalItems(int items)79 void TestJob::setTotalItems(int items)
80 {
81     setTotalAmount(KJob::Items, items);
82 }
83 
setSpeedMiB(double speed)84 void TestJob::setSpeedMiB(double speed)
85 {
86     emitSpeed(speed * 1024 * 1024);
87 }
88 
setErrorState(int errorCode,const QString & errorTest)89 void TestJob::setErrorState(int errorCode, const QString &errorTest)
90 {
91     setError(errorCode);
92     setErrorText(errorTest);
93 }
94 
setSuspendable(bool suspendable)95 void TestJob::setSuspendable(bool suspendable)
96 {
97     auto caps = capabilities();
98     caps.setFlag(KJob::Suspendable, suspendable);
99     setCapabilities(caps);
100 }
101 
setKillable(bool killable)102 void TestJob::setKillable(bool killable)
103 {
104     auto caps = capabilities();
105     caps.setFlag(KJob::Killable, killable);
106     setCapabilities(caps);
107 }
108 
doKill()109 bool TestJob::doKill()
110 {
111     // TODO add checkbox for testing what happens when you try to kill a job that refuses?
112     return true;
113 }
114 
doSuspend()115 bool TestJob::doSuspend()
116 {
117     return true;
118 }
119 
doResume()120 bool TestJob::doResume()
121 {
122     return true;
123 }
124 
TestDialog(QWidget * parent)125 TestDialog::TestDialog(QWidget *parent)
126     : QDialog(parent)
127 {
128     m_ui.setupUi(this);
129 
130     m_ui.desktopEntry->setText(QGuiApplication::desktopFileName());
131 
132     m_ui.processedBytes->setMaximum(std::numeric_limits<double>::max());
133     m_ui.totalBytes->setMaximum(m_ui.processedBytes->maximum());
134     m_ui.processedFiles->setMaximum(std::numeric_limits<int>::max());
135     m_ui.totalFiles->setMaximum(m_ui.processedFiles->maximum());
136     m_ui.processedDirectories->setMaximum(std::numeric_limits<int>::max());
137     m_ui.totalDirectories->setMaximum(m_ui.processedDirectories->maximum());
138     m_ui.processedItems->setMaximum(std::numeric_limits<int>::max());
139     m_ui.totalItems->setMaximum(m_ui.processedItems->maximum());
140 
141     m_ui.speed->setMaximum(std::numeric_limits<double>::max());
142 
143     m_ui.createButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
144     m_ui.startButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
145     m_ui.suspendButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause")));
146     m_ui.killButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop")));
147     m_ui.finishButton->setIcon(QIcon::fromTheme(QStringLiteral("view-task")));
148 
149     updateUiState();
150 
151     connect(m_ui.desktopEntry, &QLineEdit::editingFinished, this, [this] {
152         QGuiApplication::setDesktopFileName(m_ui.desktopEntry->text());
153         updateUiState();
154     });
155 
156     connect(m_ui.destUrl, &QLineEdit::editingFinished, this, &TestDialog::updateJob);
157 
158     connect(m_ui.emitDescriptionButton, &QPushButton::clicked, this, [this] {
159         m_job->description(m_job,
160                            m_ui.title->text(),
161                            qMakePair(m_ui.descriptionLabel1->text(), m_ui.descriptionValue1->text()),
162                            qMakePair(m_ui.descriptionLabel2->text(), m_ui.descriptionValue2->text()));
163     });
164 
165     connect(m_ui.percent, &QSlider::valueChanged, this, &TestDialog::updateJob);
166     connect(m_ui.processedBytes, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &TestDialog::updateJob);
167     connect(m_ui.totalBytes, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &TestDialog::updateJob);
168     connect(m_ui.processedFiles, qOverload<int>(&QSpinBox::valueChanged), this, &TestDialog::updateJob);
169     connect(m_ui.totalFiles, qOverload<int>(&QSpinBox::valueChanged), this, &TestDialog::updateJob);
170     connect(m_ui.processedDirectories, qOverload<int>(&QSpinBox::valueChanged), this, &TestDialog::updateJob);
171     connect(m_ui.totalDirectories, qOverload<int>(&QSpinBox::valueChanged), this, &TestDialog::updateJob);
172     connect(m_ui.processedItems, qOverload<int>(&QSpinBox::valueChanged), this, &TestDialog::updateJob);
173     connect(m_ui.totalItems, qOverload<int>(&QSpinBox::valueChanged), this, &TestDialog::updateJob);
174     connect(m_ui.speed, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &TestDialog::updateJob);
175 
176     connect(m_ui.infoMessage, &QLineEdit::returnPressed, m_ui.emitInfoMessage, &QPushButton::click);
177     connect(m_ui.emitInfoMessage, &QPushButton::clicked, this, [this] {
178         m_job->infoMessage(m_job, m_ui.infoMessage->text(), QString());
179     });
180 
181     connect(m_ui.errorCombo, qOverload<int>(&QComboBox::activated), this, &TestDialog::updateJob);
182     connect(m_ui.errorText, &QLineEdit::editingFinished, this, &TestDialog::updateJob);
183 
184     connect(m_ui.suspendableCheck, &QCheckBox::toggled, this, &TestDialog::updateJob);
185     connect(m_ui.killableCheck, &QCheckBox::toggled, this, &TestDialog::updateJob);
186 
187     connect(m_ui.createButton, &QPushButton::clicked, this, [this] {
188         Q_ASSERT(!m_job);
189         m_job = new TestJob(this);
190         connect(m_job, &KJob::finished, this, [this] {
191             m_job = nullptr;
192             m_registeredWithWidgetTracker = false;
193             m_registeredWithUiServerTracker = false;
194             m_registeredWithUiServerV2Tracker = false;
195             updateUiState();
196         });
197         connect(m_job, &KJob::suspended, this, &TestDialog::updateUiState);
198         connect(m_job, &KJob::resumed, this, &TestDialog::updateUiState);
199 
200         // KJob auto-calculates percent based on processed/total amount, so sync that back into the UI
201         connect(m_job, &KJob::percentChanged, this, [this](KJob *job, unsigned long percent) {
202             Q_UNUSED(job);
203             m_ui.percent->setValue(percent);
204         });
205 
206         updateJob();
207         updateUiState();
208     });
209     connect(m_ui.startButton, &QPushButton::clicked, this, [this] {
210         m_job->start();
211         updateUiState();
212     });
213     connect(m_ui.suspendButton, &QPushButton::clicked, this, [this] {
214         if (m_job->isSuspended()) {
215             m_job->resume();
216         } else {
217             m_job->suspend();
218         }
219     });
220     connect(m_ui.killButton, &QPushButton::clicked, this, [this] {
221         m_job->kill();
222     });
223     connect(m_ui.finishButton, &QPushButton::clicked, this, [this] {
224         m_job->finish();
225     });
226 
227     connect(m_ui.registerKWidgetJobTracker, &QPushButton::clicked, this, [this] {
228         if (!m_widgetTracker) {
229             // not passing "parent" so it spawns a new window
230             m_widgetTracker.reset(new KWidgetJobTracker(nullptr));
231         }
232         m_widgetTracker->registerJob(m_job.data());
233         m_registeredWithWidgetTracker = true;
234         updateUiState();
235     });
236     connect(m_ui.registerKUIServerJobTracker, &QPushButton::clicked, this, [this] {
237         if (!m_uiServerTracker) {
238             m_uiServerTracker.reset(new KUiServerJobTracker(nullptr));
239         }
240         m_uiServerTracker->registerJob(m_job.data());
241         m_registeredWithUiServerTracker = true;
242         updateUiState();
243     });
244     connect(m_ui.registerKUIServerV2JobTracker, &QPushButton::clicked, this, [this] {
245         if (!m_uiServerV2Tracker) {
246             m_uiServerV2Tracker.reset(new KUiServerV2JobTracker(nullptr));
247         }
248         m_uiServerV2Tracker->registerJob(m_job.data());
249         m_registeredWithUiServerV2Tracker = true;
250         updateUiState();
251     });
252 }
253 
254 TestDialog::~TestDialog() = default;
255 
closeEvent(QCloseEvent * event)256 void TestDialog::closeEvent(QCloseEvent *event)
257 {
258     Q_UNUSED(event)
259     if (m_job) {
260         m_job->kill(KJob::EmitResult);
261     }
262 }
263 
updateUiState()264 void TestDialog::updateUiState()
265 {
266     m_ui.destUrl->setEnabled(m_job);
267 
268     m_ui.emitDescriptionButton->setEnabled(m_job);
269     m_ui.emitInfoMessage->setEnabled(m_job);
270 
271     m_ui.processedBytes->setEnabled(m_job);
272     m_ui.totalBytes->setEnabled(m_job);
273     m_ui.processedFiles->setEnabled(m_job);
274     m_ui.totalFiles->setEnabled(m_job);
275     m_ui.processedDirectories->setEnabled(m_job);
276     m_ui.totalDirectories->setEnabled(m_job);
277     m_ui.processedItems->setEnabled(m_job);
278     m_ui.totalItems->setEnabled(m_job);
279 
280     m_ui.percent->setEnabled(m_job);
281     m_ui.speed->setEnabled(m_job);
282     m_ui.errorCombo->setEnabled(m_job);
283     m_ui.errorText->setEnabled(m_job);
284 
285     m_ui.createButton->setEnabled(!m_job);
286     m_ui.startButton->setEnabled(m_job && !m_job->started());
287     m_ui.suspendButton->setEnabled(m_job);
288     m_ui.suspendButton->setChecked(m_job && m_job->isSuspended());
289     m_ui.killButton->setEnabled(m_job);
290     m_ui.finishButton->setEnabled(m_job);
291 
292     m_ui.registerKWidgetJobTracker->setEnabled(m_job && !m_registeredWithWidgetTracker);
293     m_ui.registerKUIServerJobTracker->setEnabled(m_job && !m_registeredWithUiServerTracker);
294     m_ui.registerKUIServerV2JobTracker->setEnabled(m_job && !m_registeredWithUiServerV2Tracker && !QGuiApplication::desktopFileName().isEmpty());
295 }
296 
updateJob()297 void TestDialog::updateJob()
298 {
299     if (!m_job) {
300         return;
301     }
302 
303     m_job->setProperty("destUrl", m_ui.destUrl->text());
304     m_job->setProperty("immediateProgressReporting", m_ui.immediateCheck->isChecked());
305 
306     m_job->setPercentage(m_ui.percent->value());
307 
308     m_job->setProcessedMiB(m_ui.processedBytes->value());
309     m_job->setTotalMiB(m_ui.totalBytes->value());
310     m_job->setProcessedFiles(m_ui.processedFiles->value());
311     m_job->setTotalFiles(m_ui.totalFiles->value());
312     m_job->setProcessedDirectories(m_ui.processedDirectories->value());
313     m_job->setTotalDirectories(m_ui.totalDirectories->value());
314     m_job->setProcessedItems(m_ui.processedItems->value());
315     m_job->setTotalItems(m_ui.totalItems->value());
316 
317     m_job->setSpeedMiB(m_ui.speed->value());
318 
319     m_job->setErrorState(m_ui.errorCombo->currentIndex(), m_ui.errorText->text());
320 
321     m_job->setSuspendable(m_ui.suspendableCheck->isChecked());
322     m_job->setKillable(m_ui.killableCheck->isChecked());
323 }
324 
main(int argc,char ** argv)325 int main(int argc, char **argv)
326 {
327     QApplication app(argc, argv);
328     app.setApplicationName(QStringLiteral("kjobcreator"));
329     app.setDesktopFileName(QStringLiteral("org.kde.dolphin"));
330 
331     TestDialog dialog;
332     dialog.show();
333 
334     return app.exec();
335 }
336