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"
23 #include <kfiledialog.h>
25 #include <QHBoxLayout>
26 #include <QGridLayout>
29 #include <qpushbutton.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"
41 #include <QScrollArea>
42 #include <KMessageBox>
44 using Utilities::StringSet
;
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() )
65 bool ok
= readFile( indexXML
, fileName
);
71 return KAssistantDialog::exec() ;
74 bool ImportDialog::readFile( const QByteArray
& data
, const QString
& fileName
)
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
) );
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() ) );
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>" ) );
102 _externalSource
= ( source
== QString::fromLatin1( "external" ) );
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
) );
112 QDomElement elm
= node
.toElement();
114 DB::ImageInfoPtr info
= XMLDB::Database::createImageInfo( elm
.attribute( QString::fromLatin1( "file" ) ), elm
);
115 _images
.append( info
);
121 void ImportDialog::setupPages()
123 createIntroduction();
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 );
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() ) );
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() ) );
242 _destinationEdit
->setText( file
);
243 updateNextButtonState();
248 void ImportDialog::updateNextButtonState()
251 if ( currentPage() == _destinationPage
) {
252 QString dest
= _destinationEdit
->text();
253 if ( QFileInfo( dest
).isFile() )
255 else if ( ! QFileInfo(dest
).absoluteFilePath().startsWith( QFileInfo(Settings::SettingsData::instance()->imageDirectory()).absoluteFilePath()) )
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(),
281 _categoryMatcherPage
= addPage( _categoryMatcher
, i18n("Match Categories") );
283 QWidget
* dummy
= new QWidget
;
284 _dummy
= addPage( dummy
, QString() );
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();
303 ImportMatcher
* matcher
= new ImportMatcher( otherCategory
, myCategory
, otherItems
.toList(), myItems
, true, this );
304 addPage( matcher
, myCategory
);
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
);
317 KMessageBox::error( this, i18n("Error creating directory %1", dir
) );
325 if ( !_hasFilled
&& currentPage() == _categoryMatcherPage
) {
327 _categoryMatcher
->setEnabled( false );
330 ImportMatcher
* matcher
= 0;
331 for( QList
<CategoryMatch
*>::Iterator it
= _categoryMatcher
->_matchers
.begin();
332 it
!= _categoryMatcher
->_matchers
.end();
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()
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
);
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() );
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"