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 {
61
Entry(const QString& key, const QString& label, const QString& value)
62
: mKey(key), mLabel(label.trimmed()), mValue(value.trimmed())
68
QString label() const {
72
QString value() const {
75
void setValue(const QString& value)
77
mValue = value.trimmed();
80
void appendValue(const QString& value)
82
if (mValue.length() > 0) {
85
mValue += value.trimmed();
94
MetaInfoGroup(const QString& label)
108
void addEntry(const QString& key, const QString& label, const QString& value)
110
addEntry(new Entry(key, label, value));
113
void addEntry(Entry* entry)
116
mRowForKey[entry->key()] = mList.size() - 1;
119
void getInfoForKey(const QString& key, QString* label, QString* value) const
121
Entry* entry = getEntryForKey(key);
123
*label = entry->label();
124
*value = entry->value();
128
QString getKeyAt(int row) const {
129
Q_ASSERT(row < mList.size());
130
return mList[row]->key();
133
QString getLabelForKeyAt(int row) const {
134
Q_ASSERT(row < mList.size());
135
return mList[row]->label();
138
QString getValueForKeyAt(int row) const {
139
Q_ASSERT(row < mList.size());
140
return mList[row]->value();
143
void setValueForKeyAt(int row, const QString& value)
145
Q_ASSERT(row < mList.size());
146
mList[row]->setValue(value);
149
int getRowForKey(const QString& key) const {
150
return mRowForKey.value(key, InvalidRow);
157
QString label() const {
161
const QList<Entry*>& entryList() const {
163
Entry* getEntryForKey(const QString& key) const {
164
int row = getRowForKey(key);
165
if (row == InvalidRow) {
166
Entry* getEntryForKey(const QString& key) const {
167
int row = getRowForKey(key);
168
if (row == InvalidRow) {
172
QHash<QString, int> mRowForKey;
175
QHash<QString, int> mRowForKey;
177
179
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();
180
QVector<MetaInfoGroup*> mMetaInfoGroupVector;
181
ImageMetaInfoModel* q;
183
void clearGroup(MetaInfoGroup* group, const QModelIndex& parent)
185
if (group->size() > 0) {
186
q->beginRemoveRows(parent, 0, group->size() - 1);
192
void setGroupEntryValue(GroupRow groupRow, const QString& key, const QString& value)
194
MetaInfoGroup* group = mMetaInfoGroupVector[groupRow];
195
int entryRow = group->getRowForKey(key);
196
if (entryRow == MetaInfoGroup::InvalidRow) {
197
kWarning() << "No row for key" << key;
200
group->setValueForKeyAt(entryRow, value);
201
QModelIndex groupIndex = q->index(groupRow, 0);
202
QModelIndex entryIndex = q->index(entryRow, 1, groupIndex);
203
emit q->dataChanged(entryIndex, entryIndex);
206
QVariant displayData(const QModelIndex& index) const {
207
if (index.internalId() == NoGroup) {
208
if (index.column() != 0) {
211
QString label = mMetaInfoGroupVector[index.row()]->label();
212
return QVariant(label);
215
if (index.internalId() == NoGroupSpace) {
216
return QVariant(QString());
219
MetaInfoGroup* group = mMetaInfoGroupVector[index.internalId()];
220
if (index.column() == 0) {
221
return group->getLabelForKeyAt(index.row());
223
return group->getValueForKeyAt(index.row());
227
void initGeneralGroup()
229
MetaInfoGroup* group = mMetaInfoGroupVector[GeneralGroup];
230
group->addEntry("General.Name", i18nc("@item:intable Image file name", "Name"), QString());
231
group->addEntry("General.Size", i18nc("@item:intable", "File Size"), QString());
232
group->addEntry("General.Time", i18nc("@item:intable", "File Time"), QString());
233
group->addEntry("General.ImageSize", i18nc("@item:intable", "Image Size"), QString());
234
group->addEntry("General.Comment", i18nc("@item:intable", "Comment"), QString());
237
template <class Container, class Iterator>
238
void fillExivGroup(const QModelIndex& parent, MetaInfoGroup* group, const Container& container)
240
// key aren't always unique (for example, "Iptc.Application2.Keywords"
241
// may appear multiple times) so we can't know how many rows we will
242
// insert before going through them. That's why we create a hash
244
typedef QHash<QString, MetaInfoGroup::Entry*> EntryHash;
248
it = container.begin(),
249
end = container.end();
255
for (; it != end; ++it) {
256
QString key = QString::fromUtf8(it->key().c_str());
257
QString label = QString::fromLocal8Bit(it->tagLabel().c_str());
258
std::ostringstream stream;
260
QString value = QString::fromLocal8Bit(stream.str().c_str());
262
EntryHash::iterator hashIt = hash.find(key);
263
if (hashIt != hash.end()) {
264
hashIt.value()->appendValue(value);
266
hash.insert(key, new MetaInfoGroup::Entry(key, label, value));
270
q->beginInsertRows(parent, 0, hash.size() - 1);
271
Q_FOREACH(MetaInfoGroup::Entry * entry, hash) {
272
group->addEntry(entry);
277
278
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);
279
: d(new ImageMetaInfoModelPrivate)
282
d->mMetaInfoGroupVector.resize(4);
283
d->mMetaInfoGroupVector[GeneralGroup] = new MetaInfoGroup(i18nc("@title:group General info about the image", "General"));
284
d->mMetaInfoGroupVector[ExifGroup] = new MetaInfoGroup("EXIF");
285
d->mMetaInfoGroupVector[IptcGroup] = new MetaInfoGroup("IPTC");
286
d->mMetaInfoGroupVector[XmpGroup] = new MetaInfoGroup("XMP");
287
d->initGeneralGroup();
290
ImageMetaInfoModel::~ImageMetaInfoModel()
292
qDeleteAll(d->mMetaInfoGroupVector);
296
void ImageMetaInfoModel::setUrl(const KUrl& url)
298
KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
299
QString sizeString = KGlobal::locale()->formatByteSize(item.size());
301
d->setGroupEntryValue(GeneralGroup, "General.Name", item.name());
302
d->setGroupEntryValue(GeneralGroup, "General.Size", sizeString);
303
d->setGroupEntryValue(GeneralGroup, "General.Time", item.timeString());
306
void ImageMetaInfoModel::setImageSize(const QSize& size)
309
if (size.isValid()) {
311
"@item:intable %1 is image width, %2 is image height",
312
"%1x%2", size.width(), size.height());
314
double megaPixels = size.width() * size.height() / 1000000.;
315
if (megaPixels > 0.1) {
316
QString megaPixelsString = QString::number(megaPixels, 'f', 1);
319
"@item:intable %1 is number of millions of pixels in image",
320
"(%1MP)", megaPixelsString);
325
d->setGroupEntryValue(GeneralGroup, "General.ImageSize", imageSize);
328
void ImageMetaInfoModel::setExiv2Image(const Exiv2::Image* image)
330
MetaInfoGroup* exifGroup = d->mMetaInfoGroupVector[ExifGroup];
331
MetaInfoGroup* iptcGroup = d->mMetaInfoGroupVector[IptcGroup];
332
MetaInfoGroup* xmpGroup = d->mMetaInfoGroupVector[XmpGroup];
333
QModelIndex exifIndex = index(ExifGroup, 0);
334
QModelIndex iptcIndex = index(IptcGroup, 0);
335
QModelIndex xmpIndex = index(XmpGroup, 0);
336
d->clearGroup(exifGroup, exifIndex);
337
d->clearGroup(iptcGroup, iptcIndex);
338
d->clearGroup(xmpGroup, xmpIndex);
344
d->setGroupEntryValue(GeneralGroup, "General.Comment", QString::fromUtf8(image->comment().c_str()));
346
if (image->checkMode(Exiv2::mdExif) & Exiv2::amRead) {
347
const Exiv2::ExifData& exifData = image->exifData();
348
d->fillExivGroup<Exiv2::ExifData, Exiv2::ExifData::const_iterator>(exifIndex, exifGroup, exifData);
351
if (image->checkMode(Exiv2::mdIptc) & Exiv2::amRead) {
352
const Exiv2::IptcData& iptcData = image->iptcData();
353
d->fillExivGroup<Exiv2::IptcData, Exiv2::IptcData::const_iterator>(iptcIndex, iptcGroup, iptcData);
356
if (image->checkMode(Exiv2::mdXmp) & Exiv2::amRead) {
357
const Exiv2::XmpData& xmpData = image->xmpData();
358
d->fillExivGroup<Exiv2::XmpData, Exiv2::XmpData::const_iterator>(xmpIndex, xmpGroup, xmpData);
362
void ImageMetaInfoModel::getInfoForKey(const QString& key, QString* label, QString* value) const
364
MetaInfoGroup* group;
365
if (key.startsWith(QLatin1String("General"))) {
366
group = d->mMetaInfoGroupVector[GeneralGroup];
367
} else if (key.startsWith(QLatin1String("Exif"))) {
368
group = d->mMetaInfoGroupVector[ExifGroup];
369
} else if (key.startsWith(QLatin1String("Iptc"))) {
370
group = d->mMetaInfoGroupVector[IptcGroup];
371
} else if (key.startsWith(QLatin1String("Xmp"))) {
372
group = d->mMetaInfoGroupVector[XmpGroup];
374
kWarning() << "Unknown metainfo key" << key;
377
group->getInfoForKey(key, label, value);
380
QString ImageMetaInfoModel::getValueForKey(const QString& key) const
382
QString label, value;
383
getInfoForKey(key, &label, &value);
387
QString ImageMetaInfoModel::keyForIndex(const QModelIndex& index) const
389
if (index.internalId() == NoGroup) {
392
MetaInfoGroup* group = d->mMetaInfoGroupVector[index.internalId()];
393
return group->getKeyAt(index.row());
396
QModelIndex ImageMetaInfoModel::index(int row, int col, const QModelIndex& parent) const
398
if (col < 0 || col > 1) {
399
return QModelIndex();
401
if (!parent.isValid()) {
403
if (row < 0 || row >= d->mMetaInfoGroupVector.size()) {
404
return QModelIndex();
406
return createIndex(row, col, col == 0 ? NoGroup : NoGroupSpace);
409
int group = parent.row();
410
if (row < 0 || row >= d->mMetaInfoGroupVector[group]->size()) {
411
return QModelIndex();
413
return createIndex(row, col, group);
417
QModelIndex ImageMetaInfoModel::parent(const QModelIndex& index) const
419
if (!index.isValid()) {
420
return QModelIndex();
422
if (index.internalId() == NoGroup || index.internalId() == NoGroupSpace) {
423
return QModelIndex();
425
return createIndex(index.internalId(), 0, NoGroup);
429
int ImageMetaInfoModel::rowCount(const QModelIndex& parent) const
431
if (!parent.isValid()) {
432
return d->mMetaInfoGroupVector.size();
433
} else if (parent.internalId() == NoGroup) {
434
return d->mMetaInfoGroupVector[parent.row()]->size();
440
int ImageMetaInfoModel::columnCount(const QModelIndex& /*parent*/) const
445
QVariant ImageMetaInfoModel::data(const QModelIndex& index, int role) const
447
if (!index.isValid()) {
452
case Qt::DisplayRole:
453
return d->displayData(index);
459
QVariant ImageMetaInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
461
if (orientation == Qt::Vertical || role != Qt::DisplayRole) {
467
caption = i18nc("@title:column", "Property");
468
} else if (section == 1) {
469
caption = i18nc("@title:column", "Value");
471
kWarning() << "Unknown section" << section;
474
return QVariant(caption);