/* This file is part of the KDE project Copyright (C) 1999 David Faure Copyright (C) 2004 Nicolas GOUTTE This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CSVDialog::CSVDialog(TQWidget* parent, TQByteArray& fileArray, const TQString /*seperator*/) : KDialogBase(parent, 0, true, TQString(), Ok|Cancel, No, true), m_adjustRows(false), m_adjustCols(false), m_startRow(0), m_startCol(0), m_endRow(-1), m_endCol(-1), m_textquote('"'), m_delimiter(","), m_ignoreDups(false), m_fileArray(fileArray), m_dialog(new DialogUI(this)), m_codec( TQTextCodec::codecForName( "UTF-8" ) ) { setCaption( i18n( "Import" ) ); kapp->restoreOverrideCursor(); TQStringList encodings; encodings << i18n( "Descriptive encoding name", "Recommended ( %1 )" ).arg( "UTF-8" ); encodings << i18n( "Descriptive encoding name", "Locale ( %1 )" ).arg( TQTextCodec::codecForLocale()->name() ); encodings += TDEGlobal::charsets()->descriptiveEncodingNames(); // Add a few non-standard encodings, which might be useful for text files const TQString description(i18n("Descriptive encoding name","Other ( %1 )")); encodings << description.arg("Apple Roman"); // Apple encodings << description.arg("IBM 850") << description.arg("IBM 866"); // MS DOS encodings << description.arg("CP 1258"); // Windows m_dialog->comboBoxEncoding->insertStringList(encodings); m_formatList << i18n( "Text" ); m_formatList << i18n( "Number" ); m_formatList << i18n( "Currency" ); m_formatList << i18n( "Date" ); m_formatList << i18n( "Decimal Comma Number" ); m_formatList << i18n( "Decimal Point Number" ); m_dialog->m_formatComboBox->insertStringList( m_formatList ); m_dialog->m_sheet->setReadOnly( true ); loadSettings(); fillTable(); //resize(sizeHint()); resize( 600, 400 ); // Try to show as much as possible of the table view setMainWidget(m_dialog); m_dialog->m_sheet->setSelectionMode( TQTable::Multi ); connect(m_dialog->m_formatComboBox, TQT_SIGNAL(activated( const TQString& )), this, TQT_SLOT(formatChanged( const TQString& ))); connect(m_dialog->m_delimiterBox, TQT_SIGNAL(clicked(int)), this, TQT_SLOT(delimiterClicked(int))); connect(m_dialog->m_delimiterEdit, TQT_SIGNAL(returnPressed()), this, TQT_SLOT(returnPressed())); connect(m_dialog->m_delimiterEdit, TQT_SIGNAL(textChanged ( const TQString & )), this, TQT_SLOT(formatChanged ( const TQString & ) )); connect(m_dialog->m_comboQuote, TQT_SIGNAL(activated(const TQString &)), this, TQT_SLOT(textquoteSelected(const TQString &))); connect(m_dialog->m_sheet, TQT_SIGNAL(currentChanged(int, int)), this, TQT_SLOT(currentCellChanged(int, int))); connect(m_dialog->m_ignoreDuplicates, TQT_SIGNAL(stateChanged(int)), this, TQT_SLOT(ignoreDuplicatesChanged(int))); connect(m_dialog->m_updateButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(updateClicked())); connect(m_dialog->comboBoxEncoding, TQT_SIGNAL(textChanged ( const TQString & )), this, TQT_SLOT(encodingChanged ( const TQString & ) )); } CSVDialog::~CSVDialog() { saveSettings(); kapp->setOverrideCursor(TQt::waitCursor); } void CSVDialog::loadSettings() { TDEConfig *config = kapp->config(); config->setGroup("CSVDialog Settings"); m_textquote = config->readEntry("textquote", "\"")[0]; m_delimiter = config->readEntry("delimiter", ","); m_ignoreDups = config->readBoolEntry("ignoreDups", false); const TQString codecText = config->readEntry("codec", ""); // update widgets if (!codecText.isEmpty()) { m_dialog->comboBoxEncoding->setCurrentText(codecText); m_codec = getCodec(); } if (m_delimiter == ",") m_dialog->m_radioComma->setChecked(true); else if (m_delimiter == "\t") m_dialog->m_radioTab->setChecked(true); else if (m_delimiter == " ") m_dialog->m_radioSpace->setChecked(true); else if (m_delimiter == ";") m_dialog->m_radioSemicolon->setChecked(true); else { m_dialog->m_radioOther->setChecked(true); m_dialog->m_delimiterEdit->setText(m_delimiter); } m_dialog->m_ignoreDuplicates->setChecked(m_ignoreDups); m_dialog->m_comboQuote->setCurrentItem(m_textquote == '\'' ? 1 : m_textquote == '"' ? 0 : 2); } void CSVDialog::saveSettings() { TDEConfig *config = kapp->config(); config->setGroup("CSVDialog Settings"); TQString q = m_textquote; config->writeEntry("textquote", q); config->writeEntry("delimiter", m_delimiter); config->writeEntry("ignoreDups", m_ignoreDups); config->writeEntry("codec", m_dialog->comboBoxEncoding->currentText()); config->sync(); } void CSVDialog::fillTable( ) { int row, column; bool lastCharDelimiter = false; enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD, S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START; TQChar x; TQString field; kapp->setOverrideCursor(TQt::waitCursor); for (row = 0; row < m_dialog->m_sheet->numRows(); ++row) for (column = 0; column < m_dialog->m_sheet->numCols(); ++column) m_dialog->m_sheet->clearCell(row, column); int maxColumn = 1; row = column = 1; TQTextStream inputStream(m_fileArray, IO_ReadOnly); kdDebug(30501) << "Encoding: " << m_codec->name() << endl; inputStream.setCodec( m_codec ); bool lastCharWasCr = false; // Last character was a Carriage Return while (!inputStream.atEnd()) { inputStream >> x; // read one char // ### TODO: we should perhaps skip all other control characters if ( x == '\r' ) { // We have a Carriage Return, assume that its role is the one of a LineFeed lastCharWasCr = true; x = '\n'; // Replace by Line Feed } else if ( x == '\n' && lastCharWasCr ) { // The end of line was already handled by the Carriage Return, so do nothing for this character lastCharWasCr = false; continue; } else if ( x == TQChar( 0xc ) ) { // We have a FormFeed, skip it lastCharWasCr = false; continue; } else { lastCharWasCr = false; } if ( column > maxColumn ) maxColumn = column; switch (state) { case S_START : if (x == m_textquote) { state = S_QUOTED_FIELD; } else if (x == m_delimiter) { if ((m_ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } else if (x == '\n') { ++row; column = 1; if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 ) break; } else { field += x; state = S_MAYBE_NORMAL_FIELD; } break; case S_QUOTED_FIELD : if (x == m_textquote) { state = S_MAYBE_END_OF_QUOTED_FIELD; } else { field += x; } break; case S_MAYBE_END_OF_QUOTED_FIELD : if (x == m_textquote) { field += x; state = S_QUOTED_FIELD; } else if (x == m_delimiter || x == '\n') { setText(row - m_startRow, column - m_startCol, field); field = TQString(); if (x == '\n') { ++row; column = 1; if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 ) break; } else { if ((m_ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } state = S_START; } else { state = S_END_OF_QUOTED_FIELD; } break; case S_END_OF_QUOTED_FIELD : if (x == m_delimiter || x == '\n') { setText(row - m_startRow, column - m_startCol, field); field = TQString(); if (x == '\n') { ++row; column = 1; if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 ) break; } else { if ((m_ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } state = S_START; } break; case S_MAYBE_NORMAL_FIELD : if (x == m_textquote) { field = TQString(); state = S_QUOTED_FIELD; break; } case S_NORMAL_FIELD : if (x == m_delimiter || x == '\n') { setText(row - m_startRow, column - m_startCol, field); field = TQString(); if (x == '\n') { ++row; column = 1; if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 ) break; } else { if ((m_ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } state = S_START; } else { field += x; } } if (x != m_delimiter) lastCharDelimiter = false; } if ( !field.isEmpty() ) { // the last line of the file had not any line end setText(row - m_startRow, column - m_startCol, field); ++row; field = TQString(); } m_adjustCols = true; adjustRows( row - m_startRow ); adjustCols( maxColumn - m_startCol ); m_dialog->m_colEnd->setMaxValue( maxColumn ); if ( m_endCol == -1 ) m_dialog->m_colEnd->setValue( maxColumn ); for (column = 0; column < m_dialog->m_sheet->numCols(); ++column) { const TQString header = m_dialog->m_sheet->horizontalHeader()->label(column); if ( m_formatList.find( header ) == m_formatList.end() ) m_dialog->m_sheet->horizontalHeader()->setLabel(column, i18n("Text")); m_dialog->m_sheet->adjustColumn(column); } fillComboBox(); kapp->restoreOverrideCursor(); } void CSVDialog::fillComboBox() { if ( m_endRow == -1 ) m_dialog->m_rowEnd->setValue( m_dialog->m_sheet->numRows() ); else m_dialog->m_rowEnd->setValue( m_endRow ); if ( m_endCol == -1 ) m_dialog->m_colEnd->setValue( m_dialog->m_sheet->numCols() ); else m_dialog->m_colEnd->setValue( m_endCol ); m_dialog->m_rowEnd->setMinValue( 1 ); m_dialog->m_colEnd->setMinValue( 1 ); m_dialog->m_rowEnd->setMaxValue( m_dialog->m_sheet->numRows() ); m_dialog->m_colEnd->setMaxValue( m_dialog->m_sheet->numCols() ); m_dialog->m_rowStart->setMinValue( 1 ); m_dialog->m_colStart->setMinValue( 1 ); m_dialog->m_rowStart->setMaxValue( m_dialog->m_sheet->numRows() ); m_dialog->m_colStart->setMaxValue( m_dialog->m_sheet->numCols() ); } int CSVDialog::getRows() { int rows = m_dialog->m_sheet->numRows(); if ( m_endRow >= 0 ) { if ( rows > ( m_startRow + m_endRow ) ) rows = m_startRow + m_endRow; } return rows; } int CSVDialog::getCols() { int cols = m_dialog->m_sheet->numCols(); if ( m_endCol >= 0 ) { if ( cols > ( m_startCol + m_endCol ) ) cols = m_startCol + m_endCol; } return cols; } int CSVDialog::getHeader(int col) { TQString header = m_dialog->m_sheet->horizontalHeader()->label(col); if (header == i18n("Text")) return TEXT; else if (header == i18n("Number")) return NUMBER; else if (header == i18n("Currency")) return CURRENCY; else if ( header == i18n( "Date" ) ) return DATE; else if ( header == i18n( "Decimal Comma Number" ) ) return COMMANUMBER; else if ( header == i18n( "Decimal Point Number" ) ) return POINTNUMBER; else return TEXT; // Should not happen } TQString CSVDialog::getText(int row, int col) { return m_dialog->m_sheet->text( row, col ); } void CSVDialog::setText(int row, int col, const TQString& text) { if ( row < 1 || col < 1 ) // skipped by the user return; if ( ( row > ( m_endRow - m_startRow ) && m_endRow > 0 ) || ( col > ( m_endCol - m_startCol ) && m_endCol > 0 ) ) return; if ( m_dialog->m_sheet->numRows() < row ) { m_dialog->m_sheet->setNumRows( row + 5000 ); /* We add 5000 at a time to limit recalculations */ m_adjustRows = true; } if ( m_dialog->m_sheet->numCols() < col ) { m_dialog->m_sheet->setNumCols( col ); m_adjustCols = true; } m_dialog->m_sheet->setText( row - 1, col - 1, text ); } /* * Called after the first fillTable() when number of rows are unknown. */ void CSVDialog::adjustRows(int iRows) { if (m_adjustRows) { m_dialog->m_sheet->setNumRows( iRows ); m_adjustRows = false; } } void CSVDialog::adjustCols(int iCols) { if (m_adjustCols) { m_dialog->m_sheet->setNumCols( iCols ); m_adjustCols = false; if ( m_endCol == -1 ) { if ( iCols > ( m_endCol - m_startCol ) ) iCols = m_endCol - m_startCol; m_dialog->m_sheet->setNumCols( iCols ); } } } void CSVDialog::returnPressed() { if (m_dialog->m_delimiterBox->id(m_dialog->m_delimiterBox->selected()) != 4) return; m_delimiter = m_dialog->m_delimiterEdit->text(); fillTable(); } void CSVDialog::textChanged ( const TQString & ) { m_dialog->m_radioOther->setChecked ( true ); delimiterClicked(4); // other } void CSVDialog::formatChanged( const TQString& newValue ) { //kdDebug(30501) << "CSVDialog::formatChanged:" << newValue << endl; for ( int i = 0; i < m_dialog->m_sheet->numSelections(); ++i ) { TQTableSelection select ( m_dialog->m_sheet->selection( i ) ); for ( int j = select.leftCol(); j <= select.rightCol() ; ++j ) { m_dialog->m_sheet->horizontalHeader()->setLabel( j, newValue ); } } } void CSVDialog::delimiterClicked(int id) { switch (id) { case 0: // comma m_delimiter = ","; break; case 4: // other m_delimiter = m_dialog->m_delimiterEdit->text(); break; case 2: // tab m_delimiter = "\t"; break; case 3: // space m_delimiter = " "; break; case 1: // semicolon m_delimiter = ";"; break; } fillTable(); } void CSVDialog::textquoteSelected(const TQString& mark) { if (mark == i18n("None")) m_textquote = 0; else m_textquote = mark[0]; fillTable(); } void CSVDialog::updateClicked() { if ( !checkUpdateRange() ) return; m_startRow = m_dialog->m_rowStart->value() - 1; m_endRow = m_dialog->m_rowEnd->value(); m_startCol = m_dialog->m_colStart->value() - 1; m_endCol = m_dialog->m_colEnd->value(); fillTable(); } bool CSVDialog::checkUpdateRange() { if ( ( m_dialog->m_rowStart->value() > m_dialog->m_rowEnd->value() ) || ( m_dialog->m_colStart->value() > m_dialog->m_colEnd->value() ) ) { KMessageBox::error( this, i18n( "Please check the ranges you specified. The start value must be lower than the end value." ) ); return false; } return true; } void CSVDialog::currentCellChanged(int, int col) { const TQString header = m_dialog->m_sheet->horizontalHeader()->label(col); m_dialog->m_formatComboBox->setCurrentText( header ); } void CSVDialog::ignoreDuplicatesChanged(int) { if (m_dialog->m_ignoreDuplicates->isChecked()) m_ignoreDups = true; else m_ignoreDups = false; fillTable(); } TQTextCodec* CSVDialog::getCodec(void) const { const TQString strCodec( TDEGlobal::charsets()->encodingForName( m_dialog->comboBoxEncoding->currentText() ) ); kdDebug(30502) << "Encoding: " << strCodec << endl; bool ok = false; TQTextCodec* codec = TQTextCodec::codecForName( strCodec.utf8() ); // If TQTextCodec has not found a valid encoding, so try with KCharsets. if ( codec ) { ok = true; } else { codec = TDEGlobal::charsets()->codecForName( strCodec, ok ); } // Still nothing? if ( !codec || !ok ) { // Default: UTF-8 kdWarning(30502) << "Cannot find encoding:" << strCodec << endl; // ### TODO: what parent to use? KMessageBox::error( 0, i18n("Cannot find encoding: %1").arg( strCodec ) ); return 0; } return codec; } void CSVDialog::encodingChanged ( const TQString & ) { TQTextCodec* codec = getCodec(); if ( codec ) { m_codec = codec; fillTable(); } } #include