sieveactionwidgetlister.cpp 17.1 KB
Newer Older
1
/*
Laurent Montel's avatar
Laurent Montel committed
2
   Copyright (C) 2013-2020 Laurent Montel <montel@kde.org>
3

Laurent Montel's avatar
Laurent Montel committed
4 5 6 7
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
8

Laurent Montel's avatar
Laurent Montel committed
9 10 11 12
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
13

Laurent Montel's avatar
Laurent Montel committed
14 15 16 17
   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
*/

#include "sieveactionwidgetlister.h"
#include "autocreatescriptdialog.h"
#include "sieveeditorgraphicalmodewidget.h"
#include "sievescriptdescriptiondialog.h"
#include "sieveactions/sieveaction.h"
#include "sieveactions/sieveactionlist.h"
#include "commonwidgets/sievehelpbutton.h"
#include "autocreatescriptutil_p.h"

#include <QPushButton>
#include <KLocalizedString>
#include <QIcon>

33
#include <QComboBox>
34 35
#include <QGridLayout>
#include <QLabel>
36
#include <QPointer>
37 38 39 40
#include "libksieve_debug.h"
#include <QToolButton>
#include <QWhatsThis>

Laurent Montel's avatar
Laurent Montel committed
41 42
#include <autocreatescripts/sieveactions/sieveactionsetvariable.h>

43 44 45 46 47
using namespace KSieveUi;

static int MINIMUMACTION = 1;
static int MAXIMUMACTION = 8;

