mirror of
https://github.com/python-LimeReport/LimeReport.git
synced 2025-01-12 04:41:03 +03:00
Finish ScriptEditor
This commit is contained in:
commit
fd4e8057a9
@ -33,7 +33,6 @@
|
|||||||
#include "lrdatasourcemanager.h"
|
#include "lrdatasourcemanager.h"
|
||||||
#include "lrscriptenginemanager.h"
|
#include "lrscriptenginemanager.h"
|
||||||
#include "lrdatadesignintf.h"
|
#include "lrdatadesignintf.h"
|
||||||
#include "lrdatasourcemanager.h"
|
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
@ -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 \
|
||||||
@ -59,6 +60,9 @@ SOURCES += \
|
|||||||
$$REPORT_PATH/objectinspector/lrpropertydelegate.cpp \
|
$$REPORT_PATH/objectinspector/lrpropertydelegate.cpp \
|
||||||
$$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/lrcodeeditor.cpp \
|
||||||
|
$$REPORT_PATH/scripteditor/lrscripthighlighter.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 \
|
||||||
@ -161,6 +165,9 @@ HEADERS += \
|
|||||||
$$REPORT_PATH/objectinspector/lrpropertydelegate.h \
|
$$REPORT_PATH/objectinspector/lrpropertydelegate.h \
|
||||||
$$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/lrcodeeditor.h \
|
||||||
|
$$REPORT_PATH/scripteditor/lrscripthighlighter.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 \
|
||||||
@ -235,7 +242,8 @@ 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 \
|
||||||
$$PWD/translationeditor/languageselectdialog.ui
|
$$REPORT_PATH/translationeditor/languageselectdialog.ui \
|
||||||
|
$$REPORT_PATH/scripteditor/lrscripteditor.ui
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
$$REPORT_PATH/objectinspector/lobjectinspector.qrc \
|
$$REPORT_PATH/objectinspector/lobjectinspector.qrc \
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "lrsettingdialog.h"
|
#include "lrsettingdialog.h"
|
||||||
#include "dialogdesigner/lrdialogdesigner.h"
|
#include "dialogdesigner/lrdialogdesigner.h"
|
||||||
#include "translationeditor/translationeditor.h"
|
#include "translationeditor/translationeditor.h"
|
||||||
|
#include "scripteditor/lrscripteditor.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@ -183,6 +184,7 @@ void ReportDesignWidget::saveState(QSettings* settings)
|
|||||||
settings->setValue("vGridStep",m_verticalGridStep);
|
settings->setValue("vGridStep",m_verticalGridStep);
|
||||||
settings->setValue("defaultFont",m_defaultFont);
|
settings->setValue("defaultFont",m_defaultFont);
|
||||||
settings->setValue("useGrid",m_useGrid);
|
settings->setValue("useGrid",m_useGrid);
|
||||||
|
settings->setValue("ScriptEditorState", m_scriptEditor->saveState());
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +217,12 @@ void ReportDesignWidget::loadState(QSettings* settings)
|
|||||||
if (v.isValid()){
|
if (v.isValid()){
|
||||||
m_useGrid = v.toBool();
|
m_useGrid = v.toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v = settings->value("ScriptEditorState");
|
||||||
|
if (v.isValid()){
|
||||||
|
m_scriptEditor->restoreState(v.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
@ -241,7 +249,8 @@ void ReportDesignWidget::createTabs(){
|
|||||||
this, SLOT(slotPagePropertyObjectNameChanged(QString,QString)));
|
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"));
|
pageIndex = m_tabWidget->addTab(m_scriptEditor,QIcon(),tr("Script"));
|
||||||
m_tabWidget->setTabWhatsThis(pageIndex,"script");
|
m_tabWidget->setTabWhatsThis(pageIndex,"script");
|
||||||
m_tabWidget->setCurrentIndex(0);
|
m_tabWidget->setCurrentIndex(0);
|
||||||
@ -435,9 +444,11 @@ bool ReportDesignWidget::save()
|
|||||||
bool ReportDesignWidget::loadFromFile(const QString &fileName)
|
bool ReportDesignWidget::loadFromFile(const QString &fileName)
|
||||||
{
|
{
|
||||||
if (m_report->loadFromFile(fileName,false)){
|
if (m_report->loadFromFile(fileName,false)){
|
||||||
|
QByteArray editorState = m_scriptEditor->saveState();
|
||||||
createTabs();
|
createTabs();
|
||||||
//connectPage(m_report->pageAt(0));
|
//connectPage(m_report->pageAt(0));
|
||||||
m_scriptEditor->setPlainText(m_report->scriptContext()->initScript());
|
m_scriptEditor->setPlainText(m_report->scriptContext()->initScript());
|
||||||
|
m_scriptEditor->restoreState(editorState);
|
||||||
emit loaded();
|
emit loaded();
|
||||||
m_dialogChanged = false;
|
m_dialogChanged = false;
|
||||||
return true;
|
return true;
|
||||||
@ -787,6 +798,12 @@ void ReportDesignWidget::slotCurrentTabChanged(int index)
|
|||||||
if (activeTabType() == Translations){
|
if (activeTabType() == Translations){
|
||||||
m_traslationEditor->setReportEngine(report());
|
m_traslationEditor->setReportEngine(report());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (activeTabType() == Script){
|
||||||
|
m_scriptEditor->initCompleter();
|
||||||
|
m_scriptEditor->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
emit activePageChanged();
|
emit activePageChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ class ReportDesignWindow;
|
|||||||
class DialogDesignerManager;
|
class DialogDesignerManager;
|
||||||
class DialogDesigner;
|
class DialogDesigner;
|
||||||
class TranslationEditor;
|
class TranslationEditor;
|
||||||
|
class ScriptEditor;
|
||||||
|
|
||||||
|
|
||||||
class ReportDesignWidget : public QWidget
|
class ReportDesignWidget : public QWidget
|
||||||
{
|
{
|
||||||
@ -189,7 +191,7 @@ private:
|
|||||||
private:
|
private:
|
||||||
ReportEnginePrivate* m_report;
|
ReportEnginePrivate* m_report;
|
||||||
QGraphicsView *m_view;
|
QGraphicsView *m_view;
|
||||||
QTextEdit* m_scriptEditor;
|
ScriptEditor* m_scriptEditor;
|
||||||
TranslationEditor* m_traslationEditor;
|
TranslationEditor* m_traslationEditor;
|
||||||
#ifdef HAVE_QTDESIGNER_INTEGRATION
|
#ifdef HAVE_QTDESIGNER_INTEGRATION
|
||||||
DialogDesignerManager* m_dialogDesignerManager;
|
DialogDesignerManager* m_dialogDesignerManager;
|
||||||
@ -205,5 +207,5 @@ private:
|
|||||||
bool m_dialogChanged;
|
bool m_dialogChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace LimeReport
|
||||||
#endif // LRREPORTDESIGNWIDGET_H
|
#endif // LRREPORTDESIGNWIDGET_H
|
||||||
|
@ -664,14 +664,21 @@ void ReportDesignWindow::writeState()
|
|||||||
{
|
{
|
||||||
settings()->beginGroup("DesignerWindow");
|
settings()->beginGroup("DesignerWindow");
|
||||||
switch (m_editorTabType) {
|
switch (m_editorTabType) {
|
||||||
case ReportDesignWidget::Page:
|
case ReportDesignWidget::Page:
|
||||||
settings()->setValue("PageEditorsState", saveState());
|
settings()->setValue("PageEditorsState", saveState());
|
||||||
settings()->setValue("DialogEditorsState", m_dialogEditorsState);
|
settings()->setValue("DialogEditorsState", m_dialogEditorsState);
|
||||||
break;
|
settings()->setValue("ScriptEditorsState", m_scriptEditorState);
|
||||||
default:
|
break;
|
||||||
settings()->setValue("DialogEditorsState", saveState());
|
case ReportDesignWidget::Script:
|
||||||
settings()->setValue("PageEditorsState", m_pageEditorsState);
|
settings()->setValue("ScriptEditorsState", saveState());
|
||||||
break;
|
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()->setValue("InspectorFirsColumnWidth",m_objectInspector->columnWidth(0));
|
||||||
settings()->endGroup();
|
settings()->endGroup();
|
||||||
@ -775,6 +782,10 @@ void ReportDesignWindow::restoreSetting()
|
|||||||
if (v.isValid()){
|
if (v.isValid()){
|
||||||
m_dialogEditorsState = v.toByteArray();
|
m_dialogEditorsState = v.toByteArray();
|
||||||
}
|
}
|
||||||
|
v = settings()->value("ScriptEditorsState");
|
||||||
|
if (v.isValid()){
|
||||||
|
m_scriptEditorState = v.toByteArray();
|
||||||
|
}
|
||||||
v = settings()->value("InspectorFirsColumnWidth");
|
v = settings()->value("InspectorFirsColumnWidth");
|
||||||
if (v.isValid()){
|
if (v.isValid()){
|
||||||
m_objectInspector->setColumnWidth(0,v.toInt());
|
m_objectInspector->setColumnWidth(0,v.toInt());
|
||||||
@ -1277,12 +1288,15 @@ void ReportDesignWindow::slotActivePageChanged()
|
|||||||
updateAvaibleBands();
|
updateAvaibleBands();
|
||||||
|
|
||||||
switch (m_editorTabType) {
|
switch (m_editorTabType) {
|
||||||
case ReportDesignWidget::Dialog:
|
case ReportDesignWidget::Dialog:
|
||||||
m_dialogEditorsState = saveState();
|
m_dialogEditorsState = saveState();
|
||||||
#ifdef HAVE_UI_LOADER
|
#ifdef HAVE_UI_LOADER
|
||||||
m_scriptBrowser->updateDialogsTree();
|
m_scriptBrowser->updateDialogsTree();
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
case ReportDesignWidget::Script:
|
||||||
|
m_scriptEditorState = saveState();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
m_pageEditorsState = saveState();
|
m_pageEditorsState = saveState();
|
||||||
break;
|
break;
|
||||||
@ -1291,20 +1305,30 @@ void ReportDesignWindow::slotActivePageChanged()
|
|||||||
m_editorTabType = m_reportDesignWidget->activeTabType();
|
m_editorTabType = m_reportDesignWidget->activeTabType();
|
||||||
|
|
||||||
switch (m_editorTabType) {
|
switch (m_editorTabType) {
|
||||||
case ReportDesignWidget::Dialog:
|
case ReportDesignWidget::Dialog:
|
||||||
if (!m_dialogEditorsState.isEmpty())
|
if (!m_dialogEditorsState.isEmpty()){
|
||||||
restoreState(m_dialogEditorsState);
|
restoreState(m_dialogEditorsState);
|
||||||
else
|
} else {
|
||||||
showDefaultEditors();
|
showDefaultEditors();
|
||||||
showDefaultToolBars();
|
showDefaultToolBars();
|
||||||
break;
|
}
|
||||||
default:
|
break;
|
||||||
if (!m_pageEditors.isEmpty())
|
case ReportDesignWidget::Script:
|
||||||
restoreState(m_pageEditorsState);
|
if (!m_scriptEditorState.isEmpty()){
|
||||||
else
|
restoreState(m_scriptEditorState);
|
||||||
showDefaultEditors();
|
} else {
|
||||||
showDefaultToolBars();
|
showDefaultEditors();
|
||||||
break;
|
showDefaultToolBars();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (!m_pageEditors.isEmpty()){
|
||||||
|
restoreState(m_pageEditorsState);
|
||||||
|
} else {
|
||||||
|
showDefaultEditors();
|
||||||
|
showDefaultToolBars();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -264,6 +264,7 @@ private:
|
|||||||
ReportDesignWidget::EditorTabType m_editorTabType;
|
ReportDesignWidget::EditorTabType m_editorTabType;
|
||||||
QByteArray m_pageEditorsState;
|
QByteArray m_pageEditorsState;
|
||||||
QByteArray m_dialogEditorsState;
|
QByteArray m_dialogEditorsState;
|
||||||
|
QByteArray m_scriptEditorState;
|
||||||
QVector<QToolBar*> m_pageTools;
|
QVector<QToolBar*> m_pageTools;
|
||||||
QVector<QToolBar*> m_dialogTools;
|
QVector<QToolBar*> m_dialogTools;
|
||||||
|
|
||||||
|
322
limereport/scripteditor/lrcodeeditor.cpp
Normal file
322
limereport/scripteditor/lrcodeeditor.cpp
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
#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();
|
||||||
|
new ScriptHighlighter(document());
|
||||||
|
connect(this, SIGNAL(cursorPositionChanged()),
|
||||||
|
this, SLOT(matchParentheses()));
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CodeEditor::matchLeftParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numLeftParentheses)
|
||||||
|
{
|
||||||
|
TextBlockData *data = static_cast<TextBlockData *>(currentBlock.userData());
|
||||||
|
QVector<ParenthesisInfo *> 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<TextBlockData *>(currentBlock.userData());
|
||||||
|
QVector<ParenthesisInfo *> 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<QTextEdit::ExtraSelection> 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)
|
||||||
|
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(100);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeEditor::matchParentheses()
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> selections;
|
||||||
|
setExtraSelections(selections);
|
||||||
|
|
||||||
|
TextBlockData *data = static_cast<TextBlockData *>(textCursor().block().userData());
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
QVector<ParenthesisInfo *> 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
|
71
limereport/scripteditor/lrcodeeditor.h
Normal file
71
limereport/scripteditor/lrcodeeditor.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#ifndef LRCODEEDITOR_H
|
||||||
|
#define LRCODEEDITOR_H
|
||||||
|
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QSyntaxHighlighter>
|
||||||
|
#include "lrscripthighlighter.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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
|
150
limereport/scripteditor/lrscripteditor.cpp
Normal file
150
limereport/scripteditor/lrscripteditor.cpp
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
#include "lrscripteditor.h"
|
||||||
|
#include "ui_lrscripteditor.h"
|
||||||
|
#include <QJSValueIterator>
|
||||||
|
|
||||||
|
#include "lrdatasourcemanager.h"
|
||||||
|
#include "lrscriptenginemanager.h"
|
||||||
|
#include "lrdatadesignintf.h"
|
||||||
|
#include "lrreportengine_p.h"
|
||||||
|
|
||||||
|
namespace LimeReport{
|
||||||
|
|
||||||
|
ScriptEditor::ScriptEditor(QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
ui(new Ui::ScriptEditor)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
setFocusProxy(ui->textEdit);
|
||||||
|
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<<dsName+"."+field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (QString varName, dm->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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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<BandDesignIntf*>(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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEditor::on_twData_doubleClicked(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
if (!index.isValid()) return;
|
||||||
|
LimeReport::DataNode* node = static_cast<LimeReport::DataNode*>(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<LimeReport::ScriptEngineNode*>(index.internalPointer());
|
||||||
|
if (node->type()==LimeReport::ScriptEngineNode::Function){
|
||||||
|
ui->textEdit->insertPlainText(node->name()+"()");
|
||||||
|
}
|
||||||
|
ui->textEdit->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace LimeReport
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
47
limereport/scripteditor/lrscripteditor.h
Normal file
47
limereport/scripteditor/lrscripteditor.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef LRSCRIPTEDITOR_H
|
||||||
|
#define LRSCRIPTEDITOR_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QCompleter>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
namespace LimeReport{
|
||||||
|
|
||||||
|
class ReportEnginePrivate;
|
||||||
|
class BaseDesignIntf;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ScriptEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptEditor : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ScriptEditor(QWidget *parent = 0);
|
||||||
|
~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:
|
||||||
|
Ui::ScriptEditor *ui;
|
||||||
|
ReportEnginePrivate* m_reportEngine;
|
||||||
|
QCompleter* m_completer;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace LimeReport
|
||||||
|
|
||||||
|
#endif // LRSCRIPTEDITOR_H
|
115
limereport/scripteditor/lrscripteditor.ui
Normal file
115
limereport/scripteditor/lrscripteditor.ui
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>LimeReport::ScriptEditor</class>
|
||||||
|
<widget class="QWidget" name="LimeReport::ScriptEditor">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>706</width>
|
||||||
|
<height>541</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="LimeReport::CodeEditor" name="textEdit">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="tabPosition">
|
||||||
|
<enum>QTabWidget::South</enum>
|
||||||
|
</property>
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Data</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="twData">
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Functions</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QTreeView" name="twScriptEngine">
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="lblDescription">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>LimeReport::CodeEditor</class>
|
||||||
|
<extends>QTextEdit</extends>
|
||||||
|
<header>lrcodeeditor.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
226
limereport/scripteditor/lrscripthighlighter.cpp
Normal file
226
limereport/scripteditor/lrscripthighlighter.cpp
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
#include "lrscripthighlighter.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPalette>
|
||||||
|
|
||||||
|
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 {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() : 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, 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, Separator, Separator, 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];
|
||||||
|
|
||||||
|
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:
|
||||||
|
setCurrentBlockState(Comment2);
|
||||||
|
case Separator:
|
||||||
|
switch(oldState){
|
||||||
|
case MayBeComment2End:
|
||||||
|
setFormat(i-(buffer.length()-1), buffer.length(), m_formats[CommentFormat]);
|
||||||
|
setCurrentBlockState(-1);
|
||||||
|
buffer.clear();
|
||||||
|
break;
|
||||||
|
case MayBeKeyWord:
|
||||||
|
if (isKeyWord(buffer.left(buffer.length()-1))){
|
||||||
|
setFormat(i-(buffer.length()-1), buffer.length()-1, m_formats[KeywordFormat]);
|
||||||
|
}
|
||||||
|
buffer.clear();
|
||||||
|
break;
|
||||||
|
case MayBeNumber:
|
||||||
|
setFormat(i-(buffer.length()-1), buffer.length()-1, m_formats[NumberFormat]);
|
||||||
|
buffer.clear();
|
||||||
|
case String:
|
||||||
|
case String2:
|
||||||
|
setFormat(i-(buffer.length()-1), buffer.length(), m_formats[StringFormat]);
|
||||||
|
buffer.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( state == Comment || state == Comment2 ){
|
||||||
|
setFormat(i-(buffer.length()-1), buffer.length(), m_formats[CommentFormat]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( state == String || state == String2 ){
|
||||||
|
setFormat(i-(buffer.length()-1), buffer.length(), m_formats[StringFormat]);
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
if ( i>= 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<ParenthesisInfo*> 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
|
50
limereport/scripteditor/lrscripthighlighter.h
Normal file
50
limereport/scripteditor/lrscripthighlighter.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef LRSCRIPTHIGHLIGHTER_H
|
||||||
|
#define LRSCRIPTHIGHLIGHTER_H
|
||||||
|
|
||||||
|
#include <QSyntaxHighlighter>
|
||||||
|
|
||||||
|
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<ParenthesisInfo *> parentheses();
|
||||||
|
void insert(ParenthesisInfo *info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<ParenthesisInfo *> m_parentheses;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptHighlighter : QSyntaxHighlighter{
|
||||||
|
public:
|
||||||
|
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
|
Loading…
Reference in New Issue
Block a user