diff options
Diffstat (limited to 'filters/kspread/csv/csvexport.cpp')
-rw-r--r-- | filters/kspread/csv/csvexport.cpp | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/filters/kspread/csv/csvexport.cpp b/filters/kspread/csv/csvexport.cpp new file mode 100644 index 000000000..ceeb8b5ec --- /dev/null +++ b/filters/kspread/csv/csvexport.cpp @@ -0,0 +1,382 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <faure@kde.org> + Copyright (C) 2004 Nicolas GOUTTE <goutte@kde.org> + + 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 <csvexport.h> + +#include <tqfile.h> +#include <tqtextcodec.h> + +#include <kdebug.h> +#include <tdemessagebox.h> +#include <kgenericfactory.h> +#include <KoFilterChain.h> +#include <KoFilterManager.h> + +#include <kspread_map.h> +#include <kspread_sheet.h> +#include <kspread_doc.h> +#include <kspread_view.h> +#include <selection.h> + +#include <csvexportdialog.h> + +using namespace KSpread; + +typedef KGenericFactory<CSVExport, KoFilter> CSVExportFactory; +K_EXPORT_COMPONENT_FACTORY( libcsvexport, CSVExportFactory( "kofficefilters" ) ) + +class Cell +{ + public: + int row, col; + TQString text; + + bool operator < ( const Cell & c ) const + { + return row < c.row || ( row == c.row && col < c.col ); + } + bool operator == ( const Cell & c ) const + { + return row == c.row && col == c.col; + } +}; + + +CSVExport::CSVExport( KoFilter *, const char *, const TQStringList & ) + : KoFilter(), m_eol("\n") +{ +} + +TQString CSVExport::exportCSVCell( Sheet const * const sheet, int col, int row, TQChar const & textQuote, TQChar csvDelimiter ) +{ + // This function, given a cell, returns a string corresponding to its export in CSV format + // It proceeds by: + // - getting the value of the cell, if any + // - protecting quote characters within cells, if any + // - enclosing the cell in quotes if the cell is non empty + + KSpread::Cell const * const cell = sheet->cellAt( col, row ); + TQString text; + + if ( !cell->isDefault() && !cell->isEmpty() ) + { + if ( cell->isFormula() ) + text = cell->strOutText(); + else if ( !cell->link().isEmpty() ) + text = cell->text(); // untested + else if( cell->isTime() ) + text = cell->value().asTime().toString("hh:mm:ss"); + else if( cell->isDate() ) + text = cell->value().asDate().toString("yyyy-MM-dd"); + else + text = cell->strOutText(); + } + + // quote only when needed (try to mimic excel) + bool quote = false; + if ( !text.isEmpty() ) + { + if ( text.find( textQuote ) != -1 ) + { + TQString doubleTextQuote(textQuote); + doubleTextQuote.append(textQuote); + text.replace(textQuote, doubleTextQuote); + quote = true; + + } else if ( text[0].isSpace() || text[text.length()-1].isSpace() ) + quote = true; + else if ( text.find( csvDelimiter ) != -1 ) + quote = true; + else if ( text.find( "\n" ) != -1 || text.find( "\r" ) != -1 ) + quote = true; + } + + if ( quote ) { + text.prepend(textQuote); + text.append(textQuote); + } + + return text; +} + +// The reason why we use the KoDocument* approach and not the TQDomDocument +// approach is because we don't want to export formulas but values ! +KoFilter::ConversionStatus CSVExport::convert( const TQCString & from, const TQCString & to ) +{ + kdDebug(30501) << "CSVExport::convert" << endl; + KoDocument* document = m_chain->inputDocument(); + + if ( !document ) + return KoFilter::StupidError; + + if ( !::tqqt_cast<const KSpread::Doc *>( document ) ) + { + kdWarning(30501) << "document isn't a KSpread::Doc but a " << document->className() << endl; + return KoFilter::NotImplemented; + } + if ( ( to != "text/x-csv" && to != "text/plain" ) || from != "application/x-kspread" ) + { + kdWarning(30501) << "Invalid mimetypes " << to << " " << from << endl; + return KoFilter::NotImplemented; + } + + Doc const * const ksdoc = static_cast<const Doc *>(document); + + if ( ksdoc->mimeType() != "application/x-kspread" ) + { + kdWarning(30501) << "Invalid document mimetype " << ksdoc->mimeType() << endl; + return KoFilter::NotImplemented; + } + + CSVExportDialog *expDialog = 0; + if (!m_chain->manager()->getBatchMode()) + { + expDialog= new CSVExportDialog( 0 ); + + if (!expDialog) + { + kdError(30501) << "Dialog has not been created! Aborting!" << endl; + return KoFilter::StupidError; + } + expDialog->fillSheet( ksdoc->map() ); + + if ( !expDialog->exec() ) + { + delete expDialog; + return KoFilter::UserCancelled; + } + } + + TQTextCodec* codec = 0; + TQChar csvDelimiter; + if (expDialog) + { + codec = expDialog->getCodec(); + if ( !codec ) + { + delete expDialog; + return KoFilter::StupidError; + } + csvDelimiter = expDialog->getDelimiter(); + m_eol = expDialog->getEndOfLine(); + } + else + { + codec = TQTextCodec::codecForName("UTF-8"); + csvDelimiter = ','; + } + + + // Now get hold of the sheet to export + // (Hey, this could be part of the dialog too, choosing which sheet to export.... + // It's great to have parametrable filters... IIRC even MSOffice doesn't have that) + // Ok, for now we'll use the first sheet - my document has only one sheet anyway ;-))) + + bool first = true; + TQString str; + TQChar textQuote; + if (expDialog) + textQuote = expDialog->getTextQuote(); + else + textQuote = '"'; + + if ( expDialog && expDialog->exportSelectionOnly() ) + { + kdDebug(30501) << "Export as selection mode" << endl; + View const * const view = static_cast<View*>(ksdoc->views().getFirst()); + + if ( !view ) // no view if embedded document + { + delete expDialog; + return KoFilter::StupidError; + } + + Sheet const * const sheet = view->activeSheet(); + + TQRect selection = view->selectionInfo()->lastRange(); + // Compute the highest row and column indexes (within the selection) + // containing non-empty cells, respectively called CSVMaxRow CSVMaxCol. + // The CSV will have CSVMaxRow rows, all with CSVMaxCol columns + int right = selection.right(); + int bottom = selection.bottom(); + int CSVMaxRow = 0; + int CSVMaxCol = 0; + + for ( int idxRow = 1, row = selection.top(); row <= bottom; ++row, ++idxRow ) + { + for ( int idxCol = 1, col = selection.left(); col <= right; ++col, ++idxCol ) + { + if( ! sheet->cellAt( col, row )->isEmpty() ) + { + if ( idxRow > CSVMaxRow ) + CSVMaxRow = idxRow; + + if ( idxCol > CSVMaxCol ) + CSVMaxCol = idxCol; + } + } + } + + for ( int idxRow = 1, row = selection.top(); + row <= bottom && idxRow <= CSVMaxRow; ++row, ++idxRow ) + { + int idxCol = 1; + for ( int col = selection.left(); + col <= right && idxCol <= CSVMaxCol; ++col, ++idxCol ) + { + str += exportCSVCell( sheet, col, row, textQuote, csvDelimiter ); + + if ( idxCol < CSVMaxCol ) + str += csvDelimiter; + } + + // This is to deal with the case of non-rectangular selections + for ( ; idxCol < CSVMaxCol; ++idxCol ) + str += csvDelimiter; + + str += m_eol; + } + } + else + { + kdDebug(30501) << "Export as full mode" << endl; + TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() ); + for( ; it.current(); ++it ) + { + Sheet const * const sheet = it.current(); + + if (expDialog && !expDialog->exportSheet( sheet->sheetName() ) ) + { + continue; + } + + // Compute the highest row and column indexes containing non-empty cells, + // respectively called CSVMaxRow CSVMaxCol. + // The CSV will have CSVMaxRow rows, all with CSVMaxCol columns + int sheetMaxRow = sheet->maxRow(); + int sheetMaxCol = sheet->maxColumn(); + int CSVMaxRow = 0; + int CSVMaxCol = 0; + + for ( int row = 1 ; row <= sheetMaxRow ; ++row) + { + for ( int col = 1 ; col <= sheetMaxCol ; col++ ) + { + if( ! sheet->cellAt( col, row )->isEmpty() ) + { + if ( row > CSVMaxRow ) + CSVMaxRow = row; + + if ( col > CSVMaxCol ) + CSVMaxCol = col; + } + } + } + + // Skip the sheet altogether if it is empty + if ( CSVMaxRow + CSVMaxCol == 0) + continue; + + kdDebug(30501) << "Max row x column: " << CSVMaxRow << " x " << CSVMaxCol << endl; + + // Print sheet separators, except for the first sheet + if ( !first || ( expDialog && expDialog->printAlwaysSheetDelimiter() ) ) + { + if ( !first) + str += m_eol; + + TQString name; + if (expDialog) + name = expDialog->getSheetDelimiter(); + else + name = "********<SHEETNAME>********"; + const TQString tname( i18n("<SHEETNAME>") ); + int pos = name.find( tname ); + if ( pos != -1 ) + { + name.replace( pos, tname.length(), sheet->sheetName() ); + } + str += name; + str += m_eol; + str += m_eol; + } + + first = false; + + + // this is just a bad approximation which fails for documents with less than 50 rows, but + // we don't need any progress stuff there anyway :) (Werner) + int value = 0; + int step = CSVMaxRow > 50 ? CSVMaxRow/50 : 1; + + // Print the CSV for the sheet data + for ( int row = 1, i = 1 ; row <= CSVMaxRow ; ++row, ++i ) + { + if ( i > step ) + { + value += 2; + emit sigProgress(value); + i = 0; + } + + TQString collect; // buffer delimiters while reading empty cells + + for ( int col = 1 ; col <= CSVMaxCol ; col++ ) + { + const TQString txt = exportCSVCell( sheet, col, row, textQuote, csvDelimiter ); + + // if we encounter a non-empty cell, commit the buffered delimiters + if (!txt.isEmpty()) { + str += collect + txt; + collect = TQString(); + } + + collect += csvDelimiter; + } + // Here, throw away buffered delimiters. They're trailing and therefore + // superfluous. + + str += m_eol; + } + } + } + + emit sigProgress(100); + + TQFile out(m_chain->outputFile()); + if ( !out.open( IO_WriteOnly ) ) + { + kdError(30501) << "Unable to open output file!" << endl; + out.close(); + delete expDialog; + return KoFilter::StupidError; + } + + TQTextStream outStream( &out ); + outStream.setCodec( codec ); + + outStream << str; + + out.close(); + delete expDialog; + return KoFilter::OK; +} + +#include <csvexport.moc> |