File indexing completed on 2025-02-02 12:00:33
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org> 0003 SPDX-FileCopyrightText: 2004 Nicolas GOUTTE <goutte@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "csvimport.h" 0009 0010 #include <QFile> 0011 #include <QGuiApplication> 0012 0013 #include <kmessagebox.h> 0014 #include <kpluginfactory.h> 0015 0016 #include <KoCsvImportDialog.h> 0017 #include <KoFilterChain.h> 0018 #include <KoFilterManager.h> 0019 0020 #include <sheets/engine/ElapsedTime_p.h> 0021 #include <sheets/engine/CalculationSettings.h> 0022 #include <sheets/engine/Localization.h> 0023 #include <sheets/engine/Value.h> 0024 #include <sheets/engine/ValueConverter.h> 0025 #include <sheets/core/Cell.h> 0026 #include <sheets/core/ColFormatStorage.h> 0027 #include <sheets/core/DocBase.h> 0028 #include <sheets/core/Map.h> 0029 #include <sheets/core/Sheet.h> 0030 0031 using namespace Calligra::Sheets; 0032 0033 /* 0034 To generate a test CSV file: 0035 0036 perl -e '$i=0;while($i<30000) { print rand().",".rand()."\n"; $i++ }' > file.csv 0037 */ 0038 0039 K_PLUGIN_FACTORY_WITH_JSON(CSVImportFactory, "calligra_filter_csv2sheets.json", registerPlugin<CSVFilter>();) 0040 0041 Q_LOGGING_CATEGORY(lcCsvImport, "calligra.filter.csv.import") 0042 0043 CSVFilter::CSVFilter(QObject* parent, const QVariantList&) : 0044 KoFilter(parent) 0045 { 0046 } 0047 0048 KoFilter::ConversionStatus CSVFilter::convert(const QByteArray& from, const QByteArray& to) 0049 { 0050 QString file(m_chain->inputFile()); 0051 KoDocument* document = m_chain->outputDocument(); 0052 0053 if (!document) 0054 return KoFilter::StupidError; 0055 0056 if (!qobject_cast<const Calligra::Sheets::DocBase *>(document)) { 0057 qWarning(lcCsvImport) << "document isn't a Calligra::Sheets::Doc but a " << document->metaObject()->className(); 0058 return KoFilter::NotImplemented; 0059 } 0060 if ((from != "text/csv" && from != "text/tab-separated-values" && from != "text/plain") || to != "application/vnd.oasis.opendocument.spreadsheet") { 0061 qWarning(lcCsvImport) << "Invalid mimetypes " << from << " " << to; 0062 return KoFilter::NotImplemented; 0063 } 0064 0065 DocBase *ksdoc = dynamic_cast<DocBase *>(document); // type checked above 0066 0067 // if (ksdoc->mimeType() != "application/vnd.oasis.opendocument.spreadsheet") { 0068 // qWarning(lcCsvImport) << "Invalid document mimetype " << ksdoc->mimeType(); 0069 // return KoFilter::NotImplemented; 0070 // } 0071 0072 QFile in(file); 0073 if (!in.open(QIODevice::ReadOnly)) { 0074 KMessageBox::sorry(0L, i18n("CSV filter cannot open input file - please report.")); 0075 in.close(); 0076 return KoFilter::FileNotFound; 0077 } 0078 0079 // ###### FIXME: disabled for now 0080 //QString csv_delimiter; 0081 //if (!config.isNull()) 0082 // csv_delimiter = config[0]; 0083 0084 QByteArray inputFile(in.readAll()); 0085 in.close(); 0086 0087 Localization *locale = ksdoc->map()->calculationSettings()->locale(); 0088 ValueConverter *conv = ksdoc->map()->converter(); 0089 0090 QString decimal = locale->decimalSymbol(); 0091 QString thousands = locale->thousandsSeparator(); 0092 0093 KoCsvImportDialog* dialog = new KoCsvImportDialog(0); 0094 dialog->setData(inputFile); 0095 dialog->setDecimalSymbol(decimal); 0096 dialog->setThousandsSeparator(thousands); 0097 if (!m_chain->manager()->getBatchMode() && !dialog->exec()) 0098 return KoFilter::UserCancelled; 0099 inputFile.resize(0); // Release memory (input file content) 0100 0101 ElapsedTime t("Filling data into document"); 0102 0103 Sheet *sheet = dynamic_cast<Sheet *>(ksdoc->map()->addNewSheet()); 0104 0105 int numRows = dialog->rows(); 0106 int numCols = dialog->cols(); 0107 0108 if (numRows == 0) 0109 ++numRows; 0110 0111 // Initialize the decimal symbol and thousands separator to use for parsing. 0112 decimal = dialog->decimalSymbol(); 0113 thousands = dialog->thousandsSeparator(); 0114 0115 int value = 0; 0116 0117 emit sigProgress(value); 0118 QGuiApplication::setOverrideCursor(Qt::WaitCursor); 0119 0120 Cell cell(sheet, 1, 1); 0121 0122 int processed = 0; 0123 for (int row = 0; row < numRows; ++row) { 0124 for (int col = 0; col < numCols; ++col) { 0125 processed++; 0126 int progress = (int) (100.0 * processed / (numRows * numCols)); 0127 if (progress != value) { 0128 value = progress; 0129 emit sigProgress(value); 0130 } 0131 0132 const QString text(dialog->text(row, col)); 0133 0134 cell = Cell(sheet, col + 1, row + 1); 0135 0136 // TODO - try to format numbers 0137 0138 switch (dialog->dataType(col)) { 0139 case KoCsvImportDialog::Generic: 0140 default: { 0141 Value val; 0142 // Is this a valid number? 0143 QString numtext = text; 0144 numtext = numtext.replace(' ', QString()); 0145 numtext = numtext.replace(thousands, QString()); 0146 numtext = numtext.replace(decimal, "."); 0147 bool ok = false; 0148 double num = numtext.toDouble(&ok); 0149 if (ok) val = Value(num); 0150 0151 if (!val.isEmpty()) 0152 cell.setCellValue(val); 0153 else 0154 cell.parseUserInput(text); 0155 break; 0156 } 0157 case KoCsvImportDialog::Text: { 0158 Value value(text); 0159 cell.setCellValue(value); 0160 break; 0161 } 0162 case KoCsvImportDialog::Date: { 0163 Value value(text); 0164 cell.setCellValue(conv->asDate(value)); 0165 break; 0166 } 0167 case KoCsvImportDialog::Currency: { 0168 Value value(text); 0169 value = conv->asNumeric(value); 0170 value.setFormat(Value::fmt_Money); 0171 cell.setCellValue(value); 0172 break; 0173 } 0174 case KoCsvImportDialog::None: { 0175 // just skip the content 0176 break; 0177 } 0178 } 0179 } 0180 } 0181 0182 emit sigProgress(100); 0183 QGuiApplication::restoreOverrideCursor(); 0184 delete dialog; 0185 0186 return KoFilter::OK; 0187 } 0188 0189 #include <csvimport.moc>