filters

csvimport.cpp
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 <csvimport.h>
22
23#include <tqfile.h>
24#include <tqregexp.h>
25
26#include <tdeapplication.h>
27#include <tdemessagebox.h>
28#include <kdebug.h>
29#include <kgenericfactory.h>
30
31#include <KoFilterChain.h>
32#include <KoFilterManager.h>
33#include <kspread_doc.h>
34#include <kspread_global.h>
35#include <kspread_map.h>
36#include <kspread_sheet.h>
37#include <kspread_style.h>
38#include <kspread_style_manager.h>
39#include <kspread_cell.h>
40
41#include <csvdialog.h>
42
43using namespace KSpread;
44
45// hehe >:->
46
47/*
48 To generate a test CSV file:
49
50 perl -e '$i=0;while($i<30000) { print rand().",".rand()."\n"; $i++ }' > file.csv
51*/
52
53typedef KGenericFactory<CSVFilter, KoFilter> CSVImportFactory;
54K_EXPORT_COMPONENT_FACTORY( libcsvimport, CSVImportFactory( "kofficefilters" ) )
55
56CSVFilter::CSVFilter(KoFilter *, const char*, const TQStringList&) :
57 KoFilter() {
58}
59
60KoFilter::ConversionStatus CSVFilter::convert( const TQCString& from, const TQCString& to )
61{
62 TQString file( m_chain->inputFile() );
63 KoDocument* document = m_chain->outputDocument();
64
65 if ( !document )
66 return KoFilter::StupidError;
67
68 kdDebug(30501) << "here we go... " << document->className() << endl;
69
70 if ( !::tqt_cast<const KSpread::Doc *>( document ) )
71 {
72 kdWarning(30501) << "document isn't a KSpread::Doc but a " << document->className() << endl;
73 return KoFilter::NotImplemented;
74 }
75 if(from!="text/x-csv" && from!="text/plain" || to!="application/x-kspread")
76 {
77 kdWarning(30501) << "Invalid mimetypes " << from << " " << to << endl;
78 return KoFilter::NotImplemented;
79 }
80
81 kdDebug(30501) << "...still here..." << endl;
82
83 Doc *ksdoc = static_cast<Doc *>( document ); // type checked above
84
85 if(ksdoc->mimeType()!="application/x-kspread")
86 {
87 kdWarning(30501) << "Invalid document mimetype " << ksdoc->mimeType() << endl;
88 return KoFilter::NotImplemented;
89 }
90
91 TQFile in(file);
92 if(!in.open(IO_ReadOnly)) {
93 KMessageBox::sorry( 0L, i18n("CSV filter cannot open input file - please report.") );
94 in.close();
95 return KoFilter::FileNotFound;
96 }
97
98 TQString csv_delimiter;
99 // ###### FIXME: disabled for now
100 //if (!config.isNull())
101 // csv_delimiter = config[0];
102
103 TQByteArray inputFile( in.readAll() );
104 in.close();
105
106 CSVDialog *dialog = new CSVDialog(0L, inputFile, csv_delimiter );
107 if (!m_chain->manager()->getBatchMode() && !dialog->exec())
108 return KoFilter::UserCancelled;
109 inputFile.resize( 0 ); // Release memory (input file content)
110
111 ElapsedTime t( "Filling data into document" );
112
113 Cell*cell;
114 Sheet *sheet=ksdoc->map()->addNewSheet();
115
116 int numRows = dialog->getRows();
117 int numCols = dialog->getCols();
118
119 if (numRows == 0)
120 ++numRows;
121
122 int step = 100 / numRows * numCols;
123 int value = 0;
124
125 emit sigProgress(value);
126 TQApplication::setOverrideCursor(TQt::waitCursor);
127
128 int i;
129 double init = sheet->nonDefaultColumnFormat( 1 )->dblWidth();
130 TQMemArray<double> widths( numCols );
131 for ( i = 0; i < numCols; ++i )
132 widths[i] = init;
133
134 Cell* c = sheet->nonDefaultCell( 1, 1 );
135 TQFontMetrics fm( c->format()->textFont( 1, 1 ) );
136
137 Style * s = ksdoc->styleManager()->defaultStyle();
138
139 for ( int row = 0; row < numRows; ++row )
140 {
141 for (int col = 0; col < numCols; ++col)
142 {
143 value += step;
144 emit sigProgress(value);
145 const TQString text( dialog->getText( row, col ) );
146
147 // ### FIXME: how to calculate the width of numbers (as they might not be in the right format)
148 const double len = fm.width( text );
149 if ( len > widths[col] )
150 widths[col] = len;
151
152 switch (dialog->getHeader(col))
153 {
154 case CSVDialog::TEXT:
155 //see CSVDialog::accept(), Tomas introduced the Generic format between KOffice 1.3 and 1.4
156 //the Insert->External Data-> ... dialog uses the generic format for everything (see mentioned method)
157 //I will use this approach only for the TEXT format in the CSV import filter... (raphael)
158 //### FIXME: long term solution is to allow to select Generic format ("autodetect") in the dialog and make it the default
159
160 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
161 cell->setCellText( text );
162
163 cell->format()->setFormatType (Generic_format);
164
165 /* old code
166 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
167 cell->setCellText( text, true );
168 */
169 break;
170 // ### TODO: put the code for the different numbers together (at least partially)
171 case CSVDialog::NUMBER:
172 {
173 bool ok = false;
174 double d = ksdoc->locale()->readNumber( text, &ok );
175 // If not, try with the '.' as decimal separator
176 if ( !ok )
177 d = text.toDouble( &ok );
178 if ( !ok )
179 {
180 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
181 cell->setCellText( text, true );
182 }
183 else
184 {
185 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
186 cell->setNumber( d );
187 }
188 cell->format()->setPrecision( 2 );
189 break;
190 }
191 case CSVDialog::COMMANUMBER:
192 {
193 bool ok = false;
194 TQString tmp ( text );
195 tmp.remove ( TQRegExp( "[^0-9,Ee+-]" ) ); // Keep only 0 to 9, comma, E, e, plus, minus
196 tmp.replace ( ',', '.' );
197 kdDebug(30501) << "Comma: " << text << " => " << tmp << endl;
198 const double d = tmp.toDouble( &ok );
199 if ( !ok )
200 {
201 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
202 cell->setCellText( text, true );
203 }
204 else
205 {
206 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
207 cell->setNumber( d );
208 }
209 cell->format()->setPrecision( 2 );
210 break;
211 }
212 case CSVDialog::POINTNUMBER:
213 {
214 bool ok = false;
215 TQString tmp ( text );
216 tmp.remove ( TQRegExp( "[^0-9\\.EeD+-]" ) ); // Keep only 0 to 9, dot, E, e, D, plus, minus
217 tmp.replace ( 'D', 'E' ); // double from FORTRAN use D instead of E
218 kdDebug(30501) << "Point: " << text << " => " << tmp << endl;
219 const double d = tmp.toDouble( &ok );
220 if ( !ok )
221 {
222 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
223 cell->setCellText( text, true );
224 }
225 else
226 {
227 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
228 cell->setNumber( d );
229 }
230 cell->format()->setPrecision( 2 );
231 break;
232 }
233 case CSVDialog::DATE:
234 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
235 cell->setCellText( text );
236 cell->format()->setFormatType( ShortDate_format );
237 break;
238 case CSVDialog::CURRENCY:
239 cell = sheet->nonDefaultCell( col + 1, row + 1, false, s );
240 cell->setCellText( text, false );
241 cell->format()->setFormatType( Money_format );
242 cell->format()->setPrecision( 2 );
243 break;
244 }
245 }
246 }
247
248 emit sigProgress( 98 );
249
250 ElapsedTime t2( "Resizing columns" );
251 for ( i = 0; i < numCols; ++i )
252 {
253 ColumnFormat * c = sheet->nonDefaultColumnFormat( i + 1 );
254 c->setDblWidth( widths[i] );
255 }
256
257 emit sigProgress( 100 );
258 TQApplication::restoreOverrideCursor();
259 delete dialog;
260
261 return KoFilter::OK;
262}
263
264#include <csvimport.moc>