File indexing completed on 2023-10-01 08:39:45

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2005 Dario Massarin <nekkar@libero.it>
0004    Copyright (C) 2007 by Javier Goday <jgoday@gmail.com>
0005    Copyright (C) 2008 - 2009 by Lukas Appelhans <l.appelhans@gmx.de>
0006    Copyright (C) 2010 by Matthias Fuchs <mat69@gmx.net>
0007 
0008    This program is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU General Public
0010    License as published by the Free Software Foundation; either
0011    version 2 of the License, or (at your option) any later version.
0012 */
0013 
0014 #include "newtransferdialog.h"
0015 
0016 #include "core/filedeleter.h"
0017 #include "core/kget.h"
0018 #include "core/mostlocalurl.h"
0019 #include "core/plugin/transferfactory.h"
0020 #include "core/transfergrouphandler.h"
0021 #include "core/transfertreemodel.h"
0022 #include "core/urlchecker.h"
0023 #include "mainwindow.h"
0024 #include "settings.h"
0025 
0026 #include "kget_debug.h"
0027 #include <QDebug>
0028 
0029 #include <QApplication>
0030 #include <QClipboard>
0031 #include <QDir>
0032 #include <QFileInfo>
0033 #include <QTimer>
0034 
0035 #include <KColorScheme>
0036 #include <KLocalizedString>
0037 #include <KWindowSystem>
0038 #include <LineEditUrlDropEventFilter>
0039 #include <QListWidgetItem>
0040 #include <QStandardPaths>
0041 
0042 Q_GLOBAL_STATIC(NewTransferDialogHandler, newTransferDialogHandler)
0043 
0044 NewTransferDialog::NewTransferDialog(QWidget *parent)
0045     : QDialog(parent)
0046     , m_window(nullptr)
0047     , m_existingTransfer(nullptr)
0048     , m_multiple(false)
0049     , m_overWriteSingle(false)
0050 {
0051     setModal(true);
0052     setWindowTitle(i18n("New Download"));
0053 
0054     ui.setupUi(this);
0055 
0056     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
0057 
0058     // timer to avoid constant checking of the input
0059     m_timer = new QTimer(this);
0060     m_timer->setInterval(350);
0061     m_timer->setSingleShot(true);
0062     connect(m_timer, &QTimer::timeout, this, &NewTransferDialog::checkInput);
0063 
0064     const KColorScheme scheme = KColorScheme(QPalette::Active, KColorScheme::View);
0065     m_existingFileBackground = scheme.background(KColorScheme::NeutralBackground);
0066     m_normalBackground = scheme.background();
0067 
0068     // properties of the m_destRequester combobox
0069     auto *dropUrlEventFilter = new LineEditUrlDropEventFilter(this);
0070     dropUrlEventFilter->installEventFilter(ui.destRequester->comboBox());
0071     ui.destRequester->comboBox()->setDuplicatesEnabled(false);
0072     ui.destRequester->comboBox()->setEditable(true);
0073     ui.destRequester->setAcceptMode(QFileDialog::AcceptSave);
0074 
0075     ui.errorWidget->setCloseButtonVisible(false);
0076 
0077     connect(ui.groupComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setDefaultDestination()));
0078 
0079     connect(ui.urlRequester, &QLineEdit::textChanged, this, &NewTransferDialog::setDefaultDestination);
0080     connect(ui.destRequester, &KUrlRequester::textChanged, this, &NewTransferDialog::inputTimer);
0081     connect(ui.urlRequester, &QLineEdit::textChanged, this, &NewTransferDialog::inputTimer);
0082     connect(ui.listWidget, &QListWidget::itemChanged, this, &NewTransferDialog::inputTimer);
0083     connect(this, &QDialog::finished, this, &NewTransferDialog::slotFinished);
0084     connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0085     connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0086 }
0087 
0088 NewTransferDialog::~NewTransferDialog()
0089 {
0090 }
0091 
0092 void NewTransferDialog::setMultiple(bool useMultiple)
0093 {
0094     m_multiple = useMultiple;
0095 
0096     const Qt::Alignment alignment = Qt::AlignLeft | (m_multiple ? Qt::AlignTop : Qt::AlignVCenter);
0097     ui.urlLabel->setAlignment(alignment);
0098     ui.urlRequester->setVisible(!m_multiple);
0099     ui.listWidget->setVisible(m_multiple);
0100     ui.destRequester->setMode(m_multiple ? KFile::Directory : KFile::File);
0101 }
0102 
0103 void NewTransferDialog::clear()
0104 {
0105     ui.urlRequester->clear();
0106     ui.urlRequester->setFocus();
0107     ui.listWidget->clear();
0108     ui.destRequester->comboBox()->clear();
0109     ui.destRequester->clear();
0110     m_destination.clear();
0111     m_sources.clear();
0112     m_existingTransfer = nullptr;
0113     m_overWriteSingle = false;
0114 
0115     // add all destinations
0116     QStringList list;
0117     QString downloadPath = KGet::generalDestDir();
0118     if (!downloadPath.isEmpty()) {
0119         if (!downloadPath.endsWith('/')) {
0120             downloadPath.append('/');
0121         }
0122         list << downloadPath;
0123     }
0124     foreach (TransferGroupHandler *handler, KGet::allTransferGroups()) {
0125         const QString folder = handler->defaultFolder();
0126         if (!folder.isEmpty()) {
0127             list << (folder.endsWith('/') ? folder : folder + '/');
0128         }
0129     }
0130 
0131     list.removeDuplicates();
0132     ui.destRequester->comboBox()->insertItems(0, list);
0133 
0134     // add all transfer groups
0135     ui.groupComboBox->clear();
0136     foreach (TransferGroupHandler *group, KGet::allTransferGroups()) {
0137         ui.groupComboBox->addItem(QIcon::fromTheme(group->iconName()), group->name());
0138     }
0139     ui.groupComboBox->setCurrentItem(Settings::lastGroup());
0140     if (ui.groupComboBox->currentIndex() == -1) {
0141         ui.groupComboBox->setCurrentIndex(0);
0142     }
0143 
0144     const bool multipleGroups = KGet::transferGroupNames().count();
0145     ui.groupComboBox->setVisible(multipleGroups);
0146     ui.groupLabel->setVisible(multipleGroups);
0147 }
0148 
0149 void NewTransferDialog::setSource(const QList<QUrl> &sources)
0150 {
0151     if (sources.isEmpty()) {
0152         return;
0153     }
0154 
0155     if (sources.count() == 1) {
0156         QUrl m_srcUrl = sources.first();
0157         ui.urlRequester->clear();
0158         if (m_srcUrl.isEmpty()) {
0159             m_srcUrl = QUrl(QApplication::clipboard()->text(QClipboard::Clipboard).trimmed());
0160         }
0161 
0162         if (UrlChecker::checkSource(m_srcUrl) == UrlChecker::NoError) {
0163             ui.urlRequester->insert(m_srcUrl.toString());
0164         }
0165     } else {
0166         foreach (const QUrl &sourceUrl, sources) {
0167             if (sourceUrl.url()
0168                 != QUrl(sourceUrl.url())
0169                        .fileName()) { // TODO simplify, whatfor is this check anyway, shouldn't the sources be checked already and if not add this to UrlChecker
0170                 qCDebug(KGET_DEBUG) << "Insert" << sourceUrl;
0171                 auto *newItem = new QListWidgetItem(sourceUrl.toString(), ui.listWidget);
0172                 newItem->setCheckState(Qt::Checked);
0173             }
0174         }
0175     }
0176 
0177     const QList<TransferGroupHandler *> groups = KGet::groupsFromExceptions(sources.first());
0178     if (!groups.isEmpty()) {
0179         ui.groupComboBox->setCurrentIndex(ui.groupComboBox->findText(groups.first()->name()));
0180     }
0181 }
0182 
0183 void NewTransferDialog::setDestinationFileName(const QString &filename)
0184 {
0185     ui.destRequester->setUrl(QUrl(ui.destRequester->url().adjusted(QUrl::RemoveFilename).toString() + filename));
0186 }
0187 
0188 void NewTransferDialog::setDestination()
0189 {
0190     // sets destRequester to either display the defaultFolder of group or the generalDestDir
0191     QString group = ui.groupComboBox->currentText();
0192     TransferGroupHandler *current = nullptr;
0193     foreach (TransferGroupHandler *handler, KGet::allTransferGroups()) {
0194         if (handler->name() == group) {
0195             current = handler;
0196             break;
0197         }
0198     }
0199 
0200     if (current) {
0201         QString groupFolder = current->defaultFolder();
0202         if (groupFolder.isEmpty()) {
0203             groupFolder = KGet::generalDestDir();
0204         }
0205         if (!groupFolder.endsWith('/')) {
0206             groupFolder.append('/');
0207         }
0208         ui.destRequester->comboBox()->setCurrentItem(groupFolder, true);
0209     }
0210 }
0211 
0212 void NewTransferDialog::showDialog(QList<QUrl> list, const QString &suggestedFileName)
0213 {
0214     // TODO handle the case where for some there are suggested file names --> own file name column in multiple setting
0215     // the dialog is already in use, adapt it
0216     if (isVisible()) {
0217         list << m_sources;
0218     }
0219     clear(); // Let's clear the old stuff
0220     m_sources << list;
0221     UrlChecker::removeDuplicates(m_sources);
0222     const int size = m_sources.size();
0223     qCDebug(KGET_DEBUG) << "SET SOURCES " << m_sources << " MULTIPLE " << (size > 1);
0224     setMultiple(size > 1);
0225 
0226     if (size) {
0227         if (size == 1 && !suggestedFileName.isEmpty()) {
0228             setDestinationFileName(suggestedFileName);
0229         }
0230 
0231         setSource(m_sources);
0232     }
0233 
0234     prepareDialog();
0235 }
0236 
0237 void NewTransferDialog::setDefaultDestination()
0238 {
0239     // NOTE if the user enters a file name manually and the changes the group the manually entered file name will be overwritten
0240     setDestination();
0241 
0242     // set a file name
0243     if (!m_multiple) {
0244         const QUrl url(ui.urlRequester->text().trimmed());
0245         if ((UrlChecker::checkSource(url) == UrlChecker::NoError) && QFileInfo(ui.destRequester->url().toLocalFile()).isDir()) {
0246             setDestinationFileName(url.fileName());
0247         }
0248     }
0249 }
0250 
0251 void NewTransferDialog::prepareDialog()
0252 {
0253     if (m_window) {
0254         KWindowInfo info(m_window->winId(), NET::WMDesktop);
0255         KWindowSystem::setCurrentDesktop(info.desktop());
0256         KWindowSystem::forceActiveWindow(m_window->winId());
0257     }
0258 
0259     qCDebug(KGET_DEBUG) << "Show the dialog!";
0260     show();
0261 }
0262 
0263 bool NewTransferDialog::isEmpty()
0264 {
0265     return (m_multiple ? !ui.listWidget->count() : ui.urlRequester->text().trimmed().isEmpty());
0266 }
0267 
0268 void NewTransferDialog::inputTimer()
0269 {
0270     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
0271     m_timer->start();
0272 }
0273 
0274 void NewTransferDialog::checkInput()
0275 {
0276     qDebug() << "Check input";
0277     QUrl source = QUrl(ui.urlRequester->text().trimmed());
0278     const QUrl dest = ui.destRequester->url();
0279 
0280     // check the destination folder
0281     UrlChecker::UrlError error = UrlChecker::checkFolder(dest);
0282     const bool folderValid = (error == UrlChecker::NoError);
0283     bool destinationValid = false;
0284     QString infoText;
0285     QString warningText;
0286     if (!folderValid) {
0287         if (m_multiple) {
0288             infoText = UrlChecker::message(QUrl(), UrlChecker::Folder, error);
0289         } else {
0290             // might be a destination instead of a folder
0291             destinationValid = (UrlChecker::checkDestination(dest) == UrlChecker::NoError);
0292         }
0293     } else {
0294         m_destination = dest;
0295     }
0296 
0297     // check the source
0298     if (!m_multiple) {
0299         source = mostLocalUrl(source);
0300     }
0301     error = UrlChecker::checkSource(source);
0302     const bool sourceValid = (error == UrlChecker::NoError);
0303     if (!m_multiple && !sourceValid) {
0304         infoText = UrlChecker::message(QUrl(), UrlChecker::Source, error);
0305     }
0306 
0307     // check if any sources are checked and for existing transfers or destinations
0308     bool filesChecked = false;
0309     if (m_multiple && folderValid) {
0310         QListWidget *list = ui.listWidget;
0311 
0312         // check if some sources have been checked
0313         for (int i = 0; i < list->count(); ++i) {
0314             QListWidgetItem *item = list->item(i);
0315             if (item->checkState() == Qt::Checked) {
0316                 filesChecked = true;
0317                 break;
0318             }
0319         }
0320         if (!filesChecked) {
0321             infoText = i18n("Select at least one source url.");
0322         }
0323 
0324         // check if there are existing files
0325         if (filesChecked) {
0326             bool existingFile = false;
0327             for (int i = 0; i < list->count(); ++i) {
0328                 QListWidgetItem *item = list->item(i);
0329                 const QUrl source = QUrl(item->text());
0330                 const QUrl destUrl = UrlChecker::destUrl(dest, source);
0331                 if (UrlChecker::wouldOverwrite(source, destUrl)) {
0332                     item->setBackground(m_existingFileBackground);
0333                     existingFile = true;
0334                 } else {
0335                     item->setBackground(m_normalBackground);
0336                 }
0337             }
0338             if (existingFile) {
0339                 warningText = i18n("Files that exist already in the current folder have been marked."); // TODO better message
0340             }
0341         }
0342     }
0343 
0344     // single file
0345     UrlChecker::UrlWarning warning = UrlChecker::NoWarning;
0346     if (!m_multiple && sourceValid && (folderValid || destinationValid)) {
0347         m_destination = UrlChecker::destUrl(dest, source);
0348         // show only one message for existing transfers
0349         m_existingTransfer = UrlChecker::existingTransfer(source, UrlChecker::Source, &warning);
0350         if (m_existingTransfer) {
0351             warningText = UrlChecker::message(QUrl(), UrlChecker::Source, warning);
0352         } else {
0353             m_existingTransfer = UrlChecker::existingTransfer(m_destination, UrlChecker::Destination, &warning);
0354             if (m_existingTransfer) {
0355                 warningText = UrlChecker::message(QUrl(), UrlChecker::Destination, warning);
0356             }
0357         }
0358 
0359         if (UrlChecker::wouldOverwrite(QUrl(ui.urlRequester->text().trimmed()), m_destination)) {
0360             m_overWriteSingle = true;
0361             if (!warningText.isEmpty()) {
0362                 warningText += '\n';
0363             }
0364             warningText += UrlChecker::message(QUrl(), UrlChecker::Destination, UrlChecker::ExistingFile);
0365         } else {
0366             m_overWriteSingle = false;
0367         }
0368     }
0369 
0370     if (!infoText.isEmpty()) {
0371         setInformation(infoText);
0372     } else if (!warningText.isEmpty()) {
0373         setWarning(warningText);
0374     } else {
0375         ui.errorWidget->hide();
0376     }
0377 
0378     // activate the ok button
0379     if (m_multiple) {
0380         ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(folderValid && filesChecked);
0381     } else {
0382         ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled((folderValid || destinationValid) && sourceValid);
0383     }
0384 
0385     qCDebug(KGET_DEBUG) << source << source.fileName() << dest << dest.fileName() << "Folder valid:" << folderValid << "Destination valid:" << destinationValid
0386                         << "Source valid:" << sourceValid;
0387 }
0388 
0389 void NewTransferDialog::slotFinished(int resultCode)
0390 {
0391     if (resultCode == QDialog::Accepted) {
0392         dialogAccepted();
0393     }
0394     clear();
0395 }
0396 
0397 void NewTransferDialog::dialogAccepted()
0398 {
0399     qCDebug(KGET_DEBUG) << "Dialog accepted.";
0400 
0401     // an existing transfer has been specified and since ok was clicked, it was chosen to be overwritten
0402     if (m_existingTransfer) {
0403         qCDebug(KGET_DEBUG) << "Removing existing transfer:" << m_existingTransfer;
0404         KGet::delTransfer(m_existingTransfer);
0405     }
0406 
0407     // set the last directory
0408     QString dir = m_destination.toLocalFile();
0409     if (!QFileInfo(dir).isDir()) {
0410         dir = m_destination.adjusted(QUrl::RemoveFilename).toLocalFile();
0411     }
0412     Settings::setLastDirectory(dir);
0413     Settings::self()->save();
0414 
0415     const QString group = ui.groupComboBox->currentText();
0416 
0417     /// add data to create transfers
0418     QList<KGet::TransferData> data;
0419     if (!m_multiple) {
0420         if (m_overWriteSingle) {
0421             qCDebug(KGET_DEBUG) << "Removing existing file:" << m_destination;
0422             // removes m_destination if it exists, do that here so that it is removed no matter if a transfer could be created or not
0423             // as the user decided to throw the file away
0424             FileDeleter::deleteFile(m_destination);
0425         }
0426 
0427         // sourceUrl is valid, has been checked before
0428         const QUrl sourceUrl = QUrl(ui.urlRequester->text().trimmed());
0429         qCDebug(KGET_DEBUG) << "Downloading" << sourceUrl << "to" << m_destination;
0430         data << KGet::TransferData(sourceUrl, m_destination, group, true);
0431     } else {
0432         QList<QUrl> list;
0433         for (int i = 0; i != ui.listWidget->count(); ++i) {
0434             QListWidgetItem *item = ui.listWidget->item(i);
0435 
0436             // find selected sources
0437             if (item->checkState() == Qt::Checked) {
0438                 // both sourceUrl and destUrl are valid, they have been tested in checkInput
0439                 const QUrl sourceUrl = QUrl(item->text().trimmed());
0440                 const QUrl destUrl = UrlChecker::destUrl(m_destination, sourceUrl);
0441                 qCDebug(KGET_DEBUG) << "Downloading" << sourceUrl << "to" << destUrl;
0442 
0443                 // file exists already, remove it
0444                 if (item->background() == m_existingFileBackground) {
0445                     qCDebug(KGET_DEBUG) << "Removing existing file:" << destUrl;
0446                     // removes destUrl if it exists, do that here so that it is removed no matter if a transfer could be created or not
0447                     // as the user decided to throw the file away
0448                     FileDeleter::deleteFile(destUrl);
0449                 }
0450 
0451                 data << KGet::TransferData(sourceUrl, destUrl, group, true);
0452             }
0453         }
0454     }
0455 
0456     if (!data.isEmpty()) {
0457         Settings::setLastGroup(ui.groupComboBox->currentText());
0458         KGet::createTransfers(data);
0459     }
0460 }
0461 
0462 void NewTransferDialog::setInformation(const QString &information)
0463 {
0464     ui.errorWidget->setMessageType(KMessageWidget::Information);
0465     ui.errorWidget->setText(information);
0466     ui.errorWidget->setVisible(!information.isEmpty());
0467 }
0468 
0469 void NewTransferDialog::setWarning(const QString &warning)
0470 {
0471     ui.errorWidget->setMessageType(KMessageWidget::Warning);
0472     ui.errorWidget->setText(warning);
0473     ui.errorWidget->setVisible(!warning.isEmpty());
0474 }
0475 
0476 /**
0477  * NOTE some checks in this class might seem redundant, though target is to display as few dialogs, and then preferable
0478  * the NewTransferDialog, to the user as possible i.e. if not enough information -- e.g. no destination folder
0479  * determinable, ...-- is present for a url or a group of urls they won't be added as transfer,
0480  * instead the NewTransferDialog will be shown
0481  *
0482  * This also tries to add as many transfers as possible with one run, to ensure a high speed
0483  */
0484 NewTransferDialogHandler::NewTransferDialogHandler(QObject *parent)
0485     : QObject(parent)
0486     , m_nextJobId(0)
0487 {
0488 }
0489 
0490 NewTransferDialogHandler::~NewTransferDialogHandler()
0491 {
0492 }
0493 
0494 void NewTransferDialogHandler::showNewTransferDialog(const QUrl &url)
0495 {
0496     showNewTransferDialog(url.isEmpty() ? QList<QUrl>() : QList<QUrl>() << url);
0497 }
0498 
0499 void NewTransferDialogHandler::showNewTransferDialog(QList<QUrl> urls)
0500 {
0501     if (urls.isEmpty()) {
0502         newTransferDialogHandler->createDialog(urls, QString());
0503         return;
0504     }
0505 
0506     QHash<int, UrlData>::iterator itUrls = newTransferDialogHandler->m_urls.insert(newTransferDialogHandler->m_nextJobId, UrlData());
0507     QString folder;
0508     QString suggestedFileName;
0509 
0510     /// Only two urls defined, check if second one is a path or a file name
0511     if (urls.count() == 2) {
0512         const QUrl lastUrl = urls.last();
0513         QDir dir(lastUrl.toLocalFile());
0514 
0515         // check if last url is a file path, either absolute or relative
0516         if (lastUrl.isLocalFile()) {
0517             if (QDir::isAbsolutePath(lastUrl.toLocalFile())) {
0518                 if (dir.exists()) {
0519                     // second url is a folder path
0520                     folder = lastUrl.adjusted(QUrl::RemoveFilename).toString();
0521                 } else {
0522                     // second url is a file path, use this one
0523                     folder = lastUrl.adjusted(QUrl::RemoveFilename).toString();
0524                     suggestedFileName = lastUrl.fileName();
0525                 }
0526                 urls.removeLast();
0527             } else {
0528                 // second url is just a file name
0529                 suggestedFileName = lastUrl.fileName();
0530                 urls.removeLast();
0531             }
0532         } else if (!lastUrl.isValid() || (lastUrl.scheme().isEmpty() && lastUrl.adjusted(QUrl::RemoveFilename).isEmpty())) {
0533             // Sometimes valid filenames are not recognized by KURL::isLocalFile(), they are marked as invalid then
0534             suggestedFileName = lastUrl.url();
0535             urls.removeLast();
0536         }
0537     }
0538 
0539     /// More than two urls defined, and last is local and will be used as destination directory
0540     if (urls.count() > 2 && urls.last().isLocalFile()) {
0541         /**
0542          * FIXME should the code be uncommented again, though then inputting a wrong destination like
0543          * ~/Downloads/folderNotExisting would result in ~/Downloads/ instead of informing the user
0544          * and giving them the possibility to improve their mistake
0545          */
0546         //         if (!QFileInfo(urls.last().toLocalFile()).isDir()) {
0547         //             folder = urls.last().directory(QUrl::AppendTrailingSlash);
0548         //         } else {
0549         folder = urls.last().adjusted(QUrl::RemoveFilename).toString(); // checks if that folder is correct happen later
0550         //         }
0551         urls.removeLast();
0552     }
0553 
0554     // add a folder or suggestedFileName if they are valid
0555     if (!folder.isEmpty() && KGet::isValidDestDirectory(folder)) {
0556         (*itUrls).folder = folder;
0557     }
0558     if (!suggestedFileName.isEmpty()) {
0559         (*itUrls).suggestedFileName = QUrl(suggestedFileName).toString(); // pathOrUrl to get a non percent encoded url
0560     }
0561 
0562     newTransferDialogHandler->m_numJobs[newTransferDialogHandler->m_nextJobId] = urls.count();
0563     foreach (const QUrl &url, urls) {
0564         // needed to avoid when protocols like the desktop protocol is used, see bko:185283
0565         KIO::Job *job = mostLocalUrlJob(url);
0566         job->setProperty("jobId", (newTransferDialogHandler->m_nextJobId));
0567         connect(job, SIGNAL(result(KJob *)), newTransferDialogHandler, SLOT(slotMostLocalUrlResult(KJob *)));
0568         job->start();
0569     }
0570 
0571     ++(newTransferDialogHandler->m_nextJobId);
0572 }
0573 
0574 void NewTransferDialogHandler::slotMostLocalUrlResult(KJob *j)
0575 {
0576     auto *job = static_cast<MostLocalUrlJob *>(j);
0577     const int jobId = job->property("jobId").toInt();
0578 
0579     if (job->error()) {
0580         qCWarning(KGET_DEBUG) << "An error happened for" << job->url();
0581     } else {
0582         m_urls[jobId].urls << job->mostLocalUrl();
0583     }
0584     --m_numJobs[jobId];
0585 
0586     if (m_numJobs[jobId] <= 0) {
0587         handleUrls(jobId);
0588     }
0589 }
0590 
0591 void NewTransferDialogHandler::handleUrls(const int jobId)
0592 {
0593     QHash<int, UrlData>::iterator itUrls = m_urls.find(jobId);
0594     if (itUrls == m_urls.end()) {
0595         qCWarning(KGET_DEBUG) << "JobId" << jobId << "was not defined, could not handle urls for it.";
0596         return;
0597     }
0598 
0599     QList<QUrl> urls = (*itUrls).urls;
0600     UrlChecker::removeDuplicates(urls);
0601 
0602     QString folder = (*itUrls).folder;
0603     if (!folder.isEmpty() && (UrlChecker::checkFolder(QUrl::fromLocalFile(folder), true) != UrlChecker::NoError)) {
0604         folder.clear();
0605     }
0606 
0607     const QString suggestedFileName = (*itUrls).suggestedFileName;
0608     QUrl newDest;
0609     const QUrl folderUrl = QUrl::fromLocalFile(folder);
0610 
0611     // check if the sources are correct
0612     UrlChecker check(UrlChecker::Source);
0613     check.addUrls(urls);
0614     check.displayErrorMessages();
0615     check.existingTransfers();
0616     urls = check.correctUrls();
0617 
0618     QList<KGet::TransferData> data;
0619 
0620     /// Just one file to download, with a specified suggestedFileName, handle if possible
0621     if (!suggestedFileName.isEmpty() && (urls.count() == 1)) {
0622         const QUrl sourceUrl = urls.first();
0623         const QList<TransferGroupHandler *> groups = KGet::groupsFromExceptions(sourceUrl);
0624         const QString groupName = (groups.isEmpty() ? QString() : groups.first()->name());
0625         QString defaultFolder;
0626         if (groups.isEmpty()) {
0627             defaultFolder = (Settings::askForDestination() ? QString() : QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
0628         } else {
0629             defaultFolder = groups.first()->defaultFolder();
0630         }
0631 
0632         if (!folder.isEmpty()) {
0633             const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(folder), sourceUrl, suggestedFileName);
0634             newDest = check.checkExistingFile(sourceUrl, destUrl);
0635             if (!newDest.isEmpty()) {
0636                 data << KGet::TransferData(sourceUrl, newDest, groupName);
0637             }
0638             urls.removeFirst();
0639         } else if (((!groups.isEmpty() && !Settings::directoriesAsSuggestion()) || !Settings::askForDestination())
0640                    && (UrlChecker::checkFolder(QUrl::fromLocalFile(defaultFolder)) == UrlChecker::NoError)) {
0641             const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(defaultFolder), sourceUrl, suggestedFileName);
0642             newDest = check.checkExistingFile(sourceUrl, destUrl);
0643             if (!newDest.isEmpty()) {
0644                 data << KGet::TransferData(sourceUrl, newDest, groupName);
0645             }
0646             urls.removeFirst();
0647         }
0648     }
0649 
0650     /// A valid folder has been defined, use that for downloading
0651     if (!folder.isEmpty()) {
0652         // find the associated groups first, we just need the first matching group though
0653         const QList<TransferGroupHandler *> groups = KGet::allTransferGroups();
0654         foreach (TransferGroupHandler *group, groups) {
0655             if (urls.isEmpty()) {
0656                 break;
0657             }
0658 
0659             const QString groupName = group->name();
0660             const QStringList patterns = group->regExp().pattern().split(',');
0661 
0662             // find all urls where a group can be identified
0663             QList<QUrl>::iterator it = urls.begin();
0664             while (it != urls.end()) {
0665                 const QUrl sourceUrl = *it;
0666                 if (KGet::matchesExceptions(sourceUrl, patterns)) {
0667                     const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
0668                     newDest = check.checkExistingFile(sourceUrl, destUrl);
0669                     if (!newDest.isEmpty()) {
0670                         data << KGet::TransferData(sourceUrl, newDest, groupName);
0671                     }
0672                     it = urls.erase(it);
0673                 } else {
0674                     ++it;
0675                 }
0676             }
0677         }
0678 
0679         // there are still some unhandled urls, i.e. for those no group could be found, add them with an empty group
0680         foreach (const QUrl &sourceUrl, urls) {
0681             const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
0682             newDest = check.checkExistingFile(sourceUrl, destUrl);
0683             if (!newDest.isEmpty()) {
0684                 data << KGet::TransferData(sourceUrl, newDest);
0685             }
0686         }
0687 
0688         // all urls have been handled
0689         urls.clear();
0690     }
0691 
0692     /// Now handle default folders/groups
0693     qCDebug(KGET_DEBUG) << "DIRECTORIES AS SUGGESTION" << Settings::directoriesAsSuggestion();
0694     if (!Settings::directoriesAsSuggestion() && !urls.isEmpty()) {
0695         qCDebug(KGET_DEBUG) << "No, Directories not as suggestion";
0696 
0697         // find the associated groups first, we just need the first matching group though
0698         const QList<TransferGroupHandler *> groups = KGet::allTransferGroups();
0699         foreach (TransferGroupHandler *group, groups) {
0700             if (urls.isEmpty()) {
0701                 break;
0702             }
0703 
0704             const QUrl folderUrl = QUrl::fromLocalFile(group->defaultFolder());
0705             if (UrlChecker::checkFolder(folderUrl) != UrlChecker::NoError) {
0706                 continue;
0707             }
0708 
0709             const QString groupName = group->name();
0710             const QStringList patterns = group->regExp().pattern().split(',');
0711 
0712             QList<QUrl>::iterator it = urls.begin();
0713             while (it != urls.end()) {
0714                 const QUrl sourceUrl = *it;
0715                 if (KGet::matchesExceptions(sourceUrl, patterns)) {
0716                     const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
0717                     newDest = check.checkExistingFile(sourceUrl, destUrl);
0718                     if (!newDest.isEmpty()) {
0719                         data << KGet::TransferData(sourceUrl, newDest, groupName);
0720                     }
0721 
0722                     it = urls.erase(it);
0723                 } else {
0724                     ++it;
0725                 }
0726             }
0727         }
0728     }
0729 
0730     /// Download the rest of the urls to QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) if the user is not aksed for a destination
0731     if (!Settings::askForDestination()) {
0732         // the download path will be always used
0733         const QString dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
0734         if (!dir.isEmpty()) {
0735             QList<QUrl>::iterator it = urls.begin();
0736             while (it != urls.end()) {
0737                 const QUrl sourceUrl = *it;
0738                 const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(dir), sourceUrl);
0739                 newDest = check.checkExistingFile(sourceUrl, destUrl);
0740                 if (!newDest.isEmpty()) {
0741                     data << KGet::TransferData(sourceUrl, newDest);
0742                 }
0743 
0744                 it = urls.erase(it);
0745             }
0746         }
0747     }
0748 
0749     /// Now create transfers for the urls that provided enough data
0750     if (!data.isEmpty()) {
0751         KGet::createTransfers(data);
0752     }
0753 
0754     /// Handle custom newtransferdialogs...
0755     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?
0756         QUrl url = urls.first();
0757         QPointer<QDialog> dialog;
0758         foreach (TransferFactory *factory, KGet::factories()) {
0759             const QList<TransferGroupHandler *> groups = KGet::groupsFromExceptions(url);
0760             dialog = factory->createNewTransferDialog(url, suggestedFileName, !groups.isEmpty() ? groups.first() : nullptr);
0761             if (dialog) {
0762                 KWindowInfo info(KGet::m_mainWindow->winId(), NET::WMDesktop);
0763                 KWindowSystem::setCurrentDesktop(info.desktop());
0764                 KWindowSystem::forceActiveWindow(KGet::m_mainWindow->winId());
0765 
0766                 dialog->exec();
0767                 delete dialog;
0768             }
0769         }
0770     }
0771 
0772     m_numJobs.remove(jobId);
0773     m_urls.erase(itUrls);
0774 
0775     /// Display default NewTransferDialog
0776     if (!urls.isEmpty()) {
0777         createDialog(urls, suggestedFileName);
0778     }
0779 }
0780 
0781 void NewTransferDialogHandler::createDialog(const QList<QUrl> &urls, const QString &suggestedFileName)
0782 {
0783     if (!m_dialog) {
0784         m_dialog = new NewTransferDialog(KGet::m_mainWindow);
0785     }
0786 
0787     m_dialog->m_window = KGet::m_mainWindow;
0788     m_dialog->showDialog(urls, suggestedFileName);
0789 }
0790 
0791 #include "moc_newtransferdialog.cpp"