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