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