1 /***************************************************************************
2 *   Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net>                     *
3 *                                                                         *
4 *   This program is free software; you can redistribute it and/or modify  *
5 *   it under the terms of the GNU General Public License as published by  *
6 *   the Free Software Foundation; either version 2 of the License, or     *
7 *   (at your option) any later version.                                   *
8 *                                                                         *
9 *   This program is distributed in the hope that it will be useful,       *
10 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12 *   GNU General Public License for more details.                          *
13 *                                                                         *
14 *   You should have received a copy of the GNU General Public License     *
15 *   along with this program; if not, write to the                         *
16 *   Free Software Foundation, Inc.,                                       *
17 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
18 ***************************************************************************/
19 
20 #include "metalinkcreator.h"
21 #include "filedlg.h"
22 #include "dragdlg.h"
23 #include "localemodels.h"
24 #include "generalwidget.h"
25 
26 #include <QFileDialog>
27 #include <QDragEnterEvent>
28 #include <QMimeData>
29 #include <QPushButton>
30 #include <QSortFilterProxyModel>
31 #include <QStandardItemModel>
32 #include <QTimer>
33 
34 #include <KLocalizedString>
35 #include <KMessageBox>
36 
37 //TODO for 4.4 look at the changes of the newest Draft --> what elements have to be added/removed
38 
FileWidget(QWidget * parent)39 FileWidget::FileWidget(QWidget *parent)
40   : QWidget(parent)
41 {
42     setAcceptDrops(true);
43 }
44 
45 
dragEnterEvent(QDragEnterEvent * event)46 void FileWidget::dragEnterEvent(QDragEnterEvent *event)
47 {
48     if (event->mimeData()->hasUrls())
49     {
50         event->acceptProposedAction();
51     }
52 }
53 
dropEvent(QDropEvent * event)54 void FileWidget::dropEvent(QDropEvent *event)
55 {
56     QList<QUrl> urls = event->mimeData()->urls();
57 
58     event->acceptProposedAction();
59 
60     if (!urls.isEmpty()) {
61         Q_EMIT urlsDropped(urls);
62     }
63 }
64 
MetalinkCreator(QWidget * parent)65 MetalinkCreator::MetalinkCreator(QWidget *parent)
66   : KAssistantDialog(parent),
67     m_needUrlCount(0),
68     m_countrySort(nullptr),
69     m_languageModel(nullptr),
70     m_languageSort(nullptr),
71     m_introduction(nullptr),
72     m_generalPage(nullptr),
73     m_filesModel(nullptr)
74 {
75     create();
76 
77     connect(finishButton(), &QPushButton::clicked, this, &MetalinkCreator::slotSave);
78     connect(this, &KPageDialog::currentPageChanged, this, &MetalinkCreator::slotUpdateAssistantButtons);
79 
80     qRegisterMetaType<KGetMetalink::File>("KGetMetalink::File");
81     connect(&m_thread, SIGNAL(fileResult(KGetMetalink::File)), this, SLOT(slotAddFile(KGetMetalink::File)));
82     connect(&m_thread, &QThread::finished, this, &MetalinkCreator::slotThreadFinished);
83 
84     setWindowTitle(i18n("Create a Metalink"));
85 }
86 
~MetalinkCreator()87 MetalinkCreator::~MetalinkCreator()
88 {
89 }
90 
slotUpdateAssistantButtons(KPageWidgetItem * to,KPageWidgetItem * from)91 void MetalinkCreator::slotUpdateAssistantButtons(KPageWidgetItem *to, KPageWidgetItem *from)
92 {
93     //once we leave the introduction page the data is being loaded
94     if (m_introduction && m_generalPage && (from == m_introduction) && (to == m_generalPage))
95     {
96         load();
97     }
98 
99     //it is impossible to return to the introduction page
100     backButton()->setEnabled(to != m_generalPage);
101 
102     if (!m_filesModel->rowCount()) {
103         uiFiles.infoWidget->setText(i18n("Add at least one file."));
104     } else if (m_needUrlCount) {
105         uiFiles.infoWidget->setText(i18n("You need to set mirrors for the entries with an icon."));
106     }
107     uiFiles.infoWidget->setVisible(!m_filesModel->rowCount() || m_needUrlCount);
108 
109     //only enable finish when the metalink is valid (i.e. no required data missing)
110     //and the thread is not running
111     finishButton()->setEnabled(metalink.isValid() && !m_thread.isRunning());
112 }
113 
create()114 void MetalinkCreator::create()
115 {
116     createIntroduction();
117     m_general = new GeneralWidget(this);
118     m_generalPage = addPage(m_general, i18n("General optional information for the metalink."));
119     QTimer::singleShot(0, this, &MetalinkCreator::slotDelayedCreation);
120 }
121 
slotDelayedCreation()122 void MetalinkCreator::slotDelayedCreation()
123 {
124     auto *countryModel = new CountryModel(this);
125     countryModel->setupModelData();
126     m_countrySort = new QSortFilterProxyModel(this);
127     m_countrySort->setSourceModel(countryModel);
128     m_countrySort->setSortLocaleAware(true);
129     m_countrySort->sort(0);
130 
131     m_languageModel = new LanguageModel(this);
132     m_languageModel->setupModelData();
133     m_languageSort = new QSortFilterProxyModel(this);
134     m_languageSort->setSourceModel(m_languageModel);
135     m_languageSort->setSortLocaleAware(true);
136     m_languageSort->sort(0);
137 
138     createFiles();
139     slotUpdateIntroductionNextButton();
140 }
141 
load()142 void MetalinkCreator::load()
143 {
144     QUrl url = uiIntroduction.load->url();
145     if (uiIntroduction.loadButton->isChecked() && url.isValid())
146     {
147         if (!KGetMetalink::HandleMetalink::load(url, &metalink))
148         {
149             KMessageBox::error(this, i18n("Unable to load: %1", url.toString()), i18n("Error"));
150         }
151     }
152 
153     m_general->load(metalink);
154     loadFiles();
155 }
156 
slotSave()157 void MetalinkCreator::slotSave()
158 {
159     m_general->save(&metalink);
160 
161     QUrl url = uiIntroduction.save->url();
162     if (url.isValid())
163     {
164         if(!KGetMetalink::HandleMetalink::save(url, &metalink))
165         {
166             KMessageBox::error(this, i18n("Unable to save to: %1", url.toString()), i18n("Error"));
167         }
168     }
169 }
170 
createIntroduction()171 void MetalinkCreator::createIntroduction()
172 {
173     auto *widget = new QWidget(this);
174     uiIntroduction.setupUi(widget);
175 
176     uiIntroduction.save->setFilter("*.meta4|" + i18n("Metalink Version 4.0 file (*.meta4)") + "\n*.metalink|" + i18n("Metalink Version 3.0 file (*.metalink)"));
177     uiIntroduction.save->setAcceptMode(QFileDialog::AcceptSave);
178 
179     connect(uiIntroduction.save, &KUrlRequester::textChanged, this, &MetalinkCreator::slotUpdateIntroductionNextButton);
180     connect(uiIntroduction.load, &KUrlRequester::textChanged, this, &MetalinkCreator::slotUpdateIntroductionNextButton);
181     connect(uiIntroduction.loadButton, &QAbstractButton::toggled, this, &MetalinkCreator::slotUpdateIntroductionNextButton);
182 
183     m_introduction = addPage(widget, i18n("Define the saving location."));
184 
185     setValid(m_introduction, false);
186 }
187 
slotUpdateIntroductionNextButton()188 void MetalinkCreator::slotUpdateIntroductionNextButton()
189 {
190     bool enableNext = false;
191 
192     //check if a save location and if selected if also a load location has been specified and if the m_countrySort has been created
193     enableNext = uiIntroduction.save->url().isValid() && m_countrySort;
194     if (enableNext && uiIntroduction.loadButton->isChecked()) {
195         enableNext = uiIntroduction.load->url().isValid();
196     }
197 
198     setValid(m_introduction, enableNext);
199 }
200 
createFiles()201 void MetalinkCreator::createFiles()
202 {
203     m_handler = new DirectoryHandler(this);
204     connect(m_handler, &DirectoryHandler::finished, this, &MetalinkCreator::slotOpenDragDlg);
205 
206     auto *widget = new FileWidget(this);
207     uiFiles.setupUi(widget);
208 
209     m_filesModel = new QStandardItemModel(0, 1, this);
210     uiFiles.files->setModel(m_filesModel);
211 
212     uiFiles.infoWidget->setCloseButtonVisible(false);
213     uiFiles.infoWidget->setMessageType(KMessageWidget::Information);
214     uiFiles.add_local_file->setIcon(QIcon::fromTheme("list-add"));
215     KGuiItem::assign(uiFiles.add_file, KStandardGuiItem::add());
216     KGuiItem::assign(uiFiles.properties_file, KStandardGuiItem::properties());
217     uiFiles.properties_file->setEnabled(false);
218     KGuiItem::assign(uiFiles.remove_file, KStandardGuiItem::remove());
219     uiFiles.remove_file->setEnabled(false);
220     uiFiles.dragDrop->hide();
221 
222     connect(uiFiles.add_local_file, &QAbstractButton::clicked, this, &MetalinkCreator::slotAddLocalFilesClicked);
223     connect(uiFiles.add_file, &QAbstractButton::clicked, this, &MetalinkCreator::slotAddClicked);
224     connect(uiFiles.remove_file, &QAbstractButton::clicked, this, &MetalinkCreator::slotRemoveFile);
225     connect(uiFiles.properties_file, &QAbstractButton::clicked, this, &MetalinkCreator::slotFileProperties);
226     connect(uiFiles.files->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MetalinkCreator::slotUpdateFilesButtons);
227     connect(widget, &FileWidget::urlsDropped, m_handler, &DirectoryHandler::slotFiles);
228 
229     addPage(widget, i18nc("file as in file on hard drive", "Files"));
230 }
231 
loadFiles()232 void MetalinkCreator::loadFiles()
233 {
234     foreach (const KGetMetalink::File &file, metalink.files.files)
235     {
236         auto *item = new QStandardItem(file.name);
237         if (!file.resources.isValid())
238         {
239             ++m_needUrlCount;
240             item->setIcon(QIcon::fromTheme("edit-delete"));
241         }
242         m_filesModel->insertRow(m_filesModel->rowCount(), item);
243     }
244 }
245 
slotUpdateFilesButtons()246 void MetalinkCreator::slotUpdateFilesButtons()
247 {
248     const QModelIndexList indexes = uiFiles.files->selectionModel()->selectedRows();
249     uiFiles.remove_file->setEnabled(indexes.count());
250 
251     bool propertiesEnabled = (indexes.count() == 1);
252     uiFiles.properties_file->setEnabled(propertiesEnabled);
253 }
254 
slotAddLocalFilesClicked()255 void MetalinkCreator::slotAddLocalFilesClicked()
256 {
257     QPointer<QFileDialog> dialog = new QFileDialog(this);
258     dialog->setFileMode(QFileDialog::ExistingFiles);
259     if (dialog->exec() == QDialog::Accepted) {
260         m_handler->slotFiles(dialog->selectedUrls());
261     }
262     delete dialog;
263 }
264 
slotAddFile()265 void MetalinkCreator::slotAddFile()
266 {
267     auto *item = new QStandardItem(m_tempFile.name);
268     m_filesModel->insertRow(m_filesModel->rowCount(), item);
269     metalink.files.files.append(m_tempFile);
270     m_tempFile.clear();
271 
272     slotUpdateAssistantButtons(nullptr, m_files);
273 }
274 
slotAddFile(const KGetMetalink::File & file)275 void MetalinkCreator::slotAddFile(const KGetMetalink::File &file)
276 {
277     auto *item = new QStandardItem(file.name);
278     if (!file.resources.isValid())
279     {
280         ++m_needUrlCount;
281         item->setIcon(QIcon::fromTheme("edit-delete"));
282     }
283     m_filesModel->insertRow(m_filesModel->rowCount(), item);
284     metalink.files.files.append(file);
285 
286     slotUpdateAssistantButtons(nullptr, m_files);
287 }
288 
slotFileEdited(const QString & oldFileName,const QString & newFileName)289 void MetalinkCreator::slotFileEdited(const QString &oldFileName, const QString &newFileName)
290 {
291     Q_UNUSED(oldFileName)
292 
293     const QModelIndex index = uiFiles.files->selectionModel()->selectedRows().first();
294     QStandardItem *item = m_filesModel->itemFromIndex(index);
295     item->setText(newFileName);
296 
297     //had no url but has it now
298     if (!item->icon().isNull())
299     {
300         --m_needUrlCount;
301         item->setIcon(QIcon());
302     }
303 
304     slotUpdateAssistantButtons(nullptr, m_files);
305 }
306 
slotRemoveFile()307 void MetalinkCreator::slotRemoveFile()
308 {
309     while (uiFiles.files->selectionModel()->hasSelection()) {
310         const QModelIndex index = uiFiles.files->selectionModel()->selectedRows().first();
311         const QString filePath = index.data().toString();
312         for (int i = 0; i < metalink.files.files.size(); ++i)
313         {
314             if (metalink.files.files.at(i).name == filePath)
315             {
316                 //the entry had not url, so do not count it anymore
317                 if (!index.data(Qt::DecorationRole).isNull())
318                 {
319                     --m_needUrlCount;
320                 }
321                 metalink.files.files.removeAt(i);
322                 break;
323             }
324         }
325 
326         m_filesModel->removeRow(index.row());
327     }
328 
329     slotUpdateFilesButtons();
330     slotUpdateAssistantButtons(nullptr, m_files);
331 }
332 
slotAddClicked()333 void MetalinkCreator::slotAddClicked()
334 {
335     //no old stored data should be used
336     m_tempFile.clear();
337     fileDlg(&m_tempFile);
338 }
339 
fileDlg(KGetMetalink::File * file,bool edit)340 void MetalinkCreator::fileDlg(KGetMetalink::File *file, bool edit)
341 {
342     QStringList currentNames;
343     for (int i = 0; i < m_filesModel->rowCount(); ++i)
344     {
345         currentNames.append(m_filesModel->index(i, 0).data().toString());
346     }
347 
348     auto *fileDlg = new FileDlg(file, currentNames, m_countrySort, m_languageSort, this, edit);
349     fileDlg->setAttribute(Qt::WA_DeleteOnClose);
350     fileDlg->setWindowModality(Qt::ApplicationModal);
351     fileDlg->show();
352 
353     connect(fileDlg, SIGNAL(addFile()), this, SLOT(slotAddFile()));
354     connect(fileDlg, &FileDlg::fileEdited, this, &MetalinkCreator::slotFileEdited);
355 }
356 
slotFileProperties()357 void MetalinkCreator::slotFileProperties()
358 {
359     const QModelIndex index = uiFiles.files->selectionModel()->selectedRows().first();
360     const QString fileName = index.data().toString();
361 
362     //search the selected file in metalink
363     for (int i = 0; i < metalink.files.files.count(); ++i)
364     {
365         if (metalink.files.files.at(i).name == fileName)
366         {
367             fileDlg(&metalink.files.files[i], true);
368             break;
369         }
370     }
371 }
slotOpenDragDlg()372 void MetalinkCreator::slotOpenDragDlg()
373 {
374     m_tempResources.clear();
375     m_tempCommonData.clear();
376     auto *dragDlg = new DragDlg(&m_tempResources, &m_tempCommonData, m_countrySort, m_languageSort, this);
377     dragDlg->setAttribute(Qt::WA_DeleteOnClose);
378     dragDlg->show();
379 
380     connect(dragDlg, &DragDlg::usedTypes, this, &MetalinkCreator::slotHandleDropped);
381 }
slotHandleDropped(const QStringList & types,bool createPartial)382 void MetalinkCreator::slotHandleDropped(const QStringList &types, bool createPartial)
383 {
384     uiFiles.progressBar->setMaximum(0);
385     uiFiles.dragDrop->show();
386     m_thread.setData(m_handler->takeFiles(), types, createPartial, m_tempResources, m_tempCommonData);
387 }
388 
slotThreadFinished()389 void MetalinkCreator::slotThreadFinished()
390 {
391     uiFiles.progressBar->setMaximum(10);
392     uiFiles.dragDrop->hide();
393     slotUpdateAssistantButtons(nullptr, m_files);
394 }
395 
396 
397