48
SieveActionWidget::SieveActionWidget(SieveEditorGraphicalModeWidget *graphicalModeWidget, QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
49 50
    : QWidget(parent)
    , mSieveGraphicalModeWidget(graphicalModeWidget)
51 52 53 54 55 56 57 58 59 60 61 62
{
    initWidget();
}

SieveActionWidget::~SieveActionWidget()
{
    qDeleteAll(mActionList);
    mActionList.clear();
}

bool SieveActionWidget::isConfigurated() const
{
Laurent Montel's avatar
Laurent Montel committed
63
    return mComboBox->currentIndex() != (mComboBox->count() - 1);
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
}

void SieveActionWidget::setFilterAction(QWidget *widget)
{
    if (mLayout->itemAtPosition(1, 3)) {
        delete mLayout->itemAtPosition(1, 3)->widget();
    }

    if (widget) {
        mLayout->addWidget(widget, 1, 3);
    } else {
        mLayout->addWidget(new QLabel(i18n("Please select an action."), this), 1, 3);
    }
}

Laurent Montel's avatar
Laurent Montel committed
79
void SieveActionWidget::generatedScript(QString &script, QStringList &required, bool onlyActions, bool inForEveryPartLoop)
80 81 82 83 84 85
{
    const int index = mComboBox->currentIndex();
    if (index != mComboBox->count() - 1) {
        KSieveUi::SieveAction *widgetAction = mActionList.at(mComboBox->currentIndex());
        QWidget *currentWidget = mLayout->itemAtPosition(1, 3)->widget();
        const QStringList lstRequires = widgetAction->needRequires(currentWidget);
Laurent Montel's avatar
Laurent Montel committed
86
        for (const QString &r : lstRequires) {
Laurent Montel's avatar
Laurent Montel committed
87 88
            if (!required.contains(r)) {
                required.append(r);
89 90 91
            }
        }
        QString comment = widgetAction->comment();
Laurent Montel's avatar
Laurent Montel committed
92 93 94 95 96 97 98
        QString indent;
        if (!onlyActions) {
            indent += AutoCreateScriptUtil::indentation();
        }
        if (inForEveryPartLoop) {
            indent += AutoCreateScriptUtil::indentation();
        }
Laurent Montel's avatar
Laurent Montel committed
99
        if (!comment.trimmed().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
100 101
            const QVector<QStringRef> commentList = comment.splitRef(QLatin1Char('\n'));
            for (const QStringRef &str : commentList) {
102 103 104 105 106 107
                if (str.isEmpty()) {
                    script += QLatin1Char('\n');
                } else {
                    script += indent + QLatin1Char('#') + str + QLatin1Char('\n');
                }
            }
108
        }
Laurent Montel's avatar
Laurent Montel committed
109
        script += indent + widgetAction->code(currentWidget) + QLatin1Char('\n');
110 111 112 113 114 115 116 117
    }
}

void SieveActionWidget::initWidget()
{
    mLayout = new QGridLayout(this);
    mLayout->setContentsMargins(0, 0, 0, 0);

118
    mComboBox = new QComboBox;
119
    mComboBox->setEditable(false);
120
    mComboBox->setMinimumWidth(50);
Laurent Montel's avatar
Laurent Montel committed
121 122 123
    const QVector<KSieveUi::SieveAction *> list = KSieveUi::SieveActionList::actionList(mSieveGraphicalModeWidget);
    QVector<KSieveUi::SieveAction *>::const_iterator it;
    QVector<KSieveUi::SieveAction *>::const_iterator end(list.constEnd());
124
    int index = 0;
125
    QStringList listCapabilities = mSieveGraphicalModeWidget->sieveCapabilities();
126
    //imapflags was old name of imap4flags but still used.
Laurent Montel's avatar
Laurent Montel committed
127
    if (listCapabilities.contains(QLatin1String("imap4flags"))) {
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
        listCapabilities.append(QStringLiteral("imapflags"));
    }
    for (index = 0, it = list.constBegin(); it != end; ++it, ++index) {
        if ((*it)->needCheckIfServerHasCapability()) {
            if (listCapabilities.contains((*it)->serverNeedsCapability())) {
                // append to the list of actions:
                mActionList.append(*it);
                connect(*it, &SieveAction::valueChanged, this, &SieveActionWidget::valueChanged);
                // add (i18n-ized) name to combo box
                mComboBox->addItem((*it)->label(), (*it)->name());
            } else {
                delete(*it);
            }
        } else {
            // append to the list of actions:
            mActionList.append(*it);
            connect(*it, &SieveAction::valueChanged, this, &SieveActionWidget::valueChanged);
            // add (i18n-ized) name to combo box
            mComboBox->addItem((*it)->label(), (*it)->name());
        }
    }

Laurent Montel's avatar
Laurent Montel committed
150
    mHelpButton = new SieveHelpButton(this);
151 152 153
    connect(mHelpButton, &SieveHelpButton::clicked, this, &SieveActionWidget::slotHelp);
    mLayout->addWidget(mHelpButton, 1, 0);

Laurent Montel's avatar
Laurent Montel committed
154
    mCommentButton = new QToolButton(this);
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
    mCommentButton->setToolTip(i18n("Add comment"));
    mLayout->addWidget(mCommentButton, 1, 1);
    mCommentButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-notes")));
    connect(mCommentButton, &QToolButton::clicked, this, &SieveActionWidget::slotAddComment);

    mComboBox->addItem(QLatin1String(""));

    mComboBox->setMaxCount(mComboBox->count());
    mComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
    setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
    mComboBox->adjustSize();
    mLayout->addWidget(mComboBox, 1, 2);

    updateGeometry();

170
    connect(mComboBox, QOverload<int>::of(&QComboBox::activated), this, &SieveActionWidget::slotActionChanged);
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

    mAdd = new QPushButton(this);
    mAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
    mAdd->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));

    mRemove = new QPushButton(this);
    mRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
    mRemove->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
    mLayout->addWidget(mAdd, 1, 4);
    mLayout->addWidget(mRemove, 1, 5);

    // redirect focus to the filter action combo box
    setFocusProxy(mComboBox);

    connect(mAdd, &QPushButton::clicked, this, &SieveActionWidget::slotAddWidget);
    connect(mRemove, &QPushButton::clicked, this, &SieveActionWidget::slotRemoveWidget);
Laurent Montel's avatar
Laurent Montel committed
187 188

    clear();
189 190 191 192 193 194 195 196 197 198 199 200 201 202
}

