Warning, file /education/kmplot/kmplot/functioneditor.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 = 0) 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(0); 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 = 0l; 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 = 0l; 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 != 0); 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