You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

369 lines
15 KiB

/*
SPDX-FileCopyrightText: 2003 Andreas Gungl <a.gungl@gmx.de>
SPDX-FileCopyrightText: 2012-2020 Laurent Montel <montel@kde.org>
SPDX-License-Identifier: GPL-2.0-only
*/
#include "filterlogdialog.h"
#include <MailCommon/FilterLog>
#include <KPIMTextEdit/PlainTextEditorWidget>
#include "mailfilterpurposemenuwidget.h"
#include "mailfilteragent_debug.h"
#include <QFileDialog>
#include <KLocalizedString>
#include <KMessageBox>
#include <KStandardAction>
#include <QVBoxLayout>
#include <QIcon>
#include <QCheckBox>
#include <QLabel>
#include <QSpinBox>
#include <QStringList>
#include <QGroupBox>
#include <QAction>
#include <QMenu>
#include <QPointer>
#include <errno.h>
#include <KSharedConfig>
#include <QHBoxLayout>
#include <QDialogButtonBox>
#include <KConfigGroup>
#include <QPushButton>
#include <KGuiItem>
using namespace MailCommon;
FilterLogDialog::FilterLogDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(i18nc("@title:window", "Filter Log Viewer"));
auto *mainLayout = new QVBoxLayout(this);
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this);
mUser1Button = new QPushButton(this);
buttonBox->addButton(mUser1Button, QDialogButtonBox::ActionRole);
mUser2Button = new QPushButton(this);
buttonBox->addButton(mUser2Button, QDialogButtonBox::ActionRole);
connect(buttonBox, &QDialogButtonBox::rejected, this, &FilterLogDialog::reject);
setWindowIcon(QIcon::fromTheme(QStringLiteral("view-filter")));
setModal(false);
buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
KGuiItem::assign(mUser1Button, KStandardGuiItem::clear());
KGuiItem::assign(mUser2Button, KStandardGuiItem::saveAs());
QFrame *page = new QFrame(this);
auto *pageVBoxLayout = new QVBoxLayout;
page->setLayout(pageVBoxLayout);
pageVBoxLayout->setContentsMargins({});
mainLayout->addWidget(page);
mTextEdit = new KPIMTextEdit::PlainTextEditorWidget(new FilterLogTextEdit(this), page);
pageVBoxLayout->addWidget(mTextEdit);
mTextEdit->setReadOnly(true);
mTextEdit->editor()->setWordWrapMode(QTextOption::NoWrap);
const QStringList logEntries = FilterLog::instance()->logEntries();
QStringList::ConstIterator end(logEntries.constEnd());
for (QStringList::ConstIterator it = logEntries.constBegin();
it != end; ++it) {
mTextEdit->editor()->appendHtml(*it);
}
auto *purposeMenu = new MailfilterPurposeMenuWidget(this, this);
QPushButton *mShareButton = new QPushButton(i18n("Share..."), this);
mShareButton->setMenu(purposeMenu->menu());
mShareButton->setIcon(QIcon::fromTheme(QStringLiteral("document-share")));
purposeMenu->setEditorWidget(mTextEdit->editor());
buttonBox->addButton(mShareButton, QDialogButtonBox::ActionRole);
mLogActiveBox = new QCheckBox(i18n("&Log filter activities"), page);
pageVBoxLayout->addWidget(mLogActiveBox);
mLogActiveBox->setChecked(FilterLog::instance()->isLogging());
connect(mLogActiveBox, &QCheckBox::clicked, this, &FilterLogDialog::slotSwitchLogState);
mLogActiveBox->setWhatsThis(
i18n("You can turn logging of filter activities on and off here. "
"Of course, log data is collected and shown only when logging "
"is turned on. "));
mLogDetailsBox = new QGroupBox(i18n("Logging Details"), page);
pageVBoxLayout->addWidget(mLogDetailsBox);
auto *layout = new QVBoxLayout;
mLogDetailsBox->setLayout(layout);
mLogDetailsBox->setEnabled(mLogActiveBox->isChecked());
connect(mLogActiveBox, &QCheckBox::toggled, mLogDetailsBox, &QGroupBox::setEnabled);
mLogPatternDescBox = new QCheckBox(i18n("Log pattern description"));
layout->addWidget(mLogPatternDescBox);
mLogPatternDescBox->setChecked(
FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription));
connect(mLogPatternDescBox, &QCheckBox::clicked, this, &FilterLogDialog::slotChangeLogDetail);
mLogRuleEvaluationBox = new QCheckBox(i18n("Log filter &rule evaluation"));
layout->addWidget(mLogRuleEvaluationBox);
mLogRuleEvaluationBox->setChecked(
FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult));
connect(mLogRuleEvaluationBox, &QCheckBox::clicked, this, &FilterLogDialog::slotChangeLogDetail);
mLogRuleEvaluationBox->setWhatsThis(
i18n("You can control the feedback in the log concerning the "
"evaluation of the filter rules of applied filters: "
"having this option checked will give detailed feedback "
"for each single filter rule; alternatively, only "
"feedback about the result of the evaluation of all rules "
"of a single filter will be given."));
mLogPatternResultBox = new QCheckBox(i18n("Log filter pattern evaluation"));
layout->addWidget(mLogPatternResultBox);
mLogPatternResultBox->setChecked(
FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult));
connect(mLogPatternResultBox, &QCheckBox::clicked, this, &FilterLogDialog::slotChangeLogDetail);
// TODO
//QWhatsThis::add( mLogPatternResultBox,
// i18n( "" ) );
mLogFilterActionBox = new QCheckBox(i18n("Log filter actions"));
layout->addWidget(mLogFilterActionBox);
mLogFilterActionBox->setChecked(
FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction));
connect(mLogFilterActionBox, &QCheckBox::clicked, this, &FilterLogDialog::slotChangeLogDetail);
// TODO
//QWhatsThis::add( mLogFilterActionBox,
// i18n( "" ) );
QWidget *hbox = new QWidget(page);
auto *hboxHBoxLayout = new QHBoxLayout;
hbox->setLayout(hboxHBoxLayout);
hboxHBoxLayout->setContentsMargins({});
pageVBoxLayout->addWidget(hbox);
QLabel *logSizeLab = new QLabel(i18n("Log size limit:"), hbox);
hboxHBoxLayout->addWidget(logSizeLab);
mLogMemLimitSpin = new QSpinBox(hbox);
hboxHBoxLayout->addWidget(mLogMemLimitSpin);
mLogMemLimitSpin->setMinimum(1);
mLogMemLimitSpin->setMaximum(1024 * 256); // 256 MB
// value in the QSpinBox is in KB while it's in Byte in the FilterLog
mLogMemLimitSpin->setValue(FilterLog::instance()->maxLogSize() / 1024);
mLogMemLimitSpin->setSuffix(i18n(" KB"));
mLogMemLimitSpin->setSpecialValueText(
i18nc("@label:spinbox Set the size of the logfile to unlimited.", "unlimited"));
connect(mLogMemLimitSpin, qOverload<int>(&QSpinBox::valueChanged), this, &FilterLogDialog::slotChangeLogMemLimit);
mLogMemLimitSpin->setWhatsThis(
i18n("Collecting log data uses memory to temporarily store the "
"log data; here you can limit the maximum amount of memory "
"to be used: if the size of the collected log data exceeds "
"this limit then the oldest data will be discarded until "
"the limit is no longer exceeded. "));
connect(FilterLog::instance(), &FilterLog::logEntryAdded, this, &FilterLogDialog::slotLogEntryAdded);
connect(FilterLog::instance(), &FilterLog::logShrinked, this, &FilterLogDialog::slotLogShrinked);
connect(FilterLog::instance(), &FilterLog::logStateChanged, this, &FilterLogDialog::slotLogStateChanged);
mainLayout->addWidget(buttonBox);
connect(mUser1Button, &QPushButton::clicked, this, &FilterLogDialog::slotUser1);
connect(mUser2Button, &QPushButton::clicked, this, &FilterLogDialog::slotUser2);
connect(mTextEdit->editor(), &KPIMTextEdit::PlainTextEditor::textChanged, this, &FilterLogDialog::slotTextChanged);
slotTextChanged();
readConfig();
mIsInitialized = true;
}
void FilterLogDialog::slotTextChanged()
{
const bool hasText = !mTextEdit->isEmpty();
mUser2Button->setEnabled(hasText);
mUser1Button->setEnabled(hasText);
}
void FilterLogDialog::readConfig()
{
KSharedConfig::Ptr config = KSharedConfig::openConfig();
KConfigGroup group(config, "FilterLog");
const bool isEnabled = group.readEntry("Enabled", false);
const bool isLogPatternDescription = group.readEntry("LogPatternDescription", false);
const bool isLogRuleResult = group.readEntry("LogRuleResult", false);
const bool isLogPatternResult = group.readEntry("LogPatternResult", false);
const bool isLogAppliedAction = group.readEntry("LogAppliedAction", false);
const int maxLogSize = group.readEntry("maxLogSize", -1);
if (isEnabled != FilterLog::instance()->isLogging()) {
FilterLog::instance()->setLogging(isEnabled);
}
if (isLogPatternDescription != FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription)) {
FilterLog::instance()->setContentTypeEnabled(FilterLog::PatternDescription, isLogPatternDescription);
}
if (isLogRuleResult != FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult)) {
FilterLog::instance()->setContentTypeEnabled(FilterLog::RuleResult, isLogRuleResult);
}
if (isLogPatternResult != FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult)) {
FilterLog::instance()->setContentTypeEnabled(FilterLog::PatternResult, isLogPatternResult);
}
if (isLogAppliedAction != FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction)) {
FilterLog::instance()->setContentTypeEnabled(FilterLog::AppliedAction, isLogAppliedAction);
}
if (FilterLog::instance()->maxLogSize() != maxLogSize) {
FilterLog::instance()->setMaxLogSize(maxLogSize);
}
KConfigGroup geometryGroup(config, "Geometry");
const QSize size = geometryGroup.readEntry("filterLogSize", QSize(600, 400));
if (size.isValid()) {
resize(size);
} else {
adjustSize();
}
}
FilterLogDialog::~FilterLogDialog()
{
disconnect(mTextEdit->editor(), &KPIMTextEdit::PlainTextEditor::textChanged, this, &FilterLogDialog::slotTextChanged);
KConfigGroup myGroup(KSharedConfig::openConfig(), "Geometry");
myGroup.writeEntry("filterLogSize", size());
myGroup.sync();
}
void FilterLogDialog::writeConfig()
{
if (!mIsInitialized) {
return;
}
KSharedConfig::Ptr config = KSharedConfig::openConfig();
KConfigGroup group(config, "FilterLog");
group.writeEntry("Enabled", FilterLog::instance()->isLogging());
group.writeEntry("LogPatternDescription", FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription));
group.writeEntry("LogRuleResult", FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult));
group.writeEntry("LogPatternResult", FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult));
group.writeEntry("LogAppliedAction", FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction));
group.writeEntry("maxLogSize", static_cast<int>(FilterLog::instance()->maxLogSize()));
group.sync();
}
void FilterLogDialog::slotLogEntryAdded(const QString &logEntry)
{
mTextEdit->editor()->appendHtml(logEntry);
}
void FilterLogDialog::slotLogShrinked()
{
// limit the size of the shown log lines as soon as
// the log has reached it's memory limit
if (mTextEdit->editor()->document()->maximumBlockCount() <= 0) {
mTextEdit->editor()->document()->setMaximumBlockCount(mTextEdit->editor()->document()->blockCount());
}
}
void FilterLogDialog::slotLogStateChanged()
{
mLogActiveBox->setChecked(FilterLog::instance()->isLogging());
mLogPatternDescBox->setChecked(
FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription));
mLogRuleEvaluationBox->setChecked(
FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult));
mLogPatternResultBox->setChecked(
FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult));
mLogFilterActionBox->setChecked(
FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction));
// value in the QSpinBox is in KB while it's in Byte in the FilterLog
int newLogSize = FilterLog::instance()->maxLogSize() / 1024;
if (mLogMemLimitSpin->value() != newLogSize) {
if (newLogSize <= 0) {
mLogMemLimitSpin->setValue(1);
} else {
mLogMemLimitSpin->setValue(newLogSize);
}
}
writeConfig();
}
void FilterLogDialog::slotChangeLogDetail()
{
if (mLogPatternDescBox->isChecked()
!= FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription)) {
FilterLog::instance()->setContentTypeEnabled(FilterLog::PatternDescription,
mLogPatternDescBox->isChecked());
}
if (mLogRuleEvaluationBox->isChecked()
!= FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult)) {
FilterLog::instance()->setContentTypeEnabled(FilterLog::RuleResult,
mLogRuleEvaluationBox->isChecked());
}
if (mLogPatternResultBox->isChecked()
!= FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult)) {
FilterLog::instance()->setContentTypeEnabled(FilterLog::PatternResult,
mLogPatternResultBox->isChecked());
}
if (mLogFilterActionBox->isChecked()
!= FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction)) {
FilterLog::instance()->setContentTypeEnabled(FilterLog::AppliedAction,
mLogFilterActionBox->isChecked());
}
}
void FilterLogDialog::slotSwitchLogState()
{
FilterLog::instance()->setLogging(mLogActiveBox->isChecked());
}
void FilterLogDialog::slotChangeLogMemLimit(int value)
{
mTextEdit->editor()->document()->setMaximumBlockCount(0); //Reset value
if (value == 1) { //unilimited
FilterLog::instance()->setMaxLogSize(-1);
} else {
FilterLog::instance()->setMaxLogSize(value * 1024);
}
}
void FilterLogDialog::slotUser1()
{
FilterLog::instance()->clear();
mTextEdit->editor()->clear();
}
void FilterLogDialog::slotUser2()
{
QPointer<QFileDialog> fdlg(new QFileDialog(this));
fdlg->setAcceptMode(QFileDialog::AcceptSave);
fdlg->setFileMode(QFileDialog::AnyFile);
fdlg->selectFile(QStringLiteral("kmail-filter.html"));
if (fdlg->exec() == QDialog::Accepted) {
const QStringList fileName = fdlg->selectedFiles();
if (!fileName.isEmpty() && !FilterLog::instance()->saveToFile(fileName.at(0))) {
KMessageBox::error(this,
i18n("Could not write the file %1:\n"
"\"%2\" is the detailed error description.",
fileName.at(0),
QString::fromLocal8Bit(strerror(errno))),
i18n("KMail Error"));
}
}
delete fdlg;
}
FilterLogTextEdit::FilterLogTextEdit(QWidget *parent)
: KPIMTextEdit::PlainTextEditor(parent)
{
}
void FilterLogTextEdit::addExtraMenuEntry(QMenu *menu, QPoint pos)
{
Q_UNUSED(pos)
if (!document()->isEmpty()) {
auto *sep = new QAction(menu);
sep->setSeparator(true);
menu->addAction(sep);
QAction *clearAllAction = KStandardAction::clear(this, &FilterLogTextEdit::clear, menu);
menu->addAction(clearAllAction);
}
}