File indexing completed on 2025-07-13 03:32:32

0001 /*
0002     File                 : AsciiOptionsWidget.h
0003     Project              : LabPlot
0004     Description          : widget providing options for the import of ascii data
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2009-2017 Stefan Gerlach <stefan.gerlach@uni.kn>
0007     SPDX-FileCopyrightText: 2009-2019 Alexander Semke <alexander.semke@web.de>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "AsciiOptionsWidget.h"
0012 #include "backend/core/Settings.h"
0013 #include "backend/datasources/filters/AbstractFileFilter.h"
0014 #include "backend/datasources/filters/AsciiFilter.h"
0015 #include "backend/lib/macros.h"
0016 
0017 #include <KConfigGroup>
0018 #include <KLocalizedString>
0019 
0020 /*!
0021 \class AsciiOptionsWidget
0022 \brief Widget providing options for the import of ascii data
0023 
0024 \ingroup kdefrontend
0025 */
0026 AsciiOptionsWidget::AsciiOptionsWidget(QWidget* parent)
0027     : QWidget(parent) {
0028     ui.setupUi(parent);
0029 
0030     ui.cbSeparatingCharacter->addItems(AsciiFilter::separatorCharacters());
0031     ui.cbCommentCharacter->addItems(AsciiFilter::commentCharacters());
0032     ui.cbDecimalSeparator->addItem(i18n("Point '.'"));
0033     ui.cbDecimalSeparator->addItem(i18n("Comma ','"));
0034     ui.cbDateTimeFormat->addItems(AbstractColumn::dateTimeFormats());
0035 
0036     const QString textNumberFormatShort = i18n("This option determines how the imported strings have to be converted to numbers.");
0037     const QString textNumberFormat = textNumberFormatShort + QStringLiteral("<br><br>")
0038         + i18n("When point character is used for the decimal separator, the valid number representations are:"
0039                "<ul>"
0040                "<li>1234.56</li>"
0041                "<li>1,234.56</li>"
0042                "<li>etc.</li>"
0043                "</ul>"
0044                "For comma as the decimal separator, the valid number representations are:"
0045                "<ul>"
0046                "<li>1234,56</li>"
0047                "<li>1.234,56</li>"
0048                "<li>etc.</li>"
0049                "</ul>");
0050 
0051     ui.lDecimalSeparator->setToolTip(textNumberFormatShort);
0052     ui.lDecimalSeparator->setWhatsThis(textNumberFormat);
0053     ui.cbDecimalSeparator->setToolTip(textNumberFormatShort);
0054     ui.cbDecimalSeparator->setWhatsThis(textNumberFormat);
0055 
0056     // only available for live data, will be activated explicitly
0057     ui.chbCreateTimestamp->setVisible(false);
0058 
0059     const QString textDateTimeFormatShort = i18n(
0060         "This option determines how the imported strings have to be converted to calendar date, i.e. year, month, and day numbers in the Gregorian calendar "
0061         "and to time.");
0062     const QString textDateTimeFormat = textDateTimeFormatShort + QStringLiteral("<br><br>")
0063         + i18n("Expressions that may be used for the date part of format string:"
0064                "<table>"
0065                "<tr><td>d</td><td>the day as number without a leading zero (1 to 31).</td></tr>"
0066                "<tr><td>dd</td><td>the day as number with a leading zero (01 to 31).</td></tr>"
0067                "<tr><td>ddd</td><td>the abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name.</td></tr>"
0068                "<tr><td>dddd</td><td>the long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name.</td></tr>"
0069                "<tr><td>M</td><td>the month as number without a leading zero (1 to 12).</td></tr>"
0070                "<tr><td>MM</td><td>the month as number with a leading zero (01 to 12).</td></tr>"
0071                "<tr><td>MMM</td><td>the abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name.</td></tr>"
0072                "<tr><td>MMMM</td><td>the long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name.</td></tr>"
0073                "<tr><td>yy</td><td>the year as two digit number (00 to 99).</td></tr>"
0074                "<tr><td>yyyy</td><td>the year as four digit number. If the year is negative, a minus sign is prepended in addition.</td></tr>"
0075                "</table><br><br>"
0076                "Expressions that may be used for the time part of the format string:"
0077                "<table>"
0078                "<tr><td>h</td><td>the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)</td></tr>"
0079                "<tr><td>hh</td><td>the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)</td></tr>"
0080                "<tr><td>H</td><td>the hour without a leading zero (0 to 23, even with AM/PM display)</td></tr>"
0081                "<tr><td>HH</td><td>the hour with a leading zero (00 to 23, even with AM/PM display)</td></tr>"
0082                "<tr><td>m</td><td>the minute without a leading zero (0 to 59)</td></tr>"
0083                "<tr><td>mm</td><td>the minute with a leading zero (00 to 59)</td></tr>"
0084                "<tr><td>s</td><td>the second without a leading zero (0 to 59)</td></tr>"
0085                "<tr><td>ss</td><td>the second with a leading zero (00 to 59)</td></tr>"
0086                "<tr><td>z</td><td>the milliseconds without leading zeroes (0 to 999)</td></tr>"
0087                "<tr><td>zzz</td><td>the milliseconds with leading zeroes (000 to 999)</td></tr>"
0088                "<tr><td>AP or A</td><td>interpret as an AM/PM time. AP must be either 'AM' or 'PM'.</td></tr>"
0089                "<tr><td>ap or a</td><td>Interpret as an AM/PM time. ap must be either 'am' or 'pm'.</td></tr>"
0090                "</table><br><br>"
0091                "Examples are:"
0092                "<table>"
0093                "<tr><td>dd.MM.yyyy</td><td>20.07.1969</td></tr>"
0094                "<tr><td>ddd MMMM d yy</td><td>Sun July 20 69</td></tr>"
0095                "<tr><td>'The day is' dddd</td><td>The day is Sunday</td></tr>"
0096                "</table>"
0097                "<br><br>"
0098                "In case the provided expression is empty, the format will be auto-detected.");
0099 
0100     ui.lDateTimeFormat->setToolTip(textDateTimeFormatShort);
0101     ui.lDateTimeFormat->setWhatsThis(textDateTimeFormat);
0102     ui.cbDateTimeFormat->setToolTip(textDateTimeFormatShort);
0103     ui.cbDateTimeFormat->setWhatsThis(textDateTimeFormat);
0104 
0105     QString info = i18n("If checked, the specified line in the file will be used to determine the column names.");
0106     ui.chbHeader->setToolTip(info);
0107 
0108     info = i18n("Line in the file that should be used to determine the column names.");
0109     ui.sbHeaderLine->setToolTip(info);
0110 
0111     info = i18n("Custom column names, space separated. E.g. \"x y\"");
0112     ui.lVectorNames->setToolTip(info);
0113     ui.kleVectorNames->setToolTip(info);
0114 
0115     connect(ui.chbHeader, &QCheckBox::toggled, this, &AsciiOptionsWidget::headerChanged);
0116     connect(ui.sbHeaderLine, QOverload<int>::of(&QSpinBox::valueChanged), this, &AsciiOptionsWidget::headerLineChanged);
0117 }
0118 
0119 void AsciiOptionsWidget::showAsciiHeaderOptions(bool visible) {
0120     DEBUG(Q_FUNC_INFO);
0121     ui.chbHeader->setVisible(visible);
0122     ui.sbHeaderLine->setVisible(visible);
0123     if (visible) {
0124         ui.lVectorNames->setVisible(!ui.chbHeader->isChecked());
0125         ui.kleVectorNames->setVisible(!ui.chbHeader->isChecked());
0126     } else {
0127         ui.lVectorNames->setVisible(false);
0128         ui.kleVectorNames->setVisible(false);
0129     }
0130 }
0131 
0132 void AsciiOptionsWidget::showTimestampOptions(bool visible) {
0133     ui.chbCreateTimestamp->setVisible(visible);
0134     m_createTimeStampAvailable = visible;
0135 }
0136 
0137 /*!
0138   Shows a text field for the vector names if the option "Use the first row..." was not selected.
0139   Hides it otherwise.
0140 */
0141 void AsciiOptionsWidget::headerChanged(bool state) const {
0142     ui.sbHeaderLine->setEnabled(state);
0143     ui.kleVectorNames->setVisible(!state);
0144     ui.lVectorNames->setVisible(!state);
0145 }
0146 
0147 void AsciiOptionsWidget::applyFilterSettings(AsciiFilter* filter) const {
0148     DEBUG(Q_FUNC_INFO)
0149     Q_ASSERT(filter);
0150     filter->setCommentCharacter(ui.cbCommentCharacter->currentText());
0151     filter->setSeparatingCharacter(ui.cbSeparatingCharacter->currentText());
0152 
0153     // TODO: use general setting for decimal separator?
0154     QLocale::Language lang;
0155     if (ui.cbDecimalSeparator->currentIndex() == 0)
0156         lang = QLocale::Language::C;
0157     else
0158         lang = QLocale::Language::German;
0159     filter->setNumberFormat(lang);
0160     filter->setDateTimeFormat(ui.cbDateTimeFormat->currentText());
0161     filter->setCreateIndexEnabled(ui.chbCreateIndex->isChecked());
0162 
0163     // save the timestamp option only if it's visible, i.e. live source is used.
0164     // use the default setting in the filter (false) otherwise for non-live source
0165     if (m_createTimeStampAvailable)
0166         filter->setCreateTimestampEnabled(ui.chbCreateTimestamp->isChecked());
0167 
0168     filter->setSimplifyWhitespacesEnabled(ui.chbSimplifyWhitespaces->isChecked());
0169     filter->setNaNValueToZero(ui.chbConvertNaNToZero->isChecked());
0170     filter->setRemoveQuotesEnabled(ui.chbRemoveQuotes->isChecked());
0171     filter->setSkipEmptyParts(ui.chbSkipEmptyParts->isChecked());
0172     filter->setVectorNames(ui.kleVectorNames->text());
0173     filter->setHeaderEnabled(ui.chbHeader->isChecked());
0174     filter->setHeaderLine(ui.sbHeaderLine->value());
0175 }
0176 
0177 void AsciiOptionsWidget::setSeparatingCharacter(QLatin1Char character) {
0178     ui.cbSeparatingCharacter->setCurrentItem(QString(character));
0179 }
0180 
0181 // ##############################################################################
0182 // ########################## Template handling   ##############################
0183 // ##############################################################################
0184 void AsciiOptionsWidget::loadSettings() const {
0185     auto config = KConfig();
0186     loadConfigFromTemplate(config);
0187 }
0188 
0189 void AsciiOptionsWidget::saveSettings() const {
0190     auto config = KConfig();
0191     saveConfigAsTemplate(config);
0192 }
0193 
0194 void AsciiOptionsWidget::loadConfigFromTemplate(KConfig& config) const {
0195     auto group = config.group(QStringLiteral("ImportAscii"));
0196 
0197     ui.cbCommentCharacter->setCurrentText(group.readEntry("CommentCharacter", "#"));
0198     ui.cbSeparatingCharacter->setCurrentText(group.readEntry("SeparatingCharacter", "auto"));
0199 
0200     const auto decimalSeparator = QLocale().decimalPoint();
0201     int index = (decimalSeparator == QLatin1Char('.')) ? 0 : 1;
0202     ui.cbDecimalSeparator->setCurrentIndex(group.readEntry("DecimalSeparator", index));
0203 
0204     ui.cbDateTimeFormat->setCurrentText(group.readEntry("DateTimeFormat", "yyyy-MM-dd hh:mm:ss.zzz"));
0205     ui.chbCreateIndex->setChecked(group.readEntry("CreateIndex", false));
0206     ui.chbCreateTimestamp->setChecked(group.readEntry("CreateTimestamp", true));
0207     ui.chbSimplifyWhitespaces->setChecked(group.readEntry("SimplifyWhitespaces", true));
0208     ui.chbConvertNaNToZero->setChecked(group.readEntry("ConvertNaNToZero", false));
0209     ui.chbRemoveQuotes->setChecked(group.readEntry("RemoveQuotes", false));
0210     ui.chbSkipEmptyParts->setChecked(group.readEntry("SkipEmptyParts", false));
0211     ui.chbHeader->setChecked(group.readEntry("UseFirstRow", true)); // header enabled - yes/no
0212     headerChanged(ui.chbHeader->isChecked()); // call this to update the status of the SpinBox for the header line
0213     ui.sbHeaderLine->setValue(group.readEntry(QLatin1String("HeaderLine"), 1));
0214     ui.kleVectorNames->setText(group.readEntry("Names", ""));
0215 }
0216 
0217 void AsciiOptionsWidget::saveConfigAsTemplate(KConfig& config) const {
0218     auto group = config.group(QStringLiteral("ImportAscii"));
0219 
0220     group.writeEntry("CommentCharacter", ui.cbCommentCharacter->currentText());
0221     group.writeEntry("SeparatingCharacter", ui.cbSeparatingCharacter->currentText());
0222     group.writeEntry("DecimalSeparator", ui.cbDecimalSeparator->currentIndex());
0223     group.writeEntry("DateTimeFormat", ui.cbDateTimeFormat->currentText());
0224     group.writeEntry("CreateIndex", ui.chbCreateIndex->isChecked());
0225     group.writeEntry("CreateTimestamp", ui.chbCreateTimestamp->isChecked());
0226     group.writeEntry("SimplifyWhitespaces", ui.chbSimplifyWhitespaces->isChecked());
0227     group.writeEntry("ConvertNaNToZero", ui.chbConvertNaNToZero->isChecked());
0228     group.writeEntry("RemoveQuotes", ui.chbRemoveQuotes->isChecked());
0229     group.writeEntry("SkipEmptyParts", ui.chbSkipEmptyParts->isChecked());
0230     group.writeEntry("UseFirstRow", ui.chbHeader->isChecked()); // header enabled - yes/no
0231     group.writeEntry(QLatin1String("HeaderLine"), ui.sbHeaderLine->value());
0232     group.writeEntry("Names", ui.kleVectorNames->text());
0233 }