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

0001 /*
0002     KmPlot - a math. function plotter for the KDE-Desktop
0003 
0004     SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org>
0005 
0006     This file is part of the KDE Project.
0007     KmPlot is part of the KDE-EDU Project.
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 
0011 */
0012 
0013 #include "equationedit.h"
0014 #include "xparser.h"
0015 
0016 #include <QHBoxLayout>
0017 #include <QIcon>
0018 #include <QPushButton>
0019 
0020 #include <assert.h>
0021 
0022 #include "equationeditor.h"
0023 #include "equationeditorwidget.h"
0024 #include "equationeditwidget.h"
0025 #include "equationhighlighter.h"
0026 
0027 CharMap EquationEdit::m_replaceMap;
0028 
0029 EquationEdit::EquationEdit(QWidget *parent)
0030     : QWidget(parent)
0031 {
0032     m_cleaningText = false;
0033     m_settingText = false;
0034     m_forcingRehighlight = false;
0035     m_inputType = Expression;
0036 
0037     m_equationEditWidget = new EquationEditWidget(this);
0038     m_highlighter = new EquationHighlighter(this);
0039     m_equation = new Equation(Equation::Cartesian, nullptr);
0040     m_editButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-properties")), nullptr, this);
0041     setFocusProxy(m_equationEditWidget);
0042 
0043     connect(m_equationEditWidget, &EquationEditWidget::textChanged, this, &EquationEdit::slotTextChanged);
0044     connect(m_editButton, &QPushButton::clicked, this, &EquationEdit::invokeEquationEditor);
0045     connect(m_equationEditWidget, &EquationEditWidget::cursorPositionChanged, this, &EquationEdit::reHighlight);
0046 
0047     QHBoxLayout *layout = new QHBoxLayout(this);
0048     layout->setContentsMargins(0, 0, 0, 0);
0049     layout->addWidget(m_equationEditWidget);
0050     layout->addWidget(m_editButton);
0051 }
0052 
0053 void EquationEdit::setTabChain(QWidget *next)
0054 {
0055     QWidget::setTabOrder(m_equationEditWidget, m_editButton);
0056     QWidget::setTabOrder(m_editButton, next);
0057 }
0058 
0059 void EquationEdit::setEquationType(Equation::Type type)
0060 {
0061     delete m_equation;
0062     m_equation = new Equation(type, nullptr);
0063 }
0064 
0065 void EquationEdit::showEditButton(bool show)
0066 {
0067     m_editButton->setVisible(show);
0068 }
0069 
0070 void EquationEdit::reHighlight()
0071 {
0072     if (m_forcingRehighlight)
0073         return;
0074     m_forcingRehighlight = true;
0075 
0076     m_highlighter->setDocument(nullptr);
0077     m_highlighter->setDocument(m_equationEditWidget->document());
0078 
0079     m_forcingRehighlight = false;
0080 }
0081 
0082 void EquationEdit::invokeEquationEditor()
0083 {
0084     QPointer<EquationEditor> edit = new EquationEditor(this);
0085     edit->edit()->setInputType(m_inputType);
0086     edit->edit()->setEquationType(m_equation->type());
0087     edit->edit()->setValidatePrefix(m_validatePrefix);
0088     edit->edit()->setText(text());
0089 
0090     edit->exec();
0091 
0092     setText(edit->text());
0093     edit->deleteLater();
0094     emit editingFinished();
0095 }
0096 
0097 void EquationEdit::setInputType(InputType type)
0098 {
0099     m_inputType = type;
0100 }
0101 
0102 double EquationEdit::value(bool *ok)
0103 {
0104     assert(m_inputType == Expression); // Can't really get a value of a function as that requires an input
0105 
0106     Parser::Error error;
0107     double value = XParser::self()->eval(text(), &error);
0108 
0109     if (ok)
0110         *ok = (error == Parser::ParseSuccess);
0111 
0112     return value;
0113 }
0114 
0115 void EquationEdit::slotTextChanged()
0116 {
0117     if (m_forcingRehighlight)
0118         return;
0119 
0120     // BEGIN tidy up mathematical characters
0121     if (m_cleaningText)
0122         return;
0123     m_cleaningText = true;
0124 
0125     QTextDocument *doc = m_equationEditWidget->document();
0126 
0127     if (m_replaceMap.isEmpty()) {
0128         m_replaceMap['*'] = QChar(0x2219);
0129         m_replaceMap['-'] = MinusSymbol;
0130         m_replaceMap['|'] = AbsSymbol;
0131     }
0132 
0133     QTextCursor cursor;
0134     for (CharMap::iterator i = m_replaceMap.begin(); i != m_replaceMap.end(); ++i) {
0135         int at = 0;
0136         while (!(cursor = doc->find(i.key(), at)).isNull()) {
0137             cursor.joinPreviousEditBlock();
0138             at = cursor.position() + 1;
0139             cursor.deleteChar();
0140             cursor.insertText(i.value());
0141             cursor.endEditBlock();
0142         }
0143     }
0144 
0145     m_cleaningText = false;
0146     // END tidy up mathematical characters
0147 
0148     emit textChanged(text());
0149     if (!m_settingText)
0150         emit textEdited(text());
0151 }
0152 
0153 void EquationEdit::checkTextValidity()
0154 {
0155     QString text = m_validatePrefix + EquationEdit::text();
0156 
0157     Parser::Error error;
0158     int intError, errorPosition;
0159 
0160     if (m_inputType == Function) {
0161         m_equation->setFstr(text, &intError, &errorPosition);
0162         error = (Parser::Error)intError;
0163     } else
0164         XParser::self()->eval(text, &error, &errorPosition);
0165 
0166     if (error == Parser::ParseSuccess)
0167         setError(QString(), -1);
0168     else
0169         setError(XParser::self()->errorString(error), errorPosition - m_validatePrefix.length());
0170 }
0171 
0172 void EquationEdit::setError(const QString &message, int position)
0173 {
0174     m_equationEditWidget->setToolTip(message);
0175     m_highlighter->setErrorPosition(position);
0176 }
0177 
0178 void EquationEdit::setText(const QString &text)
0179 {
0180     m_settingText = true;
0181     m_equationEditWidget->setPlainText(text);
0182     QTextCursor cursor(m_equationEditWidget->textCursor());
0183     cursor.movePosition(QTextCursor::End);
0184     m_equationEditWidget->setTextCursor(cursor);
0185     m_settingText = false;
0186 }
0187 
0188 void EquationEdit::setValidatePrefix(const QString &prefix)
0189 {
0190     m_validatePrefix = prefix;
0191     reHighlight();
0192 }
0193 
0194 void EquationEdit::wrapSelected(const QString &before, const QString &after)
0195 {
0196     QTextCursor cursor(m_equationEditWidget->textCursor());
0197     QString newText = before + cursor.selectedText() + after;
0198     cursor.insertText(newText);
0199     cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, after.length());
0200     m_equationEditWidget->setTextCursor(cursor);
0201 }
0202 
0203 QString EquationEdit::text() const
0204 {
0205     return m_equationEditWidget->toPlainText();
0206 }
0207 
0208 void EquationEdit::clear()
0209 {
0210     m_equationEditWidget->clear();
0211 }
0212 
0213 void EquationEdit::selectAll()
0214 {
0215     m_equationEditWidget->selectAll();
0216 }
0217 
0218 void EquationEdit::insertText(const QString &text)
0219 {
0220     m_equationEditWidget->insertPlainText(text);
0221 }
0222 
0223 #include "moc_equationedit.cpp"