Finish ScriptEditor

This commit is contained in:
Arin Alexander 2017-09-20 01:02:23 +03:00
commit fd4e8057a9
13 changed files with 1064 additions and 32 deletions

View File

@ -33,7 +33,6 @@
#include "lrdatasourcemanager.h"
#include "lrscriptenginemanager.h"
#include "lrdatadesignintf.h"
#include "lrdatasourcemanager.h"
#include <QMenu>
#include <QScrollBar>

View File

@ -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 \
@ -59,6 +60,9 @@ SOURCES += \
$$REPORT_PATH/objectinspector/lrpropertydelegate.cpp \
$$REPORT_PATH/objectsbrowser/lrobjectbrowser.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/lralignpropitem.cpp \
$$REPORT_PATH/items/lrhorizontallayout.cpp \
@ -161,6 +165,9 @@ HEADERS += \
$$REPORT_PATH/objectinspector/lrpropertydelegate.h \
$$REPORT_PATH/objectsbrowser/lrobjectbrowser.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/lrfonteditorwidget.h \
$$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \
@ -235,7 +242,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 \

View File

@ -35,6 +35,7 @@
#include "lrsettingdialog.h"
#include "dialogdesigner/lrdialogdesigner.h"
#include "translationeditor/translationeditor.h"
#include "scripteditor/lrscripteditor.h"
#include <QDebug>
#include <QObject>
@ -183,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();
}
@ -215,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();
}
@ -241,7 +249,8 @@ 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);
@ -435,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;
@ -787,6 +798,12 @@ void ReportDesignWidget::slotCurrentTabChanged(int index)
if (activeTabType() == Translations){
m_traslationEditor->setReportEngine(report());
}
if (activeTabType() == Script){
m_scriptEditor->initCompleter();
m_scriptEditor->setFocus();
}
emit activePageChanged();
}

View File

@ -51,6 +51,8 @@ class ReportDesignWindow;
class DialogDesignerManager;
class DialogDesigner;
class TranslationEditor;
class ScriptEditor;
class ReportDesignWidget : public QWidget
{
@ -189,7 +191,7 @@ private:
private:
ReportEnginePrivate* m_report;
QGraphicsView *m_view;
QTextEdit* m_scriptEditor;
ScriptEditor* m_scriptEditor;
TranslationEditor* m_traslationEditor;
#ifdef HAVE_QTDESIGNER_INTEGRATION
DialogDesignerManager* m_dialogDesignerManager;
@ -205,5 +207,5 @@ private:
bool m_dialogChanged;
};
}
} // namespace LimeReport
#endif // LRREPORTDESIGNWIDGET_H

View File

@ -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;
}
}

View File

@ -264,6 +264,7 @@ private:
ReportDesignWidget::EditorTabType m_editorTabType;
QByteArray m_pageEditorsState;
QByteArray m_dialogEditorsState;
QByteArray m_scriptEditorState;
QVector<QToolBar*> m_pageTools;
QVector<QToolBar*> m_dialogTools;

View 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

View 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

View 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

View 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

View 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>

View 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

View 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