1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2013-11-18
7 * Description : a tool to export images to Dropbox web service
8 *
9 * Copyright (C) 2013 by Pankaj Kumar <me at panks dot me>
10 * Copyright (C) 2013-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software Foundation;
15 * either version 2, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 #include "dbwindow.h"
25
26 // Qt includes
27
28 #include <QWindow>
29 #include <QSpinBox>
30 #include <QCheckBox>
31 #include <QMessageBox>
32 #include <QCloseEvent>
33
34 // KDE includes
35
36 #include <klocalizedstring.h>
37 #include <ksharedconfig.h>
38 #include <kconfiggroup.h>
39
40 // Local includes
41
42 #include "digikam_debug.h"
43 #include "ditemslist.h"
44 #include "digikam_version.h"
45 #include "dbtalker.h"
46 #include "dbitem.h"
47 #include "dbnewalbumdlg.h"
48 #include "dbwidget.h"
49
50 namespace DigikamGenericDropBoxPlugin
51 {
52
53 class Q_DECL_HIDDEN DBWindow::Private
54 {
55 public:
56
Private()57 explicit Private()
58 {
59 imagesCount = 0;
60 imagesTotal = 0;
61 widget = nullptr;
62 albumDlg = nullptr;
63 talker = nullptr;
64 }
65
66 unsigned int imagesCount;
67 unsigned int imagesTotal;
68
69 DBWidget* widget;
70 DBNewAlbumDlg* albumDlg;
71 DBTalker* talker;
72
73 QString currentAlbumName;
74 QList<QUrl> transferQueue;
75 };
76
DBWindow(DInfoInterface * const iface,QWidget * const)77 DBWindow::DBWindow(DInfoInterface* const iface, QWidget* const /*parent*/)
78 : WSToolDialog(nullptr, QLatin1String("Dropbox Export Dialog")),
79 d (new Private)
80 {
81 d->widget = new DBWidget(this, iface, QLatin1String("Dropbox"));
82 d->widget->imagesList()->setIface(iface);
83
84 setMainWidget(d->widget);
85 setModal(false);
86 setWindowTitle(i18n("Export to Dropbox"));
87
88 startButton()->setText(i18n("Start Upload"));
89 startButton()->setToolTip(i18n("Start upload to Dropbox"));
90
91 d->widget->setMinimumSize(700, 500);
92
93 connect(d->widget->imagesList(), SIGNAL(signalImageListChanged()),
94 this, SLOT(slotImageListChanged()));
95
96 connect(d->widget->getChangeUserBtn(), SIGNAL(clicked()),
97 this, SLOT(slotUserChangeRequest()));
98
99 connect(d->widget->getNewAlbmBtn(), SIGNAL(clicked()),
100 this, SLOT(slotNewAlbumRequest()));
101
102 connect(d->widget->getReloadBtn(), SIGNAL(clicked()),
103 this, SLOT(slotReloadAlbumsRequest()));
104
105 connect(startButton(), SIGNAL(clicked()),
106 this, SLOT(slotStartTransfer()));
107
108 d->albumDlg = new DBNewAlbumDlg(this, QLatin1String("Dropbox"));
109
110 d->talker = new DBTalker(this);
111
112 connect(d->talker, SIGNAL(signalBusy(bool)),
113 this, SLOT(slotBusy(bool)));
114
115 connect(d->talker, SIGNAL(signalLinkingFailed()),
116 this, SLOT(slotSignalLinkingFailed()));
117
118 connect(d->talker, SIGNAL(signalLinkingSucceeded()),
119 this, SLOT(slotSignalLinkingSucceeded()));
120
121 connect(d->talker, SIGNAL(signalSetUserName(QString)),
122 this, SLOT(slotSetUserName(QString)));
123
124 connect(d->talker, SIGNAL(signalListAlbumsFailed(QString)),
125 this, SLOT(slotListAlbumsFailed(QString)));
126
127 connect(d->talker, SIGNAL(signalListAlbumsDone(QList<QPair<QString,QString> >)), // krazy:exclude=normalize
128 this, SLOT(slotListAlbumsDone(QList<QPair<QString,QString> >))); // krazy:exclude=normalize
129
130 connect(d->talker, SIGNAL(signalCreateFolderFailed(QString)),
131 this, SLOT(slotCreateFolderFailed(QString)));
132
133 connect(d->talker, SIGNAL(signalCreateFolderSucceeded()),
134 this, SLOT(slotCreateFolderSucceeded()));
135
136 connect(d->talker, SIGNAL(signalAddPhotoFailed(QString)),
137 this, SLOT(slotAddPhotoFailed(QString)));
138
139 connect(d->talker, SIGNAL(signalAddPhotoSucceeded()),
140 this, SLOT(slotAddPhotoSucceeded()));
141
142 connect(this, SIGNAL(finished(int)),
143 this, SLOT(slotFinished()));
144
145 readSettings();
146 buttonStateChange(false);
147
148 d->talker->link();
149 }
150
~DBWindow()151 DBWindow::~DBWindow()
152 {
153 delete d->talker;
154 delete d;
155 }
156
setItemsList(const QList<QUrl> & urls)157 void DBWindow::setItemsList(const QList<QUrl>& urls)
158 {
159 d->widget->imagesList()->slotAddImages(urls);
160 }
161
reactivate()162 void DBWindow::reactivate()
163 {
164 d->widget->imagesList()->loadImagesFromCurrentSelection();
165 d->widget->progressBar()->hide();
166
167 show();
168 }
169
readSettings()170 void DBWindow::readSettings()
171 {
172 KSharedConfigPtr config = KSharedConfig::openConfig();
173 KConfigGroup grp = config->group("Dropbox Settings");
174 d->currentAlbumName = grp.readEntry("Current Album", QString());
175
176 if (grp.readEntry("Resize", false))
177 {
178 d->widget->getResizeCheckBox()->setChecked(true);
179 d->widget->getDimensionSpB()->setEnabled(true);
180 }
181 else
182 {
183 d->widget->getResizeCheckBox()->setChecked(false);
184 d->widget->getDimensionSpB()->setEnabled(false);
185 }
186
187 d->widget->getOriginalCheckBox()->setChecked(grp.readEntry("Upload Original", false));
188 d->widget->getDimensionSpB()->setValue(grp.readEntry("Maximum Width", 1600));
189 d->widget->getImgQualitySpB()->setValue(grp.readEntry("Image Quality", 90));
190 }
191
writeSettings()192 void DBWindow::writeSettings()
193 {
194 KSharedConfigPtr config = KSharedConfig::openConfig();
195 KConfigGroup grp = config->group("Dropbox Settings");
196
197 grp.writeEntry("Current Album", d->currentAlbumName);
198 grp.writeEntry("Resize", d->widget->getResizeCheckBox()->isChecked());
199 grp.writeEntry("Upload Original", d->widget->getOriginalCheckBox()->isChecked());
200 grp.writeEntry("Maximum Width", d->widget->getDimensionSpB()->value());
201 grp.writeEntry("Image Quality", d->widget->getImgQualitySpB()->value());
202 }
203
slotSetUserName(const QString & msg)204 void DBWindow::slotSetUserName(const QString& msg)
205 {
206 d->widget->updateLabels(msg, QLatin1String(""));
207 }
208
slotListAlbumsDone(const QList<QPair<QString,QString>> & list)209 void DBWindow::slotListAlbumsDone(const QList<QPair<QString, QString> >& list)
210 {
211 d->widget->getAlbumsCoB()->clear();
212 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "slotListAlbumsDone:" << list.size();
213
214 for (int i = 0 ; i < list.size() ; ++i)
215 {
216 d->widget->getAlbumsCoB()->addItem(
217 QIcon::fromTheme(QLatin1String("system-users")),
218 list.value(i).second, list.value(i).first);
219
220 if (d->currentAlbumName == list.value(i).first)
221 {
222 d->widget->getAlbumsCoB()->setCurrentIndex(i);
223 }
224 }
225
226 buttonStateChange(true);
227 d->talker->getUserName();
228 }
229
slotBusy(bool val)230 void DBWindow::slotBusy(bool val)
231 {
232 if (val)
233 {
234 setCursor(Qt::WaitCursor);
235 d->widget->getChangeUserBtn()->setEnabled(false);
236 buttonStateChange(false);
237 }
238 else
239 {
240 setCursor(Qt::ArrowCursor);
241 d->widget->getChangeUserBtn()->setEnabled(true);
242 buttonStateChange(true);
243 }
244 }
245
slotStartTransfer()246 void DBWindow::slotStartTransfer()
247 {
248 d->widget->imagesList()->clearProcessedStatus();
249
250 if (d->widget->imagesList()->imageUrls().isEmpty())
251 {
252 QMessageBox::critical(this, i18nc("@title:window", "Error"),
253 i18n("No image selected. Please select which images should be uploaded."));
254 return;
255 }
256
257 if (!(d->talker->authenticated()))
258 {
259 if (QMessageBox::question(this, i18n("Login Failed"),
260 i18n("Authentication failed. Do you want to try again?"))
261 == QMessageBox::Yes)
262 {
263 d->talker->link();
264 return;
265 }
266 else
267 {
268 return;
269 }
270 }
271
272 d->transferQueue = d->widget->imagesList()->imageUrls();
273
274 if (d->transferQueue.isEmpty())
275 {
276 return;
277 }
278
279 d->currentAlbumName = d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString();
280
281 d->imagesTotal = d->transferQueue.count();
282 d->imagesCount = 0;
283
284 d->widget->progressBar()->setFormat(i18n("%v / %m"));
285 d->widget->progressBar()->setMaximum(d->imagesTotal);
286 d->widget->progressBar()->setValue(0);
287 d->widget->progressBar()->show();
288 d->widget->progressBar()->progressScheduled(i18n("Dropbox export"), true, true);
289 d->widget->progressBar()->progressThumbnailChanged(
290 QIcon::fromTheme(QLatin1String("dk-dropbox")).pixmap(22, 22));
291
292 uploadNextPhoto();
293 }
294
uploadNextPhoto()295 void DBWindow::uploadNextPhoto()
296 {
297 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "uploadNextPhoto:" << d->transferQueue.count();
298
299 if (d->transferQueue.isEmpty())
300 {
301 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "empty";
302 d->widget->progressBar()->progressCompleted();
303 return;
304 }
305
306 QString imgPath = d->transferQueue.first().toLocalFile();
307 QString temp = d->currentAlbumName + QLatin1Char('/');
308
309 bool res = d->talker->addPhoto(imgPath,
310 temp,
311 d->widget->getOriginalCheckBox()->isChecked(),
312 d->widget->getResizeCheckBox()->isChecked(),
313 d->widget->getDimensionSpB()->value(),
314 d->widget->getImgQualitySpB()->value());
315
316 if (!res)
317 {
318 slotAddPhotoFailed(QLatin1String(""));
319 return;
320 }
321 }
322
slotAddPhotoFailed(const QString & msg)323 void DBWindow::slotAddPhotoFailed(const QString& msg)
324 {
325 if (QMessageBox::question(this, i18n("Uploading Failed"),
326 i18n("Failed to upload photo to Dropbox."
327 "\n%1\n"
328 "Do you want to continue?", msg))
329 != QMessageBox::Yes)
330 {
331 d->transferQueue.clear();
332 d->widget->progressBar()->hide();
333 }
334 else
335 {
336 d->transferQueue.removeFirst();
337 d->imagesTotal--;
338 d->widget->progressBar()->setMaximum(d->imagesTotal);
339 d->widget->progressBar()->setValue(d->imagesCount);
340 uploadNextPhoto();
341 }
342 }
343
slotAddPhotoSucceeded()344 void DBWindow::slotAddPhotoSucceeded()
345 {
346 // Remove photo uploaded from the list
347 d->widget->imagesList()->removeItemByUrl(d->transferQueue.first());
348 d->transferQueue.removeFirst();
349 d->imagesCount++;
350 d->widget->progressBar()->setMaximum(d->imagesTotal);
351 d->widget->progressBar()->setValue(d->imagesCount);
352 uploadNextPhoto();
353 }
354
slotImageListChanged()355 void DBWindow::slotImageListChanged()
356 {
357 startButton()->setEnabled(!(d->widget->imagesList()->imageUrls().isEmpty()));
358 }
359
slotNewAlbumRequest()360 void DBWindow::slotNewAlbumRequest()
361 {
362 if (d->albumDlg->exec() == QDialog::Accepted)
363 {
364 DBFolder newFolder;
365 d->albumDlg->getFolderTitle(newFolder);
366 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "slotNewAlbumRequest:" << newFolder.title;
367 d->currentAlbumName = d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString();
368 d->currentAlbumName = d->currentAlbumName + newFolder.title;
369 d->talker->createFolder(d->currentAlbumName);
370 }
371 }
372
slotReloadAlbumsRequest()373 void DBWindow::slotReloadAlbumsRequest()
374 {
375 d->talker->listFolders();
376 }
377
slotSignalLinkingFailed()378 void DBWindow::slotSignalLinkingFailed()
379 {
380 slotSetUserName(QLatin1String(""));
381 d->widget->getAlbumsCoB()->clear();
382
383 if (QMessageBox::question(this, i18n("Login Failed"),
384 i18n("Authentication failed. Do you want to try again?"))
385 == QMessageBox::Yes)
386 {
387 d->talker->link();
388 }
389 }
390
slotSignalLinkingSucceeded()391 void DBWindow::slotSignalLinkingSucceeded()
392 {
393 d->talker->listFolders();
394 }
395
slotListAlbumsFailed(const QString & msg)396 void DBWindow::slotListAlbumsFailed(const QString& msg)
397 {
398 QMessageBox::critical(this, QString(), i18n("Dropbox call failed:\n%1", msg));
399 }
400
slotCreateFolderFailed(const QString & msg)401 void DBWindow::slotCreateFolderFailed(const QString& msg)
402 {
403 QMessageBox::critical(this, QString(), i18n("Dropbox call failed:\n%1", msg));
404 }
405
slotCreateFolderSucceeded()406 void DBWindow::slotCreateFolderSucceeded()
407 {
408 d->talker->listFolders();
409 }
410
slotTransferCancel()411 void DBWindow::slotTransferCancel()
412 {
413 d->transferQueue.clear();
414 d->widget->progressBar()->hide();
415 d->talker->cancel();
416 }
417
slotUserChangeRequest()418 void DBWindow::slotUserChangeRequest()
419 {
420 slotSetUserName(QLatin1String(""));
421 d->widget->getAlbumsCoB()->clear();
422 d->talker->unLink();
423 d->talker->link();
424 }
425
buttonStateChange(bool state)426 void DBWindow::buttonStateChange(bool state)
427 {
428 d->widget->getNewAlbmBtn()->setEnabled(state);
429 d->widget->getReloadBtn()->setEnabled(state);
430 startButton()->setEnabled(state);
431 }
432
slotFinished()433 void DBWindow::slotFinished()
434 {
435 writeSettings();
436 d->widget->imagesList()->listView()->clear();
437 }
438
closeEvent(QCloseEvent * e)439 void DBWindow::closeEvent(QCloseEvent* e)
440 {
441 if (!e)
442 {
443 return;
444 }
445
446 slotFinished();
447 e->accept();
448 }
449
450 } // namespace DigikamGenericDropBoxPlugin
451