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