File indexing completed on 2024-05-12 15:27:58

0001 /***************************************************************************
0002     File                 : ColumnDock.cpp
0003     Project              : LabPlot
0004     --------------------------------------------------------------------
0005     Copyright            : (C) 2011-2020 by Alexander Semke (alexander.semke@web.de)
0006     Copyright            : (C) 2013-2017 by Stefan Gerlach (stefan.gerlach@uni.kn)
0007     Description          : widget for column properties
0008 
0009  ***************************************************************************/
0010 
0011 /***************************************************************************
0012  *                                                                         *
0013  *  This program is free software; you can redistribute it and/or modify   *
0014  *  it under the terms of the GNU General Public License as published by   *
0015  *  the Free Software Foundation; either version 2 of the License, or      *
0016  *  (at your option) any later version.                                    *
0017  *                                                                         *
0018  *  This program is distributed in the hope that it will be useful,        *
0019  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0020  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0021  *  GNU General Public License for more details.                           *
0022  *                                                                         *
0023  *   You should have received a copy of the GNU General Public License     *
0024  *   along with this program; if not, write to the Free Software           *
0025  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0026  *   Boston, MA  02110-1301  USA                                           *
0027  *                                                                         *
0028  ***************************************************************************/
0029 
0030 #include "ColumnDock.h"
0031 
0032 #include "backend/core/AbstractFilter.h"
0033 #include "backend/core/datatypes/SimpleCopyThroughFilter.h"
0034 #include "backend/core/datatypes/Double2StringFilter.h"
0035 #include "backend/core/datatypes/String2DoubleFilter.h"
0036 #include "backend/core/datatypes/DateTime2StringFilter.h"
0037 #include "backend/core/datatypes/String2DateTimeFilter.h"
0038 #include "backend/spreadsheet/Spreadsheet.h"
0039 
0040 #include <KLocalizedString>
0041 
0042 /*!
0043   \class ColumnDock
0044   \brief Provides a widget for editing the properties of the spreadsheet columns currently selected in the project explorer.
0045 
0046   \ingroup kdefrontend
0047 */
0048 
0049 ColumnDock::ColumnDock(QWidget* parent) : BaseDock(parent) {
0050     ui.setupUi(this);
0051     m_leName = ui.leName;
0052     m_leComment = ui.leComment;
0053 
0054     //add formats for numeric values
0055     ui.cbNumericFormat->addItem(i18n("Decimal"), QVariant('f'));
0056     ui.cbNumericFormat->addItem(i18n("Scientific (e)"), QVariant('e'));
0057     ui.cbNumericFormat->addItem(i18n("Scientific (E)"), QVariant('E'));
0058     ui.cbNumericFormat->addItem(i18n("Automatic (e)"), QVariant('g'));
0059     ui.cbNumericFormat->addItem(i18n("Automatic (E)"), QVariant('G'));
0060 
0061     //add format for date, time and datetime values
0062     for (const auto& s : AbstractColumn::dateTimeFormats())
0063         ui.cbDateTimeFormat->addItem(s, QVariant(s));
0064 
0065     ui.cbDateTimeFormat->setEditable(true);
0066 
0067     connect(ui.leName, &QLineEdit::textChanged, this, &ColumnDock::nameChanged);
0068     connect(ui.leComment, &QLineEdit::textChanged, this, &ColumnDock::commentChanged);
0069     connect(ui.cbType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ColumnDock::typeChanged);
0070     connect(ui.cbNumericFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ColumnDock::numericFormatChanged);
0071     connect(ui.sbPrecision, QOverload<int>::of(&QSpinBox::valueChanged), this, &ColumnDock::precisionChanged);
0072     connect(ui.cbDateTimeFormat, &QComboBox::currentTextChanged, this, &ColumnDock::dateTimeFormatChanged);
0073     connect(ui.cbPlotDesignation, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ColumnDock::plotDesignationChanged);
0074 
0075     retranslateUi();
0076 }
0077 
0078 void ColumnDock::setColumns(QList<Column*> list) {
0079     m_initializing = true;
0080     m_columnsList = list;
0081     m_column = list.first();
0082     m_aspect = list.first();
0083 
0084     //check whether we have non-editable columns:
0085     //1. columns in a LiveDataSource
0086     //2. columns in the spreadsheet of a datapicker curve
0087     //3. columns for residuals calculated in XYFitCurve)
0088     bool nonEditable = false;
0089     for (auto* col : m_columnsList) {
0090         auto* s = dynamic_cast<Spreadsheet*>(col->parentAspect());
0091         if (s) {
0092             if (s->type() == AspectType::LiveDataSource || s->parentAspect()->type() == AspectType::DatapickerCurve) {
0093                 nonEditable = true;
0094                 break;
0095             }
0096         } else {
0097             nonEditable = true;
0098             break;
0099         }
0100     }
0101 
0102     //if columns of different modes are selected, change of the mode is not possible
0103     bool sameMode = true;
0104 
0105     if (list.size() == 1) {
0106         //names and comments of non-editable columns in a file data source can be changed.
0107         if (!nonEditable && m_column->parentAspect()->type() == AspectType::LiveDataSource) {
0108             ui.leName->setEnabled(false);
0109             ui.leComment->setEnabled(false);
0110         } else {
0111             ui.leName->setEnabled(true);
0112             ui.leComment->setEnabled(true);
0113         }
0114 
0115         ui.leName->setText(m_column->name());
0116         ui.leComment->setText(m_column->comment());
0117     } else {
0118         ui.leName->setEnabled(false);
0119         ui.leComment->setEnabled(false);
0120 
0121         ui.leName->setText(QString());
0122         ui.leComment->setText(QString());
0123 
0124         auto mode = m_column->columnMode();
0125         for (auto* col : m_columnsList) {
0126             if (col->columnMode() != mode) {
0127                 sameMode = false;
0128                 break;
0129             }
0130         }
0131     }
0132 
0133     ui.lType->setEnabled(sameMode);
0134     ui.cbType->setEnabled(sameMode);
0135     ui.lNumericFormat->setEnabled(sameMode);
0136     ui.cbNumericFormat->setEnabled(sameMode);
0137     ui.lPrecision->setEnabled(sameMode);
0138     ui.sbPrecision->setEnabled(sameMode);
0139     ui.lDateTimeFormat->setEnabled(sameMode);
0140     ui.cbDateTimeFormat->setEnabled(sameMode);
0141 
0142     ui.leName->setStyleSheet("");
0143     ui.leName->setToolTip("");
0144 
0145     //show the properties of the first column
0146     updateTypeWidgets(m_column->columnMode());
0147     ui.cbPlotDesignation->setCurrentIndex( int(m_column->plotDesignation()) );
0148 
0149     // slots
0150     connect(m_column, &AbstractColumn::aspectDescriptionChanged, this, &ColumnDock::columnDescriptionChanged);
0151     connect(m_column, &AbstractColumn::modeChanged, this, &ColumnDock::columnModeChanged);
0152     connect(m_column->outputFilter(), &AbstractSimpleFilter::formatChanged, this, &ColumnDock::columnFormatChanged);
0153     connect(m_column->outputFilter(), &AbstractSimpleFilter::digitsChanged, this, &ColumnDock::columnPrecisionChanged);
0154     connect(m_column, &AbstractColumn::plotDesignationChanged, this, &ColumnDock::columnPlotDesignationChanged);
0155 
0156     //don't allow to change the column type at least one non-editable column
0157     if (sameMode)
0158         ui.cbType->setEnabled(!nonEditable);
0159 
0160     m_initializing = false;
0161 }
0162 
0163 /*!
0164   depending on the currently selected column type (column mode) updates the widgets for the column format,
0165   shows/hides the allowed widgets, fills the corresponding combobox with the possible entries.
0166   Called when the type (column mode) is changed.
0167 */
0168 void ColumnDock::updateTypeWidgets(AbstractColumn::ColumnMode mode) {
0169     const Lock lock(m_initializing);
0170     ui.cbType->setCurrentIndex(ui.cbType->findData((int)mode));
0171     switch (mode) {
0172     case AbstractColumn::ColumnMode::Numeric: {
0173             auto* filter = static_cast<Double2StringFilter*>(m_column->outputFilter());
0174             ui.cbNumericFormat->setCurrentIndex(ui.cbNumericFormat->findData(filter->numericFormat()));
0175             ui.sbPrecision->setValue(filter->numDigits());
0176             break;
0177         }
0178     case AbstractColumn::ColumnMode::Month:
0179     case AbstractColumn::ColumnMode::Day:
0180     case AbstractColumn::ColumnMode::DateTime: {
0181             auto* filter = static_cast<DateTime2StringFilter*>(m_column->outputFilter());
0182 //          DEBUG(" set column format: " << STDSTRING(filter->format()));
0183             ui.cbDateTimeFormat->setCurrentIndex(ui.cbDateTimeFormat->findData(filter->format()));
0184             break;
0185         }
0186     case AbstractColumn::ColumnMode::Integer:   // nothing to set
0187     case AbstractColumn::ColumnMode::BigInt:
0188     case AbstractColumn::ColumnMode::Text:
0189         break;
0190     }
0191 
0192     //hide all the format related widgets first and
0193     //then show only what is required depending of the column mode(s)
0194     ui.lNumericFormat->hide();
0195     ui.cbNumericFormat->hide();
0196     ui.lPrecision->hide();
0197     ui.sbPrecision->hide();
0198     ui.lDateTimeFormat->hide();
0199     ui.cbDateTimeFormat->hide();
0200 
0201     if (mode == AbstractColumn::ColumnMode::Numeric) {
0202         ui.lNumericFormat->show();
0203         ui.cbNumericFormat->show();
0204     }
0205 
0206     //precision is only available for Numeric
0207     if (mode == AbstractColumn::ColumnMode::Numeric) {
0208         ui.lPrecision->show();
0209         ui.sbPrecision->show();
0210     }
0211 
0212     if (mode == AbstractColumn::ColumnMode::DateTime) {
0213         ui.lDateTimeFormat->show();
0214         ui.cbDateTimeFormat->show();
0215     }
0216 }
0217 
0218 //*************************************************************
0219 //******** SLOTs for changes triggered in ColumnDock **********
0220 //*************************************************************
0221 void ColumnDock::retranslateUi() {
0222     m_initializing = true;
0223 
0224     ui.cbType->clear();
0225     ui.cbType->addItem(i18n("Numeric"), QVariant(static_cast<int>(AbstractColumn::ColumnMode::Numeric)));
0226     ui.cbType->addItem(i18n("Integer"), QVariant(static_cast<int>(AbstractColumn::ColumnMode::Integer)));
0227     ui.cbType->addItem(i18n("Big Integer"), QVariant(static_cast<int>(AbstractColumn::ColumnMode::BigInt)));
0228     ui.cbType->addItem(i18n("Text"), QVariant(static_cast<int>(AbstractColumn::ColumnMode::Text)));
0229     ui.cbType->addItem(i18n("Month Names"), QVariant(static_cast<int>(AbstractColumn::ColumnMode::Month)));
0230     ui.cbType->addItem(i18n("Day Names"), QVariant(static_cast<int>(AbstractColumn::ColumnMode::Day)));
0231     ui.cbType->addItem(i18n("Date and Time"), QVariant(static_cast<int>(AbstractColumn::ColumnMode::DateTime)));
0232 
0233     ui.cbPlotDesignation->clear();
0234     ui.cbPlotDesignation->addItem(i18n("None"));
0235     ui.cbPlotDesignation->addItem(i18n("X"));
0236     ui.cbPlotDesignation->addItem(i18n("Y"));
0237     ui.cbPlotDesignation->addItem(i18n("Z"));
0238     ui.cbPlotDesignation->addItem(i18n("X-error"));
0239     ui.cbPlotDesignation->addItem(i18n("X-error -"));
0240     ui.cbPlotDesignation->addItem(i18n("X-error +"));
0241     ui.cbPlotDesignation->addItem(i18n("Y-error"));
0242     ui.cbPlotDesignation->addItem(i18n("Y-error -"));
0243     ui.cbPlotDesignation->addItem(i18n("Y-error +"));
0244 
0245     m_initializing = false;
0246 }
0247 
0248 /*!
0249   called when the type (column mode - numeric, text etc.) of the column was changed.
0250 */
0251 void ColumnDock::typeChanged(int index) {
0252     DEBUG("ColumnDock::typeChanged()");
0253     if (m_initializing)
0254         return;
0255 
0256     auto columnMode = (AbstractColumn::ColumnMode)ui.cbType->itemData(index).toInt();
0257 
0258     switch (columnMode) {
0259     case AbstractColumn::ColumnMode::Numeric: {
0260         int digits = ui.sbPrecision->value();
0261         for (auto* col : m_columnsList) {
0262             col->beginMacro(i18n("%1: change column type", col->name()));
0263             col->setColumnMode(columnMode);
0264             auto* filter = static_cast<Double2StringFilter*>(col->outputFilter());
0265 
0266             //TODO: using
0267             //char format = ui.cbFormat->itemData(ui.cbFormat->currentIndex()).toChar().toLatin1();
0268             //outside of the for-loop and
0269             //filter->setNumericFormat(format);
0270             //inside the loop leads to wrong results when converting from integer to numeric -> 'f' is set instead of 'e'
0271             filter->setNumericFormat(ui.cbNumericFormat->itemData(ui.cbNumericFormat->currentIndex()).toChar().toLatin1());
0272             filter->setNumDigits(digits);
0273             col->endMacro();
0274         }
0275         break;
0276     }
0277     case AbstractColumn::ColumnMode::Integer:
0278     case AbstractColumn::ColumnMode::BigInt:
0279     case AbstractColumn::ColumnMode::Text:
0280         for (auto* col : m_columnsList)
0281             col->setColumnMode(columnMode);
0282         break;
0283     case AbstractColumn::ColumnMode::Month:
0284     case AbstractColumn::ColumnMode::Day:
0285         for (auto* col : m_columnsList) {
0286             col->beginMacro(i18n("%1: change column type", col->name()));
0287             // the format is saved as item data
0288             QString format = ui.cbDateTimeFormat->itemData(ui.cbDateTimeFormat->currentIndex()).toString();
0289             col->setColumnMode(columnMode);
0290             auto* filter = static_cast<DateTime2StringFilter*>(col->outputFilter());
0291             filter->setFormat(format);
0292             col->endMacro();
0293         }
0294         break;
0295     case AbstractColumn::ColumnMode::DateTime:
0296         for (auto* col : m_columnsList) {
0297             col->beginMacro(i18n("%1: change column type", col->name()));
0298             // the format is the current text
0299             QString format = ui.cbDateTimeFormat->currentText();
0300             col->setColumnMode(columnMode);
0301             auto* filter = static_cast<DateTime2StringFilter*>(col->outputFilter());
0302             filter->setFormat(format);
0303             col->endMacro();
0304         }
0305         break;
0306     }
0307 
0308     updateTypeWidgets(columnMode);
0309 
0310     DEBUG("ColumnDock::typeChanged() DONE");
0311 }
0312 
0313 void ColumnDock::numericFormatChanged(int index) {
0314     if (m_initializing)
0315         return;
0316 
0317     char format = ui.cbNumericFormat->itemData(index).toChar().toLatin1();
0318     for (auto* col : m_columnsList) {
0319         auto* filter = static_cast<Double2StringFilter*>(col->outputFilter());
0320         filter->setNumericFormat(format);
0321     }
0322 }
0323 
0324 void ColumnDock::precisionChanged(int digits) {
0325     if (m_initializing)
0326         return;
0327 
0328     for (auto* col : m_columnsList) {
0329         auto* filter = static_cast<Double2StringFilter*>(col->outputFilter());
0330         filter->setNumDigits(digits);
0331     }
0332 }
0333 
0334 void ColumnDock::dateTimeFormatChanged(const QString& format) {
0335     if (m_initializing)
0336         return;
0337 
0338     for (auto* col : m_columnsList) {
0339         auto* filter = static_cast<DateTime2StringFilter*>(col->outputFilter());
0340         filter->setFormat(format);
0341     }
0342 }
0343 
0344 void ColumnDock::plotDesignationChanged(int index) {
0345     if (m_initializing)
0346         return;
0347 
0348     auto pd = AbstractColumn::PlotDesignation(index);
0349     for (auto* col : m_columnsList)
0350         col->setPlotDesignation(pd);
0351 }
0352 
0353 //*************************************************************
0354 //********* SLOTs for changes triggered in Column *************
0355 //*************************************************************
0356 void ColumnDock::columnDescriptionChanged(const AbstractAspect* aspect) {
0357     if (m_column != aspect)
0358         return;
0359 
0360     m_initializing = true;
0361     if (aspect->name() != ui.leName->text())
0362         ui.leName->setText(aspect->name());
0363     else if (aspect->comment() != ui.leComment->text())
0364         ui.leComment->setText(aspect->comment());
0365     m_initializing = false;
0366 }
0367 
0368 void ColumnDock::columnModeChanged(const AbstractAspect* aspect) {
0369     if (m_column != aspect)
0370         return;
0371 
0372     updateTypeWidgets(m_column->columnMode());
0373 }
0374 
0375 void ColumnDock::columnFormatChanged() {
0376     DEBUG("ColumnDock::columnFormatChanged()");
0377     const Lock lock(m_initializing);
0378     auto columnMode = m_column->columnMode();
0379     switch (columnMode) {
0380     case AbstractColumn::ColumnMode::Numeric: {
0381         auto* filter = static_cast<Double2StringFilter*>(m_column->outputFilter());
0382         ui.cbNumericFormat->setCurrentIndex(ui.cbNumericFormat->findData(filter->numericFormat()));
0383         break;
0384     }
0385     case AbstractColumn::ColumnMode::Integer:
0386     case AbstractColumn::ColumnMode::BigInt:
0387     case AbstractColumn::ColumnMode::Text:
0388         break;
0389     case AbstractColumn::ColumnMode::Month:
0390     case AbstractColumn::ColumnMode::Day:
0391     case AbstractColumn::ColumnMode::DateTime: {
0392         auto* filter = static_cast<DateTime2StringFilter*>(m_column->outputFilter());
0393         ui.cbDateTimeFormat->setCurrentText(filter->format());
0394         break;
0395     }
0396     }
0397 }
0398 
0399 void ColumnDock::columnPrecisionChanged() {
0400     const Lock lock(m_initializing);
0401     auto* filter = static_cast<Double2StringFilter*>(m_column->outputFilter());
0402     ui.sbPrecision->setValue(filter->numDigits());
0403 }
0404 
0405 void ColumnDock::columnPlotDesignationChanged(const AbstractColumn* col) {
0406     const Lock lock(m_initializing);
0407     ui.cbPlotDesignation->setCurrentIndex(int(col->plotDesignation()));
0408 }