1 /*
2     This file is part of the KDE project
3     SPDX-FileCopyrightText: 2000 Matej Koss <koss@miesto.sk>
4     SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
5     SPDX-FileCopyrightText: 2007 Rafael Fernández López <ereslibre@kde.org>
6     SPDX-FileCopyrightText: 2009 Shaun Reich <shaun.reich@kdemail.net>
7 
8     SPDX-License-Identifier: LGPL-2.0-only
9 */
10 
11 #include "kwidgetjobtracker.h"
12 #include "kjobtrackerformatters_p.h"
13 #include "kwidgetjobtracker_p.h"
14 
15 #include <QCoreApplication>
16 #include <QDir>
17 #include <QEvent>
18 #include <QGridLayout>
19 #include <QLabel>
20 #include <QProcess>
21 #include <QProgressBar>
22 #include <QPushButton>
23 #include <QStyle>
24 #include <QTimer>
25 #include <QVBoxLayout>
26 
27 #include <KSeparator>
28 #include <KSqueezedTextLabel>
29 
_k_showProgressWidget()30 void KWidgetJobTrackerPrivate::_k_showProgressWidget()
31 {
32     Q_Q(KWidgetJobTracker);
33 
34     if (progressWidgetsToBeShown.isEmpty()) {
35         return;
36     }
37 
38     KJob *job = progressWidgetsToBeShown.dequeue();
39 
40     // If the job has been unregistered before reaching this point, widget will
41     // return 0.
42     QWidget *widget = q->widget(job);
43 
44     if (widget) {
45         // Don't steal the focus from the current widget (e. g. Kate)
46         widget->setAttribute(Qt::WA_ShowWithoutActivating);
47         widget->show();
48     }
49 }
50 
KWidgetJobTracker(QWidget * parent)51 KWidgetJobTracker::KWidgetJobTracker(QWidget *parent)
52     : KAbstractWidgetJobTracker(*new KWidgetJobTrackerPrivate(parent, this), parent)
53 {
54 }
55 
56 KWidgetJobTracker::~KWidgetJobTracker() = default;
57 
widget(KJob * job)58 QWidget *KWidgetJobTracker::widget(KJob *job)
59 {
60     Q_D(KWidgetJobTracker);
61 
62     return d->progressWidget.value(job, nullptr);
63 }
64 
registerJob(KJob * job)65 void KWidgetJobTracker::registerJob(KJob *job)
66 {
67     Q_D(KWidgetJobTracker);
68 
69     auto *vi = new KWidgetJobTrackerPrivate::ProgressWidget(job, this, d->parent);
70     vi->jobRegistered = true;
71     vi->setAttribute(Qt::WA_DeleteOnClose);
72     d->progressWidget.insert(job, vi);
73     d->progressWidgetsToBeShown.enqueue(job);
74 
75     KAbstractWidgetJobTracker::registerJob(job);
76 
77     QTimer::singleShot(500, this, SLOT(_k_showProgressWidget()));
78 }
79 
unregisterJob(KJob * job)80 void KWidgetJobTracker::unregisterJob(KJob *job)
81 {
82     Q_D(KWidgetJobTracker);
83 
84     KAbstractWidgetJobTracker::unregisterJob(job);
85 
86     d->progressWidgetsToBeShown.removeAll(job);
87     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
88     if (!pWidget) {
89         return;
90     }
91 
92     pWidget->jobRegistered = false;
93     pWidget->deref();
94 }
95 
keepOpen(KJob * job) const96 bool KWidgetJobTracker::keepOpen(KJob *job) const
97 {
98     Q_D(const KWidgetJobTracker);
99 
100     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
101     if (!pWidget) {
102         return false;
103     }
104 
105     return pWidget->keepOpenCheck->isChecked();
106 }
107 
infoMessage(KJob * job,const QString & plain,const QString & rich)108 void KWidgetJobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich)
109 {
110     Q_D(KWidgetJobTracker);
111 
112     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
113     if (!pWidget) {
114         return;
115     }
116 
117     pWidget->infoMessage(plain, rich);
118 }
119 
description(KJob * job,const QString & title,const QPair<QString,QString> & field1,const QPair<QString,QString> & field2)120 void KWidgetJobTracker::description(KJob *job, const QString &title, const QPair<QString, QString> &field1, const QPair<QString, QString> &field2)
121 {
122     Q_D(KWidgetJobTracker);
123 
124     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
125     if (!pWidget) {
126         return;
127     }
128 
129     pWidget->description(title, field1, field2);
130 }
131 
totalAmount(KJob * job,KJob::Unit unit,qulonglong amount)132 void KWidgetJobTracker::totalAmount(KJob *job, KJob::Unit unit, qulonglong amount)
133 {
134     Q_D(KWidgetJobTracker);
135 
136     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
137     if (!pWidget) {
138         return;
139     }
140 
141     pWidget->totalAmount(unit, amount);
142 }
143 
processedAmount(KJob * job,KJob::Unit unit,qulonglong amount)144 void KWidgetJobTracker::processedAmount(KJob *job, KJob::Unit unit, qulonglong amount)
145 {
146     Q_D(KWidgetJobTracker);
147 
148     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
149     if (!pWidget) {
150         return;
151     }
152 
153     pWidget->processedAmount(unit, amount);
154 }
155 
percent(KJob * job,unsigned long percent)156 void KWidgetJobTracker::percent(KJob *job, unsigned long percent)
157 {
158     Q_D(KWidgetJobTracker);
159 
160     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
161     if (!pWidget) {
162         return;
163     }
164 
165     pWidget->percent(percent);
166 }
167 
speed(KJob * job,unsigned long value)168 void KWidgetJobTracker::speed(KJob *job, unsigned long value)
169 {
170     Q_D(KWidgetJobTracker);
171 
172     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
173     if (!pWidget) {
174         return;
175     }
176 
177     pWidget->speed(value);
178 }
179 
slotClean(KJob * job)180 void KWidgetJobTracker::slotClean(KJob *job)
181 {
182     Q_D(KWidgetJobTracker);
183 
184     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
185     if (!pWidget) {
186         return;
187     }
188 
189     pWidget->slotClean();
190 }
191 
suspended(KJob * job)192 void KWidgetJobTracker::suspended(KJob *job)
193 {
194     Q_D(KWidgetJobTracker);
195 
196     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
197     if (!pWidget) {
198         return;
199     }
200 
201     pWidget->suspended();
202 }
203 
resumed(KJob * job)204 void KWidgetJobTracker::resumed(KJob *job)
205 {
206     Q_D(KWidgetJobTracker);
207 
208     KWidgetJobTrackerPrivate::ProgressWidget *pWidget = d->progressWidget.value(job, nullptr);
209     if (!pWidget) {
210         return;
211     }
212 
213     pWidget->resumed();
214 }
215 
ref()216 void KWidgetJobTrackerPrivate::ProgressWidget::ref()
217 {
218     ++refCount;
219 }
220 
deref()221 void KWidgetJobTrackerPrivate::ProgressWidget::deref()
222 {
223     if (refCount) {
224         --refCount;
225     }
226 
227     if (!refCount) {
228         if (!keepOpenCheck->isChecked()) {
229             closeNow();
230         } else {
231             slotClean();
232         }
233     }
234 }
235 
closeNow()236 void KWidgetJobTrackerPrivate::ProgressWidget::closeNow()
237 {
238     close();
239 
240     // It might happen the next scenario:
241     // - Start a job which opens a progress widget. Keep it open. Address job is 0xdeadbeef
242     // - Start a new job, which is given address 0xdeadbeef. A new window is opened.
243     //   This one will take much longer to complete. The key 0xdeadbeef on the widget map now
244     //   stores the new widget address.
245     // - Close the first progress widget that was opened (and has already finished) while the
246     //   last one is still running. We remove its reference on the map. Wrong.
247     // For that reason we have to check if the map stores the widget as the current one.
248     // ereslibre
249     if (tracker->d_func()->progressWidget[job] == this) {
250         tracker->d_func()->progressWidget.remove(job);
251         tracker->d_func()->progressWidgetsToBeShown.removeAll(job);
252     }
253 }
254 
eventFilter(QObject * watched,QEvent * event)255 bool KWidgetJobTrackerPrivate::ProgressWidget::eventFilter(QObject *watched, QEvent *event)
256 {
257     // Handle context menu events for the source/dest labels here, so that we are ref()ed while the
258     // menu is exec()ed, to avoid a crash if the job finishes meanwhile. #159621.
259     if ((watched == sourceEdit || watched == destEdit) && event->type() == QEvent::ContextMenu) {
260         ref();
261         watched->event(event);
262         deref();
263         return true;
264     }
265 
266     return QWidget::eventFilter(watched, event);
267 }
268 
infoMessage(const QString & plain,const QString &)269 void KWidgetJobTrackerPrivate::ProgressWidget::infoMessage(const QString &plain, const QString & /*rich*/)
270 {
271     speedLabel->setText(plain);
272     speedLabel->setAlignment(speedLabel->alignment() & ~Qt::TextWordWrap);
273 }
274 
description(const QString & title,const QPair<QString,QString> & field1,const QPair<QString,QString> & field2)275 void KWidgetJobTrackerPrivate::ProgressWidget::description(const QString &title, const QPair<QString, QString> &field1, const QPair<QString, QString> &field2)
276 {
277     setWindowTitle(title);
278     caption = title;
279     sourceInvite->setText(QCoreApplication::translate("KWidgetJobTracker", "%1:", "%1 is the label, we add a ':' to it").arg(field1.first));
280     sourceEdit->setText(field1.second);
281 
282     if (field2.first.isEmpty()) {
283         setDestVisible(false);
284     } else {
285         setDestVisible(true);
286         checkDestination(QUrl::fromUserInput(field2.second)); // path or URL
287         destInvite->setText(QCoreApplication::translate("KWidgetJobTracker", "%1:", "%1 is the label, we add a ':' to it").arg(field2.first));
288         destEdit->setText(field2.second);
289     }
290 }
291 
totalAmount(KJob::Unit unit,qulonglong amount)292 void KWidgetJobTrackerPrivate::ProgressWidget::totalAmount(KJob::Unit unit, qulonglong amount)
293 {
294     switch (unit) {
295     case KJob::Bytes:
296         totalSizeKnown = true;
297         // size is measured in bytes
298         if (totalSize == amount) {
299             return;
300         }
301         totalSize = amount;
302         if (!startTime.isValid()) {
303             startTime.start();
304         }
305         break;
306 
307     case KJob::Files:
308         if (totalFiles == amount) {
309             return;
310         }
311         totalFiles = amount;
312         showTotals();
313         break;
314 
315     case KJob::Directories:
316         if (totalDirs == amount) {
317             return;
318         }
319         totalDirs = amount;
320         showTotals();
321         break;
322 
323     case KJob::Items:
324         if (totalItems == amount) {
325             return;
326         }
327         totalItems = amount;
328         showTotals();
329         break;
330     }
331 }
332 
processedAmount(KJob::Unit unit,qulonglong amount)333 void KWidgetJobTrackerPrivate::ProgressWidget::processedAmount(KJob::Unit unit, qulonglong amount)
334 {
335     QString tmp;
336 
337     switch (unit) {
338     case KJob::Bytes:
339         if (processedSize == amount) {
340             return;
341         }
342         processedSize = amount;
343 
344         if (totalSizeKnown) {
345             //~ singular %1 of %2 complete
346             //~ plural %1 of %2 complete
347             tmp = QCoreApplication::translate("KWidgetJobTracker", "%1 of %2 complete", "", amount)
348                       .arg(KJobTrackerFormatters::byteSize(amount), KJobTrackerFormatters::byteSize(totalSize));
349         } else {
350             tmp = KJobTrackerFormatters::byteSize(amount);
351         }
352         sizeLabel->setText(tmp);
353         if (!totalSizeKnown) { // update jumping progressbar
354             progressBar->setValue(amount);
355         }
356         break;
357 
358     case KJob::Directories:
359         if (processedDirs == amount) {
360             return;
361         }
362         processedDirs = amount;
363 
364         //~ singular %1 / %n folder
365         //~ plural %1 / %n folders
366         tmp = QCoreApplication::translate("KWidgetJobTracker", "%1 / %n folder(s)", "", totalDirs).arg(processedDirs);
367         tmp += QLatin1String("   ");
368         //~ singular %1 / %n file
369         //~ plural %1 / %n files
370         tmp += QCoreApplication::translate("KWidgetJobTracker", "%1 / %n file(s)", "", totalFiles).arg(processedFiles);
371         progressLabel->setText(tmp);
372         break;
373 
374     case KJob::Files:
375         if (processedFiles == amount) {
376             return;
377         }
378         processedFiles = amount;
379 
380         if (totalDirs > 1) {
381             //~ singular %1 / %n folder
382             //~ plural %1 / %n folders
383             tmp = QCoreApplication::translate("KWidgetJobTracker", "%1 / %n folder(s)", "", totalDirs).arg(processedDirs);
384             tmp += QLatin1String("   ");
385         }
386         //~ singular %1 / %n file
387         //~ plural %1 / %n files
388         tmp += QCoreApplication::translate("KWidgetJobTracker", "%1 / %n file(s)", "", totalFiles).arg(processedFiles);
389         progressLabel->setText(tmp);
390         break;
391 
392     case KJob::Items:
393         if (processedItems == amount) {
394             return;
395         }
396         processedItems = amount;
397         //~ singular %1 / %n item
398         //~ plural %1 / %n items
399         tmp = QCoreApplication::translate("KWidgetJobTracker", "%1 / %n item(s)", "", totalItems).arg(processedItems);
400         progressLabel->setText(tmp);
401         break;
402     }
403 }
404 
percent(unsigned long percent)405 void KWidgetJobTrackerPrivate::ProgressWidget::percent(unsigned long percent)
406 {
407     QString title = caption + QLatin1String(" (");
408 
409     if (totalSizeKnown) {
410         title += QCoreApplication::translate("KWidgetJobTracker", "%1% of %2").arg(percent).arg(KJobTrackerFormatters::byteSize(totalSize));
411     } else if (totalFiles) {
412         //~ singular %1% of %n file
413         //~ plural %1% of %n files
414         title += QCoreApplication::translate("KWidgetJobTracker", "%1% of %n file(s)", "", totalFiles).arg(percent);
415     } else {
416         title += QCoreApplication::translate("KWidgetJobTracker", "%1%").arg(percent);
417     }
418 
419     title += QLatin1Char(')');
420 
421     progressBar->setMaximum(100);
422     progressBar->setValue(percent);
423     setWindowTitle(title);
424 }
425 
speed(unsigned long value)426 void KWidgetJobTrackerPrivate::ProgressWidget::speed(unsigned long value)
427 {
428     if (value == 0) {
429         speedLabel->setText(QCoreApplication::translate("KWidgetJobTracker", "Stalled"));
430     } else {
431         const QString speedStr = KJobTrackerFormatters::byteSize(value);
432         if (totalSizeKnown) {
433             const int remaining = 1000 * (totalSize - processedSize) / value;
434             //~ singular %1/s (%2 remaining)
435             //~ plural %1/s (%2 remaining)
436             speedLabel->setText(QCoreApplication::translate("KWidgetJobTracker", "%1/s (%2 remaining)", "", remaining)
437                                     .arg(speedStr, KJobTrackerFormatters::duration(remaining)));
438         } else { // total size is not known (#24228)
439             speedLabel->setText(QCoreApplication::translate("KWidgetJobTracker", "%1/s", "speed in bytes per second").arg(speedStr));
440         }
441     }
442 }
443 
slotClean()444 void KWidgetJobTrackerPrivate::ProgressWidget::slotClean()
445 {
446     percent(100);
447     cancelClose->setText(QCoreApplication::translate("KWidgetJobTracker", "&Close"));
448     cancelClose->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
449     cancelClose->setToolTip(QCoreApplication::translate("KWidgetJobTracker", "Close the current window or document"));
450     openFile->setEnabled(true);
451     if (!totalSizeKnown || totalSize < processedSize) {
452         totalSize = processedSize;
453     }
454     processedAmount(KJob::Bytes, totalSize);
455     keepOpenCheck->setEnabled(false);
456     pauseButton->setEnabled(false);
457     if (startTime.isValid()) {
458         int s = startTime.elapsed();
459         if (!s) {
460             s = 1;
461         }
462         speedLabel->setText(QCoreApplication::translate("KWidgetJobTracker", "%1/s (done)").arg(KJobTrackerFormatters::byteSize(1000 * totalSize / s)));
463     }
464 }
465 
suspended()466 void KWidgetJobTrackerPrivate::ProgressWidget::suspended()
467 {
468     pauseButton->setText(QCoreApplication::translate("KWidgetJobTracker", "&Resume"));
469     suspendedProperty = true;
470 }
471 
resumed()472 void KWidgetJobTrackerPrivate::ProgressWidget::resumed()
473 {
474     pauseButton->setText(QCoreApplication::translate("KWidgetJobTracker", "&Pause"));
475     suspendedProperty = false;
476 }
477 
closeEvent(QCloseEvent * event)478 void KWidgetJobTrackerPrivate::ProgressWidget::closeEvent(QCloseEvent *event)
479 {
480     if (jobRegistered && tracker->stopOnClose(job)) {
481         tracker->slotStop(job);
482     }
483 
484     QWidget::closeEvent(event);
485 }
486 
init()487 void KWidgetJobTrackerPrivate::ProgressWidget::init()
488 {
489     setWindowIcon(QIcon::fromTheme(QStringLiteral("document-save"), windowIcon()));
490 
491     QVBoxLayout *topLayout = new QVBoxLayout(this);
492 
493     QGridLayout *grid = new QGridLayout();
494     topLayout->addLayout(grid);
495     const int horizontalSpacing = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
496     grid->addItem(new QSpacerItem(horizontalSpacing, 0), 0, 1);
497     // filenames or action name
498     sourceInvite = new QLabel(QCoreApplication::translate("KWidgetJobTracker", "Source:", "The source url of a job"), this);
499     grid->addWidget(sourceInvite, 0, 0);
500 
501     sourceEdit = new KSqueezedTextLabel(this);
502     sourceEdit->setTextInteractionFlags(Qt::TextSelectableByMouse);
503     sourceEdit->installEventFilter(this);
504     grid->addWidget(sourceEdit, 0, 2);
505 
506     destInvite = new QLabel(QCoreApplication::translate("KWidgetJobTracker", "Destination:", "The destination url of a job"), this);
507     grid->addWidget(destInvite, 1, 0);
508 
509     destEdit = new KSqueezedTextLabel(this);
510     destEdit->setTextInteractionFlags(Qt::TextSelectableByMouse);
511     destEdit->installEventFilter(this);
512     grid->addWidget(destEdit, 1, 2);
513 
514     QHBoxLayout *progressHBox = new QHBoxLayout();
515     topLayout->addLayout(progressHBox);
516 
517     progressBar = new QProgressBar(this);
518     progressBar->setMaximum(0); // want a jumping progress bar if percent is not emitted
519     progressHBox->addWidget(progressBar);
520 
521     suspendedProperty = false;
522 
523     // processed info
524     QHBoxLayout *hBox = new QHBoxLayout();
525     topLayout->addLayout(hBox);
526 
527     arrowButton = new QPushButton(this);
528     arrowButton->setMaximumSize(QSize(32, 25));
529     arrowButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
530     arrowButton->setToolTip(QCoreApplication::translate("KWidgetJobTracker", "Click this to expand the dialog, to show details"));
531     arrowState = Qt::DownArrow;
532     connect(arrowButton, &QPushButton::clicked, this, &KWidgetJobTrackerPrivate::ProgressWidget::arrowClicked);
533     hBox->addWidget(arrowButton);
534     hBox->addStretch(1);
535 
536     KSeparator *separator1 = new KSeparator(Qt::Horizontal, this);
537     topLayout->addWidget(separator1);
538 
539     sizeLabel = new QLabel(this);
540     hBox->addWidget(sizeLabel, 0, Qt::AlignLeft);
541 
542     resumeLabel = new QLabel(this);
543     hBox->addWidget(resumeLabel);
544 
545     pauseButton = new QPushButton(QCoreApplication::translate("KWidgetJobTracker", "&Pause"), this);
546     pauseButton->setVisible(job && (job->capabilities() & KJob::Suspendable));
547     connect(pauseButton, &QPushButton::clicked, this, &KWidgetJobTrackerPrivate::ProgressWidget::pauseResumeClicked);
548     hBox->addWidget(pauseButton);
549 
550     hBox = new QHBoxLayout();
551     topLayout->addLayout(hBox);
552 
553     speedLabel = new QLabel(this);
554     hBox->addWidget(speedLabel, 1);
555     speedLabel->hide();
556 
557     hBox = new QHBoxLayout();
558     topLayout->addLayout(hBox);
559 
560     progressLabel = new QLabel(this);
561     progressLabel->setAlignment(Qt::AlignLeft);
562     hBox->addWidget(progressLabel);
563     progressLabel->hide();
564 
565     keepOpenCheck = new QCheckBox(QCoreApplication::translate("KWidgetJobTracker", "&Keep this window open after transfer is complete"), this);
566     connect(keepOpenCheck, &QCheckBox::toggled, this, &KWidgetJobTrackerPrivate::ProgressWidget::keepOpenToggled);
567     topLayout->addWidget(keepOpenCheck);
568     keepOpenCheck->hide();
569 
570     hBox = new QHBoxLayout();
571     topLayout->addLayout(hBox);
572 
573     openFile = new QPushButton(QCoreApplication::translate("KWidgetJobTracker", "Open &File"), this);
574     connect(openFile, &QPushButton::clicked, this, &KWidgetJobTrackerPrivate::ProgressWidget::openFileClicked);
575     hBox->addWidget(openFile);
576     openFile->setEnabled(false);
577     openFile->hide();
578 
579     openLocation = new QPushButton(QCoreApplication::translate("KWidgetJobTracker", "Open &Destination"), this);
580     connect(openLocation, &QPushButton::clicked, this, &KWidgetJobTrackerPrivate::ProgressWidget::openLocationClicked);
581     hBox->addWidget(openLocation);
582     openLocation->hide();
583 
584     hBox->addStretch(1);
585 
586     cancelClose = new QPushButton(this);
587     cancelClose->setText(QCoreApplication::translate("KWidgetJobTracker", "&Cancel"));
588     cancelClose->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
589     connect(cancelClose, &QPushButton::clicked, this, &KWidgetJobTrackerPrivate::ProgressWidget::cancelClicked);
590     hBox->addWidget(cancelClose);
591 
592     resize(sizeHint());
593     setMaximumHeight(sizeHint().height());
594 
595     setWindowTitle(QCoreApplication::translate("KWidgetJobTracker", "Progress Dialog")); // show something better than kuiserver
596 }
597 
showTotals()598 void KWidgetJobTrackerPrivate::ProgressWidget::showTotals()
599 {
600     // Show the totals in the progress label, if we still haven't
601     // processed anything. This is useful when the stat'ing phase
602     // of CopyJob takes a long time (e.g. over networks).
603     if (processedFiles == 0 && processedDirs == 0 && processedItems == 0) {
604         QString total;
605         if (totalItems > 1) {
606             //~ singular %n item
607             //~ plural %n items
608             total = QCoreApplication::translate("KWidgetJobTracker", "%n item(s)", "", totalItems);
609             progressLabel->setText(total);
610         } else {
611             if (totalDirs > 1) {
612                 //~ singular %n folder
613                 //~ plural %n folders
614                 total = QCoreApplication::translate("KWidgetJobTracker", "%n folder(s)", "", totalDirs) + QLatin1String("   ");
615             }
616             //~ singular %n file
617             //~ plural %n files
618             total += QCoreApplication::translate("KWidgetJobTracker", "%n file(s)", "", totalFiles);
619             progressLabel->setText(total);
620         }
621     }
622 }
623 
setDestVisible(bool visible)624 void KWidgetJobTrackerPrivate::ProgressWidget::setDestVisible(bool visible)
625 {
626     // We can't hide the destInvite/destEdit labels,
627     // because it screws up the QGridLayout.
628     if (visible) {
629         destInvite->show();
630         destEdit->show();
631     } else {
632         destInvite->hide();
633         destEdit->hide();
634         destInvite->setText(QString());
635         destEdit->setText(QString());
636     }
637     setMaximumHeight(sizeHint().height());
638 }
639 
checkDestination(const QUrl & dest)640 void KWidgetJobTrackerPrivate::ProgressWidget::checkDestination(const QUrl &dest)
641 {
642     bool ok = true;
643 
644     if (dest.isLocalFile()) {
645         const QString path = dest.toLocalFile();
646         if (path.contains(QDir::tempPath())) {
647             ok = false; // it's in the tmp directory
648         }
649     }
650 
651     if (ok) {
652         openFile->show();
653         openLocation->show();
654         keepOpenCheck->show();
655         setMaximumHeight(sizeHint().height());
656         location = dest;
657     }
658 }
659 
keepOpenToggled(bool keepOpen)660 void KWidgetJobTrackerPrivate::ProgressWidget::keepOpenToggled(bool keepOpen)
661 {
662     if (keepOpen) {
663         Q_ASSERT(!tracker->d_func()->eventLoopLocker);
664         tracker->d_func()->eventLoopLocker = new QEventLoopLocker;
665     } else {
666         delete tracker->d_func()->eventLoopLocker;
667         tracker->d_func()->eventLoopLocker = nullptr;
668     }
669 }
670 
openFileClicked()671 void KWidgetJobTrackerPrivate::ProgressWidget::openFileClicked()
672 {
673     QProcess::startDetached(QStringLiteral("kde-open"), QStringList() << location.toDisplayString());
674 }
675 
openLocationClicked()676 void KWidgetJobTrackerPrivate::ProgressWidget::openLocationClicked()
677 {
678     QProcess::startDetached(QStringLiteral("kde-open"), QStringList() << location.adjusted(QUrl::RemoveFilename).toString());
679 }
680 
pauseResumeClicked()681 void KWidgetJobTrackerPrivate::ProgressWidget::pauseResumeClicked()
682 {
683     if (jobRegistered && !suspendedProperty) {
684         tracker->slotSuspend(job);
685     } else if (jobRegistered) {
686         tracker->slotResume(job);
687     }
688 }
689 
cancelClicked()690 void KWidgetJobTrackerPrivate::ProgressWidget::cancelClicked()
691 {
692     if (jobRegistered) {
693         tracker->slotStop(job);
694     }
695     closeNow();
696 }
697 
arrowClicked()698 void KWidgetJobTrackerPrivate::ProgressWidget::arrowClicked()
699 {
700     if (arrowState == Qt::DownArrow) {
701         // The arrow is in the down position, dialog is collapsed, expand it and change icon.
702         progressLabel->show();
703         speedLabel->show();
704         arrowButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
705         arrowButton->setToolTip(QCoreApplication::translate("KWidgetJobTracker", "Click this to collapse the dialog, to hide details"));
706         arrowState = Qt::UpArrow;
707     } else {
708         // Collapse the dialog
709         progressLabel->hide();
710         speedLabel->hide();
711         arrowButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
712         arrowButton->setToolTip(QCoreApplication::translate("KWidgetJobTracker", "Click this to expand the dialog, to show details"));
713         arrowState = Qt::DownArrow;
714     }
715     setMaximumHeight(sizeHint().height());
716 }
717 
718 #include "moc_kwidgetjobtracker.cpp"
719 #include "moc_kwidgetjobtracker_p.cpp"
720