Libksieve

sievescriptdebuggerfrontendwidget.cpp
1/*
2 SPDX-FileCopyrightText: 2015-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "sievescriptdebuggerfrontendwidget.h"
8#include "sievescriptdebuggerresulteditor.h"
9#include "sievescriptdebuggertextedit.h"
10#include "sievescriptdebuggerwarning.h"
11
12#include "editor/sievetextedit.h"
13#include "editor/sievetexteditwidget.h"
14#include <KLineEdit>
15#include <KLocalizedString>
16#include <TextCustomEditor/PlainTextEditorWidget>
17#ifdef HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
18#include <TextEditTextToSpeech/TextToSpeechContainerWidget>
19#endif
20#include <KLineEditEventHandler>
21#include <KUrlRequester>
22#include <QDate>
23#include <QFormLayout>
24#include <QLabel>
25#include <QProcess>
26#include <QScrollBar>
27#include <QSplitter>
28#include <QTemporaryFile>
29#include <QTextStream>
30#include <QVBoxLayout>
31
32using namespace KSieveUi;
33
34SieveScriptDebuggerFrontEndWidget::SieveScriptDebuggerFrontEndWidget(QWidget *parent)
35 : QWidget(parent)
36{
37 auto mainLayout = new QVBoxLayout(this);
38 mainLayout->setContentsMargins({});
39
40 auto formLayout = new QFormLayout;
41 mainLayout->addLayout(formLayout);
42
43 auto emailLab = new QLabel(i18nc("@label:textbox", "Email path:"), this);
44 emailLab->setObjectName(QLatin1StringView("emaillab"));
45
46 mEmailPath = new KUrlRequester(this);
47 mEmailPath->setObjectName(QLatin1StringView("emailpath"));
48 mEmailPath->lineEdit()->setTrapReturnKey(true);
49 mEmailPath->lineEdit()->setClearButtonEnabled(true);
50 connect(mEmailPath->lineEdit(), &KLineEdit::textChanged, this, &SieveScriptDebuggerFrontEndWidget::slotEmailChanged);
51
52 formLayout->addRow(emailLab, mEmailPath);
53
54 auto extensionLayout = new QHBoxLayout;
55 mainLayout->addLayout(extensionLayout);
56
57 auto extensionLab = new QLabel(i18nc("@label:textbox", "Extension:"));
58 extensionLab->setObjectName(QLatin1StringView("extensionlab"));
59
60 mExtension = new QLineEdit(this);
61 mExtension->setObjectName(QLatin1StringView("extension"));
62 mExtension->setPlaceholderText(i18nc("@info:placeholder", "Activate extension with \"+<name of extension>\", deactivate it with \"-<name of extension>\""));
63 mExtension->setClearButtonEnabled(true);
65
66 formLayout->addRow(extensionLab, mExtension);
67
68 mSplitter = new QSplitter(Qt::Vertical);
69 mSplitter->setObjectName(QLatin1StringView("splitter"));
70 mainLayout->addWidget(mSplitter);
71
72 auto sieveEditorWidget = new QWidget(this);
73 auto vboxSieveEditorLayout = new QVBoxLayout;
74 sieveEditorWidget->setLayout(vboxSieveEditorLayout);
75 vboxSieveEditorLayout->setContentsMargins({});
76#ifdef HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
77 auto textToSpeechWidget = new TextEditTextToSpeech::TextToSpeechContainerWidget(this);
78 textToSpeechWidget->setObjectName(QLatin1StringView("texttospeechwidget"));
79 vboxSieveEditorLayout->addWidget(textToSpeechWidget);
80#endif
81 auto textEdit = new KSieveUi::SieveScriptDebuggerTextEdit(this);
82 connect(textEdit, &KSieveUi::SieveScriptDebuggerTextEdit::textChanged, this, &SieveScriptDebuggerFrontEndWidget::slotScriptTextChanged);
83 mSieveTextEditWidget = new KSieveUi::SieveTextEditWidget(textEdit, this);
84 mSieveTextEditWidget->setObjectName(QLatin1StringView("sievetexteditwidget"));
85 vboxSieveEditorLayout->addWidget(mSieveTextEditWidget);
86#ifdef HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
87 connect(mSieveTextEditWidget->textEdit(), &SieveTextEdit::say, textToSpeechWidget, &TextEditTextToSpeech::TextToSpeechContainerWidget::say);
88#endif
89 mSplitter->addWidget(sieveEditorWidget);
90 mSplitter->setChildrenCollapsible(false);
91
92 mSieveTestResult = new TextCustomEditor::PlainTextEditorWidget(new KSieveUi::SieveScriptDebuggerResultEditor(this), this);
93 mSieveTestResult->setObjectName(QLatin1StringView("sievetextresult"));
94 mSieveTestResult->editor()->setPlaceholderText(i18nc("@info:placeholder", "Display check script result…"));
95 mSieveTestResult->setReadOnly(true);
96 mSplitter->addWidget(mSieveTestResult);
97
98 mSieveScriptDebuggerWarning = new KSieveUi::SieveScriptDebuggerWarning(this);
99 mSieveScriptDebuggerWarning->setObjectName(QLatin1StringView("sievescriptdebuggerwarning"));
100 mainLayout->addWidget(mSieveScriptDebuggerWarning);
101
102 connect(this, &SieveScriptDebuggerFrontEndWidget::debugScriptButtonClicked, this, &SieveScriptDebuggerFrontEndWidget::slotDebugScript);
103}
104
105SieveScriptDebuggerFrontEndWidget::~SieveScriptDebuggerFrontEndWidget()
106{
107 disconnect(mSieveTextEditWidget->textEdit(),
109 this,
110 &SieveScriptDebuggerFrontEndWidget::slotScriptTextChanged);
111}
112
113void SieveScriptDebuggerFrontEndWidget::updateDebugButton()
114{
115 Q_EMIT debugButtonEnabled(!mSieveTextEditWidget->textEdit()->document()->isEmpty() && !mEmailPath->lineEdit()->text().trimmed().isEmpty());
116}
117
118void SieveScriptDebuggerFrontEndWidget::slotScriptTextChanged()
119{
120 updateDebugButton();
121 Q_EMIT scriptTextChanged();
122}
123
124void SieveScriptDebuggerFrontEndWidget::setSplitterSizes(const QList<int> &sizes)
125{
126 mSplitter->setSizes(sizes);
127}
128
129QList<int> SieveScriptDebuggerFrontEndWidget::splitterSizes() const
130{
131 return mSplitter->sizes();
132}
133
134void SieveScriptDebuggerFrontEndWidget::slotEmailChanged(const QString &text)
135{
136 Q_UNUSED(text)
137 updateDebugButton();
138}
139
140void SieveScriptDebuggerFrontEndWidget::slotDebugScript()
141{
142 if (mSieveTextEditWidget->textEdit()->toPlainText().trimmed().isEmpty()) {
143 mSieveScriptDebuggerWarning->setErrorMessage(i18n("Script text is empty."));
144 return;
145 }
146 if (!mEmailPath->url().isLocalFile()) {
147 mSieveScriptDebuggerWarning->setWarningMessage(i18n("Email file must be installed locally."));
148 return;
149 }
150
151 auto temporaryFile = new QTemporaryFile();
152 if (!temporaryFile->open()) {
153 mSieveScriptDebuggerWarning->setErrorMessage(i18n("Failed to open temporary file."));
154 delete temporaryFile;
155 return;
156 }
157 Q_EMIT debugButtonEnabled(false);
158 QTextStream stream(temporaryFile);
159 stream << mSieveTextEditWidget->textEdit()->toPlainText();
160 temporaryFile->flush();
161 mProcess = new QProcess(this);
162 temporaryFile->setParent(mProcess);
163
164 QString extensionList;
165 if (!mExtension->text().trimmed().isEmpty()) {
166 extensionList = QStringLiteral("-x \"%1\"").arg(mExtension->text());
167 }
168
169 QStringList arguments;
170 if (!extensionList.isEmpty()) {
171 arguments << extensionList;
172 }
173
174 arguments << temporaryFile->fileName() << mEmailPath->url().toLocalFile();
175 mProcess->start(QStringLiteral("sieve-test"), QStringList() << arguments);
176 connect(mProcess, &QProcess::readyReadStandardOutput, this, &SieveScriptDebuggerFrontEndWidget::slotReadStandardOutput);
177 connect(mProcess, &QProcess::readyReadStandardError, this, &SieveScriptDebuggerFrontEndWidget::slotReadErrorOutput);
178 connect(mProcess, &QProcess::finished, this, &SieveScriptDebuggerFrontEndWidget::slotDebugFinished);
179 mSieveTestResult->editor()->appendPlainText(QStringLiteral("--------------------------------------"));
180 mSieveTestResult->editor()->appendPlainText(QLocale().toString(QDateTime::currentDateTime()));
181 mSieveTestResult->editor()->appendPlainText(QStringLiteral("\n"));
182 if (!mProcess->waitForStarted()) {
183 delete mProcess;
184 mProcess = nullptr;
185 Q_EMIT debugButtonEnabled(true);
186 }
187}
188
189void SieveScriptDebuggerFrontEndWidget::slotReadStandardOutput()
190{
191 const QByteArray result = mProcess->readAllStandardOutput();
192 mSieveTestResult->editor()->appendPlainText(QString::fromLocal8Bit(result));
193}
194
195void SieveScriptDebuggerFrontEndWidget::slotReadErrorOutput()
196{
197 const QByteArray result = mProcess->readAllStandardError();
198 mSieveTestResult->editor()->appendPlainText(QString::fromLocal8Bit(result));
199}
200
201void SieveScriptDebuggerFrontEndWidget::slotDebugFinished()
202{
203 delete mProcess;
204 mProcess = nullptr;
205 Q_EMIT debugButtonEnabled(true);
206}
207
208QString SieveScriptDebuggerFrontEndWidget::script() const
209{
210 return mSieveTextEditWidget->textEdit()->toPlainText();
211}
212
213void SieveScriptDebuggerFrontEndWidget::setScript(const QString &script)
214{
215 mSieveTextEditWidget->textEdit()->setPlainText(script);
216 mSieveTextEditWidget->textEdit()->verticalScrollBar()->setValue(mSieveTextEditWidget->textEdit()->verticalScrollBar()->maximum());
217}
218
219bool SieveScriptDebuggerFrontEndWidget::canAccept() const
220{
221 const QString script = mSieveTextEditWidget->textEdit()->toPlainText();
222 if (script.contains(QLatin1StringView("debug_log")) || script.contains(QLatin1StringView("vnd.dovecot.debug"))) {
223 mSieveScriptDebuggerWarning->setErrorMessage(i18n("Script still contains debug method. Remove it please."));
224 return false;
225 }
226 mSieveScriptDebuggerWarning->hide();
227 return true;
228}
229
230#include "moc_sievescriptdebuggerfrontendwidget.cpp"
KLineEdit * lineEdit() const
QUrl url() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
void catchReturnKey(QObject *lineEdit)
QScrollBar * verticalScrollBar() const const
void setValue(int)
void addLayout(QLayout *layout, int stretch)
QDateTime currentDateTime()
void setContentsMargins(const QMargins &margins)
void textChanged(const QString &text)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QTextDocument * document() const const
void setPlainText(const QString &text)
void textChanged()
QString toPlainText() const const
void finished(int exitCode, QProcess::ExitStatus exitStatus)
QByteArray readAllStandardError()
QByteArray readAllStandardOutput()
void readyReadStandardError()
void readyReadStandardOutput()
void start(OpenMode mode)
bool waitForStarted(int msecs)
void setSizes(const QList< int > &list)
QList< int > sizes() const const
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromLocal8Bit(QByteArrayView str)
bool isEmpty() const const
QString trimmed() const const
Vertical
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isLocalFile() const const
QString toLocalFile() const const
void hide()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:14:30 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.