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