void SieveActionWidget::slotHelp()
{
    const int index = mComboBox->currentIndex();
    if (index < mActionList.count()) {
        KSieveUi::SieveAction *action = mActionList.at(index);
        const QString help = action->help();
        const QUrl href = action->href();
        const QString fullWhatsThis = AutoCreateScriptUtil::createFullWhatsThis(help, href.toString());
        QWhatsThis::showText(QCursor::pos(), fullWhatsThis, mHelpButton);
    }
}

Laurent Montel's avatar
Laurent Montel committed
203 204 205 206 207 208 209 210
void SieveActionWidget::clear()
{
    mComboBox->setCurrentIndex(mComboBox->count() - 1);
    setFilterAction(nullptr);
    mCommentButton->setEnabled(false);
    mHelpButton->setEnabled(false);
}

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
void SieveActionWidget::slotAddComment()
{
    const int index = mComboBox->currentIndex();
    if (index < mActionList.count()) {
        KSieveUi::SieveAction *action = mActionList.at(index);
        const QString comment = action->comment();
        QPointer<SieveScriptDescriptionDialog> dlg = new SieveScriptDescriptionDialog;
        dlg->setDescription(comment);
        if (dlg->exec()) {
            action->setComment(dlg->description());
            Q_EMIT valueChanged();
        }
        delete dlg;
    }
}

void SieveActionWidget::slotActionChanged(int index)
{
    if (index < mActionList.count()) {
        KSieveUi::SieveAction *action = mActionList.at(index);
        mHelpButton->setEnabled(!action->help().isEmpty());
        mCommentButton->setEnabled(true);
        setFilterAction(action->createParamWidget(this));
        //All actions after stop will not execute => don't allow to add more actions.
        const bool enableAddAction = (action->name() != QLatin1String("stop"));
        mAdd->setEnabled(enableAddAction);
    } else {
        mAdd->setEnabled(true);
        mCommentButton->setEnabled(false);
Laurent Montel's avatar
Laurent Montel committed
240
        setFilterAction(nullptr);
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
        mHelpButton->setEnabled(false);
    }
    Q_EMIT valueChanged();
}

void SieveActionWidget::slotAddWidget()
{
    Q_EMIT valueChanged();
    Q_EMIT addWidget(this);
}

void SieveActionWidget::slotRemoveWidget()
{
    Q_EMIT valueChanged();
    Q_EMIT removeWidget(this);
}

void SieveActionWidget::updateAddRemoveButton(bool addButtonEnabled, bool removeButtonEnabled)
{
    mAdd->setEnabled(addButtonEnabled);
    mRemove->setEnabled(removeButtonEnabled);
}

Laurent Montel's avatar
Laurent Montel committed
264 265 266 267 268 269
void SieveActionWidget::setLocaleVariable(const SieveGlobalVariableActionWidget::VariableElement &var)
{
    const int index = mComboBox->findData(QStringLiteral("set"));
    if (index != -1) {
        mComboBox->setCurrentIndex(index);
        slotActionChanged(index);
Laurent Montel's avatar
Laurent Montel committed
270
        KSieveUi::SieveActionSetVariable *localVar = qobject_cast<KSieveUi::SieveActionSetVariable *>(mActionList.at(index));
Laurent Montel's avatar
Laurent Montel committed
271 272 273 274 275
        if (localVar) {
            localVar->setLocalVariable(this, var);
        }
    } else {
        //error += i18n("Script contains unsupported feature \"%1\"", actionName) + QLatin1Char('\n');
Laurent Montel's avatar
Laurent Montel committed
276
        //qCDebug(LIBKSIEVE_LOG) << "Action " << actionName << " not supported";
Laurent Montel's avatar
Laurent Montel committed
277 278 279
    }
}

Laurent Montel's avatar
Laurent Montel committed
280
void SieveActionWidget::setAction(const QString &actionName, QXmlStreamReader &element, const QString &comment, QString &error)
281 282 283 284 285 286
{
    const int index = mComboBox->findData(actionName);
    if (index != -1) {
        mComboBox->setCurrentIndex(index);
        slotActionChanged(index);
        KSieveUi::SieveAction *action = mActionList.at(index);
Laurent Montel's avatar
Laurent Montel committed
287
        action->setParamWidgetValue(element, this, error);
288
        action->setComment(comment);
Laurent Montel's avatar
Laurent Montel committed
289
    } else {
Laurent Montel's avatar
Laurent Montel committed
290
        error += i18n("Script contains unsupported feature \"%1\"", actionName) + QLatin1Char('\n');
Laurent Montel's avatar
Laurent Montel committed
291 292
        qCDebug(LIBKSIEVE_LOG) << "Action " << actionName << " not supported";
        element.skipCurrentElement();
293 294 295
    }
}

