00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <dialogui.h>
00022 #include <csvdialog.h>
00023
00024 #include <qtable.h>
00025 #include <qcheckbox.h>
00026 #include <qcursor.h>
00027 #include <qlineedit.h>
00028 #include <qcombobox.h>
00029 #include <qspinbox.h>
00030 #include <qtextstream.h>
00031 #include <qbuttongroup.h>
00032 #include <qpushbutton.h>
00033 #include <qradiobutton.h>
00034 #include <qtextcodec.h>
00035
00036 #include <kapplication.h>
00037 #include <kconfig.h>
00038 #include <kdebug.h>
00039 #include <klocale.h>
00040 #include <kcombobox.h>
00041 #include <kmessagebox.h>
00042 #include <kcharsets.h>
00043
00044 CSVDialog::CSVDialog(QWidget* parent, QByteArray& fileArray, const QString )
00045 : KDialogBase(parent, 0, true, QString::null, Ok|Cancel, No, true),
00046 m_adjustRows(false),
00047 m_adjustCols(false),
00048 m_startRow(0),
00049 m_startCol(0),
00050 m_endRow(-1),
00051 m_endCol(-1),
00052 m_textquote('"'),
00053 m_delimiter(","),
00054 m_ignoreDups(false),
00055 m_fileArray(fileArray),
00056 m_dialog(new DialogUI(this)),
00057 m_codec( QTextCodec::codecForName( "UTF-8" ) )
00058 {
00059 setCaption( i18n( "Import" ) );
00060 kapp->restoreOverrideCursor();
00061
00062 QStringList encodings;
00063 encodings << i18n( "Descriptive encoding name", "Recommended ( %1 )" ).arg( "UTF-8" );
00064 encodings << i18n( "Descriptive encoding name", "Locale ( %1 )" ).arg( QTextCodec::codecForLocale()->name() );
00065 encodings += KGlobal::charsets()->descriptiveEncodingNames();
00066 // Add a few non-standard encodings, which might be useful for text files
00067 const QString description(i18n("Descriptive encoding name","Other ( %1 )"));
00068 encodings << description.arg("Apple Roman"); // Apple
00069 encodings << description.arg("IBM 850") << description.arg("IBM 866"); // MS DOS
00070 encodings << description.arg("CP 1258"); // Windows
00071 m_dialog->comboBoxEncoding->insertStringList(encodings);
00072
00073 m_formatList << i18n( "Text" );
00074 m_formatList << i18n( "Number" );
00075 m_formatList << i18n( "Currency" );
00076 m_formatList << i18n( "Date" );
00077 m_formatList << i18n( "Decimal Comma Number" );
00078 m_formatList << i18n( "Decimal Point Number" );
00079 m_dialog->m_formatComboBox->insertStringList( m_formatList );
00080
00081 m_dialog->m_sheet->setReadOnly( true );
00082
00083 loadSettings();
00084
00085 fillTable();
00086
00087 //resize(sizeHint());
00088 resize( 600, 400 ); // Try to show as much as possible of the table view
00089 setMainWidget(m_dialog);
00090
00091 m_dialog->m_sheet->setSelectionMode( QTable::Multi );
00092
00093 connect(m_dialog->m_formatComboBox, SIGNAL(activated( const QString& )),
00094 this, SLOT(formatChanged( const QString& )));
00095 connect(m_dialog->m_delimiterBox, SIGNAL(clicked(int)),
00096 this, SLOT(delimiterClicked(int)));
00097 connect(m_dialog->m_delimiterEdit, SIGNAL(returnPressed()),
00098 this, SLOT(returnPressed()));
00099 connect(m_dialog->m_delimiterEdit, SIGNAL(textChanged ( const QString & )),
00100 this, SLOT(formatChanged ( const QString & ) ));
00101 connect(m_dialog->m_comboQuote, SIGNAL(activated(const QString &)),
00102 this, SLOT(textquoteSelected(const QString &)));
00103 connect(m_dialog->m_sheet, SIGNAL(currentChanged(int, int)),
00104 this, SLOT(currentCellChanged(int, int)));
00105 connect(m_dialog->m_ignoreDuplicates, SIGNAL(stateChanged(int)),
00106 this, SLOT(ignoreDuplicatesChanged(int)));
00107 connect(m_dialog->m_updateButton, SIGNAL(clicked()),
00108 this, SLOT(updateClicked()));
00109 connect(m_dialog->comboBoxEncoding, SIGNAL(textChanged ( const QString & )),
00110 this, SLOT(encodingChanged ( const QString & ) ));
00111 }
00112
00113 CSVDialog::~CSVDialog()
00114 {
00115 saveSettings();
00116 kapp->setOverrideCursor(Qt::waitCursor);
00117 }
00118
00119 void CSVDialog::loadSettings()
00120 {
00121 KConfig *config = kapp->config();
00122 config->setGroup("CSVDialog Settings");
00123 m_textquote = config->readEntry("textquote", "\"")[0];
00124 m_delimiter = config->readEntry("delimiter", ",");
00125 m_ignoreDups = config->readBoolEntry("ignoreDups", false);
00126 const QString codecText = config->readEntry("codec", "");
00127
00128
00129 if (!codecText.isEmpty()) {
00130 m_dialog->comboBoxEncoding->setCurrentText(codecText);
00131 m_codec = getCodec();
00132 }
00133 if (m_delimiter == ",") m_dialog->m_radioComma->setChecked(true);
00134 else if (m_delimiter == "\t") m_dialog->m_radioTab->setChecked(true);
00135 else if (m_delimiter == " ") m_dialog->m_radioSpace->setChecked(true);
00136 else if (m_delimiter == ";") m_dialog->m_radioSemicolon->setChecked(true);
00137 else {
00138 m_dialog->m_radioOther->setChecked(true);
00139 m_dialog->m_delimiterEdit->setText(m_delimiter);
00140 }
00141 m_dialog->m_ignoreDuplicates->setChecked(m_ignoreDups);
00142 m_dialog->m_comboQuote->setCurrentItem(m_textquote == '\'' ? 1
00143 : m_textquote == '"' ? 0 : 2);
00144 }
00145
00146 void CSVDialog::saveSettings()
00147 {
00148 KConfig *config = kapp->config();
00149 config->setGroup("CSVDialog Settings");
00150 QString q = m_textquote;
00151 config->writeEntry("textquote", q);
00152 config->writeEntry("delimiter", m_delimiter);
00153 config->writeEntry("ignoreDups", m_ignoreDups);
00154 config->writeEntry("codec", m_dialog->comboBoxEncoding->currentText());
00155 config->sync();
00156 }
00157
00158 void CSVDialog::fillTable( )
00159 {
00160 int row, column;
00161 bool lastCharDelimiter = false;
00162 enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
00163 S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
00164
00165 QChar x;
00166 QString field;
00167
00168 kapp->setOverrideCursor(Qt::waitCursor);
00169
00170 for (row = 0; row < m_dialog->m_sheet->numRows(); ++row)
00171 for (column = 0; column < m_dialog->m_sheet->numCols(); ++column)
00172 m_dialog->m_sheet->clearCell(row, column);
00173
00174 int maxColumn = 1;
00175 row = column = 1;
00176 QTextStream inputStream(m_fileArray, IO_ReadOnly);
00177 kdDebug(30501) << "Encoding: " << m_codec->name() << endl;
00178 inputStream.setCodec( m_codec );
00179
00180 bool lastCharWasCr = false;
00181 while (!inputStream.atEnd())
00182 {
00183 inputStream >> x;
00184
00185
00186 if ( x == '\r' )
00187 {
00188
00189 lastCharWasCr = true;
00190 x = '\n';
00191 }
00192 else if ( x == '\n' && lastCharWasCr )
00193 {
00194
00195 lastCharWasCr = false;
00196 continue;
00197 }
00198 else if ( x == QChar( 0xc ) )
00199 {
00200
00201 lastCharWasCr = false;
00202 continue;
00203 }
00204 else
00205 {
00206 lastCharWasCr = false;
00207 }
00208
00209 if ( column > maxColumn )
00210 maxColumn = column;
00211
00212 switch (state)
00213 {
00214 case S_START :
00215 if (x == m_textquote)
00216 {
00217 state = S_QUOTED_FIELD;
00218 }
00219 else if (x == m_delimiter)
00220 {
00221 if ((m_ignoreDups == false) || (lastCharDelimiter == false))
00222 ++column;
00223 lastCharDelimiter = true;
00224 }
00225 else if (x == '\n')
00226 {
00227 ++row;
00228 column = 1;
00229 if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 )
00230 break;
00231 }
00232 else
00233 {
00234 field += x;
00235 state = S_MAYBE_NORMAL_FIELD;
00236 }
00237 break;
00238 case S_QUOTED_FIELD :
00239 if (x == m_textquote)
00240 {
00241 state = S_MAYBE_END_OF_QUOTED_FIELD;
00242 }
00243 else
00244 {
00245 field += x;
00246 }
00247 break;
00248 case S_MAYBE_END_OF_QUOTED_FIELD :
00249 if (x == m_textquote)
00250 {
00251 field += x;
00252 state = S_QUOTED_FIELD;
00253 }
00254 else if (x == m_delimiter || x == '\n')
00255 {
00256 setText(row - m_startRow, column - m_startCol, field);
00257 field = QString::null;
00258 if (x == '\n')
00259 {
00260 ++row;
00261 column = 1;
00262 if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 )
00263 break;
00264 }
00265 else
00266 {
00267 if ((m_ignoreDups == false) || (lastCharDelimiter == false))
00268 ++column;
00269 lastCharDelimiter = true;
00270 }
00271 state = S_START;
00272 }
00273 else
00274 {
00275 state = S_END_OF_QUOTED_FIELD;
00276 }
00277 break;
00278 case S_END_OF_QUOTED_FIELD :
00279 if (x == m_delimiter || x == '\n')
00280 {
00281 setText(row - m_startRow, column - m_startCol, field);
00282 field = QString::null;
00283 if (x == '\n')
00284 {
00285 ++row;
00286 column = 1;
00287 if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 )
00288 break;
00289 }
00290 else
00291 {
00292 if ((m_ignoreDups == false) || (lastCharDelimiter == false))
00293 ++column;
00294 lastCharDelimiter = true;
00295 }
00296 state = S_START;
00297 }
00298 break;
00299 case S_MAYBE_NORMAL_FIELD :
00300 if (x == m_textquote)
00301 {
00302 field = QString::null;
00303 state = S_QUOTED_FIELD;
00304 break;
00305 }
00306 case S_NORMAL_FIELD :
00307 if (x == m_delimiter || x == '\n')
00308 {
00309 setText(row - m_startRow, column - m_startCol, field);
00310 field = QString::null;
00311 if (x == '\n')
00312 {
00313 ++row;
00314 column = 1;
00315 if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 )
00316 break;
00317 }
00318 else
00319 {
00320 if ((m_ignoreDups == false) || (lastCharDelimiter == false))
00321 ++column;
00322 lastCharDelimiter = true;
00323 }
00324 state = S_START;
00325 }
00326 else
00327 {
00328 field += x;
00329 }
00330 }
00331 if (x != m_delimiter)
00332 lastCharDelimiter = false;
00333 }
00334
00335 if ( !field.isEmpty() )
00336 {
00337
00338 setText(row - m_startRow, column - m_startCol, field);
00339 ++row;
00340 field = QString::null;
00341 }
00342
00343 m_adjustCols = true;
00344 adjustRows( row - m_startRow );
00345 adjustCols( maxColumn - m_startCol );
00346 m_dialog->m_colEnd->setMaxValue( maxColumn );
00347 if ( m_endCol == -1 )
00348 m_dialog->m_colEnd->setValue( maxColumn );
00349
00350
00351 for (column = 0; column < m_dialog->m_sheet->numCols(); ++column)
00352 {
00353 const QString header = m_dialog->m_sheet->horizontalHeader()->label(column);
00354 if ( m_formatList.find( header ) == m_formatList.end() )
00355 m_dialog->m_sheet->horizontalHeader()->setLabel(column, i18n("Text"));
00356
00357 m_dialog->m_sheet->adjustColumn(column);
00358 }
00359 fillComboBox();
00360
00361 kapp->restoreOverrideCursor();
00362 }
00363
00364 void CSVDialog::fillComboBox()
00365 {
00366 if ( m_endRow == -1 )
00367 m_dialog->m_rowEnd->setValue( m_dialog->m_sheet->numRows() );
00368 else
00369 m_dialog->m_rowEnd->setValue( m_endRow );
00370
00371 if ( m_endCol == -1 )
00372 m_dialog->m_colEnd->setValue( m_dialog->m_sheet->numCols() );
00373 else
00374 m_dialog->m_colEnd->setValue( m_endCol );
00375
00376 m_dialog->m_rowEnd->setMinValue( 1 );
00377 m_dialog->m_colEnd->setMinValue( 1 );
00378 m_dialog->m_rowEnd->setMaxValue( m_dialog->m_sheet->numRows() );
00379 m_dialog->m_colEnd->setMaxValue( m_dialog->m_sheet->numCols() );
00380
00381 m_dialog->m_rowStart->setMinValue( 1 );
00382 m_dialog->m_colStart->setMinValue( 1 );
00383 m_dialog->m_rowStart->setMaxValue( m_dialog->m_sheet->numRows() );
00384 m_dialog->m_colStart->setMaxValue( m_dialog->m_sheet->numCols() );
00385 }
00386
00387 int CSVDialog::getRows()
00388 {
00389 int rows = m_dialog->m_sheet->numRows();
00390 if ( m_endRow >= 0 )
00391 {
00392 if ( rows > ( m_startRow + m_endRow ) )
00393 rows = m_startRow + m_endRow;
00394 }
00395
00396 return rows;
00397 }
00398
00399 int CSVDialog::getCols()
00400 {
00401 int cols = m_dialog->m_sheet->numCols();
00402 if ( m_endCol >= 0 )
00403 {
00404 if ( cols > ( m_startCol + m_endCol ) )
00405 cols = m_startCol + m_endCol;
00406 }
00407
00408 return cols;
00409 }
00410
00411 int CSVDialog::getHeader(int col)
00412 {
00413 QString header = m_dialog->m_sheet->horizontalHeader()->label(col);
00414
00415 if (header == i18n("Text"))
00416 return TEXT;
00417 else if (header == i18n("Number"))
00418 return NUMBER;
00419 else if (header == i18n("Currency"))
00420 return CURRENCY;
00421 else if ( header == i18n( "Date" ) )
00422 return DATE;
00423 else if ( header == i18n( "Decimal Comma Number" ) )
00424 return COMMANUMBER;
00425 else if ( header == i18n( "Decimal Point Number" ) )
00426 return POINTNUMBER;
00427 else
00428 return TEXT;
00429 }
00430
00431 QString CSVDialog::getText(int row, int col)
00432 {
00433 return m_dialog->m_sheet->text( row, col );
00434 }
00435
00436 void CSVDialog::setText(int row, int col, const QString& text)
00437 {
00438 if ( row < 1 || col < 1 )
00439 return;
00440
00441 if ( ( row > ( m_endRow - m_startRow ) && m_endRow > 0 ) || ( col > ( m_endCol - m_startCol ) && m_endCol > 0 ) )
00442 return;
00443
00444 if ( m_dialog->m_sheet->numRows() < row )
00445 {
00446 m_dialog->m_sheet->setNumRows( row + 5000 );
00447 m_adjustRows = true;
00448 }
00449
00450 if ( m_dialog->m_sheet->numCols() < col )
00451 {
00452 m_dialog->m_sheet->setNumCols( col );
00453 m_adjustCols = true;
00454 }
00455
00456 m_dialog->m_sheet->setText( row - 1, col - 1, text );
00457 }
00458
00459
00460
00461
00462 void CSVDialog::adjustRows(int iRows)
00463 {
00464 if (m_adjustRows)
00465 {
00466 m_dialog->m_sheet->setNumRows( iRows );
00467 m_adjustRows = false;
00468 }
00469 }
00470
00471 void CSVDialog::adjustCols(int iCols)
00472 {
00473 if (m_adjustCols)
00474 {
00475 m_dialog->m_sheet->setNumCols( iCols );
00476 m_adjustCols = false;
00477
00478 if ( m_endCol == -1 )
00479 {
00480 if ( iCols > ( m_endCol - m_startCol ) )
00481 iCols = m_endCol - m_startCol;
00482
00483 m_dialog->m_sheet->setNumCols( iCols );
00484 }
00485 }
00486 }
00487
00488 void CSVDialog::returnPressed()
00489 {
00490 if (m_dialog->m_delimiterBox->id(m_dialog->m_delimiterBox->selected()) != 4)
00491 return;
00492
00493 m_delimiter = m_dialog->m_delimiterEdit->text();
00494 fillTable();
00495 }
00496
00497 void CSVDialog::textChanged ( const QString & )
00498 {
00499 m_dialog->m_radioOther->setChecked ( true );
00500 delimiterClicked(4);
00501 }
00502
00503 void CSVDialog::formatChanged( const QString& newValue )
00504 {
00505
00506 for ( int i = 0; i < m_dialog->m_sheet->numSelections(); ++i )
00507 {
00508 QTableSelection select ( m_dialog->m_sheet->selection( i ) );
00509 for ( int j = select.leftCol(); j <= select.rightCol() ; ++j )
00510 {
00511 m_dialog->m_sheet->horizontalHeader()->setLabel( j, newValue );
00512
00513 }
00514 }
00515 }
00516
00517 void CSVDialog::delimiterClicked(int id)
00518 {
00519 switch (id)
00520 {
00521 case 0:
00522 m_delimiter = ",";
00523 break;
00524 case 4:
00525 m_delimiter = m_dialog->m_delimiterEdit->text();
00526 break;
00527 case 2:
00528 m_delimiter = "\t";
00529 break;
00530 case 3:
00531 m_delimiter = " ";
00532 break;
00533 case 1:
00534 m_delimiter = ";";
00535 break;
00536 }
00537
00538 fillTable();
00539 }
00540
00541 void CSVDialog::textquoteSelected(const QString& mark)
00542 {
00543 if (mark == i18n("None"))
00544 m_textquote = 0;
00545 else
00546 m_textquote = mark[0];
00547
00548 fillTable();
00549 }
00550
00551 void CSVDialog::updateClicked()
00552 {
00553 if ( !checkUpdateRange() )
00554 return;
00555
00556 m_startRow = m_dialog->m_rowStart->value() - 1;
00557 m_endRow = m_dialog->m_rowEnd->value();
00558
00559 m_startCol = m_dialog->m_colStart->value() - 1;
00560 m_endCol = m_dialog->m_colEnd->value();
00561
00562 fillTable();
00563 }
00564
00565 bool CSVDialog::checkUpdateRange()
00566 {
00567 if ( ( m_dialog->m_rowStart->value() > m_dialog->m_rowEnd->value() )
00568 || ( m_dialog->m_colStart->value() > m_dialog->m_colEnd->value() ) )
00569 {
00570 KMessageBox::error( this, i18n( "Please check the ranges you specified. The start value must be lower than the end value." ) );
00571 return false;
00572 }
00573
00574 return true;
00575 }
00576
00577 void CSVDialog::currentCellChanged(int, int col)
00578 {
00579 const QString header = m_dialog->m_sheet->horizontalHeader()->label(col);
00580 m_dialog->m_formatComboBox->setCurrentText( header );
00581 }
00582
00583 void CSVDialog::ignoreDuplicatesChanged(int)
00584 {
00585 if (m_dialog->m_ignoreDuplicates->isChecked())
00586 m_ignoreDups = true;
00587 else
00588 m_ignoreDups = false;
00589 fillTable();
00590 }
00591
00592 QTextCodec* CSVDialog::getCodec(void) const
00593 {
00594 const QString strCodec( KGlobal::charsets()->encodingForName( m_dialog->comboBoxEncoding->currentText() ) );
00595 kdDebug(30502) << "Encoding: " << strCodec << endl;
00596
00597 bool ok = false;
00598 QTextCodec* codec = QTextCodec::codecForName( strCodec.utf8() );
00599
00600
00601 if ( codec )
00602 {
00603 ok = true;
00604 }
00605 else
00606 {
00607 codec = KGlobal::charsets()->codecForName( strCodec, ok );
00608 }
00609
00610
00611 if ( !codec || !ok )
00612 {
00613
00614 kdWarning(30502) << "Cannot find encoding:" << strCodec << endl;
00615
00616 KMessageBox::error( 0, i18n("Cannot find encoding: %1").arg( strCodec ) );
00617 return 0;
00618 }
00619
00620 return codec;
00621 }
00622
00623 void CSVDialog::encodingChanged ( const QString & )
00624 {
00625 QTextCodec* codec = getCodec();
00626
00627 if ( codec )
00628 {
00629 m_codec = codec;
00630 fillTable();
00631 }
00632 }
00633
00634
00635 #include <csvdialog.moc>