vkwindow.cpp 15.7 KB
Newer Older
1
2
3
/* ============================================================
 *
 * This file is a part of kipi-plugins project
4
 * http://www.digikam.org
5
6
7
8
 *
 * Date        : 2010-11-15
 * Description : a kipi plugin to export images to VKontakte web service
 *
9
 * Copyright (C) 2011, 2012 by Alexander Potashev <aspotashev at gmail dot com>
10
11
12
 *
 * GUI based on Yandex.Fotki KIPI Plugin
 * Copyright (C) 2005-2008 by Vardhman Jain <vardhman at gmail dot com>
Gilles Caulier's avatar
Gilles Caulier committed
13
 * Copyright (C) 2008-2012 by Gilles Caulier <caulier dot gilles at gmail dot com>
Gilles Caulier's avatar
Gilles Caulier committed
14
15
 * Copyright (C) 2009      by Luka Renko <lure at kubuntu dot org>
 * Copyright (C) 2010      by Roman Tsisyk <roman at tsisyk dot com>
16
17
18
19
20
21
22
23
24
25
26
27
28
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation;
 * either version 2, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * ============================================================ */

Gilles Caulier's avatar
Gilles Caulier committed
29
#include "vkwindow.moc"
30
31
32

// Qt includes

smit mehta's avatar
Polish    
smit mehta committed
33
34
35
36
37
38
39
40
41
42
43
44
#include <QFileInfo>
#include <QCheckBox>
#include <QGroupBox>
#include <QCloseEvent>
#include <QButtonGroup>
#include <QLabel>
#include <QSpinBox>
#include <QRadioButton>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QToolButton>
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

// KDE includes

#include <kdeversion.h>
#include <kde_file.h>
#include <kdebug.h>
#include <kconfig.h>
#include <klocale.h>
#include <kdialog.h>
#include <klineedit.h>
#include <kcombobox.h>
#include <kpushbutton.h>
#include <kmenu.h>
#include <khelpmenu.h>
#include <kmessagebox.h>
#include <kpassworddialog.h>
#include <kprogressdialog.h>
#include <kstandarddirs.h>
#include <ktoolinvocation.h>
#include <kio/renamedialog.h>

// libkvkontakte includes

#include <libkvkontakte/uploadphotosjob.h>
69
#include <libkvkontakte/getvariablejob.h>
70
71
72
73
74
75
76
77
78
79

// LibKDcraw includes

#include <libkdcraw/version.h>
#include <libkdcraw/kdcraw.h>

// LibKIPI includes

#include <libkipi/interface.h>
#include <libkipi/uploadwidget.h>
Gilles Caulier's avatar
compile    
Gilles Caulier committed
80
#include <libkipi/imagecollection.h>
81
82
83

// Local includes

Gilles Caulier's avatar
compile    
Gilles Caulier committed
84
#include "kpaboutdata.h"
Gilles Caulier's avatar
Gilles Caulier committed
85
#include "kpversion.h"
Gilles Caulier's avatar
Gilles Caulier committed
86
#include "kpimageslist.h"
smit mehta's avatar
Polish    
smit mehta committed
87
#include "kpprogresswidget.h"
Alexander Potashev's avatar
Alexander Potashev committed
88

89
#include "vkapi.h"
90
91
#include "vkalbumdialog.h"
#include "albumchooserwidget.h"
92

93
#undef SLOT_JOB_DONE_INIT
94
95
96
97
98
99
100
101
102
103
#define SLOT_JOB_DONE_INIT(JobClass) \
    JobClass *job = dynamic_cast<JobClass *>(kjob); \
    Q_ASSERT(job);          \
    m_jobs.removeAll(job);  \
    if (job->error())       \
    {                       \
        handleVkError(job); \
        return;             \
    }

