Krazy/EBN: fix i18n warnings
[kphotoalbum.git] / XMLDB / Database.cpp
blob1119292f57507e6b8c9de0680ef89d91a541ceb0
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 "Database.h"
20 #include "DB/Result.h"
21 #include "DB/ResultId.h"
22 #include "Settings/SettingsData.h"
23 #include <kmessagebox.h>
24 #include <klocale.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>
39 #ifdef HAVE_EXIV2
40 # include "Exif/Database.h"
41 #endif
43 using Utilities::StringSet;
45 bool XMLDB::Database::_anyImageWithEmptySize = false;
46 XMLDB::Database::Database( const QString& configFile ):
47 _fileName(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);
74 if (!xmlOther)
75 return false;
76 return Utilities::areSameFile(_fileName, xmlOther->_fileName);
79 /**
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() );
95 else
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
105 // to count.
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"
110 map[*it2]++;
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();
124 return map;
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 );
139 deleteList( list );
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();
149 DB::Result newCache;
150 Q_FOREACH(DB::ResultId cacheId, origCache) {
151 if (id != cacheId)
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() );
161 } else {
162 _stackMap.insert(inf->stackId(), newCache);
165 #ifdef HAVE_EXIV2
166 Exif::Database::instance()->remove( inf->fileName( DB::AbsolutePath) );
167 #endif
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 ) {
193 if ( lock ) {
194 bool match = info.match( *it );
195 if ( !exclude )
196 match = !match;
197 (*it)->setLocked( match );
199 else
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.
211 _images = newImages;
213 else if ( newImages.count() == 0 ) {
214 // case 2: No images to merge in - that's easy ;-)
215 return;
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 );
225 else{
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() );
241 emit dirty();
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 ];
262 else {
263 fileMap.clear();
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() )
276 return true;
278 DB::ImageDate::MatchType tp = info->date().isIncludedIn( _selectionRange );
279 if ( _includeFuzzyCounts )
280 return ( tp == DB::ImageDate::ExactMatch || tp == DB::ImageDate::RangeMatch );
281 else
282 return ( tp == DB::ImageDate::ExactMatch );
286 DB::MemberMap& XMLDB::Database::memberMap()
288 return _members;
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()
301 return &_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,
328 bool requireOnDisk,
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) );
338 if (match)
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,
365 bool after)
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())
380 return result;
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 ) {
387 result << *it;
388 it = _images.erase(it);
389 ++subsequenceIt;
390 if (subsequenceIt == selection.end())
391 break;
392 file = (*subsequenceIt).fetchInfo()->fileName(DB::AbsolutePath);
393 } else {
394 ++it;
397 Q_ASSERT( subsequenceIt == selection.end() ); // if not, selection was not a subsequence
399 return result;
402 DB::Result XMLDB::Database::insertList(
403 const DB::ResultId& id,
404 const DB::ImageInfoList& list,
405 bool after)
407 DB::Result result;
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 ) {
413 break;
417 if ( after )
418 imageIt++;
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)));
423 emit dirty();
425 return result;
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()) {
437 Q_ASSERT( imgInfo );
438 if ( imgInfo->isStacked() ) {
439 stacks << imgInfo->stackId();
440 stackOrder = qMax( stackOrder, imgInfo->stackOrder() + 1 );
441 } else {
442 images << imgInfo;
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();
451 it != images.end();
452 ++it, ++stackOrder ) {
453 (*it)->setStackOrder( stackOrder );
454 (*it)->setStackId( stackId );
455 _stackMap[stackId].append(ID_FOR_FILE((*it)->fileName(DB::AbsolutePath)));
456 ++changed;
459 if ( changed )
460 emit dirty();
462 return changed;
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()) {
472 Q_ASSERT( imgInfo );
473 if ( imgInfo->isStacked() ) {
474 _stackMap.remove( imgInfo->stackId() );
475 imgInfo->setStackId( 0 );
476 imgInfo->setStackOrder( 0 );
479 } else {
480 DB::ImageInfoPtr imgInfo = id.fetchInfo();
481 Q_ASSERT( imgInfo );
482 if ( imgInfo->isStacked() ) {
483 _stackMap[imgInfo->stackId()].removeAll(id);
484 imgInfo->setStackId( 0 );
485 imgInfo->setStackOrder( 0 );
490 if (!items.isEmpty())
491 emit dirty();
494 DB::Result XMLDB::Database::getStackFor(const DB::ResultId& referenceImg) const
496 DB::ImageInfoPtr imageInfo = info( referenceImg );
498 if ( !imageInfo || ! imageInfo->isStacked() )
499 return DB::Result();
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
506 _stackMap.clear();
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 );
519 #endif
521 found = _stackMap.find(imageInfo->stackId());
522 if ( found != _stackMap.end() )
523 return found.value();
524 else
525 return DB::Result();
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 );
536 Q_ASSERT( a );
537 Q_ASSERT( b );
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") );
544 QString description;
545 if ( elm.hasAttribute( QString::fromLatin1( "description" ) ) )
546 description = elm.attribute( QString::fromLatin1("description") );
548 DB::ImageDate date;
549 if ( elm.hasAttribute( QString::fromLatin1( "startDate" ) ) ) {
550 QDateTime start;
551 QDateTime end;
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 );
562 else {
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(
601 DB::GpsCoordinates(
602 elm.attribute( QLatin1String("gpsLon") ).toDouble(),
603 elm.attribute( QLatin1String("gpsLat") ).toDouble(),
604 elm.attribute( QLatin1String("gpsAlt") ).toDouble(),
605 gpsPrecision));
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
618 else {
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" ) );
630 return result;
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() ) {
647 // Read values
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 )
666 if ( db == 0 )
667 return;
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)
687 QStringList result;
688 Q_FOREACH(DB::ResultId id, items) {
689 result << Utilities::absoluteImageFileName(_idMapper[id.rawId()]);
691 return result;
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
700 if (id.isNull())
701 return DB::ImageInfoPtr(NULL);
702 return info( _idMapper[id.rawId()],DB::RelativeToImageRoot);