summaryrefslogtreecommitdiffstats
path: root/filters/kspread/csv/csvexport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filters/kspread/csv/csvexport.cpp')
-rw-r--r--filters/kspread/csv/csvexport.cpp382
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>