1 // vim: set tabstop=4 shiftwidth=4 expandtab:
2 /*
3 Gwenview: an image viewer
4 Copyright 2009 Aurélien Gâteau <agateau@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
19
20 */
21 // Self
22 #include "importdialog.h"
23
24 // Qt
25 #include <QApplication>
26 #include <QDate>
27 #include <QStackedWidget>
28 #include <QStandardPaths>
29
30 // KF
31 #include <KIO/DeleteJob>
32 #include <KLocalizedString>
33 #include <KMessageBox>
34 #include <KProtocolInfo>
35 #include <KService>
36 #include <KStandardGuiItem>
37 #include <Solid/Device>
38 #include <KIO/ApplicationLauncherJob>
39 #include <KIO/JobUiDelegate>
40
41 // Local
42 #include "dialogpage.h"
43 #include "gwenview_importer_debug.h"
44 #include "importer.h"
45 #include "importerconfig.h"
46 #include "progresspage.h"
47 #include "thumbnailpage.h"
48
49 namespace Gwenview
50 {
51 class ImportDialogPrivate
52 {
53 public:
54 ImportDialog *q;
55 QStackedWidget *mCentralWidget;
56 ThumbnailPage *mThumbnailPage;
57 ProgressPage *mProgressPage;
58 DialogPage *mDialogPage;
59 Importer *mImporter;
60
checkForFailedUrls()61 void checkForFailedUrls()
62 {
63 // First check for errors on file imports or subfolder creation
64 QList<QUrl> failedUrls = mImporter->failedUrlList();
65 QList<QUrl> failedSubFolders = mImporter->failedSubFolderList();
66 int failedUrlCount = failedUrls.count();
67 int failedSubFolderCount = failedSubFolders.count();
68 if (failedUrlCount + failedSubFolderCount > 0) {
69 QStringList files, dirs;
70 for (int i = 0; i < failedUrlCount; i++) {
71 files << failedUrls[i].toString(QUrl::PreferLocalFile);
72 }
73 for (int i = 0; i < failedSubFolderCount; i++) {
74 dirs << failedSubFolders[i].toString(QUrl::PreferLocalFile);
75 }
76 Q_EMIT q->showErrors(files, dirs);
77 }
78 }
79
deleteImportedUrls()80 void deleteImportedUrls()
81 {
82 QList<QUrl> importedUrls = mImporter->importedUrlList();
83 QList<QUrl> skippedUrls = mImporter->skippedUrlList();
84 int importedCount = importedUrls.count();
85 int skippedCount = skippedUrls.count();
86
87 if (importedCount == 0 && skippedCount == 0) {
88 return;
89 }
90
91 QStringList message;
92 message << i18np("One document has been imported.", "%1 documents have been imported.", importedCount);
93 if (skippedCount > 0) {
94 message << i18np("One document has been skipped because it had already been imported.",
95 "%1 documents have been skipped because they had already been imported.",
96 skippedCount);
97 }
98
99 if (mImporter->renamedCount() > 0) {
100 message[0].append("*");
101 message << "<small>* "
102 + i18np("One of them has been renamed because another document with the same name had already been imported.",
103 "%1 of them have been renamed because other documents with the same name had already been imported.",
104 mImporter->renamedCount())
105 + "</small>";
106 }
107
108 message << QString();
109 if (skippedCount == 0) {
110 message << i18np("Delete the imported document from the device?", "Delete the %1 imported documents from the device?", importedCount);
111 } else if (importedCount == 0) {
112 message << i18np("Delete the skipped document from the device?", "Delete the %1 skipped documents from the device?", skippedCount);
113 } else {
114 message << i18ncp("Singular sentence is actually never used.",
115 "Delete the imported or skipped document from the device?",
116 "Delete the %1 imported and skipped documents from the device?",
117 importedCount + skippedCount);
118 }
119
120 int answer = KMessageBox::questionYesNo(mCentralWidget,
121 QStringLiteral("<qt>") + message.join(QStringLiteral("<br/>")) + QStringLiteral("</qt>"),
122 i18nc("@title:window", "Import Finished"),
123 KGuiItem(i18n("Keep")),
124 KStandardGuiItem::del());
125 if (answer == KMessageBox::Yes) {
126 return;
127 }
128 QList<QUrl> urls = importedUrls + skippedUrls;
129 while (true) {
130 KIO::Job *job = KIO::del(urls);
131 if (job->exec()) {
132 break;
133 }
134 // Deleting failed
135 int answer =
136 KMessageBox::warningYesNo(mCentralWidget,
137 i18np("Failed to delete the document:\n%2", "Failed to delete documents:\n%2", urls.count(), job->errorString()),
138 QString(),
139 KGuiItem(i18n("Retry")),
140 KGuiItem(i18n("Ignore")));
141 if (answer != KMessageBox::Yes) {
142 // Ignore
143 break;
144 }
145 }
146 }
147
startGwenview()148 void startGwenview()
149 {
150 KService::Ptr service = KService::serviceByDesktopName(QStringLiteral("org.kde.gwenview"));
151 if (!service) {
152 qCCritical(GWENVIEW_IMPORTER_LOG) << "Could not find gwenview";
153 } else {
154 auto *job = new KIO::ApplicationLauncherJob(service);
155 job->setUrls({mThumbnailPage->destinationUrl()});
156 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, nullptr));
157 job->start();
158 }
159 }
160
showWhatNext()161 void showWhatNext()
162 {
163 mCentralWidget->setCurrentWidget(mDialogPage);
164 mDialogPage->setText(i18n("What do you want to do now?"));
165 mDialogPage->removeButtons();
166 int gwenview = mDialogPage->addButton(KGuiItem(i18n("View Imported Documents with Gwenview"), QStringLiteral("gwenview")));
167 int importMore = mDialogPage->addButton(KGuiItem(i18n("Import more Documents")));
168 mDialogPage->addButton(KGuiItem(i18n("Quit"), QStringLiteral("dialog-cancel")));
169
170 int answer = mDialogPage->exec();
171 if (answer == gwenview) {
172 startGwenview();
173 qApp->quit();
174 } else if (answer == importMore) {
175 mCentralWidget->setCurrentWidget(mThumbnailPage);
176 } else { /* quit */
177 qApp->quit();
178 }
179 }
180 };
181
ImportDialog()182 ImportDialog::ImportDialog()
183 : d(new ImportDialogPrivate)
184 {
185 d->q = this;
186 d->mImporter = new Importer(this);
187 connect(d->mImporter, &Importer::error, this, &ImportDialog::showImportError);
188 d->mThumbnailPage = new ThumbnailPage;
189
190 QUrl url = ImporterConfig::destinationUrl();
191 if (!url.isValid()) {
192 url = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
193 int year = QDate::currentDate().year();
194 url.setPath(url.path() + '/' + QString::number(year));
195 }
196 d->mThumbnailPage->setDestinationUrl(url);
197
198 d->mProgressPage = new ProgressPage(d->mImporter);
199
200 d->mDialogPage = new DialogPage;
201
202 d->mCentralWidget = new QStackedWidget;
203 setCentralWidget(d->mCentralWidget);
204 d->mCentralWidget->addWidget(d->mThumbnailPage);
205 d->mCentralWidget->addWidget(d->mProgressPage);
206 d->mCentralWidget->addWidget(d->mDialogPage);
207
208 connect(d->mThumbnailPage, &ThumbnailPage::importRequested, this, &ImportDialog::startImport);
209 connect(d->mThumbnailPage, &ThumbnailPage::rejected, this, &QWidget::close);
210 connect(d->mImporter, &Importer::importFinished, this, &ImportDialog::slotImportFinished);
211 connect(this, &ImportDialog::showErrors, d->mDialogPage, &DialogPage::slotShowErrors);
212
213 d->mCentralWidget->setCurrentWidget(d->mThumbnailPage);
214
215 setWindowIcon(QIcon::fromTheme(QStringLiteral("gwenview")));
216 setAutoSaveSettings();
217 }
218
~ImportDialog()219 ImportDialog::~ImportDialog()
220 {
221 delete d;
222 }
223
sizeHint() const224 QSize ImportDialog::sizeHint() const
225 {
226 return QSize(700, 500);
227 }
228
setSourceUrl(const QUrl & url,const QString & deviceUdi)229 void ImportDialog::setSourceUrl(const QUrl &url, const QString &deviceUdi)
230 {
231 QString name, iconName;
232 if (deviceUdi.isEmpty()) {
233 name = url.url(QUrl::PreferLocalFile);
234 iconName = KProtocolInfo::icon(url.scheme());
235 if (iconName.isEmpty()) {
236 iconName = QStringLiteral("folder");
237 }
238 } else {
239 Solid::Device device(deviceUdi);
240 name = device.vendor() + QLatin1Char(' ') + device.product();
241 iconName = device.icon();
242 }
243 d->mThumbnailPage->setSourceUrl(url, iconName, name);
244 }
245
startImport()246 void ImportDialog::startImport()
247 {
248 QUrl url = d->mThumbnailPage->destinationUrl();
249 ImporterConfig::setDestinationUrl(url);
250 ImporterConfig::self()->save();
251
252 d->mCentralWidget->setCurrentWidget(d->mProgressPage);
253 d->mImporter->setAutoRenameFormat(ImporterConfig::autoRename() ? ImporterConfig::autoRenameFormat() : QString());
254 d->mImporter->start(d->mThumbnailPage->urlList(), url);
255 }
256
slotImportFinished()257 void ImportDialog::slotImportFinished()
258 {
259 d->checkForFailedUrls();
260 d->deleteImportedUrls();
261 d->showWhatNext();
262 }
263
showImportError(const QString & message)264 void ImportDialog::showImportError(const QString &message)
265 {
266 KMessageBox::sorry(this, message);
267 d->mCentralWidget->setCurrentWidget(d->mThumbnailPage);
268 }
269
270 } // namespace
271