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