26 #include <KApplication>
29 #include <KInputDialog>
31 #include <KLocalizedString>
32 #include <KMessageBox>
33 #include <KProgressDialog>
34 #include <KStandardDirs>
35 #include <KUrlRequester>
37 #include <QtCore/QPointer>
38 #include <QtCore/QTextCodec>
39 #include <QtCore/QThread>
40 #include <QtCore/QUuid>
41 #include <QButtonGroup>
43 #include <QGridLayout>
45 #include <QHBoxLayout>
47 #include <QPushButton>
48 #include <QRadioButton>
49 #include <QStyledItemDelegate>
51 #include <QHeaderView>
61 class ContactFieldComboBox :
public KComboBox
65 ContactFieldComboBox(
QWidget *parent = 0 )
80 for (
int i = 0; i < count(); ++i ) {
81 maxLength = qMax( maxLength, itemText( i ).length() );
84 setMinimumContentsLength( maxLength );
85 setSizeAdjustPolicy( AdjustToMinimumContentsLength );
86 setFixedSize( sizeHint() );
91 setCurrentIndex( findData( (uint)field ) );
100 static void fillFieldMap()
102 if ( !mFieldMap.isEmpty() ) {
109 for (
int i = 0; i < fields.
count(); ++i ) {
122 ContactFieldDelegate(
QObject *parent = 0 )
135 ContactFieldComboBox *editor =
new ContactFieldComboBox( parent );
142 const unsigned int value = index.
model()->
data( index, Qt::EditRole ).
toUInt();
144 ContactFieldComboBox *fieldCombo =
static_cast<ContactFieldComboBox*
>( editor );
150 ContactFieldComboBox *fieldCombo =
static_cast<ContactFieldComboBox*
>( editor );
152 model->
setData( index, fieldCombo->currentField(), Qt::EditRole );
164 if ( index.
row() == 0 ) {
166 headerOption.font.setBold(
true );
176 :
KDialog( parent ), mDevice( 0 )
178 setCaption( i18nc(
"@title:window",
"CSV Import Dialog" ) );
179 setButtons( Ok | Cancel | User1 | User2 );
180 setDefaultButton( Ok );
182 showButtonSeparator(
true );
190 connect( mUrlRequester, SIGNAL(returnPressed(
QString)),
191 this, SLOT(setFile(
QString)) );
192 connect( mUrlRequester, SIGNAL(urlSelected(KUrl)),
193 this, SLOT(setFile(KUrl)) );
194 connect( mUrlRequester->lineEdit(), SIGNAL(textChanged(
QString)),
195 this, SLOT(urlChanged(
QString)) );
196 connect( mDelimiterGroup, SIGNAL(buttonClicked(
int)),
197 this, SLOT(delimiterClicked(
int)) );
198 connect( mDelimiterEdit, SIGNAL(returnPressed()),
199 this, SLOT(customDelimiterChanged()) );
200 connect( mDelimiterEdit, SIGNAL(textChanged(
QString)),
201 this, SLOT(customDelimiterChanged(
QString)) );
202 connect( mComboQuote, SIGNAL(activated(
QString)),
203 this, SLOT(textQuoteChanged(
QString)) );
204 connect( mCodecCombo, SIGNAL(activated(
QString)),
205 this, SLOT(codecChanged()) );
206 connect( mSkipFirstRow, SIGNAL(toggled(
bool)),
207 this, SLOT(skipFirstRowChanged(
bool)) );
209 connect( mModel, SIGNAL(finishedLoading()),
this, SLOT(modelFinishedLoading()) );
211 delimiterClicked( 0 );
213 skipFirstRowChanged(
false );
224 DateParser dateParser( mDatePatternEdit->text() );
226 KProgressDialog progressDialog( const_cast<CSVImportDialog*>(
this )->mainWidget() );
227 progressDialog.setAutoClose(
true );
228 progressDialog.progressBar()->setMaximum( mModel->
rowCount() );
229 progressDialog.setLabelText( i18nc(
"@label",
"Importing contacts" ) );
230 progressDialog.show();
232 kapp->processEvents();
234 for (
int row = 1; row < mModel->
rowCount(); ++row ) {
235 KABC::Addressee contact;
236 bool emptyRow =
true;
238 for (
int column = 0; column < mModel->
columnCount(); ++column ) {
239 QString value = mModel->
data( mModel->
index( row, column ), Qt::DisplayRole ).toString();
249 value = dateParser.parse( value ).toString( Qt::ISODate );
258 kapp->processEvents();
260 if ( progressDialog.wasCancelled() ) {
261 return KABC::AddresseeList();
264 progressDialog.progressBar()->setValue( progressDialog.progressBar()->value() + 1 );
266 if ( !emptyRow && !contact.isEmpty() ) {
267 contacts.append( contact );
274 void CSVImportDialog::initGUI()
277 setMainWidget( page );
286 QLabel *label =
new QLabel( i18nc(
"@label",
"File to import:" ), page );
289 mUrlRequester =
new KUrlRequester( page );
291 mUrlRequester->lineEdit()->setTrapReturnKey(
true );
292 mUrlRequester->setToolTip(
293 i18nc(
"@info:tooltip",
"Select a csv file to import" ) );
294 mUrlRequester->setWhatsThis(
295 i18nc(
"@info:whatsthis",
296 "Click this button to start a file chooser that will allow you to "
297 "select a csv file to import." ) );
305 delimiterLayout->
setMargin( marginHint() );
316 i18nc(
"@info:tooltip",
"Set the field separator to a comma" ) );
318 i18nc(
"@info:whatsthis",
319 "Select this option if your csv file uses the comma as a field separator." ) );
322 delimiterLayout->
addWidget( button, 0, 0 );
324 button =
new QRadioButton( i18nc(
"@option:radio Field separator",
"Semicolon" ) );
326 i18nc(
"@info:tooltip",
"Set the field separator to a semicolon" ) );
328 i18nc(
"@info:whatsthis",
329 "Select this option if your csv file uses the semicolon as a field separator." ) );
331 delimiterLayout->
addWidget( button, 0, 1 );
333 button =
new QRadioButton( i18nc(
"@option:radio Field separator",
"Tabulator" ) );
335 i18nc(
"@info:tooltip",
"Set the field separator to a tab character" ) );
337 i18nc(
"@info:whatsthis",
338 "Select this option if your csv file uses the tab character as a field separator." ) );
340 delimiterLayout->
addWidget( button, 1, 0 );
342 button =
new QRadioButton( i18nc(
"@option:radio Field separator",
"Space" ) );
344 i18nc(
"@info:tooltip",
"Set the field separator to a space character" ) );
346 i18nc(
"@info:whatsthis",
347 "Select this option if your csv file uses the space character as a field separator." ) );
349 delimiterLayout->
addWidget( button, 1, 1 );
351 button =
new QRadioButton( i18nc(
"@option:radio Custum field separator",
"Other" ) );
353 i18nc(
"@info:tooltip",
"Set the field separator to a custom character" ) );
355 i18nc(
"@info:whatsthis",
356 "Select this option if to use some other character as the field delimiter "
357 "for the data in your csv file." ) );
359 delimiterLayout->
addWidget( button, 0, 2 );
361 mDelimiterEdit =
new KLineEdit( group );
362 mDelimiterEdit->setToolTip(
363 i18nc(
"@info:tooltip",
364 "Set the custom delimiter character" ) );
365 mDelimiterEdit->setWhatsThis(
366 i18nc(
"@info:whatsthis",
367 "Enter a custom character to use as the delimiter character. "
368 "If you enter more than 1 character, only the first will be used and "
369 "the remaining characters will be ignored." ) );
370 delimiterLayout->
addWidget( mDelimiterEdit, 1, 2 );
373 label =
new QLabel( i18nc(
"@label:listbox",
"Text quote:" ), page );
376 mComboQuote =
new KComboBox( page );
377 mComboQuote->setToolTip(
378 i18nc(
"@info:tooltip",
"Select the quote character" ) );
379 mComboQuote->setWhatsThis(
380 i18nc(
"@info:whatsthis",
381 "Choose the character that your csv data uses to \"quote\" the field delimiter "
382 "if that character happens to occur within the data. For example, if the "
383 "comma is the field delimiter, then any comma occurring with the data "
384 "will be \"quoted\" by the character specified here." ) );
385 mComboQuote->setEditable(
false );
386 mComboQuote->addItem( i18nc(
"@item:inlistbox Qoute character option",
"\"" ), 0 );
387 mComboQuote->addItem( i18nc(
"@item:inlistbox Quote character option",
"'" ), 1 );
388 mComboQuote->addItem( i18nc(
"@item:inlistbox Quote character option",
"None" ), 2 );
392 label =
new QLabel( i18nc(
"@label:listbox",
"Date format:" ), page );
395 mDatePatternEdit =
new KLineEdit( page );
397 mDatePatternEdit->setToolTip(
398 i18nc(
"@info:tooltip",
399 "<para><list><item>y: year with 2 digits</item>"
400 "<item>Y: year with 4 digits</item>"
401 "<item>m: month with 1 or 2 digits</item>"
402 "<item>M: month with 2 digits</item>"
403 "<item>d: day with 1 or 2 digits</item>"
404 "<item>D: day with 2 digits</item>"
405 "<item>H: hours with 2 digits</item>"
406 "<item>I: minutes with 2 digits</item>"
407 "<item>S: seconds with 2 digits</item>"
408 "</list></para>" ) );
409 mDatePatternEdit->setWhatsThis(
410 i18nc(
"@info:whatsthis",
411 "<para>Specify a format to use for dates included in your csv data. "
412 "Use the following sequences to help you define the format:</para>"
413 "<para><list><item>y: year with 2 digits</item>"
414 "<item>Y: year with 4 digits</item>"
415 "<item>m: month with 1 or 2 digits</item>"
416 "<item>M: month with 2 digits</item>"
417 "<item>d: day with 1 or 2 digits</item>"
418 "<item>D: day with 2 digits</item>"
419 "<item>H: hours with 2 digits</item>"
420 "<item>I: minutes with 2 digits</item>"
421 "<item>S: seconds with 2 digits</item>"
423 "<para>Example: \"Y-M-D\" corresponds to a date like \"2012-01-04\"</para>" ) );
424 layout->
addWidget( mDatePatternEdit, 2, 3 );
427 label =
new QLabel( i18nc(
"@label:listbox",
"Text codec:" ), page );
430 mCodecCombo =
new KComboBox( page );
431 mCodecCombo->setToolTip(
432 i18nc(
"@info:tooltip",
"Select the text codec" ) );
433 mCodecCombo->setWhatsThis(
434 i18nc(
"@info:whatsthis",
435 "Choose the character encoding of the data in your csv file." ) );
439 mSkipFirstRow =
new QCheckBox( i18nc(
"@option:check",
"Skip first row of file" ), page );
441 i18nc(
"@info:tooltip",
"Skip first row of csv file when importing" ) );
443 i18nc(
"@info:whatsthis",
444 "Check this box if you want the import to skip over the first row "
445 "of the csv data. In many cases, the first line of a csv file will be a "
446 "comment line describing the order of the data fields included in the file." ) );
447 layout->
addWidget( mSkipFirstRow, 4, 2, 1, 2 );
459 setButtonText( User1, i18nc(
"@action:button",
"Apply Template..." ) );
460 setButtonText( User2, i18nc(
"@action:button",
"Save Template..." ) );
462 enableButton( Ok,
false );
463 enableButton( User1,
false );
464 enableButton( User2,
false );
469 void CSVImportDialog::reloadCodecs()
471 mCodecCombo->clear();
479 mCodecCombo->addItem( i18nc(
"@item:inlistbox Codec setting",
"Local (%1)",
481 mCodecCombo->addItem( i18nc(
"@item:inlistbox Codec setting",
"Latin1" ),
Latin1 );
482 mCodecCombo->addItem( i18nc(
"@item:inlistbox Codec setting",
"Unicode" ),
Uni );
483 mCodecCombo->addItem( i18nc(
"@item:inlistbox Codec setting",
"Microsoft Unicode" ),
MSBug );
485 for (
int i = 0; i < mCodecs.
count(); ++i ) {
490 void CSVImportDialog::customDelimiterChanged()
492 if ( mDelimiterGroup->
checkedId() == 4 ) {
493 delimiterClicked( 4 );
497 void CSVImportDialog::customDelimiterChanged(
const QString &,
bool reload )
500 delimiterClicked( 4, reload );
503 void CSVImportDialog::delimiterClicked(
int id,
bool reload )
510 mDelimiterEdit->setFocus( Qt::OtherFocusReason );
511 if ( !mDelimiterEdit->text().isEmpty() ) {
526 if ( mDevice && reload ) {
527 mModel->
load( mDevice );
531 void CSVImportDialog::textQuoteChanged(
const QString &mark,
bool reload )
533 if ( mComboQuote->currentIndex() == 2 ) {
539 if ( mDevice && reload ) {
540 mModel->
load( mDevice );
544 void CSVImportDialog::skipFirstRowChanged(
bool checked,
bool reload )
546 mFieldSelection.
clear();
547 for (
int column = 0; column < mModel->
columnCount(); ++column ) {
558 if ( mDevice && reload ) {
559 mModel->
load( mDevice );
565 if ( button == KDialog::Ok ) {
566 bool assigned =
false;
568 for (
int column = 0; column < mModel->
columnCount(); ++column ) {
569 if ( mModel->
data( mModel->
index( 0, column ),
579 i18nc(
"@info:status",
"You must assign at least one column." ) );
583 }
else if ( button == User1 ) {
585 }
else if ( button == User2 ) {
587 }
else if ( button == KDialog::Cancel ) {
592 void CSVImportDialog::applyTemplate()
595 if ( !dlg->templatesAvailable() ) {
598 i18nc(
"@label",
"There are no templates available yet." ),
599 i18nc(
"@title:window",
"No templates available" ) );
604 if ( !dlg->exec() || !dlg ) {
609 const QString templateFileName = dlg->selectedTemplate();
612 KConfig config( templateFileName, KConfig::SimpleConfig );
614 const KConfigGroup generalGroup( &config,
"General" );
615 mDatePatternEdit->setText( generalGroup.readEntry(
"DatePattern",
"Y-M-D" ) );
616 mDelimiterEdit->setText( generalGroup.readEntry(
"DelimiterOther" ) );
618 const int delimiterButton = generalGroup.readEntry(
"DelimiterType", 0 );
619 const int quoteType = generalGroup.readEntry(
"QuoteType", 0 );
620 const bool skipFirstRow = generalGroup.readEntry(
"SkipFirstRow",
false );
623 delimiterClicked( delimiterButton,
false );
625 mComboQuote->setCurrentIndex( quoteType );
626 textQuoteChanged( mComboQuote->currentText(), false );
634 skipFirstRowChanged( skipFirstRow,
false );
637 mModel->
load( mDevice );
640 setProperty(
"TemplateFileName", templateFileName );
641 connect( mModel, SIGNAL(finishedLoading()),
this, SLOT(finalizeApplyTemplate()) );
644 void CSVImportDialog::finalizeApplyTemplate()
646 const QString templateFileName = property(
"TemplateFileName" ).toString();
648 KConfig config( templateFileName, KConfig::SimpleConfig );
650 const KConfigGroup generalGroup( &config,
"General" );
651 const uint columns = generalGroup.readEntry(
"Columns", 0 );
654 const KConfigGroup columnMapGroup( &config,
"csv column map" );
656 for ( uint i = 0; i < columns; ++i ) {
657 const uint assignedField = columnMapGroup.readEntry(
QString::number( i ), 0 );
658 mModel->
setData( mModel->
index( 0, i ), assignedField, Qt::EditRole );
662 void CSVImportDialog::saveTemplate()
665 KInputDialog::getText( i18nc(
"@title:window",
"Template Name" ),
666 i18nc(
"@info",
"Please enter a name for the template:" ) );
673 KStandardDirs::locateLocal(
"data",
QLatin1String(
"kaddressbook/csv-templates/") +
677 KConfig config( fileName );
678 KConfigGroup generalGroup( &config,
"General" );
679 generalGroup.writeEntry(
"DatePattern", mDatePatternEdit->text() );
680 generalGroup.writeEntry(
"Columns", mModel->
columnCount() );
681 generalGroup.writeEntry(
"DelimiterType", mDelimiterGroup->
checkedId() );
682 generalGroup.writeEntry(
"DelimiterOther", mDelimiterEdit->text() );
683 generalGroup.writeEntry(
"SkipFirstRow", mSkipFirstRow->
isChecked() );
684 generalGroup.writeEntry(
"QuoteType", mComboQuote->currentIndex() );
686 KConfigGroup miscGroup( &config,
"Misc" );
687 miscGroup.writeEntry(
"Name", name );
689 KConfigGroup columnMapGroup( &config,
"csv column map" );
690 for (
int column = 0; column < mModel->
columnCount(); ++column ) {
692 mModel->
data( mModel->
index( 0, column ),
693 Qt::DisplayRole ).toUInt() );
699 void CSVImportDialog::setFile(
const KUrl &fileName )
701 setFile( fileName.toLocalFile() );
704 void CSVImportDialog::setFile(
const QString &fileName )
711 if ( !file->
open( QIODevice::ReadOnly ) ) {
712 KMessageBox::sorry(
this, i18nc(
"@info:status",
"Cannot open input file." ) );
721 mModel->
load( mDevice );
724 void CSVImportDialog::urlChanged(
const QString &file )
728 enableButton( Ok, state );
729 enableButton( User1, state );
730 enableButton( User2, state );
733 void CSVImportDialog::codecChanged(
bool reload )
735 const int code = mCodecCombo->currentIndex();
737 if ( code ==
Local ) {
739 }
else if ( code >=
Codec ) {
741 }
else if ( code ==
Uni ) {
743 }
else if ( code ==
MSBug ) {
745 }
else if ( code ==
Latin1 ) {
751 if ( mDevice && reload ) {
752 mModel->
load( mDevice );
756 void CSVImportDialog::modelFinishedLoading()
758 ContactFieldComboBox *box =
new ContactFieldComboBox();
759 int preferredWidth = box->sizeHint().width();
762 for (
int i = 0; i < mModel->
columnCount(); ++i ) {
766 for (
int column = 0; column < mFieldSelection.
count(); ++column ) {
767 mModel->
setData( mModel->
index( 0, column ), mFieldSelection.
at( column ), Qt::EditRole );
769 mFieldSelection.
clear();
772 #include <csvimportdialog.moc>
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const
void setColumnWidth(int column, int width)
void setTextCodec(QTextCodec *textCodec)
Sets the text codec that shall be used for parsing the csv list.
void append(const T &value)
KABC::AddresseeList contacts() const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
Inherited from QAbstractTableModel.
void setStartRow(uint startRow)
Sets the row from where the parsing shall be started.
void setDelimiter(const QChar &delimiter)
Sets the character that is used as delimiter for fields.
const T & at(int i) const
virtual void slotButtonClicked(int)
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
Inherited from QAbstractTableModel.
This class parses the datetime out of a given string with the help of a pattern.
bool load(QIODevice *device)
Loads the data from the device into the model.
QTextCodec * codecForLocale()
void setSpacing(int spacing)
QString number(int n, int base)
int count(const T &value) const
void append(const T &value)
virtual void setModel(QAbstractItemModel *model)
uint toUInt(bool *ok) const
CSVImportDialog(QWidget *parent=0)
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
virtual QString displayText(const QVariant &value, const QLocale &locale) const
virtual QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const
Inherited from QAbstractTableModel.
void setEditTriggers(QFlags< QAbstractItemView::EditTrigger > triggers)
virtual QVariant data(const QModelIndex &index, int role) const =0
void setMargin(int margin)
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
bool blockSignals(bool block)
void addLayout(QLayout *layout, int row, int column, QFlags< Qt::AlignmentFlag > alignment)
bool setAlignment(QWidget *w, QFlags< Qt::AlignmentFlag > alignment)
QString & replace(int position, int n, QChar after)
const T & at(int i) const
QList< QByteArray > availableCodecs()
const QAbstractItemModel * model() const
char * toString(const T &value)
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
void setTextQuote(const QChar &textQuote)
Sets the character that is used for quoting.
void setItemDelegateForRow(int row, QAbstractItemDelegate *delegate)
const QChar at(int position) const
QTextCodec * codecForName(const QByteArray &name)
int count(const T &value) const
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
virtual bool setData(const QModelIndex &index, const QVariant &data, int role=Qt::EditRole)
Inherited from QAbstractTableModel.
void setSpacing(int spacing)