File indexing completed on 2024-04-14 03:39:17

0001 
0002 /*************************************************************************************
0003  *  Copyright (C) 2007 by Aleix Pol <aleixpol@kde.org>                               *
0004  *                                                                                   *
0005  *  This program is free software; you can redistribute it and/or                    *
0006  *  modify it under the terms of the GNU General Public License                      *
0007  *  as published by the Free Software Foundation; either version 2                   *
0008  *  of the License, or (at your option) any later version.                           *
0009  *                                                                                   *
0010  *  This program is distributed in the hope that it will be useful,                  *
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
0013  *  GNU General Public License for more details.                                     *
0014  *                                                                                   *
0015  *  You should have received a copy of the GNU General Public License                *
0016  *  along with this program; if not, write to the Free Software                      *
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
0018  *************************************************************************************/
0019 
0020 #include "algebrahighlighter.h"
0021 #include <QGuiApplication>
0022 #include <QStack>
0023 #include <QPalette>
0024 
0025 #include <analitza/expression.h>
0026 #include <analitza/analyzer.h>
0027 #include <analitza/variables.h>
0028 #include <analitza/explexer.h>
0029 #include <analitza/expressionparser.h>
0030 
0031 AlgebraHighlighter::AlgebraHighlighter(QTextDocument *doc, const Analitza::Analyzer *na)
0032     : QSyntaxHighlighter(doc), m_correct(true), m_mode(Autodetect), m_pos(0), m_editingParameter(-1), m_editingBounds(-1), a(na)
0033 {
0034     bold.setFontWeight(QFont::Bold);
0035 }
0036 
0037 QString removeTags(const QString& in)
0038 {
0039     bool tag=false;
0040     QString out;
0041     for(int i=0; i<in.length(); i++){
0042         if(!tag && in[i]=='<')
0043             tag=true;
0044         else if(tag && in[i]=='>')
0045             tag=false;
0046         else if(!tag) {
0047             if(in.mid(i,4)==QLatin1String("&gt;")){
0048                 out += '>';
0049                 i+=3;
0050             } else
0051                 out += in[i];
0052         }
0053     }
0054     return out;
0055 }
0056 
0057 struct FuncItem
0058 {
0059     FuncItem() : parameter(-1), pos(-1), bounding(false) {}
0060     FuncItem(const QString& name, int p) : id(name), parameter(p), pos(-1), bounding(false) {}
0061     QString id;
0062     int parameter;
0063     int pos;
0064     bool bounding;
0065 };
0066 
0067 void AlgebraHighlighter::highlightBlock(const QString &text)
0068 {
0069     m_correct=true;
0070     if(int(m_pos)>=text.length())
0071         m_pos=text.length();
0072     
0073     QPalette pal=qApp->palette();
0074     QColor number(pal.color(QPalette::Active, QPalette::Link));
0075     QColor variable(pal.color(QPalette::Active, QPalette::LinkVisited));
0076     QColor comment(pal.color(QPalette::Active, QPalette::Window));
0077     QColor id(150,0,50);
0078     QColor string(0xbb,0,0);
0079     QColor uncorrect(Qt::red);
0080     m_editingParameter=0;
0081     m_editingName.clear();
0082     
0083     if(Analitza::Expression::isMathML(text)) {
0084         QString lasttag;
0085         int inside=0;
0086         for(int i=0; i<text.length(); i++) {
0087             if(text[i]=='<') { //We enter in a tag
0088                 lasttag.clear();
0089                 int j=i+1, k=0;
0090                 for(k=i+1; k<text.length() && text[k]!='>'; ++k){
0091                     lasttag.append(text[k]);
0092                     if(text[k]!=' ' && j==k-1)
0093                         j=k;
0094                 }
0095                 j++;
0096                 
0097                 setFormat(i, 1, bold);
0098                 setFormat(j, 1, bold);
0099                 if(lasttag.startsWith(QChar('/'))){
0100                     setFormat(i+1, j-i-1, QColor(100,0,0));
0101                     setFormat(i+1, 1, bold);
0102                     inside--;
0103                 } else if(lasttag.endsWith(QChar('/'))) {
0104                     setFormat(i+1, j-i-1, id);
0105                     setFormat(j+1, 2, bold);
0106                 } else if(j!=k) {
0107                     setFormat(i+1, j-i-1, QColor(150,0,0));
0108                     setFormat(j+1, k-j-1, QColor(150,100,0));
0109                     inside++;
0110                 } else {
0111                     setFormat(i+1, j-i-1, QColor(150,0,0));
0112                     inside++;
0113                 }
0114                 i=k;
0115             }
0116             else if(lasttag==QLatin1String("cn"))
0117                 setFormat(i, 1, number);
0118             else if(lasttag==QLatin1String("ci"))
0119                 setFormat(i, 1, variable);
0120         }
0121         
0122         m_correct=(inside==0);
0123     } else {
0124         ExpLexer lex(text);
0125         QStack<ExpLexer::TOKEN> paren;
0126         QStack<FuncItem> parameter;
0127         
0128         while(lex.lex()!=ExpressionTable::EOF_SYMBOL && lex.error().isEmpty()) {
0129             QColor f;
0130             bool isBold=false;
0131             switch(lex.current.type){
0132                 case ExpressionTable::tVal:
0133                     if(lex.current.val.mid(1,2)==QLatin1String("cn")) //if it is a number
0134                         f=number;
0135                     else { //if it is a variable
0136                         if(a && a->variables()->contains(removeTags(lex.current.val)))
0137                             f=number;
0138                         else
0139                             f=variable;
0140                     }
0141                     break;
0142                 case ExpressionTable::tId:
0143                     f=id;
0144                     m_aName=lex.current.val;
0145                     break;
0146                 case ExpressionTable::tString:
0147                     f=string;
0148                     break;
0149                 case ExpressionTable::tComment:
0150                     f=comment;
0151                     break;
0152                 case -1:
0153                     m_correct = false;
0154                     f=uncorrect;
0155                     break;
0156                 case ExpressionTable::tLpr:
0157                 case ExpressionTable::tLcb:
0158                     paren.push(lex.current);
0159                     if(m_pos>lex.current.pos)
0160                         parameter.push(FuncItem(m_aName, 0));
0161                     isBold=true;
0162                     setFormat(lex.current.pos, lex.current.len, bold);
0163                     break;
0164                 case ExpressionTable::tRpr:
0165                 case ExpressionTable::tRcb: {
0166                     isBold=true;
0167                     setFormat(lex.current.pos, lex.current.len, bold);
0168                     
0169                     bool corr=!paren.isEmpty();
0170                     if(corr) {
0171                         if(lex.current.type==ExpressionTable::tRpr && paren.top().type!=ExpressionTable::tLpr)
0172                             corr=false;
0173                         
0174                         if(m_pos==lex.current.pos || m_pos==paren.top().pos) {
0175                             QTextCharFormat bg=bold;
0176                             bg.setBackground(Qt::yellow);
0177                             
0178                             setFormat(lex.current.pos, lex.current.len, bg);
0179                             setFormat(paren.top().pos, paren.top().len, bg);
0180                         }
0181                         
0182                         paren.pop();
0183                         if(m_pos>lex.current.pos)
0184                             parameter.pop();
0185                     }
0186                     
0187                     if(!corr)
0188                         setFormat(lex.current.pos, lex.current.len, uncorrect); // error
0189                 }    break;
0190                 default:
0191                     if(m_pos>lex.current.pos && !parameter.isEmpty()
0192                             && lex.current.type==ExpressionTable::tComa) {
0193                         parameter.top().parameter++;
0194                         parameter.top().pos=lex.current.pos;
0195                     }
0196                     
0197                     
0198                     
0199                     if(m_pos>lex.current.pos && !parameter.isEmpty()
0200                             && lex.current.type==ExpressionTable::tColon) {
0201                         parameter.top().bounding=true;
0202                         parameter.top().pos=lex.current.pos;
0203                     }
0204                     
0205                     isBold=true;
0206                     setFormat(lex.current.pos, lex.current.len, bold);
0207                     break;
0208             }
0209             if(!isBold)
0210                 setFormat(lex.current.pos, lex.current.len, f);
0211         }
0212         
0213         if(!lex.error().isEmpty())
0214         {
0215             setFormat(lex.current.pos, text.size()-lex.current.pos, uncorrect);
0216         }
0217         
0218         if(!parameter.isEmpty()) {
0219             m_editingName=parameter.top().id;
0220             m_editingParameter=parameter.top().parameter;
0221             m_editingBounds=parameter.top().bounding;
0222             
0223             if(m_editingParameter!=0 || m_editingBounds) {
0224                 QTextCharFormat currentComa=bold;
0225                 currentComa.setBackground(Qt::yellow);
0226                 setFormat(parameter.top().pos, 1, currentComa);
0227             }
0228         }
0229     }
0230 }
0231 
0232 QString AlgebraHighlighter::editingName() const
0233 {
0234     return m_editingName;
0235 }
0236 
0237 int AlgebraHighlighter::editingParameter() const
0238 {
0239     return m_editingParameter;
0240 }
0241 
0242 bool AlgebraHighlighter::editingBounds() const
0243 {
0244     return m_editingBounds;
0245 }