Fix regressions
[kphotoalbum.git] / ImportExport / Export.cpp
blob3798b8526591b86fbae215b8bfdcad0e03477e2f
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 "Export.h"
20 #include <kfiledialog.h>
21 #include <kzip.h>
22 #include <qfileinfo.h>
23 #include <Q3CString>
24 #include "Utilities/Util.h"
25 #include <q3progressdialog.h>
26 #include <klocale.h>
27 #include <time.h>
28 #include "ImageManager/Manager.h"
29 #include "DB/ImageInfo.h"
30 #include "DB/ResultId.h"
31 #include <qapplication.h>
32 #include <kmessagebox.h>
33 #include <qlayout.h>
34 #include <qcheckbox.h>
35 #include <qspinbox.h>
37 #include <qradiobutton.h>
38 #include <qbuffer.h>
39 #include "XMLHandler.h"
40 #include <QVBoxLayout>
41 #include <QGroupBox>
43 using namespace ImportExport;
45 void Export::imageExport(const DB::Result& list)
47 ExportConfig config;
48 if ( config.exec() == QDialog::Rejected )
49 return;
51 int maxSize = -1;
52 if ( config._enforeMaxSize->isChecked() )
53 maxSize = config._maxSize->value();
55 // Ask for zip file name
56 QString zipFile = KFileDialog::getSaveFileName( KUrl(), QString::fromLatin1( "*.kim|KPhotoAlbum Export Files" ) );
57 if ( zipFile.isNull() )
58 return;
60 bool ok;
61 Export* exp = new Export( list, zipFile, config._compress->isChecked(), maxSize, config.imageFileLocation(),
62 QString::fromLatin1( "" ), config._generateThumbnails->isChecked(), &ok);
63 delete exp; // It will not return before done - we still need a class to connect slots etc.
65 if ( ok )
66 showUsageDialog();
69 // PENDING(blackie) add warning if images are to be copied into a non empty directory.
70 ExportConfig::ExportConfig()
72 setWindowTitle( i18n("Export Configuration / Copy Images") );
73 setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Help );
74 QWidget* top = new QWidget;
75 setMainWidget( top );
77 QVBoxLayout* lay1 = new QVBoxLayout( top );
79 // Include images
80 QGroupBox* grp = new QGroupBox( i18n("How to Handle Images") );
81 lay1->addWidget( grp );
83 QVBoxLayout* boxLay = new QVBoxLayout( grp );
84 _include = new QRadioButton( i18n("Include in .kim file"), grp );
85 _manually = new QRadioButton( i18n("Manual copy next to .kim file"), grp );
86 _auto = new QRadioButton( i18n("Automatically copy next to .kim file"), grp );
87 _link = new QRadioButton( i18n("Hard link next to .kim file"), grp );
88 _manually->setChecked( true );
90 boxLay->addWidget( _include );
91 boxLay->addWidget( _manually );
92 boxLay->addWidget( _auto );
93 boxLay->addWidget( _link );
95 // Compress
96 _compress = new QCheckBox( i18n("Compress export file"), top );
97 lay1->addWidget( _compress );
99 // Generate thumbnails
100 _generateThumbnails = new QCheckBox( i18n("Generate thumbnails"), top );
101 _generateThumbnails->setChecked(true);
102 lay1->addWidget( _generateThumbnails );
104 // Enforece max size
105 QHBoxLayout* hlay = new QHBoxLayout;
106 lay1->addLayout( hlay );
108 _enforeMaxSize = new QCheckBox( i18n( "Limit maximum image dimensions to: " ) );
109 hlay->addWidget( _enforeMaxSize );
111 _maxSize = new QSpinBox;
112 _maxSize->setRange( 100,4000 );
114 hlay->addWidget( _maxSize );
115 _maxSize->setValue( 800 );
117 connect( _enforeMaxSize, SIGNAL( toggled( bool ) ), _maxSize, SLOT( setEnabled( bool ) ) );
118 _maxSize->setEnabled( false );
120 QString txt = i18n( "<p>If your images are stored in a non-compressed file format then you may check this; "
121 "otherwise, this just wastes time during import and export operations.</p>"
122 "<p>In other words, do not check this if your images are stored in jpg, png or gif; but do check this "
123 "if your images are stored in tiff.</p>" );
124 _compress->setWhatsThis( txt );
126 txt = i18n( "<p>Generate thumbnail images</p>" );
127 _generateThumbnails->setWhatsThis( txt );
129 txt = i18n( "<p>With this option you may limit the maximum dimensions (width and height) of your images. "
130 "Doing so will make the resulting export file smaller, but will of course also make the quality "
131 "worse if someone wants to see the exported images with larger dimensions.</p>" );
133 _enforeMaxSize->setWhatsThis( txt );
134 _maxSize->setWhatsThis( txt );
136 txt = i18n("<p>When exporting images, bear in mind that there are two things the "
137 "person importing these images again will need:<br/>"
138 "1) meta information (image content, date etc.)<br/>"
139 "2) the images themselves.</p>"
141 "<p>The images themselves can either be placed next to the .kim file, "
142 "or copied into the .kim file. Copying the images into the .kim file works well "
143 "for a recipient who wants all, or most of those images, for example "
144 "when emailing a whole group of images. However, when you place the "
145 "images on the Web, a lot of people will see them but most likely only "
146 "download a few of them. It works better in this kind of case, to "
147 "separate the images and the .kim file, by place them next to each "
148 "other, so the user can access the images s/he wants.</p>");
150 grp->setWhatsThis( txt );
151 _include->setWhatsThis( txt );
152 _manually->setWhatsThis( txt );
153 _link->setWhatsThis( txt );
154 _auto->setWhatsThis( txt );
155 setHelp( QString::fromLatin1( "chp-exportDialog" ) );
158 ImageFileLocation ExportConfig::imageFileLocation() const
160 if ( _include->isChecked() )
161 return Inline;
162 else if ( _manually->isChecked() )
163 return ManualCopy;
164 else if ( _link->isChecked() )
165 return Link;
166 else
167 return AutoCopy;
171 Export::Export(
172 const DB::Result& list,
173 const QString& zipFile,
174 bool compress,
175 int maxSize,
176 ImageFileLocation location,
177 const QString& baseUrl,
178 bool doGenerateThumbnails,
179 bool *ok)
180 : _ok( ok )
181 , _maxSize( maxSize )
182 , _location( location )
184 *ok = true;
185 _destdir = QFileInfo( zipFile ).path();
186 _zip = new KZip( zipFile );
187 _zip->setCompression( compress ? KZip::DeflateCompression : KZip::NoCompression );
188 if ( ! _zip->open( QIODevice::WriteOnly ) ) {
189 KMessageBox::error( 0, i18n("Error creating zip file") );
190 *ok = false;
191 return;
194 // Create progress dialog
195 int total = 1;
196 if (location != ManualCopy)
197 total += list.size();
198 if (doGenerateThumbnails)
199 total += list.size();
201 _steps = 0;
202 _progressDialog = new Q3ProgressDialog( QString(), i18n("&Cancel"), total, 0, "progress dialog", true );
203 _progressDialog->setProgress( 0 );
204 _progressDialog->show();
206 // Copy image files and generate thumbnails
207 if ( location != ManualCopy ) {
208 _copyingFiles = true;
209 copyImages( list );
212 if ( _ok && doGenerateThumbnails ) {
213 _copyingFiles = false;
214 generateThumbnails( list );
217 if ( _ok ) {
218 // Create the index.xml file
219 _progressDialog->setLabelText(i18n("Creating index file"));
220 Q3CString indexml = XMLHandler().createIndexXML( list, baseUrl, _location, &_filenameMapper );
221 time_t t;
222 time(&t);
223 _zip->writeFile( QString::fromLatin1( "index.xml" ), QString(), QString(), indexml.data(), indexml.size()-1 );
225 _steps++;
226 _progressDialog->setProgress( _steps );
227 _zip->close();
232 void Export::generateThumbnails(const DB::Result& list)
234 _progressDialog->setLabelText( i18n("Creating thumbnails") );
235 _loopEntered = false;
236 _subdir = QString::fromLatin1( "Thumbnails/" );
237 _filesRemaining = list.size(); // Used to break the event loop.
238 Q_FOREACH(const DB::ImageInfoPtr info, list.fetchInfos()) {
239 ImageManager::ImageRequest* request = new ImageManager::ImageRequest( info->fileName(DB::AbsolutePath), QSize( 128, 128 ), info->angle(), this );
240 request->setPriority( ImageManager::BatchTask );
241 ImageManager::Manager::instance()->load( request );
243 if ( _filesRemaining > 0 ) {
244 _loopEntered = true;
245 _eventLoop.exec();
249 void Export::copyImages(const DB::Result& list)
251 Q_ASSERT( _location != ManualCopy );
253 _loopEntered = false;
254 _subdir = QString::fromLatin1( "Images/" );
256 _progressDialog->setLabelText( i18n("Copying image files") );
258 _filesRemaining = 0;
259 Q_FOREACH(const DB::ImageInfoPtr info, list.fetchInfos()) {
260 QString file = info->fileName(DB::AbsolutePath);
261 QString zippedName = _filenameMapper.uniqNameFor(file);
263 if ( _maxSize == -1 || Utilities::isVideo( file ) ) {
264 if ( QFileInfo( file ).isSymLink() )
265 file = QFileInfo(file).readLink();
267 if ( _location == Inline )
268 _zip->addLocalFile( file, QString::fromLatin1( "Images/" ) + zippedName );
269 else if ( _location == AutoCopy )
270 Utilities::copy( file, _destdir + QString::fromLatin1( "/" ) + zippedName );
271 else if ( _location == Link )
272 Utilities::makeHardLink( file, _destdir + QString::fromLatin1( "/" ) + zippedName );
274 _steps++;
275 _progressDialog->setProgress( _steps );
277 else {
278 _filesRemaining++;
279 ImageManager::ImageRequest* request =
280 new ImageManager::ImageRequest( file, QSize( _maxSize, _maxSize ), 0, this );
281 request->setPriority( ImageManager::BatchTask );
282 ImageManager::Manager::instance()->load( request );
285 // Test if the cancel button was pressed.
286 qApp->processEvents( QEventLoop::AllEvents );
288 if ( _progressDialog->wasCanceled() ) {
289 _ok = false;
290 return;
293 if ( _filesRemaining > 0 ) {
294 _loopEntered = true;
295 _eventLoop.exec();
299 void Export::pixmapLoaded( const QString& fileName, const QSize& /*size*/, const QSize& /*fullSize*/, int /*angle*/, const QImage& image, const bool loadedOK)
301 if ( !loadedOK )
302 return;
304 const QString ext = Utilities::isVideo( fileName ) ? QString::fromLatin1( "jpg" ) : QFileInfo( _filenameMapper.uniqNameFor(fileName) ).completeSuffix();
306 // Add the file to the zip archive
307 QString zipFileName = QString::fromLatin1( "%1/%2.%3" ).arg( Utilities::stripSlash(_subdir))
308 .arg(QFileInfo( _filenameMapper.uniqNameFor(fileName) ).baseName()).arg( ext );
309 QByteArray data;
310 QBuffer buffer( &data );
311 buffer.open( QIODevice::WriteOnly );
312 image.save( &buffer, QFileInfo(zipFileName).suffix().toLower().toLatin1() );
314 if ( _location == Inline || !_copyingFiles )
315 _zip->writeFile( zipFileName, QString(), QString(), data, data.size() );
316 else {
317 QString file = _destdir + QString::fromLatin1( "/" ) + _filenameMapper.uniqNameFor(fileName);
318 QFile out( file );
319 if ( !out.open( QIODevice::WriteOnly ) ) {
320 KMessageBox::error( 0, i18n("Error writing file %1", file ) );
321 _ok = false;
323 out.write( data, data.size() );
324 out.close();
327 qApp->processEvents( QEventLoop::AllEvents );
329 bool canceled = (!_ok || _progressDialog->wasCanceled());
331 if ( canceled ) {
332 _ok = false;
333 _eventLoop.exit();
334 ImageManager::Manager::instance()->stop( this );
335 return;
338 _steps++;
339 _filesRemaining--;
340 _progressDialog->setProgress( _steps );
343 if ( _filesRemaining == 0 && _loopEntered )
344 _eventLoop.exit();
347 void Export::showUsageDialog()
349 QString txt =
350 i18n( "<p>Other KPhotoAlbum users may now load the import file into their database, by choosing <b>import</b> in "
351 "the file menu.</p>"
352 "<p>If they find it on a web site, and the web server is correctly configured, all they need to do is simply "
353 "to click it from within konqueror. To enable this, your web server needs to be configured for KPhotoAlbum. You do so by adding "
354 "the following line to <b>/etc/httpd/mime.types</b> or similar:"
355 "<pre>application/vnd.kde.kphotoalbum-import kim</pre>"
356 "This will make your web server tell konqueror that it is a KPhotoAlbum file when clicking on the link, "
357 "otherwise the web server will just tell konqueror that it is a plain text file.</p>" );
359 KMessageBox::information( 0, txt, i18n("How to Use the Export File"), QString::fromLatin1("export_how_to_use_the_export_file") );
365 #include "Export.moc"