Highlighter has been added

This commit is contained in:
Arin Alexander 2017-09-16 16:04:29 +03:00
parent 090477fa68
commit aa66b6a057
9 changed files with 408 additions and 112 deletions

View File

@ -12,7 +12,8 @@ INCLUDEPATH += \
$$REPORT_PATH/bands \ $$REPORT_PATH/bands \
$$REPORT_PATH/base \ $$REPORT_PATH/base \
$$REPORT_PATH/objectinspector \ $$REPORT_PATH/objectinspector \
$$REPORT_PATH/databrowser $$REPORT_PATH/databrowser \
$$REPORT_PATH/scripteditor
SOURCES += \ SOURCES += \
$$REPORT_PATH/bands/lrpageheader.cpp \ $$REPORT_PATH/bands/lrpageheader.cpp \
@ -60,6 +61,7 @@ SOURCES += \
$$REPORT_PATH/objectsbrowser/lrobjectbrowser.cpp \ $$REPORT_PATH/objectsbrowser/lrobjectbrowser.cpp \
$$REPORT_PATH/scriptbrowser/lrscriptbrowser.cpp \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.cpp \
$$REPORT_PATH/scripteditor/lrscripteditor.cpp \ $$REPORT_PATH/scripteditor/lrscripteditor.cpp \
$$REPORT_PATH/scripteditor/lrcodeeditor.cpp \
$$REPORT_PATH/items/lrsubitemparentpropitem.cpp \ $$REPORT_PATH/items/lrsubitemparentpropitem.cpp \
$$REPORT_PATH/items/lralignpropitem.cpp \ $$REPORT_PATH/items/lralignpropitem.cpp \
$$REPORT_PATH/items/lrhorizontallayout.cpp \ $$REPORT_PATH/items/lrhorizontallayout.cpp \
@ -163,6 +165,7 @@ HEADERS += \
$$REPORT_PATH/objectsbrowser/lrobjectbrowser.h \ $$REPORT_PATH/objectsbrowser/lrobjectbrowser.h \
$$REPORT_PATH/scriptbrowser/lrscriptbrowser.h \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.h \
$$REPORT_PATH/scripteditor/lrscripteditor.h \ $$REPORT_PATH/scripteditor/lrscripteditor.h \
$$REPORT_PATH/scripteditor/lrcodeeditor.h \
$$REPORT_PATH/items/editors/lritemeditorwidget.h \ $$REPORT_PATH/items/editors/lritemeditorwidget.h \
$$REPORT_PATH/items/editors/lrfonteditorwidget.h \ $$REPORT_PATH/items/editors/lrfonteditorwidget.h \
$$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \ $$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \
@ -237,7 +240,7 @@ FORMS += \
$$REPORT_PATH/scriptbrowser/lrscriptbrowser.ui \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.ui \
$$REPORT_PATH/items/lrchartitemeditor.ui \ $$REPORT_PATH/items/lrchartitemeditor.ui \
$$REPORT_PATH/translationeditor/translationeditor.ui \ $$REPORT_PATH/translationeditor/translationeditor.ui \
$$REPORT_PATH/translationeditor/languageselectdialog.ui $$REPORT_PATH/translationeditor/languageselectdialog.ui \
$$REPORT_PATH/scripteditor/lrscripteditor.ui $$REPORT_PATH/scripteditor/lrscripteditor.ui
RESOURCES += \ RESOURCES += \

View File

