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"
21 #include <Q3ValueList>
22 #include <kcmdlineargs.h>
24 #include <kmessagebox.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
)
38 NumberedBackup().makeNumberedBackup();
40 _db
->_categoryCollection
.initIdMap();
43 doc
.appendChild( doc
.createProcessingInstruction( QString::fromLatin1("xml"), QString::fromLatin1("version=\"1.0\" encoding=\"UTF-8\"") ) );
45 if ( KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) ) {
46 top
= doc
.createElement( QString::fromLatin1("KimDaBa") );
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
) );
68 QByteArray s
= doc
.toByteArray();
69 out
.write( s
.data(), s
.size()-1 );
74 void XMLDB::FileWriter::saveCategories( QDomDocument doc
, QDomElement top
)
76 QStringList categories
= DB::ImageDB::instance()->categoryCollection()->categoryNames();
78 if ( KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) )
79 options
= doc
.createElement( QString::fromLatin1("options") );
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
);
90 if ( KCmdLineArgs::parsedArgs()->isSet( "export-in-2.1-format" ) )
91 opt
= doc
.createElement( QString::fromLatin1("option") );
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
) ) {
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
) {
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" ) );
138 for( QStringList::Iterator it
= _db
->_blockList
.begin(); it
!= _db
->_blockList
.end(); ++it
) {
140 QDomElement elm
= doc
.createElement( QString::fromLatin1( "block" ) );
141 elm
.setAttribute( QString::fromLatin1( "file" ), *it
);
142 blockList
.appendChild( elm
);
146 top
.appendChild( blockList
);
149 void XMLDB::FileWriter::saveMemberGroups( QDomDocument doc
, QDomElement top
)
151 if ( _db
->_members
.isEmpty() )
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
) )
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() );
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
);
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>" );
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() );
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
);
265 writeCategories( doc
, elm
, info
);
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
) )
281 QDomElement opt
= doc
.createElement( QString::fromLatin1("option") );
282 opt
.setAttribute( QString::fromLatin1("name"), escape( name
) );
284 StringSet items
= info
->itemsOfCategory(*categoryIt
);
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
);
294 elm
.appendChild( opt
);
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
) )
310 StringSet items
= info
->itemsOfCategory(categoryName
);
311 if ( !items
.empty() ) {
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
));
330 return dynamic_cast<XMLCategory
*>( _db
->_categoryCollection
.categoryForName( categoryName
).data() )->shouldSave();
333 QString
XMLDB::FileWriter::escape( const QString
& str
)
336 tmp
.replace( QString::fromLatin1( " " ), QString::fromLatin1( "_" ) );
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();