Krazy/EBN: changes for null strings
[kphotoalbum.git] / XMLDB / FileWriter.cpp
blob6360d49bd0e2bbcd7135243f8ee473dce650d7c3
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.
18 #include "FileWriter.h"
20 #include <Q3CString>
21 #include <Q3ValueList>
22 #include <kcmdlineargs.h>
23 #include <klocale.h>
24 #include <kmessagebox.h>
25 #include <qfile.h>
27 #include "Database.h"
28 #include "MainWindow/Window.h"
29 #include "NumberedBackup.h"
30 #include "Utilities/List.h"
31 #include "XMLCategory.h"
33 using Utilities::StringSet;
35 void XMLDB::FileWriter::save( const QString& fileName, bool isAutoSave )
37 if ( !isAutoSave )
38 NumberedBackup().makeNumberedBackup();
40 _db->_categoryCollection.initIdMap();
41 QDomDocument doc;
43 doc.appendChild( doc.createProcessingInstruction( QString::fromLatin1("xml"), QString::fromLatin1("version=\"1.0\" encoding=\"UTF-8\"") ) );
44 QDomElement top;
45 if ( KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) ) {
46 top = doc.createElement( QString::fromLatin1("KimDaBa") );
48 else {
49 top = doc.createElement( QString::fromLatin1("KPhotoAlbum") );
50 top.setAttribute( QString::fromLatin1( "version" ), QString::fromLatin1( "3" ) );
51 top.setAttribute( QString::fromLatin1( "compressed" ), Settings::SettingsData::instance()->useCompressedIndexXML() );
53 doc.appendChild( top );
55 if ( KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) )
56 add21CompatXML( top );
58 saveCategories( doc, top );
59 saveImages( doc, top );
60 saveBlockList( doc, top );
61 saveMemberGroups( doc, top );
63 QFile out( fileName );
65 if ( !out.open( QIODevice::WriteOnly ) )
66 KMessageBox::sorry( messageParent(), i18n( "Could not open file '%1'." , fileName ) );
67 else {
68 QByteArray s = doc.toByteArray();
69 out.write( s.data(), s.size()-1 );
70 out.close();
74 void XMLDB::FileWriter::saveCategories( QDomDocument doc, QDomElement top )
76 QStringList categories = DB::ImageDB::instance()->categoryCollection()->categoryNames();
77 QDomElement options;
78 if ( KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) )
79 options = doc.createElement( QString::fromLatin1("options") );
80 else
81 options = doc.createElement( QString::fromLatin1("Categories") );
82 top.appendChild( options );
85 for( QStringList::Iterator categoryIt = categories.begin(); categoryIt != categories.end(); ++categoryIt ) {
86 const QString name = *categoryIt;
87 DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName( name );
89 QDomElement opt;
90 if ( KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) )
91 opt = doc.createElement( QString::fromLatin1("option") );
92 else
93 opt = doc.createElement( QString::fromLatin1("Category") );
94 opt.setAttribute( QString::fromLatin1("name"), escape( name ) );
96 opt.setAttribute( QString::fromLatin1( "icon" ), category->iconName() );
97 opt.setAttribute( QString::fromLatin1( "show" ), category->doShow() );
98 opt.setAttribute( QString::fromLatin1( "viewtype" ), category->viewType() );
99 opt.setAttribute( QString::fromLatin1( "thumbnailsize" ), category->thumbnailSize() );
101 if ( shouldSaveCategory( name ) ) {
102 QStringList list =
103 Utilities::mergeListsUniqly(category->items(),
104 _db->_members.groups(name));
106 for( QStringList::Iterator it2 = list.begin(); it2 != list.end(); ++it2 ) {
107 QDomElement val = doc.createElement( QString::fromLatin1("value") );
108 val.setAttribute( QString::fromLatin1("value"), *it2 );
109 val.setAttribute( QString::fromLatin1( "id" ), static_cast<XMLCategory*>( category.data() )->idForName( *it2 ) );
110 opt.appendChild( val );
113 options.appendChild( opt );
117 void XMLDB::FileWriter::saveImages( QDomDocument doc, QDomElement top )
119 DB::ImageInfoList list = _db->_images;
121 // Copy files from clipboard to end of overview, so we don't loose them
122 for( DB::ImageInfoListConstIterator it = _db->_clipboard.constBegin(); it != _db->_clipboard.constEnd(); ++it ) {
123 list.append( *it );
126 QDomElement images = doc.createElement( QString::fromLatin1( "images" ) );
127 top.appendChild( images );
129 for( DB::ImageInfoListIterator it = list.begin(); it != list.end(); ++it ) {
130 images.appendChild( save( doc, *it ) );
134 void XMLDB::FileWriter::saveBlockList( QDomDocument doc, QDomElement top )
136 QDomElement blockList = doc.createElement( QString::fromLatin1( "blocklist" ) );
137 bool any=false;
138 for( QStringList::Iterator it = _db->_blockList.begin(); it != _db->_blockList.end(); ++it ) {
139 any=true;
140 QDomElement elm = doc.createElement( QString::fromLatin1( "block" ) );
141 elm.setAttribute( QString::fromLatin1( "file" ), *it );
142 blockList.appendChild( elm );
145 if (any)
146 top.appendChild( blockList );
149 void XMLDB::FileWriter::saveMemberGroups( QDomDocument doc, QDomElement top )
151 if ( _db->_members.isEmpty() )
152 return;
154 QDomElement memberNode = doc.createElement( QString::fromLatin1( "member-groups" ) );
155 for( QMap< QString,QMap<QString,StringSet> >::ConstIterator memberMapIt= _db->_members.memberMap().begin();
156 memberMapIt != _db->_members.memberMap().end(); ++memberMapIt )
158 const QString categoryName = memberMapIt.key();
159 if ( !shouldSaveCategory( categoryName ) )
160 continue;
162 QMap<QString,StringSet> groupMap = memberMapIt.value();
163 for( QMap<QString,StringSet>::Iterator groupMapIt= groupMap.begin(); groupMapIt != groupMap.end(); ++groupMapIt ) {
164 StringSet members = groupMapIt.value();
165 if ( Settings::SettingsData::instance()->useCompressedIndexXML() &&
166 !KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" )) {
167 QDomElement elm = doc.createElement( QString::fromLatin1( "member" ) );
168 elm.setAttribute( QString::fromLatin1( "category" ), categoryName );
169 elm.setAttribute( QString::fromLatin1( "group-name" ), groupMapIt.key() );
170 QStringList idList;
171 for( StringSet::const_iterator membersIt = members.begin(); membersIt != members.end(); ++membersIt ) {
172 DB::CategoryPtr catPtr = _db->_categoryCollection.categoryForName( memberMapIt.key() );
173 XMLCategory* category = static_cast<XMLCategory*>( catPtr.data() );
174 idList.append( QString::number( category->idForName( *membersIt ) ) );
176 elm.setAttribute( QString::fromLatin1( "members" ), idList.join( QString::fromLatin1( "," ) ) );
177 memberNode.appendChild( elm );
179 else {
180 for( StringSet::const_iterator membersIt = members.begin(); membersIt != members.end(); ++membersIt ) {
181 QDomElement elm = doc.createElement( QString::fromLatin1( "member" ) );
182 memberNode.appendChild( elm );
183 elm.setAttribute( QString::fromLatin1( "category" ), memberMapIt.key() );
184 elm.setAttribute( QString::fromLatin1( "group-name" ), groupMapIt.key() );
185 elm.setAttribute( QString::fromLatin1( "member" ), *membersIt );
191 top.appendChild( memberNode );
194 // This function will save an empty config element and a valid configWindowSetup element in the XML file.
195 // In versions of KPhotoAlbum newer than 2.1, these informations are stored
196 // using KConfig, rather than in the database, so I need to add them like
197 // this to make the file readable by KPhotoAlbum 2.1.
198 void XMLDB::FileWriter::add21CompatXML( QDomElement& top )
200 QDomDocument doc = top.ownerDocument();
201 top.appendChild( doc.createElement( QString::fromLatin1( "config" ) ) );
203 Q3CString conf = Q3CString( "<configWindowSetup> <dock> <name>Label and Dates</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </dock> <dock> <name>Image Preview</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </dock> <dock> <name>Description</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </dock> <dock> <name>Events</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </dock> <dock> <name>Places</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </dock> <dock> <name>People</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </dock> <splitGroup> <firstName>Label and Dates</firstName> <secondName>Description</secondName> <orientation>0</orientation> <separatorPos>31</separatorPos> <name>Label and Dates,Description</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </splitGroup> <splitGroup> <firstName>Label and Dates,Description</firstName> <secondName>Image Preview</secondName> <orientation>1</orientation> <separatorPos>70</separatorPos> <name>Label and Dates,Description,Image Preview</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </splitGroup> <splitGroup> <firstName>Places</firstName> <secondName>Events</secondName> <orientation>1</orientation> <separatorPos>50</separatorPos> <name>Places,Events</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </splitGroup> <splitGroup> <firstName>People</firstName> <secondName>Places,Events</secondName> <orientation>1</orientation> <separatorPos>34</separatorPos> <name>People,Places,Events</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </splitGroup> <splitGroup> <firstName>Label and Dates,Description,Image Preview</firstName> <secondName>People,Places,Events</secondName> <orientation>0</orientation> <separatorPos>0</separatorPos> <name>Label and Dates,Description,Image Preview,People,Places,Events</name> <hasParent>true</hasParent> <dragEnabled>true</dragEnabled> </splitGroup> <centralWidget>Label and Dates,Description,Image Preview,People,Places,Events</centralWidget> <mainDockWidget>Label and Dates</mainDockWidget> <geometry> <x>6</x> <y>6</y> <width>930</width> <height>492</height> </geometry> </configWindowSetup>" );
205 QDomDocument tmpDoc;
206 tmpDoc.setContent( conf );
207 top.appendChild( tmpDoc.documentElement() );
210 QDomElement XMLDB::FileWriter::save( QDomDocument doc, const DB::ImageInfoPtr& info )
212 QDomElement elm = doc.createElement( QString::fromLatin1("image") );
213 elm.setAttribute( QString::fromLatin1("file"), info->fileName( DB::RelativeToImageRoot ) );
214 elm.setAttribute( QString::fromLatin1("label"), info->label() );
215 if ( !info->description().isEmpty() )
216 elm.setAttribute( QString::fromLatin1("description"), info->description() );
218 DB::ImageDate date = info->date();
219 QDateTime start = date.start();
220 QDateTime end = date.end();
222 if ( KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) ) {
223 elm.setAttribute( QString::fromLatin1("yearFrom"), start.date().year() );
224 elm.setAttribute( QString::fromLatin1("monthFrom"), start.date().month() );
225 elm.setAttribute( QString::fromLatin1("dayFrom"), start.date().day() );
226 elm.setAttribute( QString::fromLatin1("hourFrom"), start.time().hour() );
227 elm.setAttribute( QString::fromLatin1("minuteFrom"), start.time().minute() );
228 elm.setAttribute( QString::fromLatin1("secondFrom"), start.time().second() );
230 elm.setAttribute( QString::fromLatin1("yearTo"), end.date().year() );
231 elm.setAttribute( QString::fromLatin1("monthTo"), end.date().month() );
232 elm.setAttribute( QString::fromLatin1("dayTo"), end.date().day() );
235 else {
236 elm.setAttribute( QString::fromLatin1( "startDate" ), start.toString(Qt::ISODate) );
237 elm.setAttribute( QString::fromLatin1( "endDate" ), end.toString(Qt::ISODate) );
240 elm.setAttribute( QString::fromLatin1("angle"), info->angle() );
241 elm.setAttribute( QString::fromLatin1( "md5sum" ), info->MD5Sum().toHexString() );
242 elm.setAttribute( QString::fromLatin1( "width" ), info->size().width() );
243 elm.setAttribute( QString::fromLatin1( "height" ), info->size().height() );
245 if ( info->rating() != -1 ) {
246 elm.setAttribute( QString::fromLatin1("rating"), info->rating() );
249 if ( info->stackId() ) {
250 elm.setAttribute( QString::fromLatin1("stackId"), info->stackId() );
251 elm.setAttribute( QString::fromLatin1("stackOrder"), info->stackOrder() );
254 const DB::GpsCoordinates& geoPos = info->geoPosition();
255 if ( !geoPos.isNull() ) {
256 elm.setAttribute( QLatin1String("gpsPrec"), geoPos.precision() );
257 elm.setAttribute( QLatin1String("gpsLon"), geoPos.longitude() );
258 elm.setAttribute( QLatin1String("gpsLat"), geoPos.latitude() );
259 elm.setAttribute( QLatin1String("gpsAlt"), geoPos.altitude() );
262 if ( Settings::SettingsData::instance()->useCompressedIndexXML() && !KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) )
263 writeCategoriesCompressed( elm, info );
264 else
265 writeCategories( doc, elm, info );
267 return elm;
270 void XMLDB::FileWriter::writeCategories( QDomDocument doc, QDomElement top, const DB::ImageInfoPtr& info )
272 QDomElement elm = doc.createElement( QString::fromLatin1("options") );
274 bool anyAtAll = false;
275 QStringList grps = info->availableCategories();
276 for( QStringList::Iterator categoryIt = grps.begin(); categoryIt != grps.end(); ++categoryIt ) {
277 QString name = *categoryIt;
278 if ( !shouldSaveCategory( name ) )
279 continue;
281 QDomElement opt = doc.createElement( QString::fromLatin1("option") );
282 opt.setAttribute( QString::fromLatin1("name"), escape( name ) );
284 StringSet items = info->itemsOfCategory(*categoryIt);
285 bool any = false;
286 for( StringSet::const_iterator itemIt = items.begin(); itemIt != items.end(); ++itemIt ) {
287 QDomElement val = doc.createElement( QString::fromLatin1("value") );
288 val.setAttribute( QString::fromLatin1("value"), *itemIt );
289 opt.appendChild( val );
290 any = true;
291 anyAtAll = true;
293 if ( any )
294 elm.appendChild( opt );
297 if ( anyAtAll )
298 top.appendChild( elm );
301 void XMLDB::FileWriter::writeCategoriesCompressed( QDomElement& elm, const DB::ImageInfoPtr& info )
303 Q3ValueList<DB::CategoryPtr> categoryList = DB::ImageDB::instance()->categoryCollection()->categories();
304 for( Q3ValueList<DB::CategoryPtr>::Iterator categoryIt = categoryList.begin(); categoryIt != categoryList.end(); ++categoryIt ) {
305 QString categoryName = (*categoryIt)->name();
307 if ( !shouldSaveCategory( categoryName ) )
308 continue;
310 StringSet items = info->itemsOfCategory(categoryName);
311 if ( !items.empty() ) {
312 QStringList idList;
313 for( StringSet::const_iterator itemIt = items.begin(); itemIt != items.end(); ++itemIt ) {
314 int id = static_cast<XMLCategory*>((*categoryIt).data())->idForName( *itemIt );
315 idList.append( QString::number( id ) );
317 elm.setAttribute( escape( categoryName ), idList.join( QString::fromLatin1( "," ) ) );
322 bool XMLDB::FileWriter::shouldSaveCategory( const QString& categoryName ) const
324 // A few bugs has shown up, where an invalid category name has crashed KPA. I therefore checks for sauch invalid names here.
325 if ( !_db->_categoryCollection.categoryForName( categoryName ) ) {
326 qWarning("Invalid category name: %s", qPrintable(categoryName));
327 return false;
330 return dynamic_cast<XMLCategory*>( _db->_categoryCollection.categoryForName( categoryName ).data() )->shouldSave();
333 QString XMLDB::FileWriter::escape( const QString& str )
335 QString tmp( str );
336 tmp.replace( QString::fromLatin1( " " ), QString::fromLatin1( "_" ) );
337 return tmp;
340 // TODO(hzeller): DEPENDENCY This pulls in the whole MainWindow dependency into the database backend.
341 QWidget *XMLDB::FileWriter::messageParent()
343 return MainWindow::Window::theMainWindow();