// vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "imagemetainfomodel.h" // Qt // KDE #include #include #include #include #include // Local namespace Gwenview { enum GroupRow { NoGroupSpace = -2, NoGroup = -1, GeneralGroup, ExifGroup, IptcGroup, XmpGroup }; class MetaInfoGroup { public: enum { InvalidRow = -1 }; class Entry { public: Entry(const QString& key, const QString& label, const QString& value) : mKey(key), mLabel(label.trimmed()), mValue(value.trimmed()) {} QString key() const { return mKey; } QString label() const { return mLabel; } QString value() const { return mValue; } void setValue(const QString& value) { mValue = value.trimmed(); } void appendValue(const QString& value) { if (mValue.length() > 0) { mValue += '\n'; } mValue += value.trimmed(); } private: QString mKey; QString mLabel; QString mValue; }; MetaInfoGroup(const QString& label) : mLabel(label) {} ~MetaInfoGroup() { qDeleteAll(mList); } void clear() { qDeleteAll(mList); mList.clear(); mRowForKey.clear(); } void addEntry(const QString& key, const QString& label, const QString& value) { addEntry(new Entry(key, label, value)); } void addEntry(Entry* entry) { mList << entry; mRowForKey[entry->key()] = mList.size() - 1; } void getInfoForKey(const QString& key, QString* label, QString* value) const { Entry* entry = getEntryForKey(key); if (entry) { *label = entry->label(); *value = entry->value(); } } QString getKeyAt(int row) const { Q_ASSERT(row < mList.size()); return mList[row]->key(); } QString getLabelForKeyAt(int row) const { Q_ASSERT(row < mList.size()); return mList[row]->label(); } QString getValueForKeyAt(int row) const { Q_ASSERT(row < mList.size()); return mList[row]->value(); } void setValueForKeyAt(int row, const QString& value) { Q_ASSERT(row < mList.size()); mList[row]->setValue(value); } int getRowForKey(const QString& key) const { return mRowForKey.value(key, InvalidRow); } int size() const { return mList.size(); } QString label() const { return mLabel; } const QList& entryList() const { return mList; } private: Entry* getEntryForKey(const QString& key) const { int row = getRowForKey(key); if (row == InvalidRow) { return 0; } return mList[row]; } QList mList; QHash mRowForKey; QString mLabel; }; struct ImageMetaInfoModelPrivate { QVector mMetaInfoGroupVector; ImageMetaInfoModel* q; void clearGroup(MetaInfoGroup* group, const QModelIndex& parent) { if (group->size() > 0) { q->beginRemoveRows(parent, 0, group->size() - 1); group->clear(); q->endRemoveRows(); } } void setGroupEntryValue(GroupRow groupRow, const QString& key, const QString& value) { MetaInfoGroup* group = mMetaInfoGroupVector[groupRow]; int entryRow = group->getRowForKey(key); if (entryRow == MetaInfoGroup::InvalidRow) { kWarning() << "No row for key" << key; return; } group->setValueForKeyAt(entryRow, value); QModelIndex groupIndex = q->index(groupRow, 0); QModelIndex entryIndex = q->index(entryRow, 1, groupIndex); emit q->dataChanged(entryIndex, entryIndex); } QVariant displayData(const QModelIndex& index) const { if (index.internalId() == NoGroup) { if (index.column() != 0) { return QVariant(); } QString label = mMetaInfoGroupVector[index.row()]->label(); return QVariant(label); } if (index.internalId() == NoGroupSpace) { return QVariant(QString()); } MetaInfoGroup* group = mMetaInfoGroupVector[index.internalId()]; if (index.column() == 0) { return group->getLabelForKeyAt(index.row()); } else { return group->getValueForKeyAt(index.row()); } } void initGeneralGroup() { MetaInfoGroup* group = mMetaInfoGroupVector[GeneralGroup]; group->addEntry("General.Name", i18nc("@item:intable Image file name", "Name"), QString()); group->addEntry("General.Size", i18nc("@item:intable", "File Size"), QString()); group->addEntry("General.Time", i18nc("@item:intable", "File Time"), QString()); group->addEntry("General.ImageSize", i18nc("@item:intable", "Image Size"), QString()); group->addEntry("General.Comment", i18nc("@item:intable", "Comment"), QString()); } }; ImageMetaInfoModel::ImageMetaInfoModel() : d(new ImageMetaInfoModelPrivate) { d->q = this; d->mMetaInfoGroupVector.resize(4); d->mMetaInfoGroupVector[GeneralGroup] = new MetaInfoGroup(i18nc("@title:group General info about the image", "General")); d->mMetaInfoGroupVector[ExifGroup] = new MetaInfoGroup("EXIF"); d->mMetaInfoGroupVector[IptcGroup] = new MetaInfoGroup("IPTC"); d->mMetaInfoGroupVector[XmpGroup] = new MetaInfoGroup("XMP"); d->initGeneralGroup(); } ImageMetaInfoModel::~ImageMetaInfoModel() { qDeleteAll(d->mMetaInfoGroupVector); delete d; } void ImageMetaInfoModel::setUrl(const KUrl& url) { KFileItem item(url); QString sizeString = KGlobal::locale()->formatByteSize(item.size()); d->setGroupEntryValue(GeneralGroup, "General.Name", item.name()); d->setGroupEntryValue(GeneralGroup, "General.Size", sizeString); d->setGroupEntryValue(GeneralGroup, "General.Time", item.timeString()); const KExiv2 kexiv2(url.path()); KExiv2PropertyList exifprops; KExiv2PropertyList iptcprops; KExiv2PropertyList xmpprops; foreach (const KExiv2Property &kexiv2property, kexiv2.metadata()) { if (kexiv2property.name.startsWith("Exif.")) { exifprops.append(kexiv2property); } else if (kexiv2property.name.startsWith("Xmp.")) { iptcprops.append(kexiv2property); } else if (kexiv2property.name.startsWith("Iptc.")) { xmpprops.append(kexiv2property); } else { kWarning() << "Unknown key" << kexiv2property.name; } } MetaInfoGroup* exifGroup = d->mMetaInfoGroupVector[ExifGroup]; MetaInfoGroup* iptcGroup = d->mMetaInfoGroupVector[IptcGroup]; MetaInfoGroup* xmpGroup = d->mMetaInfoGroupVector[XmpGroup]; QModelIndex exifIndex = index(ExifGroup, 0); QModelIndex iptcIndex = index(IptcGroup, 0); QModelIndex xmpIndex = index(XmpGroup, 0); d->clearGroup(exifGroup, exifIndex); d->clearGroup(iptcGroup, iptcIndex); d->clearGroup(xmpGroup, xmpIndex); if (!exifprops.isEmpty()) { beginInsertRows(exifIndex, 0, exifprops.size() - 1); foreach (const KExiv2Property &exifprop, exifprops) { exifGroup->addEntry(exifprop.name, exifprop.label, exifprop.value); } endInsertRows(); } if (!iptcprops.isEmpty()) { beginInsertRows(iptcIndex, 0, iptcprops.size() - 1); foreach (const KExiv2Property &iptcprop, iptcprops) { iptcGroup->addEntry(iptcprop.name, iptcprop.label, iptcprop.value); } endInsertRows(); } if (!xmpprops.isEmpty()) { beginInsertRows(xmpIndex, 0, xmpprops.size() - 1); foreach (const KExiv2Property &xmpprop, xmpprops) { xmpGroup->addEntry(xmpprop.name, xmpprop.label, xmpprop.value); } endInsertRows(); } } void ImageMetaInfoModel::setImageSize(const QSize& size) { QString imageSize; if (size.isValid()) { imageSize = i18nc( "@item:intable %1 is image width, %2 is image height", "%1x%2", size.width(), size.height()); double megaPixels = size.width() * size.height() / 1000000.; if (megaPixels > 0.1) { QString megaPixelsString = QString::number(megaPixels, 'f', 1); imageSize += ' '; imageSize += i18nc( "@item:intable %1 is number of millions of pixels in image", "(%1MP)", megaPixelsString); } } else { imageSize = '-'; } d->setGroupEntryValue(GeneralGroup, "General.ImageSize", imageSize); } void ImageMetaInfoModel::getInfoForKey(const QString& key, QString* label, QString* value) const { MetaInfoGroup* group; if (key.startsWith(QLatin1String("General"))) { group = d->mMetaInfoGroupVector[GeneralGroup]; } else if (key.startsWith(QLatin1String("Exif"))) { group = d->mMetaInfoGroupVector[ExifGroup]; } else if (key.startsWith(QLatin1String("Iptc"))) { group = d->mMetaInfoGroupVector[IptcGroup]; } else if (key.startsWith(QLatin1String("Xmp"))) { group = d->mMetaInfoGroupVector[XmpGroup]; } else { kWarning() << "Unknown metainfo key" << key; return; } group->getInfoForKey(key, label, value); } QString ImageMetaInfoModel::getValueForKey(const QString& key) const { QString label, value; getInfoForKey(key, &label, &value); return value; } QString ImageMetaInfoModel::keyForIndex(const QModelIndex& index) const { if (index.internalId() == NoGroup) { return QString(); } MetaInfoGroup* group = d->mMetaInfoGroupVector[index.internalId()]; return group->getKeyAt(index.row()); } QModelIndex ImageMetaInfoModel::index(int row, int col, const QModelIndex& parent) const { if (col < 0 || col > 1) { return QModelIndex(); } if (!parent.isValid()) { // This is a group if (row < 0 || row >= d->mMetaInfoGroupVector.size()) { return QModelIndex(); } return createIndex(row, col, col == 0 ? NoGroup : NoGroupSpace); } else { // This is an entry int group = parent.row(); if (row < 0 || row >= d->mMetaInfoGroupVector[group]->size()) { return QModelIndex(); } return createIndex(row, col, group); } } QModelIndex ImageMetaInfoModel::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } if (index.internalId() == NoGroup || index.internalId() == NoGroupSpace) { return QModelIndex(); } else { return createIndex(index.internalId(), 0, NoGroup); } } int ImageMetaInfoModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) { return d->mMetaInfoGroupVector.size(); } else if (parent.internalId() == NoGroup) { return d->mMetaInfoGroupVector[parent.row()]->size(); } else { return 0; } } int ImageMetaInfoModel::columnCount(const QModelIndex& /*parent*/) const { return 2; } QVariant ImageMetaInfoModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } switch (role) { case Qt::DisplayRole: return d->displayData(index); default: return QVariant(); } } QVariant ImageMetaInfoModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical || role != Qt::DisplayRole) { return QVariant(); } QString caption; if (section == 0) { caption = i18nc("@title:column", "Property"); } else if (section == 1) { caption = i18nc("@title:column", "Value"); } else { kWarning() << "Unknown section" << section; } return QVariant(caption); } } // namespace