296
SieveActionWidgetLister::SieveActionWidgetLister(SieveEditorGraphicalModeWidget *graphicalModeWidget, QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
297 298
    : KPIM::KWidgetLister(false, MINIMUMACTION, MAXIMUMACTION, parent)
    , mSieveGraphicalModeWidget(graphicalModeWidget)
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
{
    slotClear();
    updateAddRemoveButton();
}

SieveActionWidgetLister::~SieveActionWidgetLister()
{
}

void SieveActionWidgetLister::slotAddWidget(QWidget *w)
{
    addWidgetAfterThisWidget(w);
    updateAddRemoveButton();
}

void SieveActionWidgetLister::slotRemoveWidget(QWidget *w)
{
    removeWidget(w);
    updateAddRemoveButton();
}

void SieveActionWidgetLister::updateAddRemoveButton()
{
    QList<QWidget *> widgetList = widgets();
    const int numberOfWidget(widgetList.count());
    bool addButtonEnabled = false;
    bool removeButtonEnabled = false;
    if (numberOfWidget <= widgetsMinimum()) {
        addButtonEnabled = true;
        removeButtonEnabled = false;
    } else if (numberOfWidget >= widgetsMaximum()) {
        addButtonEnabled = false;
        removeButtonEnabled = true;
    } else {
        addButtonEnabled = true;
        removeButtonEnabled = true;
    }
    QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
    QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
    for (; wIt != wEnd; ++wIt) {
        SieveActionWidget *w = qobject_cast<SieveActionWidget *>(*wIt);
        w->updateAddRemoveButton(addButtonEnabled, removeButtonEnabled);
    }
}

Laurent Montel's avatar
Laurent Montel committed
344
void SieveActionWidgetLister::generatedScript(QString &script, QStringList &requireModules, bool onlyActions, bool inForEveryPartLoop)
345 346 347 348 349 350
{
    const QList<QWidget *> widgetList = widgets();
    QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
    QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
    for (; wIt != wEnd; ++wIt) {
        SieveActionWidget *w = qobject_cast<SieveActionWidget *>(*wIt);
Laurent Montel's avatar
Laurent Montel committed
351
        w->generatedScript(script, requireModules, onlyActions, inForEveryPartLoop);
352 353 354 355 356 357 358 359 360 361 362 363
    }
}

void SieveActionWidgetLister::reconnectWidget(SieveActionWidget *w)
{
    connect(w, &SieveActionWidget::addWidget, this, &SieveActionWidgetLister::slotAddWidget, Qt::UniqueConnection);
    connect(w, &SieveActionWidget::removeWidget, this, &SieveActionWidgetLister::slotRemoveWidget, Qt::UniqueConnection);
    connect(w, &SieveActionWidget::valueChanged, this, &SieveActionWidgetLister::valueChanged, Qt::UniqueConnection);
}

void SieveActionWidgetLister::clearWidget(QWidget *aWidget)
{
Laurent Montel's avatar
Laurent Montel committed
364 365 366 367
    if (aWidget) {
        SieveActionWidget *widget = static_cast<SieveActionWidget *>(aWidget);
        widget->clear();
    }
368 369 370 371 372
    Q_EMIT valueChanged();
}

QWidget *SieveActionWidgetLister::createWidget(QWidget *parent)
{
373
    SieveActionWidget *w = new SieveActionWidget(mSieveGraphicalModeWidget, parent);
374 375 376 377 378 379 380 381 382
    reconnectWidget(w);
    return w;
}

int SieveActionWidgetLister::actionNumber() const
{
    return widgets().count();
}

Laurent Montel's avatar
Laurent Montel committed
383 384 385 386 387 388 389 390 391 392
void SieveActionWidgetLister::loadLocalVariable(const SieveGlobalVariableActionWidget::VariableElement &var)
{
    SieveActionWidget *w = qobject_cast<SieveActionWidget *>(widgets().constLast());
    if (w->isConfigurated()) {
        addWidgetAfterThisWidget(widgets().constLast());
        w = qobject_cast<SieveActionWidget *>(widgets().constLast());
    }
    w->setLocaleVariable(var);
}

Laurent Montel's avatar
Laurent Montel committed
393
void SieveActionWidgetLister::loadScript(QXmlStreamReader &element, bool onlyActions, QString &error)
394
{
395 396 397 398
    QString comment;
    if (onlyActions) {
        const QStringRef tagName = element.name();
        if (tagName == QLatin1String("action")) {
Laurent Montel's avatar
Laurent Montel committed
399 400
            if (element.attributes().hasAttribute(QLatin1String("name"))) {
                const QString actionName = element.attributes().value(QLatin1String("name")).toString();
401 402 403 404 405 406 407 408
                SieveActionWidget *w = qobject_cast<SieveActionWidget *>(widgets().constLast());
                if (w->isConfigurated()) {
                    addWidgetAfterThisWidget(widgets().constLast());
                    w = qobject_cast<SieveActionWidget *>(widgets().constLast());
                }
                w->setAction(actionName, element, comment, error);
                //comment.clear();
            } else if (tagName == QLatin1String("crlf")) {
Laurent Montel's avatar
Laurent Montel committed
409
                element.skipCurrentElement();
410 411 412 413 414
                //nothing
            } else {
                qCDebug(LIBKSIEVE_LOG) << " SieveActionWidgetLister::loadScript don't have name attribute " << tagName;
            }
        } else {
Yuri Chornoivan's avatar
Yuri Chornoivan committed
415
            qCDebug(LIBKSIEVE_LOG) << " SieveActionWidgetLister::loadScript Unknown tag name " << tagName;
416 417 418 419 420 421 422
        }
    } else {
        bool firstAction = true;
        bool previousActionWasAComment = false;
        while (element.readNextStartElement()) {
            const QStringRef tagName = element.name();
            if (tagName == QLatin1String("action") || tagName == QLatin1String("control") /*for break action*/) {
Laurent Montel's avatar
Laurent Montel committed
423 424
                if (element.attributes().hasAttribute(QLatin1String("name"))) {
                    const QString actionName = element.attributes().value(QLatin1String("name")).toString();
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
                    if (tagName == QLatin1String("control") && actionName == QLatin1String("if")) {
                        qCDebug(LIBKSIEVE_LOG) << "We found an loop if in a loop if. Not supported";
                        error += i18n("We detected a loop if in a loop if. It's not supported") + QLatin1Char('\n');
                    }
                    if (firstAction) {
                        firstAction = false;
                    } else {
                        addWidgetAfterThisWidget(widgets().constLast());
                    }
                    SieveActionWidget *w = qobject_cast<SieveActionWidget *>(widgets().constLast());
                    w->setAction(actionName, element, comment, error);
                    comment.clear();
                } else {
                    qCDebug(LIBKSIEVE_LOG) << " SieveActionWidgetLister::loadScript don't have name attribute " << tagName;
                }
                previousActionWasAComment = false;
            } else if (tagName == QLatin1String("comment")) {
                if (!comment.isEmpty()) {
                    comment += QLatin1Char('\n');
                }
                previousActionWasAComment = true;
                comment += element.readElementText();
            } else if (tagName == QLatin1String("crlf")) {
                //Add new line if previous action was a comment
                if (previousActionWasAComment) {
                    comment += QLatin1Char('\n');
                }
Laurent Montel's avatar
Laurent Montel committed
452
                element.skipCurrentElement();
453 454 455 456 457
            } else {
                qCDebug(LIBKSIEVE_LOG) << " SieveActionWidgetLister::loadScript unknown tagName " << tagName;
            }
        }
    }
458
}