From 090477fa684319385f844b8bd43181b676527724 Mon Sep 17 00:00:00 2001 From: Arin Alexander Date: Wed, 13 Sep 2017 17:16:54 +0300 Subject: [PATCH 1/4] Initial commit --- limereport/items/lrtextitemeditor.cpp | 1 - limereport/limereport.pri | 5 +- limereport/lrreportdesignwidget.cpp | 12 ++ limereport/lrreportdesignwidget.h | 5 +- limereport/scripteditor/lrscripteditor.cpp | 189 +++++++++++++++++++++ limereport/scripteditor/lrscripteditor.h | 56 ++++++ limereport/scripteditor/lrscripteditor.ui | 139 +++++++++++++++ 7 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 limereport/scripteditor/lrscripteditor.cpp create mode 100644 limereport/scripteditor/lrscripteditor.h create mode 100644 limereport/scripteditor/lrscripteditor.ui diff --git a/limereport/items/lrtextitemeditor.cpp b/limereport/items/lrtextitemeditor.cpp index 76e6891..f46333d 100644 --- a/limereport/items/lrtextitemeditor.cpp +++ b/limereport/items/lrtextitemeditor.cpp @@ -33,7 +33,6 @@ #include "lrdatasourcemanager.h" #include "lrscriptenginemanager.h" #include "lrdatadesignintf.h" -#include "lrdatasourcemanager.h" #include #include diff --git a/limereport/limereport.pri b/limereport/limereport.pri index 69af57f..d27d6f7 100644 --- a/limereport/limereport.pri +++ b/limereport/limereport.pri @@ -59,6 +59,7 @@ SOURCES += \ $$REPORT_PATH/objectinspector/lrpropertydelegate.cpp \ $$REPORT_PATH/objectsbrowser/lrobjectbrowser.cpp \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.cpp \ + $$REPORT_PATH/scripteditor/lrscripteditor.cpp \ $$REPORT_PATH/items/lrsubitemparentpropitem.cpp \ $$REPORT_PATH/items/lralignpropitem.cpp \ $$REPORT_PATH/items/lrhorizontallayout.cpp \ @@ -161,6 +162,7 @@ HEADERS += \ $$REPORT_PATH/objectinspector/lrpropertydelegate.h \ $$REPORT_PATH/objectsbrowser/lrobjectbrowser.h \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.h \ + $$REPORT_PATH/scripteditor/lrscripteditor.h \ $$REPORT_PATH/items/editors/lritemeditorwidget.h \ $$REPORT_PATH/items/editors/lrfonteditorwidget.h \ $$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \ @@ -235,7 +237,8 @@ FORMS += \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.ui \ $$REPORT_PATH/items/lrchartitemeditor.ui \ $$REPORT_PATH/translationeditor/translationeditor.ui \ - $$PWD/translationeditor/languageselectdialog.ui + $$REPORT_PATH/translationeditor/languageselectdialog.ui + $$REPORT_PATH/scripteditor/lrscripteditor.ui RESOURCES += \ $$REPORT_PATH/objectinspector/lobjectinspector.qrc \ diff --git a/limereport/lrreportdesignwidget.cpp b/limereport/lrreportdesignwidget.cpp index 9472f13..2b7f160 100644 --- a/limereport/lrreportdesignwidget.cpp +++ b/limereport/lrreportdesignwidget.cpp @@ -35,6 +35,7 @@ #include "lrsettingdialog.h" #include "dialogdesigner/lrdialogdesigner.h" #include "translationeditor/translationeditor.h" +#include "scripteditor/lrscripteditor.h" #include #include @@ -246,6 +247,12 @@ void ReportDesignWidget::createTabs(){ m_tabWidget->setTabWhatsThis(pageIndex,"script"); m_tabWidget->setCurrentIndex(0); + m_newScriptEditor = new ScriptEditor(this); + m_newScriptEditor->setReportEngine(m_report); + pageIndex = m_tabWidget->addTab(m_newScriptEditor,QIcon(),tr("New Script Editor")); + m_tabWidget->setTabWhatsThis(pageIndex,"script"); + m_tabWidget->setCurrentIndex(0); + #ifdef HAVE_QTDESIGNER_INTEGRATION QWidget* dialogDesigner; foreach(DialogDescriber::Ptr dialogDesc, m_report->scriptContext()->dialogDescribers()){ @@ -787,6 +794,11 @@ void ReportDesignWidget::slotCurrentTabChanged(int index) if (activeTabType() == Translations){ m_traslationEditor->setReportEngine(report()); } + + if (activeTabType() == Script){ + m_newScriptEditor->initCompleter(); + } + emit activePageChanged(); } diff --git a/limereport/lrreportdesignwidget.h b/limereport/lrreportdesignwidget.h index 4d3b25d..d710537 100644 --- a/limereport/lrreportdesignwidget.h +++ b/limereport/lrreportdesignwidget.h @@ -51,6 +51,8 @@ class ReportDesignWindow; class DialogDesignerManager; class DialogDesigner; class TranslationEditor; +class ScriptEditor; + class ReportDesignWidget : public QWidget { @@ -190,6 +192,7 @@ private: ReportEnginePrivate* m_report; QGraphicsView *m_view; QTextEdit* m_scriptEditor; + ScriptEditor* m_newScriptEditor; TranslationEditor* m_traslationEditor; #ifdef HAVE_QTDESIGNER_INTEGRATION DialogDesignerManager* m_dialogDesignerManager; @@ -205,5 +208,5 @@ private: bool m_dialogChanged; }; -} +} // namespace LimeReport #endif // LRREPORTDESIGNWIDGET_H diff --git a/limereport/scripteditor/lrscripteditor.cpp b/limereport/scripteditor/lrscripteditor.cpp new file mode 100644 index 0000000..80bbd27 --- /dev/null +++ b/limereport/scripteditor/lrscripteditor.cpp @@ -0,0 +1,189 @@ +#include "lrscripteditor.h" +#include "ui_lrscripteditor.h" +#include + +#include "lrdatasourcemanager.h" +#include "lrscriptenginemanager.h" +#include "lrdatadesignintf.h" +#include "lrreportengine_p.h" + +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) : + QWidget(parent), + ui(new Ui::ScriptEditor) +{ + ui->setupUi(this); + m_completer = new QCompleter(this); + ui->textEdit->setCompleter(m_completer); +} + +ScriptEditor::~ScriptEditor() +{ + delete ui; +} + +void ScriptEditor::setReportEngine(ReportEnginePrivate* reportEngine) +{ + m_reportEngine = reportEngine; + DataSourceManager* dm = m_reportEngine->dataManager(); + ScriptEngineManager& se = LimeReport::ScriptEngineManager::instance(); + se.setDataManager(dm); + + initCompleter(); + + if (dm){ + if (dm->isNeedUpdateDatasourceModel()) + dm->updateDatasourceModel(); + ui->twData->setModel(dm->datasourcesModel()); + ui->twScriptEngine->setModel(se.model()); + } + +} + +void ScriptEditor::initCompleter() +{ + QStringList dataWords; + DataSourceManager* dm = m_reportEngine->dataManager(); + ScriptEngineManager& se = LimeReport::ScriptEngineManager::instance(); + + QJSValue globalObject = se.scriptEngine()->globalObject(); + QJSValueIterator it(globalObject); + while (it.hasNext()){ + it.next(); + if (it.value().isCallable() ){ + dataWords << it.name(); + } + } + + foreach(const QString &dsName,dm->dataSourceNames()){ + dataWords << dsName; + foreach(const QString &field, dm->fieldNames(dsName)){ + dataWords<variableNames()) { + dataWords << varName.remove("#"); + } + + for ( int i = 0; i < m_reportEngine->pageCount(); ++i){ + PageDesignIntf* page = m_reportEngine->pageAt(i); + dataWords << page->pageItem()->objectName(); + foreach (BaseDesignIntf* item, page->pageItem()->childBaseItems()){ + addItemToCompleater(page->pageItem()->objectName(), item, dataWords); + } + } + + m_completer->setModel(new QStringListModel(dataWords,m_completer)); +} + +void ScriptEditor::addItemToCompleater(const QString& pageName, BaseDesignIntf* item, QStringList& dataWords) +{ + BandDesignIntf* band = dynamic_cast(item); + if (band){ + dataWords << band->objectName(); + dataWords << pageName+"_"+band->objectName(); + dataWords << pageName+"_"+band->objectName()+".beforeRender"; + dataWords << pageName+"_"+item->objectName()+".afterData"; + dataWords << pageName+"_"+band->objectName()+".afterRender"; + foreach (BaseDesignIntf* child, band->childBaseItems()){ + addItemToCompleater(pageName, child, dataWords); + } + } else { + dataWords << item->objectName(); + dataWords << pageName+"_"+item->objectName(); + dataWords << pageName+"_"+item->objectName()+".beforeRender"; + dataWords << pageName+"_"+item->objectName()+".afterData"; + dataWords << pageName+"_"+item->objectName()+".afterRender"; + } +} + +} // namespace LimeReport diff --git a/limereport/scripteditor/lrscripteditor.h b/limereport/scripteditor/lrscripteditor.h new file mode 100644 index 0000000..7674025 --- /dev/null +++ b/limereport/scripteditor/lrscripteditor.h @@ -0,0 +1,56 @@ +#ifndef LRSCRIPTEDITOR_H +#define LRSCRIPTEDITOR_H + +#include +#include +#include +#include +#include + +namespace LimeReport{ + +class ReportEnginePrivate; +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 { +class ScriptEditor; +} + +class ScriptEditor : public QWidget +{ + Q_OBJECT +public: + explicit ScriptEditor(QWidget *parent = 0); + ~ScriptEditor(); + void setReportEngine(ReportEnginePrivate* reportEngine); + void initCompleter(); +private: + void addItemToCompleater(const QString& pageName, BaseDesignIntf* item, QStringList& dataWords); +private: + Ui::ScriptEditor *ui; + ReportEnginePrivate* m_reportEngine; + QCompleter* m_completer; + +}; + +} // namespace LimeReport + +#endif // LRSCRIPTEDITOR_H diff --git a/limereport/scripteditor/lrscripteditor.ui b/limereport/scripteditor/lrscripteditor.ui new file mode 100644 index 0000000..5a5af17 --- /dev/null +++ b/limereport/scripteditor/lrscripteditor.ui @@ -0,0 +1,139 @@ + + + ScriptEditor + + + + 0 + 0 + 706 + 541 + + + + Form + + + + + + + 0 + 0 + + + + Content + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + 12 + + + + + + QTabWidget::South + + + 0 + + + + Data + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + false + + + + + + + + Functions + + + + + + Qt::Vertical + + + + false + + + + + + + + + + + + + + + + + + + + + + TextEditorWithCompleater + QTextEdit +
lrscripteditor.h
+
+
+ + +
From aa66b6a057c83e12f01062167c49c3443fe2b981 Mon Sep 17 00:00:00 2001 From: Arin Alexander Date: Sat, 16 Sep 2017 16:04:29 +0300 Subject: [PATCH 2/4] Highlighter has been added --- limereport/limereport.pri | 7 +- limereport/limereport.pro | 6 + limereport/scripteditor/lrcodeeditor.cpp | 193 ++++++++++++++++++ limereport/scripteditor/lrcodeeditor.h | 64 ++++++ limereport/scripteditor/lrscripteditor.cpp | 87 -------- limereport/scripteditor/lrscripteditor.h | 18 -- limereport/scripteditor/lrscripteditor.ui | 10 +- .../scripteditor/lrscripthighlighter.cpp | 118 +++++++++++ limereport/scripteditor/lrscripthighlighter.h | 17 ++ 9 files changed, 408 insertions(+), 112 deletions(-) create mode 100644 limereport/scripteditor/lrcodeeditor.cpp create mode 100644 limereport/scripteditor/lrcodeeditor.h create mode 100644 limereport/scripteditor/lrscripthighlighter.cpp create mode 100644 limereport/scripteditor/lrscripthighlighter.h diff --git a/limereport/limereport.pri b/limereport/limereport.pri index d27d6f7..649f8f6 100644 --- a/limereport/limereport.pri +++ b/limereport/limereport.pri @@ -12,7 +12,8 @@ INCLUDEPATH += \ $$REPORT_PATH/bands \ $$REPORT_PATH/base \ $$REPORT_PATH/objectinspector \ - $$REPORT_PATH/databrowser + $$REPORT_PATH/databrowser \ + $$REPORT_PATH/scripteditor SOURCES += \ $$REPORT_PATH/bands/lrpageheader.cpp \ @@ -60,6 +61,7 @@ SOURCES += \ $$REPORT_PATH/objectsbrowser/lrobjectbrowser.cpp \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.cpp \ $$REPORT_PATH/scripteditor/lrscripteditor.cpp \ + $$REPORT_PATH/scripteditor/lrcodeeditor.cpp \ $$REPORT_PATH/items/lrsubitemparentpropitem.cpp \ $$REPORT_PATH/items/lralignpropitem.cpp \ $$REPORT_PATH/items/lrhorizontallayout.cpp \ @@ -163,6 +165,7 @@ HEADERS += \ $$REPORT_PATH/objectsbrowser/lrobjectbrowser.h \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.h \ $$REPORT_PATH/scripteditor/lrscripteditor.h \ + $$REPORT_PATH/scripteditor/lrcodeeditor.h \ $$REPORT_PATH/items/editors/lritemeditorwidget.h \ $$REPORT_PATH/items/editors/lrfonteditorwidget.h \ $$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \ @@ -237,7 +240,7 @@ FORMS += \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.ui \ $$REPORT_PATH/items/lrchartitemeditor.ui \ $$REPORT_PATH/translationeditor/translationeditor.ui \ - $$REPORT_PATH/translationeditor/languageselectdialog.ui + $$REPORT_PATH/translationeditor/languageselectdialog.ui \ $$REPORT_PATH/scripteditor/lrscripteditor.ui RESOURCES += \ diff --git a/limereport/limereport.pro b/limereport/limereport.pro index e98a9d1..fa57ae6 100644 --- a/limereport/limereport.pro +++ b/limereport/limereport.pro @@ -118,4 +118,10 @@ contains(CONFIG,build_translations){ #### EN AUTOMATIC TRANSLATIONS +HEADERS += \ + scripteditor/lrscripthighlighter.h + +SOURCES += \ + scripteditor/lrscripthighlighter.cpp + diff --git a/limereport/scripteditor/lrcodeeditor.cpp b/limereport/scripteditor/lrcodeeditor.cpp new file mode 100644 index 0000000..ae3aec9 --- /dev/null +++ b/limereport/scripteditor/lrcodeeditor.cpp @@ -0,0 +1,193 @@ +#include "lrcodeeditor.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 diff --git a/limereport/scripteditor/lrcodeeditor.h b/limereport/scripteditor/lrcodeeditor.h new file mode 100644 index 0000000..83c2dc0 --- /dev/null +++ b/limereport/scripteditor/lrcodeeditor.h @@ -0,0 +1,64 @@ +#ifndef LRCODEEDITOR_H +#define LRCODEEDITOR_H + +#include +#include + +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 diff --git a/limereport/scripteditor/lrscripteditor.cpp b/limereport/scripteditor/lrscripteditor.cpp index 80bbd27..613926e 100644 --- a/limereport/scripteditor/lrscripteditor.cpp +++ b/limereport/scripteditor/lrscripteditor.cpp @@ -9,93 +9,6 @@ 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) : QWidget(parent), ui(new Ui::ScriptEditor) diff --git a/limereport/scripteditor/lrscripteditor.h b/limereport/scripteditor/lrscripteditor.h index 7674025..6943fcb 100644 --- a/limereport/scripteditor/lrscripteditor.h +++ b/limereport/scripteditor/lrscripteditor.h @@ -12,24 +12,6 @@ namespace LimeReport{ class ReportEnginePrivate; 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 { class ScriptEditor; } diff --git a/limereport/scripteditor/lrscripteditor.ui b/limereport/scripteditor/lrscripteditor.ui index 5a5af17..49fcfb6 100644 --- a/limereport/scripteditor/lrscripteditor.ui +++ b/limereport/scripteditor/lrscripteditor.ui @@ -1,7 +1,7 @@ - ScriptEditor - + LimeReport::ScriptEditor + 0 @@ -52,7 +52,7 @@ Qt::Horizontal - + 12 @@ -129,9 +129,9 @@ - TextEditorWithCompleater + LimeReport::CodeEditor QTextEdit -
lrscripteditor.h
+
lrcodeeditor.h
diff --git a/limereport/scripteditor/lrscripthighlighter.cpp b/limereport/scripteditor/lrscripthighlighter.cpp new file mode 100644 index 0000000..d85a571 --- /dev/null +++ b/limereport/scripteditor/lrscripthighlighter.cpp @@ -0,0 +1,118 @@ +#include "lrscripthighlighter.h" +#include + +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()<= text.length()) break; + } +} + +} // namespace LimeReport diff --git a/limereport/scripteditor/lrscripthighlighter.h b/limereport/scripteditor/lrscripthighlighter.h new file mode 100644 index 0000000..3a54693 --- /dev/null +++ b/limereport/scripteditor/lrscripthighlighter.h @@ -0,0 +1,17 @@ +#ifndef LRSCRIPTHIGHLIGHTER_H +#define LRSCRIPTHIGHLIGHTER_H + +#include + +namespace LimeReport{ + +class ScriptHighlighter : QSyntaxHighlighter{ +public: + ScriptHighlighter(QTextDocument* parent): QSyntaxHighlighter(parent){} + // QSyntaxHighlighter interface +protected: + void highlightBlock(const QString& text); +}; + +} +#endif // LRSCRIPTHIGHLIGHTER_H From f4dbddafade4569b0557a06c93c025713f310b8b Mon Sep 17 00:00:00 2001 From: Arin Alexander Date: Tue, 19 Sep 2017 19:05:38 +0300 Subject: [PATCH 3/4] Script highlighter has been finished --- limereport/limereport.pri | 2 + limereport/limereport.pro | 6 - limereport/scripteditor/lrcodeeditor.cpp | 135 +++++++++++++++++- limereport/scripteditor/lrcodeeditor.h | 7 + .../scripteditor/lrscripthighlighter.cpp | 130 +++++++++++++++-- limereport/scripteditor/lrscripthighlighter.h | 37 ++++- 6 files changed, 295 insertions(+), 22 deletions(-) diff --git a/limereport/limereport.pri b/limereport/limereport.pri index 649f8f6..385ad4d 100644 --- a/limereport/limereport.pri +++ b/limereport/limereport.pri @@ -62,6 +62,7 @@ SOURCES += \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.cpp \ $$REPORT_PATH/scripteditor/lrscripteditor.cpp \ $$REPORT_PATH/scripteditor/lrcodeeditor.cpp \ + $$REPORT_PATH/scripteditor/lrscripthighlighter.cpp \ $$REPORT_PATH/items/lrsubitemparentpropitem.cpp \ $$REPORT_PATH/items/lralignpropitem.cpp \ $$REPORT_PATH/items/lrhorizontallayout.cpp \ @@ -166,6 +167,7 @@ HEADERS += \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.h \ $$REPORT_PATH/scripteditor/lrscripteditor.h \ $$REPORT_PATH/scripteditor/lrcodeeditor.h \ + $$REPORT_PATH/scripteditor/lrscripthighlighter.h \ $$REPORT_PATH/items/editors/lritemeditorwidget.h \ $$REPORT_PATH/items/editors/lrfonteditorwidget.h \ $$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \ diff --git a/limereport/limereport.pro b/limereport/limereport.pro index fa57ae6..e98a9d1 100644 --- a/limereport/limereport.pro +++ b/limereport/limereport.pro @@ -118,10 +118,4 @@ contains(CONFIG,build_translations){ #### EN AUTOMATIC TRANSLATIONS -HEADERS += \ - scripteditor/lrscripthighlighter.h - -SOURCES += \ - scripteditor/lrscripthighlighter.cpp - diff --git a/limereport/scripteditor/lrcodeeditor.cpp b/limereport/scripteditor/lrcodeeditor.cpp index ae3aec9..273a1b3 100644 --- a/limereport/scripteditor/lrcodeeditor.cpp +++ b/limereport/scripteditor/lrcodeeditor.cpp @@ -24,7 +24,9 @@ CodeEditor::CodeEditor(QWidget *parent) updateLineNumberAreaWidth(0); highlightCurrentLine(); - (void) new ScriptHighlighter(document()); + new ScriptHighlighter(document()); + connect(this, SIGNAL(cursorPositionChanged()), + this, SLOT(matchParentheses())); } void CodeEditor::setCompleter(QCompleter *value) @@ -143,6 +145,105 @@ QString CodeEditor::textUnderCursor() const return tc.selectedText(); } +bool CodeEditor::matchLeftParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numLeftParentheses) +{ + TextBlockData *data = static_cast(currentBlock.userData()); + QVector infos = data->parentheses(); + + int docPos = currentBlock.position(); + for (; i < infos.size(); ++i) { + ParenthesisInfo *info = infos.at(i); + + if (info->character == parenthesisType) { + ++numLeftParentheses; + continue; + } + + if (info->character == getParenthesisReverceChar(parenthesisType)){ + if (numLeftParentheses == 0) { + createParenthesisSelection(docPos + info->position); + return true; + } else + --numLeftParentheses; + } + + } + + currentBlock = currentBlock.next(); + if (currentBlock.isValid()) + return matchLeftParenthesis(currentBlock, parenthesisType, 0, numLeftParentheses); + + return false; +} + +bool CodeEditor::matchRightParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numRightParentheses) +{ + TextBlockData *data = static_cast(currentBlock.userData()); + QVector parentheses = data->parentheses(); + + int docPos = currentBlock.position(); + for (; i > -1 && parentheses.size() > 0; --i) { + ParenthesisInfo *info = parentheses.at(i); + if (info->character == parenthesisType) { + ++numRightParentheses; + continue; + } + if (info->character == getParenthesisReverceChar(parenthesisType)){ + if (numRightParentheses == 0) { + createParenthesisSelection(docPos + info->position); + return true; + } else + --numRightParentheses; + } + } + + currentBlock = currentBlock.previous(); + if (currentBlock.isValid()) + return matchRightParenthesis(currentBlock, parenthesisType, 0, numRightParentheses); + + return false; +} + +void CodeEditor::createParenthesisSelection(int pos) +{ + QList selections = extraSelections(); + + QTextEdit::ExtraSelection selection; + QTextCharFormat format = selection.format; + format.setBackground(QColor("#619934")); + format.setForeground(QColor("#ffffff")); + selection.format = format; + + QTextCursor cursor = textCursor(); + cursor.setPosition(pos); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + selection.cursor = cursor; + + selections.append(selection); + + setExtraSelections(selections); +} + +bool CodeEditor::charIsParenthesis(QChar character, ParenthesisType type) +{ + for (int i = 0; i < PARENHEIS_COUNT; ++i){ + if (character == parenthesisCharacters[type][i]) + return true; + } + return false; +} + +QChar CodeEditor::getParenthesisReverceChar(QChar parenthesisChar) +{ + for (int i = 0; i < PARENHEIS_COUNT; ++i){ + if ( parenthesisCharacters[RightParenthesis][i] == parenthesisChar) + return parenthesisCharacters[LeftParenthesis][i]; + if ( parenthesisCharacters[LeftParenthesis][i] == parenthesisChar) + return parenthesisCharacters[RightParenthesis][i]; + } + return ' '; +} + void CodeEditor::insertCompletion(const QString &completion) { if (m_compleater->widget() != this) @@ -155,7 +256,7 @@ void CodeEditor::insertCompletion(const QString &completion) setTextCursor(tc); } -void CodeEditor::updateLineNumberAreaWidth(int newBlockCount) +void CodeEditor::updateLineNumberAreaWidth(int /*newBlockCount*/) { setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); } @@ -167,7 +268,7 @@ void CodeEditor::highlightCurrentLine() if (!isReadOnly()) { QTextEdit::ExtraSelection selection; - QColor lineColor = QColor(QPalette().background().color()).darker(160); + QColor lineColor = QColor(QPalette().background().color()).darker(100); selection.format.setBackground(lineColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); @@ -190,4 +291,32 @@ void CodeEditor::updateLineNumberArea(const QRect& rect, int dy) updateLineNumberAreaWidth(0); } +void CodeEditor::matchParentheses() +{ + QList selections; + setExtraSelections(selections); + + TextBlockData *data = static_cast(textCursor().block().userData()); + + if (data) { + QVector infos = data->parentheses(); + + int pos = textCursor().block().position(); + for (int i = 0; i < infos.size(); ++i) { + ParenthesisInfo *info = infos.at(i); + + int curPos = textCursor().position() - textCursor().block().position(); + + if ( (info->position == (curPos - 1)) && charIsParenthesis(info->character, LeftParenthesis)) + { + if (matchLeftParenthesis(textCursor().block(), info->character, i + 1, 0)) + createParenthesisSelection(pos + info->position); + } else if ( (info->position == (curPos - 1)) && charIsParenthesis(info->character, RightParenthesis)) { + if (matchRightParenthesis(textCursor().block(), info->character, i - 1, 0)) + createParenthesisSelection(pos + info->position); + } + } + } +} + } //namespace LimeReport diff --git a/limereport/scripteditor/lrcodeeditor.h b/limereport/scripteditor/lrcodeeditor.h index 83c2dc0..851583f 100644 --- a/limereport/scripteditor/lrcodeeditor.h +++ b/limereport/scripteditor/lrcodeeditor.h @@ -3,6 +3,7 @@ #include #include +#include "lrscripthighlighter.h" QT_BEGIN_NAMESPACE class QWidget; @@ -28,11 +29,17 @@ protected: void resizeEvent(QResizeEvent *event); private: QString textUnderCursor() const; + bool matchLeftParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numLeftParentheses); + bool matchRightParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numRightParentheses); + void createParenthesisSelection(int pos); + bool charIsParenthesis(QChar character, ParenthesisType type); + QChar getParenthesisReverceChar(QChar parenthesisChar); private slots: void insertCompletion(const QString& completion); void updateLineNumberAreaWidth(int newBlockCount); void highlightCurrentLine(); void updateLineNumberArea(const QRect &rect, int dy); + void matchParentheses(); private: QCompleter* m_compleater; QWidget *lineNumberArea; diff --git a/limereport/scripteditor/lrscripthighlighter.cpp b/limereport/scripteditor/lrscripthighlighter.cpp index d85a571..d88e18f 100644 --- a/limereport/scripteditor/lrscripthighlighter.cpp +++ b/limereport/scripteditor/lrscripthighlighter.cpp @@ -1,5 +1,6 @@ #include "lrscripthighlighter.h" #include +#include namespace LimeReport{ @@ -17,25 +18,38 @@ static const char *const keywords[KEYWORDS_COUNT] = { 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}; +enum States {Start, MayBeKeyWord, Code, MayBeComment, Comment, Comment2, MayBeComment2End, String, String2, MayBeNumber, Separator, StatesCount}; + +void ScriptHighlighter::createParentheisisInfo(const char& literal, TextBlockData *data, const QString& text) +{ + int pos = text.indexOf(literal); + while (pos != -1) { + ParenthesisInfo *info = new ParenthesisInfo; + info->character = literal; + info->position = pos; + data->insert(info); + pos = text.indexOf(literal, pos + 1); + } +} void ScriptHighlighter::highlightBlock(const QString& text) { int literal = -1; bool lastWasBackSlash = false; - int state = previousBlockState() != -1 ? previousBlockState() : MayBeKeyWord ; + int state = previousBlockState() != -1 ? previousBlockState() : Start ; int oldState = -1; int stateMaschine[StatesCount][LiteralsCount] = { + {Separator, MayBeKeyWord, MayBeNumber, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Separator}, {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}, + {Separator, Code, MayBeNumber, 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 } + {Separator, MayBeKeyWord, MayBeNumber, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Separator } }; QString buffer; @@ -86,33 +100,127 @@ void ScriptHighlighter::highlightBlock(const QString& text) oldState = state; state = stateMaschine[state][literal]; + buffer += currentChar; + if (oldState != state){ switch( state ){ case MayBeComment: + if (oldState == MayBeNumber){ + setFormat(i-(buffer.length()-1), buffer.length()-1, m_formats[NumberFormat]); + } + buffer.clear(); + buffer += currentChar; + + break; + case MayBeKeyWord: + case MayBeNumber: buffer.clear(); buffer += currentChar; break; case Comment2: - buffer += currentChar; - qDebug()<= text.length()) break; } + + TextBlockData *data = new TextBlockData; + + + for (int i = 0; i < PARENHEIS_COUNT; ++i){ + createParentheisisInfo(parenthesisCharacters[LeftParenthesis][i].toLatin1(), data, text); + createParentheisisInfo(parenthesisCharacters[RightParenthesis][i].toLatin1(), data, text); + } +// createParentheisisInfo('(', data, text); +// createParentheisisInfo(')', data, text); +// createParentheisisInfo('{', data, text); +// createParentheisisInfo('}', data, text); +// createParentheisisInfo('[', data, text); +// createParentheisisInfo(']', data, text); + + setCurrentBlockUserData(data); +} + +bool ScriptHighlighter::isKeyWord(const QString& word) +{ + for (int i = 0; i < KEYWORDS_COUNT-1; ++i){ + if (QLatin1String(keywords[i]) == word) return true; + } + return false; +} + +bool ScriptHighlighter::isDark(QColor color) +{ + double a = 1 - ( 0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255; + return (a < 0.5); +} + +ScriptHighlighter::ScriptHighlighter(QTextDocument* parent): + QSyntaxHighlighter(parent) +{ + + if ( isDark(QPalette().background().color())){ + m_formats[NumberFormat].setForeground(Qt::darkBlue); + m_formats[StringFormat].setForeground(Qt::darkGreen); + m_formats[KeywordFormat].setForeground(Qt::darkYellow); + m_formats[CommentFormat].setForeground(Qt::darkGreen); + m_formats[CommentFormat].setFontItalic(true); + } else { + m_formats[NumberFormat].setForeground(QColor("#ff6aad")); + m_formats[StringFormat].setForeground(QColor("#b27f40")); + m_formats[KeywordFormat].setForeground(QColor("#45c5d5")); + m_formats[CommentFormat].setForeground(QColor("#a1a4a9")); + m_formats[CommentFormat].setFontItalic(true); + } +} + +QVector TextBlockData::parentheses() +{ + return m_parentheses; +} + +void TextBlockData::insert(ParenthesisInfo* info) +{ + int i = 0; + while (i < m_parentheses.size() && + info->position > m_parentheses.at(i)->position) + ++i; + + m_parentheses.insert(i, info); } } // namespace LimeReport diff --git a/limereport/scripteditor/lrscripthighlighter.h b/limereport/scripteditor/lrscripthighlighter.h index 3a54693..4580484 100644 --- a/limereport/scripteditor/lrscripthighlighter.h +++ b/limereport/scripteditor/lrscripthighlighter.h @@ -5,13 +5,46 @@ namespace LimeReport{ +enum ParenthesisType {LeftParenthesis, RightParenthesis, ParenthesisTypeCount}; + +#define PARENHEIS_COUNT 3 +const QChar parenthesisCharacters[ParenthesisTypeCount][PARENHEIS_COUNT] = { + {'(', '{', '['}, + {')', '}', ']'} +}; + +struct ParenthesisInfo +{ + char character; + int position; +}; + +class TextBlockData : public QTextBlockUserData +{ +public: + TextBlockData(){} + QVector parentheses(); + void insert(ParenthesisInfo *info); + +private: + QVector m_parentheses; +}; + class ScriptHighlighter : QSyntaxHighlighter{ public: - ScriptHighlighter(QTextDocument* parent): QSyntaxHighlighter(parent){} - // QSyntaxHighlighter interface + ScriptHighlighter(QTextDocument* parent); protected: void highlightBlock(const QString& text); + enum ScriptFormats { + NumberFormat, StringFormat, KeywordFormat, + CommentFormat, FormatsCount + }; + QTextCharFormat m_formats[FormatsCount]; + bool isKeyWord(const QString& word); + bool isDark(QColor color); + void createParentheisisInfo(const char& literal, TextBlockData *data, const QString& text); }; + } #endif // LRSCRIPTHIGHLIGHTER_H From 47643349404d6a6d2f3b8b97462a89b0959961b2 Mon Sep 17 00:00:00 2001 From: Arin Alexander Date: Wed, 20 Sep 2017 00:51:17 +0300 Subject: [PATCH 4/4] Script editor has been finishes --- limereport/lrreportdesignwidget.cpp | 21 ++- limereport/lrreportdesignwidget.h | 3 +- limereport/lrreportdesignwindow.cpp | 76 ++++++---- limereport/lrreportdesignwindow.h | 1 + limereport/scripteditor/lrscripteditor.cpp | 48 ++++++ limereport/scripteditor/lrscripteditor.h | 9 ++ limereport/scripteditor/lrscripteditor.ui | 162 +++++++++------------ 7 files changed, 191 insertions(+), 129 deletions(-) diff --git a/limereport/lrreportdesignwidget.cpp b/limereport/lrreportdesignwidget.cpp index 2b7f160..0145940 100644 --- a/limereport/lrreportdesignwidget.cpp +++ b/limereport/lrreportdesignwidget.cpp @@ -184,6 +184,7 @@ void ReportDesignWidget::saveState(QSettings* settings) settings->setValue("vGridStep",m_verticalGridStep); settings->setValue("defaultFont",m_defaultFont); settings->setValue("useGrid",m_useGrid); + settings->setValue("ScriptEditorState", m_scriptEditor->saveState()); settings->endGroup(); } @@ -216,6 +217,12 @@ void ReportDesignWidget::loadState(QSettings* settings) if (v.isValid()){ m_useGrid = v.toBool(); } + + v = settings->value("ScriptEditorState"); + if (v.isValid()){ + m_scriptEditor->restoreState(v.toByteArray()); + } + settings->endGroup(); applySettings(); } @@ -242,17 +249,12 @@ void ReportDesignWidget::createTabs(){ this, SLOT(slotPagePropertyObjectNameChanged(QString,QString))); } - m_scriptEditor = new QTextEdit(this); + m_scriptEditor = new ScriptEditor(this); + m_scriptEditor->setReportEngine(m_report); pageIndex = m_tabWidget->addTab(m_scriptEditor,QIcon(),tr("Script")); m_tabWidget->setTabWhatsThis(pageIndex,"script"); m_tabWidget->setCurrentIndex(0); - m_newScriptEditor = new ScriptEditor(this); - m_newScriptEditor->setReportEngine(m_report); - pageIndex = m_tabWidget->addTab(m_newScriptEditor,QIcon(),tr("New Script Editor")); - m_tabWidget->setTabWhatsThis(pageIndex,"script"); - m_tabWidget->setCurrentIndex(0); - #ifdef HAVE_QTDESIGNER_INTEGRATION QWidget* dialogDesigner; foreach(DialogDescriber::Ptr dialogDesc, m_report->scriptContext()->dialogDescribers()){ @@ -442,9 +444,11 @@ bool ReportDesignWidget::save() bool ReportDesignWidget::loadFromFile(const QString &fileName) { if (m_report->loadFromFile(fileName,false)){ + QByteArray editorState = m_scriptEditor->saveState(); createTabs(); //connectPage(m_report->pageAt(0)); m_scriptEditor->setPlainText(m_report->scriptContext()->initScript()); + m_scriptEditor->restoreState(editorState); emit loaded(); m_dialogChanged = false; return true; @@ -796,7 +800,8 @@ void ReportDesignWidget::slotCurrentTabChanged(int index) } if (activeTabType() == Script){ - m_newScriptEditor->initCompleter(); + m_scriptEditor->initCompleter(); + m_scriptEditor->setFocus(); } emit activePageChanged(); diff --git a/limereport/lrreportdesignwidget.h b/limereport/lrreportdesignwidget.h index d710537..b7e38b7 100644 --- a/limereport/lrreportdesignwidget.h +++ b/limereport/lrreportdesignwidget.h @@ -191,8 +191,7 @@ private: private: ReportEnginePrivate* m_report; QGraphicsView *m_view; - QTextEdit* m_scriptEditor; - ScriptEditor* m_newScriptEditor; + ScriptEditor* m_scriptEditor; TranslationEditor* m_traslationEditor; #ifdef HAVE_QTDESIGNER_INTEGRATION DialogDesignerManager* m_dialogDesignerManager; diff --git a/limereport/lrreportdesignwindow.cpp b/limereport/lrreportdesignwindow.cpp index 0bdae75..1ad41c7 100644 --- a/limereport/lrreportdesignwindow.cpp +++ b/limereport/lrreportdesignwindow.cpp @@ -664,14 +664,21 @@ void ReportDesignWindow::writeState() { settings()->beginGroup("DesignerWindow"); switch (m_editorTabType) { - case ReportDesignWidget::Page: - settings()->setValue("PageEditorsState", saveState()); - settings()->setValue("DialogEditorsState", m_dialogEditorsState); - break; - default: - settings()->setValue("DialogEditorsState", saveState()); - settings()->setValue("PageEditorsState", m_pageEditorsState); - break; + case ReportDesignWidget::Page: + settings()->setValue("PageEditorsState", saveState()); + settings()->setValue("DialogEditorsState", m_dialogEditorsState); + settings()->setValue("ScriptEditorsState", m_scriptEditorState); + break; + case ReportDesignWidget::Script: + settings()->setValue("ScriptEditorsState", saveState()); + settings()->setValue("DialogEditorsState", m_dialogEditorsState); + settings()->setValue("PageEditorsState", m_pageEditorsState); + break; + default: + settings()->setValue("DialogEditorsState", saveState()); + settings()->setValue("PageEditorsState", m_pageEditorsState); + settings()->setValue("ScriptEditorsState", m_scriptEditorState); + break; } settings()->setValue("InspectorFirsColumnWidth",m_objectInspector->columnWidth(0)); settings()->endGroup(); @@ -775,6 +782,10 @@ void ReportDesignWindow::restoreSetting() if (v.isValid()){ m_dialogEditorsState = v.toByteArray(); } + v = settings()->value("ScriptEditorsState"); + if (v.isValid()){ + m_scriptEditorState = v.toByteArray(); + } v = settings()->value("InspectorFirsColumnWidth"); if (v.isValid()){ m_objectInspector->setColumnWidth(0,v.toInt()); @@ -1277,12 +1288,15 @@ void ReportDesignWindow::slotActivePageChanged() updateAvaibleBands(); switch (m_editorTabType) { - case ReportDesignWidget::Dialog: - m_dialogEditorsState = saveState(); + case ReportDesignWidget::Dialog: + m_dialogEditorsState = saveState(); #ifdef HAVE_UI_LOADER - m_scriptBrowser->updateDialogsTree(); + m_scriptBrowser->updateDialogsTree(); #endif - break; + break; + case ReportDesignWidget::Script: + m_scriptEditorState = saveState(); + break; default: m_pageEditorsState = saveState(); break; @@ -1291,20 +1305,30 @@ void ReportDesignWindow::slotActivePageChanged() m_editorTabType = m_reportDesignWidget->activeTabType(); switch (m_editorTabType) { - case ReportDesignWidget::Dialog: - if (!m_dialogEditorsState.isEmpty()) - restoreState(m_dialogEditorsState); - else - showDefaultEditors(); - showDefaultToolBars(); - break; - default: - if (!m_pageEditors.isEmpty()) - restoreState(m_pageEditorsState); - else - showDefaultEditors(); - showDefaultToolBars(); - break; + case ReportDesignWidget::Dialog: + if (!m_dialogEditorsState.isEmpty()){ + restoreState(m_dialogEditorsState); + } else { + showDefaultEditors(); + showDefaultToolBars(); + } + break; + case ReportDesignWidget::Script: + if (!m_scriptEditorState.isEmpty()){ + restoreState(m_scriptEditorState); + } else { + showDefaultEditors(); + showDefaultToolBars(); + } + break; + default: + if (!m_pageEditors.isEmpty()){ + restoreState(m_pageEditorsState); + } else { + showDefaultEditors(); + showDefaultToolBars(); + } + break; } } diff --git a/limereport/lrreportdesignwindow.h b/limereport/lrreportdesignwindow.h index 94c4bd5..1779ed3 100644 --- a/limereport/lrreportdesignwindow.h +++ b/limereport/lrreportdesignwindow.h @@ -264,6 +264,7 @@ private: ReportDesignWidget::EditorTabType m_editorTabType; QByteArray m_pageEditorsState; QByteArray m_dialogEditorsState; + QByteArray m_scriptEditorState; QVector m_pageTools; QVector m_dialogTools; diff --git a/limereport/scripteditor/lrscripteditor.cpp b/limereport/scripteditor/lrscripteditor.cpp index 613926e..461a8f9 100644 --- a/limereport/scripteditor/lrscripteditor.cpp +++ b/limereport/scripteditor/lrscripteditor.cpp @@ -14,6 +14,7 @@ ScriptEditor::ScriptEditor(QWidget *parent) : ui(new Ui::ScriptEditor) { ui->setupUi(this); + setFocusProxy(ui->textEdit); m_completer = new QCompleter(this); ui->textEdit->setCompleter(m_completer); } @@ -78,6 +79,26 @@ void ScriptEditor::initCompleter() m_completer->setModel(new QStringListModel(dataWords,m_completer)); } +QByteArray ScriptEditor::saveState() +{ + return ui->splitter->saveState(); +} + +void ScriptEditor::restoreState(QByteArray state) +{ + ui->splitter->restoreState(state); +} + +void ScriptEditor::setPlainText(const QString& text) +{ + ui->textEdit->setPlainText(text); +} + +QString ScriptEditor::toPlainText() +{ + return ui->textEdit->toPlainText(); +} + void ScriptEditor::addItemToCompleater(const QString& pageName, BaseDesignIntf* item, QStringList& dataWords) { BandDesignIntf* band = dynamic_cast(item); @@ -99,4 +120,31 @@ void ScriptEditor::addItemToCompleater(const QString& pageName, BaseDesignIntf* } } +void ScriptEditor::on_twData_doubleClicked(const QModelIndex &index) +{ + if (!index.isValid()) return; + LimeReport::DataNode* node = static_cast(index.internalPointer()); + if (node->type()==LimeReport::DataNode::Field){ + ui->textEdit->insertPlainText(QString("$D{%1.%2}").arg(node->parent()->name()).arg(node->name())); + } + if (node->type()==LimeReport::DataNode::Variable){ + ui->textEdit->insertPlainText(QString("$V{%1}").arg(node->name())); + } + ui->textEdit->setFocus(); +} + +void ScriptEditor::on_twScriptEngine_doubleClicked(const QModelIndex &index) +{ + if (!index.isValid()) return; + LimeReport::ScriptEngineNode* node = static_cast(index.internalPointer()); + if (node->type()==LimeReport::ScriptEngineNode::Function){ + ui->textEdit->insertPlainText(node->name()+"()"); + } + ui->textEdit->setFocus(); +} + } // namespace LimeReport + + + + diff --git a/limereport/scripteditor/lrscripteditor.h b/limereport/scripteditor/lrscripteditor.h index 6943fcb..eedb66d 100644 --- a/limereport/scripteditor/lrscripteditor.h +++ b/limereport/scripteditor/lrscripteditor.h @@ -24,6 +24,15 @@ public: ~ScriptEditor(); void setReportEngine(ReportEnginePrivate* reportEngine); void initCompleter(); + QByteArray saveState(); + void restoreState(QByteArray state); + void setPlainText(const QString &text); + QString toPlainText(); +private slots: + void on_twData_doubleClicked(const QModelIndex &index); + + void on_twScriptEngine_doubleClicked(const QModelIndex &index); + private: void addItemToCompleater(const QString& pageName, BaseDesignIntf* item, QStringList& dataWords); private: diff --git a/limereport/scripteditor/lrscripteditor.ui b/limereport/scripteditor/lrscripteditor.ui index 49fcfb6..27f8e7f 100644 --- a/limereport/scripteditor/lrscripteditor.ui +++ b/limereport/scripteditor/lrscripteditor.ui @@ -13,116 +13,92 @@ Form - + - + 0 0 - - Content + + Qt::Horizontal - - - 2 + + + + 0 + 0 + - - 2 + + + 12 + - - 2 + + + + QTabWidget::South - - 2 + + 0 - - 2 - - - - - - 0 - 0 - + + + Data + + + + 3 - - Qt::Horizontal + + 3 - - - - 12 - - - - - - QTabWidget::South - - - 0 - - - - Data + + 3 + + + 3 + + + 3 + + + + + false - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - false - - - - - - - Functions - - - - - - Qt::Vertical - - - - false - - - - - - - - - - + + + + + + Functions + + + + + + Qt::Vertical + + + + false + + + + + + + - - - - + + +
+