Krazy/EBN fixes: QString::clear() instead of assigning QString()
[kphotoalbum.git] / DB / ImageInfo.cpp
blob263fe56d2cb4a0f252b14474a8b656cc500e028a
1 /* Copyright (C) 2003-2006 Jesper K. Pedersen <blackie@kde.org>
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public
5 License as published by the Free Software Foundation; either
6 version 2 of the License, or (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; see the file COPYING. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
19 #include "ImageInfo.h"
20 #include <qfileinfo.h>
21 #include "Settings/SettingsData.h"
22 #include "Utilities/Util.h"
23 #include "DB/ImageDB.h"
24 #include "DB/CategoryCollection.h"
25 #include "FileInfo.h"
26 #include <qstringlist.h>
27 #include "DB/MemberMap.h"
28 #include <config-kpa-exiv2.h>
29 #include "Exif/Database.h"
30 #include <kdebug.h>
31 #include <Utilities/Set.h>
33 using namespace DB;
35 ImageInfo::ImageInfo() :_null( true ), _rating(-1), _stackId(0), _stackOrder(0),
36 _geoPosition(),
37 _locked( false ), _dirty( false ), _delaySaving( false )
41 ImageInfo::ImageInfo( const QString& relativeFileName, MediaType type, bool readExifInfo )
42 : _imageOnDisk( YesOnDisk ), _null( false ), _size( -1, -1 ), _type( type ),
43 _rating(-1), _stackId(0), _stackOrder(0),
44 _geoPosition(),
45 _locked(false), _delaySaving( true )
47 QString fullPath = Settings::SettingsData::instance()->imageDirectory()+ relativeFileName;
48 QFileInfo fi( fullPath );
49 _label = fi.completeBaseName();
50 _angle = 0;
52 setFileName(relativeFileName);
54 // Read EXIF information
55 if ( readExifInfo )
56 readExif(fullPath, EXIFMODE_INIT);
58 _dirty = false;
59 _delaySaving = false;
62 /** Change delaying of saving changes.
64 * Will save changes when set to false.
66 * Use this method to set multiple attributes with only one
67 * database operation.
69 * Example:
70 * \code
71 * info.delaySavingChanges(true);
72 * info.setLabel("Hello");
73 * info.setDescription("Hello world");
74 * info.delaySavingChanges(false);
75 * \endcode
77 * \see saveChanges()
79 void ImageInfo::delaySavingChanges(bool b)
81 _delaySaving = b;
82 if (!b)
83 saveChanges();
86 void ImageInfo::setLabel( const QString& desc )
88 if (desc != _label)
89 _dirty = true;
90 _label = desc;
91 saveChangesIfNotDelayed();
94 QString ImageInfo::label() const
96 return _label;
99 void ImageInfo::setDescription( const QString& desc )
101 if (desc != _description)
102 _dirty = true;
103 _description = desc;
104 saveChangesIfNotDelayed();
107 QString ImageInfo::description() const
109 return _description;
113 void ImageInfo::setCategoryInfo( const QString& key, const StringSet& value )
115 // Don't check if really changed, because it's too slow.
116 _dirty = true;
117 _categoryInfomation[key] = value;
118 saveChangesIfNotDelayed();
121 bool ImageInfo::hasCategoryInfo( const QString& key, const QString& value ) const
123 return _categoryInfomation[key].contains(value);
126 bool DB::ImageInfo::hasCategoryInfo( const QString& key, const StringSet& values ) const
128 return Utilities::overlap( _categoryInfomation[key], values );
133 StringSet ImageInfo::itemsOfCategory( const QString& key ) const
135 return _categoryInfomation[key];
138 void ImageInfo::renameItem( const QString& key, const QString& oldValue, const QString& newValue )
140 StringSet& set = _categoryInfomation[key];
141 StringSet::iterator it = set.find( oldValue );
142 if ( it != set.end() ) {
143 _dirty = true;
144 set.erase( it );
145 set.insert( newValue );
146 saveChangesIfNotDelayed();
150 QString ImageInfo::fileName( PathType type ) const
152 switch (type) {
153 case DB::RelativeToImageRoot: return _relativeFileName;
154 case DB::AbsolutePath: return _absoluteFileName;
155 default:
156 kFatal("Invalid parameter to ImageInfo::fileName()");
157 return QString();
161 void ImageInfo::setFileName( const QString& relativeFileName )
163 if (relativeFileName != _relativeFileName)
164 _dirty = true;
165 _relativeFileName = relativeFileName;
166 setAbsoluteFileName();
167 _imageOnDisk = Unchecked;
168 DB::CategoryPtr folderCategory = DB::ImageDB::instance()->categoryCollection()->
169 categoryForName(QString::fromLatin1("Folder"));
170 if (folderCategory) {
171 DB::MemberMap& map = DB::ImageDB::instance()->memberMap();
172 createFolderCategoryItem( folderCategory, map );
173 //ImageDB::instance()->setMemberMap( map );
175 saveChangesIfNotDelayed();
179 void ImageInfo::rotate( int degrees )
181 if (degrees != 0)
182 _dirty = true;
183 _angle += degrees + 360;
184 _angle = _angle % 360;
185 saveChangesIfNotDelayed();
188 int ImageInfo::angle() const
190 return _angle;
193 void ImageInfo::setAngle( int angle )
195 if (angle != _angle)
196 _dirty = true;
197 _angle = angle;
198 saveChangesIfNotDelayed();
201 short ImageInfo::rating() const
203 return _rating;
206 void ImageInfo::setRating( short rating )
208 Q_ASSERT( (rating >= 0 && rating <= 10) || rating == -1 );
210 if ( rating > 10 )
211 rating = 10;
212 if ( rating < -1 )
213 rating = -1;
214 if ( _rating != rating )
215 _dirty = true;
217 _rating = rating;
218 saveChangesIfNotDelayed();
221 DB::StackID ImageInfo::stackId() const
223 return _stackId;
226 void ImageInfo::setStackId( const DB::StackID stackId )
228 if ( stackId != _stackId )
229 _dirty = true;
230 _stackId = stackId;
231 saveChangesIfNotDelayed();
234 unsigned int ImageInfo::stackOrder() const
236 return _stackOrder;
239 void ImageInfo::setStackOrder( const unsigned int stackOrder )
241 if ( stackOrder != _stackOrder )
242 _dirty = true;
243 _stackOrder = stackOrder;
244 saveChangesIfNotDelayed();
247 const GpsCoordinates& ImageInfo::geoPosition() const
249 return _geoPosition;
252 void ImageInfo::setGeoPosition( const GpsCoordinates& geoPosition )
254 if ( geoPosition != _geoPosition )
255 _dirty = true;
256 _geoPosition = geoPosition;
257 saveChangesIfNotDelayed();
260 void ImageInfo::setDate( const ImageDate& date )
262 if (date != _date)
263 _dirty = true;
264 _date = date;
265 saveChangesIfNotDelayed();
268 ImageDate& ImageInfo::date()
270 return _date;
273 ImageDate ImageInfo::date() const
275 return _date;
278 bool ImageInfo::operator!=( const ImageInfo& other ) const
280 return !(*this == other);
283 bool ImageInfo::operator==( const ImageInfo& other ) const
285 bool changed =
286 ( _relativeFileName != other._relativeFileName ||
287 _label != other._label ||
288 ( !_description.isEmpty() && !other._description.isEmpty() && _description != other._description ) || // one might be isNull.
289 _date != other._date ||
290 _angle != other._angle ||
291 _geoPosition != other._geoPosition ||
292 _rating != other._rating ||
293 ( _stackId != other._stackId ||
294 ! ( ( _stackId == 0 ) ? true :
295 ( _stackOrder == other._stackOrder ) ) )
297 if ( !changed ) {
298 QStringList keys = DB::ImageDB::instance()->categoryCollection()->categoryNames();
299 for( QStringList::ConstIterator it = keys.constBegin(); it != keys.constEnd(); ++it )
300 changed |= _categoryInfomation[*it] != other._categoryInfomation[*it];
302 return !changed;
305 void ImageInfo::renameCategory( const QString& oldName, const QString& newName )
307 _dirty = true;
308 _categoryInfomation[newName] = _categoryInfomation[oldName];
309 _categoryInfomation.remove(oldName);
310 saveChangesIfNotDelayed();
313 void ImageInfo::setLocked( bool locked )
315 _locked = locked;
318 bool ImageInfo::isLocked() const
320 return _locked;
323 void ImageInfo::readExif(const QString& fullPath, DB::ExifMode mode)
325 DB::FileInfo exifInfo = DB::FileInfo::read( fullPath, mode );
327 bool oldDelaySaving = _delaySaving;
328 delaySavingChanges(true);
330 // Date
331 if ( updateDateInformation(mode) ) {
332 setDate( exifInfo.dateTime() );
335 // Orientation
336 if ( (mode & EXIFMODE_ORIENTATION) && Settings::SettingsData::instance()->useEXIFRotate() ) {
337 setAngle( exifInfo.angle() );
340 // Description
341 if ( (mode & EXIFMODE_DESCRIPTION) && Settings::SettingsData::instance()->useEXIFComments() ) {
342 setDescription( exifInfo.description() );
345 delaySavingChanges(false);
346 _delaySaving = oldDelaySaving;
348 // Database update
349 if ( mode & EXIFMODE_DATABASE_UPDATE ) {
350 #ifdef HAVE_EXIV2
351 Exif::Database::instance()->remove( fullPath );
352 Exif::Database::instance()->add( fullPath );
353 #endif
358 QStringList ImageInfo::availableCategories() const
360 return _categoryInfomation.keys();
363 QSize ImageInfo::size() const
365 return _size;
368 void ImageInfo::setSize( const QSize& size )
370 if (size != _size)
371 _dirty = true;
372 _size = size;
373 saveChangesIfNotDelayed();
376 bool ImageInfo::imageOnDisk( const QString& fileName )
378 QFileInfo fi( fileName );
379 return fi.exists();
382 ImageInfo::ImageInfo( const QString& fileName,
383 const QString& label,
384 const QString& description,
385 const ImageDate& date,
386 int angle,
387 const MD5& md5sum,
388 const QSize& size,
389 MediaType type,
390 short rating,
391 unsigned int stackId,
392 unsigned int stackOrder,
393 const GpsCoordinates& geoPosition )
395 _delaySaving = true;
396 _relativeFileName = fileName;
397 setAbsoluteFileName();
398 _label =label;
399 _description =description;
400 _date = date;
401 _angle =angle;
402 _md5sum =md5sum;
403 _size = size;
404 _imageOnDisk = Unchecked;
405 _locked = false;
406 _null = false;
407 _type = type;
408 _dirty = true;
409 delaySavingChanges(false);
411 if ( rating > 10 )
412 rating = 10;
413 if ( rating < -1 )
414 rating = -1;
415 _rating = rating;
416 _geoPosition = geoPosition;
417 _stackId = stackId;
418 _stackOrder = stackOrder;
421 // TODO: we should get rid of this operator. It seems only be necessary
422 // because of the 'delaySavings' field that gets a special value.
423 // ImageInfo should just be a dumb data object holder and not incorporate
424 // storing strategies.
425 ImageInfo& ImageInfo::operator=( const ImageInfo& other )
427 _relativeFileName = other._relativeFileName;
428 setAbsoluteFileName();
429 _label = other._label;
430 _description = other._description;
431 _date = other._date;
432 _categoryInfomation = other._categoryInfomation;
433 _angle = other._angle;
434 _imageOnDisk = other._imageOnDisk;
435 _md5sum = other._md5sum;
436 _null = other._null;
437 _size = other._size;
438 _dirty = other._dirty;
439 _rating = other._rating;
440 _stackId = other._stackId;
441 _stackOrder = other._stackOrder;
442 _geoPosition = other._geoPosition;
444 delaySavingChanges(false);
446 return *this;
449 MediaType DB::ImageInfo::mediaType() const
451 return _type;
455 * During profiling I found that it took almost 5% of the time during
456 * categorizing when browsing, simply to calculate the absolute filename, therefore it is
457 * now an instance variable rather than calculated dynamically in
458 * fileName().
460 void DB::ImageInfo::setAbsoluteFileName()
462 _absoluteFileName = Settings::SettingsData::instance()->imageDirectory() + _relativeFileName;
465 void DB::ImageInfo::createFolderCategoryItem( DB::CategoryPtr folderCategory, DB::MemberMap& memberMap )
467 QString folderName = Utilities::relativeFolderName( _relativeFileName );
468 if ( folderName.isEmpty() )
469 return;
471 QStringList directories = folderName.split(QString::fromLatin1( "/" ) );
473 QString curPath;
474 for( QStringList::ConstIterator directoryIt = directories.constBegin(); directoryIt != directories.constEnd(); ++directoryIt ) {
475 if ( curPath.isEmpty() )
476 curPath = *directoryIt;
477 else {
478 QString oldPath = curPath;
479 curPath = curPath + QString::fromLatin1( "/" ) + *directoryIt;
480 memberMap.addMemberToGroup( folderCategory->name(), oldPath, curPath );
484 _categoryInfomation.insert( folderCategory->name() , StringSet() << folderName );
485 folderCategory->addItem( folderName );
488 void DB::ImageInfo::copyExtraData( const DB::ImageInfo& from)
490 _categoryInfomation = from._categoryInfomation;
491 _description = from._description;
492 // Hmm... what should the date be? orig or modified?
493 // _date = from._date;
494 _angle = from._angle;
495 _rating = from._rating;
496 _geoPosition = from._geoPosition;
499 void DB::ImageInfo::removeExtraData ()
501 _categoryInfomation.clear();
502 _description.clear();
503 _rating = -1;
504 _geoPosition = GpsCoordinates();
507 void DB::ImageInfo::addCategoryInfo( const QString& category, const StringSet& values )
509 for ( StringSet::const_iterator valueIt = values.constBegin(); valueIt != values.constEnd(); ++valueIt ) {
510 if (! _categoryInfomation[category].contains( *valueIt ) ) {
511 _dirty = true;
512 _categoryInfomation[category].insert( *valueIt );
515 saveChangesIfNotDelayed();
518 void DB::ImageInfo::clearAllCategoryInfo()
520 _categoryInfomation.clear();
523 void DB::ImageInfo::removeCategoryInfo( const QString& category, const StringSet& values )
525 for ( StringSet::const_iterator valueIt = values.constBegin(); valueIt != values.constEnd(); ++valueIt ) {
526 if ( _categoryInfomation[category].contains( *valueIt ) ) {
527 _dirty = true;
528 _categoryInfomation[category].remove(*valueIt);
531 saveChangesIfNotDelayed();
534 void DB::ImageInfo::addCategoryInfo( const QString& category, const QString& value )
536 if (! _categoryInfomation[category].contains( value ) ) {
537 _dirty = true;
538 _categoryInfomation[category].insert( value );
540 saveChangesIfNotDelayed();
543 void DB::ImageInfo::removeCategoryInfo( const QString& category, const QString& value )
545 if ( _categoryInfomation[category].contains( value ) ) {
546 _dirty = true;
547 _categoryInfomation[category].remove( value );
549 saveChangesIfNotDelayed();
552 bool DB::ImageInfo::updateDateInformation( int mode ) const
554 if ((mode & EXIFMODE_DATE) == 0)
555 return false;
557 if ( (mode & EXIFMODE_FORCE) != 0 )
558 return true;
560 #ifdef HAVE_EXIV2
561 return true;
562 #endif
564 return Settings::SettingsData::instance()->trustTimeStamps();