1 /** ===========================================================
2  * @file
3  *
4  * This file is a part of KDE project
5  *
6  *
7  * @date   2009-11-21
8  * @brief  kipi host test application
9  *
10  * @author Copyright (C) 2009-2010 by Michael G. Hansen
11  *         <a href="mailto:mike at mghansen dot de">mike at mghansen dot de</a>
12  * @author Copyright (C) 2011-2018 by Gilles Caulier
13  *         <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
14  *
15  * This program is free software; you can redistribute it
16  * and/or modify it under the terms of the GNU General
17  * Public License as published by the Free Software Foundation;
18  * either version 2, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * ============================================================ */
26 
27 #include "kipiinterface.h"
28 
29 // Qt includes
30 
31 #include <QTextStream>
32 #include <QDebug>
33 #include <QIcon>
34 #include <QFileInfo>
35 
36 // Libkipi includes
37 
38 #include "libkipi_version.h"
39 #include "imagecollection.h"
40 
41 // KF includes
42 
43 #ifdef HAVE_KEXIV2
44 #   include <kexiv2/kexiv2.h>
45 #endif
46 
47 // Local includes
48 
49 #include "kipiimageinfoshared.h"
50 #include "kipiimagecollectionselector.h"
51 #include "kipiuploadwidget.h"
52 #include "kipiimagecollectionshared.h"
53 #include "kipiwriteimage.h"
54 
55 namespace KXMLKipiCmd
56 {
57 
KipiInterface(QObject * const parent,const QString & name)58 KipiInterface::KipiInterface(QObject* const parent, const QString& name)
59     : Interface(parent, name),
60       m_selectedImages(),
61       m_selectedAlbums(),
62       m_albums()
63 {
64 }
65 
~KipiInterface()66 KipiInterface::~KipiInterface()
67 {
68 }
69 
currentAlbum()70 ImageCollection KipiInterface::currentAlbum()
71 {
72     qDebug() << "Called by plugins";
73 
74     QUrl currentAlbumUrl;
75 
76     if (!m_selectedAlbums.isEmpty())
77     {
78         currentAlbumUrl = m_selectedAlbums.at(0);
79     }
80 
81     return (ImageCollection(new KipiImageCollectionShared(currentAlbumUrl)));
82 }
83 
currentSelection()84 ImageCollection KipiInterface::currentSelection()
85 {
86     qDebug() << "Called by plugins";
87 
88     return (ImageCollection(new KipiImageCollectionShared(m_selectedImages)));
89 }
90 
allAlbums()91 QList<ImageCollection> KipiInterface::allAlbums()
92 {
93     QList<ImageCollection> listAllAlbums;
94 
95     for (QList<QUrl>::const_iterator it = m_albums.constBegin(); it!=m_albums.constEnd(); ++it)
96     {
97         listAllAlbums.append(ImageCollection(new KipiImageCollectionShared(*it)));
98     }
99 
100     // make sure albums which have been specified as selectedalbums are also in the allAlbums list:
101     for (QList<QUrl>::const_iterator it = m_selectedAlbums.constBegin(); it!=m_selectedAlbums.constEnd(); ++it)
102     {
103         if (!m_albums.contains(*it))
104         {
105             listAllAlbums.append(ImageCollection(new KipiImageCollectionShared(*it)));
106         }
107     }
108 
109     return listAllAlbums;
110 }
111 
info(const QUrl & url)112 ImageInfo KipiInterface::info(const QUrl& url)
113 {
114     qDebug() << QString::fromLatin1( "Plugin wants information about image \"%1\"").arg( url.url() );
115 
116     return (ImageInfo(new KipiImageInfoShared(this, url)));
117 }
118 
addImage(const QUrl & url,QString & errmsg)119 bool KipiInterface::addImage(const QUrl& url, QString& errmsg)
120 {
121     Q_UNUSED(errmsg);
122     qDebug() << QString::fromLatin1( "Plugin added an image: \"%1\"").arg( url.url() );
123 
124     return true;
125 }
126 
delImage(const QUrl & url)127 void KipiInterface::delImage(const QUrl& url)
128 {
129     qDebug() << QString::fromLatin1( "Plugin deleted an image: \"%1\"").arg( url.url() );
130 }
131 
refreshImages(const QList<QUrl> & urls)132 void KipiInterface::refreshImages(const QList<QUrl>& urls)
133 {
134     qDebug() << QString::fromLatin1( "Plugin asks to refresh %1 images:").arg( urls.size() );
135 
136     for (QList<QUrl>::ConstIterator it = urls.constBegin(); it!=urls.constEnd(); ++it)
137     {
138         qDebug() << QString::fromLatin1("  ") + (*it).url();
139     }
140 }
141 
features() const142 int KipiInterface::features() const
143 {
144     qDebug() << "Called by plugins";
145 
146     return   ImagesHasTime
147 #ifdef HAVE_KEXIV2
148            | HostSupportsMetadataProcessing
149 #endif
150 
151     ;
152 }
153 
imageCollectionSelector(QWidget * parent)154 ImageCollectionSelector* KipiInterface::imageCollectionSelector(QWidget* parent)
155 {
156     qDebug() << "Called by plugins";
157 
158     return (new KipiImageCollectionSelector(this, parent));
159 }
160 
uploadWidget(QWidget * parent)161 UploadWidget* KipiInterface::uploadWidget(QWidget* parent)
162 {
163     qDebug() << "Called by plugins";
164 
165     return (new KipiUploadWidget(this, parent));
166 }
167 
addSelectedImages(const QList<QUrl> & images)168 void KipiInterface::addSelectedImages(const QList<QUrl>& images)
169 {
170     m_selectedImages.append(images);
171 }
172 
addSelectedImage(const QUrl & image)173 void KipiInterface::addSelectedImage(const QUrl& image)
174 {
175     m_selectedImages.append(image);
176 }
177 
addAlbums(const QList<QUrl> & albums)178 void KipiInterface::addAlbums(const QList<QUrl>& albums)
179 {
180     for (QList<QUrl>::const_iterator it = albums.constBegin(); it!=albums.constEnd(); ++it)
181     {
182         addAlbum(*it);
183     }
184 }
185 
addAlbum(const QUrl & album)186 void KipiInterface::addAlbum(const QUrl& album)
187 {
188     m_albums.append(album);
189 
190     // TODO: recurse through sub-directories?
191 }
192 
addSelectedAlbums(const QList<QUrl> & albums)193 void KipiInterface::addSelectedAlbums(const QList<QUrl>& albums)
194 {
195     for (QList<QUrl>::const_iterator it = albums.constBegin(); it!=albums.constEnd(); ++it)
196     {
197         addSelectedAlbum(*it);
198     }
199 }
200 
addSelectedAlbum(const QUrl & album)201 void KipiInterface::addSelectedAlbum(const QUrl& album)
202 {
203     m_selectedAlbums.append(album);
204 
205     // TODO: recurse through sub-directories?
206 }
207 
hostSetting(const QString & settingName)208 QVariant KipiInterface::hostSetting(const QString& settingName)
209 {
210     Q_UNUSED(settingName);
211     return QVariant();
212 }
213 
thumbnails(const QList<QUrl> & list,int)214 void KipiInterface::thumbnails(const QList<QUrl>& list, int)
215 {
216     foreach(const QUrl& url, list)
217     {
218         QIcon icon(url.url());
219         emit gotThumbnail(url, icon.pixmap(256));
220     }
221 }
222 
saveImage(const QUrl & url,const QString & format,const QByteArray & data,uint width,uint height,bool sixteenBit,bool hasAlpha,bool * cancel)223 bool KipiInterface::saveImage(const QUrl& url, const QString& format,
224                               const QByteArray& data, uint width, uint height,
225                               bool  sixteenBit, bool hasAlpha, bool* cancel)
226 {
227     KIPIWriteImage writer;
228     writer.setImageData(data, width, height, sixteenBit, hasAlpha);
229     writer.setCancel(cancel);
230 
231     if (format.toUpper() == QLatin1String("JPG") ||
232         format.toUpper() == QLatin1String("JPEG"))
233     {
234         return writer.write2JPEG(url.toLocalFile());
235     }
236 
237     if (format.toUpper() == QLatin1String("TIF") ||
238         format.toUpper() == QLatin1String("TIFF"))
239     {
240         return writer.write2TIFF(url.toLocalFile());
241     }
242 
243     if (format.toUpper() == QLatin1String("PNG"))
244     {
245         return writer.write2PNG(url.toLocalFile());
246     }
247 
248     if (format.toUpper() == QLatin1String("PPM"))
249     {
250         return writer.write2PPM(url.toLocalFile());
251     }
252 
253     return false;
254 }
255 
256 // ---------------------------------------------------------------------------------------
257 
258 #ifdef HAVE_KEXIV2
259 
260 class KipiMetadataProcessor : public KIPI::MetadataProcessor
261 {
262 public:
263 
KipiMetadataProcessor()264     KipiMetadataProcessor()  {};
~KipiMetadataProcessor()265     ~KipiMetadataProcessor() override {};
266 
load(const QUrl & url)267     bool load(const QUrl& url) override
268     {
269         return m_meta.load(url.toLocalFile());
270     }
271 
save(const QUrl & url,bool writeToFileOnly)272     bool save(const QUrl& url, bool writeToFileOnly) override
273     {
274         if (writeToFileOnly)
275             m_meta.setMetadataWritingMode((int) KExiv2Iface::KExiv2::WRITETOIMAGEONLY);
276 
277         return m_meta.save(url.toLocalFile());
278     }
279 
applyChanges()280     bool applyChanges() override
281     {
282         return m_meta.applyChanges();
283     }
284 
getPixelSize()285     QSize getPixelSize() override
286     {
287         return m_meta.getPixelSize();
288     }
289 
setImageProgramId(const QString & program,const QString & version)290     bool setImageProgramId(const QString& program, const QString& version) override
291     {
292         return m_meta.setImageProgramId(program, version);
293     }
294 
getImageDimensions()295     QSize getImageDimensions() override
296     {
297         return m_meta.getImageDimensions();
298     }
299 
setImageDimensions(const QSize & size)300     bool setImageDimensions(const QSize& size) override
301     {
302         return m_meta.setImageDimensions(size);
303     }
304 
getImageOrientation()305     int getImageOrientation() override
306     {
307         return m_meta.getImageOrientation();
308     }
309 
setImageOrientation(int orientation)310     bool setImageOrientation(int orientation) override
311     {
312         return m_meta.setImageOrientation((KExiv2Iface::KExiv2::ImageOrientation)orientation);
313     }
314 
rotateExifQImage(QImage & img,int orientation)315     bool rotateExifQImage(QImage& img, int orientation) override
316     {
317         return m_meta.rotateExifQImage(img, (KExiv2Iface::KExiv2::ImageOrientation)orientation);
318     }
319 
getImageDateTime()320     QDateTime getImageDateTime() override
321     {
322         return m_meta.getImageDateTime();
323     }
324 
setImageDateTime(const QDateTime & dt)325     bool setImageDateTime(const QDateTime& dt) override
326     {
327         return m_meta.setImageDateTime(dt);
328     }
329 
getImagePreview(QImage & img)330     bool getImagePreview(QImage& img) override
331     {
332         return m_meta.getImagePreview(img);
333     }
334 
setImagePreview(const QImage & img)335     bool setImagePreview(const QImage& img) override
336     {
337         return m_meta.setImagePreview(img);
338     }
339 
hasExif()340     bool hasExif() override
341     {
342         return m_meta.hasExif();
343     }
344 
hasIptc()345     bool hasIptc() override
346     {
347         return m_meta.hasIptc();
348     }
349 
hasXmp()350     bool hasXmp() override
351     {
352         return m_meta.hasXmp();
353     }
354 
getExif()355     QByteArray getExif() override
356     {
357         return m_meta.getExifEncoded();
358     }
359 
getIptc()360     QByteArray getIptc() override
361     {
362         return m_meta.getIptc();
363     }
364 
getXmp()365     QByteArray getXmp() override
366     {
367         return m_meta.getXmp();
368     }
369 
setExif(const QByteArray & data)370     bool setExif(const QByteArray& data) override
371     {
372         return m_meta.setExif(data);
373     }
374 
setIptc(const QByteArray & data)375     bool setIptc(const QByteArray& data) override
376     {
377         return m_meta.setIptc(data);
378     }
379 
setXmp(const QByteArray & data)380     bool setXmp(const QByteArray& data) override
381     {
382         return m_meta.setXmp(data);
383     }
384 
registerXmpNameSpace(const QString & uri,const QString & prefix)385     bool registerXmpNameSpace(const QString& uri, const QString& prefix) override
386     {
387         return m_meta.registerXmpNameSpace(uri, prefix);
388     }
389 
supportXmp()390     bool supportXmp() override
391     {
392         return m_meta.supportXmp();
393     }
394 
canWriteXmp(const QUrl & url)395     bool canWriteXmp(const QUrl& url) override
396     {
397         return m_meta.canWriteXmp(url.toLocalFile());
398     }
399 
removeExifTags(const QStringList & tagFilters)400     bool removeExifTags(const QStringList& tagFilters) override
401     {
402         KExiv2Iface::KExiv2::MetaDataMap m = m_meta.getExifTagsDataList(tagFilters);
403 
404         if (m.isEmpty())
405             return false;
406 
407         for (KExiv2Iface::KExiv2::MetaDataMap::iterator it = m.begin(); it != m.end(); ++it)
408         {
409             m_meta.removeExifTag(it.key().toLatin1().constData());
410         }
411 
412         return true;
413     }
414 
removeIptcTags(const QStringList & tagFilters)415     bool removeIptcTags(const QStringList& tagFilters) override
416     {
417         KExiv2Iface::KExiv2::MetaDataMap m = m_meta.getIptcTagsDataList(tagFilters);
418 
419         if (m.isEmpty())
420             return false;
421 
422         for (KExiv2Iface::KExiv2::MetaDataMap::iterator it = m.begin(); it != m.end(); ++it)
423         {
424             m_meta.removeIptcTag(it.key().toLatin1().constData());
425         }
426 
427         return true;
428     }
429 
removeXmpTags(const QStringList & tagFilters)430     bool removeXmpTags(const QStringList& tagFilters) override
431     {
432         KExiv2Iface::KExiv2::MetaDataMap m = m_meta.getXmpTagsDataList(tagFilters);
433 
434         if (m.isEmpty())
435             return false;
436 
437         for (KExiv2Iface::KExiv2::MetaDataMap::iterator it = m.begin(); it != m.end(); ++it)
438         {
439             m_meta.removeXmpTag(it.key().toLatin1().constData());
440         }
441 
442         return true;
443     }
444 
getGPSInfo(double & alt,double & lat,double & lon)445     bool getGPSInfo(double& alt, double& lat, double& lon) override
446     {
447         return m_meta.getGPSInfo(alt, lat, lon);
448     }
449 
setGPSInfo(const double alt,const double lat,const double lon)450     bool setGPSInfo(const double alt, const double lat, const double lon) override
451     {
452         return m_meta.setGPSInfo(alt, lat, lon);
453     }
454 
removeGPSInfo()455     bool removeGPSInfo() override
456     {
457         return m_meta.removeGPSInfo();
458     }
459 
getExifTagString(const QString & tag)460     QString getExifTagString(const QString& tag) override
461     {
462         return m_meta.getExifTagString(tag.toLatin1().constData());
463     }
464 
setExifTagString(const QString & tag,const QString & val)465     bool setExifTagString(const QString& tag, const QString& val) override
466     {
467         return m_meta.setExifTagString(tag.toLatin1().constData(), val);
468     }
469 
getExifTagRational(const QString & tag,long int & num,long int & den)470     bool getExifTagRational(const QString& tag, long int& num, long int& den) override
471     {
472         return m_meta.getExifTagRational(tag.toLatin1().constData(), num, den);
473     }
474 
setExifTagRational(const QString & tag,long int num,long int den)475     bool setExifTagRational(const QString& tag, long int num, long int den) override
476     {
477         return m_meta.setExifTagRational(tag.toLatin1().constData(), num, den);
478     }
479 
getXmpTagString(const QString & tag)480     QString getXmpTagString(const QString& tag) override
481     {
482         return m_meta.getXmpTagString(tag.toLatin1().constData());
483     }
484 
setXmpTagString(const QString & tag,const QString & val)485     bool setXmpTagString(const QString& tag, const QString& val) override
486     {
487         return m_meta.setXmpTagString(tag.toLatin1().constData(), val);
488     }
489 
getXmpKeywords()490     QStringList getXmpKeywords() override
491     {
492         return m_meta.getXmpKeywords();
493     }
494 
setXmpKeywords(const QStringList & keywords)495     bool  setXmpKeywords(const QStringList& keywords) override
496     {
497         return m_meta.setXmpKeywords(keywords);
498     }
499 
getXmpTagVariant(const QString & tag)500     QVariant getXmpTagVariant(const QString& tag) override
501     {
502         return m_meta.getXmpTagVariant(tag.toLatin1().constData());
503     }
504 
505 private:
506 
507     KExiv2Iface::KExiv2 m_meta;
508 };
509 
createMetadataProcessor() const510 MetadataProcessor* KipiInterface::createMetadataProcessor() const
511 {
512     return (new KipiMetadataProcessor);
513 }
514 
515 #else // HAVE_KEXIV2
516 
createMetadataProcessor() const517 MetadataProcessor* KipiInterface::createMetadataProcessor() const
518 {
519     qDebug() << "This interface was not compiled with libkexiv2 to support Metadata processing";
520     return 0;
521 }
522 
523 #endif // HAVE_KEXIV2
524 
525 // ---------------------------------------------------------------------------------------
526 
createReadWriteLock(const QUrl &) const527 FileReadWriteLock* KipiInterface::createReadWriteLock(const QUrl&) const
528 {
529     return nullptr;  // TODO
530 }
531 
532 } // namespace KXMLKipiCmd
533