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