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