File indexing completed on 2024-04-21 03:42:06

0001 /*
0002     KmPlot - a math. function plotter for the KDE-Desktop
0003 
0004     SPDX-FileCopyrightText: 1998-2002 Klaus-Dieter Möller <kd.moeller@t-online.de>
0005     SPDX-FileCopyrightText: 2004 Fredrik Edemar <f_edemar@linux.se>
0006     SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org>
0007 
0008     This file is part of the KDE Project.
0009     KmPlot is part of the KDE-EDU Project.
0010 
0011     SPDX-License-Identifier: GPL-2.0-or-later
0012 
0013 */
0014 
0015 #include "functioneditor.h"
0016 #include "equationedit.h"
0017 #include "kgradientdialog.h"
0018 #include "kmplotio.h"
0019 #include "maindlg.h"
0020 #include "parameterswidget.h"
0021 #include "ui_functioneditorwidget.h"
0022 #include "view.h"
0023 #include "xparser.h"
0024 
0025 #include <KColorButton>
0026 #include <KMessageBox>
0027 
0028 #include <QDebug>
0029 #include <QRadioButton>
0030 #include <QTimer>
0031 
0032 #include <assert.h>
0033 
0034 class FunctionEditorWidget : public QWidget, public Ui::FunctionEditorWidget
0035 {
0036 public:
0037     FunctionEditorWidget(QWidget *parent = nullptr)
0038         : QWidget(parent)
0039     {
0040         setupUi(this);
0041     }
0042 };
0043 
0044 // BEGIN class FunctionEditor
0045 FunctionEditor::FunctionEditor(QMenu *createNewPlotsMenu, QWidget *parent)
0046     : QDockWidget(i18n("Functions"), parent)
0047 {
0048     m_functionID = -1;
0049 
0050     // need a name for saving and restoring the position of this dock widget
0051     setObjectName(QStringLiteral("FunctionEditor"));
0052 
0053     setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
0054     setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
0055 
0056     for (int i = 0; i < 5; ++i) {
0057         m_saveTimer[i] = new QTimer(this);
0058         m_saveTimer[i]->setSingleShot(true);
0059     }
0060 
0061     m_syncFunctionListTimer = new QTimer(this);
0062     m_syncFunctionListTimer->setSingleShot(true);
0063 
0064     connect(m_saveTimer[Function::Cartesian], &QTimer::timeout, this, &FunctionEditor::saveCartesian);
0065     connect(m_saveTimer[Function::Polar], &QTimer::timeout, this, &FunctionEditor::savePolar);
0066     connect(m_saveTimer[Function::Parametric], &QTimer::timeout, this, &FunctionEditor::saveParametric);
0067     connect(m_saveTimer[Function::Implicit], &QTimer::timeout, this, &FunctionEditor::saveImplicit);
0068     connect(m_saveTimer[Function::Differential], &QTimer::timeout, this, &FunctionEditor::saveDifferential);
0069     connect(m_syncFunctionListTimer, &QTimer::timeout, this, &FunctionEditor::syncFunctionList);
0070 
0071     m_editor = new FunctionEditorWidget;
0072     m_functionList = m_editor->functionList;
0073 
0074     m_editor->createNewPlot->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0075     m_editor->deleteButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0076 
0077     // BEGIN initialize equation edits
0078     m_editor->cartesianEquation->setInputType(EquationEdit::Function);
0079     m_editor->cartesianEquation->setEquationType(Equation::Cartesian);
0080     m_editor->cartesianParameters->associateEquationEdit(m_editor->cartesianEquation);
0081 
0082     m_editor->polarEquation->setInputType(EquationEdit::Function);
0083     m_editor->polarEquation->setEquationType(Equation::Polar);
0084     m_editor->polarParameters->associateEquationEdit(m_editor->polarEquation);
0085 
0086     m_editor->parametricX->setInputType(EquationEdit::Function);
0087     m_editor->parametricX->setEquationType(Equation::ParametricX);
0088     m_editor->parametricParameters->associateEquationEdit(m_editor->parametricX);
0089 
0090     m_editor->parametricY->setInputType(EquationEdit::Function);
0091     m_editor->parametricY->setEquationType(Equation::ParametricY);
0092     m_editor->parametricParameters->associateEquationEdit(m_editor->parametricY);
0093 
0094     m_editor->implicitEquation->setInputType(EquationEdit::Function);
0095     m_editor->implicitEquation->setEquationType(Equation::Implicit);
0096 
0097     m_editor->differentialEquation->setInputType(EquationEdit::Function);
0098     m_editor->differentialEquation->setEquationType(Equation::Differential);
0099     m_editor->differentialParameters->associateEquationEdit(m_editor->differentialEquation);
0100     // END initialize equation edits
0101 
0102     for (unsigned i = 0; i < 5; ++i)
0103         m_editor->stackedWidget->widget(i)->layout()->setContentsMargins(0, 0, 0, 0);
0104 
0105     connect(m_editor->deleteButton, &QPushButton::clicked, this, &FunctionEditor::deleteCurrent);
0106     connect(m_functionList, &FunctionListWidget::currentItemChanged, this, &FunctionEditor::functionSelected);
0107     connect(m_functionList, &FunctionListWidget::itemClicked, this, &FunctionEditor::saveItem);
0108 
0109     // BEGIN connect up all editing widgets
0110 #define CONNECT_WIDGETS(name, signal)                                                                                                                          \
0111     {                                                                                                                                                          \
0112         QList<name *> widgets = m_editor->findChildren<name *>();                                                                                              \
0113         for (name * w : qAsConst(widgets))                                                                                                                     \
0114             connect(w, SIGNAL(signal), this, SLOT(save()));                                                                                                    \
0115     }
0116 
0117     CONNECT_WIDGETS(QLineEdit, editingFinished());
0118     CONNECT_WIDGETS(EquationEdit, editingFinished());
0119     CONNECT_WIDGETS(QCheckBox, stateChanged(int));
0120     CONNECT_WIDGETS(KColorButton, changed(const QColor &));
0121     CONNECT_WIDGETS(QRadioButton, toggled(bool));
0122     CONNECT_WIDGETS(QComboBox, currentIndexChanged(int));
0123     CONNECT_WIDGETS(ParametersWidget, parameterListChanged());
0124     CONNECT_WIDGETS(KGradientButton, gradientChanged(const QGradient &));
0125 
0126     connect(m_editor->initialConditions, &InitialConditionsEditor::dataChanged, this, &FunctionEditor::save);
0127     // END connect up all editing widgets
0128 
0129     connect(XParser::self(), &XParser::functionAdded, this, &FunctionEditor::functionsChanged);
0130     connect(XParser::self(), &XParser::functionRemoved, this, &FunctionEditor::functionsChanged);
0131 
0132     m_editor->createNewPlot->setMenu(createNewPlotsMenu);
0133 
0134     resetFunctionEditing();
0135     setWidget(m_editor);
0136 }
0137 
0138 FunctionEditor::~FunctionEditor()
0139 {
0140 }
0141 
0142 void FunctionEditor::deleteCurrent()
0143 {
0144     m_editor->initialConditions->init(nullptr);
0145 
0146     FunctionListItem *functionItem = static_cast<FunctionListItem *>(m_functionList->currentItem());
0147     if (!functionItem) {
0148         qDebug() << "Nothing currently selected!\n";
0149         return;
0150     }
0151 
0152     if (!XParser::self()->removeFunction(functionItem->function())) {
0153         qDebug() << "Couldn't delete function.\n";
0154         // couldn't delete it, as e.g. another function depends on it
0155         return;
0156     }
0157 
0158     MainDlg::self()->requestSaveCurrentState();
0159     View::self()->drawPlot();
0160 }
0161 
0162 void FunctionEditor::functionsChanged()
0163 {
0164     m_syncFunctionListTimer->start(0);
0165 }
0166 
0167 void FunctionEditor::syncFunctionList()
0168 {
0169     int oldFunctionCount = m_functionList->count();
0170 
0171     QListWidgetItem *currentItem = m_functionList->currentItem();
0172     QString currentText = currentItem ? currentItem->text() : QString();
0173 
0174     // build up a list of IDs that we have
0175     QMap<int, FunctionListItem *> currentIDs;
0176     QList<FunctionListItem *> currentFunctionItems;
0177     for (int row = 0; row < m_functionList->count(); ++row) {
0178         FunctionListItem *item = static_cast<FunctionListItem *>(m_functionList->item(row));
0179         currentFunctionItems << item;
0180         currentIDs[item->function()] = item;
0181 
0182         // also update what is displayed
0183         item->update();
0184     }
0185 
0186     FunctionListItem *toSelect = nullptr;
0187     int newFunctionCount = 0;
0188 
0189     for (QMap<int, Function *>::iterator it = XParser::self()->m_ufkt.begin(); it != XParser::self()->m_ufkt.end(); ++it) {
0190         Function *function = *it;
0191 
0192         if (currentIDs.contains(function->id())) {
0193             // already have the function
0194             currentFunctionItems.removeAll(currentIDs[function->id()]);
0195             currentIDs.remove(function->id());
0196             continue;
0197         }
0198 
0199         toSelect = new FunctionListItem(m_functionList, function->id());
0200         newFunctionCount++;
0201     }
0202 
0203     if (newFunctionCount != 1) {
0204         // only select a new functionlistitem if there was precisely one added
0205         toSelect = nullptr;
0206     }
0207 
0208     // Now, any IDs left in currentIDs are of functions that have been deleted
0209     for (FunctionListItem *item : qAsConst(currentFunctionItems)) {
0210         if (m_functionID == item->function())
0211             m_functionID = -1;
0212 
0213         delete m_functionList->takeItem(m_functionList->row(item));
0214     }
0215 
0216     m_functionList->sortItems();
0217 
0218     // Try and see if there is an item with the same text as was initially selected, if we have
0219     // the same number of functions
0220     if ((oldFunctionCount == m_functionList->count()) && !currentText.isEmpty()) {
0221         QList<QListWidgetItem *> matchedItems = m_functionList->findItems(currentText, Qt::MatchExactly);
0222         if (matchedItems.count() == 1)
0223             toSelect = static_cast<FunctionListItem *>(matchedItems.first());
0224     }
0225 
0226     if (toSelect)
0227         m_functionList->setCurrentItem(toSelect);
0228 
0229     if (m_functionList->count() == 0)
0230         resetFunctionEditing();
0231 }
0232 
0233 void FunctionEditor::setCurrentFunction(int functionID)
0234 {
0235     for (int row = 0; row < m_functionList->count(); ++row) {
0236         FunctionListItem *item = static_cast<FunctionListItem *>(m_functionList->item(row));
0237         if (item->function() != functionID)
0238             continue;
0239 
0240         m_functionList->setCurrentRow(row);
0241         return;
0242     }
0243 }
0244 
0245 void FunctionEditor::functionSelected(QListWidgetItem *item)
0246 {
0247     m_editor->deleteButton->setEnabled(item != nullptr);
0248     if (!item)
0249         return;
0250 
0251     // If there are any pending save events, then cancel them
0252     for (int i = 0; i < 5; ++i)
0253         m_saveTimer[i]->stop();
0254 
0255     FunctionListItem *functionItem = static_cast<FunctionListItem *>(item);
0256 
0257     m_functionID = functionItem->function();
0258     Function *f = XParser::self()->functionWithID(m_functionID);
0259     if (!f)
0260         return;
0261 
0262     switch (f->type()) {
0263     case Function::Cartesian:
0264         initFromCartesian();
0265         break;
0266 
0267     case Function::Polar:
0268         initFromPolar();
0269         break;
0270 
0271     case Function::Parametric:
0272         initFromParametric();
0273         break;
0274 
0275     case Function::Implicit:
0276         initFromImplicit();
0277         break;
0278 
0279     case Function::Differential:
0280         initFromDifferential();
0281     }
0282 
0283     functionItem->update();
0284 }
0285 
0286 void FunctionEditor::initFromCartesian()
0287 {
0288     Function *f = XParser::self()->functionWithID(m_functionID);
0289 
0290     if (!f) {
0291         qWarning() << "No f! (id=" << m_functionID << ")\n";
0292         return;
0293     }
0294 
0295     m_editor->cartesianEquation->setText(f->eq[0]->fstr());
0296     m_editor->cartesian_f0->init(f->plotAppearance(Function::Derivative0), Function::Cartesian);
0297     m_editor->cartesian_f1->init(f->plotAppearance(Function::Derivative1), Function::Cartesian);
0298     m_editor->cartesian_f2->init(f->plotAppearance(Function::Derivative2), Function::Cartesian);
0299     m_editor->cartesian_integral->init(f->plotAppearance(Function::Integral), Function::Cartesian);
0300 
0301     m_editor->showDerivative1->setChecked(f->plotAppearance(Function::Derivative1).visible);
0302     m_editor->showDerivative2->setChecked(f->plotAppearance(Function::Derivative2).visible);
0303 
0304     m_editor->cartesianCustomMin->setChecked(f->usecustomxmin);
0305     m_editor->cartesianMin->setText(f->dmin.expression());
0306 
0307     m_editor->cartesianCustomMax->setChecked(f->usecustomxmax);
0308     m_editor->cartesianMax->setText(f->dmax.expression());
0309 
0310     m_editor->cartesianParameters->init(f->m_parameters);
0311 
0312     m_editor->showIntegral->setChecked(f->plotAppearance(Function::Integral).visible);
0313     m_editor->integralStep->setText(f->eq[0]->differentialStates.step().expression());
0314 
0315     DifferentialState state = f->eq[0]->differentialStates[0];
0316     m_editor->txtInitX->setText(state.x0.expression());
0317     m_editor->txtInitY->setText(state.y0[0].expression());
0318 
0319     m_editor->stackedWidget->setCurrentIndex(0);
0320     m_editor->tabWidget->setCurrentIndex(0);
0321     m_editor->cartesianEquation->setFocus();
0322 }
0323 
0324 void FunctionEditor::initFromPolar()
0325 {
0326     Function *f = XParser::self()->functionWithID(m_functionID);
0327 
0328     if (!f)
0329         return;
0330 
0331     QString function = f->eq[0]->fstr();
0332     m_editor->polarEquation->setText(function);
0333     m_editor->polarMin->setText(f->dmin.expression());
0334     m_editor->polarMax->setText(f->dmax.expression());
0335     m_editor->polar_f0->init(f->plotAppearance(Function::Derivative0), Function::Polar);
0336 
0337     m_editor->polarParameters->init(f->m_parameters);
0338 
0339     m_editor->stackedWidget->setCurrentIndex(2);
0340     m_editor->polarEquation->setFocus();
0341 }
0342 
0343 void FunctionEditor::initFromParametric()
0344 {
0345     Function *f = XParser::self()->functionWithID(m_functionID);
0346 
0347     if (!f)
0348         return;
0349 
0350     m_editor->parametricX->setText(f->eq[0]->fstr());
0351     m_editor->parametricY->setText(f->eq[1]->fstr());
0352 
0353     m_editor->parametricMin->setText(f->dmin.expression());
0354     m_editor->parametricMax->setText(f->dmax.expression());
0355 
0356     m_editor->parametricParameters->init(f->m_parameters);
0357 
0358     m_editor->parametric_f0->init(f->plotAppearance(Function::Derivative0), Function::Parametric);
0359 
0360     m_editor->stackedWidget->setCurrentIndex(1);
0361     m_editor->parametricX->setFocus();
0362 }
0363 
0364 void FunctionEditor::initFromImplicit()
0365 {
0366     Function *f = XParser::self()->functionWithID(m_functionID);
0367 
0368     if (!f)
0369         return;
0370 
0371     QString name, expression;
0372     splitImplicitEquation(f->eq[0]->fstr(), &name, &expression);
0373 
0374     m_editor->implicitEquation->setValidatePrefix(name + '=');
0375 
0376     m_editor->implicitName->setText(name);
0377     m_editor->implicitEquation->setText(expression);
0378     m_editor->implicit_f0->init(f->plotAppearance(Function::Derivative0), Function::Implicit);
0379 
0380     m_editor->implicitParameters->init(f->m_parameters);
0381 
0382     m_editor->stackedWidget->setCurrentIndex(3);
0383     m_editor->implicitEquation->setFocus();
0384 }
0385 
0386 void FunctionEditor::initFromDifferential()
0387 {
0388     Function *f = XParser::self()->functionWithID(m_functionID);
0389 
0390     if (!f)
0391         return;
0392 
0393     m_editor->differentialEquation->setText(f->eq[0]->fstr());
0394     m_editor->differentialStep->setText(f->eq[0]->differentialStates.step().expression());
0395 
0396     m_editor->differential_f0->init(f->plotAppearance(Function::Derivative0), Function::Differential);
0397     m_editor->differentialParameters->init(f->m_parameters);
0398     m_editor->initialConditions->init(f);
0399 
0400     m_editor->differentialTabWidget->setCurrentIndex(0);
0401     m_editor->stackedWidget->setCurrentIndex(4);
0402     m_editor->differentialEquation->setFocus();
0403 }
0404 
0405 void FunctionEditor::splitImplicitEquation(const QString &equation, QString *name, QString *expression)
0406 {
0407     int equalsPos = equation.indexOf('=');
0408     assert(equalsPos >= 0);
0409     *name = equation.left(equalsPos).trimmed();
0410     *expression = equation.right(equation.length() - equalsPos - 1).trimmed();
0411 }
0412 
0413 void FunctionEditor::resetFunctionEditing()
0414 {
0415     m_functionID = -1;
0416 
0417     // page 5 is an empty page
0418     m_editor->stackedWidget->setCurrentIndex(5);
0419 
0420     // assume that if there are functions in the list, then one will be selected
0421     m_editor->deleteButton->setEnabled(m_functionList->count() != 0);
0422 }
0423 
0424 void FunctionEditor::createCartesian()
0425 {
0426     QString name;
0427     if (Settings::defaultEquationForm() == Settings::EnumDefaultEquationForm::Function)
0428         name = XParser::self()->findFunctionName(QStringLiteral("f"), -1) + "(x)";
0429     else
0430         name = 'y';
0431 
0432     createFunction(name + " = 0", QString(), Function::Cartesian);
0433 }
0434 
0435 void FunctionEditor::createParametric()
0436 {
0437     QString name =
0438         XParser::self()->findFunctionName(QStringLiteral("f"), -1, QStringList() << QStringLiteral("%1") << QStringLiteral("%1_x") << QStringLiteral("%1_y"));
0439 
0440     QString name_x, name_y;
0441 
0442     if (Settings::defaultEquationForm() == Settings::EnumDefaultEquationForm::Function) {
0443         name_x = QStringLiteral("%1_x(t)").arg(name);
0444         name_y = QStringLiteral("%1_y(t)").arg(name);
0445     } else {
0446         name_x = 'x';
0447         name_y = 'y';
0448     }
0449 
0450     createFunction(name_x + " = 0", name_y + " = 0", Function::Parametric);
0451 }
0452 
0453 void FunctionEditor::createPolar()
0454 {
0455     QString name;
0456     if (Settings::defaultEquationForm() == Settings::EnumDefaultEquationForm::Function)
0457         name = XParser::self()->findFunctionName(QStringLiteral("f"), -1) + "(x)";
0458     else
0459         name = 'r';
0460 
0461     createFunction(name + " = 0", QString(), Function::Polar);
0462 }
0463 
0464 void FunctionEditor::createImplicit()
0465 {
0466     QString name = XParser::self()->findFunctionName(QStringLiteral("f"), -1);
0467     if (Settings::defaultEquationForm() == Settings::EnumDefaultEquationForm::Function)
0468         name += QLatin1String("(x,y)");
0469 
0470     createFunction(name + " = y² = x³ − x + 1", QString(), Function::Implicit);
0471 }
0472 
0473 void FunctionEditor::createDifferential()
0474 {
0475     QString name;
0476     if (Settings::defaultEquationForm() == Settings::EnumDefaultEquationForm::Function)
0477         name = QStringLiteral("%1''(x) = -%1").arg(XParser::self()->findFunctionName(QStringLiteral("f"), -1));
0478     else
0479         name = QLatin1String("y'' = -y");
0480 
0481     createFunction(name, QString(), Function::Differential);
0482 }
0483 
0484 void FunctionEditor::createFunction(const QString &eq0, const QString &eq1, Function::Type type)
0485 {
0486     m_functionID = XParser::self()->Parser::addFunction(eq0, eq1, type);
0487     assert(m_functionID != -1);
0488     MainDlg::self()->requestSaveCurrentState();
0489 }
0490 
0491 void FunctionEditor::save()
0492 {
0493     Function *f = XParser::self()->functionWithID(m_functionID);
0494     if (!f)
0495         return;
0496 
0497     m_saveTimer[f->type()]->start(0);
0498 }
0499 
0500 // TODO This should be a part of a model. The proper model should be created later.
0501 void FunctionEditor::saveItem(QListWidgetItem *item)
0502 {
0503     if (item != m_functionList->currentItem()) {
0504         m_functionList->setCurrentItem(item);
0505         if (item->checkState() == Qt::Checked) {
0506             item->setCheckState(Qt::Unchecked);
0507         } else {
0508             item->setCheckState(Qt::Checked);
0509         }
0510     }
0511 
0512     save();
0513 }
0514 
0515 void FunctionEditor::saveCartesian()
0516 {
0517     FunctionListItem *functionListItem = static_cast<FunctionListItem *>(m_functionList->currentItem());
0518     if (!functionListItem)
0519         return;
0520 
0521     QString f_str(m_editor->cartesianEquation->text());
0522     XParser::self()->fixFunctionName(f_str, Equation::Cartesian, m_functionID);
0523 
0524     Function tempFunction(Function::Cartesian);
0525     tempFunction.setId(m_functionID);
0526 
0527     tempFunction.usecustomxmin = m_editor->cartesianCustomMin->isChecked();
0528     if (!tempFunction.dmin.updateExpression(m_editor->cartesianMin->text()))
0529         return;
0530 
0531     tempFunction.usecustomxmax = m_editor->cartesianCustomMax->isChecked();
0532     if (!tempFunction.dmax.updateExpression(m_editor->cartesianMax->text()))
0533         return;
0534 
0535     tempFunction.plotAppearance(Function::Derivative0) = m_editor->cartesian_f0->plot((functionListItem->checkState() == Qt::Checked));
0536     tempFunction.plotAppearance(Function::Derivative1) = m_editor->cartesian_f1->plot(m_editor->showDerivative1->isChecked());
0537     tempFunction.plotAppearance(Function::Derivative2) = m_editor->cartesian_f2->plot(m_editor->showDerivative2->isChecked());
0538     tempFunction.plotAppearance(Function::Integral) = m_editor->cartesian_integral->plot(m_editor->showIntegral->isChecked());
0539 
0540     DifferentialState *state = &tempFunction.eq[0]->differentialStates[0];
0541     state->setOrder(1);
0542     state->x0.updateExpression(m_editor->txtInitX->text());
0543     state->y0[0].updateExpression(m_editor->txtInitY->text());
0544 
0545     if (!tempFunction.eq[0]->differentialStates.setStep(m_editor->integralStep->text()))
0546         return;
0547     tempFunction.m_parameters = m_editor->cartesianParameters->parameterSettings();
0548 
0549     if (!tempFunction.eq[0]->setFstr(f_str))
0550         return;
0551 
0552     saveFunction(&tempFunction);
0553 }
0554 
0555 void FunctionEditor::savePolar()
0556 {
0557     FunctionListItem *functionListItem = static_cast<FunctionListItem *>(m_functionList->currentItem());
0558     if (!functionListItem)
0559         return;
0560 
0561     QString f_str = m_editor->polarEquation->text();
0562 
0563     XParser::self()->fixFunctionName(f_str, Equation::Polar, m_functionID);
0564     Function tempFunction(Function::Polar); // all settings are saved here until we know that no errors have appeared
0565     tempFunction.setId(m_functionID);
0566 
0567     if (!tempFunction.dmin.updateExpression(m_editor->polarMin->text()))
0568         return;
0569     if (!tempFunction.dmax.updateExpression(m_editor->polarMax->text()))
0570         return;
0571 
0572     tempFunction.m_parameters = m_editor->polarParameters->parameterSettings();
0573     tempFunction.plotAppearance(Function::Derivative0) = m_editor->polar_f0->plot((functionListItem->checkState() == Qt::Checked));
0574 
0575     if (!tempFunction.eq[0]->setFstr(f_str))
0576         return;
0577 
0578     saveFunction(&tempFunction);
0579 }
0580 
0581 void FunctionEditor::saveParametric()
0582 {
0583     FunctionListItem *functionListItem = static_cast<FunctionListItem *>(m_functionList->currentItem());
0584     if (!functionListItem)
0585         return;
0586 
0587     Function tempFunction(Function::Parametric);
0588     tempFunction.setId(m_functionID);
0589 
0590     QString f_str = m_editor->parametricX->text();
0591     XParser::self()->fixFunctionName(f_str, Equation::ParametricX, m_functionID);
0592     if (!tempFunction.eq[0]->setFstr(f_str))
0593         return;
0594 
0595     f_str = m_editor->parametricY->text();
0596     XParser::self()->fixFunctionName(f_str, Equation::ParametricY, m_functionID);
0597     if (!tempFunction.eq[1]->setFstr(f_str))
0598         return;
0599 
0600     if (!tempFunction.dmin.updateExpression(m_editor->parametricMin->text()))
0601         return;
0602 
0603     if (!tempFunction.dmax.updateExpression(m_editor->parametricMax->text()))
0604         return;
0605 
0606     tempFunction.m_parameters = m_editor->parametricParameters->parameterSettings();
0607     tempFunction.plotAppearance(Function::Derivative0) = m_editor->parametric_f0->plot((functionListItem->checkState() == Qt::Checked));
0608 
0609     saveFunction(&tempFunction);
0610 }
0611 
0612 void FunctionEditor::saveImplicit()
0613 {
0614     FunctionListItem *functionListItem = static_cast<FunctionListItem *>(m_functionList->currentItem());
0615     if (!functionListItem)
0616         return;
0617 
0618     // find a name not already used
0619     if (m_editor->implicitName->text().isEmpty()) {
0620         QString fname;
0621         XParser::self()->fixFunctionName(fname, Equation::Implicit, m_functionID);
0622         int const pos = fname.indexOf('(');
0623         m_editor->implicitName->setText(fname.mid(1, pos - 1));
0624     }
0625 
0626     QString prefix = m_editor->implicitName->text() + " = ";
0627     QString f_str = prefix + m_editor->implicitEquation->text();
0628     m_editor->implicitEquation->setValidatePrefix(prefix);
0629 
0630     Function tempFunction(Function::Implicit); // all settings are saved here until we know that no errors have appeared
0631     tempFunction.setId(m_functionID);
0632 
0633     tempFunction.m_parameters = m_editor->implicitParameters->parameterSettings();
0634     tempFunction.plotAppearance(Function::Derivative0) = m_editor->implicit_f0->plot((functionListItem->checkState() == Qt::Checked));
0635 
0636     if (!tempFunction.eq[0]->setFstr(f_str))
0637         return;
0638 
0639     saveFunction(&tempFunction);
0640 }
0641 
0642 void FunctionEditor::saveDifferential()
0643 {
0644     FunctionListItem *functionListItem = static_cast<FunctionListItem *>(m_functionList->currentItem());
0645     if (!functionListItem)
0646         return;
0647 
0648     Function tempFunction(Function::Differential); // all settings are saved here until we know that no errors have appeared
0649     tempFunction.setId(m_functionID);
0650 
0651     QString f_str = m_editor->differentialEquation->text();
0652     if (!tempFunction.eq[0]->setFstr(f_str))
0653         return;
0654 
0655     tempFunction.m_parameters = m_editor->differentialParameters->parameterSettings();
0656     tempFunction.plotAppearance(Function::Derivative0) = m_editor->differential_f0->plot((functionListItem->checkState() == Qt::Checked));
0657 
0658     m_editor->initialConditions->setOrder(tempFunction.eq[0]->order());
0659     tempFunction.eq[0]->differentialStates = *m_editor->initialConditions->differentialStates();
0660     if (!tempFunction.eq[0]->differentialStates.setStep(m_editor->differentialStep->text()))
0661         return;
0662 
0663     saveFunction(&tempFunction);
0664 }
0665 
0666 void FunctionEditor::saveFunction(Function *tempFunction)
0667 {
0668     FunctionListItem *functionListItem = static_cast<FunctionListItem *>(m_functionList->currentItem());
0669     Function *f = XParser::self()->functionWithID(m_functionID);
0670     if (!f || !functionListItem)
0671         return;
0672 
0673     for (Equation *eq : qAsConst(f->eq))
0674         eq->differentialStates.resetToInitial();
0675 
0676     // save all settings in the function now when we know no errors have appeared
0677     bool changed = f->copyFrom(*tempFunction);
0678     if (!changed)
0679         return;
0680 
0681     qDebug() << "Changed\n";
0682 
0683     if (f->eq[0]->looksLikeFunction())
0684         Settings::setDefaultEquationForm(Settings::EnumDefaultEquationForm::Function);
0685     else
0686         Settings::setDefaultEquationForm(Settings::EnumDefaultEquationForm::Implicit);
0687     Settings::self()->save();
0688 
0689     MainDlg::self()->requestSaveCurrentState();
0690     functionListItem->update();
0691     View::self()->drawPlot();
0692 }
0693 // END class FunctionEditor
0694 
0695 // BEGIN class FunctionListWidget
0696 FunctionListWidget::FunctionListWidget(QWidget *parent)
0697     : QListWidget(parent)
0698 {
0699     setAcceptDrops(true);
0700     setDragEnabled(true);
0701     show();
0702 }
0703 
0704 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0705 QMimeData *FunctionListWidget::mimeData(const QList<QListWidgetItem *> items) const
0706 #else
0707 QMimeData *FunctionListWidget::mimeData(const QList<QListWidgetItem *> &items) const
0708 #endif
0709 {
0710     QDomDocument doc(QStringLiteral("kmpdoc"));
0711     QDomElement root = doc.createElement(QStringLiteral("kmpdoc"));
0712     doc.appendChild(root);
0713 
0714     KmPlotIO io;
0715 
0716     for (QListWidgetItem *item : qAsConst(items)) {
0717         int f = static_cast<FunctionListItem *>(item)->function();
0718 
0719         if (Function *function = XParser::self()->functionWithID(f))
0720             io.addFunction(doc, root, function);
0721     }
0722 
0723     QMimeData *md = new QMimeData;
0724     md->setData(QStringLiteral("text/kmplot"), doc.toByteArray());
0725 
0726     return md;
0727 }
0728 
0729 QStringList FunctionListWidget::mimeTypes() const
0730 {
0731     QStringList mt;
0732     mt << QStringLiteral("text/kmplot");
0733     return mt;
0734 }
0735 
0736 void FunctionListWidget::dragEnterEvent(QDragEnterEvent *event)
0737 {
0738     const QMimeData *md = event->mimeData();
0739     if (md->hasFormat(QStringLiteral("text/kmplot")))
0740         event->acceptProposedAction();
0741 }
0742 
0743 void FunctionListWidget::dropEvent(QDropEvent *event)
0744 {
0745     const QMimeData *md = event->mimeData();
0746 
0747     QDomDocument doc(QStringLiteral("kmpdoc"));
0748     doc.setContent(md->data(QStringLiteral("text/kmplot")));
0749     QDomElement element = doc.documentElement();
0750 
0751     KmPlotIO io;
0752 
0753     for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
0754         if (n.nodeName() == QLatin1String("function"))
0755             io.parseFunction(n.toElement(), true);
0756         else
0757             qWarning() << "Unexpected node with name " << n.nodeName();
0758     }
0759 }
0760 // END class FunctionListWidget
0761 
0762 // BEGIN class FunctionListItem
0763 FunctionListItem::FunctionListItem(QListWidget *parent, int function)
0764     : QListWidgetItem(parent)
0765 {
0766     m_function = function;
0767     assert(m_function != -1);
0768     update();
0769 }
0770 
0771 void FunctionListItem::update()
0772 {
0773     Function *f = XParser::self()->functionWithID(m_function);
0774 
0775     if (!f) {
0776         // The function was probably deleted
0777         return;
0778     }
0779 
0780     setText(f->name());
0781     setCheckState(f->plotAppearance(Function::Derivative0).visible ? Qt::Checked : Qt::Unchecked);
0782     setForeground(f->plotAppearance(Function::Derivative0).color);
0783 }
0784 // END class FunctionListItem
0785 
0786 #include "moc_functioneditor.cpp"