1 /* This file is part of the KDE project
2    Copyright (C) 1999 David Faure <faure@kde.org>
3    Copyright (C) 2004 Nicolas GOUTTE <goutte@kde.org>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public License
16    along with this library; see the file COPYING.LIB.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19 */
20 
21 #include "KoCsvImportDialog.h"
22 
23 // Qt
24 #include <QButtonGroup>
25 #include <QTextCodec>
26 #include <QTextStream>
27 
28 #include <QTableWidget>
29 #include <QTableWidgetSelectionRange>
30 
31 // KF5
32 #include <kcharsets.h>
33 #include <kconfig.h>
34 #include <WidgetsDebug.h>
35 #include <klocalizedstring.h>
36 #include <kmessagebox.h>
37 #include <ksharedconfig.h>
38 
39 #include "ui_KoCsvImportDialog.h"
40 
41 class KoCsvImportWidget : public QWidget, public Ui::KoCsvImportWidget
42 {
43 Q_OBJECT
44 public:
KoCsvImportWidget(QWidget * parent)45     explicit KoCsvImportWidget(QWidget* parent) : QWidget(parent) { setupUi(this); }
46 };
47 
48 
49 class Q_DECL_HIDDEN KoCsvImportDialog::Private
50 {
51 public:
52     KoCsvImportDialog* q;
53     KoCsvImportWidget* dialog;
54 
55     bool        rowsAdjusted;
56     bool        columnsAdjusted;
57     int         startRow;
58     int         startCol;
59     int         endRow;
60     int         endCol;
61     QChar       textQuote;
62     QString     delimiter;
63     QString     commentSymbol;
64     bool        ignoreDuplicates;
65     QByteArray  data;
66     QTextCodec* codec;
67     QStringList formatList; ///< List of the column formats
68 
Private(KoCsvImportDialog * qq)69     explicit Private(KoCsvImportDialog* qq) : q(qq) {}
70     void loadSettings();
71     void saveSettings();
72     void fillTable();
73     void setText(int row, int col, const QString& text);
74     void adjustRows(int iRows);
75     void adjustCols(int iCols);
76     bool checkUpdateRange();
77     QTextCodec* updateCodec() const;
78 };
79 
KoCsvImportDialog(QWidget * parent)80 KoCsvImportDialog::KoCsvImportDialog(QWidget* parent)
81     : KoDialog(parent)
82     , d(new Private(this))
83 {
84     d->dialog = new KoCsvImportWidget(this);
85     d->rowsAdjusted = false;
86     d->columnsAdjusted = false;
87     d->startRow = 0;
88     d->startCol = 0;
89     d->endRow = -1;
90     d->endCol = -1;
91     d->textQuote = QChar('"');
92     d->delimiter = QString(',');
93     d->commentSymbol = QString('#');
94     d->ignoreDuplicates = false;
95     d->codec = QTextCodec::codecForName("UTF-8");
96 
97     setButtons( KoDialog::Ok|KoDialog::Cancel );
98     setCaption( i18n( "Import Data" ) );
99 
100     QStringList encodings;
101     encodings << i18nc( "Descriptive encoding name", "Recommended ( %1 )" ,"UTF-8" );
102     encodings << i18nc( "Descriptive encoding name", "Locale ( %1 )" ,QString(QTextCodec::codecForLocale()->name() ));
103     encodings += KCharsets::charsets()->descriptiveEncodingNames();
104     // Add a few non-standard encodings, which might be useful for text files
105     const QString description(i18nc("Descriptive encoding name","Other ( %1 )"));
106     encodings << description.arg("Apple Roman"); // Apple
107     encodings << description.arg("IBM 850") << description.arg("IBM 866"); // MS DOS
108     encodings << description.arg("CP 1258"); // Windows
109     d->dialog->comboBoxEncoding->insertItems( 0, encodings );
110 
111     setDataTypes(Generic|Text|Date|None);
112 
113     // XXX:	Qt3->Q4
114     //d->dialog->m_sheet->setReadOnly( true );
115 
116     d->loadSettings();
117 
118     //resize(sizeHint());
119     resize( 600, 400 ); // Try to show as much as possible of the table view
120     setMainWidget(d->dialog);
121 
122     d->dialog->m_sheet->setSelectionMode( QAbstractItemView::MultiSelection );
123 
124     QButtonGroup* buttonGroup = new QButtonGroup( this );
125     buttonGroup->addButton(d->dialog->m_radioComma, 0);
126     buttonGroup->addButton(d->dialog->m_radioSemicolon, 1);
127     buttonGroup->addButton(d->dialog->m_radioSpace, 2);
128     buttonGroup->addButton(d->dialog->m_radioTab, 3);
129     buttonGroup->addButton(d->dialog->m_radioOther, 4);
130 
131     connect(d->dialog->m_formatComboBox, SIGNAL(activated(QString)),
132             this, SLOT(formatChanged(QString)));
133     connect(buttonGroup, SIGNAL(buttonClicked(int)),
134             this, SLOT(delimiterClicked(int)));
135     connect(d->dialog->m_delimiterEdit, SIGNAL(returnPressed()),
136             this, SLOT(returnPressed()));
137     connect(d->dialog->m_delimiterEdit, SIGNAL(textChanged(QString)),
138             this, SLOT(genericDelimiterChanged(QString)));
139     connect(d->dialog->m_comboQuote, SIGNAL(activated(QString)),
140             this, SLOT(textquoteSelected(QString)));
141     connect(d->dialog->m_sheet, SIGNAL(currentCellChanged(int,int,int,int)),
142             this, SLOT(currentCellChanged(int,int)));
143     connect(d->dialog->m_ignoreDuplicates, SIGNAL(stateChanged(int)),
144             this, SLOT(ignoreDuplicatesChanged(int)));
145     connect(d->dialog->m_updateButton, SIGNAL(clicked()),
146             this, SLOT(updateClicked()));
147     connect(d->dialog->comboBoxEncoding, SIGNAL(textChanged(QString)),
148             this, SLOT(encodingChanged(QString)));
149 }
150 
151 
~KoCsvImportDialog()152 KoCsvImportDialog::~KoCsvImportDialog()
153 {
154     d->saveSettings();
155     delete d;
156 }
157 
158 
159 // ----------------------------------------------------------------
160 //                       public methods
161 
162 
setData(const QByteArray & data)163 void KoCsvImportDialog::setData( const QByteArray& data )
164 {
165     d->data = data;
166     d->fillTable();
167 }
168 
169 
firstRowContainHeaders() const170 bool KoCsvImportDialog::firstRowContainHeaders() const
171 {
172     return d->dialog->m_firstRowHeader->isChecked();
173 }
174 
175 
firstColContainHeaders() const176 bool KoCsvImportDialog::firstColContainHeaders() const
177 {
178     return d->dialog->m_firstColHeader->isChecked();
179 }
180 
181 
rows() const182 int KoCsvImportDialog::rows() const
183 {
184     int rows = d->dialog->m_sheet->rowCount();
185 
186     if ( d->endRow >= 0 )
187 	rows = d->endRow - d->startRow + 1;
188 
189     return rows;
190 }
191 
192 
cols() const193 int KoCsvImportDialog::cols() const
194 {
195     int cols = d->dialog->m_sheet->columnCount();
196 
197     if ( d->endCol >= 0 )
198 	cols = d->endCol - d->startCol + 1;
199 
200     return cols;
201 }
202 
203 
text(int row,int col) const204 QString KoCsvImportDialog::text(int row, int col) const
205 {
206     // Check for overflow.
207     if ( row >= rows() || col >= cols())
208         return QString();
209 
210     QTableWidgetItem* item = d->dialog->m_sheet->item( row - d->startRow, col - d->startCol );
211     if ( !item )
212         return QString();
213     return item->text();
214 }
215 
setDataTypes(DataTypes dataTypes)216 void KoCsvImportDialog::setDataTypes(DataTypes dataTypes)
217 {
218     d->formatList.clear();
219     if (dataTypes & Generic)
220         d->formatList << i18n("Generic");
221     if (dataTypes & Text)
222         d->formatList << i18n("Text");
223     if (dataTypes & Date)
224         d->formatList << i18n("Date");
225     if (dataTypes & Currency)
226         d->formatList << i18n("Currency");
227     if (dataTypes & None)
228         d->formatList << i18n("None");
229     d->dialog->m_formatComboBox->insertItems(0, d->formatList);
230 }
231 
setDataWidgetEnabled(bool enable)232 void KoCsvImportDialog::setDataWidgetEnabled(bool enable)
233 {
234     d->dialog->m_tabWidget->setTabEnabled(0, enable);
235 }
236 
decimalSymbol() const237 QString KoCsvImportDialog::decimalSymbol() const
238 {
239     return d->dialog->m_decimalSymbol->text();
240 }
241 
setDecimalSymbol(const QString & symbol)242 void KoCsvImportDialog::setDecimalSymbol(const QString& symbol)
243 {
244     d->dialog->m_decimalSymbol->setText(symbol);
245 }
246 
thousandsSeparator() const247 QString KoCsvImportDialog::thousandsSeparator() const
248 {
249     return d->dialog->m_thousandsSeparator->text();
250 }
251 
setThousandsSeparator(const QString & separator)252 void KoCsvImportDialog::setThousandsSeparator(const QString& separator)
253 {
254     d->dialog->m_thousandsSeparator->setText(separator);
255 }
256 
delimiter() const257 QString KoCsvImportDialog::delimiter() const
258 {
259     return d->delimiter;
260 }
261 
setDelimiter(const QString & delimit)262 void KoCsvImportDialog::setDelimiter(const QString& delimit)
263 {
264     d->delimiter = delimit;
265     if (delimit == ",")
266         d->dialog->m_radioComma->setChecked(true);
267     else if (delimit == "\t")
268         d->dialog->m_radioTab->setChecked(true);
269     else if (delimit == " ")
270         d->dialog->m_radioSpace->setChecked(true);
271     else if (delimit == ";")
272         d->dialog->m_radioSemicolon->setChecked(true);
273     else {
274         d->dialog->m_radioOther->setChecked(true);
275         d->dialog->m_delimiterEdit->setText(delimit);
276     }
277 }
278 
279 
280 // ----------------------------------------------------------------
281 
282 
loadSettings()283 void KoCsvImportDialog::Private::loadSettings()
284 {
285     KConfigGroup configGroup =  KSharedConfig::openConfig()->group("CSVDialog Settings");
286     textQuote = configGroup.readEntry("textQuote", "\"").at(0);
287     delimiter = configGroup.readEntry("delimiter", ",");
288     ignoreDuplicates = configGroup.readEntry("ignoreDups", false);
289     const QString codecText = configGroup.readEntry("codec", "");
290 
291     // update widgets
292     if (!codecText.isEmpty()) {
293       dialog->comboBoxEncoding->setCurrentIndex(dialog->comboBoxEncoding->findText(codecText));
294       codec = updateCodec();
295     }
296     q->setDelimiter(delimiter);
297     dialog->m_ignoreDuplicates->setChecked(ignoreDuplicates);
298     dialog->m_comboQuote->setCurrentIndex(textQuote == '\'' ? 1 : textQuote == '"' ? 0 : 2);
299 }
300 
saveSettings()301 void KoCsvImportDialog::Private::saveSettings()
302 {
303     KConfigGroup configGroup =  KSharedConfig::openConfig()->group("CSVDialog Settings");
304     configGroup.writeEntry("textQuote", QString(textQuote));
305     configGroup.writeEntry("delimiter", delimiter);
306     configGroup.writeEntry("ignoreDups", ignoreDuplicates);
307     configGroup.writeEntry("codec", dialog->comboBoxEncoding->currentText());
308     configGroup.sync();
309 }
310 
fillTable()311 void KoCsvImportDialog::Private::fillTable()
312 {
313     int row, column;
314     bool lastCharDelimiter = false;
315     enum { Start, InQuotedField, MaybeQuotedFieldEnd, QuotedFieldEnd,
316            MaybeInNormalField, InNormalField } state = Start;
317 
318     QChar x;
319     QString field;
320 
321     QApplication::setOverrideCursor(Qt::WaitCursor);
322 
323     dialog->m_sheet->setRowCount(0);
324     dialog->m_sheet->setColumnCount(0);
325 
326     int maxColumn = 1;
327     row = column = 1;
328     QTextStream inputStream(data, QIODevice::ReadOnly);
329     debugWidgets <<"Encoding:" << codec->name();
330     inputStream.setCodec( codec );
331 
332     int delimiterIndex = 0;
333     const int delimiterLength = delimiter.size();
334     bool lastCharWasCr = false; // Last character was a Carriage Return
335     while (!inputStream.atEnd())
336     {
337         inputStream >> x; // read one char
338 
339         // ### TODO: we should perhaps skip all other control characters
340         if ( x == '\r' )
341         {
342             // We have a Carriage Return, assume that its role is the one of a LineFeed
343             lastCharWasCr = true;
344             x = '\n'; // Replace by Line Feed
345         }
346         else if ( x == '\n' && lastCharWasCr )
347         {
348             // The end of line was already handled by the Carriage Return, so do nothing for this character
349             lastCharWasCr = false;
350             continue;
351         }
352         else if ( x == QChar( 0xc ) )
353         {
354             // We have a FormFeed, skip it
355             lastCharWasCr = false;
356             continue;
357         }
358         else
359         {
360             lastCharWasCr = false;
361         }
362 
363         if ( column > maxColumn )
364           maxColumn = column;
365         switch (state)
366         {
367          case Start :
368             if (x == textQuote)
369             {
370                 state = InQuotedField;
371             }
372             else if (delimiterIndex < delimiterLength && x == delimiter.at(delimiterIndex))
373             {
374                 field += x;
375                 delimiterIndex++;
376                 if (field.right(delimiterIndex) == delimiter)
377                 {
378                     if ((ignoreDuplicates == false) || (lastCharDelimiter == false))
379                         column += delimiterLength;
380                     lastCharDelimiter = true;
381                     field.clear();
382                     delimiterIndex = 0;
383                     state = Start;
384                 }
385                 else if (delimiterIndex >= delimiterLength)
386                     delimiterIndex = 0;
387             }
388             else if (x == '\n')
389             {
390                 ++row;
391                 column = 1;
392                 if ( row > ( endRow - startRow ) && endRow >= 0 )
393                   break;
394             }
395             else
396             {
397                 field += x;
398                 state = MaybeInNormalField;
399             }
400             break;
401          case InQuotedField :
402             if (x == textQuote)
403             {
404                 state = MaybeQuotedFieldEnd;
405             }
406             else if (x == '\n')
407             {
408                 setText(row - startRow, column - startCol, field);
409                 field.clear();
410 
411                 ++row;
412                 column = 1;
413                 if ( row > ( endRow - startRow ) && endRow >= 0 )
414                   break;
415 
416                 state = Start;
417             }
418             else
419             {
420                 field += x;
421             }
422             break;
423          case MaybeQuotedFieldEnd :
424             if (x == textQuote)
425             {
426                 field += x;
427                 state = InQuotedField;
428             }
429             else if (x == '\n')
430             {
431                 setText(row - startRow, column - startCol, field);
432                 field.clear();
433                 ++row;
434                 column = 1;
435                 if ( row > ( endRow - startRow ) && endRow >= 0 )
436                     break;
437                 state = Start;
438             }
439             else if (delimiterIndex < delimiterLength && x == delimiter.at(delimiterIndex))
440             {
441                 field += x;
442                 delimiterIndex++;
443                 if (field.right(delimiterIndex) == delimiter)
444                 {
445                     setText(row - startRow, column - startCol, field.left(field.count()-delimiterIndex));
446                     field.clear();
447                     if ((ignoreDuplicates == false) || (lastCharDelimiter == false))
448                         column += delimiterLength;
449                     lastCharDelimiter = true;
450                     field.clear();
451                     delimiterIndex = 0;
452                 }
453                 else if (delimiterIndex >= delimiterLength)
454                     delimiterIndex = 0;
455                 state = Start;
456             }
457             else
458             {
459                 state = QuotedFieldEnd;
460             }
461             break;
462          case QuotedFieldEnd :
463             if (x == '\n')
464             {
465                 setText(row - startRow, column - startCol, field);
466                 field.clear();
467                 ++row;
468                 column = 1;
469                 if ( row > ( endRow - startRow ) && endRow >= 0 )
470                     break;
471                 state = Start;
472             }
473             else if (delimiterIndex < delimiterLength && x == delimiter.at(delimiterIndex))
474             {
475                 field += x;
476                 delimiterIndex++;
477                 if (field.right(delimiterIndex) == delimiter)
478                 {
479                     setText(row - startRow, column - startCol, field.left(field.count()-delimiterIndex));
480                     field.clear();
481                     if ((ignoreDuplicates == false) || (lastCharDelimiter == false))
482                         column += delimiterLength;
483                     lastCharDelimiter = true;
484                     field.clear();
485                     delimiterIndex = 0;
486                 }
487                 else if (delimiterIndex >= delimiterLength)
488                     delimiterIndex = 0;
489                 state = Start;
490             }
491             else
492             {
493                 state = QuotedFieldEnd;
494             }
495             break;
496          case MaybeInNormalField :
497             if (x == textQuote)
498             {
499                 field.clear();
500                 state = InQuotedField;
501                 break;
502             }
503             state = InNormalField;
504          case InNormalField :
505             if (x == '\n')
506             {
507                 setText(row - startRow, column - startCol, field);
508                 field.clear();
509                 ++row;
510                 column = 1;
511                 if ( row > ( endRow - startRow ) && endRow >= 0 )
512                     break;
513                 state = Start;
514             }
515             else if (delimiterIndex < delimiterLength && x == delimiter.at(delimiterIndex))
516             {
517                 field += x;
518                 delimiterIndex++;
519                 if (field.right(delimiterIndex) == delimiter)
520                 {
521                     setText(row - startRow, column - startCol, field.left(field.count()-delimiterIndex));
522                     field.clear();
523                     if ((ignoreDuplicates == false) || (lastCharDelimiter == false))
524                         column += delimiterLength;
525                     lastCharDelimiter = true;
526                     field.clear();
527                     delimiterIndex = 0;
528                 }
529                 else if (delimiterIndex >= delimiterLength)
530                     delimiterIndex = 0;
531                 state = Start;
532             }
533             else
534             {
535                 field += x;
536             }
537         }
538         if (delimiter.isEmpty() || x != delimiter.at(0))
539           lastCharDelimiter = false;
540     }
541 
542     if ( !field.isEmpty() )
543     {
544       // the last line of the file had not any line end
545       setText(row - startRow, column - startCol, field);
546       ++row;
547       field.clear();
548     }
549     if (row) row--;  // row is higher by 1, so reduce it
550 
551     columnsAdjusted = true;
552     adjustRows( row - startRow );
553     adjustCols( maxColumn - startCol );
554 
555     for (column = 0; column < dialog->m_sheet->columnCount(); ++column)
556     {
557         const QTableWidgetItem* headerItem = dialog->m_sheet->horizontalHeaderItem(column);
558         if (!headerItem || !formatList.contains(headerItem->text())) {
559             dialog->m_sheet->setHorizontalHeaderItem(column, new QTableWidgetItem(i18n("Generic")));
560         }
561     }
562 
563     dialog->m_rowStart->setMinimum(1);
564     dialog->m_colStart->setMinimum(1);
565     dialog->m_rowStart->setMaximum(row);
566     dialog->m_colStart->setMaximum(maxColumn);
567 
568     dialog->m_rowEnd->setMinimum(1);
569     dialog->m_colEnd->setMinimum(1);
570     dialog->m_rowEnd->setMaximum(row);
571     dialog->m_colEnd->setMaximum(maxColumn);
572     dialog->m_rowEnd->setValue(endRow == -1 ? row : endRow);
573     dialog->m_colEnd->setValue(endCol == -1 ? maxColumn : endCol);
574 
575     QApplication::restoreOverrideCursor();
576 }
577 
dataType(int col) const578 KoCsvImportDialog::DataType KoCsvImportDialog::dataType(int col) const
579 {
580     const QString header = d->dialog->m_sheet->model()->headerData(col, Qt::Horizontal).toString();
581 
582     if (header == i18n("Generic"))
583         return Generic;
584     else if (header == i18n("Text"))
585         return Text;
586     else if (header == i18n("Date"))
587         return Date;
588     else if (header == i18n("Currency"))
589         return Currency;
590     else if (header == i18n("None"))
591         return None;
592     return Generic;
593 }
594 
setText(int row,int col,const QString & text)595 void KoCsvImportDialog::Private::setText(int row, int col, const QString& text)
596 {
597     if (row < 1 || col < 1) // skipped by the user
598         return;
599 
600     if ((row > (endRow - startRow) && endRow > 0) || (col > (endCol - startCol) && endCol > 0))
601       return;
602 
603     if (dialog->m_sheet->rowCount() < row)
604     {
605         dialog->m_sheet->setRowCount(row + 5000); /* We add 5000 at a time to limit recalculations */
606         rowsAdjusted = true;
607     }
608 
609     if (dialog->m_sheet->columnCount() < col)
610     {
611         dialog->m_sheet->setColumnCount(col);
612         columnsAdjusted = true;
613     }
614 
615     QTableWidgetItem* item = dialog->m_sheet->item(row - 1, col - 1);
616     if (!item) {
617         item = new QTableWidgetItem();
618         dialog->m_sheet->setItem(row - 1, col - 1, item);
619     }
620     item->setText(text);
621 }
622 
623 /*
624  * Called after the first fillTable() when number of rows are unknown.
625  */
adjustRows(int iRows)626 void KoCsvImportDialog::Private::adjustRows(int iRows)
627 {
628     if (rowsAdjusted)
629     {
630         dialog->m_sheet->setRowCount(iRows);
631         rowsAdjusted = false;
632     }
633 }
634 
adjustCols(int iCols)635 void KoCsvImportDialog::Private::adjustCols(int iCols)
636 {
637     if (columnsAdjusted)
638     {
639         dialog->m_sheet->setColumnCount(iCols);
640         columnsAdjusted = false;
641 
642         if (endCol == -1)
643         {
644           if (iCols > (endCol - startCol))
645             iCols = endCol - startCol;
646 
647           dialog->m_sheet->setColumnCount(iCols);
648         }
649     }
650 }
651 
returnPressed()652 void KoCsvImportDialog::returnPressed()
653 {
654     if (d->dialog->m_radioOther->isChecked())
655         return;
656 
657     d->delimiter = d->dialog->m_delimiterEdit->text();
658     d->fillTable();
659 }
660 
genericDelimiterChanged(const QString &)661 void KoCsvImportDialog::genericDelimiterChanged( const QString & )
662 {
663     d->dialog->m_radioOther->setChecked ( true );
664     delimiterClicked(d->dialog->m_radioOther->group()->id(d->dialog->m_radioOther)); // other
665 }
666 
formatChanged(const QString & newValue)667 void KoCsvImportDialog::formatChanged( const QString& newValue )
668 {
669     QList<QTableWidgetSelectionRange> selectionRanges = d->dialog->m_sheet->selectedRanges();
670     foreach (const QTableWidgetSelectionRange &selectionRange, selectionRanges) {
671         for (int j = selectionRange.leftColumn(); j <= selectionRange.rightColumn(); ++j) {
672              d->dialog->m_sheet->horizontalHeaderItem(j)->setText(newValue);
673         }
674     }
675 }
676 
delimiterClicked(int id)677 void KoCsvImportDialog::delimiterClicked(int id)
678 {
679     const QButtonGroup* group = d->dialog->m_radioComma->group();
680     if (id == group->id(d->dialog->m_radioComma) )
681         d->delimiter = ',';
682     else if (id == group->id(d->dialog->m_radioOther))
683         d->delimiter = d->dialog->m_delimiterEdit->text();
684     else if (id == group->id(d->dialog->m_radioTab))
685         d->delimiter = '\t';
686     else if (id == group->id(d->dialog->m_radioSpace))
687         d->delimiter = ' ';
688     else if (id == group->id(d->dialog->m_radioSemicolon))
689         d->delimiter = ';';
690 
691     debugWidgets << "Delimiter" << d->delimiter << "selected.";
692     d->fillTable();
693 }
694 
textquoteSelected(const QString & mark)695 void KoCsvImportDialog::textquoteSelected(const QString& mark)
696 {
697     if (mark == i18n("None"))
698         d->textQuote = 0;
699     else
700         d->textQuote = mark[0];
701 
702     d->fillTable();
703 }
704 
updateClicked()705 void KoCsvImportDialog::updateClicked()
706 {
707   if ( !d->checkUpdateRange() )
708     return;
709 
710   d->startRow = d->dialog->m_rowStart->value() - 1;
711   d->endRow   = d->dialog->m_rowEnd->value();
712 
713   d->startCol  = d->dialog->m_colStart->value() - 1;
714   d->endCol    = d->dialog->m_colEnd->value();
715 
716   d->fillTable();
717 }
718 
checkUpdateRange()719 bool KoCsvImportDialog::Private::checkUpdateRange()
720 {
721     if ((dialog->m_rowStart->value() > dialog->m_rowEnd->value()) ||
722         (dialog->m_colStart->value() > dialog->m_colEnd->value()))
723     {
724         KMessageBox::error(0, i18n("Please check the ranges you specified. The start value must be lower than the end value."));
725         return false;
726     }
727     return true;
728 }
729 
currentCellChanged(int,int col)730 void KoCsvImportDialog::currentCellChanged(int, int col)
731 {
732     const QString header = d->dialog->m_sheet->model()->headerData(col, Qt::Horizontal).toString();
733     const int index = d->dialog->m_formatComboBox->findText(header);
734     d->dialog->m_formatComboBox->setCurrentIndex(index > -1 ? index : 0);
735 }
736 
ignoreDuplicatesChanged(int)737 void KoCsvImportDialog::ignoreDuplicatesChanged(int)
738 {
739   if (d->dialog->m_ignoreDuplicates->isChecked())
740     d->ignoreDuplicates = true;
741   else
742     d->ignoreDuplicates = false;
743   d->fillTable();
744 }
745 
updateCodec() const746 QTextCodec* KoCsvImportDialog::Private::updateCodec() const
747 {
748     const QString strCodec( KCharsets::charsets()->encodingForName( dialog->comboBoxEncoding->currentText() ) );
749     debugWidgets <<"Encoding:" << strCodec;
750 
751     bool ok = false;
752     QTextCodec* codec = QTextCodec::codecForName( strCodec.toUtf8() );
753 
754     // If QTextCodec has not found a valid encoding, so try with KCharsets.
755     if ( codec )
756     {
757         ok = true;
758     }
759     else
760     {
761         codec = KCharsets::charsets()->codecForName( strCodec, ok );
762     }
763 
764     // Still nothing?
765     if ( !codec || !ok )
766     {
767         // Default: UTF-8
768         warnWidgets << "Cannot find encoding:" << strCodec;
769         // ### TODO: what parent to use?
770         KMessageBox::error( 0, i18n("Cannot find encoding: %1", strCodec ) );
771         return 0;
772     }
773 
774     return codec;
775 }
776 
encodingChanged(const QString &)777 void KoCsvImportDialog::encodingChanged(const QString &)
778 {
779     QTextCodec* codec = d->updateCodec();
780 
781     if ( codec )
782     {
783         d->codec = codec;
784         d->fillTable();
785     }
786 }
787 #include "KoCsvImportDialog.moc"
788