1 /* This file is part of the KDE project
2 
3    Copyright (C) 2005 Dario Massarin <nekkar@libero.it>
4    Copyright (C) 2007 by Javier Goday <jgoday@gmail.com>
5    Copyright (C) 2008 - 2009 by Lukas Appelhans <l.appelhans@gmx.de>
6    Copyright (C) 2010 by Matthias Fuchs <mat69@gmx.net>
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public
10    License as published by the Free Software Foundation; either
11    version 2 of the License, or (at your option) any later version.
12 */
13 
14 #include "newtransferdialog.h"
15 
16 #include "core/filedeleter.h"
17 #include "core/kget.h"
18 #include "mainwindow.h"
19 #include "core/mostlocalurl.h"
20 #include "core/transfertreemodel.h"
21 #include "core/transfergrouphandler.h"
22 #include "core/plugin/transferfactory.h"
23 #include "core/urlchecker.h"
24 #include "settings.h"
25 
26 #include "kget_debug.h"
27 #include <QDebug>
28 
29 #include <QApplication>
30 #include <QClipboard>
31 #include <QDir>
32 #include <QFileInfo>
33 #include <QTimer>
34 
35 #include <KLocalizedString>
36 #include <QListWidgetItem>
37 #include <KColorScheme>
38 #include <KWindowSystem>
39 #include <LineEditUrlDropEventFilter>
40 #include <QStandardPaths>
41 
Q_GLOBAL_STATIC(NewTransferDialogHandler,newTransferDialogHandler)42 Q_GLOBAL_STATIC(NewTransferDialogHandler, newTransferDialogHandler)
43 
44 
45 NewTransferDialog::NewTransferDialog(QWidget *parent)
46   : QDialog(parent),
47     m_window(nullptr),
48     m_existingTransfer(nullptr),
49     m_multiple(false),
50     m_overWriteSingle(false)
51 {
52     setModal(true);
53     setWindowTitle(i18n("New Download"));
54 
55     ui.setupUi(this);
56 
57     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
58 
59     //timer to avoid constant checking of the input
60     m_timer = new QTimer(this);
61     m_timer->setInterval(350);
62     m_timer->setSingleShot(true);
63     connect(m_timer, &QTimer::timeout, this, &NewTransferDialog::checkInput);
64 
65     const KColorScheme scheme = KColorScheme(QPalette::Active, KColorScheme::View);
66     m_existingFileBackground = scheme.background(KColorScheme::NeutralBackground);
67     m_normalBackground = scheme.background();
68 
69 
70     // properties of the m_destRequester combobox
71     auto *dropUrlEventFilter = new LineEditUrlDropEventFilter(this);
72     dropUrlEventFilter->installEventFilter(ui.destRequester->comboBox());
73     ui.destRequester->comboBox()->setDuplicatesEnabled(false);
74     ui.destRequester->comboBox()->setEditable(true);
75     ui.destRequester->setAcceptMode(QFileDialog::AcceptSave);
76 
77     ui.errorWidget->setCloseButtonVisible(false);
78 
79     connect(ui.groupComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setDefaultDestination()));
80 
81     connect(ui.urlRequester, &QLineEdit::textChanged, this, &NewTransferDialog::setDefaultDestination);
82     connect(ui.destRequester, &KUrlRequester::textChanged, this, &NewTransferDialog::inputTimer);
83     connect(ui.urlRequester, &QLineEdit::textChanged, this, &NewTransferDialog::inputTimer);
84     connect(ui.listWidget, &QListWidget::itemChanged, this, &NewTransferDialog::inputTimer);
85     connect(this, &QDialog::finished, this, &NewTransferDialog::slotFinished);
86     connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
87     connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
88 }
89 
~NewTransferDialog()90 NewTransferDialog::~NewTransferDialog()
91 {
92 }
93 
setMultiple(bool useMultiple)94 void NewTransferDialog::setMultiple(bool useMultiple)
95 {
96     m_multiple = useMultiple;
97 
98     const Qt::Alignment alignment = Qt::AlignLeft | (m_multiple ? Qt::AlignTop : Qt::AlignVCenter);
99     ui.urlLabel->setAlignment(alignment);
100     ui.urlRequester->setVisible(!m_multiple);
101     ui.listWidget->setVisible(m_multiple);
102     ui.destRequester->setMode(m_multiple ? KFile::Directory : KFile::File);
103 }
104 
clear()105 void NewTransferDialog::clear()
106 {
107     ui.urlRequester->clear();
108     ui.urlRequester->setFocus();
109     ui.listWidget->clear();
110     ui.destRequester->comboBox()->clear();
111     ui.destRequester->clear();
112     m_destination.clear();
113     m_sources.clear();
114     m_existingTransfer = nullptr;
115     m_overWriteSingle = false;
116 
117     //add all destinations
118     QStringList list;
119     QString downloadPath = KGet::generalDestDir();
120     if (!downloadPath.isEmpty()) {
121         if (!downloadPath.endsWith('/')) {
122             downloadPath.append('/');
123         }
124         list << downloadPath;
125     }
126     foreach (TransferGroupHandler *handler, KGet::allTransferGroups()) {
127         const QString folder = handler->defaultFolder();
128         if (!folder.isEmpty()) {
129             list << (folder.endsWith('/') ? folder : folder + '/');
130         }
131     }
132 
133     list.removeDuplicates();
134     ui.destRequester->comboBox()->insertItems(0, list);
135 
136     //add all transfer groups
137     ui.groupComboBox->clear();
138     foreach (TransferGroupHandler *group, KGet::allTransferGroups()) {
139         ui.groupComboBox->addItem(QIcon::fromTheme(group->iconName()), group->name());
140     }
141     ui.groupComboBox->setCurrentItem(Settings::lastGroup());
142     if (ui.groupComboBox->currentIndex() == -1) {
143         ui.groupComboBox->setCurrentIndex(0);
144     }
145 
146     const bool multipleGroups = KGet::transferGroupNames().count();
147     ui.groupComboBox->setVisible(multipleGroups);
148     ui.groupLabel->setVisible(multipleGroups);
149 }
150 
setSource(const QList<QUrl> & sources)151 void NewTransferDialog::setSource(const QList<QUrl> &sources)
152 {
153     if (sources.isEmpty()) {
154         return;
155     }
156 
157     if (sources.count() == 1) {
158         QUrl m_srcUrl = sources.first();
159         ui.urlRequester->clear();
160         if (m_srcUrl.isEmpty()) {
161             m_srcUrl = QUrl(QApplication::clipboard()->text(QClipboard::Clipboard).trimmed());
162         }
163 
164         if (UrlChecker::checkSource(m_srcUrl) == UrlChecker::NoError) {
165             ui.urlRequester->insert(m_srcUrl.toString());
166         }
167     } else {
168         foreach (const QUrl &sourceUrl, sources) {
169             if (sourceUrl.url() != QUrl(sourceUrl.url()).fileName()) {//TODO simplify, whatfor is this check anyway, shouldn't the sources be checked already and if not add this to UrlChecker
170                 qCDebug(KGET_DEBUG) << "Insert" << sourceUrl;
171                 auto *newItem = new QListWidgetItem(sourceUrl.toString(), ui.listWidget);
172                 newItem->setCheckState(Qt::Checked);
173             }
174         }
175     }
176 
177     const QList<TransferGroupHandler*> groups = KGet::groupsFromExceptions(sources.first());
178     if (!groups.isEmpty()) {
179         ui.groupComboBox->setCurrentIndex(ui.groupComboBox->findText(groups.first()->name()));
180     }
181 }
182 
setDestinationFileName(const QString & filename)183 void NewTransferDialog::setDestinationFileName(const QString &filename)
184 {
185     ui.destRequester->setUrl(QUrl(ui.destRequester->url().adjusted(QUrl::RemoveFilename).toString() + filename));
186 }
187 
setDestination()188 void NewTransferDialog::setDestination()
189 {
190     //sets destRequester to either display the defaultFolder of group or the generalDestDir
191     QString group = ui.groupComboBox->currentText();
192     TransferGroupHandler * current = nullptr;
193     foreach (TransferGroupHandler * handler, KGet::allTransferGroups()) {
194         if (handler->name() == group) {
195             current = handler;
196             break;
197         }
198     }
199 
200     if (current) {
201         QString groupFolder = current->defaultFolder();
202         if (groupFolder.isEmpty()) {
203             groupFolder = KGet::generalDestDir();
204         }
205         if (!groupFolder.endsWith('/')) {
206             groupFolder.append('/');
207         }
208         ui.destRequester->comboBox()->setCurrentItem(groupFolder, true);
209     }
210 }
211 
showDialog(QList<QUrl> list,const QString & suggestedFileName)212 void NewTransferDialog::showDialog(QList<QUrl> list, const QString &suggestedFileName)
213 {
214     //TODO handle the case where for some there are suggested file names --> own file name column in multiple setting
215     //the dialog is already in use, adapt it
216     if (isVisible()) {
217         list << m_sources;
218     }
219     clear();//Let's clear the old stuff
220     m_sources << list;
221     UrlChecker::removeDuplicates(m_sources);
222     const int size = m_sources.size();
223     qCDebug(KGET_DEBUG) << "SET SOURCES " << m_sources << " MULTIPLE " << (size > 1);
224     setMultiple(size > 1);
225 
226     if (size) {
227         if (size == 1 && !suggestedFileName.isEmpty()) {
228             setDestinationFileName(suggestedFileName);
229         }
230 
231         setSource(m_sources);
232     }
233 
234     prepareDialog();
235 }
236 
setDefaultDestination()237 void NewTransferDialog::setDefaultDestination()
238 {
239     //NOTE if the user enters a file name manually and the changes the group the manually entered file name will be overwritten
240     setDestination();
241 
242     //set a file name
243     if (!m_multiple) {
244         const QUrl url(ui.urlRequester->text().trimmed());
245         if ((UrlChecker::checkSource(url) == UrlChecker::NoError) &&
246             QFileInfo(ui.destRequester->url().toLocalFile()).isDir()) {
247             setDestinationFileName(url.fileName());
248         }
249     }
250 }
251 
prepareDialog()252 void NewTransferDialog::prepareDialog()
253 {
254     if (m_window) {
255         KWindowInfo info(m_window->winId(), NET::WMDesktop);
256         KWindowSystem::setCurrentDesktop(info.desktop());
257         KWindowSystem::forceActiveWindow(m_window->winId());
258     }
259 
260     qCDebug(KGET_DEBUG) << "Show the dialog!";
261     show();
262 }
263 
isEmpty()264 bool NewTransferDialog::isEmpty()
265 {
266     return (m_multiple ? !ui.listWidget->count() : ui.urlRequester->text().trimmed().isEmpty());
267 }
268 
inputTimer()269 void NewTransferDialog::inputTimer()
270 {
271     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
272     m_timer->start();
273 }
274 
checkInput()275 void NewTransferDialog::checkInput()
276 {
277     qDebug() << "Check input";
278     QUrl source = QUrl(ui.urlRequester->text().trimmed());
279     const QUrl dest = ui.destRequester->url();
280 
281     //check the destination folder
282     UrlChecker::UrlError error = UrlChecker::checkFolder(dest);
283     const bool folderValid = (error == UrlChecker::NoError);
284     bool destinationValid = false;
285     QString infoText;
286     QString warningText;
287     if (!folderValid) {
288         if (m_multiple) {
289             infoText = UrlChecker::message(QUrl(), UrlChecker::Folder, error);
290         } else {
291             //might be a destination instead of a folder
292             destinationValid = (UrlChecker::checkDestination(dest) == UrlChecker::NoError);
293         }
294     } else {
295         m_destination = dest;
296     }
297 
298     //check the source
299     if (!m_multiple) {
300         source = mostLocalUrl(source);
301     }
302     error = UrlChecker::checkSource(source);
303     const bool sourceValid = (error == UrlChecker::NoError);
304     if (!m_multiple && !sourceValid) {
305         infoText = UrlChecker::message(QUrl(), UrlChecker::Source, error);
306     }
307 
308     //check if any sources are checked and for existing transfers or destinations
309     bool filesChecked = false;
310     if (m_multiple && folderValid) {
311         QListWidget *list = ui.listWidget;
312 
313         //check if some sources have been checked
314         for (int i = 0; i < list->count(); ++i) {
315             QListWidgetItem *item = list->item(i);
316             if (item->checkState() == Qt::Checked) {
317                 filesChecked = true;
318                 break;
319             }
320         }
321         if (!filesChecked) {
322             infoText = i18n("Select at least one source url.");
323         }
324 
325         //check if there are existing files
326         if (filesChecked) {
327             bool existingFile = false;
328             for (int i = 0; i < list->count(); ++i) {
329                 QListWidgetItem *item = list->item(i);
330                 const QUrl source = QUrl(item->text());
331                 const QUrl destUrl = UrlChecker::destUrl(dest, source);
332                 if (UrlChecker::wouldOverwrite(source, destUrl)) {
333                     item->setBackground(m_existingFileBackground);
334                     existingFile = true;
335                 } else {
336                     item->setBackground(m_normalBackground);
337                 }
338             }
339             if (existingFile) {
340                 warningText = i18n("Files that exist already in the current folder have been marked.");//TODO better message
341             }
342         }
343     }
344 
345     //single file
346     UrlChecker::UrlWarning warning = UrlChecker::NoWarning;
347     if (!m_multiple && sourceValid && (folderValid || destinationValid)) {
348         m_destination = UrlChecker::destUrl(dest, source);
349         //show only one message for existing transfers
350         m_existingTransfer = UrlChecker::existingTransfer(source, UrlChecker::Source, &warning);
351         if (m_existingTransfer) {
352             warningText = UrlChecker::message(QUrl(), UrlChecker::Source, warning);
353         } else {
354             m_existingTransfer = UrlChecker::existingTransfer(m_destination, UrlChecker::Destination, &warning);
355             if (m_existingTransfer) {
356                 warningText = UrlChecker::message(QUrl(), UrlChecker::Destination, warning);
357             }
358         }
359 
360         if (UrlChecker::wouldOverwrite(QUrl(ui.urlRequester->text().trimmed()), m_destination)) {
361             m_overWriteSingle = true;
362             if (!warningText.isEmpty()) {
363                 warningText += '\n';
364             }
365             warningText += UrlChecker::message(QUrl(), UrlChecker::Destination, UrlChecker::ExistingFile);
366         } else {
367             m_overWriteSingle = false;
368         }
369     }
370 
371     if (!infoText.isEmpty()) {
372         setInformation(infoText);
373     } else if (!warningText.isEmpty()) {
374         setWarning(warningText);
375     } else {
376         ui.errorWidget->hide();
377     }
378 
379     //activate the ok button
380     if (m_multiple) {
381         ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(folderValid && filesChecked);
382     } else {
383         ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled((folderValid || destinationValid) && sourceValid);
384     }
385 
386     qCDebug(KGET_DEBUG) << source << source.fileName() << dest << dest.fileName() << "Folder valid:" << folderValid
387                  << "Destination valid:" << destinationValid << "Source valid:" << sourceValid;
388 }
389 
slotFinished(int resultCode)390 void NewTransferDialog::slotFinished(int resultCode)
391 {
392     if (resultCode == QDialog::Accepted) {
393         dialogAccepted();
394     }
395     clear();
396 }
397 
dialogAccepted()398 void NewTransferDialog::dialogAccepted()
399 {
400     qCDebug(KGET_DEBUG) << "Dialog accepted.";
401 
402     //an existing transfer has been specified and since ok was clicked, it was chosen to be overwritten
403     if (m_existingTransfer) {
404         qCDebug(KGET_DEBUG) << "Removing existing transfer:" << m_existingTransfer;
405         KGet::delTransfer(m_existingTransfer);
406     }
407 
408     //set the last directory
409     QString dir = m_destination.toLocalFile();
410     if (!QFileInfo(dir).isDir()) {
411         dir = m_destination.adjusted(QUrl::RemoveFilename).toLocalFile();
412     }
413     Settings::setLastDirectory(dir);
414     Settings::self()->save();
415 
416     const QString group = ui.groupComboBox->currentText();
417 
418     ///add data to create transfers
419     QList<KGet::TransferData> data;
420     if (!m_multiple) {
421         if (m_overWriteSingle) {
422             qCDebug(KGET_DEBUG) << "Removing existing file:" << m_destination;
423             //removes m_destination if it exists, do that here so that it is removed no matter if a transfer could be created or not
424             //as the user decided to throw the file away
425             FileDeleter::deleteFile(m_destination);
426         }
427 
428         //sourceUrl is valid, has been checked before
429         const QUrl sourceUrl = QUrl(ui.urlRequester->text().trimmed());
430         qCDebug(KGET_DEBUG) << "Downloading" << sourceUrl << "to" << m_destination;
431         data << KGet::TransferData(sourceUrl, m_destination, group, true);
432     } else {
433         QList<QUrl> list;
434         for (int i = 0; i != ui.listWidget->count(); ++i) {
435             QListWidgetItem *item = ui.listWidget->item(i);
436 
437             //find selected sources
438             if (item->checkState() == Qt::Checked) {
439                 //both sourceUrl and destUrl are valid, they have been tested in checkInput
440                 const QUrl sourceUrl = QUrl(item->text().trimmed());
441                 const QUrl destUrl = UrlChecker::destUrl(m_destination, sourceUrl);
442                 qCDebug(KGET_DEBUG) << "Downloading" << sourceUrl << "to" << destUrl;
443 
444                 //file exists already, remove it
445                 if (item->background() == m_existingFileBackground) {
446                     qCDebug(KGET_DEBUG) << "Removing existing file:" << destUrl;
447                     //removes destUrl if it exists, do that here so that it is removed no matter if a transfer could be created or not
448                     //as the user decided to throw the file away
449                     FileDeleter::deleteFile(destUrl);
450                 }
451 
452                 data << KGet::TransferData(sourceUrl, destUrl, group, true);
453             }
454         }
455     }
456 
457     if (!data.isEmpty()) {
458         Settings::setLastGroup(ui.groupComboBox->currentText());
459         KGet::createTransfers(data);
460     }
461 }
462 
setInformation(const QString & information)463 void NewTransferDialog::setInformation(const QString &information)
464 {
465     ui.errorWidget->setMessageType(KMessageWidget::Information);
466     ui.errorWidget->setText(information);
467     ui.errorWidget->setVisible(!information.isEmpty());
468 }
469 
setWarning(const QString & warning)470 void NewTransferDialog::setWarning(const QString &warning)
471 {
472     ui.errorWidget->setMessageType(KMessageWidget::Warning);
473     ui.errorWidget->setText(warning);
474     ui.errorWidget->setVisible(!warning.isEmpty());
475 }
476 
477 
478 
479 /**
480  * NOTE some checks in this class might seem redundant, though target is to display as few dialogs, and then preferable
481  * the NewTransferDialog, to the user as possible i.e. if not enough information -- e.g. no destination folder
482  * determinable, ...-- is present for a url or a group of urls they won't be added as transfer,
483  * instead the NewTransferDialog will be shown
484  *
485  * This also tries to add as many transfers as possible with one run, to ensure a high speed
486  */
NewTransferDialogHandler(QObject * parent)487 NewTransferDialogHandler::NewTransferDialogHandler(QObject *parent)
488   : QObject(parent),
489     m_nextJobId(0)
490 {
491 }
492 
~NewTransferDialogHandler()493 NewTransferDialogHandler::~NewTransferDialogHandler()
494 {
495 }
496 
497 
showNewTransferDialog(const QUrl & url)498 void NewTransferDialogHandler::showNewTransferDialog(const QUrl &url)
499 {
500     showNewTransferDialog(url.isEmpty() ? QList<QUrl>() : QList<QUrl>() << url);
501 }
502 
showNewTransferDialog(QList<QUrl> urls)503 void NewTransferDialogHandler::showNewTransferDialog(QList<QUrl> urls)
504 {
505     if (urls.isEmpty()) {
506         newTransferDialogHandler->createDialog(urls, QString());
507         return;
508     }
509 
510     QHash<int, UrlData>::iterator itUrls = newTransferDialogHandler->m_urls.insert(newTransferDialogHandler->m_nextJobId, UrlData());
511     QString folder;
512     QString suggestedFileName;
513 
514     ///Only two urls defined, check if second one is a path or a file name
515     if (urls.count() == 2) {
516         const QUrl lastUrl = urls.last();
517         QDir dir(lastUrl.toLocalFile());
518 
519         //check if last url is a file path, either absolute or relative
520         if (lastUrl.isLocalFile()) {
521             if (QDir::isAbsolutePath(lastUrl.toLocalFile())) {
522                 if (dir.exists()) {
523                     //second url is a folder path
524                     folder = lastUrl.adjusted(QUrl::RemoveFilename).toString();
525                 } else {
526                     //second url is a file path, use this one
527                     folder = lastUrl.adjusted(QUrl::RemoveFilename).toString();
528                     suggestedFileName = lastUrl.fileName();
529                 }
530                 urls.removeLast();
531             } else {
532                 //second url is just a file name
533                 suggestedFileName = lastUrl.fileName();
534                 urls.removeLast();
535             }
536         } else if (!lastUrl.isValid() || (lastUrl.scheme().isEmpty() && lastUrl.adjusted(QUrl::RemoveFilename).isEmpty())) {
537             // Sometimes valid filenames are not recognized by KURL::isLocalFile(), they are marked as invalid then
538             suggestedFileName = lastUrl.url();
539             urls.removeLast();
540         }
541     }
542 
543     ///More than two urls defined, and last is local and will be used as destination directory
544     if (urls.count() > 2 && urls.last().isLocalFile()) {
545 
546     /**
547      * FIXME should the code be uncommented again, though then inputting a wrong destination like
548      * ~/Downloads/folderNotExisting would result in ~/Downloads/ instead of informing the user
549      * and giving them the possibility to improve their mistake
550      */
551 //         if (!QFileInfo(urls.last().toLocalFile()).isDir()) {
552 //             folder = urls.last().directory(QUrl::AppendTrailingSlash);
553 //         } else {
554             folder = urls.last().adjusted(QUrl::RemoveFilename).toString();//checks if that folder is correct happen later
555 //         }
556         urls.removeLast();
557     }
558 
559     //add a folder or suggestedFileName if they are valid
560     if (!folder.isEmpty() && KGet::isValidDestDirectory(folder)) {
561         (*itUrls).folder = folder;
562     }
563     if (!suggestedFileName.isEmpty()) {
564         (*itUrls).suggestedFileName = QUrl(suggestedFileName).toString();//pathOrUrl to get a non percent encoded url
565     }
566 
567     newTransferDialogHandler->m_numJobs[newTransferDialogHandler->m_nextJobId] = urls.count();
568     foreach (const QUrl &url, urls) {
569         //needed to avoid when protocols like the desktop protocol is used, see bko:185283
570         KIO::Job *job = mostLocalUrlJob(url);
571         job->setProperty("jobId", (newTransferDialogHandler->m_nextJobId));
572         connect(job, SIGNAL(result(KJob*)), newTransferDialogHandler, SLOT(slotMostLocalUrlResult(KJob*)));
573         job->start();
574     }
575 
576     ++(newTransferDialogHandler->m_nextJobId);
577 }
578 
slotMostLocalUrlResult(KJob * j)579 void NewTransferDialogHandler::slotMostLocalUrlResult(KJob *j)
580 {
581     auto *job = static_cast<MostLocalUrlJob*>(j);
582     const int jobId = job->property("jobId").toInt();
583 
584     if (job->error()) {
585         qCWarning(KGET_DEBUG) << "An error happened for" << job->url();
586     } else {
587         m_urls[jobId].urls << job->mostLocalUrl();
588     }
589     --m_numJobs[jobId];
590 
591     if (m_numJobs[jobId] <= 0) {
592         handleUrls(jobId);
593     }
594 }
595 
handleUrls(const int jobId)596 void NewTransferDialogHandler::handleUrls(const int jobId)
597 {
598     QHash<int, UrlData>::iterator itUrls = m_urls.find(jobId);
599     if (itUrls == m_urls.end()) {
600         qCWarning(KGET_DEBUG) << "JobId" << jobId << "was not defined, could not handle urls for it.";
601         return;
602     }
603 
604     QList<QUrl> urls = (*itUrls).urls;
605     UrlChecker::removeDuplicates(urls);
606 
607     QString folder = (*itUrls).folder;
608     if (!folder.isEmpty() && (UrlChecker::checkFolder(QUrl::fromLocalFile(folder), true) != UrlChecker::NoError)) {
609         folder.clear();
610     }
611 
612     const QString suggestedFileName = (*itUrls).suggestedFileName;
613     QUrl newDest;
614     const QUrl folderUrl = QUrl::fromLocalFile(folder);
615 
616     //check if the sources are correct
617     UrlChecker check(UrlChecker::Source);
618     check.addUrls(urls);
619     check.displayErrorMessages();
620     check.existingTransfers();
621     urls = check.correctUrls();
622 
623     QList<KGet::TransferData> data;
624 
625     ///Just one file to download, with a specified suggestedFileName, handle if possible
626     if (!suggestedFileName.isEmpty() && (urls.count() == 1)) {
627         const QUrl sourceUrl = urls.first();
628         const QList<TransferGroupHandler*> groups = KGet::groupsFromExceptions(sourceUrl);
629         const QString groupName = (groups.isEmpty() ? QString() : groups.first()->name());
630         QString defaultFolder;
631         if (groups.isEmpty()) {
632             defaultFolder = (Settings::askForDestination() ? QString() : QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
633         } else {
634             defaultFolder = groups.first()->defaultFolder();
635         }
636 
637         if (!folder.isEmpty()) {
638             const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(folder), sourceUrl, suggestedFileName);
639             newDest = check.checkExistingFile(sourceUrl, destUrl);
640             if (!newDest.isEmpty()) {
641                 data << KGet::TransferData(sourceUrl, newDest, groupName);
642             }
643             urls.removeFirst();
644         } else if (((!groups.isEmpty() && !Settings::directoriesAsSuggestion()) || !Settings::askForDestination()) &&
645                    (UrlChecker::checkFolder(QUrl::fromLocalFile(defaultFolder)) == UrlChecker::NoError)) {
646             const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(defaultFolder), sourceUrl, suggestedFileName);
647             newDest = check.checkExistingFile(sourceUrl, destUrl);
648             if (!newDest.isEmpty()) {
649                 data << KGet::TransferData(sourceUrl, newDest, groupName);
650             }
651             urls.removeFirst();
652         }
653     }
654 
655     ///A valid folder has been defined, use that for downloading
656     if (!folder.isEmpty()) {
657         //find the associated groups first, we just need the first matching group though
658         const QList<TransferGroupHandler*> groups = KGet::allTransferGroups();
659         foreach (TransferGroupHandler *group, groups) {
660             if (urls.isEmpty()) {
661                 break;
662             }
663 
664             const QString groupName = group->name();
665             const QStringList patterns = group->regExp().pattern().split(',');
666 
667             //find all urls where a group can be identified
668             QList<QUrl>::iterator it = urls.begin();
669             while (it != urls.end()) {
670                 const QUrl sourceUrl = *it;
671                 if (KGet::matchesExceptions(sourceUrl, patterns)) {
672                     const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
673                     newDest = check.checkExistingFile(sourceUrl, destUrl);
674                     if (!newDest.isEmpty()) {
675                         data << KGet::TransferData(sourceUrl, newDest, groupName);
676                     }
677                     it = urls.erase(it);
678                 } else {
679                     ++it;
680                 }
681             }
682         }
683 
684         //there are still some unhandled urls, i.e. for those no group could be found, add them with an empty group
685         foreach (const QUrl &sourceUrl, urls) {
686             const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
687             newDest = check.checkExistingFile(sourceUrl, destUrl);
688             if (!newDest.isEmpty()) {
689                 data << KGet::TransferData(sourceUrl, newDest);
690             }
691         }
692 
693         //all urls have been handled
694         urls.clear();
695     }
696 
697     ///Now handle default folders/groups
698     qCDebug(KGET_DEBUG) << "DIRECTORIES AS SUGGESTION" << Settings::directoriesAsSuggestion();
699     if (!Settings::directoriesAsSuggestion() && !urls.isEmpty()) {
700         qCDebug(KGET_DEBUG) << "No, Directories not as suggestion";
701 
702         //find the associated groups first, we just need the first matching group though
703         const QList<TransferGroupHandler*> groups = KGet::allTransferGroups();
704         foreach (TransferGroupHandler *group, groups) {
705             if (urls.isEmpty()) {
706                 break;
707             }
708 
709             const QUrl folderUrl = QUrl::fromLocalFile(group->defaultFolder());
710             if (UrlChecker::checkFolder(folderUrl) != UrlChecker::NoError) {
711                 continue;
712             }
713 
714             const QString groupName = group->name();
715             const QStringList patterns = group->regExp().pattern().split(',');
716 
717             QList<QUrl>::iterator it = urls.begin();
718             while (it != urls.end()) {
719                 const QUrl sourceUrl = *it;
720                 if (KGet::matchesExceptions(sourceUrl, patterns)) {
721                     const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
722                     newDest = check.checkExistingFile(sourceUrl, destUrl);
723                     if (!newDest.isEmpty()) {
724                         data << KGet::TransferData(sourceUrl, newDest, groupName);
725                     }
726 
727                     it = urls.erase(it);
728                 } else {
729                     ++it;
730                 }
731             }
732         }
733     }
734 
735     ///Download the rest of the urls to QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) if the user is not aksed for a destination
736     if (!Settings::askForDestination()) {
737         //the download path will be always used
738         const QString dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
739         if (!dir.isEmpty()) {
740             QList<QUrl>::iterator it = urls.begin();
741             while (it != urls.end()) {
742                 const QUrl sourceUrl = *it;
743                 const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(dir), sourceUrl);
744                 newDest = check.checkExistingFile(sourceUrl, destUrl);
745                 if (!newDest.isEmpty()) {
746                     data << KGet::TransferData(sourceUrl, newDest);
747                 }
748 
749                 it = urls.erase(it);
750             }
751         }
752     }
753 
754     ///Now create transfers for the urls that provided enough data
755     if (!data.isEmpty()) {
756         KGet::createTransfers(data);
757     }
758 
759     ///Handle custom newtransferdialogs...
760     if ((!m_dialog || m_dialog->isEmpty()) && urls.count() == 1) {//FIXME why the m_dialog check? whenever a dialog has been created this would not be shown?
761         QUrl url = urls.first();
762         QPointer<QDialog> dialog;
763         foreach (TransferFactory * factory, KGet::factories()) {
764             const QList<TransferGroupHandler*> groups =  KGet::groupsFromExceptions(url);
765             dialog = factory->createNewTransferDialog(url, suggestedFileName, !groups.isEmpty() ? groups.first() : nullptr);
766             if (dialog) {
767                 KWindowInfo info(KGet::m_mainWindow->winId(), NET::WMDesktop);
768                 KWindowSystem::setCurrentDesktop(info.desktop());
769                 KWindowSystem::forceActiveWindow(KGet::m_mainWindow->winId());
770 
771                 dialog->exec();
772                 delete dialog;
773             }
774         }
775     }
776 
777     m_numJobs.remove(jobId);
778     m_urls.erase(itUrls);
779 
780     ///Display default NewTransferDialog
781     if (!urls.isEmpty()) {
782         createDialog(urls, suggestedFileName);
783     }
784 }
785 
createDialog(const QList<QUrl> & urls,const QString & suggestedFileName)786 void NewTransferDialogHandler::createDialog(const QList<QUrl> &urls, const QString &suggestedFileName)
787 {
788     if (!m_dialog) {
789         m_dialog = new NewTransferDialog(KGet::m_mainWindow);
790     }
791 
792     m_dialog->m_window = KGet::m_mainWindow;
793     m_dialog->showDialog(urls, suggestedFileName);
794 }
795 
796 
797