1
// vim: set tabstop=4 shiftwidth=4 noexpandtab:
3
Gwenview: an image viewer
4
Copyright 2007 AurĆ©lien GĆ¢teau <agateau@kde.org>
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.
22
#include "imagemetainfomodel.h"
28
#include <kfileitem.h>
33
#include <exiv2/exif.hpp>
34
#include <exiv2/image.hpp>
35
#include <exiv2/iptc.hpp>
61
Entry(const QString& key, const QString& label, const QString& value)
62
: mKey(key), mLabel(label.trimmed()), mValue(value.trimmed())
65
QString key() const { return mKey; }
66
QString label() const { return mLabel; }
68
QString value() const { return mValue; }
69
void setValue(const QString& value) { mValue = value.trimmed(); }
71
void appendValue(const QString& value) {
72
if (mValue.length() > 0) {
75
mValue += value.trimmed();
84
MetaInfoGroup(const QString& label)
100
void addEntry(const QString& key, const QString& label, const QString& value) {
101
addEntry(new Entry(key, label, value));
105
void addEntry(Entry* entry) {
107
mRowForKey[entry->key()] = mList.size() - 1;
111
void getInfoForKey(const QString& key, QString* label, QString* value) const {
112
Entry* entry = getEntryForKey(key);
114
*label = entry->label();
115
*value = entry->value();
120
QString getKeyAt(int row) const {
121
Q_ASSERT(row < mList.size());
122
return mList[row]->key();
126
QString getLabelForKeyAt(int row) const {
127
Q_ASSERT(row < mList.size());
128
return mList[row]->label();
132
QString getValueForKeyAt(int row) const {
133
Q_ASSERT(row < mList.size());
134
return mList[row]->value();
138
void setValueForKeyAt(int row, const QString& value) {
139
Q_ASSERT(row < mList.size());
140
mList[row]->setValue(value);
144
int getRowForKey(const QString& key) const {
145
return mRowForKey.value(key, InvalidRow);
154
QString label() const {
158
const QList<Entry*>& entryList() const {
163
Entry* getEntryForKey(const QString& key) const {
164
int row = getRowForKey(key);
165
if (row == InvalidRow) {
172
QHash<QString, int> mRowForKey;
177
struct ImageMetaInfoModelPrivate {
178
QVector<MetaInfoGroup*> mMetaInfoGroupVector;
179
ImageMetaInfoModel* mModel;
182
void clearGroup(MetaInfoGroup* group, const QModelIndex& parent) {
183
if (group->size() > 0) {
184
mModel->beginRemoveRows(parent, 0, group->size() - 1);
186
mModel->endRemoveRows();
191
void setGroupEntryValue(GroupRow groupRow, const QString& key, const QString& value) {
192
MetaInfoGroup* group = mMetaInfoGroupVector[groupRow];
193
int entryRow = group->getRowForKey(key);
194
if (entryRow == MetaInfoGroup::InvalidRow) {
195
kWarning() << "No row for key" << key;
198
group->setValueForKeyAt(entryRow, value);
199
QModelIndex groupIndex = mModel->index(groupRow, 0);
200
QModelIndex entryIndex = mModel->index(entryRow, 1, groupIndex);
201
emit mModel->dataChanged(entryIndex, entryIndex);
205
QVariant displayData(const QModelIndex& index) const {
206
if (index.internalId() == NoGroup) {
207
if (index.column() != 0) {
210
QString label = mMetaInfoGroupVector[index.row()]->label();
211
return QVariant(label);
214
if (index.internalId() == NoGroupSpace) {
215
return QVariant(QString());
218
MetaInfoGroup* group = mMetaInfoGroupVector[index.internalId()];
219
if (index.column() == 0) {
220
return group->getLabelForKeyAt(index.row());
222
return group->getValueForKeyAt(index.row());
227
void initGeneralGroup() {
228
MetaInfoGroup* group = mMetaInfoGroupVector[GeneralGroup];
229
group->addEntry("General.Name", i18nc("@item:intable Image file name", "Name"), QString());
230
group->addEntry("General.Size", i18nc("@item:intable", "File Size"), QString());
231
group->addEntry("General.Time", i18nc("@item:intable", "File Time"), QString());
232
group->addEntry("General.ImageSize", i18nc("@item:intable", "Image Size"), QString());
236
template <class Container, class Iterator>
237
void fillExivGroup(const QModelIndex& parent, MetaInfoGroup* group, const Container& container) {
238
// key aren't always unique (for example, "Iptc.Application2.Keywords"
239
// may appear multiple times) so we can't know how many rows we will
240
// insert before going through them. That's why we create a hash
242
typedef QHash<QString, MetaInfoGroup::Entry*> EntryHash;
246
it = container.begin(),
247
end = container.end();
253
for (;it != end; ++it) {
254
QString key = QString::fromUtf8(it->key().c_str());
255
QString label = QString::fromLocal8Bit(it->tagLabel().c_str());
256
std::ostringstream stream;
258
QString value = QString::fromLocal8Bit(stream.str().c_str());
260
EntryHash::iterator hashIt = hash.find(key);
261
if (hashIt != hash.end()) {
262
hashIt.value()->appendValue(value);
264
hash.insert(key, new MetaInfoGroup::Entry(key, label, value));
268
mModel->beginInsertRows(parent, 0, hash.size() - 1);
269
Q_FOREACH(MetaInfoGroup::Entry* entry, hash) {
270
group->addEntry(entry);
272
mModel->endInsertRows();
277
ImageMetaInfoModel::ImageMetaInfoModel()
278
: d(new ImageMetaInfoModelPrivate) {
280
d->mMetaInfoGroupVector.resize(4);
281
d->mMetaInfoGroupVector[GeneralGroup] = new MetaInfoGroup(i18nc("@title:group General info about the image", "General"));
282
d->mMetaInfoGroupVector[ExifGroup] = new MetaInfoGroup("EXIF");
283
d->mMetaInfoGroupVector[IptcGroup] = new MetaInfoGroup("IPTC");
284
d->mMetaInfoGroupVector[XmpGroup] = new MetaInfoGroup("XMP");
285
d->initGeneralGroup();
289
ImageMetaInfoModel::~ImageMetaInfoModel() {
290
qDeleteAll(d->mMetaInfoGroupVector);
295
void ImageMetaInfoModel::setUrl(const KUrl& url) {
296
KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
297
QString sizeString = KGlobal::locale()->formatByteSize(item.size());
299
d->setGroupEntryValue(GeneralGroup, "General.Name", item.name());
300
d->setGroupEntryValue(GeneralGroup, "General.Size", sizeString);
301
d->setGroupEntryValue(GeneralGroup, "General.Time", item.timeString());
305
void ImageMetaInfoModel::setImageSize(const QSize& size) {
307
if (size.isValid()) {
309
"@item:intable %1 is image width, %2 is image height",
310
"%1x%2", size.width(), size.height());
312
double megaPixels = size.width() * size.height() / 1000000.;
313
if (megaPixels > 0.1) {
314
QString megaPixelsString = QString::number(megaPixels, 'f', 1);
317
"@item:intable %1 is number of millions of pixels in image",
318
"(%1MP)", megaPixelsString);
323
d->setGroupEntryValue(GeneralGroup, "General.ImageSize", imageSize);
327
void ImageMetaInfoModel::setExiv2Image(const Exiv2::Image* image) {
328
MetaInfoGroup* exifGroup = d->mMetaInfoGroupVector[ExifGroup];
329
MetaInfoGroup* iptcGroup = d->mMetaInfoGroupVector[IptcGroup];
330
MetaInfoGroup* xmpGroup = d->mMetaInfoGroupVector[XmpGroup];
331
QModelIndex exifIndex = index(ExifGroup, 0);
332
QModelIndex iptcIndex = index(IptcGroup, 0);
333
QModelIndex xmpIndex = index(XmpGroup, 0);
334
d->clearGroup(exifGroup, exifIndex);
335
d->clearGroup(iptcGroup, iptcIndex);
336
d->clearGroup(xmpGroup, xmpIndex);
342
if (image->checkMode(Exiv2::mdExif) & Exiv2::amRead) {
343
const Exiv2::ExifData& exifData = image->exifData();
344
d->fillExivGroup<Exiv2::ExifData, Exiv2::ExifData::const_iterator>(exifIndex, exifGroup, exifData);
347
if (image->checkMode(Exiv2::mdIptc) & Exiv2::amRead) {
348
const Exiv2::IptcData& iptcData = image->iptcData();
349
d->fillExivGroup<Exiv2::IptcData, Exiv2::IptcData::const_iterator>(iptcIndex, iptcGroup, iptcData);
352
if (image->checkMode(Exiv2::mdXmp) & Exiv2::amRead) {
353
const Exiv2::XmpData& xmpData = image->xmpData();
354
d->fillExivGroup<Exiv2::XmpData, Exiv2::XmpData::const_iterator>(xmpIndex, xmpGroup, xmpData);
359
void ImageMetaInfoModel::getInfoForKey(const QString& key, QString* label, QString* value) const {
360
MetaInfoGroup* group;
361
if (key.startsWith(QLatin1String("General"))) {
362
group = d->mMetaInfoGroupVector[GeneralGroup];
363
} else if (key.startsWith(QLatin1String("Exif"))) {
364
group = d->mMetaInfoGroupVector[ExifGroup];
365
} else if (key.startsWith(QLatin1String("Iptc"))) {
366
group = d->mMetaInfoGroupVector[IptcGroup];
367
} else if (key.startsWith(QLatin1String("Xmp"))) {
368
group = d->mMetaInfoGroupVector[XmpGroup];
370
kWarning() << "Unknown metainfo key" << key;
373
group->getInfoForKey(key, label, value);
377
QString ImageMetaInfoModel::getValueForKey(const QString& key) const {
378
QString label, value;
379
getInfoForKey(key, &label, &value);
384
QString ImageMetaInfoModel::keyForIndex(const QModelIndex& index) const {
385
if (index.internalId() == NoGroup) {
388
MetaInfoGroup* group = d->mMetaInfoGroupVector[index.internalId()];
389
return group->getKeyAt(index.row());
393
QModelIndex ImageMetaInfoModel::index(int row, int col, const QModelIndex& parent) const {
394
if (col < 0 || col > 1) {
395
return QModelIndex();
397
if (!parent.isValid()) {
399
if (row < 0 || row >= d->mMetaInfoGroupVector.size()) {
400
return QModelIndex();
402
return createIndex(row, col, col == 0 ? NoGroup : NoGroupSpace);
405
int group = parent.row();
406
if (row < 0 || row >= d->mMetaInfoGroupVector[group]->size()) {
407
return QModelIndex();
409
return createIndex(row, col, group);
414
QModelIndex ImageMetaInfoModel::parent(const QModelIndex& index) const {
415
if (!index.isValid()) {
416
return QModelIndex();
418
if (index.internalId() == NoGroup || index.internalId() == NoGroupSpace) {
419
return QModelIndex();
421
return createIndex(index.internalId(), 0, NoGroup);
426
int ImageMetaInfoModel::rowCount(const QModelIndex& parent) const {
427
if (!parent.isValid()) {
428
return d->mMetaInfoGroupVector.size();
429
} else if (parent.internalId() == NoGroup) {
430
return d->mMetaInfoGroupVector[parent.row()]->size();
437
int ImageMetaInfoModel::columnCount(const QModelIndex& /*parent*/) const {
442
QVariant ImageMetaInfoModel::data(const QModelIndex& index, int role) const {
443
if (!index.isValid()) {
448
case Qt::DisplayRole:
449
return d->displayData(index);
456
QVariant ImageMetaInfoModel::headerData(int section, Qt::Orientation orientation, int role) const {
457
if (orientation == Qt::Vertical || role != Qt::DisplayRole) {
463
caption = i18nc("@title:column", "Property");
464
} else if (section == 1) {
465
caption = i18nc("@title:column", "Value");
467
kWarning() << "Unknown section" << section;
470
return QVariant(caption);