@ -118,4 +118,10 @@ contains(CONFIG,build_translations){
#### EN AUTOMATIC TRANSLATIONS #### EN AUTOMATIC TRANSLATIONS
HEADERS += \
scripteditor/lrscripthighlighter.h
SOURCES += \
scripteditor/lrscripthighlighter.cpp

View File

@ -0,0 +1,193 @@
#include "lrcodeeditor.h"
#include <QAbstractItemView>
#include <QWidget>
#include <QCompleter>
#include <QKeyEvent>
#include <QScrollBar>
#include <QPainter>
#include <QTextBlock>
#include <QDebug>
#include "lrscripthighlighter.h"
namespace LimeReport{
CodeEditor::CodeEditor(QWidget *parent)
: QPlainTextEdit(parent), m_compleater(0)
{
lineNumberArea = new LineNumberArea(this);
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
updateLineNumberAreaWidth(0);
highlightCurrentLine();
(void) new ScriptHighlighter(document());
}
void CodeEditor::setCompleter(QCompleter *value)
{
if (value) disconnect(value,0,this,0);
m_compleater = value;
if (!m_compleater) return;
m_compleater->setWidget(this);
m_compleater->setCompletionMode(QCompleter::PopupCompletion);
m_compleater->setCaseSensitivity(Qt::CaseInsensitive);
connect(m_compleater,SIGNAL(activated(QString)),this,SLOT(insertCompletion(QString)));
}
void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent* event)
{
QPainter painter(lineNumberArea);
painter.fillRect(event->rect(), QPalette().background().color());
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + (int) blockBoundingRect(block).height();
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
QString number = QString::number(blockNumber + 1);
painter.setPen(QPalette().text().color());
painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
Qt::AlignCenter, number);
}
block = block.next();
top = bottom;
bottom = top + (int) blockBoundingRect(block).height();
++blockNumber;
}
}
int CodeEditor::lineNumberAreaWidth()
{
int digits = 1;
int max = qMax(1, blockCount());
while (max >= 10) {
max /= 10;
++digits;
}
int space = fontMetrics().width(QLatin1Char('9'))*2 + fontMetrics().width(QLatin1Char('9')) * digits;
return space;
}
void CodeEditor::keyPressEvent(QKeyEvent *e)
{
if (m_compleater && m_compleater->popup()->isVisible()) {
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
e->ignore();
return;
default:
break;
}
}
bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_Space);
if (!m_compleater || !isShortcut) QPlainTextEdit::keyPressEvent(e);
const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
if (!m_compleater || (ctrlOrShift && e->text().isEmpty()))
return;
static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3
|| eow.contains(e->text().right(1)))) {
m_compleater->popup()->hide();
return;
}
if (completionPrefix != m_compleater->completionPrefix()) {
m_compleater->setCompletionPrefix(completionPrefix);
m_compleater->popup()->setCurrentIndex(m_compleater->completionModel()->index(0, 0));
}
QRect cr = cursorRect();
cr.setWidth(m_compleater->popup()->sizeHintForColumn(0)
+ m_compleater->popup()->verticalScrollBar()->sizeHint().width());
m_compleater->complete(cr);
}
void CodeEditor::focusInEvent(QFocusEvent *e)
{
if (m_compleater) m_compleater->setWidget(this);
QPlainTextEdit::focusInEvent(e);
}
void CodeEditor::resizeEvent(QResizeEvent* event)
{
QPlainTextEdit::resizeEvent(event);
QRect cr = contentsRect();
lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
QString CodeEditor::textUnderCursor() const
{
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
return tc.selectedText();
}
void CodeEditor::insertCompletion(const QString &completion)
{
if (m_compleater->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - m_compleater->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
}
void CodeEditor::updateLineNumberAreaWidth(int newBlockCount)
{
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}
void CodeEditor::highlightCurrentLine()
{
QList<QTextEdit::ExtraSelection> extraSelections;
if (!isReadOnly()) {
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(QPalette().background().color()).darker(160);
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
extraSelections.append(selection);
}
setExtraSelections(extraSelections);
}
void CodeEditor::updateLineNumberArea(const QRect& rect, int dy)
{
if (dy)
lineNumberArea->scroll(0, dy);
else
lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
if (rect.contains(viewport()->rect()))
updateLineNumberAreaWidth(0);
}
} //namespace LimeReport

View File

@ -0,0 +1,64 @@
#ifndef LRCODEEDITOR_H
#define LRCODEEDITOR_H
#include <QPlainTextEdit>
#include <QSyntaxHighlighter>
QT_BEGIN_NAMESPACE
class QWidget;
class QCompleter;
class QKeyEvent;
class QScrollBar;
QT_END_NAMESPACE
namespace LimeReport{
class CodeEditor :public QPlainTextEdit
{
Q_OBJECT
public:
CodeEditor(QWidget* parent=0);
void setCompleter(QCompleter* value);
QCompleter* compleater() const{ return m_compleater;}
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
protected:
void keyPressEvent(QKeyEvent *e);
void focusInEvent(QFocusEvent *e);
void resizeEvent(QResizeEvent *event);
private:
QString textUnderCursor() const;
private slots:
void insertCompletion(const QString& completion);
void updateLineNumberAreaWidth(int newBlockCount);
void highlightCurrentLine();
void updateLineNumberArea(const QRect &rect, int dy);
private:
QCompleter* m_compleater;
QWidget *lineNumberArea;
};
class LineNumberArea : public QWidget
{
public:
LineNumberArea(CodeEditor *editor) : QWidget(editor) {
codeEditor = editor;
}
QSize sizeHint() const {
return QSize(codeEditor->lineNumberAreaWidth(), 0);
}
protected:
void paintEvent(QPaintEvent *event) {
codeEditor->lineNumberAreaPaintEvent(event);
}
private:
CodeEditor *codeEditor;
};
} // namespace LimeReport
#endif // LRCODEEDITOR_H

View File

@ -9,93 +9,6 @@
namespace LimeReport{ namespace LimeReport{
TextEditorWithCompleater::TextEditorWithCompleater(QWidget *parent)
: QTextEdit(parent),m_compleater(0)
{
}
void TextEditorWithCompleater::setCompleter(QCompleter *value)
{
if (value) disconnect(value,0,this,0);
m_compleater = value;
if (!m_compleater) return;
m_compleater->setWidget(this);
m_compleater->setCompletionMode(QCompleter::PopupCompletion);
m_compleater->setCaseSensitivity(Qt::CaseInsensitive);
connect(m_compleater,SIGNAL(activated(QString)),this,SLOT(insertCompletion(QString)));
}
void TextEditorWithCompleater::keyPressEvent(QKeyEvent *e)
{
if (m_compleater && m_compleater->popup()->isVisible()) {
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
e->ignore();
return;
default:
break;
}
}
bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_Space);
if (!m_compleater || !isShortcut) QTextEdit::keyPressEvent(e);
const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
if (!m_compleater || (ctrlOrShift && e->text().isEmpty()))
return;
static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3
|| eow.contains(e->text().right(1)))) {
m_compleater->popup()->hide();
return;
}
if (completionPrefix != m_compleater->completionPrefix()) {
m_compleater->setCompletionPrefix(completionPrefix);
m_compleater->popup()->setCurrentIndex(m_compleater->completionModel()->index(0, 0));
}
QRect cr = cursorRect();
cr.setWidth(m_compleater->popup()->sizeHintForColumn(0)
+ m_compleater->popup()->verticalScrollBar()->sizeHint().width());
m_compleater->complete(cr);
}
void TextEditorWithCompleater::focusInEvent(QFocusEvent *e)
{
if (m_compleater) m_compleater->setWidget(this);
QTextEdit::focusInEvent(e);
}
QString TextEditorWithCompleater::textUnderCursor() const
{
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
return tc.selectedText();
}
void TextEditorWithCompleater::insertCompletion(const QString &completion)
{
if (m_compleater->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - m_compleater->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
}
ScriptEditor::ScriptEditor(QWidget *parent) : ScriptEditor::ScriptEditor(QWidget *parent) :
QWidget(parent), QWidget(parent),
ui(new Ui::ScriptEditor) ui(new Ui::ScriptEditor)

View File

@ -12,24 +12,6 @@ namespace LimeReport{
class ReportEnginePrivate; class ReportEnginePrivate;
class BaseDesignIntf; class BaseDesignIntf;
class TextEditorWithCompleater :public QTextEdit
{
Q_OBJECT
public:
TextEditorWithCompleater(QWidget* parent=0);
void setCompleter(QCompleter* value);
QCompleter* compleater() const{ return m_compleater;}
protected:
virtual void keyPressEvent(QKeyEvent *e);
virtual void focusInEvent(QFocusEvent *e);
private:
QString textUnderCursor() const;
private slots:
void insertCompletion(const QString& completion);
private:
QCompleter* m_compleater;
};
namespace Ui { namespace Ui {
class ScriptEditor; class ScriptEditor;
} }

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>ScriptEditor</class> <class>LimeReport::ScriptEditor</class>
<widget class="QWidget" name="ScriptEditor"> <widget class="QWidget" name="LimeReport::ScriptEditor">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
@ -52,7 +52,7 @@
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<widget class="TextEditorWithCompleater" name="textEdit"> <widget class="LimeReport::CodeEditor" name="textEdit">
<property name="font"> <property name="font">
<font> <font>
<pointsize>12</pointsize> <pointsize>12</pointsize>
@ -129,9 +129,9 @@
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>TextEditorWithCompleater</class> <class>LimeReport::CodeEditor</class>
<extends>QTextEdit</extends> <extends>QTextEdit</extends>
<header>lrscripteditor.h</header> <header>lrcodeeditor.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>

View File

@ -0,0 +1,118 @@
#include "lrscripthighlighter.h"
#include <QDebug>
namespace LimeReport{
#define KEYWORDS_COUNT 59
static const char *const keywords[KEYWORDS_COUNT] = {
"do","if","in","for","int","new","try","var","byte","case","char","else","enum",
"goto","long","null","this","true","void","with","break","catch","class","const",
"false","final","float","short","super","throw","while","delete","double","export",
"import","native","public","return","static","switch","throws","typeof","boolean",
"default","extends","finally","package","private","abstract","continue","debugger",
"function","volatile","interface","protected","transient","implements","instanceof",
"synchronized"
};
enum LiteralsType{SpaceFound, AlpahabetFound, NumberFound, HashFound, SlashFound, AsterixFound,
BracketFound, QuotationFound, ApostropheFound, SeparatorFound, BackSlashFound, LiteralsCount};
enum States {MayBeKeyWord, Code, MayBeComment, Comment, Comment2, MayBeComment2End, String, String2, MayBeNumber, Separator, StatesCount};
void ScriptHighlighter::highlightBlock(const QString& text)
{
int literal = -1;
bool lastWasBackSlash = false;
int state = previousBlockState() != -1 ? previousBlockState() : MayBeKeyWord ;
int oldState = -1;
int stateMaschine[StatesCount][LiteralsCount] = {
{Separator, MayBeKeyWord, Code, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Separator},
{Separator, Code, Code, Separator, Separator, Separator, Separator, String, String2, Separator, Separator},
{Separator, Code, Code, Code, Comment, Comment2, Code, String, String2, Separator, Code},
{Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment},
{Comment2, Comment2, Comment2, Comment2, Comment2, MayBeComment2End, Comment2, Comment2, Comment2, Comment2, Comment2},
{Comment2, Comment2, Comment2, Comment2, Separator, Comment2, Comment2, Comment2, Comment2, Comment2, Comment2},
{String, String, String, String, String, String, String, Separator, String, String, String},
{String2, String2, String2, String2, String2, String2, String2, String2, Separator, String2, String2},
{Separator, Code, MayBeNumber, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Code},
{Separator, MayBeKeyWord, MayBeNumber, Separator, MayBeComment, String, String2, Separator, Separator }
};
QString buffer;
if (text.isEmpty()) return;
int i = 0;
for (;;){
QChar currentChar = text.at(i);
switch(currentChar.toLatin1()){
case ' ':
literal = SpaceFound;
break;
case '/':
literal = SlashFound;
break;
case '*':
literal = AsterixFound;
break;
case '#':
literal = HashFound;
break;
case '\'':
literal = ApostropheFound;
break;
case '\\':
literal = BackSlashFound;
break;
case '"':
literal = QuotationFound;
break;
case '{': case '[': case '(':
case '}': case ']': case ')':
literal = BracketFound;
break;
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': case '0':
literal = NumberFound;
break;
default:
if (currentChar.isLetter())
literal = AlpahabetFound;
else
literal = SeparatorFound;
};
lastWasBackSlash = !lastWasBackSlash && currentChar == QLatin1Char('\\');
oldState = state;
state = stateMaschine[state][literal];
if (oldState != state){
switch( state ){
case MayBeComment:
buffer.clear();
buffer += currentChar;
break;
case Comment2:
buffer += currentChar;
qDebug()<<buffer;
break;
case Separator:
switch(oldState){
case Comment2:
qDebug()<<buffer;
buffer.clear();
break;
}
default:
break;
}
} else {
buffer += currentChar;
}
i++;
if ( i>= text.length()) break;
}
}
} // namespace LimeReport

View File

@ -0,0 +1,17 @@
#ifndef LRSCRIPTHIGHLIGHTER_H
#define LRSCRIPTHIGHLIGHTER_H
#include <QSyntaxHighlighter>
namespace LimeReport{
class ScriptHighlighter : QSyntaxHighlighter{
public:
ScriptHighlighter(QTextDocument* parent): QSyntaxHighlighter(parent){}
// QSyntaxHighlighter interface
protected:
void highlightBlock(const QString& text);
};
}
#endif // LRSCRIPTHIGHLIGHTER_H