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"
26 #include <qstringlist.h>
27 #include "DB/MemberMap.h"
28 #include <config-kpa-exiv2.h>
29 #include "Exif/Database.h"
31 #include <Utilities/Set.h>
35 ImageInfo::ImageInfo() :_null( true ), _rating(-1), _stackId(0), _stackOrder(0),
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),
45 _locked(false), _delaySaving( true )
47 QString fullPath
= Settings::SettingsData::instance()->imageDirectory()+ relativeFileName
;
48 QFileInfo
fi( fullPath
);
49 _label
= fi
.completeBaseName();
52 setFileName(relativeFileName
);
54 // Read EXIF information
56 readExif(fullPath
, EXIFMODE_INIT
);
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
71 * info.delaySavingChanges(true);
72 * info.setLabel("Hello");
73 * info.setDescription("Hello world");
74 * info.delaySavingChanges(false);
79 void ImageInfo::delaySavingChanges(bool b
)
86 void ImageInfo::setLabel( const QString
& desc
)
91 saveChangesIfNotDelayed();
94 QString
ImageInfo::label() const
99 void ImageInfo::setDescription( const QString
& desc
)
101 if (desc
!= _description
)
104 saveChangesIfNotDelayed();
107 QString
ImageInfo::description() const
113 void ImageInfo::setCategoryInfo( const QString
& key
, const StringSet
& value
)
115 // Don't check if really changed, because it's too slow.
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() ) {
145 set
.insert( newValue
);
146 saveChangesIfNotDelayed();
150 QString
ImageInfo::fileName( PathType type
) const
153 case DB::RelativeToImageRoot
: return _relativeFileName
;
154 case DB::AbsolutePath
: return _absoluteFileName
;
156 kFatal("Invalid parameter to ImageInfo::fileName()");
161 void ImageInfo::setFileName( const QString
& relativeFileName
)
163 if (relativeFileName
!= _relativeFileName
)
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
)
183 _angle
+= degrees
+ 360;
184 _angle
= _angle
% 360;
185 saveChangesIfNotDelayed();
188 int ImageInfo::angle() const
193 void ImageInfo::setAngle( int angle
)
198 saveChangesIfNotDelayed();
201 short ImageInfo::rating() const
206 void ImageInfo::setRating( short rating
)
208 Q_ASSERT( (rating
>= 0 && rating
<= 10) || rating
== -1 );
214 if ( _rating
!= rating
)
218 saveChangesIfNotDelayed();
221 DB::StackID
ImageInfo::stackId() const
226 void ImageInfo::setStackId( const DB::StackID stackId
)
228 if ( stackId
!= _stackId
)
231 saveChangesIfNotDelayed();
234 unsigned int ImageInfo::stackOrder() const
239 void ImageInfo::setStackOrder( const unsigned int stackOrder
)
241 if ( stackOrder
!= _stackOrder
)
243 _stackOrder
= stackOrder
;
244 saveChangesIfNotDelayed();
247 const GpsCoordinates
& ImageInfo::geoPosition() const
252 void ImageInfo::setGeoPosition( const GpsCoordinates
& geoPosition
)
254 if ( geoPosition
!= _geoPosition
)
256 _geoPosition
= geoPosition
;
257 saveChangesIfNotDelayed();
260 void ImageInfo::setDate( const ImageDate
& date
)
265 saveChangesIfNotDelayed();
268 ImageDate
& ImageInfo::date()
273 ImageDate
ImageInfo::date() const
278 bool ImageInfo::operator!=( const ImageInfo
& other
) const
280 return !(*this == other
);
283 bool ImageInfo::operator==( const ImageInfo
& other
) const
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
) ) )
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
];
305 void ImageInfo::renameCategory( const QString
& oldName
, const QString
& newName
)
308 _categoryInfomation
[newName
] = _categoryInfomation
[oldName
];
309 _categoryInfomation
.remove(oldName
);
310 saveChangesIfNotDelayed();
313 void ImageInfo::setLocked( bool locked
)
318 bool ImageInfo::isLocked() const
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);
331 if ( updateDateInformation(mode
) ) {
332 setDate( exifInfo
.dateTime() );
336 if ( (mode
& EXIFMODE_ORIENTATION
) && Settings::SettingsData::instance()->useEXIFRotate() ) {
337 setAngle( exifInfo
.angle() );
341 if ( (mode
& EXIFMODE_DESCRIPTION
) && Settings::SettingsData::instance()->useEXIFComments() ) {
342 setDescription( exifInfo
.description() );
345 delaySavingChanges(false);
346 _delaySaving
= oldDelaySaving
;
349 if ( mode
& EXIFMODE_DATABASE_UPDATE
) {
351 Exif::Database::instance()->remove( fullPath
);
352 Exif::Database::instance()->add( fullPath
);
358 QStringList
ImageInfo::availableCategories() const
360 return _categoryInfomation
.keys();
363 QSize
ImageInfo::size() const
368 void ImageInfo::setSize( const QSize
& size
)
373 saveChangesIfNotDelayed();
376 bool ImageInfo::imageOnDisk( const QString
& fileName
)
378 QFileInfo
fi( fileName
);
382 ImageInfo::ImageInfo( const QString
& fileName
,
383 const QString
& label
,
384 const QString
& description
,
385 const ImageDate
& date
,
391 unsigned int stackId
,
392 unsigned int stackOrder
,
393 const GpsCoordinates
& geoPosition
)
396 _relativeFileName
= fileName
;
397 setAbsoluteFileName();
399 _description
=description
;
404 _imageOnDisk
= Unchecked
;
409 delaySavingChanges(false);
416 _geoPosition
= geoPosition
;
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
;
432 _categoryInfomation
= other
._categoryInfomation
;
433 _angle
= other
._angle
;
434 _imageOnDisk
= other
._imageOnDisk
;
435 _md5sum
= other
._md5sum
;
438 _dirty
= other
._dirty
;
439 _rating
= other
._rating
;
440 _stackId
= other
._stackId
;
441 _stackOrder
= other
._stackOrder
;
442 _geoPosition
= other
._geoPosition
;
444 delaySavingChanges(false);
449 MediaType
DB::ImageInfo::mediaType() const
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
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() )
471 QStringList directories
= folderName
.split(QString::fromLatin1( "/" ) );
474 for( QStringList::ConstIterator directoryIt
= directories
.constBegin(); directoryIt
!= directories
.constEnd(); ++directoryIt
) {
475 if ( curPath
.isEmpty() )
476 curPath
= *directoryIt
;
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();
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
) ) {
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
) ) {
528 _categoryInfomation
[category
].remove(*valueIt
);
531 saveChangesIfNotDelayed();
534 void DB::ImageInfo::addCategoryInfo( const QString
& category
, const QString
& value
)
536 if (! _categoryInfomation
[category
].contains( value
) ) {
538 _categoryInfomation
[category
].insert( value
);
540 saveChangesIfNotDelayed();
543 void DB::ImageInfo::removeCategoryInfo( const QString
& category
, const QString
& value
)
545 if ( _categoryInfomation
[category
].contains( value
) ) {
547 _categoryInfomation
[category
].remove( value
);
549 saveChangesIfNotDelayed();
552 bool DB::ImageInfo::updateDateInformation( int mode
) const
554 if ((mode
& EXIFMODE_DATE
) == 0)
557 if ( (mode
& EXIFMODE_FORCE
) != 0 )
564 return Settings::SettingsData::instance()->trustTimeStamps();