PythonEditor.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <QContextMenuEvent>
00027 # include <QMenu>
00028 # include <QShortcut>
00029 # include <QTextCursor>
00030 #endif
00031 
00032 #include "PythonEditor.h"
00033 #include "Application.h"
00034 #include "BitmapFactory.h"
00035 #include "FileDialog.h"
00036 #include "DlgEditorImp.h"
00037 
00038 #include <Base/Interpreter.h>
00039 #include <Base/Exception.h>
00040 #include <Base/Parameter.h>
00041 
00042 using namespace Gui;
00043 
00044 namespace Gui {
00045 struct PythonEditorP
00046 {
00047     QMap<QString, QColor> colormap; // Color map
00048     PythonEditorP()
00049     {
00050         colormap[QLatin1String("Text")] = Qt::black;
00051         colormap[QLatin1String("Bookmark")] = Qt::cyan;
00052         colormap[QLatin1String("Breakpoint")] = Qt::red;
00053         colormap[QLatin1String("Keyword")] = Qt::blue;
00054         colormap[QLatin1String("Comment")] = QColor(0, 170, 0);
00055         colormap[QLatin1String("Block comment")] = QColor(160, 160, 164);
00056         colormap[QLatin1String("Number")] = Qt::blue;
00057         colormap[QLatin1String("String")] = Qt::red;
00058         colormap[QLatin1String("Character")] = Qt::red;
00059         colormap[QLatin1String("Class name")] = QColor(255, 170, 0);
00060         colormap[QLatin1String("Define name")] = QColor(255, 170, 0);
00061         colormap[QLatin1String("Operator")] = QColor(160, 160, 164);
00062         colormap[QLatin1String("Python output")] = QColor(170, 170, 127);
00063         colormap[QLatin1String("Python error")] = Qt::red;
00064         colormap[QLatin1String("Line")] = QColor(224,224,224);
00065     }
00066 };
00067 } // namespace Gui
00068 
00069 /* TRANSLATOR Gui::PythonEditor */
00070 
00075 PythonEditor::PythonEditor(QWidget* parent)
00076   : TextEditor(parent)
00077 {
00078     d = new PythonEditorP();
00079     this->setSyntaxHighlighter(new PythonSyntaxHighlighter(this));
00080 
00081     // set acelerators
00082     QShortcut* comment = new QShortcut(this);
00083     comment->setKey(Qt::ALT + Qt::Key_C);
00084 
00085     QShortcut* uncomment = new QShortcut(this);
00086     uncomment->setKey(Qt::ALT + Qt::Key_U);
00087 
00088     connect(comment, SIGNAL(activated()), 
00089             this, SLOT(onComment()));
00090     connect(uncomment, SIGNAL(activated()), 
00091             this, SLOT(onUncomment()));
00092 }
00093 
00095 PythonEditor::~PythonEditor()
00096 {
00097     getWindowParameter()->Detach( this );
00098     delete d;
00099 }
00100 
00101 void PythonEditor::drawMarker(int line, int x, int y, QPainter* p)
00102 {
00103 #if 0
00104     Breakpoint bp = _dbg->getBreakpoint(fileName());
00105     if (bp.checkLine(line)) {
00106         p->drawPixmap(x, y, breakpoint);
00107     }
00108     if (m_debugLine == line) {
00109         p->drawPixmap(x, y+2, debugMarker);
00110         debugRect = QRect(x, y+2, debugMarker.width(), debugMarker.height());
00111     }
00112 #endif
00113 }
00114 
00115 void PythonEditor::contextMenuEvent ( QContextMenuEvent * e )
00116 {
00117     QMenu* menu = createStandardContextMenu();
00118     menu->addSeparator();
00119     menu->addAction( tr("Comment"), this, SLOT( onComment() ), Qt::ALT + Qt::Key_C );
00120     menu->addAction( tr("Uncomment"), this, SLOT( onUncomment() ), Qt::ALT + Qt::Key_U );
00121 
00122     menu->exec(e->globalPos());
00123     delete menu;
00124 }
00125 
00126 void PythonEditor::onComment()
00127 {
00128     QTextCursor cursor = textCursor();
00129     int selStart = cursor.selectionStart();
00130     int selEnd = cursor.selectionEnd();
00131     QTextBlock block;
00132     cursor.beginEditBlock();
00133     for (block = document()->begin(); block.isValid(); block = block.next()) {
00134         int pos = block.position();
00135         int off = block.length()-1;
00136         // at least one char of the block is part of the selection
00137         if ( pos >= selStart || pos+off >= selStart) {
00138             if ( pos+1 > selEnd )
00139                 break; // end of selection reached
00140             cursor.setPosition(block.position());
00141             cursor.insertText(QLatin1String("#"));
00142                 selEnd++;
00143         }
00144     }
00145 
00146     cursor.endEditBlock();
00147 }
00148 
00149 void PythonEditor::onUncomment()
00150 {
00151     QTextCursor cursor = textCursor();
00152     int selStart = cursor.selectionStart();
00153     int selEnd = cursor.selectionEnd();
00154     QTextBlock block;
00155     cursor.beginEditBlock();
00156     for (block = document()->begin(); block.isValid(); block = block.next()) {
00157         int pos = block.position();
00158         int off = block.length()-1;
00159         // at least one char of the block is part of the selection
00160         if ( pos >= selStart || pos+off >= selStart) {
00161             if ( pos+1 > selEnd )
00162                 break; // end of selection reached
00163             if (block.text().startsWith(QLatin1String("#"))) {
00164                 cursor.setPosition(block.position());
00165                 cursor.deleteChar();
00166                 selEnd--;
00167             }
00168         }
00169     }
00170 
00171     cursor.endEditBlock();
00172 }
00173 
00174 // ------------------------------------------------------------------------
00175 
00176 namespace Gui {
00177 class PythonSyntaxHighlighterP
00178 {
00179 public:
00180     PythonSyntaxHighlighterP()
00181     {
00182         keywords << QLatin1String("and") << QLatin1String("as")
00183                  << QLatin1String("assert")
00184                  << QLatin1String("break") << QLatin1String("class")
00185                  << QLatin1String("continue") << QLatin1String("def")
00186                  << QLatin1String("del") << QLatin1String("elif")
00187                  << QLatin1String("else") << QLatin1String("except")
00188                  << QLatin1String("exec") << QLatin1String("finally")
00189                  << QLatin1String("for") << QLatin1String("from")
00190                  << QLatin1String("global") << QLatin1String("if")
00191                  << QLatin1String("import") << QLatin1String("in")
00192                  << QLatin1String("is") << QLatin1String("lambda")
00193                  << QLatin1String("None") << QLatin1String("not")
00194                  << QLatin1String("or") << QLatin1String("pass")
00195                  << QLatin1String("print") << QLatin1String("raise")
00196                  << QLatin1String("return") << QLatin1String("try")
00197                  << QLatin1String("while") << QLatin1String("yield");
00198     }
00199 
00200     QStringList keywords;
00201 };
00202 } // namespace Gui
00203 
00207 PythonSyntaxHighlighter::PythonSyntaxHighlighter(QObject* parent)
00208     : SyntaxHighlighter(parent)
00209 {
00210     d = new PythonSyntaxHighlighterP;
00211 }
00212 
00214 PythonSyntaxHighlighter::~PythonSyntaxHighlighter()
00215 {
00216     delete d;
00217 }
00218 
00222 void PythonSyntaxHighlighter::highlightBlock (const QString & text)
00223 {
00224   int i = 0;
00225   QChar prev, ch;
00226 
00227   const int Standard      = 0;     // Standard text
00228   const int Digit         = 1;     // Digits
00229   const int Comment       = 2;     // Comment begins with #
00230   const int Literal1      = 3;     // String literal beginning with "
00231   const int Literal2      = 4;     // Other string literal beginning with '
00232   const int Blockcomment1 = 5;     // Block comments beginning and ending with """
00233   const int Blockcomment2 = 6;     // Other block comments beginning and ending with '''
00234   const int ClassName     = 7;     // Text after the keyword class
00235   const int DefineName    = 8;     // Text after the keyword def
00236 
00237   int endStateOfLastPara = previousBlockState();
00238   if (endStateOfLastPara < 0 || endStateOfLastPara > maximumUserState()) 
00239     endStateOfLastPara = Standard;
00240 
00241   while ( i < text.length() )
00242   {
00243     ch = text.at( i );
00244 
00245     switch ( endStateOfLastPara )
00246     {
00247     case Standard:
00248       {
00249         switch ( ch.unicode() )
00250         {
00251         case '#':
00252           {
00253             // begin a comment
00254             setFormat( i, 1, this->colorByType(SyntaxHighlighter::Comment));
00255             endStateOfLastPara=Comment;
00256           } break;
00257         case '"':
00258           {
00259             // Begin either string literal or block comment
00260             if ((i>=2) && text.at(i-1) == QLatin1Char('"') &&
00261                 text.at(i-2) == QLatin1Char('"'))
00262             {
00263               setFormat( i-2, 3, this->colorByType(SyntaxHighlighter::BlockComment));
00264               endStateOfLastPara=Blockcomment1;
00265             }
00266             else
00267             {
00268               setFormat( i, 1, this->colorByType(SyntaxHighlighter::String));
00269               endStateOfLastPara=Literal1;
00270             }
00271           } break;
00272         case '\'':
00273           {
00274             // Begin either string literal or block comment
00275             if ((i>=2) && text.at(i-1) == QLatin1Char('\'') && 
00276                 text.at(i-2) == QLatin1Char('\''))
00277             {
00278               setFormat( i-2, 3, this->colorByType(SyntaxHighlighter::BlockComment));
00279               endStateOfLastPara=Blockcomment2;
00280             }
00281             else
00282             {
00283               setFormat( i, 1, this->colorByType(SyntaxHighlighter::String));
00284               endStateOfLastPara=Literal2;
00285             }
00286           } break;
00287         case ' ':
00288         case '\t':
00289           {
00290             // ignore whitespaces
00291           } break;
00292         case '(': case ')': case '[': case ']': 
00293         case '+': case '-': case '*': case '/': 
00294         case ':': case '%': case '^': case '~': 
00295         case '!': case '=': case '<': case '>': // possibly two characters
00296           {
00297             setFormat(i, 1, this->colorByType(SyntaxHighlighter::Operator));
00298             endStateOfLastPara=Standard;
00299           } break;
00300         default:
00301           {
00302             // Check for normal text
00303             if ( ch.isLetter() || ch == QLatin1Char('_') )
00304             {
00305               QString buffer;
00306               int j=i;
00307               while ( ch.isLetterOrNumber() || ch == QLatin1Char('_') ) {
00308                 buffer += ch;
00309                 ++j;
00310                 if (j >= text.length())
00311                   break; // end of text
00312                 ch = text.at(j);
00313               }
00314 
00315               if ( d->keywords.contains( buffer ) != 0 ) {
00316                 if ( buffer == QLatin1String("def"))
00317                   endStateOfLastPara = DefineName;
00318                 else if ( buffer == QLatin1String("class"))
00319                   endStateOfLastPara = ClassName;
00320 
00321                 QTextCharFormat keywordFormat;
00322                 keywordFormat.setForeground(this->colorByType(SyntaxHighlighter::Keyword));
00323                 keywordFormat.setFontWeight(QFont::Bold);
00324                 setFormat( i, buffer.length(), keywordFormat);
00325               }
00326               else {
00327                 setFormat( i, buffer.length(),this->colorByType(SyntaxHighlighter::Text));
00328               }
00329 
00330               // increment i
00331               if ( !buffer.isEmpty() )
00332                 i = j-1;
00333             }
00334             // this is the beginning of a number
00335             else if ( ch.isDigit() )
00336             {
00337               setFormat(i, 1, this->colorByType(SyntaxHighlighter::Number));
00338               endStateOfLastPara=Digit;
00339             }
00340             // probably an operator
00341             else if ( ch.isSymbol() || ch.isPunct() )
00342             {
00343               setFormat( i, 1, this->colorByType(SyntaxHighlighter::Operator));
00344             }
00345           }
00346         }
00347       } break;
00348     case Comment:
00349       {
00350         setFormat( i, 1, this->colorByType(SyntaxHighlighter::Comment));
00351       } break;
00352     case Literal1:
00353       {
00354         setFormat( i, 1, this->colorByType(SyntaxHighlighter::String));
00355         if ( ch == QLatin1Char('"') )
00356           endStateOfLastPara = Standard;
00357       } break;
00358     case Literal2:
00359       {
00360         setFormat( i, 1, this->colorByType(SyntaxHighlighter::String));
00361         if ( ch == QLatin1Char('\'') )
00362           endStateOfLastPara = Standard;
00363       } break;
00364     case Blockcomment1:
00365       {
00366         setFormat( i, 1, this->colorByType(SyntaxHighlighter::BlockComment));
00367         if ( i>=2 && ch == QLatin1Char('"') &&
00368             text.at(i-1) == QLatin1Char('"') &&
00369             text.at(i-2) == QLatin1Char('"'))
00370           endStateOfLastPara = Standard;
00371       } break;
00372     case Blockcomment2:
00373       {
00374         setFormat( i, 1, this->colorByType(SyntaxHighlighter::BlockComment));
00375         if ( i>=2 && ch == QLatin1Char('\'') &&
00376             text.at(i-1) == QLatin1Char('\'') &&
00377             text.at(i-2) == QLatin1Char('\''))
00378           endStateOfLastPara = Standard;
00379       } break;
00380     case DefineName:
00381       {
00382         if ( ch.isLetterOrNumber() || ch == QLatin1Char(' ') || ch == QLatin1Char('_') )
00383         {
00384           setFormat( i, 1, this->colorByType(SyntaxHighlighter::Defname));
00385         }
00386         else
00387         {
00388           if ( ch.isSymbol() || ch.isPunct() )
00389             setFormat(i, 1, this->colorByType(SyntaxHighlighter::Operator));
00390           endStateOfLastPara = Standard;
00391         }
00392       } break;
00393     case ClassName:
00394       {
00395         if ( ch.isLetterOrNumber() || ch == QLatin1Char(' ') || ch == QLatin1Char('_') )
00396         {
00397           setFormat( i, 1, this->colorByType(SyntaxHighlighter::Classname));
00398         }
00399         else
00400         {
00401           if (ch.isSymbol() || ch.isPunct() )
00402             setFormat( i, 1, this->colorByType(SyntaxHighlighter::Operator));
00403           endStateOfLastPara = Standard;
00404         }
00405       } break;
00406     case Digit:
00407       {
00408         if (ch.isDigit() || ch == QLatin1Char('.'))
00409         {
00410           setFormat( i, 1, this->colorByType(SyntaxHighlighter::Number));
00411         }
00412         else
00413         {
00414           if ( ch.isSymbol() || ch.isPunct() )
00415             setFormat( i, 1, this->colorByType(SyntaxHighlighter::Operator));
00416           endStateOfLastPara = Standard;
00417         }
00418       }break;
00419     }
00420 
00421     prev = ch;
00422     i++;
00423   }
00424 
00425   // only block comments can have several lines
00426   if ( endStateOfLastPara != Blockcomment1 && endStateOfLastPara != Blockcomment2 ) 
00427   {
00428     endStateOfLastPara = Standard ;
00429   } 
00430 
00431   setCurrentBlockState(endStateOfLastPara);
00432 }
00433 
00434 #include "moc_PythonEditor.cpp"

Generated on Wed Nov 23 19:00:32 2011 for FreeCAD by  doxygen 1.6.1