104
105
106
namespace KIPIVkontaktePlugin
{

Gilles Caulier's avatar
Gilles Caulier committed
107
VkontakteWindow::VkontakteWindow(bool import, QWidget* const parent)
Gilles Caulier's avatar
Gilles Caulier committed
108
    : KPToolDialog(parent)
109
{
110
111
    m_albumsBox = NULL;

112
    m_vkapi = new VkAPI(this);
113
114
115

    // read settings from file
    readSettings();
Gilles Caulier's avatar
Gilles Caulier committed
116

Alexander Potashev's avatar
Alexander Potashev committed
117
    connect(this, SIGNAL(finished()),
Gilles Caulier's avatar
Gilles Caulier committed
118
            this, SLOT(slotFinished()));
119

Gilles Caulier's avatar
Gilles Caulier committed
120
    m_import                = import;
Gilles Caulier's avatar
Gilles Caulier committed
121
122
    m_mainWidget            = new QWidget(this);
    QHBoxLayout* mainLayout = new QHBoxLayout(m_mainWidget);
Gilles Caulier's avatar
Gilles Caulier committed
123
    m_imgList               = new KPImagesList(this);
Gilles Caulier's avatar
Gilles Caulier committed
124
    m_imgList->setControlButtonsPlacement(KPImagesList::ControlButtonsBelow);
125
126
127
128
129
    m_imgList->setAllowRAW(false); // TODO: implement conversion
    m_imgList->loadImagesFromCurrentSelection();
    m_imgList->listView()->setWhatsThis(
        i18n("This is the list of images to upload to your VKontakte album."));

130
131
    m_settingsBox = new QWidget(this);
    QVBoxLayout *settingsBoxLayout = new QVBoxLayout(m_settingsBox);
132

133
    m_headerLabel = new QLabel(m_settingsBox);
134
135
136
137
138
139
140
141
    m_headerLabel->setWhatsThis(i18n("This is a clickable link to open the "
                                     "VKontakte service in a web browser."));
    m_headerLabel->setOpenExternalLinks(true);
    m_headerLabel->setFocusPolicy(Qt::NoFocus);

    /*
     * Account box
     */
142
    m_accountBox = new QGroupBox(i18n("Account"), m_settingsBox);
143
144
145
146
147
148
149
150
151
152
153
    m_accountBox->setWhatsThis(i18n("This account is used for authentication."));

    QGridLayout* accountBoxLayout = new QGridLayout(m_accountBox);
    QLabel* loginDescLabel = new QLabel(m_accountBox);
    loginDescLabel->setText(i18n("Name:"));
    loginDescLabel->setWhatsThis(i18n("Your VKontakte login"));

    m_loginLabel = new QLabel(m_accountBox);
    m_changeUserButton = new KPushButton(
        KGuiItem(i18n("Change Account"), "system-switch-user",
                 i18n("Change VKontakte account used to authenticate")), m_accountBox);
Alexander Potashev's avatar
Alexander Potashev committed
154
    m_changeUserButton->hide(); // changing account does not work anyway
155
156
157
158
159
160
161
162

    accountBoxLayout->addWidget(loginDescLabel, 0, 0);
    accountBoxLayout->addWidget(m_loginLabel, 0, 1);

    accountBoxLayout->addWidget(m_changeUserButton, 1, 1);
    accountBoxLayout->setSpacing(KDialog::spacingHint());
    accountBoxLayout->setMargin(KDialog::spacingHint());

Alexander Potashev's avatar
Alexander Potashev committed
163
    connect(m_changeUserButton, SIGNAL(clicked()),
Gilles Caulier's avatar
Gilles Caulier committed
164
            this, SLOT(slotChangeUserClicked()));
165
166
167
168

    /*
     * Album box
     */
169
170
    m_albumsBox = new AlbumChooserWidget(this, m_vkapi);
    m_albumsBox->selectAlbum(m_albumToSelect);
171
172
173

    // ------------------------------------------------------------------------

174
    QGroupBox* uploadBox         = new QGroupBox(i18n("Destination"), m_settingsBox);
Gilles Caulier's avatar
Gilles Caulier committed
175
    uploadBox->setWhatsThis(i18n("This is the location where VKontakte images will be downloaded."));
176
    QVBoxLayout* uploadBoxLayout = new QVBoxLayout(uploadBox);
Gilles Caulier's avatar
Gilles Caulier committed
177
    m_uploadWidget               = iface()->uploadWidget(uploadBox);
178
179
180
181
    uploadBoxLayout->addWidget(m_uploadWidget);

    // ------------------------------------------------------------------------

Alexander Potashev's avatar
Alexander Potashev committed
182
#if 0
183
    QGroupBox *optionsBox = new QGroupBox(i18n("Options"), m_settingsBox);
184
185
    optionsBox->setWhatsThis(
        i18n("These are options that will be applied to images before upload."));
Alexander Potashev's avatar
Alexander Potashev committed
186
#endif
187

188
//     m_checkKeepOriginal = new QCheckBox(i18n("Save in high resolution"), m_settingsBox); // store state in kipirc
189

Alexander Potashev's avatar
Alexander Potashev committed
190
//     QVBoxLayout *optionsBoxLayout = new QVBoxLayout(optionsBox);
191
192
//     optionsBoxLayout->addWidget(m_checkKeepOriginal);

193
    m_progressBar = new KPProgressWidget(m_settingsBox);
194
195
196
197
198
199
200
201
202
203
    m_progressBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
    m_progressBar->hide();

    /*
     * Layouts
     */
    settingsBoxLayout->addWidget(m_headerLabel);
    settingsBoxLayout->addWidget(m_accountBox);
    settingsBoxLayout->addWidget(m_albumsBox);
    settingsBoxLayout->addWidget(uploadBox);
Alexander Potashev's avatar
Alexander Potashev committed
204
//     settingsBoxLayout->addWidget(optionsBox);
205
    settingsBoxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
206
207
208
209
210
    settingsBoxLayout->addWidget(m_progressBar);
    settingsBoxLayout->setSpacing(KDialog::spacingHint());
    settingsBoxLayout->setMargin(KDialog::spacingHint());

    mainLayout->addWidget(m_imgList);
211
    mainLayout->addWidget(m_settingsBox);
212
213
214
215
216
    mainLayout->setSpacing(KDialog::spacingHint());
    mainLayout->setMargin(0);

    setMainWidget(m_mainWidget);
    setWindowIcon(KIcon("vkontakte"));
Alexander Potashev's avatar
cleanup    
Alexander Potashev committed
217
    setButtons(KDialog::Help | KDialog::User1 | KDialog::Close);
218
219
220
221
222
    setDefaultButton(Close);
    setModal(false);

    if (!m_import)
    {
Alexander Potashev's avatar
Alexander Potashev committed
223
        setWindowTitle(i18nc("@title:window", "Export to VKontakte Web Service"));
224
225
226
227
228
229
230
231
232
233
        setButtonGuiItem(KDialog::User1,
                         KGuiItem(i18n("Start Upload"), "network-workgroup",
                                  i18n("Start upload to VKontakte service")));
        setMinimumSize(700, 520);
        uploadBox->hide();
    }
    else
    {
        // TODO: import support
        m_imgList->hide();
Alexander Potashev's avatar
Alexander Potashev committed
234
//         optionsBox->hide();
235
236
    }

Gilles Caulier's avatar
Gilles Caulier committed
237
238
239
240
241
242
243
244
245
246
    KPAboutData* about = new KPAboutData(ki18n("VKontakte Plugin"),
                                         0,
                                         KAboutData::License_GPL,
                                         ki18n("A Kipi plugin to export image collections to "
                                               "VKontakte web service."),
                                         ki18n("(c) 2007-2009, Vardhman Jain\n"
                                               "(c) 2008-2010, Gilles Caulier\n"
                                               "(c) 2009, Luka Renko\n"
                                               "(c) 2010, Roman Tsisyk\n"
                                               "(c) 2011, Alexander Potashev"));
247

Alexander Potashev's avatar
cleanup    
Alexander Potashev committed
248
    about->addAuthor(ki18n("Alexander Potashev"), ki18n("Author"), "aspotashev@gmail.com");
249

250
    about->setHandbookEntry("VKontakte");
Gilles Caulier's avatar
Gilles Caulier committed
251
    setAboutData(about);
252
253
254
255

    /*
     * UI slots
     */
Gilles Caulier's avatar
Gilles Caulier committed
256
257
258
    connect(this, SIGNAL(user1Clicked()),
            this, SLOT(slotStartTransfer()));

259
    connect(m_vkapi, SIGNAL(authenticated()),
Gilles Caulier's avatar
Gilles Caulier committed
260
            this, SLOT(startAlbumsUpdate()));
261

262
    connect(m_vkapi, SIGNAL(authenticated()),
Gilles Caulier's avatar
Gilles Caulier committed
263
            this, SLOT(startGetFullName()));
264

265
    connect(m_vkapi, SIGNAL(authenticated()),
Gilles Caulier's avatar
Gilles Caulier committed
266
            this, SLOT(startGetUserId()));
267
268

    // for startReactivation()
269
    connect(m_vkapi, SIGNAL(authenticated()),
Gilles Caulier's avatar
Gilles Caulier committed
270
            this, SLOT(show()));
271
272
273
274
275
276
277
278
279
280
281
282
283
284

    /*
     * Dialog update slots
     */
    connect(this, SIGNAL(signalUpdateBusyStatus(bool)),
            this, SLOT(updateBusyStatus(bool)));

    connect(this, SIGNAL(signalUpdateBusyStatus(bool)),
            this, SLOT(updateBusyStatus(bool)));

    connect(this, SIGNAL(signalUpdateAuthInfo()),
            this, SLOT(updateAuthInfo()));

    updateBusyStatus(true);
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
}

VkontakteWindow::~VkontakteWindow()
{
    reset();
}

void VkontakteWindow::startReactivation()
{
    m_imgList->loadImagesFromCurrentSelection();

    reset();
    startAuthentication(false); // show() will be called after that
}

void VkontakteWindow::reset()
{
302
303
    emit signalUpdateBusyStatus(false);
    emit signalUpdateAuthInfo();
304
305
}

306
void VkontakteWindow::updateBusyStatus(bool busy)
307
{
308
309
    if (m_albumsBox)
        m_albumsBox->setEnabled(!busy && m_vkapi->isAuthenticated());
310

311
312
    if (!busy)
    {
313
        setCursor(Qt::ArrowCursor);
314
        enableButton(User1, m_vkapi->isAuthenticated());
315
316
317
318
319
320
321
322
323
324
325
326
327
328
        setButtonGuiItem(KDialog::Close,
                         KGuiItem(i18n("Close"), "dialog-close",
                                  i18n("Close window")));
    }
    else
    {
        setCursor(Qt::WaitCursor);
        enableButton(User1, false);
        setButtonGuiItem(KDialog::Close,
                         KGuiItem(i18n("Cancel"), "dialog-cancel",
                                  i18n("Cancel current operation")));
    }
}

329
void VkontakteWindow::updateAuthInfo()
330
331
332
333
{
    QString loginText;
    QString urlText;

334
    if (m_vkapi->isAuthenticated())
335
    {
336
        loginText = m_userFullName;
337
338
        if (m_albumsBox)
            m_albumsBox->setEnabled(true);
339
340
341
342
    }
    else
    {
        loginText = i18n("Unauthorized");
343
344
        if (m_albumsBox)
            m_albumsBox->clearList();
345
346
    }

347
    if (m_vkapi->isAuthenticated() && m_userId != -1)
348
349
350
351
        urlText = QString("http://vkontakte.ru/albums%1").arg(m_userId);
    else
        urlText = "http://vkontakte.ru/";

352
353
    m_loginLabel->setText(QString("<b>%1</b>").arg(loginText));
    m_headerLabel->setText(
Alexander Potashev's avatar
cleanup    
Alexander Potashev committed
354
        QString("<b><h2><a href=\"%1\"><font color=\"black\">%2</font></a></h2></b>")
355
356
357
            .arg(urlText).arg(i18n("VKontakte")));
}

Gilles Caulier's avatar
Gilles Caulier committed
358
//---------------------------------------------------------------------------
359
360
361
362
363
364

void VkontakteWindow::readSettings()
{
    KConfig config("kipirc");
    KConfigGroup grp = config.group("VKontakte Settings");

Gilles Caulier's avatar
Gilles Caulier committed
365
    m_appId         = grp.readEntry("VkAppId", "2446321");
366
    m_albumToSelect = grp.readEntry("SelectedAlbumId", -1);
367
    m_vkapi->setInitialAccessToken(grp.readEntry("AccessToken", ""));
368
}
369

370
371
372
373
374
375
376
void VkontakteWindow::writeSettings()
{
    KConfig config("kipirc");
    KConfigGroup grp = config.group("VKontakte Settings");

    grp.writeEntry("VkAppId", m_appId);

377
378
    if (!m_vkapi->accessToken().isEmpty())
        grp.writeEntry("AccessToken", m_vkapi->accessToken());
379

380
    Vkontakte::AlbumInfoPtr album = m_albumsBox->currentAlbum();
Gilles Caulier's avatar
Gilles Caulier committed
381

382
    if (album.isNull())
383
        grp.deleteEntry("SelectedAlbumId");
384
385
    else
        grp.writeEntry("SelectedAlbumId", album->aid());
386
}
387

Gilles Caulier's avatar
Gilles Caulier committed
388
//---------------------------------------------------------------------------
389
390
391
392
393
394
395
396
397
398
399
400

QString VkontakteWindow::getDestinationPath() const
{
    return m_uploadWidget->selectedImageCollection().uploadPath().path();
}

void VkontakteWindow::slotChangeUserClicked()
{
    // force authenticate window
    startAuthentication(true);
}

401
void VkontakteWindow::slotFinished()
402
{
403
    writeSettings();
404
405
406
407
408
409
410
411
412
413
414
    reset();
}

void VkontakteWindow::slotButtonClicked(int button)
{
    switch (button)
    {
        case KDialog::User1:
            slotStartTransfer();
            break;
        case KDialog::Close:
415
416
            // TODO: grab better code from picasawebexport/picasawebwindow.cpp:219
            reset();
417
418
419
420
421
        default:
            KDialog::slotButtonClicked(button);
    }
}

Alexander Potashev's avatar
Alexander Potashev committed
422
void VkontakteWindow::startAuthentication(bool forceLogout)
423
{
Alexander Potashev's avatar
Alexander Potashev committed
424
    m_userFullName.clear();
425
    m_userId = -1;
426
427
    if (m_albumsBox)
        m_albumsBox->setEnabled(false);
428

429
    m_vkapi->startAuthentication(forceLogout);
430
431
}

Gilles Caulier's avatar
Gilles Caulier committed
432
//---------------------------------------------------------------------------
433
434
435

void VkontakteWindow::handleVkError(KJob *kjob)
{
436
    KMessageBox::error(this, kjob->errorText(), i18nc("@title:window", "Request to VKontakte failed"));
437
438
}

Gilles Caulier's avatar
Gilles Caulier committed
439
//---------------------------------------------------------------------------
Alexander Potashev's avatar
cleanup    
Alexander Potashev committed
440

441
442
void VkontakteWindow::startGetFullName()
{
443
    Vkontakte::GetVariableJob* job = new Vkontakte::GetVariableJob(m_vkapi->accessToken(), 1281);
Gilles Caulier's avatar
Gilles Caulier committed
444
445
446
447

    connect(job, SIGNAL(result(KJob*)),
            this, SLOT(slotGetFullNameDone(KJob*)));

448
449
450
451
452
453
    m_jobs.append(job);
    job->start();
}

void VkontakteWindow::slotGetFullNameDone(KJob *kjob)
{
454
    SLOT_JOB_DONE_INIT(Vkontakte::GetVariableJob)
455
456

    m_userFullName = job->variable().toString();
457
    updateAuthInfo();
458
459
}

Gilles Caulier's avatar
Gilles Caulier committed
460
//---------------------------------------------------------------------------
461

462
463
void VkontakteWindow::startGetUserId()
{
464
    Vkontakte::GetVariableJob* job = new Vkontakte::GetVariableJob(m_vkapi->accessToken(), 1280);
Gilles Caulier's avatar
Gilles Caulier committed
465
466
467
468

    connect(job, SIGNAL(result(KJob*)),
            this, SLOT(slotGetUserIdDone(KJob*)));

469
470
471
472
473
474
    m_jobs.append(job);
    job->start();
}

void VkontakteWindow::slotGetUserIdDone(KJob *kjob)
{
475
    SLOT_JOB_DONE_INIT(Vkontakte::GetVariableJob)
476
477

    m_userId = job->variable().toInt();
478
    emit signalUpdateAuthInfo();
479
480
}

Gilles Caulier's avatar
Gilles Caulier committed
481
//---------------------------------------------------------------------------
482

483
484
void VkontakteWindow::slotStartTransfer()
{
485
486
    Vkontakte::AlbumInfoPtr album = m_albumsBox->currentAlbum();
    if (album.isNull())
487
    {
488
        // TODO: offer the user to create an album if there are no albums yet
489
        KMessageBox::information(this, i18n("Please select album first."));
490
491
492
493
494
495
        return;
    }

    // TODO: import support
    if (!m_import)
    {
496
        emit signalUpdateBusyStatus(true);
497
498

        QStringList files;
Gilles Caulier's avatar
Gilles Caulier committed
499
        foreach(const KUrl& url, m_imgList->imageUrls(true))
500
501
            files.append(url.toLocalFile());

Gilles Caulier's avatar
Gilles Caulier committed
502
        Vkontakte::UploadPhotosJob* job = new Vkontakte::UploadPhotosJob(
503
            m_vkapi->accessToken(), files, false /*m_checkKeepOriginal->isChecked()*/, album->aid());
Gilles Caulier's avatar
Gilles Caulier committed
504
505
506
507
508
509
510

        connect(job, SIGNAL(result(KJob*)),
                this, SLOT(slotPhotoUploadDone(KJob*)));

        connect(job, SIGNAL(progress(int)),
                m_progressBar, SLOT(setValue(int)));

511
512
513
        m_jobs.append(job);
        job->start();
    }
Alexander Potashev's avatar
Alexander Potashev committed
514
515

    m_progressBar->show();
smit mehta's avatar
Polish    
smit mehta committed
516
517
    m_progressBar->progressScheduled(i18n("Vkontakte Export"), false, true);
    m_progressBar->progressThumbnailChanged(KIcon("kipi").pixmap(22, 22));
518
519
520
521
}

void VkontakteWindow::slotPhotoUploadDone(KJob *kjob)
{
522
    SLOT_JOB_DONE_INIT(Vkontakte::UploadPhotosJob)
523

Alexander Potashev's avatar
Alexander Potashev committed
524
    m_progressBar->hide();
smit mehta's avatar
Polish    
smit mehta committed
525
    m_progressBar->progressCompleted();
526
    emit signalUpdateBusyStatus(false);
527
528
529
}

} // namespace KIPIVkontaktePlugin