File indexing completed on 2024-04-28 15:14:03

0001 /***************************************************************************
0002     File             : EquationHighlighter.h
0003     Project          : LabPlot
0004     --------------------------------------------------------------------
0005     Copyright        : (C) 2014 Alexander Semke (alexander.semke@web.de)
0006     Copyright        : (C) 2006  David Saxton <david@bluehaze.org>
0007     Description      : syntax highligher for mathematical equations
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 "EquationHighlighter.h"
0031 #include "backend/gsl/ExpressionParser.h"
0032 
0033 #include <QPalette>
0034 #include <KTextEdit>
0035 
0036 EquationHighlighter::EquationHighlighter(KTextEdit* parent)
0037     : QSyntaxHighlighter(parent),
0038       m_parent(parent)
0039 //  m_errorPosition = -1
0040 {}
0041 
0042 void EquationHighlighter::setVariables(const QStringList& variables) {
0043     m_variables = variables;
0044     rehighlight();
0045 }
0046 
0047 void EquationHighlighter::highlightBlock(const QString& text) {
0048     //TODO: m_parent->checkTextValidity();
0049 
0050     if (text.isEmpty())
0051         return;
0052 
0053     QTextCharFormat number;
0054     QTextCharFormat function;
0055     QTextCharFormat variable;
0056     QTextCharFormat constant;
0057     QTextCharFormat matchedParenthesis;
0058 
0059     QPalette palette;
0060     if (qGray(palette.color(QPalette::Base).rgb()) > 160) {
0061         number.setForeground(QColor(0, 0, 127));
0062         function.setForeground(QColor(85, 0, 0));
0063         function.setFontWeight(QFont::Bold);
0064         variable.setForeground(QColor(0, 85, 0));
0065         constant.setForeground(QColor(85, 0, 0));
0066         matchedParenthesis.setBackground(QColor(255, 255, 183));
0067     } else {
0068         number.setForeground(QColor(160, 160, 255));
0069         function.setForeground(QColor(255, 160, 160));
0070         function.setFontWeight(QFont::Bold);
0071         variable.setForeground(QColor(160, 255, 160));
0072         constant.setForeground(QColor(255, 160, 160));
0073         matchedParenthesis.setBackground(QColor(85, 85, 0));
0074     }
0075 
0076     QTextCharFormat other;
0077 
0078     static const QStringList& functions = ExpressionParser::getInstance()->functions();
0079     static const QStringList& constants = ExpressionParser::getInstance()->constants();
0080 
0081     for (int i = 0; i < text.length(); ++i) {
0082         QString remaining = text.right(text.length() - i);
0083         bool found = false;
0084 
0085         //variables
0086         for (const QString& var: m_variables) {
0087             if (remaining.startsWith(var)) {
0088                 QString nextChar = remaining.mid(var.length(), 1);
0089                 if (nextChar == " " || nextChar == ")" || nextChar == "+" || nextChar == "-"
0090                     || nextChar == "*" || nextChar == "/" || nextChar == "^") {
0091                     setFormat(i, var.length(), variable);
0092                     i += var.length() - 1;
0093                     found = true;
0094                     break;
0095                 }
0096             }
0097         }
0098         if (found)
0099             continue;
0100 
0101         //functions
0102         for (const QString& f: functions) {
0103             if (remaining.startsWith(f)) {
0104                 setFormat(i, f.length(), function);
0105                 i += f.length() - 1;
0106                 found = true;
0107                 break;
0108             }
0109         }
0110         if (found)
0111             continue;
0112 
0113         //constants
0114         for (const QString& f: constants) {
0115             if (remaining.startsWith(f)) {
0116                 setFormat(i, f.length(), function);
0117                 i += f.length() - 1;
0118                 found = true;
0119                 break;
0120             }
0121         }
0122         if (found)
0123             continue;
0124 
0125         //TODO
0126         /*
0127         ushort u = text[i].unicode();
0128         bool isFraction = (u >= 0xbc && u <= 0xbe) || (u >= 0x2153 && u <= 0x215e);
0129         bool isPower = (u >= 0xb2 && u <= 0xb3) || (u == 0x2070) || (u >= 0x2074 && u <= 0x2079);
0130         bool isDigit = text[i].isDigit();
0131         SET_NUMBER_LOCALE
0132         bool isDecimalPoint = text[i] == numberLocale.decimalPoint();
0133 
0134         if (isFraction || isPower || isDigit || isDecimalPoint)
0135             setFormat(i, 1, number);
0136         else
0137             setFormat(i, 1, other);
0138         */
0139     }
0140 
0141     //highlight matched brackets
0142     int cursorPos = m_parent->textCursor().position();
0143     if (cursorPos < 0)
0144         cursorPos = 0;
0145 
0146     // Adjust cursorpos to allow for a bracket before the cursor position
0147     if (cursorPos >= text.size())
0148         cursorPos = text.size() - 1;
0149     else if (cursorPos > 0 && (text[cursorPos-1] == '(' || text[cursorPos-1] == ')'))
0150         cursorPos--;
0151 
0152     bool haveOpen =  text[cursorPos] == '(';
0153     bool haveClose = text[cursorPos] == ')';
0154 
0155     if ((haveOpen || haveClose) && m_parent->hasFocus()) {
0156         // Search for the other bracket
0157 
0158         int inc = haveOpen ? 1 : -1; // which direction to search in
0159 
0160         int level = 0;
0161         for (int i = cursorPos; i >= 0 && i < text.size(); i += inc) {
0162             if (text[i] == ')')
0163                 level--;
0164             else if (text[i] == '(')
0165                 level++;
0166 
0167             if (level == 0) {
0168                 // Matched!
0169                 setFormat(cursorPos, 1, matchedParenthesis);
0170                 setFormat(i, 1, matchedParenthesis);
0171                 break;
0172             }
0173         }
0174     }
0175 
0176     //TODO: highlight the position of the error
0177 //  if (m_errorPosition != -1) {
0178 //      QTextCharFormat error;
0179 //      error.setForeground(Qt::red);
0180 //
0181 //      setFormat(m_errorPosition, 1, error);
0182 //  }
0183 }
0184 
0185 void EquationHighlighter::rehighlight() {
0186     setDocument(nullptr);
0187     setDocument(m_parent->document());
0188 }
0189 
0190 /**
0191 * This is used to indicate the position where the error occurred.
0192 * If \p position is negative, then no error will be shown.
0193 */
0194 // void EquationHighlighter::setErrorPosition(int position) {
0195 //  m_errorPosition = position;
0196 // }