File indexing completed on 2024-05-12 05:10:09
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "csvexporter.h" 0026 #include "../collection.h" 0027 #include "../core/filehandler.h" 0028 0029 #include <KLocalizedString> 0030 #include <KConfigGroup> 0031 0032 #include <QLineEdit> 0033 #include <QGroupBox> 0034 #include <QCheckBox> 0035 #include <QRadioButton> 0036 #include <QGridLayout> 0037 #include <QVBoxLayout> 0038 #include <QLabel> 0039 0040 using namespace Tellico; 0041 using Tellico::Export::CSVExporter; 0042 0043 CSVExporter::CSVExporter(Data::CollPtr coll_) : Tellico::Export::Exporter(coll_), 0044 m_includeTitles(true), 0045 m_delimiter(QStringLiteral(",")), 0046 m_colDelimiter(QStringLiteral(":")), 0047 m_rowDelimiter(QStringLiteral("|")), 0048 m_widget(nullptr), 0049 m_checkIncludeTitles(nullptr), 0050 m_radioComma(nullptr), 0051 m_radioSemicolon(nullptr), 0052 m_radioTab(nullptr), 0053 m_radioOther(nullptr), 0054 m_editOther(nullptr), 0055 m_colDelimiterEdit(nullptr), 0056 m_rowDelimiterEdit(nullptr) { 0057 } 0058 0059 QString CSVExporter::formatString() const { 0060 return QStringLiteral("CSV"); 0061 } 0062 0063 QString CSVExporter::fileFilter() const { 0064 return i18n("CSV Files") + QLatin1String(" (*.csv)") + QLatin1String(";;") + i18n("All Files") + QLatin1String(" (*)"); 0065 } 0066 0067 QString& CSVExporter::escapeText(QString& text_) const { 0068 bool quotes = false; 0069 if(text_.contains(QLatin1Char('"'))) { 0070 quotes = true; 0071 // quotation marks will be escaped by using a double pair 0072 text_.replace(QLatin1Char('"'), QLatin1String("\"\"")); 0073 } 0074 // if the text contains quotes or the delimiter, it needs to be surrounded by quotes 0075 if(quotes || text_.contains(m_delimiter) || text_.contains(QLatin1Char('\n'))) { 0076 text_.prepend(QLatin1Char('"')); 0077 text_.append(QLatin1Char('"')); 0078 } 0079 return text_; 0080 } 0081 0082 bool CSVExporter::exec() { 0083 if(!collection()) { 0084 return false; 0085 } 0086 0087 return FileHandler::writeTextURL(url(), text(), options() & ExportUTF8, options() & Export::ExportForce); 0088 } 0089 0090 QString CSVExporter::text() const { 0091 QString text; 0092 0093 if(m_includeTitles) { 0094 foreach(Data::FieldPtr fIt, fields()) { 0095 QString title = fIt->title(); 0096 // because of Microsoft Excel bug, https://support.microsoft.com/kb/323626 0097 if(text.isEmpty() && title == QLatin1String("ID")) { 0098 title = QStringLiteral("Id"); 0099 } 0100 text += escapeText(title) + m_delimiter; 0101 } 0102 // remove last delimiter 0103 text.truncate(text.length() - m_delimiter.length()); 0104 text += QLatin1Char('\n'); 0105 } 0106 0107 FieldFormat::Request format = (options() & Export::ExportFormatted ? 0108 FieldFormat::ForceFormat : 0109 FieldFormat::AsIsFormat); 0110 0111 const bool replaceColDelimiter = (m_colDelimiter != FieldFormat::columnDelimiterString()); 0112 const bool replaceRowDelimiter = (m_rowDelimiter != FieldFormat::rowDelimiterString()); 0113 0114 foreach(Data::EntryPtr entryIt, entries()) { 0115 QStringList values; 0116 foreach(Data::FieldPtr fIt, fields()) { 0117 QString value = entryIt->formattedField(fIt, format); 0118 if(replaceColDelimiter) { 0119 value.replace(FieldFormat::columnDelimiterString(), m_colDelimiter); 0120 } 0121 if(replaceRowDelimiter) { 0122 value.replace(FieldFormat::rowDelimiterString(), m_rowDelimiter); 0123 } 0124 values += escapeText(value); 0125 } 0126 text += values.join(m_delimiter) + QLatin1Char('\n'); 0127 } 0128 return text; 0129 } 0130 0131 QWidget* CSVExporter::widget(QWidget* parent_) { 0132 if(m_widget && m_widget->parent() == parent_) { 0133 return m_widget; 0134 } 0135 0136 m_widget = new QWidget(parent_); 0137 QVBoxLayout* l = new QVBoxLayout(m_widget); 0138 0139 QGroupBox* gbox = new QGroupBox(i18n("CSV Options"), m_widget); 0140 QVBoxLayout* vlay = new QVBoxLayout(gbox); 0141 0142 m_checkIncludeTitles = new QCheckBox(i18n("Include field titles as column headers"), gbox); 0143 m_checkIncludeTitles->setChecked(m_includeTitles); 0144 m_checkIncludeTitles->setWhatsThis(i18n("If checked, a header row will be added with the " 0145 "field titles.")); 0146 0147 QGroupBox* delimiterGroup = new QGroupBox(i18n("Delimiter"), gbox); 0148 QGridLayout* m_delimiterGroupLayout = new QGridLayout(delimiterGroup); 0149 m_delimiterGroupLayout->setAlignment(Qt::AlignTop); 0150 delimiterGroup->setWhatsThis(i18n("In addition to a comma, other characters may be used as " 0151 "a delimiter, separating each value in the file.")); 0152 0153 m_radioComma = new QRadioButton(delimiterGroup); 0154 m_radioComma->setText(i18n("Comma")); 0155 m_radioComma->setChecked(true); 0156 m_radioComma->setWhatsThis(i18n("Use a comma as the delimiter.")); 0157 m_delimiterGroupLayout->addWidget(m_radioComma, 0, 0); 0158 0159 m_radioSemicolon = new QRadioButton( delimiterGroup); 0160 m_radioSemicolon->setText(i18n("Semicolon")); 0161 m_radioSemicolon->setWhatsThis(i18n("Use a semi-colon as the delimiter.")); 0162 m_delimiterGroupLayout->addWidget(m_radioSemicolon, 0, 1); 0163 0164 m_radioTab = new QRadioButton(delimiterGroup); 0165 m_radioTab->setText(i18n("Tab")); 0166 m_radioTab->setWhatsThis(i18n("Use a tab as the delimiter.")); 0167 m_delimiterGroupLayout->addWidget(m_radioTab, 1, 0); 0168 0169 m_radioOther = new QRadioButton(delimiterGroup); 0170 m_radioOther->setText(i18n("Other")); 0171 m_radioOther->setWhatsThis(i18n("Use a custom string as the delimiter.")); 0172 m_delimiterGroupLayout->addWidget(m_radioOther, 1, 1); 0173 0174 m_editOther = new QLineEdit(delimiterGroup); 0175 m_editOther->setEnabled(m_radioOther->isChecked()); 0176 m_editOther->setWhatsThis(i18n("A custom string, such as a colon, may be used as a delimiter.")); 0177 m_delimiterGroupLayout->addWidget(m_editOther, 1, 2); 0178 QObject::connect(m_radioOther, &QAbstractButton::toggled, 0179 m_editOther, &QWidget::setEnabled); 0180 0181 if(m_delimiter == QLatin1String(",")) { 0182 m_radioComma->setChecked(true); 0183 } else if(m_delimiter == QLatin1String(";")) { 0184 m_radioSemicolon->setChecked(true); 0185 } else if(m_delimiter == QLatin1String("\t")) { 0186 m_radioTab->setChecked(true); 0187 } else if(!m_delimiter.isEmpty()) { 0188 m_radioOther->setChecked(true); 0189 m_editOther->setEnabled(true); 0190 m_editOther->setText(m_delimiter); 0191 } 0192 0193 QLabel* label = new QLabel(i18n("Table column delimiter:"), gbox); 0194 m_colDelimiterEdit = new QLineEdit(gbox); 0195 m_colDelimiterEdit->setText(m_colDelimiter); 0196 m_delimiterGroupLayout->addWidget(label, 2, 0, 1, 2); 0197 m_delimiterGroupLayout->addWidget(m_colDelimiterEdit, 2, 2); 0198 QString w = i18n("The column delimiter separates values in each column of a <i>Table</i> field."); 0199 label->setWhatsThis(w); 0200 m_colDelimiterEdit->setWhatsThis(w); 0201 0202 label = new QLabel(i18n("Table row delimiter:"), gbox); 0203 m_rowDelimiterEdit = new QLineEdit(gbox); 0204 m_rowDelimiterEdit->setText(m_rowDelimiter); 0205 m_delimiterGroupLayout->addWidget(label, 3, 0, 1, 2); 0206 m_delimiterGroupLayout->addWidget(m_rowDelimiterEdit, 3, 2); 0207 w = i18n("The row delimiter separates values in each row of a <i>Table</i> field."); 0208 label->setWhatsThis(w); 0209 m_rowDelimiterEdit->setWhatsThis(w); 0210 0211 vlay->addWidget(m_checkIncludeTitles); 0212 vlay->addWidget(delimiterGroup); 0213 0214 l->addWidget(gbox); 0215 l->addStretch(1); 0216 return m_widget; 0217 } 0218 0219 void CSVExporter::readOptions(KSharedConfigPtr config_) { 0220 KConfigGroup group(config_, QStringLiteral("ExportOptions - %1").arg(formatString())); 0221 m_includeTitles = group.readEntry("Include Titles", m_includeTitles); 0222 m_delimiter = group.readEntry("Delimiter", m_delimiter); 0223 m_rowDelimiter = group.readEntry("RowDelimiter", m_rowDelimiter); 0224 m_colDelimiter = group.readEntry("ColumnDelimiter", m_colDelimiter); 0225 } 0226 0227 void CSVExporter::saveOptions(KSharedConfigPtr config_) { 0228 m_includeTitles = m_checkIncludeTitles->isChecked(); 0229 if(m_radioComma->isChecked()) { 0230 m_delimiter = QLatin1Char(','); 0231 } else if(m_radioSemicolon->isChecked()) { 0232 m_delimiter = QLatin1Char(';'); 0233 } else if(m_radioTab->isChecked()) { 0234 m_delimiter = QLatin1Char('\t'); 0235 } else { 0236 m_delimiter = m_editOther->text(); 0237 } 0238 0239 QString s = m_colDelimiterEdit->text(); 0240 if(!s.isEmpty()) { 0241 m_colDelimiter = s; 0242 } 0243 0244 s = m_rowDelimiterEdit->text(); 0245 if(!s.isEmpty()) { 0246 m_rowDelimiter = s; 0247 } 0248 0249 KConfigGroup group(config_, QStringLiteral("ExportOptions - %1").arg(formatString())); 0250 group.writeEntry("Include Titles", m_includeTitles); 0251 group.writeEntry("Delimiter", m_delimiter); 0252 group.writeEntry("RowDelimiter", m_rowDelimiter); 0253 group.writeEntry("ColumnDelimiter", m_colDelimiter); 0254 }