File indexing completed on 2024-04-21 03:41:34
0001 /************************************************************************************* 0002 * Copyright (C) 2007-2009 by Aleix Pol <aleixpol@kde.org> * 0003 * * 0004 * This program is free software; you can redistribute it and/or * 0005 * modify it under the terms of the GNU General Public License * 0006 * as published by the Free Software Foundation; either version 2 * 0007 * of the License, or (at your option) any later version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, * 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0012 * GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License * 0015 * along with this program; if not, write to the Free Software * 0016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 0017 *************************************************************************************/ 0018 0019 #include "functionedit.h" 0020 0021 #include <QHBoxLayout> 0022 #include <QVBoxLayout> 0023 0024 #include <KColorScheme> 0025 0026 #include <analitza/analyzer.h> 0027 #include <analitza/expression.h> 0028 #include <analitza/value.h> 0029 #include <analitza/variables.h> 0030 #include <analitzagui/algebrahighlighter.h> 0031 #include <analitzagui/expressionedit.h> 0032 #include <analitzagui/plotsview2d.h> 0033 #include <analitzaplot/planecurve.h> 0034 #include <analitzaplot/plotsfactory.h> 0035 #include <analitzaplot/plotsmodel.h> 0036 #include <klocalizedstring.h> 0037 0038 using namespace Analitza; 0039 0040 namespace 0041 { 0042 static const int resolution = 200; 0043 } 0044 0045 FunctionEdit::FunctionEdit(QWidget *parent) 0046 : QWidget(parent) 0047 , m_calcUplimit(0) 0048 , m_calcDownlimit(0) 0049 , m_modmode(false) 0050 { 0051 setWindowTitle(i18n("Add/Edit a function")); 0052 0053 QVBoxLayout *topLayout = new QVBoxLayout(this); 0054 topLayout->setContentsMargins(2, 2, 2, 2); 0055 topLayout->setSpacing(5); 0056 0057 m_name = new QLineEdit(this); 0058 0059 m_func = new ExpressionEdit(this); 0060 m_func->setExamples(PlotsFactory::self()->examples(Dim2D)); 0061 m_func->setAns(QStringLiteral("x")); 0062 connect(m_func, &QPlainTextEdit::textChanged, this, &FunctionEdit::edit); 0063 connect(m_func, &ExpressionEdit::returnPressed, this, &FunctionEdit::ok); 0064 0065 m_valid = new QLabel(this); 0066 m_valid->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); 0067 0068 QPalette p = palette(); 0069 p.setColor(QPalette::Active, QPalette::Base, Qt::white); 0070 m_valid->setPalette(p); 0071 0072 m_validIcon = new QLabel(this); 0073 m_validIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0074 QLayout *validLayout = new QHBoxLayout; 0075 validLayout->addWidget(m_validIcon); 0076 validLayout->addWidget(m_valid); 0077 0078 m_color = new KColorCombo(this); 0079 m_color->setColor(QColor(0, 150, 0)); 0080 connect(m_color, SIGNAL(currentIndexChanged(int)), this, SLOT(colorChange(int))); 0081 0082 m_funcsModel = new PlotsModel(this); 0083 m_funcsModel->setResolution(resolution); 0084 0085 m_viewTabs = new QTabWidget(this); 0086 0087 m_graph = new PlotsView2D(m_viewTabs); 0088 m_graph->setModel(m_funcsModel); 0089 m_graph->setViewport(QRectF(QPointF(-10.0, 10.0), QSizeF(20.0, -20.0))); 0090 m_graph->setFocusPolicy(Qt::NoFocus); 0091 m_graph->setMouseTracking(false); 0092 m_graph->setFramed(true); 0093 m_graph->setReadOnly(true); 0094 m_graph->setTicksShown(Qt::Orientation(0)); 0095 0096 m_viewTabs->addTab(m_graph, QIcon::fromTheme(QStringLiteral("document-preview")), i18n("Preview")); 0097 QWidget *options = new QWidget(m_viewTabs); 0098 options->setLayout(new QVBoxLayout); 0099 m_uplimit = new ExpressionEdit(options); 0100 m_downlimit = new ExpressionEdit(options); 0101 m_uplimit->setText(QStringLiteral("2*pi")); 0102 m_downlimit->setText(QStringLiteral("0")); 0103 options->layout()->addWidget(new QLabel(i18n("From:"), options)); 0104 options->layout()->addWidget(m_downlimit); 0105 options->layout()->addWidget(new QLabel(i18n("To:"), options)); 0106 options->layout()->addWidget(m_uplimit); 0107 options->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); 0108 m_viewTabs->addTab(options, QIcon::fromTheme(QStringLiteral("configure")), i18n("Options")); 0109 connect(m_uplimit, &QPlainTextEdit::textChanged, this, &FunctionEdit::updateUplimit); 0110 connect(m_downlimit, &QPlainTextEdit::textChanged, this, &FunctionEdit::updateDownlimit); 0111 0112 QHBoxLayout *m_butts = new QHBoxLayout; 0113 m_ok = new QPushButton(i18n("OK"), this); 0114 m_ok->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok"))); 0115 m_remove = new QPushButton(i18nc("@action:button", "Remove"), this); 0116 m_remove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0117 connect(m_ok, &QAbstractButton::clicked, this, &FunctionEdit::ok); 0118 connect(m_remove, &QAbstractButton::clicked, this, &FunctionEdit::removeEditingPlot); 0119 0120 topLayout->addWidget(m_name); 0121 topLayout->addWidget(m_func); 0122 topLayout->addWidget(m_color); 0123 topLayout->addLayout(validLayout); 0124 topLayout->addWidget(m_viewTabs); 0125 topLayout->addLayout(m_butts); 0126 0127 m_name->hide(); // FIXME: Remove this when the name has any sense 0128 0129 m_butts->addWidget(m_ok); 0130 m_butts->addWidget(m_remove); 0131 0132 m_func->setFocus(); 0133 m_ok->setEnabled(false); 0134 0135 QFont errorFont = m_valid->font(); 0136 errorFont.setBold(true); 0137 m_valid->setFont(errorFont); 0138 } 0139 0140 FunctionEdit::~FunctionEdit() 0141 { 0142 } 0143 0144 void FunctionEdit::clear() 0145 { 0146 m_func->setText(QString()); 0147 m_funcsModel->clear(); 0148 edit(); 0149 } 0150 0151 void FunctionEdit::setFunction(const QString &newText) 0152 { 0153 m_func->setText(newText); 0154 m_func->document()->setModified(true); 0155 } 0156 0157 void FunctionEdit::setColor(const QColor &newColor) 0158 { 0159 m_color->setColor(newColor); 0160 if (m_funcsModel->rowCount() > 0) 0161 m_funcsModel->setData(m_funcsModel->index(0), newColor); 0162 } 0163 0164 void FunctionEdit::colorChange(int) 0165 { 0166 setColor(m_color->color()); 0167 } 0168 0169 static double calcExp(const Analitza::Expression &exp, const QSharedPointer<Analitza::Variables> &v, bool *corr) 0170 { 0171 Q_ASSERT(exp.isCorrect()); 0172 Analitza::Analyzer d(v); 0173 d.setExpression(exp); 0174 Analitza::Expression r = d.calculate(); 0175 0176 *corr = r.isCorrect() && r.isReal(); 0177 0178 if (*corr) 0179 return r.toReal().value(); 0180 else 0181 return 0.; 0182 } 0183 0184 void FunctionEdit::updateUplimit() 0185 { 0186 bool corr = m_uplimit->isCorrect(); 0187 if (corr) { 0188 Analitza::Expression e = m_uplimit->expression(); 0189 m_calcUplimit = calcExp(e, m_vars, &corr); 0190 m_uplimit->setCorrect(corr); 0191 if (corr) 0192 edit(); 0193 } 0194 } 0195 0196 void FunctionEdit::updateDownlimit() 0197 { 0198 bool corr = m_downlimit->isCorrect(); 0199 if (corr) { 0200 Analitza::Expression e = m_downlimit->expression(); 0201 m_calcDownlimit = calcExp(e, m_vars, &corr); 0202 m_downlimit->setCorrect(corr); 0203 if (corr) 0204 edit(); 0205 } 0206 } 0207 0208 void FunctionEdit::setState(const QString &text, bool negative) 0209 { 0210 QFontMetrics fm(m_valid->font()); 0211 m_valid->setText(fm.elidedText(text, Qt::ElideRight, m_valid->width())); 0212 m_valid->setToolTip(text); 0213 0214 KColorScheme scheme(QPalette::Normal); 0215 KColorScheme::ForegroundRole role = negative ? KColorScheme::NegativeText : KColorScheme::PositiveText; 0216 0217 QPalette p = m_valid->palette(); 0218 p.setColor(foregroundRole(), scheme.foreground(role).color()); 0219 m_valid->setPalette(p); 0220 0221 if (negative) 0222 m_validIcon->setPixmap(QIcon::fromTheme(QStringLiteral("flag-red")).pixmap(QSize(16, 16))); 0223 else 0224 m_validIcon->setPixmap(QIcon::fromTheme(QStringLiteral("flag-green")).pixmap(QSize(16, 16))); 0225 } 0226 0227 /// Let's see if the exp is correct 0228 void FunctionEdit::edit() 0229 { 0230 if (m_func->text().isEmpty()) { 0231 m_func->setCorrect(true); 0232 m_ok->setEnabled(false); 0233 m_valid->clear(); 0234 m_valid->setToolTip(QString()); 0235 m_validIcon->setPixmap(QIcon::fromTheme(QStringLiteral("flag-yellow")).pixmap(QSize(16, 16))); 0236 0237 m_funcsModel->clear(); 0238 m_graph->forceRepaint(); 0239 return; 0240 } 0241 0242 if (!m_uplimit->isCorrect() || !m_downlimit->isCorrect()) { 0243 setState(i18n("The options you specified are not correct"), true); 0244 return; 0245 } 0246 0247 if (m_calcDownlimit > m_calcUplimit) { 0248 setState(i18n("Downlimit cannot be greater than uplimit"), true); 0249 return; 0250 } 0251 bool added = false; 0252 0253 PlaneCurve *f = 0; 0254 PlotBuilder req = PlotsFactory::self()->requestPlot(expression(), Dim2D, m_vars); 0255 if (req.canDraw()) 0256 f = createFunction(); 0257 0258 if (f && f->isCorrect()) 0259 f->update(QRect(-10, 10, 20, -20)); 0260 0261 m_funcsModel->clear(); 0262 if (f && f->isCorrect()) { 0263 m_funcsModel->addPlot(f); 0264 added = true; 0265 setState(QStringLiteral("%1:=%2").arg(m_name->text(), f->expression().toString()), false); 0266 } else { 0267 QStringList errors = req.errors(); 0268 if (f) 0269 errors = f->errors(); 0270 Q_ASSERT(!errors.isEmpty()); 0271 0272 setState(errors.first(), true); 0273 m_valid->setToolTip(errors.join(QStringLiteral("<br />"))); 0274 delete f; 0275 } 0276 m_func->setCorrect(added); 0277 m_ok->setEnabled(added); 0278 } 0279 0280 void FunctionEdit::ok() 0281 { 0282 if (m_ok->isEnabled()) 0283 Q_EMIT accept(); 0284 } 0285 0286 void FunctionEdit::focusInEvent(QFocusEvent *) 0287 { 0288 m_func->setFocus(); 0289 } 0290 0291 PlaneCurve *FunctionEdit::createFunction() const 0292 { 0293 PlotBuilder req = PlotsFactory::self()->requestPlot(expression(), Dim2D, m_vars); 0294 PlaneCurve *curve = static_cast<PlaneCurve *>(req.create(color(), name())); 0295 curve->setResolution(resolution); 0296 if (m_calcUplimit != m_calcDownlimit) { 0297 const auto parameters = curve->parameters(); 0298 for (const QString &var : parameters) 0299 curve->setInterval(var, m_calcUplimit, m_calcDownlimit); 0300 } 0301 return curve; 0302 } 0303 0304 Analitza::Expression FunctionEdit::expression() const 0305 { 0306 return m_func->expression(); 0307 } 0308 0309 void FunctionEdit::setOptionsShown(bool shown) 0310 { 0311 m_viewTabs->setVisible(shown); 0312 } 0313 0314 void FunctionEdit::resizeEvent(QResizeEvent *) 0315 { 0316 QFontMetrics fm(m_valid->font()); 0317 m_valid->setText(fm.elidedText(m_valid->toolTip(), Qt::ElideRight, m_valid->width())); 0318 } 0319 0320 void FunctionEdit::setEditing(bool m) 0321 { 0322 m_modmode = m; 0323 m_remove->setVisible(m); 0324 }