# HG changeset patch # User Jacob Dawid # Date 1302547690 -7200 # Node ID 17b9b85bd1a6b4fb9ed3e4365814a41c0b212d2e # Parent 8832a22215633749925185167d3e9be5c8e854b3 Added files from QtOctave. diff --git a/gui//Quint.pro b/gui//Quint.pro --- a/gui//Quint.pro +++ b/gui//Quint.pro @@ -42,7 +42,9 @@ src/OctaveTerminal.cpp \ src/VariablesDockWidget.cpp \ src/HistoryDockWidget.cpp \ - src/FilesDockWidget.cpp + src/FilesDockWidget.cpp \ + src/CodeEdit.cpp \ + src/Syntax.cpp HEADERS += \ src/TerminalCharacterDecoder.h \ @@ -81,11 +83,13 @@ src/OctaveTerminal.h \ src/VariablesDockWidget.h \ src/HistoryDockWidget.h \ - src/FilesDockWidget.h + src/FilesDockWidget.h \ + src/CodeEdit.h \ + src/Syntax.h INCFLAGS = -g3 $$system(mkoctfile -p INCFLAGS) LFLAGS = $$system(mkoctfile -p LFLAGS) \ $$system(mkoctfile -p OCTAVE_LIBS) \ $$system(mkoctfile -p LIBS) -LIBS += $$LFLAGS -loctave -loctinterp -lreadline -lutil -L../Quint -lqcodeedit +LIBS += $$LFLAGS -loctave -loctinterp -lreadline -lutil QMAKE_CXXFLAGS += $$INCFLAGS diff --git a/gui//src/CodeEdit.cpp b/gui//src/CodeEdit.cpp new file mode 100644 --- /dev/null +++ b/gui//src/CodeEdit.cpp @@ -0,0 +1,433 @@ +/* Copyright (C) 2007 Alejandro Álvarez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "CodeEdit.h" + +CodeEdit::CodeEdit(QWidget *parent, QString syntaxF): QPlainTextEdit(parent) +, contextMenu(this) +{ + syntax=new Syntax(document()); + if(!syntaxF.isEmpty()) + syntax->load(syntaxF); + + setUndoRedoEnabled(true); + setTabStopWidth(32); + setFrameStyle(QFrame::NoFrame); + + autocompletion_ok=true;//(get_config("autoCompletion")!="false"); + brakets_match_ok=true;//(get_config("bracketsMatch")!="false"); + + // ContextMenu + + connect(contextMenu.addAction(tr("Undo")), SIGNAL(triggered()), + this, SLOT(undo())); + connect(contextMenu.addAction(tr("Redo")), SIGNAL(triggered()), + this, SLOT(redo())); + + contextMenu.addSeparator(); + + connect(contextMenu.addAction(tr("Cut")), SIGNAL(triggered()), + this, SLOT(cut())); + connect(contextMenu.addAction(tr("Copy")), SIGNAL(triggered()), + this, SLOT(copy())); + connect(contextMenu.addAction(tr("Paste")), SIGNAL(triggered()), + this, SLOT(paste())); + connect(contextMenu.addAction(tr("Delete")), SIGNAL(triggered()), + this, SLOT(deleteSelection())); + + contextMenu.addSeparator(); + + connect(contextMenu.addAction(tr("Select all")), SIGNAL(triggered()), + this, SLOT(selectAll())); + + contextMenu.addSeparator(); + + connect(contextMenu.addAction(tr("Toggle breakpoint")), SIGNAL(triggered()), + this, SLOT(toggleBreakpoint())); + + //connect(this->document(), SIGNAL( contentsChange ( int , int , int )), this, SLOT(buildUndoRedoStack(int , int , int)) ); + + if(autocompletion_ok) + { + connect(this->document(), SIGNAL( contentsChange ( int , int , int )), this, SLOT(buildAutoCompletionList(int , int , int)) ); + connect(&completion, SIGNAL(activated ( const QModelIndex &)), this, SLOT(doCompletion(const QModelIndex &)) ); + } + + if(brakets_match_ok) + { + connect(&braketsTimer, SIGNAL(timeout ()), this, SLOT(braketsMatch())); + } + braketsTimer.setSingleShot(true); + braketsTimer.setInterval(50); + + octaveCommandTimer.setSingleShot(true); + octaveCommandTimer.setInterval(2000); + + connect(&octaveCommandTimer, SIGNAL(timeout ()), this, SLOT(octaveCommandCompletion())); + + completionTimer.setSingleShot(true); + completionTimer.setInterval(200); + + connect(&completionTimer, SIGNAL(timeout ()), this, SLOT(buildAutoCompletionList())); + + connect(this, SIGNAL( cursorPositionChanged() ), this, SLOT( cursorChanged_cb() ) ); + + auto_indent=true; + setAcceptDrops(false); + if(autocompletion_ok) + { + completion.setWidget(this); + completion_model=new QStringListModel(&completion); + completion.setModel(completion_model); + completion.setCompletionMode(QCompleter::UnfilteredPopupCompletion); + } + + text_modified_stop_ok=context_changed_ok=false; + + connect(document(), SIGNAL(modificationChanged (bool)), this, SLOT(textModified_cb(bool))); + + auto_indent=true;//("false"!=get_config("autoindent")); + automatic_indention_statement_ok=true;//(get_config("autoindent_statements")=="true"); +} + +CodeEdit::~CodeEdit() +{ + //printf("Borrando la sintaxis\n"); + delete syntax; + //printf("CodeEdit destruido\n"); +} + +void CodeEdit::contextMenuEvent(QContextMenuEvent *e) +{ + //setTextCursor(cursorForPosition(e->pos())); + contextMenu.exec(e->globalPos()); +} + +void CodeEdit::undo() +{ + document()->undo(); +} + +void CodeEdit::redo() +{ + document()->redo(); +} + +void CodeEdit::deleteSelection() +{ + textCursor().removeSelectedText(); +} + +void CodeEdit::toggleBreakpoint() +{ + int line = 1; + for(QTextBlock tb = document()->begin(); + tb.isValid(); + line++, tb = tb.next()) + { + if(tb == textCursor().block()) + { + emit toggleBreakpoint(line); + return; + } + } +} + +bool CodeEdit::event( QEvent * e ) +{ + + if(QEvent::KeyPress==e->type() ) + { + QKeyEvent *k=(QKeyEvent *)e; + if( autocompletion_ok && ( Qt::Key_Left==k->key() || Qt::Key_Right==k->key() ) ) + { + completion.popup()->hide(); + } + else if( Qt::Key_Return==k->key() || Qt::Key_Enter==k->key()) + { + if(autocompletion_ok && !completion.popup()->isHidden()) + { + doCompletion(completion.popup()->currentIndex()); + } + else if( auto_indent ) + { + QTextCursor cursor=textCursor(); + int pos=cursor.position(); + cursor.movePosition(QTextCursor::StartOfBlock,QTextCursor::KeepAnchor); + QString line=cursor.selectedText(); + QString start_blank; + start_blank.append('\n'); + for(int i=0;iisModified(); + text_modified_stop_ok=true; + //if(autocompletion_ok) disconnect(this->document(), SIGNAL( contentsChange ( int , int , int )), this, SLOT(buildAutoCompletionList(int , int , int)) ); + int pos=textCursor().position(); + int start=-1, end=-1; + if(pos>=0) syntax->braketsMacth(pos, start, end, rehigh); + QList selection_list; + if(start>=0 && end >=0) + { + QTextEdit::ExtraSelection selection; + selection.cursor=textCursor(); + selection.cursor.setPosition(start); + selection.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + selection.format=selection.cursor.blockCharFormat(); + QBrush brush(Qt::yellow); + selection.format.setBackground (brush); + selection_list.append(selection); + selection.cursor=textCursor(); + selection.cursor.setPosition(end); + selection.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + selection_list.append(selection); + } + setExtraSelections(selection_list); + //if(autocompletion_ok) connect(this->document(), SIGNAL( contentsChange ( int , int , int )), this, SLOT(buildAutoCompletionList(int , int , int)) ); + document()->setModified(text_modified_ok); + text_modified_stop_ok=false; +} + +void CodeEdit::buildAutoCompletionList(int pos, int charsRemoved, int charsAdded ) +{ + octaveCommandTimer.stop(); + + if(brakets_match_ok) + { + braketsTimer.start(); + //braketsMatch(true); + } + document()->setModified(true); + context_changed_ok=true; + + completionPosition=pos; + completionTimer.start(); + completion.popup()->hide(); +} + +void CodeEdit::buildAutoCompletionList() +{ + QTextCursor cursor=textCursor(); + int pos=cursor.position()-1; + //printf("[CodeEdit::buildAutoCompletionList] pos=%d completionPosition=%d\n", pos, completionPosition); + if(pos!=completionPosition) + return; + + //QTextBlock block=document()->findBlock(0); + //Cleans brakets match information + + //while( block.isValid() ) + //{ + //block.setUserData(NULL); + //block=block.next(); + //} + //Buscando palabra a completar + QTextBlock block=document()->findBlock(pos); + //int _pos=pos; + pos-=block.position(); + int i=pos; + QString text=block.text(); + QRegExp re("([^a-zA-Z_0-9]+)"); + i=re.lastIndexIn(text, i); + //printf("pos=%d i=%d len=%d\n", pos, i, re.matchedLength()); + if( i==pos ) {completion.popup()->hide();return;} + QString word_to_complete=text.mid(i+1,pos-i); + //printf("i=%d word=>%s<\n",i, word_to_complete.toLocal8Bit().data()); + QString actual_word; + re.setPattern("([a-zA-Z_0-9]+)"); + i=re.indexIn(text, pos); + if( i==pos ) actual_word=word_to_complete+text.mid(pos+1,re.matchedLength()-1); + //printf("i=%d word=>%s<\n",i, actual_word.toLocal8Bit().data()); + + if(word_to_complete.length()==2) + { + completion_model->setStringList(syntax->octave_comands); + completion.setCompletionPrefix(word_to_complete); + completion.popup()->hide(); + + octaveCommandTimer.start(); + + return; + } + else if(word_to_complete.length()<3) {completion.popup()->hide();return;} + + //Searchs help for command + //printf("%s\n", word_to_complete.toLocal8Bit().data()); + emit dynamic_help_required(word_to_complete); + + //Se construye la lista de palabras a completar + + QTextBlock blockInit, blockEnd; + blockInit=document()->firstBlock(); + blockEnd =document()->lastBlock(); + completion_list.clear(); + buildAutoCompletionListSlide(completion_list, blockInit, blockEnd, word_to_complete, actual_word); + + if(completion_list.isEmpty()) {completion.popup()->hide();return;} + + completion_model->setStringList(completion_list); + + + QRect _position=cursorRect(); + + //printf("x=%d y=%d width=%d height=%d\n", _position.x(), _position.y(), _position.width(), _position.height() ); + + //_position.moveTo(_position.bottomRight() ); + ////_position.setWidth(100); + _position.setWidth(width()/3); + + completion.setCompletionPrefix(word_to_complete); + completion.complete(_position); + completion.popup()->show(); + completion.popup()->setFocus(Qt::TabFocusReason); +} + +void CodeEdit::buildAutoCompletionListSlide(QStringList &list, QTextBlock blockInit, QTextBlock blockEnd, QString word_to_complete, QString actual_word) +{ + //QStringList list; + + //printf("Buscando lista\n"); + //block=document()->findBlock(0); + + QTextBlock block=blockInit; + + //QString match; + QRegExp rx("([a-zA-Z_0-9]+)"); + + while( block.isValid() ) + { + QString text=block.text(); + int i = 0; + + while ((i = rx.indexIn(text, i)) != -1) { + QString word=rx.cap(1); + if( word.startsWith(word_to_complete) && !list.contains(word) && word!=actual_word ) + { + list << word; + //printf("i=%d word=>%s< actual_word=>%s<\n",i, word.toLocal8Bit().data(), actual_word.toLocal8Bit().data()); + } + i += rx.matchedLength(); + } + + if(block!=blockEnd) block=block.next(); + else break; + } +} + +void CodeEdit::octaveCommandCompletion() +{ + QRect _position=cursorRect(); + + _position.setWidth(width()); + + completion.complete(_position); + completion.popup()->show(); + completion.popup()->setFocus(Qt::TabFocusReason); +} + + +void CodeEdit::doCompletion(const QModelIndex &index) +{ + QString word=index.data().toString(); + QString prefix=completion.completionPrefix(); + + QString suffix=word.mid(prefix.length()); + + QTextCursor cursor=textCursor(); + cursor.insertText(suffix); + + completion.popup()->hide(); +} + +bool CodeEdit::getbraketsMatchOk() +{ + return syntax->getIsActiveBraketsMacth(); +} + +void CodeEdit::textModified_cb(bool ok) +{ + //printf("[CodeEdit::textModified_cb] Entered\n"); + if(text_modified_stop_ok) return; + emit text_modified(ok); + //printf("[CodeEdit::textModified_cb] text_modified emit\n"); +} + +void CodeEdit::publicBlockBoundingRectList(QVector &list, int &first_line) +{ + qreal pageBottom = /*viewport()->*/height(); + QPointF offset=contentOffset(); + QTextBlock block=firstVisibleBlock(); + first_line=block.blockNumber()+1; + qreal first_position=blockBoundingGeometry(block).topLeft().y(); + + for ( ; block.isValid(); block = block.next() ) + { + QRectF position=blockBoundingGeometry(block); + qreal y=position.topLeft().y()+offset.y()-first_position; + + if(y>pageBottom) break; + + list.append(y); + } +} + diff --git a/gui//src/CodeEdit.h b/gui//src/CodeEdit.h new file mode 100644 --- /dev/null +++ b/gui//src/CodeEdit.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2007 Alejandro Álvarez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CODEEDIT_H__ +#define __CODEEDIT_H__ + +#include +#include +#include +#include "Syntax.h" +#include +#include +#include +#include "config.h" +#include + + +struct UndoRedoItem +{ + int size, pos; + QString text; +}; + + +/**TextEdit that supports highlited syntax and autocompletion.*/ +class CodeEdit: public QPlainTextEdit +{ + Q_OBJECT + + bool auto_indent; + QCompleter completion; + QStringListModel *completion_model; + QTimer braketsTimer, octaveCommandTimer, completionTimer; + QStringList completion_list; + int completionPosition; + + /**Builds auto completion list from block blockInit to blockEnd.*/ + void buildAutoCompletionListSlide(QStringList &list, QTextBlock blockInit, QTextBlock blockEnd, QString word_to_complete, QString actual_word); + + bool text_modified_stop_ok; //Stops emit of text_modified signal + bool context_changed_ok; + + //Editor properties + + /**Automatic indention for while, if, for, switch, do and try statements.*/ + bool automatic_indention_statement_ok; + /**Auto completion*/ + bool autocompletion_ok; + /**Brackets Macth*/ + bool brakets_match_ok; + + protected: + Syntax *syntax; + QMenu contextMenu; + + void contextMenuEvent(QContextMenuEvent *e); + bool event( QEvent * e ); + + public slots: + void undo(); + void redo(); + void deleteSelection(); + void toggleBreakpoint(); + void braketsMatch(bool rehigh=true); + void cursorChanged_cb(); + void buildAutoCompletionList(int pos, int charsRemoved, int charsAdded ); + void buildAutoCompletionList(); + void doCompletion(const QModelIndex &index); + void octaveCommandCompletion(); + void textModified_cb(bool ok); + + public: + CodeEdit(QWidget *parent = 0, QString syntaxF = QString()); + ~CodeEdit(); + void setAutoindent(bool ai_ok); + bool getAutoindent(); + + bool getbraketsMatchOk(); + + /**List of y top left positions of bounding rects of each visible block of text. + * @param list List of top left positions. + * @param first_line First visible block in TextEdit. + */ + void publicBlockBoundingRectList(QVector &list, int &first_line); + + signals: + void toggleBreakpoint(int lineno); + + /**Dinamic help required.*/ + void dynamic_help_required(const QString &text); + + /**Text modified.*/ + void text_modified(bool ok); +}; + +#endif diff --git a/gui//src/MainWindow.cpp b/gui//src/MainWindow.cpp --- a/gui//src/MainWindow.cpp +++ b/gui//src/MainWindow.cpp @@ -42,14 +42,14 @@ m_variablesDockWidget = new VariablesDockWidget(this); m_historyDockWidget = new HistoryDockWidget(this); m_filesDockWidget = new FilesDockWidget(this); - m_codeEdit = new QCodeEdit(this); + m_codeEdit = new CodeEdit(this); m_centralTabWidget = new QTabWidget(this); setWindowTitle("Octave"); setCentralWidget(m_centralTabWidget); m_centralTabWidget->addTab(m_octaveTerminal, "Terminal"); - m_centralTabWidget->addTab(m_codeEdit->editor(), "Editor"); + m_centralTabWidget->addTab(m_codeEdit, "Editor"); addDockWidget(Qt::LeftDockWidgetArea, m_variablesDockWidget); addDockWidget(Qt::LeftDockWidgetArea, m_historyDockWidget); diff --git a/gui//src/MainWindow.h b/gui//src/MainWindow.h --- a/gui//src/MainWindow.h +++ b/gui//src/MainWindow.h @@ -27,7 +27,7 @@ #include "VariablesDockWidget.h" #include "HistoryDockWidget.h" #include "FilesDockWidget.h" -#include "qcodeedit.h" +#include "CodeEdit.h" // Octave includes #undef PACKAGE_BUGREPORT @@ -92,7 +92,7 @@ VariablesDockWidget *variablesDockWidget() { return m_variablesDockWidget; } HistoryDockWidget *historyDockWidget() { return m_historyDockWidget; } FilesDockWidget *filesDockWidget() { return m_filesDockWidget; } - QCodeEdit *codeEdit() { return m_codeEdit; } + CodeEdit *codeEdit() { return m_codeEdit; } public slots: private: @@ -102,7 +102,7 @@ VariablesDockWidget *m_variablesDockWidget; HistoryDockWidget *m_historyDockWidget; FilesDockWidget *m_filesDockWidget; - QCodeEdit *m_codeEdit; + CodeEdit *m_codeEdit; QTabWidget *m_centralTabWidget; // Threads for running octave and managing the data interaction. diff --git a/gui//src/Syntax.cpp b/gui//src/Syntax.cpp new file mode 100644 --- /dev/null +++ b/gui//src/Syntax.cpp @@ -0,0 +1,706 @@ +/* Copyright (C) 2006-2008 P.L. Lucas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "Syntax.h" +#include +#include +#include +#include +#include +#include + +//QMap > Syntax::instances; + +QList Syntax::rules; +QStringList Syntax::octave_comands; + +/*** Xml Handler ***/ +class SyntaxXmlHandler:public QXmlDefaultHandler +{ +private: + Syntax *syntax; + QString type_name, text; + struct Tag + { + QString tag, type; + QStringList items; + QList childs; + }; + QList stack; + + QStringList *octave_comands; +public: + // Constructor + SyntaxXmlHandler(Syntax *s, QStringList *octave_comands): QXmlDefaultHandler(), syntax(s) + { + this->octave_comands=octave_comands; + } + + bool startElement(const QString &/*namespaceURI*/, const QString &/*localName*/, + const QString &qname, const QXmlAttributes &atts) + { + Tag tag; + tag.tag=qname; + + if(qname == "list") + {// List block. Get the type name. + tag.type = atts.value("name").trimmed(); + if(tag.type=="functions") + { + tag.items << (*octave_comands); + } + } + //else if(qname == "item") + //{// Item. Next string is an item. + //} + else if(qname == "comment") + {// Comments. + syntax->setComment(atts.value("start"), "$", atts.value("name")); + } + + + stack.append(tag); + return true; + } + + + bool characters(const QString &ch) + { + text+=ch; + return true; + } + + bool endElement( const QString & /*namespaceURI*/, const QString & /*localName*/, const QString & qname ) + { + Tag tag; + + if(stack.isEmpty()) return true; + + tag=stack.last(); + stack.removeLast(); + + if(tag.tag!=qname) + { + printf("Error reading XML syntax\n"); + return false; + } + + if(qname == "list") + {// List block. Get the type name. + if(stack.last().tag=="list") + { + stack.last().childs.append(tag); + } + else + { + syntax->setItem(tag.items.join("|"), tag.type); + for(int i=0;isetItem(tag.childs[i].items.join("|"), tag.childs[i].type,tag.type); + } + } + } + else if(qname == "item") + {// Item. Next string is an item. + if(! text.trimmed().isEmpty() ) + stack.last().items << text.trimmed(); + } + + text=""; + + return true; + } +}; + + +/*** Block data ***/ + +class BlockData:public QTextBlockUserData +{ + public: + BlockData() {braket_start_pos=braket_end_pos=-1;} + ~BlockData (){} + int braket_start_pos, braket_end_pos; + QHash bracket; +}; + + + +/*** Syntax ***/ + +Syntax::Syntax(QTextDocument *parent): QSyntaxHighlighter(parent) +{ + QTextCharFormat f; + + QFont text_edit_font; + QString font_name="Monospace";//get_config("textEditFont"); + QString font_size="10";//get_config("textEditFontSize"); + if(font_name.isEmpty()) + { + font_name=text_edit_font.family(); + } + if(font_size.isEmpty()) + { + font_size=QString::number(text_edit_font.pointSize()); + } + text_edit_font.setFamily(font_name); + text_edit_font.setPointSize(font_size.toInt()); + + f.setFont(text_edit_font); + f.setFontWeight(QFont::Bold); + _format["keywords"] = f; + _format["commands"] = f; + f.setFontWeight(QFont::Normal); + f.setForeground(Qt::darkGreen); + _format["builtin"] = f; + f.setForeground(Qt::blue); + _format["functions"] = f; + // operators + f.setForeground(Qt::black); + _format["variables"] = f; + f.setForeground(Qt::darkMagenta); + _format["numbers"] = f; + f.setForeground(Qt::red); + _format["strings"] = f; + // delimiters + f.setForeground(Qt::darkGray); + _format["singleLine"] = f; + //Brackets matched + f.setForeground(Qt::black); + //f.setFontWeight(QFont::Bold); + f.setBackground(Qt::yellow); + _format["bracket match"]=f; + + active_ok=true; + + braketsMacth_ok=false; + + //printf("Syntax Builded\n"); + + //Add rules to vectors to help to highlightBlock method + __re.clear(); + __i_aux.clear(); + for(int n=0;npattern); + __i_aux.append(-1); + //printf("%s %d %d\n", rules.at(n)->type.toLocal8Bit().data(), __re.size(), __i_aux[n]); + } + +} + + +Syntax::~Syntax() +{ + //foreach(Rule *value, rules_map) + //{ + // delete value; + //} + + //This line is added because sometimes Qt try rehighlight text at destroy + setDocument(NULL); +} + +void Syntax::load(const QString &path) +{ + if(octave_comands.isEmpty()) + { + QString home=QDir::home().path()+"/.qtoctave/commands.txt"; + + QFile file(home); + + if (file.open(QFile::ReadOnly)) + { + char buf[1024]; + + while(file.readLine(buf, sizeof(buf))>=0) + { + octave_comands.append(QString(buf).trimmed()); + } + + file.close(); + } + + + } + + //rules = &(instances[path]); + if(rules.isEmpty()) + { + // Load from file + FILE *fl; + + fl = fopen(path.toLocal8Bit().constData(), "rt"); + if(!fl) + { + std::cerr << "[Syntax::load] Can not load the syntax file" << std::endl; + return; + } + + QFile file(path); + QXmlSimpleReader parser; + QXmlInputSource source(&file); + SyntaxXmlHandler handler(this, &octave_comands); + + file.open(fl, QIODevice::ReadOnly); + + parser.setContentHandler(&handler); + parser.setErrorHandler(&handler); + + parser.parse(&source); + + file.close(); + + fclose(fl); + + std::cout << "[Sytax::load] " + << path.toLocal8Bit().constData() + << " loaded" + << std::endl; + } + + +} + +// void Syntax::setItem(const QString &item, const QString &type) +// { +// Rule r; +// if(!item.isEmpty()) +// { +// r.pattern = QRegExp(item); +// r.type = type; +// r.format = _format[type]; +// rules.push_back(r); +// } +// } + +void Syntax::setItem(const QString &item, const QString &type, const QString parent) +{ + Rule *r; + if(!item.isEmpty()) + { + r=new Rule; + r->pattern = QRegExp(item); + r->type = type; + r->format = _format[type]; + rules_map[type]=r; + if(parent.isEmpty() || !rules_map.contains(parent)) + rules.push_back(r); + else + rules_map[parent]->rules.push_back(r); + } +} + +void Syntax::setComment(const QString &start, const QString &end, const QString &type) +{ + Rule *r; + if(!type.isEmpty()) + { + r=new Rule; + r->pattern = QRegExp(/*QString("^") +*/ start + ".*" + end); + r->type = type; + r->format = _format[type]; + rules_map[type]=r; + rules.push_back(r); + } +} + +void Syntax::setType(const QString &type, const QTextCharFormat &f) +{ + _format[type] = f; +} + +void Syntax::highlightBlock(const QString &str) +{ + //Para aumentar el rendimiento se hace una tabla i_aux con la posición de lo + //que ha encontrado cada expresión regular rules.at(n)->pattern. + //Se aplicará el formato debido a la Rule que tenga la i_aux más pequeña + if( !str.isEmpty() && !rules.isEmpty() && active_ok ) + { + + + //printf("Current block %d\n", currentBlock().blockNumber()); + + //setFormat(0, str.length(), _format["variables"]); + + int i=0, len=0; //Actual position + int n_min; //Minimal position + + BlockData *dat=(BlockData *)currentBlockUserData(); + if(dat!=NULL) + { + dat->bracket.clear(); + } + + //int *__i_aux=new int[rules.size()]; //Auxiliar position + //QRegExp *__re=new QRegExp[rules.size()]; + + //printf("rules %d re %d i_aux %d\n", rules.size(), __re.size(), __i_aux.size()); + + for(int n=0;n<__re.size();n++) + { + //re[n]=rules.at(n)->pattern; + __i_aux[n] = __re[n].indexIn( str, i); + //printf("%s %d %d\n", rules.at(n)->type.toLocal8Bit().data(), n, __i_aux[n]); + } + + while(i >= 0) + { + n_min=-1; + for(int n=0;n<__re.size();n++) + { + if(__i_aux[n]<0) continue; + if(__i_aux[n]type.toLocal8Bit().data(), n, i_aux[n], n_min, i); + if( n_min<0 || __i_aux[n_min]<0 || (__i_aux[n]>=0 && __i_aux[n]<__i_aux[n_min]) ) + { + n_min=n; + if(__i_aux[n]==i) break; + } + } + //printf("n_min=%d elegido\n", n_min); + if(n_min>=0) i=__i_aux[n_min]; + else break; + if( i<0 ) break; + len = __re[n_min].matchedLength(); + + //QStringList list=re[n_min].capturedTexts (); + //printf("\n"); + //for(int n=0;n%s<\n", n, list.at(n).toLocal8Bit().data() ); + //} + //printf("Aplicando %s i=%d len=%d\n", rules.at(n_min)->type.toLocal8Bit().data(), i, len); + if(len<1) break; + //QTextCharFormat i_format=format(i); + //if( !(i_format==strings) ) + + if(rules.at(n_min)->rules.isEmpty()) + { + setFormat(i, len, rules.at(n_min)->format); + + if( rules.at(n_min)->type=="delimiters" ) + { + QString bracket_found=__re[n_min].cap(); + + if(dat==NULL) + { + dat=new BlockData(); + setCurrentBlockUserData(dat); + } + dat->bracket[i]=bracket_found; + + //Do brackets macth + if( braketsMacth_ok && dat != NULL ) + { + if(dat->braket_start_pos>=0) + setFormat(dat->braket_start_pos, 1, _format["bracket match"]); + if(dat->braket_end_pos>=0) + setFormat(dat->braket_end_pos, 1, _format["bracket match"]); + } + } + + } + else + { + //Rules can contains another rules + QString text=str.mid(i,len); + //printf("text=%s\n", text.toLocal8Bit().data() ); + bool format_ok=true; + for(int n=0;nrules.size(); n++) + { + if(rules.at(n_min)->rules.at(n)->pattern.exactMatch(text)) + { + setFormat(i, len, rules.at(n_min)->rules.at(n)->format); + format_ok=false; + break; + } + } + if(format_ok) setFormat(i, len, rules.at(n_min)->format); + } + i+=len; + //printf("i=%d\n",i); + } + + //delete [] i_aux; + //delete [] re; + + } +} + + +int Syntax::forward_search(QTextBlock & block, int pos, char bracket_start, char bracket_end) +{ + int i=pos, open=0; + + while(block.isValid()) + { + /* + if(!block.text().isEmpty()) + { + QString str=block.text(); + int len=str.length(); + + //This line is added to check lower position + if(i<0) i=0; + + for(;i positions=dat->bracket.keys(); + qSort(positions); + for(int k=0;kbracket[b_pos].at(0); + + if(ch==bracket_end) + { + open--; + if(open==0) return b_pos; + } + else if(ch==bracket_start) open++; + } + } + + block=block.next(); + + i=0; + } + + return -1; +} + +int Syntax::backward_search(QTextBlock & block, int pos, char bracket_start, char bracket_end) +{ + int i=pos, open=0; + + while(block.isValid()) + { + /* + if(!block.text().isEmpty()) + { + QString str=block.text(); + int len=str.length(); + + //This line is added to check upper position + if(i>=len) i=len-1; + + for(;i>=0;i--) //i>=0 checks lower position + { + QChar ch=str.at(i); + + if(ch==bracket_start) + { + open--; + if(open==0) return i; + } + else if(ch==bracket_end) open++; + } + } + */ + + BlockData *dat=(BlockData *)block.userData(); + if(dat!=NULL) + { + QList positions=dat->bracket.keys(); + qSort(positions); + for(int k=positions.size()-1;k>=0;k--) + { + int b_pos=positions[k]; + if(b_pos>i) continue; + + QChar ch=dat->bracket[b_pos].at(0); + + if(ch==bracket_start) + { + open--; + if(open==0) return b_pos; + } + else if(ch==bracket_end) open++; + } + } + + block=block.previous(); + + if(block.isValid() && !block.text().isEmpty()) i=block.length()-1; + } + + return -1; +} + +static void set_block_data(QTextBlock & block0, QTextBlock & block1, int start, int end) +{ + BlockData *udat=(BlockData *)block0.userData(); + if(udat==NULL) + { + udat=new BlockData(); + block0.setUserData(udat); + } + udat->braket_start_pos=start; + + if(block0==block1) + { + udat->braket_end_pos=end; + } + else + { + BlockData *udat=(BlockData *)block1.userData(); + if(udat==NULL) + { + udat=new BlockData(); + block1.setUserData(udat); + } + udat->braket_end_pos=end; + } +} + +static void clear_block_data(QTextDocument *doc, bool rehigh ) +{ + QTextBlock block=doc->findBlock(0); + + while( block.isValid() ) + { + BlockData *udat=(BlockData *)block.userData(); + if(udat!=NULL && (udat->braket_end_pos!=-1 || udat->braket_start_pos!=-1) ) + { + udat->braket_end_pos=-1; udat->braket_start_pos=-1; + if(rehigh) + { + //QTextCursor cursor(doc); + //cursor.setPosition(block.position()); + //cursor.setBlockFormat(block.blockFormat()); + } + } + block=block.next(); + } +} + +void Syntax::braketsMacth(int pos, int &start, int &end, bool rehigh) +{ + QTextDocument *doc=document(); + + if(!rehigh) + { + clear_block_data(doc, true); + return; + } + + QTextBlock block0=doc->findBlock(pos), block1; + if(!block0.isValid() || block0.text().length()<=0) + { + return; + } + + + pos=pos-block0.position(); + if (block0.text().size()<=pos) pos=block0.text().size()-1; + if(pos<0) pos=0; + + QChar ch=block0.text().at(pos); + + BlockData *dat=(BlockData *)block0.userData(); + if(dat!=NULL) + { + if( !dat->bracket.contains(pos) ) ch=' '; + } + + + block1=block0; + + int i=-1; + if(ch=='(') + { + i=forward_search(block1,pos,'(', ')'); + } + else if(ch==')') + { + i=backward_search(block1,pos,'(', ')'); + } + else if(ch=='[') + { + i=forward_search(block1,pos,'[', ']'); + } + else if(ch==']') + { + i=backward_search(block1,pos,'[', ']'); + } + else if(ch=='{') + { + i=forward_search(block1,pos,'{', '}'); + } + else if(ch=='}') + { + i=backward_search(block1,pos,'{', '}'); + } + else + { + if( braketsMacth_ok ) + { + clear_block_data(doc, rehigh); + braketsMacth_ok=false; + } + + return; + } + + if(i>=0) + { + clear_block_data(doc, true); + //set_block_data(block0, block1, pos, i); + start=pos+block0.position(); + end=i+block1.position(); + //braketsMacth_ok=true; + //if(rehigh) rehighlight(); + + /* + QTextCursor cursor(doc); + + cursor.beginEditBlock(); + cursor.setPosition(block0.position()+pos); + cursor.setBlockFormat(block0.blockFormat()); + if(block1!=block0) + { + cursor.setPosition(block1.position()+i); + cursor.setBlockFormat(block1.blockFormat()); + } + cursor.endEditBlock(); + */ + } +} + + +void Syntax::setActive(bool active) +{ + active_ok=active; +} diff --git a/gui//src/Syntax.h b/gui//src/Syntax.h new file mode 100644 --- /dev/null +++ b/gui//src/Syntax.h @@ -0,0 +1,83 @@ +/* Copyright (C) 2006 P.L. Lucas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __SYNTAX_H__ +#define __SYNTAX_H__ +#include +#include +#include +#include +#include +#include "config.h" + +/**SyntaxHighlighter for Octave code.*/ +class Syntax: public QSyntaxHighlighter +{ + Q_OBJECT + public: + Syntax(QTextDocument *parent); + ~Syntax(); + void highlightBlock(const QString &str); + void load(const QString &file); + + //void setItem(const QString &item, const QString &type); + void setItem(const QString &item, const QString &type, const QString parent=QString() ); + void setComment(const QString &start, const QString &end, const QString &type); + void setType(const QString &type, const QTextCharFormat &format); + + /**Stops syntax highlight*/ + void setActive(bool active); + + static QStringList octave_comands; + + public slots: + + void braketsMacth(int pos, int &start, int &end, bool rehigh=true); + + /**Return true or false if brackets are been macthed*/ + inline bool getIsActiveBraketsMacth() {return braketsMacth_ok;} + + + private: + struct Rule + { + QRegExp pattern; + QString type; + QTextCharFormat format; + QList rules; + }; + + int backward_search(QTextBlock & block, int pos, char bracket_start, char bracket_end); + int forward_search(QTextBlock & block, int pos, char bracket_start, char bracket_end); + + //static QMap > instances; + + QMap rules_map; + + static QList rules; + QMap _format; + + //Next two properties are used inside highlightBlock method + QVector __i_aux; //Auxiliar positions + QVector __re; //Regular expresions + + bool active_ok; + bool braketsMacth_ok; +}; + +#endif