Fix regressions
[kphotoalbum.git] / ImportExport / ImportDialog.cpp
blobfdd309d7e43575b829a6e9bc97bbd675094ef94d
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 "ImportDialog.h"
20 #include "MD5CheckPage.h"
21 #include "KimFileReader.h"
22 #include "ImageRow.h"
23 #include <kfiledialog.h>
24 #include <qlabel.h>
25 #include <QHBoxLayout>
26 #include <QGridLayout>
27 #include <QPixmap>
28 #include <klocale.h>
29 #include <qpushbutton.h>
30 #include <qdom.h>
31 #include <klineedit.h>
32 #include <kpushbutton.h>
33 #include "Settings/SettingsData.h"
34 #include "ImportMatcher.h"
35 #include <qcheckbox.h>
36 #include <kstandarddirs.h>
37 #include <ktoolinvocation.h>
38 #include "DB/ImageInfo.h"
39 #include "XMLDB/Database.h"
40 #include <QComboBox>
41 #include <QScrollArea>
42 #include <KMessageBox>
44 using Utilities::StringSet;
46 class KPushButton;
47 using namespace ImportExport;
50 ImportDialog::ImportDialog( QWidget* parent )
51 :KAssistantDialog( parent ), _hasFilled( false ), _md5CheckPage(0)
55 bool ImportDialog::exec( KimFileReader* kimFileReader, const QString& fileName, const KUrl& kimFileURL )
57 _kimFileReader = kimFileReader;
59 _kimFile = kimFileURL;
61 QByteArray indexXML = _kimFileReader->indexXML();
62 if ( indexXML.isNull() )
63 return false;
65 bool ok = readFile( indexXML, fileName );
66 if ( !ok )
67 return false;
69 setupPages();
71 return KAssistantDialog::exec() ;
74 bool ImportDialog::readFile( const QByteArray& data, const QString& fileName )
76 QDomDocument doc;
77 QString errMsg;
78 int errLine;
79 int errCol;
81 if ( !doc.setContent( data, false, &errMsg, &errLine, &errCol )) {
82 KMessageBox::error( this, i18n( "Error in file %1 on line %2 col %3: %4" ,fileName,errLine,errCol,errMsg) );
83 return false;
86 QDomElement top = doc.documentElement();
87 if ( top.tagName().toLower() != QString::fromLatin1( "kimdaba-export" ) &&
88 top.tagName().toLower() != QString::fromLatin1( "kphotoalbum-export" ) ) {
89 KMessageBox::error( this, i18n("Unexpected top element while reading file %1. Expected KPhotoAlbum-export found %2",
90 fileName ,top.tagName() ) );
91 return false;
94 // Read source
95 QString source = top.attribute( QString::fromLatin1( "location" ) ).toLower();
96 if ( source != QString::fromLatin1( "inline" ) && source != QString::fromLatin1( "external" ) ) {
97 KMessageBox::error( this, i18n("<p>XML file did not specify the source of the images, "
98 "this is a strong indication that the file is corrupted</p>" ) );
99 return false;
102 _externalSource = ( source == QString::fromLatin1( "external" ) );
104 // Read base url
105 _baseUrl = top.attribute( QString::fromLatin1( "baseurl" ) );
107 for ( QDomNode node = top.firstChild(); !node.isNull(); node = node.nextSibling() ) {
108 if ( !node.isElement() || ! (node.toElement().tagName().toLower() == QString::fromLatin1( "image" ) ) ) {
109 KMessageBox::error( this, i18n("Unknown element while reading %1, expected image.", fileName ) );
110 return false;
112 QDomElement elm = node.toElement();
114 DB::ImageInfoPtr info = XMLDB::Database::createImageInfo( elm.attribute( QString::fromLatin1( "file" ) ), elm );
115 _images.append( info );
118 return true;
121 void ImportDialog::setupPages()
123 createIntroduction();
124 createImagesPage();
125 createDestination();
126 createCategoryPages();
127 connect( this, SIGNAL( currentPageChanged( KPageWidgetItem*, KPageWidgetItem* ) ), this, SLOT( updateNextButtonState() ) );
128 connect( this, SIGNAL( helpClicked() ), this, SLOT( slotHelp() ) );
131 void ImportDialog::createIntroduction()
133 QString txt = i18n( "<h1><font size=\"+2\">Welcome to KPhotoAlbum Import</font></h1>"
134 "This wizard will take you through the steps of an import operation. The steps are: "
135 "<ol><li>First you must select which images you want to import from the export file. "
136 "You do so by selecting the checkbox next to the image.</li>"
137 "<li>Next you must tell KPhotoAlbum in which directory to put the images. This directory must "
138 "of course be below the directory root KPhotoAlbum uses for images. "
139 "KPhotoAlbum will take care to avoid name clashes</li>"
140 "<li>The next step is to specify which categories you want to import (People, Places, ... ) "
141 "and also tell KPhotoAlbum how to match the categories from the file to your categories. "
142 "Imagine you load from a file, where a category is called <b>Blomst</b> (which is the "
143 "Danish word for flower), then you would likely want to match this with your category, which might be "
144 "called <b>Blume</b> (which is the German word for flower) - of course given you are German.</li>"
145 "<li>The final steps, is matching the individual tokens from the categories. I may call myself <b>Jesper</b> "
146 "in my image database, while you want to call me by my full name, namely <b>Jesper K. Pedersen</b>. "
147 "In this step non matches will be highlighted in red, so you can see which tokens was not found in your "
148 "database, or which tokens was only a partial match.</li></ol>");
150 QLabel* intro = new QLabel( txt, this );
151 intro->setWordWrap(true);
152 addPage( intro, i18n("Introduction") );
155 void ImportDialog::createImagesPage()
157 QScrollArea* top = new QScrollArea;
158 top->setWidgetResizable(true);
160 QWidget* container = new QWidget;
161 QVBoxLayout* lay1 = new QVBoxLayout( container );
162 top->setWidget( container );
164 // Select all and Deselect All buttons
165 QHBoxLayout* lay2 = new QHBoxLayout;
166 lay1->addLayout(lay2);
168 QPushButton* selectAll = new QPushButton( i18n("Select All"), container );
169 lay2->addWidget( selectAll );
170 QPushButton* selectNone = new QPushButton( i18n("Deselect All"), container );
171 lay2->addWidget( selectNone );
172 lay2->addStretch( 1 );
173 connect( selectAll, SIGNAL( clicked() ), this, SLOT( slotSelectAll() ) );
174 connect( selectNone, SIGNAL( clicked() ), this, SLOT( slotSelectNone() ) );
176 QGridLayout* lay3 = new QGridLayout;
177 lay1->addLayout( lay3 );
179 lay3->setColumnStretch( 2, 1 );
181 int row = 0;
182 for( DB::ImageInfoListConstIterator it = _images.constBegin(); it != _images.constEnd(); ++it, ++row ) {
183 DB::ImageInfoPtr info = *it;
184 ImageRow* ir = new ImageRow( info, this, _kimFileReader, container );
185 lay3->addWidget( ir->m_checkbox, row, 0 );
187 QPixmap pixmap = _kimFileReader->loadThumbnail( info->fileName( DB::RelativeToImageRoot ) );
188 if ( !pixmap.isNull() ) {
189 QPushButton* but = new QPushButton( container );
190 but->setIcon( pixmap );
191 but->setIconSize( pixmap.size() );
192 lay3->addWidget( but, row, 1 );
193 connect( but, SIGNAL( clicked() ), ir, SLOT( showImage() ) );
195 else {
196 QLabel* label = new QLabel( info->label() );
197 lay3->addWidget( label, row, 1 );
200 QLabel* label = new QLabel( QString::fromLatin1("<p>%1</p>").arg(info->description()) );
201 lay3->addWidget( label, row, 2 );
202 _imagesSelect.append( ir );
205 addPage( top, i18n("Select Which Images to Import") );
208 void ImportDialog::createDestination()
210 QWidget* top = new QWidget( this );
211 QVBoxLayout* topLay = new QVBoxLayout( top );
212 QHBoxLayout* lay = new QHBoxLayout;
213 topLay->addLayout(lay);
215 topLay->addStretch( 1 );
217 QLabel* label = new QLabel( i18n( "Destination of images: " ), top );
218 lay->addWidget( label );
220 _destinationEdit = new KLineEdit( top );
221 lay->addWidget( _destinationEdit, 1 );
223 KPushButton* but = new KPushButton( QString::fromLatin1("..." ), top );
224 but->setFixedWidth( 30 );
225 lay->addWidget( but );
228 _destinationEdit->setText( Settings::SettingsData::instance()->imageDirectory());
229 connect( but, SIGNAL( clicked() ), this, SLOT( slotEditDestination() ) );
230 connect( _destinationEdit, SIGNAL( textChanged( const QString& ) ), this, SLOT( updateNextButtonState() ) );
231 _destinationPage = addPage( top, i18n("Destination of Images" ) );
234 void ImportDialog::slotEditDestination()
236 QString file = KFileDialog::getExistingDirectory( _destinationEdit->text(), this );
237 if ( !file.isNull() ) {
238 if ( ! QFileInfo(file).absoluteFilePath().startsWith( QFileInfo(Settings::SettingsData::instance()->imageDirectory()).absoluteFilePath()) ) {
239 KMessageBox::error( this, i18n("The directory must be a subdirectory of %1", Settings::SettingsData::instance()->imageDirectory() ) );
241 else {
242 _destinationEdit->setText( file );
243 updateNextButtonState();
248 void ImportDialog::updateNextButtonState()
250 bool enabled = true;
251 if ( currentPage() == _destinationPage ) {
252 QString dest = _destinationEdit->text();
253 if ( QFileInfo( dest ).isFile() )
254 enabled = false;
255 else if ( ! QFileInfo(dest).absoluteFilePath().startsWith( QFileInfo(Settings::SettingsData::instance()->imageDirectory()).absoluteFilePath()) )
256 enabled = false;
259 setValid( currentPage(), enabled );
262 void ImportDialog::createCategoryPages()
264 QStringList categories;
265 DB::ImageInfoList images = selectedImages();
266 for( DB::ImageInfoListConstIterator it = images.constBegin(); it != images.constEnd(); ++it ) {
267 DB::ImageInfoPtr info = *it;
268 QStringList categoriesForImage = info->availableCategories();
269 for( QStringList::Iterator categoryIt = categoriesForImage.begin(); categoryIt != categoriesForImage.end(); ++categoryIt ) {
270 if ( !categories.contains( *categoryIt ) &&
271 (*categoryIt) != QString::fromLatin1( "Folder" ) &&
272 (*categoryIt) != QString::fromLatin1( "Tokens" ) &&
273 (*categoryIt) != QString::fromLatin1( "Media Type" ))
274 categories.append( *categoryIt );
278 if ( !categories.isEmpty() ) {
279 _categoryMatcher = new ImportMatcher( QString(), QString(), categories, DB::ImageDB::instance()->categoryCollection()->categoryNames(),
280 false, this );
281 _categoryMatcherPage = addPage( _categoryMatcher, i18n("Match Categories") );
283 QWidget* dummy = new QWidget;
284 _dummy = addPage( dummy, QString() );
286 else {
287 _categoryMatcherPage = 0;
288 possiblyAddMD5CheckPage();
292 ImportMatcher* ImportDialog::createCategoryPage( const QString& myCategory, const QString& otherCategory )
294 StringSet otherItems;
295 DB::ImageInfoList images = selectedImages();
296 for( DB::ImageInfoListConstIterator it = images.constBegin(); it != images.constEnd(); ++it ) {
297 otherItems += (*it)->itemsOfCategory( otherCategory );
300 QStringList myItems = DB::ImageDB::instance()->categoryCollection()->categoryForName( myCategory )->itemsInclCategories();
301 myItems.sort();
303 ImportMatcher* matcher = new ImportMatcher( otherCategory, myCategory, otherItems.toList(), myItems, true, this );
304 addPage( matcher, myCategory );
305 return matcher;
308 void ImportDialog::next()
310 if ( currentPage() == _destinationPage ) {
311 QString dir = _destinationEdit->text();
312 if ( !QFileInfo( dir ).exists() ) {
313 int answer = KMessageBox::questionYesNo( this, i18n("Directory %1 does not exist. Should it be created?", dir ) );
314 if ( answer == KMessageBox::Yes ) {
315 bool ok = KStandardDirs::makeDir( dir );
316 if ( !ok ) {
317 KMessageBox::error( this, i18n("Error creating directory %1", dir ) );
318 return;
321 else
322 return;
325 if ( !_hasFilled && currentPage() == _categoryMatcherPage ) {
326 _hasFilled = true;
327 _categoryMatcher->setEnabled( false );
328 removePage(_dummy);
330 ImportMatcher* matcher = 0;
331 for( QList<CategoryMatch*>::Iterator it = _categoryMatcher->_matchers.begin();
332 it != _categoryMatcher->_matchers.end();
333 ++it )
335 CategoryMatch* match = *it;
336 if ( match->_checkbox->isChecked() ) {
337 matcher = createCategoryPage( match->_combobox->currentText(), match->_text );
338 _matchers.append( matcher );
341 possiblyAddMD5CheckPage();
344 KAssistantDialog::next();
347 void ImportDialog::slotSelectAll()
349 selectImage( true );
352 void ImportDialog::slotSelectNone()
354 selectImage( false );
357 void ImportDialog::selectImage( bool on )
359 for( QList<ImageRow*>::Iterator it = _imagesSelect.begin(); it != _imagesSelect.end(); ++it ) {
360 (*it)->m_checkbox->setChecked( on );
364 DB::ImageInfoList ImportDialog::selectedImages() const
366 DB::ImageInfoList res;
367 for( QList<ImageRow*>::ConstIterator it = _imagesSelect.begin(); it != _imagesSelect.end(); ++it ) {
368 if ( (*it)->m_checkbox->isChecked() )
369 res.append( (*it)->m_info );
371 return res;
374 void ImportDialog::slotHelp()
376 KToolInvocation::invokeHelp( QString::fromLatin1( "kphotoalbum#chp-exportDialog" ) );
379 ImportSettings ImportExport::ImportDialog::settings()
381 ImportSettings settings;
382 settings.setSelectedImages( selectedImages() );
383 settings.setDestination( _destinationEdit->text() );
384 settings.setExternalSource( _externalSource );
385 settings.setKimFile( _kimFile );
386 settings.setBaseURL( _baseUrl );
388 if ( _md5CheckPage ) {
389 settings.setImportActions( _md5CheckPage->settings() );
392 Q_FOREACH( ImportMatcher* match, _matchers )
393 settings.addCategoryMatchSetting( match->settings() );
395 return settings;
399 void ImportExport::ImportDialog::possiblyAddMD5CheckPage()
401 if ( MD5CheckPage::pageNeeded( settings() ) ) {
402 _md5CheckPage = new MD5CheckPage( settings() );
403 addPage(_md5CheckPage, i18n("How to resolve clashes") );
407 #include "ImportDialog.moc"