1 // vim: set tabstop=4 shiftwidth=4 noexpandtab:
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.
22 #include "imagemetainfomodel.h"
28 #include <kfileitem.h>
33 #include <exiv2/exif.hpp>
34 #include <exiv2/image.hpp>
35 #include <exiv2/iptc.hpp>
43 enum GroupRow
{ NoGroup
= -1, GeneralGroup
, ExifGroup
, IptcGroup
};
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) {
68 mValue
+= value
.trimmed();
77 MetaInfoGroup(const QString
& label
)
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
);
99 entry
->appendValue(value
);
101 entry
= new Entry(key
, label
);
102 entry
->setValue(value
);
104 mRowForKey
[key
] = mList
.size() - 1;
109 void getInfoForKey(const QString
& key
, QString
* label
, QString
* value
) const {
110 Entry
* entry
= getEntryForKey(key
);
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
);
152 QString
label() const {
156 const QList
<Entry
*>& entryList() const {
161 Entry
* getEntryForKey(const QString
& key
) const {
162 int row
= getRowForKey(key
);
163 if (row
== InvalidRow
) {
170 QHash
<QString
, int> mRowForKey
;
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);
184 mModel
->endRemoveRows();
189 void notifyGroupFilled(MetaInfoGroup
* group
, const QModelIndex
& parent
) {
190 if (group
->size() == 0) {
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
;
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) {
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());
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
) {
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
);
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
) {
268 if (size
.isValid()) {
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);
278 "@item:intable %1 is number of millions of pixels in image",
279 "(%1MP)", megaPixelsString
);
284 d
->setGroupEntryValue(GeneralGroup
, "General.ImageSize", imageSize
);
288 template <class iterator
>
289 static void fillExivGroup(MetaInfoGroup
* group
, iterator begin
, iterator end
) {
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
;
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
);
314 if (image
->supportsMetadata(Exiv2::mdExif
)) {
315 const Exiv2::ExifData
& exifData
= image
->exifData();
323 d
->notifyGroupFilled(exifGroup
, exifIndex
);
326 if (image
->supportsMetadata(Exiv2::mdIptc
)) {
327 const Exiv2::IptcData
& iptcData
= image
->iptcData();
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
];
349 kWarning() << "Unknown metainfo key" << key
;
352 group
->getInfoForKey(key
, label
, value
);
356 QString
ImageMetaInfoModel::getValueForKey(const QString
& key
) const {
357 QString label
, value
;
358 getInfoForKey(key
, &label
, &value
);
363 QString
ImageMetaInfoModel::keyForIndex(const QModelIndex
& index
) const {
364 if (index
.internalId() == NoGroup
) {
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()) {
376 return QModelIndex();
378 if (row
>= d
->mMetaInfoGroupVector
.size()) {
379 return QModelIndex();
381 return createIndex(row
, col
, NoGroup
);
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();
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();
419 int ImageMetaInfoModel::columnCount(const QModelIndex
& /*parent*/) const {
424 QVariant
ImageMetaInfoModel::data(const QModelIndex
& index
, int role
) const {
425 if (!index
.isValid()) {
430 case Qt::DisplayRole
:
431 return d
->displayData(index
);
438 QVariant
ImageMetaInfoModel::headerData(int section
, Qt::Orientation orientation
, int role
) const {
439 if (orientation
== Qt::Vertical
|| role
!= Qt::DisplayRole
) {
445 caption
= i18nc("@title:column", "Property");
446 } else if (section
== 1) {
447 caption
= i18nc("@title:column", "Value");
449 kWarning() << "Unknown section" << section
;
452 return QVariant(caption
);