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.
20 #include "DB/Result.h"
21 #include "DB/ResultId.h"
22 #include "Settings/SettingsData.h"
23 #include <kmessagebox.h>
25 #include "Utilities/Util.h"
26 #include "DB/GroupCounter.h"
27 #include "Browser/BrowserWidget.h"
28 #include "DB/ImageInfo.h"
29 #include "DB/ImageInfoPtr.h"
30 #include "DB/CategoryCollection.h"
31 #include "DB/ResultId.h"
32 #include "Database.moc"
33 #include "XMLCategory.h"
34 #include <ksharedptr.h>
35 #include "XMLImageDateCollection.h"
36 #include "FileReader.h"
37 #include "FileWriter.h"
38 #include <Q3ValueList>
40 # include "Exif/Database.h"
43 using Utilities::StringSet
;
45 bool XMLDB::Database::_anyImageWithEmptySize
= false;
46 XMLDB::Database::Database( const QString
& configFile
):
49 Utilities::checkForBackupFile( configFile
);
50 FileReader
reader( this );
51 reader
.read( configFile
);
52 _nextStackId
= reader
.nextStackId();
54 connect( categoryCollection(), SIGNAL( itemRemoved( DB::Category
*, const QString
& ) ),
55 this, SLOT( deleteItem( DB::Category
*, const QString
& ) ) );
56 connect( categoryCollection(), SIGNAL( itemRenamed( DB::Category
*, const QString
&, const QString
& ) ),
57 this, SLOT( renameItem( DB::Category
*, const QString
&, const QString
& ) ) );
59 connect( categoryCollection(), SIGNAL( itemRemoved( DB::Category
*, const QString
& ) ),
60 &_members
, SLOT( deleteItem( DB::Category
*, const QString
& ) ) );
61 connect( categoryCollection(), SIGNAL( itemRenamed( DB::Category
*, const QString
&, const QString
& ) ),
62 &_members
, SLOT( renameItem( DB::Category
*, const QString
&, const QString
& ) ) );
65 uint
XMLDB::Database::totalCount() const
67 return _images
.count();
70 bool XMLDB::Database::operator==(const DB::ImageDB
& other
) const
72 const XMLDB::Database
* xmlOther
=
73 dynamic_cast<const XMLDB::Database
*>(&other
);
76 return Utilities::areSameFile(_fileName
, xmlOther
->_fileName
);
80 * I was considering merging the two calls to this method (one for images, one for video), but then I
81 * realized that all the work is really done after the check for whether the given
82 * imageInfo is of the right type, and as a match can't be both, this really
83 * would buy me nothing.
85 QMap
<QString
,uint
> XMLDB::Database::classify( const DB::ImageSearchInfo
& info
, const QString
&category
, DB::MediaType typemask
)
87 QMap
<QString
, uint
> map
;
88 DB::GroupCounter
counter( category
);
89 Q3Dict
<void> alreadyMatched
= info
.findAlreadyMatched( category
);
91 DB::ImageSearchInfo noMatchInfo
= info
;
92 QString currentMatchTxt
= noMatchInfo
.categoryMatchText( category
);
93 if ( currentMatchTxt
.isEmpty() )
94 noMatchInfo
.setCategoryMatchText( category
, DB::ImageDB::NONE() );
96 noMatchInfo
.setCategoryMatchText( category
, QString::fromLatin1( "%1 & %2" ).arg(currentMatchTxt
).arg(DB::ImageDB::NONE()) );
98 // Iterate through the whole database of images.
99 for( DB::ImageInfoListConstIterator it
= _images
.constBegin(); it
!= _images
.constEnd(); ++it
) {
100 bool match
= ( (*it
)->mediaType() & typemask
) && !(*it
)->isLocked() && info
.match( *it
) && rangeInclude( *it
);
101 if ( match
) { // If the given image is currently matched.
103 // Now iterate through all the categories the current image
104 // contains, and increase them in the map mapping from category
106 StringSet items
= (*it
)->itemsOfCategory(category
);
107 counter
.count( items
);
108 for( StringSet::const_iterator it2
= items
.begin(); it2
!= items
.end(); ++it2
) {
109 if ( !alreadyMatched
[*it2
] ) // We do not want to match "Jesper & Jesper"
113 // Find those with no other matches
114 if ( noMatchInfo
.match( *it
) )
115 map
[DB::ImageDB::NONE()]++;
119 QMap
<QString
,uint
> groups
= counter
.result();
120 for( QMap
<QString
,uint
>::iterator it
= groups
.begin(); it
!= groups
.end(); ++it
) {
121 map
[it
.key()] = it
.value();
127 void XMLDB::Database::renameCategory( const QString
& oldName
, const QString newName
)
129 for( DB::ImageInfoListIterator it
= _images
.begin(); it
!= _images
.end(); ++it
) {
130 (*it
)->renameCategory( oldName
, newName
);
134 void XMLDB::Database::addToBlockList(const DB::Result
& list
)
136 Q_FOREACH(DB::ImageInfoPtr inf
, list
.fetchInfos()) {
137 _blockList
<< inf
->fileName( DB::RelativeToImageRoot
);
142 void XMLDB::Database::deleteList(const DB::Result
& list
)
144 Q_FOREACH(DB::ResultId id
, list
) {
145 DB::ImageInfoPtr inf
= id
.fetchInfo();
146 StackMap::iterator found
= _stackMap
.find(inf
->stackId());
147 if ( inf
->isStacked() && found
!= _stackMap
.end() ) {
148 const DB::Result
& origCache
= found
.value();
150 Q_FOREACH(DB::ResultId cacheId
, origCache
) {
152 newCache
.append(cacheId
);
154 if (newCache
.size() <= 1) {
155 // we're destroying a stack
156 Q_FOREACH(DB::ImageInfoPtr cacheInf
, newCache
.fetchInfos()) {
157 cacheInf
->setStackId(0);
158 cacheInf
->setStackOrder(0);
160 _stackMap
.remove( inf
->stackId() );
162 _stackMap
.insert(inf
->stackId(), newCache
);
166 Exif::Database::instance()->remove( inf
->fileName( DB::AbsolutePath
) );
168 _idMapper
.remove( inf
->fileName(DB::RelativeToImageRoot
) );
169 _images
.remove( inf
);
171 emit
totalChanged( _images
.count() );
172 emit
imagesDeleted( list
);
175 void XMLDB::Database::renameItem( DB::Category
* category
, const QString
& oldName
, const QString
& newName
)
177 for( DB::ImageInfoListIterator it
= _images
.begin(); it
!= _images
.end(); ++it
) {
178 (*it
)->renameItem( category
->name(), oldName
, newName
);
182 void XMLDB::Database::deleteItem( DB::Category
* category
, const QString
& value
)
184 for( DB::ImageInfoListIterator it
= _images
.begin(); it
!= _images
.end(); ++it
) {
185 (*it
)->removeCategoryInfo( category
->name(), value
);
189 void XMLDB::Database::lockDB( bool lock
, bool exclude
)
191 DB::ImageSearchInfo info
= Settings::SettingsData::instance()->currentLock();
192 for( DB::ImageInfoListIterator it
= _images
.begin(); it
!= _images
.end(); ++it
) {
194 bool match
= info
.match( *it
);
197 (*it
)->setLocked( match
);
200 (*it
)->setLocked( false );
205 void XMLDB::Database::addImages( const DB::ImageInfoList
& images
)
207 // FIXME: merge stack information
208 DB::ImageInfoList newImages
= images
.sort();
209 if ( _images
.count() == 0 ) {
210 // case 1: The existing imagelist is empty.
213 else if ( newImages
.count() == 0 ) {
214 // case 2: No images to merge in - that's easy ;-)
217 else if ( newImages
.first()->date().start() > _images
.last()->date().start() ) {
218 // case 2: The new list is later than the existsing
219 _images
.appendList(newImages
);
221 else if ( _images
.isSorted() ) {
222 // case 3: The lists overlaps, and the existsing list is sorted
223 _images
.mergeIn( newImages
);
226 // case 4: The lists overlaps, and the existsing list is not sorted in the overlapping range.
227 _images
.appendList( newImages
);
230 for( DB::ImageInfoListConstIterator imageIt
= images
.constBegin(); imageIt
!= images
.constEnd(); ++imageIt
) {
231 DB::ImageInfoPtr info
= *imageIt
;
232 info
->addCategoryInfo( QString::fromLatin1( "Media Type" ),
233 info
->mediaType() == DB::Image
? QString::fromLatin1( "Image" ) : QString::fromLatin1( "Video" ) );
236 Q_FOREACH( const DB::ImageInfoPtr
& info
, images
) {
237 _idMapper
.add( info
->fileName(DB::RelativeToImageRoot
) );
240 emit
totalChanged( _images
.count() );
244 void XMLDB::Database::renameImage( DB::ImageInfoPtr info
, const QString
& newName
)
246 info
->delaySavingChanges(false);
247 _idMapper
.remove( info
->fileName(DB::RelativeToImageRoot
) );
248 info
->setFileName( newName
);
249 _idMapper
.add( info
->fileName(DB::RelativeToImageRoot
) );
252 DB::ImageInfoPtr
XMLDB::Database::info( const QString
& fileName
, DB::PathType type
) const
254 static QMap
<QString
, DB::ImageInfoPtr
> fileMap
;
256 QString name
= fileName
;
257 if ( type
== DB::RelativeToImageRoot
)
258 name
= Settings::SettingsData::instance()->imageDirectory() + fileName
;
260 if ( fileMap
.contains( name
) )
261 return fileMap
[ name
];
264 for( DB::ImageInfoListConstIterator it
= _images
.constBegin(); it
!= _images
.constEnd(); ++it
) {
265 fileMap
.insert( (*it
)->fileName(DB::AbsolutePath
), *it
);
267 if ( fileMap
.contains( name
) )
268 return fileMap
[ name
];
270 return DB::ImageInfoPtr();
273 bool XMLDB::Database::rangeInclude( DB::ImageInfoPtr info
) const
275 if (_selectionRange
.start().isNull() )
278 DB::ImageDate::MatchType tp
= info
->date().isIncludedIn( _selectionRange
);
279 if ( _includeFuzzyCounts
)
280 return ( tp
== DB::ImageDate::ExactMatch
|| tp
== DB::ImageDate::RangeMatch
);
282 return ( tp
== DB::ImageDate::ExactMatch
);
286 DB::MemberMap
& XMLDB::Database::memberMap()
292 void XMLDB::Database::save( const QString
& fileName
, bool isAutoSave
)
294 FileWriter
saver( this );
295 saver
.save( fileName
, isAutoSave
);
299 DB::MD5Map
* XMLDB::Database::md5Map()
304 bool XMLDB::Database::isBlocking( const QString
& fileName
)
306 return _blockList
.contains( fileName
);
310 DB::Result
XMLDB::Database::images()
312 QList
<DB::RawId
> result
;
313 for( DB::ImageInfoListIterator it
= _images
.begin(); it
!= _images
.end(); ++it
) {
314 result
.append( _idMapper
[(*it
)->fileName( DB::RelativeToImageRoot
)]);
316 return DB::Result(result
);
319 DB::Result
XMLDB::Database::search(
320 const DB::ImageSearchInfo
& info
,
321 bool requireOnDisk
) const
323 return searchPrivate( info
, requireOnDisk
, true );
326 DB::Result
XMLDB::Database::searchPrivate(
327 const DB::ImageSearchInfo
& info
,
329 bool onlyItemsMatchingRange
) const
331 // When searching for images counts for the datebar, we want matches outside the range too.
332 // When searching for images for the thumbnail view, we only want matches inside the range.
333 QList
<DB::RawId
> result
;
334 for( DB::ImageInfoListConstIterator it
= _images
.constBegin(); it
!= _images
.constEnd(); ++it
) {
335 bool match
= !(*it
)->isLocked() && info
.match( *it
) && ( !onlyItemsMatchingRange
|| rangeInclude( *it
));
336 match
&= !requireOnDisk
|| DB::ImageInfo::imageOnDisk( (*it
)->fileName(DB::AbsolutePath
) );
339 result
.append(_idMapper
[(*it
)->fileName( DB::RelativeToImageRoot
)]);
341 return DB::Result(result
);
344 void XMLDB::Database::sortAndMergeBackIn(const DB::Result
& idList
)
346 DB::ImageInfoList infoList
;
347 infoList
+= idList
.fetchInfos();
348 _images
.sortAndMergeBackIn(infoList
);
351 DB::CategoryCollection
* XMLDB::Database::categoryCollection()
353 return &_categoryCollection
;
356 KSharedPtr
<DB::ImageDateCollection
> XMLDB::Database::rangeCollection()
358 return KSharedPtr
<DB::ImageDateCollection
>(
359 new XMLImageDateCollection( searchPrivate( Browser::BrowserWidget::instance()->currentContext(), false, false ) ) );
362 void XMLDB::Database::reorder(
363 const DB::ResultId
& item
,
364 const DB::Result
& selection
,
367 Q_ASSERT(!item
.isNull());
368 DB::ImageInfoList list
= takeImagesFromSelection( selection
);
369 insertList( item
, list
, after
);
372 // Remove all the images from the database that match the given selection and
373 // return that sublist.
374 // Note: The selection is known to be sorted wrt the order in the image list,
375 // i.e. it is a common subsequence.
376 DB::ImageInfoList
XMLDB::Database::takeImagesFromSelection(const DB::Result
& selection
)
378 DB::ImageInfoList result
;
379 if (selection
.isEmpty())
382 DB::Result::ConstIterator subsequenceIt
= selection
.begin();
383 QString file
= (*subsequenceIt
).fetchInfo()->fileName(DB::AbsolutePath
);
385 for( DB::ImageInfoListIterator it
= _images
.begin(); it
!= _images
.end(); /**/ ) {
386 if ( (*it
)->fileName(DB::AbsolutePath
) == file
) {
388 it
= _images
.erase(it
);
390 if (subsequenceIt
== selection
.end())
392 file
= (*subsequenceIt
).fetchInfo()->fileName(DB::AbsolutePath
);
397 Q_ASSERT( subsequenceIt
== selection
.end() ); // if not, selection was not a subsequence
402 DB::Result
XMLDB::Database::insertList(
403 const DB::ResultId
& id
,
404 const DB::ImageInfoList
& list
,
408 QString fileName
= id
.fetchInfo()->fileName(DB::AbsolutePath
);
410 DB::ImageInfoListIterator imageIt
= _images
.begin();
411 for( ; imageIt
!= _images
.end(); ++imageIt
) {
412 if ( (*imageIt
)->fileName(DB::AbsolutePath
) == fileName
) {
419 for( DB::ImageInfoListConstIterator it
= list
.begin(); it
!= list
.end(); ++it
) {
420 _images
.insert( imageIt
, *it
);
421 result
.append(ID_FOR_FILE((*it
)->fileName(DB::AbsolutePath
)));
429 bool XMLDB::Database::stack(const DB::Result
& items
)
431 unsigned int changed
= 0;
432 QSet
<DB::StackID
> stacks
;
433 QList
<DB::ImageInfoPtr
> images
;
434 unsigned int stackOrder
= 1;
436 Q_FOREACH(DB::ImageInfoPtr imgInfo
, items
.fetchInfos()) {
438 if ( imgInfo
->isStacked() ) {
439 stacks
<< imgInfo
->stackId();
440 stackOrder
= qMax( stackOrder
, imgInfo
->stackOrder() + 1 );
446 if ( stacks
.size() > 1 )
447 return false; // images already in different stacks -> can't stack
449 DB::StackID stackId
= ( stacks
.size() == 1 ) ? *(stacks
.begin() ) : _nextStackId
++;
450 for ( QList
<DB::ImageInfoPtr
>::iterator it
= images
.begin();
452 ++it
, ++stackOrder
) {
453 (*it
)->setStackOrder( stackOrder
);
454 (*it
)->setStackId( stackId
);
455 _stackMap
[stackId
].append(ID_FOR_FILE((*it
)->fileName(DB::AbsolutePath
)));
465 void XMLDB::Database::unstack(const DB::Result
& items
)
467 Q_FOREACH(DB::ResultId id
, items
) {
468 DB::Result allInStack
= getStackFor(id
);
469 if (allInStack
.size() <= 2) {
470 // we're destroying stack here
471 Q_FOREACH(DB::ImageInfoPtr imgInfo
, allInStack
.fetchInfos()) {
473 if ( imgInfo
->isStacked() ) {
474 _stackMap
.remove( imgInfo
->stackId() );
475 imgInfo
->setStackId( 0 );
476 imgInfo
->setStackOrder( 0 );
480 DB::ImageInfoPtr imgInfo
= id
.fetchInfo();
482 if ( imgInfo
->isStacked() ) {
483 _stackMap
[imgInfo
->stackId()].removeAll(id
);
484 imgInfo
->setStackId( 0 );
485 imgInfo
->setStackOrder( 0 );
490 if (!items
.isEmpty())
494 DB::Result
XMLDB::Database::getStackFor(const DB::ResultId
& referenceImg
) const
496 DB::ImageInfoPtr imageInfo
= info( referenceImg
);
498 if ( !imageInfo
|| ! imageInfo
->isStacked() )
501 StackMap::iterator found
= _stackMap
.find(imageInfo
->stackId());
502 if ( found
!= _stackMap
.end() )
503 return found
.value();
505 // it wasn't in the cache -> rebuild it
507 for( DB::ImageInfoListConstIterator it
= _images
.constBegin(); it
!= _images
.constEnd(); ++it
) {
508 if ( (*it
)->isStacked() ) {
509 DB::StackID stackid
= (*it
)->stackId();
510 _stackMap
[stackid
].append(ID_FOR_FILE((*it
)->fileName(DB::AbsolutePath
))); // will need to be sorted later
514 #ifdef KDAB_TEMPORARILY_REMOVED // TODO(hzeller)/QWERTY: won't work with the limited iterator impl. of Result.
515 StackSortHelper
sortHelper( this );
516 for ( QMap
<DB::StackID
,QStringList
>::iterator it
= _stackMap
.begin(); it
!= _stackMap
.end(); ++it
) {
517 qSort( it
->begin(), it
->end(), sortHelper
);
521 found
= _stackMap
.find(imageInfo
->stackId());
522 if ( found
!= _stackMap
.end() )
523 return found
.value();
528 XMLDB::Database::StackSortHelper::StackSortHelper( const Database
* const db
): _db(db
)
532 int XMLDB::Database::StackSortHelper::operator()( const QString
& fileA
, const QString
& fileB
) const
534 DB::ImageInfoPtr a
= _db
->info( fileA
, DB::AbsolutePath
);
535 DB::ImageInfoPtr b
= _db
->info( fileB
, DB::AbsolutePath
);
538 return ( a
->stackId() == b
->stackId() ) && ( a
->stackOrder() < b
->stackOrder() );
541 DB::ImageInfoPtr
XMLDB::Database::createImageInfo( const QString
& fileName
, const QDomElement
& elm
, Database
* db
)
543 QString label
= elm
.attribute( QString::fromLatin1("label") );
545 if ( elm
.hasAttribute( QString::fromLatin1( "description" ) ) )
546 description
= elm
.attribute( QString::fromLatin1("description") );
549 if ( elm
.hasAttribute( QString::fromLatin1( "startDate" ) ) ) {
553 QString str
= elm
.attribute( QString::fromLatin1( "startDate" ) );
554 if ( !str
.isEmpty() )
555 start
= QDateTime::fromString( str
, Qt::ISODate
);
557 str
= elm
.attribute( QString::fromLatin1( "endDate" ) );
558 if ( !str
.isEmpty() )
559 end
= QDateTime::fromString( str
, Qt::ISODate
);
560 date
= DB::ImageDate( start
, end
);
563 int yearFrom
= 0, monthFrom
= 0, dayFrom
= 0, yearTo
= 0, monthTo
= 0, dayTo
= 0, hourFrom
= -1, minuteFrom
= -1, secondFrom
= -1;
565 yearFrom
= elm
.attribute( QString::fromLatin1("yearFrom"), QString::number( yearFrom
) ).toInt();
566 monthFrom
= elm
.attribute( QString::fromLatin1("monthFrom"), QString::number(monthFrom
) ).toInt();
567 dayFrom
= elm
.attribute( QString::fromLatin1("dayFrom"), QString::number(dayFrom
) ).toInt();
568 hourFrom
= elm
.attribute( QString::fromLatin1("hourFrom"), QString::number(hourFrom
) ).toInt();
569 minuteFrom
= elm
.attribute( QString::fromLatin1("minuteFrom"), QString::number(minuteFrom
) ).toInt();
570 secondFrom
= elm
.attribute( QString::fromLatin1("secondFrom"), QString::number(secondFrom
) ).toInt();
572 yearTo
= elm
.attribute( QString::fromLatin1("yearTo"), QString::number(yearTo
) ).toInt();
573 monthTo
= elm
.attribute( QString::fromLatin1("monthTo"), QString::number(monthTo
) ).toInt();
574 dayTo
= elm
.attribute( QString::fromLatin1("dayTo"), QString::number(dayTo
) ).toInt();
575 date
= DB::ImageDate( yearFrom
, monthFrom
, dayFrom
, yearTo
, monthTo
, dayTo
, hourFrom
, minuteFrom
, secondFrom
);
578 int angle
= elm
.attribute( QString::fromLatin1("angle"), QString::fromLatin1("0") ).toInt();
579 DB::MD5
md5sum(elm
.attribute( QString::fromLatin1( "md5sum" ) ));
581 _anyImageWithEmptySize
|= !elm
.hasAttribute( QString::fromLatin1( "width" ) );
583 int w
= elm
.attribute( QString::fromLatin1( "width" ), QString::fromLatin1( "-1" ) ).toInt();
584 int h
= elm
.attribute( QString::fromLatin1( "height" ), QString::fromLatin1( "-1" ) ).toInt();
585 QSize size
= QSize( w
,h
);
587 DB::MediaType mediaType
= Utilities::isVideo(fileName
) ? DB::Video
: DB::Image
;
589 short rating
= elm
.attribute( QString::fromLatin1("rating"), QString::fromLatin1("-1") ).toShort();
590 DB::StackID stackId
= elm
.attribute( QString::fromLatin1("stackId"), QString::fromLatin1("0") ).toULong();
591 unsigned int stackOrder
= elm
.attribute( QString::fromLatin1("stackOrder"), QString::fromLatin1("0") ).toULong();
593 DB::ImageInfo
* info
= new DB::ImageInfo( fileName
, label
, description
, date
,
594 angle
, md5sum
, size
, mediaType
, rating
, stackId
, stackOrder
);
596 int gpsPrecision
= elm
.attribute(
597 QLatin1String("gpsPrec"),
598 QString::number(DB::GpsCoordinates::PrecisionDataForNull
)).toInt();
599 if ( gpsPrecision
!= DB::GpsCoordinates::PrecisionDataForNull
)
600 info
->setGeoPosition(
602 elm
.attribute( QLatin1String("gpsLon") ).toDouble(),
603 elm
.attribute( QLatin1String("gpsLat") ).toDouble(),
604 elm
.attribute( QLatin1String("gpsAlt") ).toDouble(),
607 DB::ImageInfoPtr
result(info
);
608 for ( QDomNode child
= elm
.firstChild(); !child
.isNull(); child
= child
.nextSibling() ) {
609 if ( child
.isElement() ) {
610 QDomElement childElm
= child
.toElement();
611 if ( childElm
.tagName() == QString::fromLatin1( "categories" ) || childElm
.tagName() == QString::fromLatin1( "options" ) ) {
612 // options is for KimDaBa 2.1 compatibility
613 readOptions( result
, childElm
);
615 else if ( childElm
.tagName() == QString::fromLatin1( "drawings" ) ) {
616 // Ignore - KPhotoAlbum 3.0 and older version had drawings, that is not supported anymore
619 KMessageBox::error( 0, i18n("<p>Unknown tag %1, while reading configuration file.</p>"
620 "<p>Expected one of: Options, Drawings</p>", childElm
.tagName() ) );
625 possibleLoadCompressedCategories( elm
, result
, db
);
627 info
->addCategoryInfo( QString::fromLatin1( "Media Type" ),
628 info
->mediaType() == DB::Image
? QString::fromLatin1( "Image" ) : QString::fromLatin1( "Video" ) );
633 void XMLDB::Database::readOptions( DB::ImageInfoPtr info
, QDomElement elm
)
635 // options is for KimDaBa 2.1 compatibility
636 Q_ASSERT( elm
.tagName() == QString::fromLatin1( "categories" ) || elm
.tagName() == QString::fromLatin1( "options" ) );
638 for ( QDomNode nodeOption
= elm
.firstChild(); !nodeOption
.isNull(); nodeOption
= nodeOption
.nextSibling() ) {
640 if ( nodeOption
.isElement() ) {
641 QDomElement elmOption
= nodeOption
.toElement();
642 // option is for KimDaBa 2.1 compatibility
643 Q_ASSERT( elmOption
.tagName() == QString::fromLatin1("category") || elmOption
.tagName() == QString::fromLatin1("option") );
644 QString name
= FileReader::unescape( elmOption
.attribute( QString::fromLatin1("name") ) );
646 if ( !name
.isNull() ) {
648 for ( QDomNode nodeValue
= elmOption
.firstChild(); !nodeValue
.isNull();
649 nodeValue
= nodeValue
.nextSibling() ) {
650 if ( nodeValue
.isElement() ) {
651 QDomElement elmValue
= nodeValue
.toElement();
652 Q_ASSERT( elmValue
.tagName() == QString::fromLatin1("value") );
653 QString value
= elmValue
.attribute( QString::fromLatin1("value") );
654 if ( !value
.isNull() ) {
655 info
->addCategoryInfo( name
, value
);
664 void XMLDB::Database::possibleLoadCompressedCategories( const QDomElement
& elm
, DB::ImageInfoPtr info
, Database
* db
)
669 Q3ValueList
<DB::CategoryPtr
> categoryList
= db
->_categoryCollection
.categories();
670 for( Q3ValueList
<DB::CategoryPtr
>::Iterator categoryIt
= categoryList
.begin(); categoryIt
!= categoryList
.end(); ++categoryIt
) {
671 QString categoryName
= (*categoryIt
)->name();
672 QString str
= elm
.attribute( FileWriter::escape( categoryName
) );
673 if ( !str
.isEmpty() ) {
674 QStringList list
= str
.split(QString::fromLatin1( "," ), QString::SkipEmptyParts
);
675 for( QStringList::Iterator listIt
= list
.begin(); listIt
!= list
.end(); ++listIt
) {
676 int id
= (*listIt
).toInt();
677 QString name
= static_cast<XMLCategory
*>((*categoryIt
).data())->nameForId(id
);
678 info
->addCategoryInfo( categoryName
, name
);
684 // PENDING(blackie) THIS NEEDS TO GO AWAY //QWERTY
685 QStringList
XMLDB::Database::CONVERT(const DB::Result
& items
)
688 Q_FOREACH(DB::ResultId id
, items
) {
689 result
<< Utilities::absoluteImageFileName(_idMapper
[id
.rawId()]);
694 DB::ResultId
XMLDB::Database::ID_FOR_FILE( const QString
& filename
) const {
695 return DB::ResultId::createContextless(_idMapper
[ Utilities::imageFileNameToRelative(filename
)]);
698 DB::ImageInfoPtr
XMLDB::Database::info( const DB::ResultId
& id
) const
701 return DB::ImageInfoPtr(NULL
);
702 return info( _idMapper
[id
.rawId()],DB::RelativeToImageRoot
);