/* ============================================================ * * This file is a part of kipi-plugins project * http://www.kipi-plugins.org * * Date : 2010-01-07 * Description : a plugin to export to a remote Piwigo server. * * Copyright (C) 2003-2005 by Renchi Raju * Copyright (C) 2006 by Colin Guthrie * Copyright (C) 2006-2009 by Gilles Caulier * Copyright (C) 2008 by Andrea Diamantini * Copyright (C) 2010 by Frederic Coiffier * * 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. * * ============================================================ */ #include "piwigowindow.h" #include "piwigowindow.moc" // Qt includes #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // LibKIPI includes #include // Local includes #include "albumdlg.h" #include "piwigos.h" #include "piwigoconfig.h" #include "piwigoitem.h" #include "piwigotalker.h" #include "imagedialog.h" #include "kpaboutdata.h" namespace KIPIPiwigoExportPlugin { class PiwigoWindow::Private { public: Private(PiwigoWindow* parent); QWidget* widget; QTreeWidget* albumView; QPushButton* newAlbumBtn; QPushButton* addPhotoBtn; QCheckBox* captTitleCheckBox; QCheckBox* captDescrCheckBox; QCheckBox* resizeCheckBox; QSpinBox* dimensionSpinBox; QSpinBox* thumbDimensionSpinBox; QHash albumDict; KUrlLabel* logo; }; PiwigoWindow::Private::Private(PiwigoWindow* parent) { widget = new QWidget(parent); parent->setMainWidget(widget); parent->setModal(false); QHBoxLayout *hlay = new QHBoxLayout(widget); // --------------------------------------------------------------------------- logo = new KUrlLabel; logo->setText(QString()); logo->setUrl("http://piwigo.menalto.com"); logo->setPixmap(QPixmap(KStandardDirs::locate("data", "kipiplugin_piwigoexport/pics/piwigo_logo.png"))); logo->setAlignment(Qt::AlignLeft); // --------------------------------------------------------------------------- albumView = new QTreeWidget; QStringList labels; labels << i18n("Albums"); // << i18n("ID"); albumView->setHeaderLabels(labels); // --------------------------------------------------------------------------- QFrame *optionFrame = new QFrame; QVBoxLayout *vlay = new QVBoxLayout(); newAlbumBtn = new QPushButton; newAlbumBtn->setText(i18n("&New Album")); newAlbumBtn->setIcon(KIcon("folder-new")); newAlbumBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); newAlbumBtn->setEnabled(false); addPhotoBtn = new QPushButton; addPhotoBtn->setText(i18n("&Add Selected Photos")); addPhotoBtn->setIcon(KIcon("list-add")); addPhotoBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); addPhotoBtn->setEnabled(false); QGroupBox *optionsBox = new QGroupBox(i18n("Override Default Options")); QVBoxLayout *vlay2 = new QVBoxLayout(); captTitleCheckBox = new QCheckBox(optionsBox); captTitleCheckBox->setText(i18n("EXIF Comment (instead of file name) sets Title")); captDescrCheckBox = new QCheckBox(optionsBox); captDescrCheckBox->setText(i18n("EXIF Comment (instead of file name) sets Comment")); resizeCheckBox = new QCheckBox(optionsBox); resizeCheckBox->setText(i18n("Resize photos before uploading")); QHBoxLayout *hlay2 = new QHBoxLayout; QLabel *resizeLabel = new QLabel(i18n("Maximum dimension:")); dimensionSpinBox = new QSpinBox; dimensionSpinBox->setRange(1,1600); dimensionSpinBox->setValue(600); QHBoxLayout *hlay3 = new QHBoxLayout; QLabel *resizeThumbLabel= new QLabel(i18n("Maximum thumbnail dimension:")); thumbDimensionSpinBox = new QSpinBox; thumbDimensionSpinBox->setRange(32,800); thumbDimensionSpinBox->setValue(120); captTitleCheckBox->setChecked(true); captDescrCheckBox->setChecked(false); resizeCheckBox->setChecked(false); dimensionSpinBox->setEnabled(false); thumbDimensionSpinBox->setEnabled(true); // --------------------------------------------------------------------------- hlay2->addWidget(resizeLabel); hlay2->addWidget(dimensionSpinBox); hlay2->setSpacing(KDialog::spacingHint()); hlay2->setMargin(KDialog::spacingHint()); // --------------------------------------------------------------------------- hlay3->addWidget(resizeThumbLabel); hlay3->addWidget(thumbDimensionSpinBox); hlay3->setSpacing(KDialog::spacingHint()); hlay3->setMargin(KDialog::spacingHint()); // --------------------------------------------------------------------------- vlay2->addWidget(captTitleCheckBox); vlay2->addWidget(captDescrCheckBox); vlay2->addWidget(resizeCheckBox); vlay2->addLayout(hlay2); vlay2->addLayout(hlay3); vlay2->setSpacing(KDialog::spacingHint()); vlay2->setMargin(KDialog::spacingHint()); optionsBox->setLayout(vlay2); // --------------------------------------------------------------------------- vlay->addWidget(newAlbumBtn); vlay->addWidget(addPhotoBtn); vlay->addWidget(optionsBox); vlay->setSpacing(KDialog::spacingHint()); vlay->setMargin(KDialog::spacingHint()); optionFrame->setLayout(vlay); // --------------------------------------------------------------------------- hlay->addWidget(logo); hlay->addWidget(albumView); hlay->addWidget(optionFrame); hlay->setSpacing(KDialog::spacingHint()); hlay->setMargin(KDialog::spacingHint()); widget->setLayout(hlay); } // -------------------------------------------------------------------------------------------------------------- PiwigoWindow::PiwigoWindow(KIPI::Interface* interface, QWidget *parent, Piwigo* pPiwigo) : KDialog(parent), m_interface(interface), mpPiwigo(pPiwigo), d(new Private(this)) { kDebug() << "BEGIN Window\n"; setWindowTitle( i18n("Piwigo Export") ); setButtons( KDialog::Close | KDialog::User1 | KDialog::Help); setModal(false); // About data. m_about = new KIPIPlugins::KPAboutData(ki18n("Piwigo Export"), 0, KAboutData::License_GPL, ki18n("A Kipi plugin to export image collections to a remote Piwigo server."), ki18n("(c) 2003-2005, Renchi Raju\n" "(c) 2006-2007, Colin Guthrie\n" "(c) 2006-2009, Gilles Caulier\n" "(c) 2008, Andrea Diamantini\n" "(c) 2010, Frédéric Coiffier\n")); m_about->addAuthor(ki18n("Renchi Raju"), ki18n("Author"), "renchi dot raju at gmail dot com"); m_about->addAuthor(ki18n("Colin Guthrie"), ki18n("Maintainer"), "kde at colin dot guthr dot ie"); m_about->addAuthor(ki18n("Andrea Diamantini"), ki18n("Developer"), "adjam7 at gmail dot com"); m_about->addAuthor(ki18n("Gilles Caulier"), ki18n("Developer"), "caulier dot gilles at gmail dot com"); m_about->addAuthor(ki18n("Frédéric Coiffier"), ki18n("Developer"), "fcoiffie at gmail dot com"); // help button disconnect(this, SIGNAL(helpClicked()), this, SLOT(slotHelp())); KHelpMenu *helpMenu = new KHelpMenu(this, m_about, false); helpMenu->menu()->removeAction(helpMenu->menu()->actions().first()); QAction *handbook = new QAction(i18n("Handbook"), this); connect(handbook, SIGNAL(triggered(bool)), this, SLOT(slotHelp())); helpMenu->menu()->insertAction(helpMenu->menu()->actions().first(), handbook); button(Help)->setMenu(helpMenu->menu()); // User1 Button : to conf piwigo settings KPushButton *confButton = button( User1 ); confButton->setText( i18n("Settings") ); confButton->setIcon( KIcon("configure") ); connect(confButton, SIGNAL(clicked()), this, SLOT(slotSettings() ) ); // we need to let m_talker work.. m_talker = new PiwigoTalker(d->widget); // setting progressDlg and its numeric hints m_progressDlg = new QProgressDialog(this); m_progressDlg->setModal(true); m_progressDlg->setAutoReset(true); m_progressDlg->setAutoClose(true); m_uploadCount = 0; m_uploadTotal = 0; mpUploadList = new QStringList; // connect functions connectSignals(); // read Settings readSettings(); slotDoLogin(); } PiwigoWindow::~PiwigoWindow() { // write config KConfig config("kipirc"); KConfigGroup group = config.group("PiwigoSync Galleries"); group.writeEntry("Resize", d->resizeCheckBox->isChecked()); group.writeEntry("Set title", d->captTitleCheckBox->isChecked()); group.writeEntry("Set description", d->captDescrCheckBox->isChecked()); group.writeEntry("Maximum Width", d->dimensionSpinBox->value()); delete m_talker; delete mpUploadList; delete m_about; delete d; } void PiwigoWindow::connectSignals() { connect(d->albumView, SIGNAL(itemSelectionChanged()), this , SLOT( slotAlbumSelected() ) ); connect(d->newAlbumBtn, SIGNAL(clicked()), this, SLOT(slotNewAlbum())); connect(d->addPhotoBtn, SIGNAL(clicked()), this, SLOT(slotAddPhoto())); connect(d->resizeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotEnableSpinBox(int))); connect(d->logo, SIGNAL(leftClickedUrl(const QString&)), this, SLOT(slotProcessUrl(const QString&))); connect(m_progressDlg, SIGNAL( canceled() ), this, SLOT( slotAddPhotoCancel() )); connect(m_talker, SIGNAL(signalError(const QString&)), this, SLOT(slotError(const QString&))); connect(m_talker, SIGNAL(signalBusy(bool)), this, SLOT(slotBusy(bool))); connect(m_talker, SIGNAL(signalLoginFailed(const QString&)), this, SLOT(slotLoginFailed(const QString&))); connect(m_talker, SIGNAL(signalAlbums(const QList&)), this, SLOT(slotAlbums(const QList&))); connect(m_talker, SIGNAL(signalPhotos(const QList&)), this, SLOT(slotPhotos(const QList&))); connect(m_talker, SIGNAL(signalAddPhotoSucceeded()), this, SLOT(slotAddPhotoSucceeded())); connect(m_talker, SIGNAL(signalAddPhotoFailed(const QString&)), this, SLOT(slotAddPhotoFailed(const QString&))); } void PiwigoWindow::slotProcessUrl(const QString& url) { KToolInvocation::self()->invokeBrowser(url); } void PiwigoWindow::readSettings() { // read Config KConfig config("kipirc"); KConfigGroup group = config.group("PiwigoSync Galleries"); if (group.readEntry("Resize", false)) { d->resizeCheckBox->setChecked(true); d->dimensionSpinBox->setEnabled(true); d->dimensionSpinBox->setValue(group.readEntry("Maximum Width", 600)); } else { d->resizeCheckBox->setChecked(false); d->dimensionSpinBox->setEnabled(false); } if (group.readEntry("Set title", true)) d->captTitleCheckBox->setChecked(true); else d->captTitleCheckBox->setChecked(false); if (group.readEntry("Set description", false)) d->captDescrCheckBox->setChecked(true); else d->captDescrCheckBox->setChecked(false); } void PiwigoWindow::slotHelp() { KToolInvocation::invokeHelp("piwigoexport", "kipi-plugins"); } void PiwigoWindow::slotDoLogin() { KUrl url(mpPiwigo->url()); if (url.protocol().isEmpty()) { url.setProtocol("http"); url.setHost(mpPiwigo->url()); } if (!url.url().endsWith(QLatin1String(".php"))) { url.addPath("ws.php"); } // If we've done something clever, save it back to the piwigo. if (mpPiwigo->url() != url.url()) { mpPiwigo->setUrl(url.url()); mpPiwigo->save(); } m_talker->login(url.url(), mpPiwigo->username(), mpPiwigo->password()); } void PiwigoWindow::slotLoginFailed(const QString& msg) { if (KMessageBox::warningYesNo(this, i18n("Failed to login into remote piwigo. ") + msg + i18n("\nDo you want to check your settings and try again?")) != KMessageBox::Yes) { close(); return; } QPointer configDlg = new PiwigoEdit(kapp->activeWindow(), mpPiwigo, i18n("Edit Piwigo Data") ); if ( configDlg->exec() != QDialog::Accepted ) { delete configDlg; return; } slotDoLogin(); delete configDlg; } void PiwigoWindow::slotBusy(bool val) { if (val) { setCursor(Qt::WaitCursor); d->newAlbumBtn->setEnabled(false); d->addPhotoBtn->setEnabled(false); } else { setCursor(Qt::ArrowCursor); bool loggedIn = m_talker->loggedIn(); // TODO Enable the button to implement the feature //d->newAlbumBtn->setEnabled(loggedIn); d->addPhotoBtn->setEnabled(loggedIn && d->albumView->currentItem()); } } void PiwigoWindow::slotError(const QString& msg) { m_progressDlg->hide(); KMessageBox::error(this, msg); } void PiwigoWindow::slotAlbums(const QList& albumList) { d->albumDict.clear(); d->albumView->clear(); // album work list QList workList(albumList); QList parentItemList; // fill QTreeWidget while ( !workList.isEmpty() ) { // the album to work on GAlbum album = workList.takeFirst(); int parentRefNum = album.parent_ref_num; if ( parentRefNum == -1 ) { QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(0, cleanName(album.name) ); item->setIcon(0, KIcon("inode-directory") ); item->setData(1, Qt::UserRole, QVariant(album.ref_num) ); item->setText(2, i18n("Album") ); kDebug() << "Top : " << album.name << " " << album.ref_num << "\n"; d->albumView->addTopLevelItem(item); d->albumDict.insert(album.name, album); parentItemList << item; } else { QTreeWidgetItem *parentItem = 0; bool found = false; int i = 0; kDebug() << "Parent : " << parentRefNum << "\n"; while ( !found && i < parentItemList.size() ) { parentItem = parentItemList.at(i); kDebug() << "User : " << parentItem->data(1, Qt::UserRole).toInt() << "\n"; if (parentItem && (parentItem->data(1, Qt::UserRole).toInt() == parentRefNum)) { QTreeWidgetItem *item = new QTreeWidgetItem(parentItem); item->setText(0, cleanName(album.name) ); item->setIcon(0, KIcon("inode-directory") ); item->setData(1, Qt::UserRole, album.ref_num ); item->setText(2, i18n("Album") ); kDebug() << "Child : " << album.name << " " << album.ref_num << "\n"; parentItem->addChild(item); d->albumDict.insert(album.name, album); parentItemList << item; found = true; } i++; } /*if (!found && i == parentItemList.size() ) { workList.append(album); }*/ } } } // FIXME: avoid duplications void PiwigoWindow::slotPhotos(const QList& photoList) { QTreeWidgetItem* parentItem = d->albumView->currentItem(); typedef QList GPhotoList; GPhotoList::const_iterator iterator; for (iterator = photoList.begin(); iterator != photoList.end(); ++iterator) { QString plain = (*iterator).caption; QTreeWidgetItem *item = new QTreeWidgetItem(parentItem); item->setText(0, cleanName(plain) ); item->setIcon(0, KIcon("image-x-generic") ); item->setText(1, (*iterator).name); item->setText(2, i18n("Image") ); } } void PiwigoWindow::slotAlbumSelected() { QTreeWidgetItem* item = d->albumView->currentItem(); kDebug() << "slotAlbumSelected\n"; // stop loading if user clicked an image if ( item->text(2) == i18n("Image") ) return; kDebug() << "Album\n"; if (!item) { d->addPhotoBtn->setEnabled(false); } else { kDebug() << "Album selected\n"; int albumId = item->data(1, Qt::UserRole).toInt(); kDebug() << albumId << "\n"; if (m_talker->loggedIn() && albumId ) { d->addPhotoBtn->setEnabled(true); //m_talker->listPhotos(albumName); } else { d->addPhotoBtn->setEnabled(false); } } } void PiwigoWindow::slotNewAlbum() { QPointer dlg = new AlbumDlg(d->widget); dlg->titleEdit->setFocus(); if ( dlg->exec() != QDialog::Accepted ) { delete dlg; return; } QString name = dlg->nameEdit->text(); QString title = dlg->titleEdit->text(); QString caption = dlg->captionEdit->text(); delete dlg; // check for prohibited chars in the album name // \ / * ? " ' & < > | . + # ( ) or spaces // TODO: Change this to a QRegExp check. QChar ch; bool clean = true; for (int i = 0; i < name.length(); ++i) { ch = name[i]; if (ch == '\\') { clean = false; break; } else if (ch == '/') { clean = false; break; } else if (ch == '*') { clean = false; break; } else if (ch == '?') { clean = false; break; } else if (ch == '"') { clean = false; break; } else if (ch == '\'') { clean = false; break; } else if (ch == '&') { clean = false; break; } else if (ch == '<') { clean = false; break; } else if (ch == '>') { clean = false; break; } else if (ch == '|') { clean = false; break; } else if (ch == '.') { clean = false; break; } else if (ch == '+') { clean = false; break; } else if (ch == '#') { clean = false; break; } else if (ch == '(') { clean = false; break; } else if (ch == ')') { clean = false; break; } /* else if (ch == ' ') { clean = false; break; } */ } if (!clean) { KMessageBox::error(this, i18n("Sorry, these characters are not allowed in album name: \\ / * ? \" \' & < > | . + # ( ) or spaces")); return; } QString parentAlbumName; QTreeWidgetItem* item = d->albumView->currentItem(); int column = d->albumView->currentColumn(); if (item) { const GAlbum& album = d->albumDict.value( item->text(column) ); parentAlbumName = album.name; m_talker->createAlbum( parentAlbumName, name, title, caption); } else { m_talker->createAlbum( firstAlbumName, name, title, caption ); } } void PiwigoWindow::slotAddPhoto() { #if 0 QTreeWidgetItem* item = d->albumView->currentItem(); int column = d->albumView->currentColumn(); if (!item) return; // NO album selected: FIXME: do something // albumName QString albumTitle = item->text(column); if (!d->albumDict.contains(albumTitle)) return; // NO album name found: FIXME: do something // photoPath KUrl::List urls = KIPIPlugins::ImageDialog::getImageUrls(this, m_interface); if (urls.isEmpty()) return; // NO photo selected: FIXME: do something for (KUrl::List::iterator it = urls.begin(); it != urls.end(); ++it) { mpUploadList->append( (*it).path() ); } m_uploadTotal = mpUploadList->count(); m_progressDlg->reset(); m_progressDlg->setMaximum(m_uploadTotal); m_uploadCount = 0; slotAddPhotoNext(); #endif KUrl::List *urls = new KUrl::List(m_interface->currentSelection().images()); if (urls == NULL || urls->isEmpty()) { KMessageBox::error(this, i18n("Nothing to upload - please select photos to upload.")); return; } for (KUrl::List::iterator it = urls->begin(); it != urls->end(); ++it) { mpUploadList->append( (*it).path() ); } m_uploadTotal = mpUploadList->count(); m_progressDlg->reset(); m_progressDlg->setMaximum(m_uploadTotal); m_uploadCount = 0; slotAddPhotoNext(); } void PiwigoWindow::slotAddPhotoNext() { if ( mpUploadList->isEmpty() ) { m_progressDlg->reset(); m_progressDlg->hide(); //slotAlbumSelected(); // ? return; } QTreeWidgetItem* item = d->albumView->currentItem(); int column = d->albumView->currentColumn(); QString albumTitle = item->text(column); const GAlbum& album = d->albumDict.value(albumTitle); QString photoPath = mpUploadList->takeFirst(); QString photoName = QFileInfo(photoPath).baseName(); bool res = m_talker->addPhoto(album.ref_num, photoPath, photoName, d->captTitleCheckBox->isChecked(), d->captDescrCheckBox->isChecked(), d->resizeCheckBox->isChecked(), d->dimensionSpinBox->value(), d->thumbDimensionSpinBox->value() ); if (!res) { slotAddPhotoFailed( "" ); return; } m_progressDlg->setLabelText( i18n("Uploading file ") + KUrl(photoPath).fileName() ); if (m_progressDlg->isHidden()) m_progressDlg->show(); } void PiwigoWindow::slotAddPhotoSucceeded() { m_uploadCount++; m_progressDlg->setValue(m_uploadCount); slotAddPhotoNext(); } void PiwigoWindow::slotAddPhotoFailed(const QString& msg) { m_progressDlg->reset(); m_progressDlg->hide(); if (KMessageBox::warningContinueCancel(this, i18n("Failed to upload photo into " "remote piwigo. ") + msg + i18n("\nDo you want to continue?")) != KMessageBox::Continue) { } else { slotAddPhotoNext(); } } void PiwigoWindow::slotAddPhotoCancel() { m_progressDlg->reset(); m_progressDlg->hide(); m_talker->cancel(); } void PiwigoWindow::slotEnableSpinBox(int n) { bool b; switch (n) { case 0: b = false; break; case 1: case 2: b = true; break; default: b = false; } d->dimensionSpinBox->setEnabled(b); } void PiwigoWindow::slotSettings() { // TODO: reload albumlist if OK slot used. QPointer dlg = new PiwigoEdit(kapp->activeWindow(), mpPiwigo, i18n("Edit Piwigo Data") ); if ( dlg->exec() == QDialog::Accepted ) { slotDoLogin(); } delete dlg; } QString PiwigoWindow::cleanName(const QString& str) { QString plain = str; plain.replace("<", "<"); plain.replace(">", ">"); plain.replace(""", "\""); plain.replace("&", "&"); return plain; } } // namespace KIPIPiwigoExportPlugin