SXXXXXXX_PyBusMonitor1553/cpp/GrifoScope/QgPython/Files/qgpyeditdialog.cpp
2025-12-17 07:59:30 +01:00

392 lines
12 KiB
C++

#include "qgpyeditdialog.h"
#include <QMimeData>
#include <QUrl>
#include <QLineEdit>
#include <QLabel>
#include <QPushButton>
#include <QToolButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFormLayout>
#include <QDialogButtonBox>
#include <QPlainTextEdit>
#include <QTextEdit>
#include <QCheckBox>
#include <QComboBox>
#include <QSpinBox>
#include <QFile>
#include <QTextStream>
#include <QAction>
#include <QGroupBox>
#include <QSyntaxHighlighter>
#include "qgpython.h"
class Highlighter : public QSyntaxHighlighter
{
//Q_OBJECT
public:
Highlighter(QTextDocument *parent = 0):
QSyntaxHighlighter(parent)
{
HighlightingRule rule;
keywordFormat.setForeground(Qt::darkBlue);
keywordFormat.setFontWeight(QFont::Bold);
QStringList keywordPatterns;
keywordPatterns << "\\bFalse\\b" << "\\bclas\\b" << "\\bfinally\\b"
<< "\\breturn\\b" << "\\bNone\\b" << "\\bcontinue\\b"
<< "\\bfor\\b" << "\\blambda\\b" << "\\btry\\b"
<< "\\bTrue\\b" << "\\bdef\\b" << "\\bfor\\b"
<< "\\bis\\b" << "\\bfrom\\b" << "\\bnonlocal\\b"
<< "\\bwhile\\b" << "\\band\\b" << "\\bglobal\\b"
<< "\\bnot\\b" << "\\bwith\\b" << "\\bas\\b"
<< "\\belif\\b" << "\\bor\\b" << "\\byeld\\b"
<< "\\bassert\\b" << "\\belse\\b" << "\\bimport\\b"
<< "\\bpass\\b" << "\\bbreak\\b" << "\\bin\\b" << "\\braise\\b" << "\\bif\\b" <<"\\bself\\b";
foreach (const QString &pattern, keywordPatterns) {
rule.pattern = QRegularExpression(pattern);
rule.format = keywordFormat;
highlightingRules.append(rule);
}
classFormat.setFontWeight(QFont::Bold);
classFormat.setForeground(Qt::darkMagenta);
rule.pattern = QRegularExpression("\\bQ[A-Za-z]+\\b");
rule.format = classFormat;
highlightingRules.append(rule);
quotationFormat.setForeground(Qt::darkGreen);
rule.pattern = QRegularExpression("\".*\"");
rule.format = quotationFormat;
highlightingRules.append(rule);
functionFormat.setFontItalic(true);
functionFormat.setForeground(Qt::blue);
rule.pattern = QRegularExpression("\\b[A-Za-z0-9_]+(?=\\()");
rule.format = functionFormat;
highlightingRules.append(rule);
singleLineCommentFormat.setForeground(Qt::red);
rule.pattern = QRegularExpression("//[^\n]*");
rule.format = singleLineCommentFormat;
highlightingRules.append(rule);
multiLineCommentFormat.setForeground(Qt::red);
commentStartExpression = QRegularExpression("\"\"\"");
commentEndExpression = QRegularExpression("\"\"\"");
}
protected:
void highlightBlock(const QString &text) override
{
foreach (const HighlightingRule &rule, highlightingRules) {
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
}
}
setCurrentBlockState(0);
int startIndex = 0;
if (previousBlockState() != 1)
startIndex = text.indexOf(commentStartExpression);
while (startIndex >= 0)
{
QRegularExpressionMatch match = commentEndExpression.match(text, startIndex);
int endIndex = match.capturedStart();
int commentLength = 0;
if (endIndex == -1) {
setCurrentBlockState(1);
commentLength = text.length() - startIndex;
} else {
commentLength = endIndex - startIndex
+ match.capturedLength();
}
setFormat(startIndex, commentLength, multiLineCommentFormat);
startIndex = text.indexOf(commentStartExpression, startIndex + commentLength);
}
}
private:
struct HighlightingRule
{
QRegularExpression pattern;
QTextCharFormat format;
};
QVector<HighlightingRule> highlightingRules;
QRegularExpression commentStartExpression;
QRegularExpression commentEndExpression;
QTextCharFormat keywordFormat;
QTextCharFormat classFormat;
QTextCharFormat singleLineCommentFormat;
QTextCharFormat multiLineCommentFormat;
QTextCharFormat quotationFormat;
QTextCharFormat functionFormat;
};
class QgPyEditDialog::Implementation
{
public:
//const QMimeData* md;
QHBoxLayout* fbox;
QLineEdit* lblPath;
QToolButton* btnFile;
QToolButton* btnEdit;
//QLabel* lblContents;
QTextEdit* txt;
QVBoxLayout* ly;
QLabel* lblMode;
QComboBox* cbMode;
QDialogButtonBox* bbox;
QAction* actPreviewOff;
QAction* actPreviewFull;
QAction* actPreviewEdit;
QAction* actDownload;
QString fileName;
QString txtStore;
QLineEdit* leRemPattern;
QCheckBox* cbLinePattern;
QGroupBox* boxLineDelay;
QLabel* lblLineDelay;
QSpinBox* sbLineDelay;
QSpinBox* sbCharDelay;
QLineEdit* lePrompt;
//QComboBox* cbEolTranslate;
Highlighter* highlighter;
void fileMode(bool enable)
{
lblPath->setVisible(true); //enable);
btnFile->setVisible(true); //false);
btnEdit->setVisible(true); //enable);
fbox->setEnabled(true); //enable);
if (!enable)
fileName.clear();
}
void preview(bool full)
{
if (!fileName.isEmpty())
{
QFile f(fileName);
if (f.open(QIODevice::ReadOnly|QIODevice::Text))
{
QTextStream in(&f);
if (full)
txtStore=in.readAll();
else
{
txtStore=in.read(2048);
if (f.size()>2048)
txtStore+="\r\n[...]";
}
//p_.lblContents->setVisible(true);
}
else
txtStore="Error reading file: "+f.errorString();
}
txt->setPlainText(txtStore);
}
};
QgPyEditDialog::QgPyEditDialog(QWidget *parent):
QDialog(parent, Qt::Tool),
p_(*new Implementation)
{
//File name box:
p_.fbox=new QHBoxLayout;
p_.lblPath=new QLineEdit("<path>");
p_.lblPath->setReadOnly(true);
p_.lblPath->setDisabled(true);
p_.btnFile=new QToolButton;
p_.btnFile->setText("<");
p_.btnEdit=new QToolButton;
p_.btnEdit->setText("+");
p_.btnEdit->setPopupMode(QToolButton::InstantPopup);
p_.actPreviewOff=new QAction("Small Preview", this);
p_.actPreviewFull=new QAction("Full Preview", this);
p_.actPreviewEdit=new QAction("Edit", this);
//p_.actDownload=new QAction("Download...", this);
p_.btnEdit->addAction(p_.actPreviewOff);
p_.btnEdit->addAction(p_.actPreviewFull);
p_.btnEdit->addAction(p_.actPreviewEdit);
//p_.btnEdit->addAction(p_.actDownload);
p_.fbox->addWidget(p_.lblPath);
p_.fbox->addWidget(p_.btnFile);
p_.fbox->addWidget(p_.btnEdit);
p_.ly=new QVBoxLayout;
p_.ly->addLayout(p_.fbox); //lblPath);
//p_.lblContents=new QLabel("<Contents>");
//p_.lblContents->setFrameShape(QFrame::Box);
p_.txt=new QTextEdit;
p_.txt->setReadOnly(true);
p_.txt->addAction(p_.actPreviewEdit);
p_.txt->setContextMenuPolicy(Qt::ActionsContextMenu);
p_.txt->setVisible(false);
p_.txt->setAcceptDrops(false);
p_.ly->addWidget(p_.txt);
//p_.ly->addWidget(p_.lblContents);
p_.highlighter = new Highlighter(p_.txt->document());
//p_.lblMode=new QLabel("Mode:");
#if 0
QFormLayout* hly=new QFormLayout;
p_.cbMode=new QComboBox;
p_.cbMode->addItems(QStringList()<<"auto"<<"Text"<<"Binary");
p_.cbMode->setEnabled(true);
hly->addRow("Mode:", p_.cbMode);
p_.cbLinePattern=new QCheckBox("Remove:", this);
p_.leRemPattern=new QLineEdit("-----", this);
p_.leRemPattern->setEnabled(false);
connect(p_.cbLinePattern, SIGNAL(toggled(bool)), p_.leRemPattern, SLOT(setEnabled(bool)));
hly->addRow(p_.cbLinePattern, p_.leRemPattern);
//hly->addWidget(p_.lblMode);
//hly->addWidget(p_.cbMode);
p_.ly->addLayout(hly);
//Line delay
{
p_.boxLineDelay=new QGroupBox("Line Control:");
QVBoxLayout* lyCtrl=new QVBoxLayout;
QHBoxLayout* lyDelay=new QHBoxLayout;
p_.lblLineDelay=new QLabel("Char/Line delay (ms)");
lyDelay->addWidget(p_.lblLineDelay);
p_.sbCharDelay=new QSpinBox;
p_.sbCharDelay->setRange(-1, 1000);
p_.sbCharDelay->setToolTip("<0 = wait for echo with timeout");
lyDelay->addWidget(p_.sbCharDelay);
p_.sbLineDelay=new QSpinBox;
p_.sbLineDelay->setRange(-1, 1000);
p_.sbLineDelay->setToolTip("<0 = wait for prompt, with timeout");
lyDelay->addWidget(p_.sbLineDelay);
p_.lePrompt=new QLineEdit;
p_.lePrompt->setToolTip("Prompt pettern");
p_.lePrompt->setPlaceholderText("Prompt");
lyDelay->addWidget(p_.lePrompt);
lyCtrl->addLayout(lyDelay);
p_.boxLineDelay->setLayout(lyCtrl);
p_.ly->addWidget(p_.boxLineDelay);
}
#endif
p_.bbox=new QDialogButtonBox(QDialogButtonBox::Ok
| QDialogButtonBox::Cancel); //|QDialogButtonBox::Open);
//p_.bbox->button(QDialogButtonBox::Open)->setText("Execute");
p_.bbox->button(QDialogButtonBox::Ok)->setText("Execute");
p_.ly->addWidget(p_.bbox);
setLayout(p_.ly);
connect(p_.actPreviewEdit, SIGNAL(triggered()), SLOT(previewEdit()));
connect(p_.actPreviewFull, SIGNAL(triggered()), SLOT(previewFull()));
connect(p_.actPreviewOff, SIGNAL(triggered()), SLOT(previewSmall()));
connect(p_.bbox, SIGNAL(rejected()), SLOT(reject()));
//connect(p_.bbox, SIGNAL(accepted()), SLOT(accept()));
connect(p_.bbox, SIGNAL(accepted()), SLOT(executeScript()));
}
QgPyEditDialog::~QgPyEditDialog()
{
delete &p_;
}
void QgPyEditDialog::previewSmall()
{
p_.txt->setVisible(true);
p_.preview(false);
}
void QgPyEditDialog::previewFull()
{
p_.txt->setVisible(true);
p_.preview(true);
}
void QgPyEditDialog::previewEdit()
{
previewFull();
p_.txt->setContextMenuPolicy(Qt::DefaultContextMenu);
p_.txt->setReadOnly(false);
p_.btnEdit->setEnabled(false);
p_.fileMode(false);
//p_.cbMode->setCurrentIndex(1);
//p_.cbMode->setEnabled(false);
}
void QgPyEditDialog::executeScript()
{
QgPython::instance().mainContext().evalScript(p_.txt->toPlainText());
}
void QgPyEditDialog::showFile(const QString& filename)
{
QMimeData md;
md.setUrls(QList<QUrl>()<<QUrl::fromLocalFile(filename));
setMimeInfo(&md);
show();
}
void QgPyEditDialog::setMimeInfo(const QMimeData* md)
{
//p_.md=md;
p_.btnEdit->setEnabled(true);
p_.txt->setReadOnly(true);
p_.txt->setContextMenuPolicy(Qt::ActionsContextMenu);
if (md->hasUrls())
{
p_.lblPath->setText(md->urls()[0].fileName());
p_.fileName=md->urls()[0].toLocalFile();
p_.txtStore.clear();
p_.txt->clear();
p_.txt->setVisible(false);
p_.fileMode(true);
}
else if (md->hasText())
{
p_.txtStore=md->text();
p_.fileMode(false);
previewFull();
}
}
QString QgPyEditDialog::text()
{
if (!p_.txt->isReadOnly())
return p_.txt->toPlainText();
return QString();
}
QString QgPyEditDialog::execFile()
{
return p_.fileName;
}
QgPyEditDialog* QgPyEditDialog::singleInstance()
{
static QgPyEditDialog dlg;
return &dlg;
}