]> git.sesse.net Git - kdenlive/blob - src/utils/resourcewidget.cpp
8d10c2a563ea77d739a39968d839fc4df706f372
[kdenlive] / src / utils / resourcewidget.cpp
1 /***************************************************************************
2  *   Copyright (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
19  ***************************************************************************/
20
21
22 #include "resourcewidget.h"
23 #include "freesound.h"
24 #include "openclipart.h"
25 #include "archiveorg.h"
26 #include "kdenlivesettings.h"
27
28 #include <QPushButton>
29 #include <QSpinBox>
30 #include <QListWidget>
31 #include <QAction>
32 #include <QMenu>
33
34 #include <KDebug>
35 #include <kdeversion.h>
36 #include <KGlobalSettings>
37 #include <KMessageBox>
38 #include <KFileDialog>
39 #include <kio/job.h>
40 #include <KIO/NetAccess>
41 #include <Solid/Networking>
42 #include <KRun>
43 #include <KConfigGroup>
44
45 #if KDE_IS_VERSION(4,4,0)
46 #include <KPixmapSequence>
47 #include <KPixmapSequenceOverlayPainter>
48 #endif
49 #include <KFileItem>
50
51 #ifdef USE_NEPOMUK
52 #if KDE_IS_VERSION(4,6,0)
53 #include <Nepomuk/Variant>
54 #include <Nepomuk/Resource>
55 #include <Nepomuk/ResourceManager>
56 #include <Nepomuk/Vocabulary/NIE>
57 #include <Nepomuk/Vocabulary/NCO>
58 #include <Nepomuk/Vocabulary/NDO>
59 #endif
60 #endif
61
62 #ifdef USE_NEPOMUKCORE
63 #include <Nepomuk2/Variant>
64 #include <Nepomuk2/Resource>
65 #include <Nepomuk2/ResourceManager>
66 #include <Nepomuk2/Vocabulary/NIE>
67 #include <Nepomuk2/Vocabulary/NCO>
68 #include <Nepomuk2/Vocabulary/NDO>
69 #endif
70
71 ResourceWidget::ResourceWidget(const QString & folder, QWidget * parent) :
72     QDialog(parent),
73     m_folder(folder),
74     m_currentService(NULL)
75 {
76     setFont(KGlobalSettings::toolBarFont());
77     setupUi(this);
78     setAttribute(Qt::WA_DeleteOnClose);
79 #ifdef USE_QJSON
80     service_list->addItem(i18n("Freesound Audio Library"), FREESOUND);
81     service_list->addItem(i18n("Archive.org Video Library"), ARCHIVEORG);
82 #endif
83     service_list->addItem(i18n("Open Clip Art Graphic Library"), OPENCLIPART);
84     setWindowTitle(i18n("Search Online Resources"));
85     info_browser->setStyleSheet(QString("QTextBrowser { background-color: transparent;}"));
86     connect(button_search, SIGNAL(clicked()), this, SLOT(slotStartSearch()));
87     connect(search_results, SIGNAL(currentRowChanged(int)), this, SLOT(slotUpdateCurrentSound()));
88     connect(button_preview, SIGNAL(clicked()), this, SLOT(slotPlaySound()));
89     connect(button_import, SIGNAL(clicked()), this, SLOT(slotSaveItem()));
90     connect(item_license, SIGNAL(leftClickedUrl(QString)), this, SLOT(slotOpenUrl(QString)));
91     connect(service_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeService()));
92     if (Solid::Networking::status() == Solid::Networking::Unconnected) {
93         slotOffline();
94     }
95     connect(Solid::Networking::notifier(), SIGNAL(shouldConnect()), this, SLOT(slotOnline()));
96     connect(Solid::Networking::notifier(), SIGNAL(shouldDisconnect()), this, SLOT(slotOffline()));
97     connect(page_next, SIGNAL(clicked()), this, SLOT(slotNextPage()));
98     connect(page_prev, SIGNAL(clicked()), this, SLOT(slotPreviousPage()));
99     connect(page_number, SIGNAL(valueChanged(int)), this, SLOT(slotStartSearch(int)));
100     connect(info_browser, SIGNAL(anchorClicked(QUrl)), this, SLOT(slotOpenLink(QUrl)));
101     
102     m_autoPlay = new QAction(i18n("Auto Play"), this);
103     m_autoPlay->setCheckable(true);
104     QMenu *resourceMenu = new QMenu;
105     resourceMenu->addAction(m_autoPlay);
106     config_button->setMenu(resourceMenu);
107     config_button->setIcon(KIcon("configure"));
108
109 #if KDE_IS_VERSION(4,4,0)
110     m_busyWidget = new KPixmapSequenceOverlayPainter(this);
111     m_busyWidget->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
112     m_busyWidget->setWidget(search_results->viewport());
113 #endif
114     
115     sound_box->setEnabled(false);
116     search_text->setFocus();
117 #ifdef USE_NEPOMUK
118 #if KDE_IS_VERSION(4,6,0)    
119     Nepomuk::ResourceManager::instance()->init();
120 #endif
121 #endif
122     slotChangeService();
123     loadConfig();
124 }
125
126 ResourceWidget::~ResourceWidget()
127 {
128     delete m_currentService;
129     KIO::NetAccess::removeTempFile(m_tmpThumbFile);
130     saveConfig();
131 }
132
133 void ResourceWidget::loadConfig()
134 {
135     KSharedConfigPtr config = KGlobal::config();
136     KConfigGroup resourceConfig(config, "ResourceWidget");
137     QList<int> size;
138     size << 100 << 400;
139     splitter->setSizes(resourceConfig.readEntry( "mainSplitter", size));
140 }
141
142 void ResourceWidget::saveConfig()
143 {
144     KSharedConfigPtr config = KGlobal::config();
145     KConfigGroup resourceConfig(config, "ResourceWidget");
146     resourceConfig.writeEntry(QLatin1String("mainsplitter"), splitter->size());
147     config->sync();
148 }
149
150 void ResourceWidget::slotStartSearch(int page)
151 {
152     page_number->blockSignals(true);
153     page_number->setValue(page);
154     page_number->blockSignals(false);
155 #if KDE_IS_VERSION(4,4,0)
156     m_busyWidget->start();
157 #endif
158     m_currentService->slotStartSearch(search_text->text(), page);
159 }
160
161 void ResourceWidget::slotUpdateCurrentSound()
162 {
163     KIO::NetAccess::removeTempFile(m_tmpThumbFile);
164     if (!m_autoPlay->isChecked())
165         m_currentService->stopItemPreview(NULL);
166     item_license->clear();
167     m_title.clear();
168     m_image.clear();
169     m_desc.clear();
170     m_meta.clear();
171     QListWidgetItem *item = search_results->currentItem();
172     if (!item) {
173         sound_box->setEnabled(false);
174         return;
175     }
176     m_currentInfo = m_currentService->displayItemDetails(item);
177     
178     if (m_autoPlay->isChecked() && m_currentService->hasPreview)
179         m_currentService->startItemPreview(item);
180     sound_box->setEnabled(true);
181     QString title = "<h3>" + m_currentInfo.itemName;
182     if (!m_currentInfo.infoUrl.isEmpty())
183         title += QString(" (<a href=\"%1\">%2</a>)").arg(m_currentInfo.infoUrl).arg(i18nc("the url link pointing to a web page", "link"));
184     title.append("</h3>");
185     
186     if (!m_currentInfo.authorUrl.isEmpty()) {
187         title += QString("<a href=\"%1\">").arg(m_currentInfo.authorUrl);
188         if (!m_currentInfo.author.isEmpty())
189             title.append(m_currentInfo.author);
190         else title.append(i18n("Author"));
191         title.append("</a><br />");
192     }
193     else if (!m_currentInfo.author.isEmpty())
194         title.append(m_currentInfo.author + "<br />");
195     else
196         title.append("<br />");
197     
198     slotSetTitle(title);
199     if (!m_currentInfo.description.isEmpty()) slotSetDescription(m_currentInfo.description);
200     if (!m_currentInfo.license.isEmpty()) parseLicense(m_currentInfo.license);
201 }
202
203
204 void ResourceWidget::slotLoadThumb(const QString &url)
205 {
206     KUrl img(url);
207     if (img.isEmpty()) return;
208     if (KIO::NetAccess::exists(img, KIO::NetAccess::SourceSide, this)) {
209         if (KIO::NetAccess::download(img, m_tmpThumbFile, this)) {
210             slotSetImage(m_tmpThumbFile);
211             /*QPixmap pix(tmpFile);
212
213             int newHeight = pix.height() * item_image->width() / pix.width();
214             if (newHeight > 200) {
215                 item_image->setScaledContents(false);
216                 //item_image->setFixedHeight(item_image->width());
217             }
218             else {
219                 item_image->setScaledContents(true);
220                 item_image->setFixedHeight(newHeight);
221             }
222             item_image->setPixmap(pix);*/
223         }
224     }
225 }
226
227
228 void ResourceWidget::slotDisplayMetaInfo(const QMap<QString, QString> &metaInfo)
229 {
230     if (metaInfo.contains(QLatin1String("license"))) {
231         parseLicense(metaInfo.value(QLatin1String("license")));
232     }
233     if (metaInfo.contains(QLatin1String("description"))) {
234         slotSetDescription(metaInfo.value(QLatin1String("description")));
235     }
236 }
237
238
239 void ResourceWidget::slotPlaySound()
240 {
241     if (!m_currentService)
242         return;
243     const bool started = m_currentService->startItemPreview(search_results->currentItem());
244     if (started)
245         button_preview->setText(i18n("Preview"));
246     else
247         button_preview->setText(i18n("Stop"));
248 }
249
250
251 void ResourceWidget::slotSaveItem(const QString &originalUrl)
252 {
253     //if (m_currentUrl.isEmpty()) return;
254     QListWidgetItem *item = search_results->currentItem();
255     if (!item) return;
256     QString path = m_folder;
257     QString ext;
258     if (!path.endsWith('/')) path.append('/');
259     if (!originalUrl.isEmpty()) {
260         path.append(KUrl(originalUrl).fileName());
261         ext = "*." + KUrl(originalUrl).fileName().section('.', -1);
262         m_currentInfo.itemDownload = originalUrl;
263     }
264     else {
265         path.append(m_currentService->getDefaultDownloadName(item));
266         ext = m_currentService->getExtension(search_results->currentItem());
267     }
268     QString saveUrl = KFileDialog::getSaveFileName(KUrl(path), ext);
269     KIO::UDSEntry entry;
270     KUrl srcUrl(m_currentInfo.itemDownload);
271     if (saveUrl.isEmpty() || !KIO::NetAccess::stat(srcUrl, entry, this))
272         return;
273     KIO::FileCopyJob * getJob = KIO::file_copy(srcUrl, KUrl(saveUrl), -1, KIO::Overwrite);
274     
275     KFileItem info(entry, srcUrl);
276     getJob->setSourceSize(info.size());
277     getJob->setProperty("license", item_license->text());
278     getJob->setProperty("licenseurl", item_license->url());
279     getJob->setProperty("originurl", m_currentInfo.itemDownload);
280     if (!m_currentInfo.authorUrl.isEmpty()) getJob->setProperty("author", m_currentInfo.authorUrl);
281     else if (!m_currentInfo.author.isEmpty()) getJob->setProperty("author", m_currentInfo.author);
282     connect(getJob, SIGNAL(result(KJob*)), this, SLOT(slotGotFile(KJob*)));
283     getJob->start();
284 }
285
286 void ResourceWidget::slotGotFile(KJob *job)
287 {
288     if (job->error() != 0 ) return;
289     KIO::FileCopyJob* copyJob = static_cast<KIO::FileCopyJob*>( job );
290     const KUrl filePath = copyJob->destUrl();
291 #ifdef USE_NEPOMUK
292 #if KDE_IS_VERSION(4,6,0)
293     Nepomuk::Resource res( filePath );
294     res.setProperty( Nepomuk::Vocabulary::NIE::license(), (Nepomuk::Variant) job->property("license") );
295     res.setProperty( Nepomuk::Vocabulary::NIE::licenseType(), (Nepomuk::Variant) job->property("licenseurl") );
296     res.setProperty( Nepomuk::Vocabulary::NDO::copiedFrom(), (Nepomuk::Variant) job->property("originurl") );
297     res.setProperty( Nepomuk::Vocabulary::NCO::creator(), (Nepomuk::Variant) job->property("author") );
298     //res.setDescription(item_description->toPlainText());
299     //res.setProperty( Soprano::Vocabulary::NAO::description(),
300 #endif
301 #endif
302
303 #ifdef USE_NEPOMUKCORE
304     Nepomuk2::Resource res( filePath );
305     res.setProperty( Nepomuk2::Vocabulary::NIE::license(), (Nepomuk2::Variant) job->property("license") );
306     res.setProperty( Nepomuk2::Vocabulary::NIE::licenseType(), (Nepomuk2::Variant) job->property("licenseurl") );
307     res.setProperty( Nepomuk2::Vocabulary::NDO::copiedFrom(), (Nepomuk2::Variant) job->property("originurl") );
308     res.setProperty( Nepomuk2::Vocabulary::NCO::creator(), (Nepomuk2::Variant) job->property("author") );
309     //res.setDescription(item_description->toPlainText());
310     //res.setProperty( Soprano::Vocabulary::NAO::description(),
311 #endif
312
313     emit addClip(filePath, stringMap());
314 }
315
316 void ResourceWidget::slotOpenUrl(const QString &url)
317 {
318     new KRun(KUrl(url), this);
319 }
320
321 void ResourceWidget::slotChangeService()
322 {
323     delete m_currentService;
324     m_currentService = NULL;
325     SERVICETYPE service = (SERVICETYPE) service_list->itemData(service_list->currentIndex()).toInt();
326     if (service == FREESOUND) {
327         m_currentService = new FreeSound(search_results);
328     } else if (service == OPENCLIPART) {
329         m_currentService = new OpenClipArt(search_results);
330     } else if (service == ARCHIVEORG) {
331         m_currentService = new ArchiveOrg(search_results);
332     }
333
334     connect(m_currentService, SIGNAL(gotMetaInfo(QString)), this, SLOT(slotSetMetadata(QString)));
335     connect(m_currentService, SIGNAL(gotMetaInfo(QMap<QString,QString>)), this, SLOT(slotDisplayMetaInfo(QMap<QString,QString>)));
336     connect(m_currentService, SIGNAL(maxPages(int)), this, SLOT(slotSetMaximum(int)));
337     connect(m_currentService, SIGNAL(searchInfo(QString)), search_info, SLOT(setText(QString)));
338     connect(m_currentService, SIGNAL(gotThumb(QString)), this, SLOT(slotLoadThumb(QString)));
339 #if KDE_IS_VERSION(4,4,0)
340     connect(m_currentService, SIGNAL(searchDone()), m_busyWidget, SLOT(stop()));
341 #endif
342     
343     button_preview->setVisible(m_currentService->hasPreview);
344     button_import->setVisible(!m_currentService->inlineDownload);
345     search_info->setText(QString());
346     if (!search_text->text().isEmpty())
347         slotStartSearch();
348 }
349
350 void ResourceWidget::slotSetMaximum(int max)
351 {
352     page_number->setMaximum(max);
353 }
354
355 void ResourceWidget::slotOnline()
356 {
357     button_search->setEnabled(true);
358     search_info->setText(QString());
359 }
360
361 void ResourceWidget::slotOffline()
362 {
363     button_search->setEnabled(false);
364     search_info->setText(i18n("You need to be online\n for searching"));
365 }
366
367 void ResourceWidget::slotNextPage()
368 {
369     const int ix = page_number->value();
370     if (search_results->count() > 0)
371         page_number->setValue(ix + 1);
372 }
373
374 void ResourceWidget::slotPreviousPage()
375 {
376     const int ix = page_number->value();
377     if (ix > 1)
378         page_number->setValue(ix - 1);
379 }
380
381 void ResourceWidget::parseLicense(const QString &licenseUrl)
382 {
383     QString licenseName;
384     if (licenseUrl.contains("/sampling+/"))
385         licenseName = "Sampling+";
386     else if (licenseUrl.contains("/by/"))
387         licenseName = "Attribution";
388     else if (licenseUrl.contains("/by-nd/"))
389         licenseName = "Attribution-NoDerivs";
390     else if (licenseUrl.contains("/by-nc-sa/"))
391         licenseName = "Attribution-NonCommercial-ShareAlike";
392     else if (licenseUrl.contains("/by-sa/"))
393         licenseName = "Attribution-ShareAlike";
394     else if (licenseUrl.contains("/by-nc/"))
395         licenseName = "Attribution-NonCommercial";
396     else if (licenseUrl.contains("/by-nc-nd/"))
397         licenseName = "Attribution-NonCommercial-NoDerivs";
398     else if (licenseUrl.contains("/publicdomain/zero/"))
399         licenseName = "Creative Commons 0";
400     else if (licenseUrl.endsWith("/publicdomain"))
401         licenseName = "Public Domain";
402     else licenseName = i18n("Unknown");
403     item_license->setText(licenseName);
404     item_license->setUrl(licenseUrl);
405 }
406
407
408 void ResourceWidget::slotOpenLink(const QUrl &url)
409 {
410     QString path = url.toEncoded();
411     if (path.endsWith("_import")) {
412         path.chop(7);
413         // import file in Kdenlive
414         slotSaveItem(path);
415     }
416     else {
417         slotOpenUrl(path);
418     }
419 }
420
421 void ResourceWidget::slotSetDescription(const QString &desc)
422 {
423     if(m_desc != desc) {
424         m_desc = desc;
425         updateLayout();
426     }
427 }
428
429 void ResourceWidget::slotSetMetadata(const QString &desc)
430 {
431     if (m_meta != desc) {
432         m_meta = desc;
433         updateLayout();
434     }
435 }
436
437 void ResourceWidget::slotSetImage(const QString &desc)
438 {
439     m_image = QString::fromLatin1("<img src=\"%1\" style=\"max-height:150px\" max-width=\"%2px\" />").arg(desc).arg((int) (info_browser->width() * 0.9));
440     updateLayout();
441 }
442
443 void ResourceWidget::slotSetTitle(const QString &desc)
444 {
445     if (m_title != desc) {
446         m_title = desc;
447         updateLayout();
448     }
449 }
450
451 void ResourceWidget::updateLayout()
452 {
453     QString content = m_title;
454     if (!m_image.isEmpty())
455         content.append(m_image + "<br clear=\"all\" />");
456     if (!m_desc.isEmpty())
457         content.append(m_desc);
458     if (!m_meta.isEmpty())
459         content.append(m_meta);
460     info_browser->setHtml(content);
461 }
462
463
464 #include "resourcewidget.moc"