diff --git a/calbumroots.cpp b/calbumroots.cpp index 8b71bac..f89ec4f 100644 --- a/calbumroots.cpp +++ b/calbumroots.cpp @@ -13,7 +13,8 @@ cAlbumRoots::cAlbumRoots(const qint32& id, QObject* parent) : m_type(0), m_identifier(""), m_specificPath(""), - m_item(nullptr) + m_item(nullptr), + m_drive("") { } @@ -28,6 +29,10 @@ cAlbumRoots::cAlbumRoots(const qint32& id, const QString& label, const quint32 s { } +cAlbumRoots::~cAlbumRoots() +{ +} + qint32 cAlbumRoots::id() { return(m_id); @@ -93,6 +98,19 @@ QStandardItem* cAlbumRoots::item() return(m_item); } +void cAlbumRoots::setDrive(const QString& drive) +{ + m_drive = drive; + + if(m_drive.right(1) == "/") + m_drive = m_drive.left(m_drive.length()-1); +} + +QString cAlbumRoots::drive() +{ + return(m_drive); +} + cAlbumRootsList::cAlbumRootsList(QSqlDatabase* dbDigikam, QSqlDatabase *dbThumbnail, QObject* parent) : QObject(parent), m_dbDigikam(dbDigikam), @@ -100,7 +118,11 @@ cAlbumRootsList::cAlbumRootsList(QSqlDatabase* dbDigikam, QSqlDatabase *dbThumbn { } -bool cAlbumRootsList::load() +cAlbumRootsList::~cAlbumRootsList() +{ +} + +bool cAlbumRootsList::load(QList* volumes) { if(!m_dbDigikam) return(false); @@ -121,7 +143,16 @@ bool cAlbumRootsList::load() } while(query.next()) - add(query.value("id").toInt(), query.value("label").toString(), query.value("status").toInt(), query.value("type").toInt(), query.value("identifier").toString(), query.value("specificPath").toString(), this); + { + cAlbumRoots* albumRoots = add(query.value("id").toInt(), query.value("label").toString(), query.value("status").toInt(), query.value("type").toInt(), query.value("identifier").toString(), query.value("specificPath").toString(), this); + + QString identifier = albumRoots->identifier().mid(albumRoots->identifier().lastIndexOf("=")+1); + for(int x = 0;x < volumes->count();x++) + { + if(volumes->at(x).uuid == identifier) + albumRoots->setDrive(volumes->at(x).path); + } + } return(true); } diff --git a/calbumroots.h b/calbumroots.h index 45d14f3..e465790 100644 --- a/calbumroots.h +++ b/calbumroots.h @@ -2,6 +2,8 @@ #define CALBUMROOTS_H +#include "common.h" + #include #include #include @@ -20,6 +22,7 @@ class cAlbumRoots : public QObject public: cAlbumRoots(const qint32& id, QObject* parent = nullptr); cAlbumRoots(const qint32& id, const QString& label, const quint32 status, const qint32 type, const QString& identifier, const QString& specificPath, QObject* parent = nullptr); + ~cAlbumRoots(); qint32 id(); @@ -41,6 +44,9 @@ public: void setItem(QStandardItem* item); QStandardItem* item(); + void setDrive(const QString &drive); + QString drive(); + private: qint32 m_id; QString m_label; @@ -49,6 +55,7 @@ private: QString m_identifier; QString m_specificPath; QStandardItem* m_item; + QString m_drive; }; Q_DECLARE_METATYPE(cAlbumRoots*) @@ -59,15 +66,16 @@ class cAlbumRootsList : public QObject, public QList public: cAlbumRootsList(QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, QObject* parent = nullptr); + ~cAlbumRootsList(); - bool load(); - cAlbumRoots* add(const qint32 &id, QObject* parent = nullptr); - cAlbumRoots* add(const qint32& id, const QString& label, const quint32 status, const qint32 type, const QString& identifier, const QString& specificPath, QObject* parent = nullptr); - cAlbumRoots* find(const qint32& id); + bool load(QList* volumes); + cAlbumRoots* add(const qint32 &id, QObject* parent = nullptr); + cAlbumRoots* add(const qint32& id, const QString& label, const quint32 status, const qint32 type, const QString& identifier, const QString& specificPath, QObject* parent = nullptr); + cAlbumRoots* find(const qint32& id); private: - QSqlDatabase* m_dbDigikam; - QSqlDatabase* m_dbThumbnail; + QSqlDatabase* m_dbDigikam; + QSqlDatabase* m_dbThumbnail; }; Q_DECLARE_METATYPE(cAlbumRootsList*) diff --git a/calbums.cpp b/calbums.cpp index c66fcfb..394412e 100644 --- a/calbums.cpp +++ b/calbums.cpp @@ -41,6 +41,12 @@ cAlbums::cAlbums(const qint32& id, cAlbumRoots* albumRoots, const QString& relat { } +cAlbums::~cAlbums() +{ + if(m_imagesList) + delete m_imagesList; +} + qint32 cAlbums::id() { return(m_id); @@ -159,6 +165,10 @@ cAlbumsList::cAlbumsList(QSqlDatabase* dbDigikam, QSqlDatabase *dbThumbnail, cAl { } +cAlbumsList::~cAlbumsList() +{ +} + bool cAlbumsList::load() { if(!m_dbDigikam) diff --git a/calbums.h b/calbums.h index 6fcda7d..129b149 100644 --- a/calbums.h +++ b/calbums.h @@ -23,6 +23,7 @@ class cAlbums : public QObject public: cAlbums(const qint32& id, cAlbumsList* albumsList, QSqlDatabase* dbDigikam, QSqlDatabase* m_dbThumbnail, QObject *parent = nullptr); cAlbums(const qint32& id, cAlbumRoots* albumRoots, const QString& relativePath, const QDate& date, const QString& caption, const QString& collection, const QDateTime& modificationDate, cAlbumsList* albumsList, QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, QObject *parent = nullptr); + ~cAlbums(); qint32 id(); @@ -79,6 +80,7 @@ class cAlbumsList : public QObject, public QList public: cAlbumsList(QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, cAlbumRootsList* albumRootsList, QObject* parent = nullptr); + ~cAlbumsList(); bool load(); cAlbums* add(const qint32& id, QObject* parent = nullptr); diff --git a/cexif.cpp b/cexif.cpp new file mode 100644 index 0000000..0967d47 --- /dev/null +++ b/cexif.cpp @@ -0,0 +1,1118 @@ +/*! + \file cexif.cpp + +*/ + +#include "cexif.h" + +#include "common.h" + +#include +#include +#include +#include + +#include +#include + +#include "cimage.h" + +#include + + +cEXIF::cEXIF(cEXIFTagList* lpEXIFTagList, cEXIFCompressionList* lpEXIFCompressionList, cEXIFLightSourceList* lpEXIFLightSourceList, cEXIFFlashList* lpEXIFFlashList, cIPTCTagList* lpIPTCTagList, cXMPTagList* lpXMPTagList) : + m_szMimeType(""), + m_iWidth(0), + m_iHeight(0), + m_szFileName(""), + m_lpEXIFTagList(lpEXIFTagList), + m_lpEXIFCompressionList(lpEXIFCompressionList), + m_lpEXIFLightSourceList(lpEXIFLightSourceList), + m_lpEXIFFlashList(lpEXIFFlashList), + m_lpIPTCTagList(lpIPTCTagList), + m_lpXMPTagList(lpXMPTagList) +{ +} + +bool cEXIF::fromFile(const QString& szFileName, bool withThumbnail) +{ + if(!QFile::exists(szFileName)) + return(false); + + m_exifValueList.clear(); + m_previewList.clear(); + + m_szFileName = ""; + + try + { + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(szFileName.toLocal8Bit().toStdString()); + if(!image.get()) + return(false); + + image->readMetadata(); + + Exiv2::ExifData& exifData = image->exifData(); + Exiv2::IptcData& iptcData = image->iptcData(); + Exiv2::XmpData& xmpData = image->xmpData(); + + m_szMimeType = QString::fromStdString(image->mimeType()); + m_iWidth = image->pixelWidth(); + m_iHeight = image->pixelHeight(); + + m_szFileName = szFileName; + + if(!iptcData.empty()) + { + Exiv2::IptcData::const_iterator end = iptcData.end(); + + for(Exiv2::IptcData::const_iterator i = iptcData.begin(); i != end; ++i) + { + cIPTCTag* lpTag = m_lpIPTCTagList->find(i->tag()); + + if(lpTag) + { + cIPTCValue* lpValue = m_iptcValueList.add(lpTag); + if(lpValue) + lpValue->setValue(QString::fromStdString(i->value().toString()), i->typeId(), i->count()); + } + } + + Exiv2::PreviewManager previewManager(*image); + Exiv2::PreviewPropertiesList previewPropertiesList = previewManager.getPreviewProperties(); + + for(Exiv2::PreviewPropertiesList::const_iterator i = previewPropertiesList.begin();i != previewPropertiesList.end();i++) + { + Exiv2::PreviewImage previewImage = previewManager.getPreviewImage(*i); + QImage image; + image.loadFromData(static_cast(previewImage.pData()), static_cast(previewImage.size())); + + m_previewList.append(image); + } + } + + if(!exifData.empty()) + { + Exiv2::ExifData::const_iterator end = exifData.end(); + for(Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) + { + cEXIFTag* lpTag = m_lpEXIFTagList->find(QString::fromUtf8(i->key().c_str())); + + if(lpTag) + { + cEXIFValue* lpValue = m_exifValueList.add(lpTag); + if(lpValue) + lpValue->setValue(QString::fromStdString(i->value().toString()), i->typeId(), i->count()); + } + } + + Exiv2::PreviewManager previewManager(*image); + Exiv2::PreviewPropertiesList previewPropertiesList = previewManager.getPreviewProperties(); + + for(Exiv2::PreviewPropertiesList::const_iterator i = previewPropertiesList.begin();i != previewPropertiesList.end();i++) + { + Exiv2::PreviewImage previewImage = previewManager.getPreviewImage(*i); + QImage image; + image.loadFromData(static_cast(previewImage.pData()), static_cast(previewImage.size())); + + m_previewList.append(image); + } + } + + if(!xmpData.empty()) + { + Exiv2::XmpData::const_iterator end = xmpData.end(); + + for(Exiv2::XmpData::const_iterator i = xmpData.begin(); i != end; ++i) + { + cXMPTag* lpTag = m_lpXMPTagList->find(QString::fromStdString(i->key())); + + if(lpTag) + { + cXMPValue* lpValue = m_xmpValueList.add(lpTag); + if(lpValue) + lpValue->setValue(QString::fromStdString(i->value().toString()), i->typeId(), i->count()); + } + } + + Exiv2::PreviewManager previewManager(*image); + Exiv2::PreviewPropertiesList previewPropertiesList = previewManager.getPreviewProperties(); + + for(Exiv2::PreviewPropertiesList::const_iterator i = previewPropertiesList.begin();i != previewPropertiesList.end();i++) + { + Exiv2::PreviewImage previewImage = previewManager.getPreviewImage(*i); + QImage image; + image.loadFromData(static_cast(previewImage.pData()), static_cast(previewImage.size())); + + m_previewList.append(image); + } + } + } + catch (Exiv2::AnyError& e) + { + qDebug() << "Caught Exiv2 exception '" << e.what(); + return(false); + } + + if(!m_previewList.count() && withThumbnail) + { + cImage image; + if(image.load(szFileName)) + { + QTransform rotation; + int angle = 0; + + switch(imageOrientation()) + { + case 8: + angle = 270; + break; + case 3: + angle = 180; + break; + case 6: + angle = 90; + break; + } + + if(angle != 0) + { + rotation.rotate(angle); + image = image.transformed(rotation); + } + + m_thumbnail = image.scaled(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + } + else + { + qint32 index = -1; + qint32 widthDiff = std::numeric_limits::max(); + qint32 heightDiff = std::numeric_limits::max(); + + for(qint32 i = 0;i < m_previewList.count();i++) + { + qint32 wd = m_previewList[i].width() - THUMBNAIL_WIDTH; + qint32 hd = m_previewList[i].height() - THUMBNAIL_HEIGHT; + + if(wd >= 0 && hd >= 0) + { + if(wd <= widthDiff && hd <= heightDiff) + { + index = i; + widthDiff = wd; + heightDiff = hd; + } + } + } + + if(index == -1) + { + for(qint32 i = 0;i < m_previewList.count();i++) + { + qint32 wd = m_previewList[i].width() - THUMBNAIL_WIDTH; + qint32 hd = m_previewList[i].height() - THUMBNAIL_HEIGHT; + + if(abs(wd) <= widthDiff && abs(hd) <= heightDiff) + { + index = i; + widthDiff = abs(wd); + heightDiff = abs(hd); + } + } + } + + if(index != -1) + { + QTransform rotation; + int angle = 0; + + switch(imageOrientation()) + { + case 8: + angle = 270; + break; + case 3: + angle = 180; + break; + case 6: + angle = 90; + break; + } + + m_thumbnail = m_previewList[index].scaled(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, Qt::KeepAspectRatio, Qt::SmoothTransformation); + + if(angle != 0) + { + rotation.rotate(angle); + m_thumbnail = m_thumbnail.transformed(rotation); + } + } + } + + return(true); +} + +QString cEXIF::mimeType() +{ + return(m_szMimeType); +} + +qint32 cEXIF::imageWidth() +{ + if(m_iWidth) + return(m_iWidth); + + qint32 iWidth = getEXIFTag(0x0100, "Image").value(); + + if(iWidth) + return(iWidth); + + return(getXMPTag("Xmp.video.Width").value()); +} + +qint32 cEXIF::imageHeight() +{ + if(m_iHeight) + return(m_iHeight); + + qint32 iHeight = getEXIFTag(0x0101, "Image").value(); + + if(iHeight) + return(iHeight); + + return(getXMPTag("Xmp.video.Height").value()); +} + +qint16 cEXIF::imageOrientation() +{ + return(getEXIFTag(0x0112, "Image").value()); +} + +QString cEXIF::cameraMake() +{ + QString szCameraMake = getEXIFTag(0x010f, "Image").toString(); + + if(!szCameraMake.isEmpty()) + return(szCameraMake); + + return(getXMPTag("Xmp.video.MajorBrand").toString()); +} + +QString cEXIF::cameraModel() +{ + QString szCameraModel = getEXIFTag(0x0110, "Image").toString(); + + if(!szCameraModel.isEmpty()) + return(szCameraModel); + + return(getXMPTag("Xmp.video.MajorBrand").toString()); +} + +QDateTime cEXIF::dateTime() +{ + QDateTime dateTime = QDateTime::fromString(getEXIFTag(0x9004, "Image").value(), "yyyy:MM:dd hh:mm:ss"); + if(dateTime.isValid()) + return(dateTime); + + dateTime = QDateTime::fromString(getXMPTag("Xmp.video.DateTimeDigitized").toString(), "yyyy-MM-ddThh:mm:ssZ"); + if(dateTime.isValid()) + return(dateTime); + + dateTime = QDateTime::fromString(getEXIFTag(0x0132, "Image").value(), "yyyy:MM:dd hh:mm:ss"); + if(dateTime.isValid()) + return(dateTime); + + QFileInfo info(m_szFileName); + return(info.lastModified()); +} + +QString cEXIF::fNumber() +{ + return(getEXIFTag(0x829d, "Photo").value()); +} + +qint32 cEXIF::iso() +{ + return(getEXIFTag(0x8827, "Photo").value()); +} + +QString cEXIF::flash() +{ + return(m_lpEXIFFlashList->flash(getEXIFTag(0x9209, "Photo").value())); +} + +qint32 cEXIF::flashID() +{ + return(getEXIFTag(0x9209, "Photo").value()); +} + +qreal cEXIF::focalLength() +{ + return(getEXIFTag(0x920a, "Photo").value()); +} + +QString cEXIF::lensMake() +{ + return(getEXIFTag(0xa433, "Photo").value()); +} + +QString cEXIF::lensModel() +{ + return(getEXIFTag(0xa434, "Photo").value()); +} + +QString cEXIF::exposureTime() +{ + qreal value = getEXIFTag(0x829a, "Photo").value(); + + if(value == 0.0) + return("error"); + + if(value >= 1.0) + return(QString("%1 sec").arg(value)); + + return(QString("1/%1").arg(1/value)); +} + +qint32 cEXIF::exposureBias() +{ + return(getEXIFTag(0x9204, "Photo").value()); +} + +QString cEXIF::exifVersion() +{ + return(getEXIFTag(0x9000, "Photo").value()); +} + +QDateTime cEXIF::dateTimeOriginal() +{ + return(QDateTime::fromString(getEXIFTag(0x9003, "Photo").value(), "yyyy:MM:dd hh:mm:ss")); +} + +QDateTime cEXIF::dateTimeDigitized() +{ + return(QDateTime::fromString(getEXIFTag(0x9004, "Photo").value(), "yyyy:MM:dd hh:mm:ss")); +} + +qint32 cEXIF::whiteBalance() +{ + return(getEXIFTag(0xa403, "Photo").value()); +} + +qreal cEXIF::focalLength35() +{ + return(getEXIFTag(0xa405, "Photo").value()); +} + +QString cEXIF::gps() +{ + QList nList = getTagList(0x0002, "GPSInfo"); + QList eList = getTagList(0x0004, "GPSInfo"); + + if(nList.count() != 3) + return(""); + if(eList.count() != 3) + return(""); + + QString szGPS = QString("%1 %2° %3' %4\" %5 %6° %7' %8\"").arg(getEXIFTag(0x0001, "GPSInfo").value()).arg(nList[0].value()).arg(nList[1].value()).arg(nList[2].value()).arg(getEXIFTag(0x0001, "GPSInfo").value()).arg(eList[0].value()).arg(eList[1].value()).arg(eList[2].value()); + return(szGPS); +} + +QString cEXIF::duration() +{ + return(getXMPTag("Xmp.video.Duration").toString()); +} + +QString cEXIF::fileName() +{ + return(m_szFileName); +} + +QList cEXIF::previewList() +{ + return(m_previewList); +} + +QImage cEXIF::thumbnail() +{ + return(m_thumbnail); +} + +QVariant cEXIF::getEXIFTag(qint32 iTAGID, const QString& szIFD) +{ + cEXIFTag* lpTag = m_lpEXIFTagList->find(iTAGID, szIFD); + + if(!lpTag) + return(QVariant()); + + cEXIFValue* lpValue = m_exifValueList.find(lpTag); + + if(!lpValue) + return(QVariant()); + + return(lpValue->value()); +} + +QVariant cEXIF::getIPTCTag(qint32 iTAGID) +{ + cIPTCTag* lpTag = m_lpIPTCTagList->find(iTAGID); + + if(!lpTag) + return(QVariant()); + + cIPTCValue* lpValue = m_iptcValueList.find(lpTag); + + if(!lpValue) + return(QVariant()); + + return(lpValue->value()); +} + +QVariant cEXIF::getXMPTag(const QString& szTAGName) +{ + cXMPTag* lpTag = m_lpXMPTagList->find(szTAGName); + + if(!lpTag) + return(QVariant()); + + cXMPValue* lpValue = m_xmpValueList.find(lpTag); + + if(!lpValue) + return(QVariant()); + + return(lpValue->value()); +} + +QList cEXIF::getTagList(qint32 iTAGID, const QString& szIFD) +{ + cEXIFTag* lpTag = m_lpEXIFTagList->find(iTAGID, szIFD); + + if(!lpTag) + return(QList()); + + cEXIFValue* lpValue = m_exifValueList.find(lpTag); + + if(!lpValue) + return(QList()); + + return(lpValue->valueList()); +} + +bool cEXIF::copyTo(const QString& fileName) +{ + try + { +// Exiv2::Image::UniquePtr image = Exiv2::ImageFactory::open(fileName.toLocal8Bit().toStdString()); + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(fileName.toLocal8Bit().toStdString()); + if(!image.get()) + return(false); + + image->readMetadata(); + + Exiv2::ExifData& exifData = image->exifData(); + + for(int x = 0;x < m_exifValueList.count();x++) + { + cEXIFValue* lpEXIFValue = m_exifValueList[x]; + +// if(!lpEXIFValue->exifTag()->m_szKey.startsWith("Exif.Image")) + exifData[lpEXIFValue->exifTag()->m_szKey.toUtf8().constData()] = lpEXIFValue->value().toString().toUtf8().constData(); + } + + image->setExifData(exifData); + image->writeMetadata(); + } + catch (Exiv2::AnyError& e) + { + qDebug() << "Caught Exiv2 exception '" << e.what(); + return(false); + } + + return(true); +} + +cEXIFCompression::cEXIFCompression(const qint32& iID, const QString& szCompression) : + m_iID(iID), + m_szCompression(szCompression) +{ +} + +cEXIFCompressionList::cEXIFCompressionList(QSqlDatabase* db) : + m_dbExportDigikam(db) +{ + QSqlQuery query(*m_dbExportDigikam); + + query.prepare("SELECT id, compression FROM exifCompression;"); + if(!query.exec()) + return; + + while(query.next()) + add(query.value("id").toInt(), query.value("compression").toString()); +} + +cEXIFCompression* cEXIFCompressionList::add(const qint32& iID, const QString& szCompression) +{ + cEXIFCompression* lpNew = find(iID); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFCompression(iID, szCompression); + append(lpNew); + return(lpNew); +} + +cEXIFCompression* cEXIFCompressionList::find(const qint32& iID) +{ + for(int x = 0;x < count();x++) + { + cEXIFCompression* lpCompression= at(x); + + if(lpCompression->m_iID == iID) + return(lpCompression); + } + return(nullptr); +} + +QString cEXIFCompressionList::compression(const qint32& iID) +{ + cEXIFCompression* lpCompression = find(iID); + if(!lpCompression) + return(QObject::tr("unknown")); + return(lpCompression->m_szCompression); +} + +cEXIFLightSource::cEXIFLightSource(const qint32& iID, const QString& szLightSource) : + m_iID(iID), + m_szLightSource(szLightSource) +{ +} + +cEXIFLightSourceList::cEXIFLightSourceList(QSqlDatabase* db) : + m_dbExportDigikam(db) +{ + QSqlQuery query(*m_dbExportDigikam); + + query.prepare("SELECT id, lightsource FROM exifLightSource;"); + if(!query.exec()) + return; + + while(query.next()) + add(query.value("id").toInt(), query.value("lightsource").toString()); +} + +cEXIFLightSource* cEXIFLightSourceList::add(const qint32& iID, const QString& szLightSource) +{ + cEXIFLightSource* lpNew = find(iID); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFLightSource(iID, szLightSource); + append(lpNew); + return(lpNew); +} + +cEXIFLightSource* cEXIFLightSourceList::find(const qint32& iID) +{ + for(int x = 0;x < count();x++) + { + cEXIFLightSource* lpLightSource = at(x); + + if(lpLightSource->m_iID == iID) + return(lpLightSource); + } + return(nullptr); +} + +QString cEXIFLightSourceList::lightSource(const qint32& iID) +{ + cEXIFLightSource* lpLightSource = find(iID); + if(!lpLightSource) + return(QObject::tr("unknown")); + return(lpLightSource->m_szLightSource); +} + +cEXIFFlash::cEXIFFlash(const qint32& iID, const QString& szFlash) : + m_iID(iID), + m_szFlash(szFlash) +{ +} + +cEXIFFlashList::cEXIFFlashList(QSqlDatabase* db) : + m_dbExportDigikam(db) +{ + QSqlQuery query(*m_dbExportDigikam); + + query.prepare("SELECT id, lightsource FROM exifLightSource;"); + if(!query.exec()) + return; + + while(query.next()) + add(query.value("id").toInt(), query.value("lightsource").toString()); +} + +cEXIFFlash* cEXIFFlashList::add(const qint32& iID, const QString& szFlash) +{ + cEXIFFlash* lpNew = find(iID); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFFlash(iID, szFlash); + append(lpNew); + return(lpNew); +} + +cEXIFFlash* cEXIFFlashList::find(const qint32& iID) +{ + for(int x = 0;x < count();x++) + { + cEXIFFlash* lpFlash = at(x); + + if(lpFlash->m_iID == iID) + return(lpFlash); + } + return(nullptr); +} + +QString cEXIFFlashList::flash(const qint32& iID) +{ + cEXIFFlash* lpFlash = find(iID); + if(!lpFlash) + return(QObject::tr("unknown")); + return(lpFlash->m_szFlash); +} + +cEXIFTag::cEXIFTag(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription) : + m_iTAGID(iTAGID), + m_szTAGName(szTAGName), + m_szIFD(szIFD), + m_szKey(szKey), + m_szType(szType), + m_szDescription(szDescription) +{ +} + +cEXIFTagList::cEXIFTagList(QSqlDatabase* db) : + m_dbExportDigikam(db) +{ + QSqlQuery query(*m_dbExportDigikam); + + query.prepare("SELECT tag, ifd, key, type, description FROM exifTags;"); + if(!query.exec()) + return; + + while(query.next()) + add(query.value("tag").toInt(), query.value("key").toString(), query.value("ifd").toString(), query.value("key").toString(), query.value("type").toString(), query.value("description").toString()); +} + +cEXIFTag* cEXIFTagList::add(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription) +{ + cEXIFTag* lpNew = find(iTAGID, szIFD); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFTag(iTAGID, szTAGName, szIFD, szKey, szType, szDescription); + append(lpNew); + return(lpNew); +} + +cEXIFTag* cEXIFTagList::find(const QString& szKey) +{ + for(int x = 0;x < count();x++) + { + cEXIFTag* lpTag = at(x); + + if(lpTag->m_szKey == szKey) + return(lpTag); + } + return(nullptr); +} + +cEXIFTag* cEXIFTagList::find(const qint32& iTAGID, const QString& szIFD) +{ + for(int x = 0;x < count();x++) + { + cEXIFTag* lpTag = at(x); + + if(lpTag->m_iTAGID == iTAGID && lpTag->m_szIFD== szIFD) + return(lpTag); + } + return(nullptr); +} + +cEXIFTag* cEXIFTagList::find(const QString& szTAG, const QString& szIFD) +{ + for(int x = 0;x < count();x++) + { + cEXIFTag* lpTag = at(x); + + if(lpTag->m_szTAGName == szTAG && lpTag->m_szIFD== szIFD) + return(lpTag); + } + return(nullptr); +} + +cEXIFTag* cEXIFValue::exifTag() +{ + return(m_lpEXIFTag); +} + +cEXIFValue::cEXIFValue(cEXIFTag* lpEXIFTag) : + m_lpEXIFTag(lpEXIFTag) +{ +} + +QVariant cEXIFValue::convertValue(const QString& szValue, qint32 iTypeId) +{ + QVariant variant; + + switch(iTypeId) + { + case 1: //byte + case 3: //short + case 4: //long + case 8: //signed short + variant = QVariant::fromValue(szValue.toLong()); + break; + case 5: //rational + case 10: //signed rational + case 11: //float + if(szValue.contains("/")) + { + QStringList tmp = szValue.split("/"); + variant = QVariant::fromValue(tmp[0].toDouble()/tmp[1].toDouble()); + } + else + variant = QVariant::fromValue(szValue.toDouble()); + break; + default: + variant = szValue; + } + return(variant); +} + +void cEXIFValue::setValue(const QString& szValue, qint32 iTypeId, qint32 iCount) +{ + if(iCount == 1 || iTypeId == 2 || iTypeId == 7) + { + m_valueList.append(convertValue(szValue, iTypeId)); + return; + } + + QStringList valueList = szValue.split(" "); + + if(valueList.count() != iCount) + return; + + for(int x = 0;x < iCount;x++) + m_valueList.append(convertValue(valueList[x], iTypeId)); +} + +QVariant cEXIFValue::value() +{ + if(m_valueList.count()) + return(m_valueList[0]); + + return(QVariant()); +} + +QList cEXIFValue::valueList() +{ + return(m_valueList); +} + +cEXIFValueList::cEXIFValueList() +{ +} + +cEXIFValue* cEXIFValueList::add(cEXIFTag* lpEXIFTag) +{ + cEXIFValue* lpNew = find(lpEXIFTag); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFValue(lpEXIFTag); + + append(lpNew); + return(lpNew); +} + +cEXIFValue* cEXIFValueList::find(cEXIFTag* lpEXIFTag) +{ + for(int x = 0;x < count();x++) + { + cEXIFValue* lpValue = at(x); + if(lpValue->exifTag() == lpEXIFTag) + return(lpValue); + } + return(nullptr); +} + +cIPTCTag::cIPTCTag(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription) : + m_iTAGID(iTAGID), + m_szTAGName(szTAGName), + m_iTypeID(iTypeID), + m_szDescription(szDescription) +{ +} + +cIPTCTagList::cIPTCTagList(QSqlDatabase* db) : + m_dbExportDigikam(db) +{ +// add(0x000b, QObject::tr("ProcessingSoftware"), 1, 2, QObject::tr("The name and version of the software used to post-process the picture.")); +} + +cIPTCTag* cIPTCTagList::add(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription) +{ + cIPTCTag* lpNew = find(iTAGID); + + if(lpNew) + return(nullptr); + + lpNew = new cIPTCTag(iTAGID, szTAGName, iTypeID, szDescription); + append(lpNew); + return(lpNew); +} + +cIPTCTag* cIPTCTagList::find(const qint32& iTAGID) +{ + for(int x = 0;x < count();x++) + { + cIPTCTag* lpTag = at(x); + + if(lpTag->m_iTAGID == iTAGID) + return(lpTag); + } + return(nullptr); +} + +cIPTCTag* cIPTCValue::iptcTag() +{ + return(m_lpIPTCTag); +} + +cIPTCValue::cIPTCValue(cIPTCTag* lpIPTCTag) : + m_lpIPTCTag(lpIPTCTag) +{ +} + +QVariant cIPTCValue::convertValue(const QString& szValue, qint32 iTypeId) +{ + QVariant variant; + + switch(iTypeId) + { + case 1: //byte + case 3: //short + case 4: //long + case 8: //signed short + variant = QVariant::fromValue(szValue.toLong()); + break; + case 5: //rational + case 10: //signed rational + case 11: //float + if(szValue.contains("/")) + { + QStringList tmp = szValue.split("/"); + variant = QVariant::fromValue(tmp[0].toDouble()/tmp[1].toDouble()); + } + else + variant = QVariant::fromValue(szValue.toDouble()); + break; + default: + variant = szValue; + } + return(variant); +} + +void cIPTCValue::setValue(const QString& szValue, qint32 iTypeId, qint32 iCount) +{ + if(iCount == 1 || iTypeId == 2 || iTypeId == 7) + { + m_valueList.append(convertValue(szValue, iTypeId)); + return; + } + + QStringList valueList = szValue.split(" "); + + if(valueList.count() != iCount) + return; + + for(int x = 0;x < iCount;x++) + m_valueList.append(convertValue(valueList[x], iTypeId)); +} + +QVariant cIPTCValue::value() +{ + if(m_valueList.count()) + return(m_valueList[0]); + + return(QVariant()); +} + +QList cIPTCValue::valueList() +{ + return(m_valueList); +} + +cIPTCValueList::cIPTCValueList() +{ +} + +cIPTCValue* cIPTCValueList::add(cIPTCTag* lpIPTCTag) +{ + cIPTCValue* lpNew = find(lpIPTCTag); + + if(lpNew) + return(nullptr); + + lpNew = new cIPTCValue(lpIPTCTag); + + append(lpNew); + return(lpNew); +} + +cIPTCValue* cIPTCValueList::find(cIPTCTag* lpIPTCTag) +{ + for(int x = 0;x < count();x++) + { + cIPTCValue* lpValue = at(x); + if(lpValue->iptcTag() == lpIPTCTag) + return(lpValue); + } + return(nullptr); +} + +cXMPTag::cXMPTag(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription) : + m_szTAGName(szTAGName), + m_iTypeID(iTypeID), + m_szDescription(szDescription) +{ +} + +cXMPTagList::cXMPTagList(QSqlDatabase* db) : + m_dbExportDigikam(db) +{ + QSqlQuery query(*m_dbExportDigikam); + + query.prepare("SELECT tag, type, description FROM xmpTags;"); + if(!query.exec()) + return; + + while(query.next()) + add(query.value("tag").toString(), query.value("type").toInt(), query.value("description").toString()); +} + +cXMPTag* cXMPTagList::add(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription) +{ + cXMPTag* lpNew = find(szTAGName); + + if(lpNew) + return(nullptr); + + lpNew = new cXMPTag(szTAGName, iTypeID, szDescription); + append(lpNew); + return(lpNew); +} + +cXMPTag* cXMPTagList::find(const QString& szTAGName) +{ + for(int x = 0;x < count();x++) + { + cXMPTag* lpTag = at(x); + + if(lpTag->m_szTAGName == szTAGName) + return(lpTag); + } + return(nullptr); +} + +cXMPTag* cXMPValue::xmpTag() +{ + return(m_lpXMPTag); +} + +cXMPValue::cXMPValue(cXMPTag* lpXMPTag) : + m_lpXMPTag(lpXMPTag) +{ +} + +QVariant cXMPValue::convertValue(const QString& szValue, qint32 iTypeId) +{ + QVariant variant; + + switch(iTypeId) + { + case 1: //byte + case 3: //short + case 4: //long + case 8: //signed short + variant = QVariant::fromValue(szValue.toLong()); + break; + case 5: //rational + case 10: //signed rational + case 11: //float + if(szValue.contains("/")) + { + QStringList tmp = szValue.split("/"); + variant = QVariant::fromValue(tmp[0].toDouble()/tmp[1].toDouble()); + } + else + variant = QVariant::fromValue(szValue.toDouble()); + break; + default: + variant = szValue; + } + return(variant); +} + +void cXMPValue::setValue(const QString& szValue, qint32 iTypeId, qint32 /*iCount*/) +{ + m_valueList.append(convertValue(szValue, iTypeId)); +} + +QVariant cXMPValue::value() +{ + if(m_valueList.count()) + return(m_valueList[0]); + + return(QVariant()); +} + +QList cXMPValue::valueList() +{ + return(m_valueList); +} + +cXMPValueList::cXMPValueList() +{ +} + +cXMPValue* cXMPValueList::add(cXMPTag* lpXMPTag) +{ + cXMPValue* lpNew = find(lpXMPTag); + + if(lpNew) + return(nullptr); + + lpNew = new cXMPValue(lpXMPTag); + + append(lpNew); + return(lpNew); +} + +cXMPValue* cXMPValueList::find(cXMPTag* lpXMPTag) +{ + for(int x = 0;x < count();x++) + { + cXMPValue* lpValue = at(x); + if(lpValue->xmpTag() == lpXMPTag) + return(lpValue); + } + return(nullptr); +} diff --git a/cexif.h b/cexif.h new file mode 100644 index 0000000..eab92d4 --- /dev/null +++ b/cexif.h @@ -0,0 +1,889 @@ +/*! + \file cexif.h + +*/ + +#ifndef CEXIF_H +#define CEXIF_H + +#include +#include +#include +#include + +#include +#include + +#include + + +/*! + \brief + + \class cEXIFCompression cexif.h "cexif.h" +*/ +class cEXIFCompression +{ +public: + cEXIFCompression(const qint32& iID, const QString& szCompression); + + qint32 m_iID; /*!< TODO: describe */ + QString m_szCompression; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cEXIFCompression*) + +/*! + \brief + + \class cEXIFCompressionList cexif.h "cexif.h" +*/ +class cEXIFCompressionList : public QList +{ +public: + cEXIFCompressionList(QSqlDatabase* db); + + /*! + \brief + + \fn add + \param iID + \param szCompression + \return cEXIFCompression + */ + cEXIFCompression* add(const qint32& iID, const QString& szCompression); + /*! + \brief + + \fn find + \param iID + \return cEXIFCompression + */ + cEXIFCompression* find(const qint32& iID); + + /*! + \brief + + \fn compression + \param iID + \return QString + */ + QString compression(const qint32& iID); + +private: + QSqlDatabase* m_dbExportDigikam; +}; + +//Q_DECLARE_METATYPE(cEXIFCompressionList) + +/*! + \brief + + \class cEXIFLightSource cexif.h "cexif.h" +*/ +class cEXIFLightSource +{ +public: + cEXIFLightSource(const qint32& iID, const QString& szLightSource); + + qint32 m_iID; /*!< TODO: describe */ + QString m_szLightSource; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cEXIFLightSource*) + +/*! + \brief + + \class cEXIFLightSourceList cexif.h "cexif.h" +*/ +class cEXIFLightSourceList : public QList +{ +public: + cEXIFLightSourceList(QSqlDatabase* db); + + /*! + \brief + + \fn add + \param iID + \param szLightSource + \return cEXIFLightSource + */ + cEXIFLightSource* add(const qint32& iID, const QString& szLightSource); + /*! + \brief + + \fn find + \param iID + \return cEXIFLightSource + */ + cEXIFLightSource* find(const qint32& iID); + + /*! + \brief + + \fn lightSource + \param iID + \return QString + */ + QString lightSource(const qint32& iID); + +private: + QSqlDatabase* m_dbExportDigikam; +}; + +/*! + \brief + + \class cEXIFFlash cexif.h "cexif.h" +*/ +class cEXIFFlash +{ +public: + cEXIFFlash(const qint32& iID, const QString& szFlash); + + qint32 m_iID; /*!< TODO: describe */ + QString m_szFlash; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cEXIFFlash*) + +/*! + \brief + + \class cEXIFFlashList cexif.h "cexif.h" +*/ +class cEXIFFlashList : public QList +{ +public: + cEXIFFlashList(QSqlDatabase* db); + + /*! + \brief + + \fn add + \param iID + \param szFlash + \return cEXIFFlash + */ + cEXIFFlash* add(const qint32& iID, const QString& szFlash); + /*! + \brief + + \fn find + \param iID + \return cEXIFFlash + */ + cEXIFFlash* find(const qint32& iID); + + /*! + \brief + + \fn flash + \param iID + \return QString + */ + QString flash(const qint32& iID); + +private: + QSqlDatabase* m_dbExportDigikam; +}; + +/** + * @brief + * + */ +class cEXIFTag +{ +public: + cEXIFTag(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription); + + qint32 m_iTAGID; /*!< TODO: describe */ + QString m_szTAGName; /*!< TODO: describe */ + QString m_szIFD; /*!< TODO: describe */ + QString m_szKey; /*!< TODO: describe */ + QString m_szType; /*!< TODO: describe */ + QString m_szDescription; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cEXIFTag*) + +/*! + \brief + + \class cEXIFTagList cexif.h "cexif.h" +*/ +class cEXIFTagList : public QList +{ +public: + cEXIFTagList(QSqlDatabase* db); + + /*! + \brief + + \fn add + \param iTAGID + \param szTAGName + \param szIFD + \param szType + \param szDescription + \return cEXIFTag + */ + cEXIFTag* add(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription); + /*! + \brief + + \fn find + \param szKey + \return cEXIFTag + */ + cEXIFTag* find(const QString& szKey); + /*! + \brief + + \fn find + \param iTAGID + \param szIFD + \return cEXIFTag + */ + cEXIFTag* find(const qint32& iTAGID, const QString& szIFD); + /*! + \brief + + \fn find + \param szTAG + \param szIFD + \return cEXIFTag + */ + cEXIFTag* find(const QString& szTAG, const QString& szIFD); + +private: + QSqlDatabase* m_dbExportDigikam; +}; + +/*! + \brief + +*/ +class cEXIFValue +{ +public: + cEXIFValue(cEXIFTag* lpEXIFTag); + + /*! + \brief + + \fn exifTag + \return cEXIFTag + */ + cEXIFTag* exifTag(); + /*! + \brief + + \fn setValue + \param szValue + \param iTypeId + \param iCount + */ + void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1); + /*! + \brief + + \fn value + \return QVariant + */ + QVariant value(); + /*! + \brief + + \fn valueList + \return QList + */ + QList valueList(); + +private: + cEXIFTag* m_lpEXIFTag; /*!< TODO: describe */ + QList m_valueList; /*!< TODO: describe */ + + /*! + \brief + + \fn convertValue + \param szValue + \param iTypeId + \return QVariant + */ + QVariant convertValue(const QString& szValue, qint32 iTypeId); +}; + +Q_DECLARE_METATYPE(cEXIFValue*) + +/*! + \brief + + \class cEXIFValueList cexif.h "cexif.h" +*/ +class cEXIFValueList : public QList +{ +public: + cEXIFValueList(); + + /*! + \brief + + \fn add + \param lpEXIFTag + \return cEXIFValue + */ + cEXIFValue* add(cEXIFTag* lpEXIFTag); + /*! + \brief + + \fn find + \param lpEXIFTag + \return cEXIFValue + */ + cEXIFValue* find(cEXIFTag* lpEXIFTag); +}; + +/** + * @brief + * + */ +class cIPTCTag +{ +public: + cIPTCTag(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription); + + qint32 m_iTAGID; /*!< TODO: describe */ + QString m_szTAGName; /*!< TODO: describe */ + qint32 m_iTypeID; /*!< TODO: describe */ + QString m_szDescription; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cIPTCTag*) + +/*! + \brief + + \class cIPTCTagList cexif.h "cexif.h" +*/ +class cIPTCTagList : public QList +{ +public: + cIPTCTagList(QSqlDatabase* db); + + /*! + \brief + + \fn add + \param iTAGID + \param szTAGName + \param iTypeID + \param szDescription + \return cIPTCTag + */ + cIPTCTag* add(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription); + /*! + \brief + + \fn find + \param iTAGID + \return cIPTCTag + */ + cIPTCTag* find(const qint32& iTAGID); + +private: + QSqlDatabase* m_dbExportDigikam; +}; + +/*! + \brief + +*/ +class cIPTCValue +{ +public: + cIPTCValue(cIPTCTag* lpIPTCTag); + + /*! + \brief + + \fn iptcTag + \return cIPTCTag + */ + cIPTCTag* iptcTag(); + /*! + \brief + + \fn setValue + \param szValue + \param iTypeId + \param iCount + */ + void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1); + /*! + \brief + + \fn value + \return QVariant + */ + QVariant value(); + /*! + \brief + + \fn valueList + \return QList + */ + QList valueList(); + +private: + cIPTCTag* m_lpIPTCTag; /*!< TODO: describe */ + QList m_valueList; /*!< TODO: describe */ + + /*! + \brief + + \fn convertValue + \param szValue + \param iTypeId + \return QVariant + */ + QVariant convertValue(const QString& szValue, qint32 iTypeId); +}; + +Q_DECLARE_METATYPE(cIPTCValue*) + +/*! + \brief + + \class cIPTCValueList cexif.h "cexif.h" +*/ +class cIPTCValueList : public QList +{ +public: + cIPTCValueList(); + + /*! + \brief + + \fn add + \param lpIPTCTag + \return cIPTCValue + */ + cIPTCValue* add(cIPTCTag* lpIPTCTag); + /*! + \brief + + \fn find + \param lpIPTCTag + \return cIPTCValue + */ + cIPTCValue* find(cIPTCTag* lpIPTCTag); +}; + +/** + * @brief + * + */ +class cXMPTag +{ +public: + cXMPTag(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription); + + QString m_szTAGName; /*!< TODO: describe */ + qint32 m_iTypeID; /*!< TODO: describe */ + QString m_szDescription; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cXMPTag*) + +/*! + \brief + + \class cXMPTagList cexif.h "cexif.h" +*/ +class cXMPTagList : public QList +{ +public: + cXMPTagList(QSqlDatabase* db); + + /*! + \brief + + \fn add + \param szTAGName + \param iTypeID + \param szDescription + \return cXMPTag + */ + cXMPTag* add(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription); + /*! + \brief + + \fn find + \param szTAGName + \return cXMPTag + */ + cXMPTag* find(const QString& szTAGName); + +private: + QSqlDatabase* m_dbExportDigikam; +}; + +/*! + \brief + +*/ +class cXMPValue +{ +public: + cXMPValue(cXMPTag* lpXMPTag); + + /*! + \brief + + \fn exifTag + \return cXMPTag + */ + cXMPTag* xmpTag(); + /*! + \brief + + \fn setValue + \param szValue + \param iTypeId + \param iCount + */ + void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1); + /*! + \brief + + \fn value + \return QVariant + */ + QVariant value(); + /*! + \brief + + \fn valueList + \return QList + */ + QList valueList(); + +private: + cXMPTag* m_lpXMPTag; /*!< TODO: describe */ + QList m_valueList; /*!< TODO: describe */ + + /*! + \brief + + \fn convertValue + \param szValue + \param iTypeId + \return QVariant + */ + QVariant convertValue(const QString& szValue, qint32 iTypeId); +}; + +Q_DECLARE_METATYPE(cXMPValue*) + +/*! + \brief + + \class cXMPValueList cexif.h "cexif.h" +*/ +class cXMPValueList : public QList +{ +public: + cXMPValueList(); + + /*! + \brief + + \fn add + \param lpXMPTag + \return cXMPValue + */ + cXMPValue* add(cXMPTag* lpXMPTag); + /*! + \brief + + \fn find + \param lpXMPTag + \return cXMPValue + */ + cXMPValue* find(cXMPTag* lpXMPTag); +}; + +/*! + \brief + + \class cEXIF cexif.h "cexif.h" +*/ +class cEXIF +{ +public: + cEXIF(cEXIFTagList* lpEXIFTagList, cEXIFCompressionList* lpEXIFCompressionList, cEXIFLightSourceList* lpEXIFLightSourceList, cEXIFFlashList* lpEXIFFlashList, cIPTCTagList* lpIPTCTagList, cXMPTagList* lpXMPTagList); + + /*! + \brief + + \fn fromFile + \param szFileName + \param withThumbnail + \return bool + */ + bool fromFile(const QString& szFileName, bool withThumbnail = true); + + /*! + \brief + + \fn mimeType + \return QString + */ + QString mimeType(); + /*! + \brief + + \fn imageWidth + \return qint32 + */ + qint32 imageWidth(); + /*! + \brief + + \fn imageHeight + \return qint32 + */ + qint32 imageHeight(); + /*! + \brief + + \fn imageOrientation + \return qint16 + */ + qint16 imageOrientation(); + /*! + \brief + + \fn cameraMake + \return QString + */ + QString cameraMake(); + /*! + \brief + + \fn cameraModel + \return QString + */ + QString cameraModel(); + /*! + \brief + + \fn dateTime + \return QDateTime + */ + QDateTime dateTime(); + /*! + \brief + + \fn fNumber + \return QString + */ + QString fNumber(); + /*! + \brief + + \fn iso + \return qint32 + */ + qint32 iso(); + /*! + \brief + + \fn flash + \return QString + */ + QString flash(); + /*! + \brief + + \fn flashID + \return qint32 + */ + qint32 flashID(); + /*! + \brief + + \fn focalLength + \return qreal + */ + qreal focalLength(); + /*! + \brief + + \fn lensMake + \return QString + */ + QString lensMake(); + /*! + \brief + + \fn lensModel + \return QString + */ + QString lensModel(); + /*! + \brief + + \fn exposureTime + \return QString + */ + QString exposureTime(); + /*! + \brief + + \fn exposureBias + \return qint32 + */ + qint32 exposureBias(); + /*! + \brief + + \fn exifVersion + \return QString + */ + QString exifVersion(); + /*! + \brief + + \fn dateTimeOriginal + \return QDateTime + */ + QDateTime dateTimeOriginal(); + /*! + \brief + + \fn dateTimeDigitized + \return QDateTime + */ + QDateTime dateTimeDigitized(); + /*! + \brief + + \fn whiteBalance + \return qint32 + */ + qint32 whiteBalance(); + /*! + \brief + + \fn focalLength35 + \return qreal + */ + qreal focalLength35(); + /*! + \brief + + \fn gps + \return QString + */ + QString gps(); + /*! + \brief + + \fn duration + \return QString + */ + QString duration(); + /*! + \brief + + \fn fileName + \return QString + */ + QString fileName(); + /*! + \brief + + \fn previewList + \return QList + */ + QList previewList(); + /*! + \brief + + \fn thumbnail + \return QImage + */ + QImage thumbnail(); + + /*! + \brief + + \fn copyTo + \param fileName + */ + bool copyTo(const QString& fileName); + +private: + cEXIFValueList m_exifValueList; /*!< TODO: describe */ + cIPTCValueList m_iptcValueList; /*!< TODO: describe */ + cXMPValueList m_xmpValueList; /*!< TODO: describe */ + QString m_szMimeType; /*!< TODO: describe */ + qint32 m_iWidth; /*!< TODO: describe */ + qint32 m_iHeight; /*!< TODO: describe */ + QString m_szFileName; /*!< TODO: describe */ + QList m_previewList; /*!< TODO: describe */ + QImage m_thumbnail; /*!< TODO: describe */ + + cEXIFTagList* m_lpEXIFTagList; /*!< TODO: describe */ + cEXIFCompressionList* m_lpEXIFCompressionList; /*!< TODO: describe */ + cEXIFLightSourceList* m_lpEXIFLightSourceList; /*!< TODO: describe */ + cEXIFFlashList* m_lpEXIFFlashList; /*!< TODO: describe */ + + cIPTCTagList* m_lpIPTCTagList; /*!< TODO: describe */ + + cXMPTagList* m_lpXMPTagList; /*!< TODO: describe */ + + /*! + \brief + + \fn getEXIFTag + \param iTAGID + \param szIFD + \return QVariant + */ + QVariant getEXIFTag(qint32 iTAGID, const QString& szIFD); + /*! + \brief + + \fn getIPTCTag + \param iTAGID + \return QVariant + */ + QVariant getIPTCTag(qint32 iTAGID); + /*! + \brief + + \fn getXMPTag + \param szTAGName + \return QVariant + */ + QVariant getXMPTag(const QString& szTAGName); + /*! + \brief + + \fn getTagList + \param iTAGID + \param iIFDID + \return QList + */ + QList getTagList(qint32 iTAGID, const QString& szIFD); +}; + +Q_DECLARE_METATYPE(cEXIF*) + +#endif // CEXIF_H diff --git a/cimage.cpp b/cimage.cpp index 20b0959..a6159e5 100644 --- a/cimage.cpp +++ b/cimage.cpp @@ -16,6 +16,8 @@ #include #include +#include + cImage::cImage() : QImage(), @@ -119,7 +121,35 @@ bool cImage::openBuffer(const QString &fileName, const QScopedPointerisEmpty()) + { +#ifdef Q_OS_WIN + QString in = fileName; + long length = 0; + TCHAR input[fileName.length()]; + TCHAR* buffer = NULL; + QString x; + + in.toWCharArray(input); + length = GetShortPathName(input, NULL, 0); + buffer = new TCHAR[length]; + + length = GetShortPathName(input, buffer, length); + if(length) + { + x = QString::fromWCharArray(buffer); + error = iProcessor.open_file(x.toStdString().c_str()); + delete [] buffer; + } + else + { + delete [] buffer; + return(false); + } + +#elif Q_OS_UNIX error = iProcessor.open_file(fileName.toStdString().c_str()); +#endif + } else { if(ba->isEmpty() || ba->size() < 100) diff --git a/cimages.cpp b/cimages.cpp index 24a4f49..6767e67 100644 --- a/cimages.cpp +++ b/cimages.cpp @@ -41,6 +41,15 @@ cImages::cImages(const qint32& id, cAlbums* albums, const QString& name, const q { } +cImages::~cImages() +{ + if(m_thumbnail) + delete m_thumbnail; + + if(m_item) + delete m_item; +} + qint32 cImages::id() { return(m_id); @@ -308,6 +317,10 @@ cImagesList::cImagesList(QSqlDatabase* dbDigikam, QSqlDatabase *dbThumbnail, cAl { } +cImagesList::~cImagesList() +{ +} + bool cImagesList::load(bool loadThumbnails, cAlbums* albums) { if(!m_dbDigikam) diff --git a/cimages.h b/cimages.h index 89f896a..fa0d101 100644 --- a/cimages.h +++ b/cimages.h @@ -26,6 +26,7 @@ class cImages : public QObject public: cImages(const qint32& id, QObject *parent = nullptr); cImages(const qint32& id, cAlbums* albums, const QString& name, const qint8& status, const qint8& category, const QDateTime& modificationDate, const qint32& fileSize, const QString& uniqueHash, const qint32& manualOrder, QSqlDatabase* dbThumbnail, QObject *parent = nullptr); + ~cImages(); qint32 id(); @@ -114,6 +115,7 @@ class cImagesList : public QObject, public QList public: cImagesList(QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, cAlbumsList* albumsList, QObject* parent = nullptr); + ~cImagesList(); bool load(bool loadThumbnails, cAlbums* albums); bool load(bool loadThumbnails, const qint32& albumsID); diff --git a/cmainwindow.cpp b/cmainwindow.cpp index a944f12..95d394f 100644 --- a/cmainwindow.cpp +++ b/cmainwindow.cpp @@ -1,3 +1,5 @@ +#include "common.h" + #include "cmainwindow.h" #include "ui_cmainwindow.h" @@ -17,6 +19,12 @@ #include #include +#include +#include +#include + + +#define DBROOT "C:\\Users\\birkeh\\OneDrive\\__MEDIA__" void addToExportLog(QString& exportLog, const QString& text) { @@ -30,7 +38,7 @@ cMainWindow::cMainWindow(QWidget *parent) : ui(new Ui::cMainWindow), m_progressBar(nullptr), m_listToolBar(nullptr), - m_refreshAction(nullptr), +// m_refreshAction(nullptr), m_actionToolBar(nullptr), m_exportAction(nullptr), m_stopAction(nullptr), @@ -38,9 +46,17 @@ cMainWindow::cMainWindow(QWidget *parent) : m_albumsList(nullptr), m_folderViewModel(nullptr), m_folderSortFilterProxyModel(nullptr), + m_includeViewModel(nullptr), + m_excludeViewModel(nullptr), m_thumbnailViewModel(nullptr), m_thumbnailSortFilterProxyModel(nullptr), m_rootItem(nullptr), + m_exifTAGList(nullptr), + m_exifCompressionList(nullptr), + m_exifLightSourceList(nullptr), + m_exifFlashList(nullptr), + m_iptcTagList(nullptr), + m_xmpTagList(nullptr), m_loading(false), m_working(false), m_stopIt(false), @@ -48,6 +64,9 @@ cMainWindow::cMainWindow(QWidget *parent) : { initUI(); createActions(); + + m_volumes = actuallyListVolumes(); + setImageFormats(); loadData(); initSignals(); @@ -55,8 +74,27 @@ cMainWindow::cMainWindow(QWidget *parent) : cMainWindow::~cMainWindow() { - if(m_albumRootsList) - delete m_albumRootsList; + _DELETE_(m_progressBar); + _DELETE_(m_listToolBar); + _DELETE_(m_refreshAction); + _DELETE_(m_actionToolBar); + _DELETE_(m_exportAction); + _DELETE_(m_stopAction); + _DELETE_(m_albumRootsList); + _DELETE_(m_albumsList); + _DELETE_(m_excludeViewModel); + _DELETE_(m_includeViewModel); + _DELETE_(m_folderViewModel); + _DELETE_(m_folderSortFilterProxyModel); + _DELETE_(m_thumbnailViewModel); + _DELETE_(m_thumbnailSortFilterProxyModel); + _DELETE_(m_rootItem); + _DELETE_(m_exifTAGList); + _DELETE_(m_exifCompressionList); + _DELETE_(m_exifLightSourceList); + _DELETE_(m_exifFlashList); + _DELETE_(m_iptcTagList); + _DELETE_(m_xmpTagList); if(m_dbThumbnail.isOpen()) m_dbThumbnail.close(); @@ -64,6 +102,9 @@ cMainWindow::~cMainWindow() if(m_dbDigikam.isOpen()) m_dbDigikam.close(); + if(m_dbExportDigikam.isOpen()) + m_dbExportDigikam.close(); + delete ui; } @@ -100,11 +141,17 @@ void cMainWindow::initUI() ui->m_folderView->setModel(m_folderSortFilterProxyModel); m_folderSortFilterProxyModel->setSourceModel(m_folderViewModel); - m_thumbnailViewModel = new QStandardItemModel; + m_thumbnailViewModel = new QStandardItemModel; m_thumbnailSortFilterProxyModel = new cThumbnailSortFilterProxyModel(this); ui->m_thumbnailView->setModel(m_thumbnailSortFilterProxyModel); m_thumbnailSortFilterProxyModel->setSourceModel(m_thumbnailViewModel); + m_includeViewModel = new QStandardItemModel; + ui->m_includeView->setModel(m_includeViewModel); + + m_excludeViewModel = new QStandardItemModel; + ui->m_excludeView->setModel(m_excludeViewModel); + QSettings settings; if(!settings.value("main/maximized").toBool()) @@ -167,26 +214,31 @@ void cMainWindow::setImageFormats() QList readList = QImageReader::supportedImageFormats(); QList writeList = QImageWriter::supportedImageFormats(); - QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "exportDigikam"); - db.setHostName("localhost"); - db.setDatabaseName("exportDigikam.db"); + m_dbExportDigikam = QSqlDatabase::addDatabase("QSQLITE", "exportDigikam"); + m_dbExportDigikam.setHostName("localhost"); + m_dbExportDigikam.setDatabaseName("exportDigikam.db"); - if(!db.open()) + if(!m_dbExportDigikam.open()) return; - QSqlQuery query(db); + QSqlQuery query(m_dbExportDigikam); query.prepare("SELECT shortname, description, extension FROM imageFormat;"); if(!query.exec()) { - db.close(); + qDebug() << "reading from exportDigikam.db failed. " << query.lastError(); return; } while(query.next()) addImageFormat(query.value("shortname").toString(), query.value("description").toString(), query.value("extension").toString(), readList, writeList); - db.close(); + m_exifTAGList = new cEXIFTagList(&m_dbExportDigikam); + m_exifCompressionList = new cEXIFCompressionList(&m_dbExportDigikam); + m_exifLightSourceList = new cEXIFLightSourceList(&m_dbExportDigikam); + m_exifFlashList = new cEXIFFlashList(&m_dbExportDigikam); + m_iptcTagList = new cIPTCTagList(&m_dbExportDigikam); + m_xmpTagList = new cXMPTagList(&m_dbExportDigikam); } void cMainWindow::addImageFormat(const QString& shortName, const QString& description, const QString& extension, QList& readList, QList& writeList) @@ -208,12 +260,14 @@ void cMainWindow::addImageFormat(const QString& shortName, const QString& descri void cMainWindow::loadData() { + m_includeViewModel->clear(); + m_excludeViewModel->clear(); m_thumbnailViewModel->clear(); m_folderViewModel->clear(); - QElapsedTimer timer; +// QElapsedTimer timer; - timer.start(); +// timer.start(); if(m_albumRootsList) delete m_albumRootsList; @@ -224,7 +278,7 @@ void cMainWindow::loadData() if(!m_dbDigikam.isOpen()) { m_dbDigikam = QSqlDatabase::addDatabase("QSQLITE", "digikam4"); - m_dbDigikam.setDatabaseName("C:\\Temp\\__DIGIKAM__\\digikam4.db"); + m_dbDigikam.setDatabaseName(QString(DBROOT) + QString("\\digikam4.db")); if(!m_dbDigikam.open()) { qDebug() << "Digikam: DB Open failed. " << m_dbDigikam.lastError().text(); @@ -235,7 +289,7 @@ void cMainWindow::loadData() if(!m_dbThumbnail.isOpen()) { m_dbThumbnail = QSqlDatabase::addDatabase("QSQLITE", "thumbnails-digikam"); - m_dbThumbnail.setDatabaseName("C:\\Temp\\__DIGIKAM__\\thumbnails-digikam.db"); + m_dbThumbnail.setDatabaseName(DBROOT + QString("\\thumbnails-digikam.db")); if(!m_dbThumbnail.open()) { qDebug() << "Thumbnail: DB Open failed. " << m_dbDigikam.lastError().text(); @@ -243,6 +297,19 @@ void cMainWindow::loadData() } } + m_tagsList = new cTagsList(&m_dbDigikam); + if(!m_tagsList) + { + qDebug() << "Tags List: out of memory."; + return; + } + + if(!m_tagsList->load()) + { + qDebug() << "Tags List: load failed."; + return; + } + m_albumRootsList = new cAlbumRootsList(&m_dbDigikam, &m_dbThumbnail, this); if(!m_albumRootsList) { @@ -250,7 +317,7 @@ void cMainWindow::loadData() return; } - if(!m_albumRootsList->load()) + if(!m_albumRootsList->load(&m_volumes)) { qDebug() << "Album Root List: load failed."; return; @@ -269,7 +336,7 @@ void cMainWindow::loadData() return; } - qDebug() << "Loading took " << timer.elapsed() << "ms"; +// qDebug() << "Loading took " << timer.elapsed() << "ms"; displayData(); ui->m_folderView->expandAll(); @@ -279,6 +346,33 @@ void cMainWindow::displayData() { m_loading = true; + m_includeViewModel->clear(); + m_excludeViewModel->clear(); + + for(int x = 0;x < m_tagsList->count();x++) + { + cTags* lpTags = m_tagsList->at(x); + + if(!lpTags->parent()) + { + QStandardItem* lpInclude = new QStandardItem(lpTags->name()); + lpInclude->setData(QVariant::fromValue(lpTags), Qt::UserRole+1); + lpInclude->setCheckable(true); + + QStandardItem* lpExclude = new QStandardItem(lpTags->name()); + lpExclude->setData(QVariant::fromValue(lpTags), Qt::UserRole+1); + lpExclude->setCheckable(true); + + lpTags->setIncludeItem(lpInclude); + lpTags->setExcludeItem(lpExclude); + + m_includeViewModel->appendRow(lpInclude); + m_excludeViewModel->appendRow(lpExclude); + + addChildren(lpTags, lpInclude, lpExclude); + } + } + m_thumbnailViewModel->clear(); m_folderViewModel->clear(); @@ -307,13 +401,47 @@ void cMainWindow::displayData() if(lpAlbums->parentAlbums()) lpRootItem = lpAlbums->parentAlbums()->item(); - lpAlbums->setItem(lpItem); - lpRootItem->appendRow(lpItem); + if(!lpRootItem) + delete(lpItem); + else + { + lpAlbums->setItem(lpItem); + lpRootItem->appendRow(lpItem); + } } m_loading = false; } +void cMainWindow::addChildren(cTags* tags, QStandardItem* include, QStandardItem* exclude) +{ + cTagsList* tagsList = tags->childList(); + + if(!tagsList) + return; + + for(int i = 0;i < tagsList->count();i++) + { + cTags* tags = tagsList->at(i); + + QStandardItem* lpInclude = new QStandardItem(tags->name()); + lpInclude->setData(QVariant::fromValue(tags), Qt::UserRole+1); + lpInclude->setCheckable(true); + + QStandardItem* lpExclude = new QStandardItem(tags->name()); + lpExclude->setData(QVariant::fromValue(tags), Qt::UserRole+1); + lpExclude->setCheckable(true); + + tags->setIncludeItem(lpInclude); + tags->setExcludeItem(lpExclude); + + include->appendRow(lpInclude); + exclude->appendRow(lpExclude); + + addChildren(tags, lpInclude, lpExclude); + } +} + QStandardItem* cMainWindow::findDBRootItem(QStandardItemModel* model, QStandardItem *rootItem, cAlbumRoots* albumRoots) { QModelIndex parent; @@ -359,6 +487,8 @@ QStandardItem* cMainWindow::findParentItem(QStandardItemModel* model, QStandardI void cMainWindow::initSignals() { connect(m_folderViewModel, &QStandardItemModel::itemChanged, this, &cMainWindow::onFolderViewItemChanged); + connect(m_includeViewModel, &QStandardItemModel::itemChanged, this, &cMainWindow::onIncludeViewItemChanged); + connect(m_excludeViewModel, &QStandardItemModel::itemChanged, this, &cMainWindow::onExcludeViewItemChanged); connect(ui->m_folderView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &cMainWindow::onFolderSelected); connect(ui->m_thumbnailView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &cMainWindow::onThumbnailSelected); } @@ -411,6 +541,36 @@ void cMainWindow::onFolderViewItemChanged(QStandardItem* item) } } +void cMainWindow::onIncludeViewItemChanged(QStandardItem* item) +{ + Qt::CheckState state = item->checkState(); + QModelIndex parent = item->index(); + + for(int x = 0;x < m_includeViewModel->rowCount(parent);x++) + { + QStandardItem* curItem = m_includeViewModel->itemFromIndex(m_includeViewModel->index(x, 0, parent)); + if(!curItem) + continue; + + curItem->setCheckState(state); + } +} + +void cMainWindow::onExcludeViewItemChanged(QStandardItem* item) +{ + Qt::CheckState state = item->checkState(); + QModelIndex parent = item->index(); + + for(int x = 0;x < m_excludeViewModel->rowCount(parent);x++) + { + QStandardItem* curItem = m_excludeViewModel->itemFromIndex(m_excludeViewModel->index(x, 0, parent)); + if(!curItem) + continue; + + curItem->setCheckState(state); + } +} + void cMainWindow::onFolderSelected(const QItemSelection& /*selection*/, const QItemSelection& /*previous*/) { if(m_loading) @@ -565,6 +725,28 @@ done: if(exportDialog.exec() == QDialog::Rejected) return; + m_exportInclude.clear(); + m_exportExclude.clear(); + + for(int i = 0;i < m_tagsList->count();i++) + { + cTags* tags = m_tagsList->at(i); + QStandardItem* included = tags->includeItem(); + QStandardItem* excluded = tags->excludeItem(); + + if(included) + { + if(included->checkState()) + m_exportInclude.append(tags->id()); + } + + if(excluded) + { + if(excluded->checkState()) + m_exportExclude.append(tags->id()); + } + } + m_working = true; m_stopIt = false; // setActionEnabled(false, false, false, false, false, true); @@ -575,6 +757,7 @@ done: void cMainWindow::onStop() { + m_stopIt = true; } void cMainWindow::doExport() @@ -606,16 +789,25 @@ void cMainWindow::doExport() qint32 fileCount = 0; for(int x = 0;x < m_albumsList->count();x++) { + if(m_stopIt) + break; + cAlbums* albums = m_albumsList->at(x); if(albums) { + if(m_stopIt) + break; + cImagesList* imagesList = albums->imagesList(); if(imagesList) { for(int y = 0;y < imagesList->count();y++) { + if(m_stopIt) + break; + cImages* images = albums->imagesList()->at(y); if(images) @@ -660,12 +852,23 @@ void cMainWindow::doExport() { if(item->checkState() == Qt::Checked) { - m_progressBar->setValue(curFile); - ui->m_statusBar->showMessage(images->name()); - qApp->processEvents(); -// overwrite = exportFile(exportSettings, overwrite); + if(m_stopIt) + break; - curFile++; + cAlbums* albums = images->albums(); + QString path = albums->albumRoots()->drive() + albums->albumRoots()->specificPath() + "/" + albums->relativePath(); + QString file = path + "/" + images->name(); + + ui->m_statusBar->showMessage(QString(tr("checking file %1...")).arg(file)); + m_progressBar->setValue(curFile); + qApp->processEvents(); + + cEXIF exif(m_exifTAGList, m_exifCompressionList, m_exifLightSourceList, m_exifFlashList, m_iptcTagList, m_xmpTagList); + if(exif.fromFile(file, false)) + { + overwrite = exportFile(exportSettings, &exif, images, overwrite); + curFile++; + } } } } @@ -674,29 +877,6 @@ void cMainWindow::doExport() } } -// for(int i = 0;i < m_lpFileListModel->rowCount();i++) -// { -// QStandardItem* lpItem = m_lpFileListModel->item(i, 0); -// if(!lpItem) -// continue; - -// cEXIF* lpExif = lpItem->data(Qt::UserRole+1).value(); -// if(!lpExif) -// continue; - -// overwrite = exportFile(exportSettings, lpExif, overwrite); - -// m_lpProgressBar->setValue(i+1); - -// qApp->processEvents(); - -// if(m_stopIt) -// { -// addToExportLog(m_exportLog, "aborted."); -// break; -// } -// } - m_progressBar->setVisible(false); ui->m_statusBar->showMessage(tr("export done."), 3000); @@ -710,21 +890,6 @@ void cMainWindow::doExport() logWindow.exec(); } -/* - DIRECTORY_METHOD directoryMethod; - QString directory; - bool keepStructure; - QString directoryTag; - STRIP_LAST_FOLDER_METHOD stripLastFolderMethod; - QStringList stripLastFolderIfList; - FILE_METHOD fileMethod; - FILE_ADD fileAdd; - QString fileTag; - OVERWRITE_METHOD overwriteMethod; - bool copyEXIF; - QString fileFormat; - int quality; -*/ void cMainWindow::getExportSettings(EXPORTSETTINGS& exportSettings) { QSettings settings; @@ -840,3 +1005,316 @@ void cMainWindow::getExportSettings(EXPORTSETTINGS& exportSettings) addToExportLog(m_exportLog, "File Format: " + exportSettings.fileFormat + ""); addToExportLog(m_exportLog, "Default Output Quality: " + QString::number(exportSettings.quality) + ""); } + +OVERWRITE cMainWindow::exportFile(const EXPORTSETTINGS& exportSettings, cEXIF* lpExif, cImages* images, OVERWRITE overwrite) +{ + addToExportLog(m_exportLog, "Loading file: " + lpExif->fileName() + ""); + + if(m_exportExclude.count() || m_exportInclude.count()) + { + QSqlQuery query(m_dbDigikam); + QString sql = QString("SELECT tagId " + "FROM ImageTags " + "WHERE imageId=%1;").arg(images->id()); + + bool doExport = false; + QList idList; + + if(query.exec(sql)) + { + while(query.next()) + idList.append(query.value("tagId").toInt()); + } + + if(!m_exportInclude.count()) + doExport = true; + else + { + for(int i = 0;i < idList.count();i++) + { + if(m_exportInclude.contains(idList.at(i))) + { + doExport = true; + break; + } + } + } + + if(doExport) + { + for(int i = 0;i < idList.count();i++) + { + if(m_exportExclude.contains(idList.at(i))) + { + doExport = false; + break; + } + } + } + + if(!doExport) + { + addToExportLog(m_exportLog, "Skipped due to exclude/include list"); + return(overwrite); + } + } + + QString destPath; + QString destFile; + QFileInfo fileInfo(lpExif->fileName()); + QString extension = exportSettings.fileFormat; + bool isMovie = false; + + extension = extension.mid(extension.lastIndexOf(".")+1); + extension = extension.left(extension.length()-1); + + if(!images->videoMetadata().videoCodec.isEmpty()) + { + extension = lpExif->fileName().mid(lpExif->fileName().lastIndexOf(".")+1); + isMovie = true; + } + + switch(exportSettings.directoryMethod) + { + case DIRECTORY_METHOD_NEW: + destPath = exportSettings.directory; + if(exportSettings.keepStructure) + { + destPath.append(images->albums()->relativePath()); + + if(exportSettings.stripLastFolderMethod != STRIP_LAST_FOLDER_NO) + { + bool strip = true; + + if(exportSettings.stripLastFolderMethod == STRIP_LAST_FOLDER_IF) + { + QString lastFolder = destPath.mid(destPath.lastIndexOf("/")+1); + if(!exportSettings.stripLastFolderIfList.contains(lastFolder, Qt::CaseInsensitive)) + strip = false; + } + + if(strip) + destPath = destPath.left(destPath.lastIndexOf("/")); + } + } + break; + case DIRECTORY_METHOD_KEEP: + destPath = fileInfo.absolutePath(); + break; + case DIRECTORY_METHOD_TAG: + destPath = replaceTags(exportSettings.directoryTag, lpExif, extension); + break; + } + + switch(exportSettings.fileMethod) + { + case FILE_METHOD_KEEP: + destFile = fileInfo.completeBaseName() + "." + extension; + break; + case FILE_METHOD_RENAME: + switch(exportSettings.fileAdd) + { + case FILE_ADD_CONVERTED: + destFile = fileInfo.completeBaseName() + "_converted" + "." + extension; + break; + case FILE_ADD_TAG: + destFile = replaceTags(exportSettings.fileTag, lpExif, extension, false) + "." + extension; + break; + } + break; + } + + destFile.prepend(destPath + "/"); + QFileInfo destInfo(destFile); + + addToExportLog(m_exportLog, "Destination: " + destFile + ""); + + if(destInfo.exists() && overwrite != OVERWRITE_ALL) + { + switch(exportSettings.overwriteMethod) + { + case OVERWRITE_METHOD_ASK: + { + QMessageBox::StandardButton ret = QMessageBox::question(this, tr("File Exists"), QString(tr("File %1 exists. Do you want to overwrite?")).arg(destFile), QMessageBox::Yes | QMessageBox::No | QMessageBox::YesAll | QMessageBox::NoAll); + if(ret == QMessageBox::YesAll) + { + overwrite = OVERWRITE_ALL; + addToExportLog(m_exportLog, "New Option: overwrite all"); + } + else if(ret == QMessageBox::NoAll) + { + overwrite = OVERWRITE_NONE; + addToExportLog(m_exportLog, "New Option: overwrite none"); + addToExportLog(m_exportLog, "aborting, destination file exists"); + return(overwrite); + } + else if(ret == QMessageBox::No) + { + addToExportLog(m_exportLog, "aborting, destination file exists"); + return(overwrite); + } + else + addToExportLog(m_exportLog, "file exists, overwrite"); + } + break; + case OVERWRITE_METHOD_RENAME: + destFile = findFreeFileName(destFile); + if(destFile.isEmpty()) + { + QMessageBox::information(this, tr("File Export"), QString("%1 cannot be renamed.").arg(lpExif->fileName())); + addToExportLog(m_exportLog, "aborting, file cannot be renamed"); + return(overwrite); + } + addToExportLog(m_exportLog, "renaming to " + destFile + ""); + break; + case OVERWRITE_METHOD_OVERWRITE: + break; + case OVERWRITE_METHOD_NOEXPORT: + addToExportLog(m_exportLog, "aborting, destination file exists"); + return(overwrite); + break; + } + } + else if(destInfo.exists() && (overwrite == OVERWRITE_NONE || exportSettings.overwriteMethod == OVERWRITE_METHOD_NOEXPORT)) + { + addToExportLog(m_exportLog, "aborting, destination file exists"); + return(overwrite); + } + + ui->m_statusBar->showMessage(QString(tr("loading %1...")).arg(lpExif->fileName())); + qApp->processEvents(); + + if(!isMovie) + { + cImage image(lpExif->fileName()); + if(!image.isNull()) + { + if(!image.isRaw() || !fileInfo.suffix().compare("cr3", Qt::CaseInsensitive)) + { + QTransform rotation; + int angle = 0; + + switch(images->imageInformation().orientation) + { + case 8: + angle = 270; + break; + case 3: + angle = 180; + break; + case 6: + angle = 90; + break; + } + + if(angle != 0) + { + rotation.rotate(angle); + image = image.transformed(rotation); + } + } + + addToExportLog(m_exportLog, "Loading file: successful"); + + QFileInfo info(destFile); + + ui->m_statusBar->showMessage(QString(tr("converting to %1...")).arg(destFile)); + qApp->processEvents(); + + QDir dir; + + if(dir.mkpath(info.absolutePath())) + { + QImageWriter writer(destFile); + + addToExportLog(m_exportLog, "Writing file: " + destFile + ""); + writer.setQuality(exportSettings.quality); + if(writer.write(image)) + { + addToExportLog(m_exportLog, "Writing file: successful"); + + if(exportSettings.copyEXIF) + { + if(lpExif->copyTo(destFile)) + addToExportLog(m_exportLog, "Copy EXIF: successful"); + else + addToExportLog(m_exportLog, "Copy EXIF: error"); + } + } + else + addToExportLog(m_exportLog, "Writing file: error: " + writer.errorString() + ""); + } + } + } + else + { + addToExportLog(m_exportLog, "Loading file: no image"); + + QFileInfo info(destFile); + + ui->m_statusBar->showMessage(QString(tr("copying to %1...")).arg(destFile)); + qApp->processEvents(); + + QDir dir; + + if(dir.mkpath(info.absolutePath())) + { + if(QFile::copy(lpExif->fileName(), destFile)) + addToExportLog(m_exportLog, "Writing file: successful"); + else + addToExportLog(m_exportLog, "Copy EXIF: error"); + } + } + + return(overwrite); +} + +QString cMainWindow::replaceTags(const QString& path, cEXIF* lpExif, const QString& extension, bool directory) +{ + QString dest = path; + QFileInfo fileInfo(lpExif->fileName()); + QString model = lpExif->cameraModel().replace("/", "_").replace("\\", "_").replace(":", "_"); + QString maker = lpExif->cameraMake().replace("/", "_").replace("\\", "_").replace(":", "_"); + + if(model.isEmpty()) + model = "UNKNOWN"; + + if(maker.isEmpty()) + maker = "UNKNOWN"; + + if(directory) + dest = dest.replace("%o", fileInfo.absolutePath()); + else + dest = dest.replace("%o", fileInfo.fileName()); + + dest = dest.replace("%y", lpExif->dateTime().toString("yyyy")); + dest = dest.replace("%m", lpExif->dateTime().toString("MM")); + dest = dest.replace("%d", lpExif->dateTime().toString("dd")); + dest = dest.replace("%H", lpExif->dateTime().toString("hh")); + dest = dest.replace("%M", lpExif->dateTime().toString("mm")); + dest = dest.replace("%S", lpExif->dateTime().toString("ss")); + dest = dest.replace("%c", maker); + dest = dest.replace("%l", model); + dest = dest.replace("%t", extension); + + dest = dest.replace("\\", "/"); + + return(dest); +} + +QString cMainWindow::findFreeFileName(const QString& fileName) +{ + QFileInfo fileInfo(fileName); + QString newName; + int number = 1; + + for(;;) + { + newName = fileInfo.absolutePath() + "/" + fileInfo.completeBaseName() + "-" + QStringLiteral("%1").arg(number, 3, 10, QLatin1Char('0')) + "." + fileInfo.suffix(); + + if(!QFileInfo::exists(newName)) + return(newName); + + number++; + } +} diff --git a/cmainwindow.h b/cmainwindow.h index a131004..bc4b81d 100644 --- a/cmainwindow.h +++ b/cmainwindow.h @@ -6,9 +6,12 @@ #include "calbumroots.h" #include "calbums.h" +#include "ctags.h" #include "cfoldersortfilterproxymodel.h" #include "cthumbnailsortfilterproxymodel.h" +#include "cexif.h" + #include #include @@ -109,23 +112,39 @@ private: QSqlDatabase m_dbDigikam; QSqlDatabase m_dbThumbnail; + QSqlDatabase m_dbExportDigikam; cAlbumRootsList* m_albumRootsList; cAlbumsList* m_albumsList; + cTagsList* m_tagsList; QStandardItemModel* m_folderViewModel; cFolderSortFilterProxyModel* m_folderSortFilterProxyModel; + QStandardItemModel* m_includeViewModel; + QStandardItemModel* m_excludeViewModel; QStandardItemModel* m_thumbnailViewModel; cThumbnailSortFilterProxyModel* m_thumbnailSortFilterProxyModel; QStandardItem* m_rootItem; QList m_imageFormats; + cEXIFTagList* m_exifTAGList; + cEXIFCompressionList* m_exifCompressionList; + cEXIFLightSourceList* m_exifLightSourceList; + cEXIFFlashList* m_exifFlashList; + cIPTCTagList* m_iptcTagList; + cXMPTagList* m_xmpTagList; + bool m_loading; bool m_working; bool m_stopIt; + QList m_exportInclude; + QList m_exportExclude; + QString m_exportLog; + QList m_volumes; + void initUI(); void createActions(); void createMenuActions(); @@ -134,6 +153,7 @@ private: void addImageFormat(const QString& shortName, const QString& description, const QString& extension, QList& readList, QList& writeList); void loadData(); void displayData(); + void addChildren(cTags* tags, QStandardItem* include, QStandardItem* exclude); void initSignals(); QStandardItem* findDBRootItem(QStandardItemModel* model, QStandardItem* rootItem, cAlbumRoots* albumRoots); @@ -141,6 +161,10 @@ private: void doExport(); void getExportSettings(EXPORTSETTINGS& exportSettings); + OVERWRITE exportFile(const EXPORTSETTINGS& exportSettings, cEXIF* lpExif, cImages *images, OVERWRITE overwrite); + QString replaceTags(const QString& path, cEXIF* lpExif, const QString& extension = QString(""), bool directory = true); + QString findFreeFileName(const QString& fileName); + protected: void closeEvent(QCloseEvent* event); @@ -149,6 +173,8 @@ private slots: void onExport(); void onStop(); void onFolderViewItemChanged(QStandardItem* item); + void onIncludeViewItemChanged(QStandardItem* item); + void onExcludeViewItemChanged(QStandardItem* item); void onFolderSelected(const QItemSelection& selection, const QItemSelection& previous); void onThumbnailSelected(const QItemSelection& selection, const QItemSelection& previous); }; diff --git a/cmainwindow.ui b/cmainwindow.ui index 896f4b3..f6ff551 100644 --- a/cmainwindow.ui +++ b/cmainwindow.ui @@ -20,16 +20,61 @@ Qt::Horizontal - - - QAbstractItemView::NoEditTriggers - - - true - - - true + + + Qt::Vertical + + + QAbstractItemView::NoEditTriggers + + + true + + + true + + + + + include + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + true + + + + + + + + exclude + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + true + + + + + diff --git a/common.cpp b/common.cpp index adcbdbb..7234f52 100644 --- a/common.cpp +++ b/common.cpp @@ -5,6 +5,9 @@ #include "common.h" +#include +#include + QString generateReadList(const QList& imageFormats) { @@ -58,3 +61,74 @@ QString generateWriteList(const QList& imageFormats) return(writeList); } +QList actuallyListVolumes() +{ + QList volumes; + + QList devices = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); + + foreach (const Solid::Device& accessDevice, devices) + { + if (!accessDevice.is()) + continue; + + const Solid::StorageAccess* access = accessDevice.as(); + + if (!access->isAccessible()) + continue; + + Solid::Device driveDevice; + + for (Solid::Device currentDevice = accessDevice; currentDevice.isValid() ; currentDevice = currentDevice.parent()) + { + if(currentDevice.is()) + { + driveDevice = currentDevice; + break; + } + } + + + Solid::StorageDrive* drive = driveDevice.as(); + + // check for StorageVolume + + Solid::Device volumeDevice; + + for (Solid::Device currentDevice = accessDevice; currentDevice.isValid() ; currentDevice = currentDevice.parent()) + { + if (currentDevice.is()) + { + volumeDevice = currentDevice; + break; + } + } + + if (!volumeDevice.isValid()) + continue; + + Solid::StorageVolume* const volume = volumeDevice.as(); + + SolidVolumeInfo info; + info.udi = accessDevice.udi(); + info.path = QDir::fromNativeSeparators(access->filePath()); + info.isMounted = access->isAccessible(); + + if (!info.path.isEmpty() && !info.path.endsWith(QLatin1Char('/'))) + { + info.path += QLatin1Char('/'); + } + + info.uuid = volume->uuid(); + info.label = volume->label(); + + if (drive) + info.isRemovable = (drive->isHotpluggable() || drive->isRemovable()); + else + info.isRemovable = false; + + volumes << info; + } + + return volumes; +} diff --git a/common.h b/common.h index 97b05b1..b2c256d 100644 --- a/common.h +++ b/common.h @@ -7,6 +7,16 @@ #define COMMON_H +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include @@ -14,6 +24,7 @@ #define THUMBNAIL_WIDTH 160 #define THUMBNAIL_HEIGHT 120 +#define _DELETE_(x) {if(x) delete(x); x = nullptr;} #ifdef __GNUC__ #define myDebug qDebug() << __FILE__ << "(" << __LINE__ << ") - " << __PRETTY_FUNCTION__ << ":" @@ -133,6 +144,34 @@ typedef struct tagVIDEOMETADATA QString videoCodec; } VIDEOMETADATA; +class Q_DECL_HIDDEN SolidVolumeInfo +{ + +public: + + SolidVolumeInfo() + : isRemovable (false), + isOpticalDisc(false), + isMounted (false) + { + } + + bool isNull() const + { + return path.isNull(); + } + +public: + + QString udi; ///< Solid device UDI of the StorageAccess device + QString path; ///< mount path of volume, with trailing slash + QString uuid; ///< UUID as from Solid + QString label; ///< volume label (think of CDs) + bool isRemovable; ///< may be removed + bool isOpticalDisc; ///< is an optical disk device as CD/DVD/BR + bool isMounted; ///< is mounted on File System. +}; + /*! \brief @@ -150,5 +189,7 @@ QString generateReadList(const QList& imageFormats); */ QString generateWriteList(const QList& imageFormats); +QList actuallyListVolumes(); + #endif // COMMON_H diff --git a/ctags.cpp b/ctags.cpp new file mode 100644 index 0000000..bf9caa5 --- /dev/null +++ b/ctags.cpp @@ -0,0 +1,186 @@ +#include "ctags.h" + +#include + +#include + + +cTags::cTags(const qint32& id, QObject *parent) : + QObject(parent), + m_id(id), + m_pid(0), + m_name(""), + m_parent(nullptr), + m_childList(nullptr), + m_includeItem(nullptr), + m_excludeItem(nullptr) +{ +} + +cTags::cTags(const qint32& id, const qint32& pid, const QString& name, QObject *parent) : + QObject(parent), + m_id(id), + m_pid(pid), + m_name(name), + m_parent(nullptr), + m_childList(nullptr), + m_includeItem(nullptr), + m_excludeItem(nullptr) +{ +} + +qint32 cTags::id() +{ + return(m_id); +} + +void cTags::setName(const QString& name) +{ + m_name = name; +} + +QString cTags::name() +{ + return(m_name); +} + +void cTags::setPID(const qint32& pid) +{ + m_pid = pid; +} + +qint32 cTags::pid() +{ + return(m_pid); +} + +void cTags::setParent(cTags* parent) +{ + m_parent = parent; +} + +cTags* cTags::parent() +{ + return(m_parent); +} + +void cTags::setIncludeItem(QStandardItem* includeItem) +{ + m_includeItem = includeItem; +} + +QStandardItem* cTags::includeItem() +{ + return(m_includeItem); +} + +void cTags::setExcludeItem(QStandardItem* excludeItem) +{ + m_excludeItem = excludeItem; +} + +QStandardItem* cTags::excludeItem() +{ + return(m_excludeItem); +} + +void cTags::addChild(cTags* tags) +{ + if(!m_childList) + m_childList = new cTagsList(nullptr); + m_childList->append(tags); +} + +cTagsList* cTags::childList() +{ + return(m_childList); +} + +cTagsList::cTagsList(QSqlDatabase* dbDigikam, QObject* parent) : + QObject(parent), + m_dbDigikam(dbDigikam) +{ +} + +cTagsList::~cTagsList() +{ +} + +bool cTagsList::load() +{ + if(!m_dbDigikam) + return(false); + + QSqlQuery query(*m_dbDigikam); + + if(!query.exec("SELECT id, " + " pid, " + " name " + "FROM Tags " + "ORDER BY name;")) + { + qDebug() << "cTagsList: loading failed."; + return(false); + } + + while(query.next()) + { + add(query.value("id").toInt(), query.value("pid").toInt(), query.value("name").toString(), this); + } + + processParent(); + + return(true); +} + +cTags* cTagsList::add(const qint32& id, QObject* parent) +{ + cTags* lpNew = find(id); + if(lpNew) + return(lpNew); + + lpNew = new cTags(id, parent); + append(lpNew); + return(lpNew); +} + +cTags* cTagsList::add(const qint32& id, const qint32& pid, const QString& name, QObject* parent) +{ + cTags* lpNew = find(id); + if(lpNew) + return(lpNew); + + lpNew = new cTags(id, pid, name, parent); + append(lpNew); + return(lpNew); +} + +cTags* cTagsList::find(const qint32& id) +{ + for(int i = 0;i < count();i++) + { + if(at(i)->id() == id) + return(at(i)); + } + + return(nullptr); +} + +void cTagsList::processParent() +{ + for(int i = 0;i < count();i++) + { + cTags* tags = at(i); + + if(tags->pid()) + { + cTags* parent = find(tags->pid()); + + if(parent) + { + tags->setParent(parent); + parent->addChild(tags); + } + } + } +} diff --git a/ctags.h b/ctags.h new file mode 100644 index 0000000..d10d91e --- /dev/null +++ b/ctags.h @@ -0,0 +1,77 @@ +#ifndef CTAGS_H +#define CTAGS_H + + +#include "common.h" + +#include +#include +#include + +#include + +#include + +#include + + +class cTagsList; + +class cTags : public QObject +{ + Q_OBJECT + +public: + cTags(const qint32& id, QObject *parent = nullptr); + cTags(const qint32& id, const qint32& pid, const QString& name, QObject *parent = nullptr); + + qint32 id(); + + void setName(const QString& name); + QString name(); + + void setPID(const qint32& pid); + qint32 pid(); + + void setParent(cTags* parent); + cTags* parent(); + + void setIncludeItem(QStandardItem* includeItem); + QStandardItem* includeItem(); + + void setExcludeItem(QStandardItem* excludeItem); + QStandardItem* excludeItem(); + + void addChild(cTags* tags); + cTagsList* childList(); +private: + qint32 m_id; + qint32 m_pid; + QString m_name; + cTags* m_parent; + cTagsList* m_childList; + QStandardItem* m_includeItem; + QStandardItem* m_excludeItem; +}; + +Q_DECLARE_METATYPE(cTags*) + +class cTagsList : public QObject, public QList +{ + Q_OBJECT + +public: + cTagsList(QSqlDatabase* dbDigikam, QObject* parent = nullptr); + ~cTagsList(); + + bool load(); + cTags* add(const qint32& id, QObject* parent = nullptr); + cTags* add(const qint32& id, const qint32& pid, const QString& name, QObject* parent = nullptr); + cTags* find(const qint32& id); + void processParent(); + +private: + QSqlDatabase* m_dbDigikam; +}; + +#endif // CTAGS_H diff --git a/exportDigikam.pro b/exportDigikam.pro index 8f16669..c139974 100644 --- a/exportDigikam.pro +++ b/exportDigikam.pro @@ -20,8 +20,8 @@ win32-msvc* { win32-g++ { message("mingw") - INCLUDEPATH += C:\dev\3rdParty\exiv2-0.27.5\include C:\dev\3rdParty\libraw C:\dev\3rdParty\libjpeg\include C:\dev\3rdParty\opencv\include dng - LIBS += -LC:\dev\3rdParty\exiv2-0.27.5\lib -lexiv2.dll -LC:\dev\3rdParty\libraw\lib -LC:\dev\3rdParty\opencv\x64\mingw\lib -lraw -lws2_32 -lz -lopencv_core412.dll -lopencv_imgproc412.dll + INCLUDEPATH += C:\dev\3rdParty\libsolid\ucrt64\include\KF5\Solid C:\dev\3rdParty\exiv2-0.27.5\include C:\dev\3rdParty\libraw C:\dev\3rdParty\libjpeg\include C:\dev\3rdParty\opencv\include dng + LIBS += -LC:\dev\3rdParty\libsolid\ucrt64\lib -lKF5Solid.dll -LC:\dev\3rdParty\exiv2-0.27.5\lib -lexiv2.dll -LC:\dev\3rdParty\libraw\lib -LC:\dev\3rdParty\opencv\x64\mingw\lib -lraw -lws2_32 -lz -lopencv_core412.dll -lopencv_imgproc412.dll } unix { @@ -41,12 +41,14 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\" SOURCES += \ calbumroots.cpp \ calbums.cpp \ + cexif.cpp \ cexportdialog.cpp \ cfoldersortfilterproxymodel.cpp \ cimage.cpp \ cimages.cpp \ clogwindow.cpp \ common.cpp \ + ctags.cpp \ cthumbnailsortfilterproxymodel.cpp \ main.cpp \ cmainwindow.cpp \ @@ -61,6 +63,7 @@ SOURCES += \ HEADERS += \ calbumroots.h \ calbums.h \ + cexif.h \ cexportdialog.h \ cfoldersortfilterproxymodel.h \ cimage.h \ @@ -68,6 +71,7 @@ HEADERS += \ clogwindow.h \ cmainwindow.h \ common.h \ + ctags.h \ cthumbnailsortfilterproxymodel.h \ pgfutils/libpgf/BitStream.h \ pgfutils/libpgf/Decoder.h \ @@ -78,7 +82,8 @@ HEADERS += \ pgfutils/libpgf/PGFtypes.h \ pgfutils/libpgf/Subband.h \ pgfutils/libpgf/WaveletTransform.h \ - pgfutils/pgfutils.h + pgfutils/pgfutils.h \ + solid/device_p.h FORMS += \ cexportdialog.ui \