libkipi from trunk (KDE 4.3) : add support of kipi host settings "file timestamp...
[kdegraphics.git] / gwenview / lib / imagemetainfomodel.cpp
blob5ffe32e9812dd622ff4eab21bfef3a6a92636512
1 // vim: set tabstop=4 shiftwidth=4 noexpandtab:
2 /*
3 Gwenview: an image viewer
4 Copyright 2007 Aurélien Gâteau <aurelien.gateau@free.fr>
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 // Self
22 #include "imagemetainfomodel.h"
24 // Qt
26 // KDE
27 #include <kdebug.h>
28 #include <kfileitem.h>
29 #include <kglobal.h>
30 #include <klocale.h>
32 // Exiv2
33 #include <exiv2/exif.hpp>
34 #include <exiv2/image.hpp>
35 #include <exiv2/iptc.hpp>
37 // Local
40 namespace Gwenview {
43 enum GroupRow { NoGroup = -1, GeneralGroup, ExifGroup, IptcGroup };
46 class MetaInfoGroup {
47 public:
48 enum {
49 InvalidRow = -1
52 class Entry {
53 public:
54 Entry(const QString& key, const QString& label)
55 : mKey(key), mLabel(label.trimmed())
58 QString key() const { return mKey; }
59 QString label() const { return mLabel; }
61 QString value() const { return mValue; }
62 void setValue(const QString& value) { mValue = value.trimmed(); }
64 void appendValue(const QString& value) {
65 if (mValue.length() > 0) {
66 mValue += '\n';
68 mValue += value.trimmed();
71 private:
72 QString mKey;
73 QString mLabel;
74 QString mValue;
77 MetaInfoGroup(const QString& label)
78 : mLabel(label) {}
81 ~MetaInfoGroup() {
82 qDeleteAll(mList);
86 void clear() {
87 qDeleteAll(mList);
88 mList.clear();
89 mRowForKey.clear();
93 void addEntry(const QString& key, const QString& label, const QString& value) {
94 // key aren't always unique (for example, "Iptc.Application2.Keywords"
95 // may appear multiple times). In this case, append the value at the
96 // end of the existing one.
97 Entry* entry = getEntryForKey(key);
98 if (entry) {
99 entry->appendValue(value);
100 } else {
101 entry = new Entry(key, label);
102 entry->setValue(value);
103 mList << entry;
104 mRowForKey[key] = mList.size() - 1;
109 void getInfoForKey(const QString& key, QString* label, QString* value) const {
110 Entry* entry = getEntryForKey(key);
111 if (entry) {
112 *label = entry->label();
113 *value = entry->value();
118 QString getKeyAt(int row) const {
119 Q_ASSERT(row < mList.size());
120 return mList[row]->key();
124 QString getLabelForKeyAt(int row) const {
125 Q_ASSERT(row < mList.size());
126 return mList[row]->label();
130 QString getValueForKeyAt(int row) const {
131 Q_ASSERT(row < mList.size());
132 return mList[row]->value();
136 void setValueForKeyAt(int row, const QString& value) {
137 Q_ASSERT(row < mList.size());
138 mList[row]->setValue(value);
142 int getRowForKey(const QString& key) const {
143 return mRowForKey.value(key, InvalidRow);
147 int size() const {
148 return mList.size();
152 QString label() const {
153 return mLabel;
156 const QList<Entry*>& entryList() const {
157 return mList;
160 private:
161 Entry* getEntryForKey(const QString& key) const {
162 int row = getRowForKey(key);
163 if (row == InvalidRow) {
164 return 0;
166 return mList[row];
169 QList<Entry*> mList;
170 QHash<QString, int> mRowForKey;
171 QString mLabel;
175 struct ImageMetaInfoModelPrivate {
176 QVector<MetaInfoGroup*> mMetaInfoGroupVector;
177 ImageMetaInfoModel* mModel;
180 void clearGroup(MetaInfoGroup* group, const QModelIndex& parent) {
181 if (group->size() > 0) {
182 mModel->beginRemoveRows(parent, 0, group->size() - 1);
183 group->clear();
184 mModel->endRemoveRows();
189 void notifyGroupFilled(MetaInfoGroup* group, const QModelIndex& parent) {
190 if (group->size() == 0) {
191 return;
193 mModel->beginInsertRows(parent, 0, group->size() - 1);
194 mModel->endInsertRows();
198 void setGroupEntryValue(GroupRow groupRow, const QString& key, const QString& value) {
199 MetaInfoGroup* group = mMetaInfoGroupVector[groupRow];
200 int entryRow = group->getRowForKey(key);
201 if (entryRow == MetaInfoGroup::InvalidRow) {
202 kWarning() << "No row for key" << key;
203 return;
205 group->setValueForKeyAt(entryRow, value);
206 QModelIndex groupIndex = mModel->index(groupRow, 0);
207 QModelIndex entryIndex = mModel->index(entryRow, 1, groupIndex);
208 emit mModel->dataChanged(entryIndex, entryIndex);
212 QVariant displayData(const QModelIndex& index) const {
213 if (index.internalId() == NoGroup) {
214 if (index.column() > 0) {
215 return QVariant();
217 QString label = mMetaInfoGroupVector[index.row()]->label();
218 return QVariant(label);
221 MetaInfoGroup* group = mMetaInfoGroupVector[index.internalId()];
222 if (index.column() == 0) {
223 return group->getLabelForKeyAt(index.row());
224 } else {
225 return group->getValueForKeyAt(index.row());
230 void initGeneralGroup() {
231 MetaInfoGroup* group = mMetaInfoGroupVector[GeneralGroup];
232 group->addEntry("General.Name", i18nc("@item:intable Image file name", "Name"), QString());
233 group->addEntry("General.Size", i18nc("@item:intable", "File Size"), QString());
234 group->addEntry("General.Time", i18nc("@item:intable", "File Time"), QString());
235 group->addEntry("General.ImageSize", i18nc("@item:intable", "Image Size"), QString());
240 ImageMetaInfoModel::ImageMetaInfoModel()
241 : d(new ImageMetaInfoModelPrivate) {
242 d->mModel = this;
243 d->mMetaInfoGroupVector.resize(3);
244 d->mMetaInfoGroupVector[GeneralGroup] = new MetaInfoGroup(i18nc("@title:group General info about the image", "General"));
245 d->mMetaInfoGroupVector[ExifGroup] = new MetaInfoGroup(i18nc("@title:group", "Exif"));
246 d->mMetaInfoGroupVector[IptcGroup] = new MetaInfoGroup(i18nc("@title:group", "Iptc"));
247 d->initGeneralGroup();
251 ImageMetaInfoModel::~ImageMetaInfoModel() {
252 qDeleteAll(d->mMetaInfoGroupVector);
253 delete d;
257 void ImageMetaInfoModel::setFileItem(const KFileItem& item) {
258 QString sizeString = KGlobal::locale()->formatByteSize(item.size());
260 d->setGroupEntryValue(GeneralGroup, "General.Name", item.name());
261 d->setGroupEntryValue(GeneralGroup, "General.Size", sizeString);
262 d->setGroupEntryValue(GeneralGroup, "General.Time", item.timeString());
266 void ImageMetaInfoModel::setImageSize(const QSize& size) {
267 QString imageSize;
268 if (size.isValid()) {
269 imageSize = i18nc(
270 "@item:intable %1 is image width, %2 is image height",
271 "%1x%2", size.width(), size.height());
273 double megaPixels = size.width() * size.height() / 1000000.;
274 if (megaPixels > 0.1) {
275 QString megaPixelsString = QString::number(megaPixels, 'f', 1);
276 imageSize += ' ';
277 imageSize + i18nc(
278 "@item:intable %1 is number of millions of pixels in image",
279 "(%1MP)", megaPixelsString);
281 } else {
282 imageSize = "-";
284 d->setGroupEntryValue(GeneralGroup, "General.ImageSize", imageSize);
288 template <class iterator>
289 static void fillExivGroup(MetaInfoGroup* group, iterator begin, iterator end) {
290 iterator it = begin;
291 for (;it != end; ++it) {
292 QString key = QString::fromUtf8(it->key().c_str());
293 QString label = QString::fromLocal8Bit(it->tagLabel().c_str());
294 std::ostringstream stream;
295 stream << *it;
296 QString value = QString::fromLocal8Bit(stream.str().c_str());
297 group->addEntry(key, label, value);
302 void ImageMetaInfoModel::setExiv2Image(const Exiv2::Image* image) {
303 MetaInfoGroup* exifGroup = d->mMetaInfoGroupVector[ExifGroup];
304 MetaInfoGroup* iptcGroup = d->mMetaInfoGroupVector[IptcGroup];
305 QModelIndex exifIndex = index(ExifGroup, 0);
306 QModelIndex iptcIndex = index(IptcGroup, 0);
307 d->clearGroup(exifGroup, exifIndex);
308 d->clearGroup(iptcGroup, iptcIndex);
310 if (!image) {
311 return;
314 if (image->supportsMetadata(Exiv2::mdExif)) {
315 const Exiv2::ExifData& exifData = image->exifData();
317 fillExivGroup(
318 exifGroup,
319 exifData.begin(),
320 exifData.end()
323 d->notifyGroupFilled(exifGroup, exifIndex);
326 if (image->supportsMetadata(Exiv2::mdIptc)) {
327 const Exiv2::IptcData& iptcData = image->iptcData();
329 fillExivGroup(
330 iptcGroup,
331 iptcData.begin(),
332 iptcData.end()
335 d->notifyGroupFilled(iptcGroup, iptcIndex);
340 void ImageMetaInfoModel::getInfoForKey(const QString& key, QString* label, QString* value) const {
341 MetaInfoGroup* group;
342 if (key.startsWith("General")) {
343 group = d->mMetaInfoGroupVector[GeneralGroup];
344 } else if (key.startsWith("Exif")) {
345 group = d->mMetaInfoGroupVector[ExifGroup];
346 } else if (key.startsWith("Iptc")) {
347 group = d->mMetaInfoGroupVector[IptcGroup];
348 } else {
349 kWarning() << "Unknown metainfo key" << key;
350 return;
352 group->getInfoForKey(key, label, value);
356 QString ImageMetaInfoModel::getValueForKey(const QString& key) const {
357 QString label, value;
358 getInfoForKey(key, &label, &value);
359 return value;
363 QString ImageMetaInfoModel::keyForIndex(const QModelIndex& index) const {
364 if (index.internalId() == NoGroup) {
365 return QString();
367 MetaInfoGroup* group = d->mMetaInfoGroupVector[index.internalId()];
368 return group->getKeyAt(index.row());
372 QModelIndex ImageMetaInfoModel::index(int row, int col, const QModelIndex& parent) const {
373 if (!parent.isValid()) {
374 // This is a group
375 if (col > 0) {
376 return QModelIndex();
378 if (row >= d->mMetaInfoGroupVector.size()) {
379 return QModelIndex();
381 return createIndex(row, col, NoGroup);
382 } else {
383 // This is an entry
384 if (col > 1) {
385 return QModelIndex();
387 int group = parent.row();
388 if (row >= d->mMetaInfoGroupVector[group]->size()) {
389 return QModelIndex();
391 return createIndex(row, col, group);
396 QModelIndex ImageMetaInfoModel::parent(const QModelIndex& index) const {
397 if (!index.isValid()) {
398 return QModelIndex();
400 if (index.internalId() == NoGroup) {
401 return QModelIndex();
402 } else {
403 return createIndex(index.internalId(), 0, NoGroup);
408 int ImageMetaInfoModel::rowCount(const QModelIndex& parent) const {
409 if (!parent.isValid()) {
410 return d->mMetaInfoGroupVector.size();
411 } else if (parent.internalId() == NoGroup) {
412 return d->mMetaInfoGroupVector[parent.row()]->size();
413 } else {
414 return 0;
419 int ImageMetaInfoModel::columnCount(const QModelIndex& /*parent*/) const {
420 return 2;
424 QVariant ImageMetaInfoModel::data(const QModelIndex& index, int role) const {
425 if (!index.isValid()) {
426 return QVariant();
429 switch (role) {
430 case Qt::DisplayRole:
431 return d->displayData(index);
432 default:
433 return QVariant();
438 QVariant ImageMetaInfoModel::headerData(int section, Qt::Orientation orientation, int role) const {
439 if (orientation == Qt::Vertical || role != Qt::DisplayRole) {
440 return QVariant();
443 QString caption;
444 if (section == 0) {
445 caption = i18nc("@title:column", "Property");
446 } else if (section == 1) {
447 caption = i18nc("@title:column", "Value");
448 } else {
449 kWarning() << "Unknown section" << section;
452 return QVariant(caption);
456 } // namespace