/*
  Copyright (c) 2013-2020 Laurent Montel <montel@kde.org>

  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License, version 2, as
  published by the Free Software Foundation.

  This program 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
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "configurecomposerpage.h"
#include <PimCommon/ConfigureImmutableWidgetUtils>
using namespace PimCommon::ConfigureImmutableWidgetUtils;
#include "kmkernel.h"

#include "kmmainwidget.h"
#include <PimCommon/AutoCorrectionWidget>
#include <MessageComposer/ImageScalingWidget>
#include <MessageComposer/MessageComposerSettings>
#include <MessageCore/MessageCoreSettings>
#include "settings/kmailsettings.h"
#include "configuredialog/configuredialoglistview.h"
#include <PimCommon/SimpleStringListEditor>
#include "templatesconfiguration_kfg.h"
#include <TemplateParser/TemplatesConfiguration>
#include <TemplateParser/CustomTemplates>
#include "globalsettings_templateparser.h"

#include <PimCommonAkonadi/RecentAddresses>
#include <KLDAP/LdapClientSearch>
#include <PimCommonAkonadi/CompletionOrderEditor>
using PimCommon::RecentAddresses;

#include <KLocalizedString>
#include <KSeparator>
#include <KCharsets>
#include <QHBoxLayout>
#include <KMessageBox>
#include <QSpinBox>
#include <KPluralHandlingSpinBox>
#include "kmail_debug.h"

#include <QLabel>
#include <QGroupBox>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QTextCodec>
#include <QCheckBox>
#include <KConfigGroup>
#include <QRegularExpression>
#include <QRegularExpressionValidator>

#include <PimCommonAkonadi/CompletionConfigureDialog>
#ifdef KDEPIM_ENTERPRISE_BUILD
#include <QComboBox>
#endif
QString ComposerPage::helpAnchor() const
{
    return QStringLiteral("configure-composer");
}

ComposerPage::ComposerPage(QWidget *parent)
    : ConfigModuleWithTabs(parent)
{
    //
    // "General" tab:
    //
    GeneralTab *generalTab = new GeneralTab();
    addTab(generalTab, i18nc("General settings for the composer.", "General"));
    addConfig(KMailSettings::self(), generalTab);

    //
    // "Templates" tab:
    //
    TemplatesTab *templatesTab = new TemplatesTab();
    addTab(templatesTab, i18n("Standard Templates"));

    //
    // "Custom Templates" tab:
    //
    CustomTemplatesTab *customTemplatesTab = new CustomTemplatesTab();
    addTab(customTemplatesTab, i18n("Custom Templates"));

    //
    // "Subject" tab:
    //
    SubjectTab *subjectTab = new SubjectTab();
    addTab(subjectTab, i18nc("Settings regarding the subject when composing a message.", "Subject"));
    addConfig(KMailSettings::self(), subjectTab);

    //
    // "Charset" tab:
    //
    CharsetTab *charsetTab = new CharsetTab();
    addTab(charsetTab, i18n("Charset"));

    //
    // "Headers" tab:
    //
    HeadersTab *headersTab = new HeadersTab();
    addTab(headersTab, i18n("Headers"));

    //
    // "Attachments" tab:
    //
    AttachmentsTab *attachmentsTab = new AttachmentsTab();
    addTab(attachmentsTab, i18nc("Config->Composer->Attachments", "Attachments"));

    //
    // "autocorrection" tab:
    //
    AutoCorrectionTab *autoCorrectionTab = new AutoCorrectionTab();
    addTab(autoCorrectionTab, i18n("Autocorrection"));

    //
    // "autoresize" tab:
    //
    AutoImageResizeTab *autoImageResizeTab = new AutoImageResizeTab();
    addTab(autoImageResizeTab, i18n("Auto Resize Image"));
}

QString ComposerPage::GeneralTab::helpAnchor() const
{
    return QStringLiteral("configure-composer-general");
}

ComposerPageGeneralTab::ComposerPageGeneralTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    // Main layout
    QGridLayout *grid = new QGridLayout(this);

    // "Signature" group
    QGroupBox *groupBox = new QGroupBox(i18nc("@title:group", "Signature"));
    QVBoxLayout *groupVBoxLayout = new QVBoxLayout();

    // "Automatically insert signature" checkbox
    mAutoAppSignFileCheck = new QCheckBox(
        MessageComposer::MessageComposerSettings::self()->autoTextSignatureItem()->label(),
        this);

    QString helpText = i18n("Automatically insert the configured signature\n"
                            "when starting to compose a message");
    mAutoAppSignFileCheck->setToolTip(helpText);
    mAutoAppSignFileCheck->setWhatsThis(helpText);

    connect(mAutoAppSignFileCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupVBoxLayout->addWidget(mAutoAppSignFileCheck);

    // "Insert signature above quoted text" checkbox
    mTopQuoteCheck = new QCheckBox(
        MessageComposer::MessageComposerSettings::self()->prependSignatureItem()->label(), this);
    mTopQuoteCheck->setEnabled(false);

    helpText = i18n("Insert the signature above any quoted text");
    mTopQuoteCheck->setToolTip(helpText);
    mTopQuoteCheck->setWhatsThis(helpText);

    connect(mTopQuoteCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    connect(mAutoAppSignFileCheck, &QAbstractButton::toggled, mTopQuoteCheck, &QWidget::setEnabled);
    groupVBoxLayout->addWidget(mTopQuoteCheck);

    // "Prepend separator to signature" checkbox
    mDashDashCheck = new QCheckBox(
        MessageComposer::MessageComposerSettings::self()->dashDashSignatureItem()->label(), this);
    mDashDashCheck->setEnabled(false);

    helpText = i18n("Insert the RFC-compliant signature separator\n"
                    "(two dashes and a space on a line) before the signature");
    mDashDashCheck->setToolTip(helpText);
    mDashDashCheck->setWhatsThis(helpText);

    connect(mDashDashCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    connect(mAutoAppSignFileCheck, &QAbstractButton::toggled, mDashDashCheck, &QWidget::setEnabled);
    groupVBoxLayout->addWidget(mDashDashCheck);

    // "Remove signature when replying" checkbox
    mStripSignatureCheck = new QCheckBox(TemplateParser::TemplateParserSettings::self()->stripSignatureItem()->label(),
                                         this);

    helpText = i18n("When replying, do not quote any existing signature");
    mStripSignatureCheck->setToolTip(helpText);
    mStripSignatureCheck->setWhatsThis(helpText);

    connect(mStripSignatureCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupVBoxLayout->addWidget(mStripSignatureCheck);
    groupVBoxLayout->addStretch(1);

    groupBox->setLayout(groupVBoxLayout);
    grid->addWidget(groupBox, 0, 0);

    // "Format" group
    groupBox = new QGroupBox(i18nc("@title:group", "Format"));
    QGridLayout *groupGridLayout = new QGridLayout();
    int row = 0;

    // "Only quote selected text when replying" checkbox
    mQuoteSelectionOnlyCheck = new QCheckBox(MessageComposer::MessageComposerSettings::self()->quoteSelectionOnlyItem()->label(),
                                             this);
    helpText = i18n("When replying, only quote the selected text\n"
                    "(instead of the complete message), if\n"
                    "there is text selected in the message window.");
    mQuoteSelectionOnlyCheck->setToolTip(helpText);
    mQuoteSelectionOnlyCheck->setWhatsThis(helpText);

    connect(mQuoteSelectionOnlyCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupGridLayout->addWidget(mQuoteSelectionOnlyCheck, row, 0, 1, -1);
    ++row;

    // "Use smart quoting" checkbox
    mSmartQuoteCheck = new QCheckBox(
        TemplateParser::TemplateParserSettings::self()->smartQuoteItem()->label(), this);
    helpText = i18n("When replying, add quote signs in front of all lines of the quoted text,\n"
                    "even when the line was created by adding an additional line break while\n"
                    "word-wrapping the text.");
    mSmartQuoteCheck->setToolTip(helpText);
    mSmartQuoteCheck->setWhatsThis(helpText);

    connect(mSmartQuoteCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupGridLayout->addWidget(mSmartQuoteCheck, row, 0, 1, -1);
    ++row;

    // "Word wrap at column" checkbox/spinbox
    mWordWrapCheck = new QCheckBox(
        MessageComposer::MessageComposerSettings::self()->wordWrapItem()->label(), this);

    helpText = i18n("Enable automatic word wrapping at the specified width");
    mWordWrapCheck->setToolTip(helpText);
    mWordWrapCheck->setWhatsThis(helpText);

    mWrapColumnSpin = new QSpinBox(this);
    mWrapColumnSpin->setMaximum(78 /*max*/);
    mWrapColumnSpin->setMinimum(30 /*min*/);
    mWrapColumnSpin->setSingleStep(1 /*step*/);
    mWrapColumnSpin->setValue(78 /*init*/);
    mWrapColumnSpin->setEnabled(false);   // since !mWordWrapCheck->isChecked()

    helpText = i18n("Set the text width for automatic word wrapping");
    mWrapColumnSpin->setToolTip(helpText);
    mWrapColumnSpin->setWhatsThis(helpText);

    connect(mWordWrapCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    connect(mWrapColumnSpin, qOverload<int>(&QSpinBox::valueChanged), this, &ComposerPageGeneralTab::slotEmitChanged);
    // only enable the spinbox if the checkbox is checked
    connect(mWordWrapCheck, &QAbstractButton::toggled, mWrapColumnSpin, &QWidget::setEnabled);

    groupGridLayout->addWidget(mWordWrapCheck, row, 0);
    groupGridLayout->addWidget(mWrapColumnSpin, row, 1);
    ++row;

    // Spacing
    ++row;

    // "Reply/Forward using HTML if present" checkbox
    mReplyUsingVisualFormat = new QCheckBox(TemplateParser::TemplateParserSettings::self()->replyUsingVisualFormatItem()->label(), this);
    helpText = i18n("When replying or forwarding, quote the message\n"
                    "in the original format it was received.\n"
                    "If unchecked, the reply will be as plain text by default.");
    mReplyUsingVisualFormat->setToolTip(helpText);
    mReplyUsingVisualFormat->setWhatsThis(helpText);

    connect(mReplyUsingVisualFormat, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupGridLayout->addWidget(mReplyUsingVisualFormat, row, 0, 1, -1);
    ++row;

    // "Improve plain text of HTML" checkbox
    mImprovePlainTextOfHtmlMessage = new QCheckBox(MessageComposer::MessageComposerSettings::self()->improvePlainTextOfHtmlMessageItem()->label(), this);

    // For what is supported see http://www.grantlee.org/apidox/classGrantlee_1_1PlainTextMarkupBuilder.html
    helpText = i18n("Format the plain text part of a message from the HTML markup.\n"
                    "Bold, italic and underlined text, lists, and external references\n"
                    "are supported.");
    mImprovePlainTextOfHtmlMessage->setToolTip(helpText);
    mImprovePlainTextOfHtmlMessage->setWhatsThis(helpText);

    connect(mImprovePlainTextOfHtmlMessage, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupGridLayout->addWidget(mImprovePlainTextOfHtmlMessage, row, 0, 1, -1);
    ++row;
    QLabel *label = nullptr;
#ifdef KDEPIM_ENTERPRISE_BUILD
    ++row;
    // "Default forwarding type" combobox
    mForwardTypeCombo = new QComboBox(this);
    mForwardTypeCombo->addItems(QStringList() << i18nc("@item:inlistbox Inline mail forwarding",
                                                       "Inline")
                                              << i18n("As Attachment"));

    helpText = i18n("Set the default forwarded message format");
    mForwardTypeCombo->setToolTip(helpText);
    mForwardTypeCombo->setWhatsThis(helpText);

    label = new QLabel(i18n("Default forwarding type:"), this);
    label->setBuddy(mForwardTypeCombo);

    connect(mForwardTypeCombo, qOverload<int>(&QComboBox::activated), this, &ComposerPageGeneralTab::slotEmitChanged);

    groupGridLayout->addWidget(label, row, 0);
    groupGridLayout->addWidget(mForwardTypeCombo, row, 1);
#endif
    groupGridLayout->setRowStretch(row, 1);

    groupBox->setLayout(groupGridLayout);
    grid->addWidget(groupBox, 1, 0);

    // "Recipients" group
    groupBox = new QGroupBox(i18nc("@title:group", "Recipients"));
    groupGridLayout = new QGridLayout();
    row = 0;

    // "Automatically request MDNs" checkbox
    mAutoRequestMDNCheck = new QCheckBox(KMailSettings::self()->requestMDNItem()->label(),
                                         this);

    helpText = i18n("By default, request an MDN when starting to compose a message.\n"
                    "You can select this on a per-message basis using \"Options - Request Disposition Notification\"");
    mAutoRequestMDNCheck->setToolTip(helpText);
    mAutoRequestMDNCheck->setWhatsThis(helpText);

    connect(mAutoRequestMDNCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupGridLayout->addWidget(mAutoRequestMDNCheck, row, 0, 1, -1);
    ++row;

    // Spacing
    ++row;

    // "Use Baloo search in composer" checkbox
    mShowAkonadiSearchAddressesInComposer = new QCheckBox(
        MessageComposer::MessageComposerSettings::self()->showBalooSearchInComposerItem()->label(),
        this);

    connect(mShowAkonadiSearchAddressesInComposer, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupGridLayout->addWidget(mShowAkonadiSearchAddressesInComposer, row, 0, 1, -1);
    ++row;

#ifdef KDEPIM_ENTERPRISE_BUILD
    // "Warn if too many recipients" checkbox/spinbox
    mRecipientCheck = new QCheckBox(
        KMailSettings::self()->tooManyRecipientsItem()->label(), this);
    mRecipientCheck->setObjectName(QStringLiteral("kcfg_TooManyRecipients"));
    helpText = i18n(KMailSettings::self()->tooManyRecipientsItem()->whatsThis().toUtf8().constData());
    mRecipientCheck->setWhatsThis(helpText);
    mRecipientCheck->setToolTip(i18n("Warn if too many recipients are specified"));

    mRecipientSpin = new QSpinBox(this);
    mRecipientSpin->setMaximum(100 /*max*/);
    mRecipientSpin->setMinimum(1 /*min*/);
    mRecipientSpin->setSingleStep(1 /*step*/);
    mRecipientSpin->setValue(5 /*init*/);
    mRecipientSpin->setObjectName(QStringLiteral("kcfg_RecipientThreshold"));
    mRecipientSpin->setEnabled(false);
    helpText = i18n(KMailSettings::self()->recipientThresholdItem()->whatsThis().toUtf8().constData());
    mRecipientSpin->setWhatsThis(helpText);
    mRecipientSpin->setToolTip(i18n("Set the maximum number of recipients for the warning"));

    connect(mRecipientCheck, &QCheckBox::stateChanged, this, &ComposerPageGeneralTab::slotEmitChanged);
    connect(mRecipientSpin, qOverload<int>(&QSpinBox::valueChanged), this, &ComposerPageGeneralTab::slotEmitChanged);
    // only enable the spinbox if the checkbox is checked
    connect(mRecipientCheck, &QCheckBox::toggled, mRecipientSpin, &QSpinBox::setEnabled);

    groupGridLayout->addWidget(mRecipientCheck, row, 0, 1, 2);
    groupGridLayout->addWidget(mRecipientSpin, row, 2);
    ++row;
#endif

    // "Maximum Reply-to-All recipients" spinbox
    mMaximumRecipients = new QSpinBox(this);
    mMaximumRecipients->setMaximum(9999);
    mMaximumRecipients->setMinimum(0);
    mMaximumRecipients->setSingleStep(1);
    mMaximumRecipients->setValue(1);

    helpText = i18n("Only allow this many recipients to be specified for the message.\n"
                    "This applies to doing a \"Reply to All\", entering recipients manually\n"
                    "or using the \"Select...\" picker.  Setting this limit helps you to\n"
                    "avoid accidentally sending a message to too many people.  Note,\n"
                    "however, that it does not take account of distribution lists or\n"
                    "mailing lists.");
    mMaximumRecipients->setToolTip(helpText);
    mMaximumRecipients->setWhatsThis(helpText);

    label = new QLabel(MessageComposer::MessageComposerSettings::self()->maximumRecipientsItem()->label(), this);
    label->setBuddy(mMaximumRecipients);

    connect(mMaximumRecipients, qOverload<int>(&QSpinBox::valueChanged), this, &ConfigModuleTab::slotEmitChanged);

    groupGridLayout->addWidget(label, row, 0, 1, 2);
    groupGridLayout->addWidget(mMaximumRecipients, row, 2);
    ++row;

    // Spacing
    ++row;

    // "Use recent addresses for autocompletion" checkbox
    mShowRecentAddressesInComposer = new QCheckBox(
        MessageComposer::MessageComposerSettings::self()->showRecentAddressesInComposerItem()->label(),
        this);

    helpText = i18n("Remember recent addresses entered,\n"
                    "and offer them for recipient completion");
    mShowRecentAddressesInComposer->setToolTip(helpText);
    mShowRecentAddressesInComposer->setWhatsThis(helpText);

    connect(mShowRecentAddressesInComposer, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    groupGridLayout->addWidget(mShowRecentAddressesInComposer, row, 0, 1, -1);
    ++row;

    // "Maximum recent addresses retained" spinbox
    mMaximumRecentAddress = new QSpinBox(this);
    mMaximumRecentAddress->setMinimum(0);
    mMaximumRecentAddress->setMaximum(999);
    mMaximumRecentAddress->setSpecialValueText(i18nc("No addresses are retained", "No save"));
    mMaximumRecentAddress->setEnabled(false);

    label = new QLabel(i18n("Maximum recent addresses retained:"));
    label->setBuddy(mMaximumRecentAddress);
    label->setEnabled(false);

    helpText = i18n("The maximum number of recently entered addresses that will\n"
                    "be remembered for completion");
    mMaximumRecentAddress->setToolTip(helpText);
    mMaximumRecentAddress->setWhatsThis(helpText);

    connect(mMaximumRecentAddress, qOverload<int>(&QSpinBox::valueChanged), this, &ConfigModuleTab::slotEmitChanged);
    connect(mShowRecentAddressesInComposer, &QAbstractButton::toggled, mMaximumRecentAddress, &QWidget::setEnabled);
    connect(mShowRecentAddressesInComposer, &QAbstractButton::toggled, label, &QWidget::setEnabled);

    groupGridLayout->addWidget(label, row, 0, 1, 2);
    groupGridLayout->addWidget(mMaximumRecentAddress, row, 2);
    ++row;

    // Configure All Address settings
    QPushButton *configureCompletionButton = new QPushButton(i18n("Configure Completion..."), this);
    connect(configureCompletionButton, &QAbstractButton::clicked, this, &ComposerPageGeneralTab::slotConfigureAddressCompletion);
    groupGridLayout->addWidget(configureCompletionButton, row, 1, 1, 2);
    groupGridLayout->setRowStretch(row, 1);

    groupBox->setLayout(groupGridLayout);
    grid->addWidget(groupBox, 0, 1);

    groupBox = new QGroupBox(i18nc("@title:group", "Autosave"));
    groupGridLayout = new QGridLayout();
    row = 0;

    // "Autosave interval" spinbox
    mAutoSave = new KPluralHandlingSpinBox(this);
    mAutoSave->setMaximum(60);
    mAutoSave->setMinimum(0);
    mAutoSave->setSingleStep(1);
    mAutoSave->setValue(1);
    mAutoSave->setObjectName(QStringLiteral("kcfg_AutosaveInterval"));
    mAutoSave->setSpecialValueText(i18n("No autosave"));
    mAutoSave->setSuffix(ki18ncp("Interval suffix", " minute", " minutes"));

    helpText = i18n("Automatically save the message at this specified interval");
    mAutoSave->setToolTip(helpText);
    mAutoSave->setWhatsThis(helpText);

    label = new QLabel(KMailSettings::self()->autosaveIntervalItem()->label(), this);
    label->setBuddy(mAutoSave);

    connect(mAutoSave, qOverload<int>(&QSpinBox::valueChanged), this, &ConfigModuleTab::slotEmitChanged);

    groupGridLayout->addWidget(label, row, 0);
    groupGridLayout->addWidget(mAutoSave, row, 1);
    row++;
    groupGridLayout->setRowStretch(row, 1);
    groupBox->setLayout(groupGridLayout);

    grid->addWidget(groupBox, 1, 1);

    grid->setRowStretch(2, 1);
}

void ComposerPage::GeneralTab::doResetToDefaultsOther()
{
    const bool bUseDefaults = MessageComposer::MessageComposerSettings::self()->useDefaults(true);

    const bool autoAppSignFile = MessageComposer::MessageComposerSettings::self()->autoTextSignature() == QLatin1String("auto");
    const bool topQuoteCheck = MessageComposer::MessageComposerSettings::self()->prependSignature();
    const bool dashDashSignature = MessageComposer::MessageComposerSettings::self()->dashDashSignature();
    const bool smartQuoteCheck = MessageComposer::MessageComposerSettings::self()->quoteSelectionOnly();
    const bool wordWrap = MessageComposer::MessageComposerSettings::self()->wordWrap();
    const int wrapColumn = MessageComposer::MessageComposerSettings::self()->lineWrapWidth();
    const bool showRecentAddress = MessageComposer::MessageComposerSettings::self()->showRecentAddressesInComposer();
    const int maximumRecipient = MessageComposer::MessageComposerSettings::self()->maximumRecipients();
    const bool improvePlainText = MessageComposer::MessageComposerSettings::self()->improvePlainTextOfHtmlMessage();
    const bool showBalooSearchInComposer = MessageComposer::MessageComposerSettings::self()->showBalooSearchInComposer();
    MessageComposer::MessageComposerSettings::self()->useDefaults(bUseDefaults);

    mAutoAppSignFileCheck->setChecked(autoAppSignFile);
    mTopQuoteCheck->setChecked(topQuoteCheck);
    mDashDashCheck->setChecked(dashDashSignature);
    mQuoteSelectionOnlyCheck->setChecked(smartQuoteCheck);
    mWordWrapCheck->setChecked(wordWrap);
    mWrapColumnSpin->setValue(wrapColumn);
    mMaximumRecipients->setValue(maximumRecipient);
    mShowRecentAddressesInComposer->setChecked(showRecentAddress);
    mShowAkonadiSearchAddressesInComposer->setChecked(showBalooSearchInComposer);
    mImprovePlainTextOfHtmlMessage->setChecked(improvePlainText);

    mMaximumRecentAddress->setValue(200);
}

void ComposerPage::GeneralTab::doLoadFromGlobalSettings()
{
    // various check boxes:

    mAutoAppSignFileCheck->setChecked(
        MessageComposer::MessageComposerSettings::self()->autoTextSignature() == QLatin1String("auto"));
    loadWidget(mTopQuoteCheck, MessageComposer::MessageComposerSettings::self()->prependSignatureItem());
    loadWidget(mDashDashCheck, MessageComposer::MessageComposerSettings::self()->dashDashSignatureItem());
    loadWidget(mSmartQuoteCheck, TemplateParser::TemplateParserSettings::self()->smartQuoteItem());
    loadWidget(mQuoteSelectionOnlyCheck, MessageComposer::MessageComposerSettings::self()->quoteSelectionOnlyItem());

    loadWidget(mReplyUsingVisualFormat, TemplateParser::TemplateParserSettings::self()->replyUsingVisualFormatItem());
    loadWidget(mStripSignatureCheck, TemplateParser::TemplateParserSettings::self()->stripSignatureItem());
    loadWidget(mAutoRequestMDNCheck, KMailSettings::self()->requestMDNItem());
    loadWidget(mWordWrapCheck, MessageComposer::MessageComposerSettings::self()->wordWrapItem());

    loadWidget(mWrapColumnSpin, MessageComposer::MessageComposerSettings::self()->lineWrapWidthItem());
    loadWidget(mMaximumRecipients, MessageComposer::MessageComposerSettings::self()->maximumRecipientsItem());
    mAutoSave->setValue(KMailSettings::self()->autosaveInterval());
    loadWidget(mShowRecentAddressesInComposer, MessageComposer::MessageComposerSettings::self()->showRecentAddressesInComposerItem());
    loadWidget(mShowAkonadiSearchAddressesInComposer, MessageComposer::MessageComposerSettings::self()->showBalooSearchInComposerItem());
    mImprovePlainTextOfHtmlMessage->setChecked(MessageComposer::MessageComposerSettings::self()->improvePlainTextOfHtmlMessage());

#ifdef KDEPIM_ENTERPRISE_BUILD
    mRecipientCheck->setChecked(KMailSettings::self()->tooManyRecipients());
    mRecipientSpin->setValue(KMailSettings::self()->recipientThreshold());
    if (KMailSettings::self()->forwardingInlineByDefault()) {
        mForwardTypeCombo->setCurrentIndex(0);
    } else {
        mForwardTypeCombo->setCurrentIndex(1);
    }
#endif

    mMaximumRecentAddress->setValue(RecentAddresses::self(MessageComposer::MessageComposerSettings::self()->config())->maxCount());
}

void ComposerPage::GeneralTab::save()
{
    saveCheckBox(mTopQuoteCheck, MessageComposer::MessageComposerSettings::self()->prependSignatureItem());
    saveCheckBox(mDashDashCheck, MessageComposer::MessageComposerSettings::self()->dashDashSignatureItem());
    saveCheckBox(mSmartQuoteCheck, TemplateParser::TemplateParserSettings::self()->smartQuoteItem());
    saveCheckBox(mQuoteSelectionOnlyCheck, MessageComposer::MessageComposerSettings::self()->quoteSelectionOnlyItem());

    saveCheckBox(mReplyUsingVisualFormat, TemplateParser::TemplateParserSettings::self()->replyUsingVisualFormatItem());
    saveCheckBox(mStripSignatureCheck, TemplateParser::TemplateParserSettings::self()->stripSignatureItem());
    saveCheckBox(mAutoRequestMDNCheck, KMailSettings::self()->requestMDNItem());
    saveCheckBox(mWordWrapCheck, MessageComposer::MessageComposerSettings::self()->wordWrapItem());

    MessageComposer::MessageComposerSettings::self()->setAutoTextSignature(
        mAutoAppSignFileCheck->isChecked() ? QStringLiteral("auto") : QStringLiteral("manual"));
    saveSpinBox(mWrapColumnSpin, MessageComposer::MessageComposerSettings::self()->lineWrapWidthItem());
    saveSpinBox(mMaximumRecipients, MessageComposer::MessageComposerSettings::self()->maximumRecipientsItem());
    KMailSettings::self()->setAutosaveInterval(mAutoSave->value());
    MessageComposer::MessageComposerSettings::self()->setShowRecentAddressesInComposer(mShowRecentAddressesInComposer->isChecked());
    MessageComposer::MessageComposerSettings::self()->setShowBalooSearchInComposer(mShowAkonadiSearchAddressesInComposer->isChecked());
    MessageComposer::MessageComposerSettings::self()->setImprovePlainTextOfHtmlMessage(mImprovePlainTextOfHtmlMessage->isChecked());
#ifdef KDEPIM_ENTERPRISE_BUILD
    KMailSettings::self()->setTooManyRecipients(mRecipientCheck->isChecked());
    KMailSettings::self()->setRecipientThreshold(mRecipientSpin->value());
    KMailSettings::self()->setForwardingInlineByDefault(mForwardTypeCombo->currentIndex() == 0);
#endif

    RecentAddresses::self(MessageComposer::MessageComposerSettings::self()->config())->setMaxCount(mMaximumRecentAddress->value());

    MessageComposer::MessageComposerSettings::self()->requestSync();
}

void ComposerPage::GeneralTab::slotConfigureAddressCompletion()
{
    KLDAP::LdapClientSearch search;
    QPointer<PimCommon::CompletionConfigureDialog> dlg(new PimCommon::CompletionConfigureDialog(this));
    dlg->setRecentAddresses(PimCommon::RecentAddresses::self(MessageComposer::MessageComposerSettings::self()->config())->addresses());
    dlg->setLdapClientSearch(&search);
    KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist"));
    KConfigGroup group(config, "AddressLineEdit");
    const QStringList balooBlackList = group.readEntry("BalooBackList", QStringList());

    dlg->setEmailBlackList(balooBlackList);
    dlg->load();
    if (dlg->exec()) {
        if (dlg->recentAddressWasChanged()) {
            PimCommon::RecentAddresses::self(MessageComposer::MessageComposerSettings::self()->config())->clear();
            dlg->storeAddresses(MessageComposer::MessageComposerSettings::self()->config());
        }
    }
    delete dlg;
}

QString ComposerPage::TemplatesTab::helpAnchor() const
{
    return QStringLiteral("configure-composer-templates");
}

ComposerPageTemplatesTab::ComposerPageTemplatesTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    QVBoxLayout *vlay = new QVBoxLayout(this);

    mWidget = new TemplateParser::TemplatesConfiguration(this);
    vlay->addWidget(mWidget);

    connect(mWidget, &TemplateParser::TemplatesConfiguration::changed, this, &ConfigModuleTab::slotEmitChanged);
}

void ComposerPage::TemplatesTab::doLoadFromGlobalSettings()
{
    mWidget->loadFromGlobal();
}

void ComposerPage::TemplatesTab::save()
{
    mWidget->saveToGlobal();
}

void ComposerPage::TemplatesTab::doResetToDefaultsOther()
{
    mWidget->resetToDefault();
}

QString ComposerPage::CustomTemplatesTab::helpAnchor() const
{
    return QStringLiteral("configure-composer-custom-templates");
}

ComposerPageCustomTemplatesTab::ComposerPageCustomTemplatesTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    QVBoxLayout *vlay = new QVBoxLayout(this);

    mWidget = new TemplateParser::CustomTemplates(kmkernel->getKMMainWidget() ? kmkernel->getKMMainWidget()->actionCollections() : QList<KActionCollection *>(), this);
    vlay->addWidget(mWidget);

    connect(mWidget, &TemplateParser::CustomTemplates::changed, this, &ConfigModuleTab::slotEmitChanged);
    if (KMKernel::self()) {
        connect(mWidget, &TemplateParser::CustomTemplates::templatesUpdated, KMKernel::self(), &KMKernel::updatedTemplates);
    }
}

void ComposerPage::CustomTemplatesTab::doLoadFromGlobalSettings()
{
    mWidget->load();
}

void ComposerPage::CustomTemplatesTab::save()
{
    mWidget->save();
}

QString ComposerPage::SubjectTab::helpAnchor() const
{
    return QStringLiteral("configure-composer-subject");
}

ComposerPageSubjectTab::ComposerPageSubjectTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    QVBoxLayout *vlay = new QVBoxLayout(this);

    QGroupBox *group = new QGroupBox(i18n("Repl&y Subject Prefixes"), this);
    QLayout *layout = new QVBoxLayout(group);

    // row 0: help text:
    QLabel *label = new QLabel(i18n("Recognize any sequence of the following prefixes\n"
                                    "(entries are case-insensitive regular expressions):"), group);
    label->setWordWrap(true);
    label->setAlignment(Qt::AlignLeft);

    // row 1, string list editor:
    PimCommon::SimpleStringListEditor::ButtonCode buttonCode
        = static_cast<PimCommon::SimpleStringListEditor::ButtonCode>(PimCommon::SimpleStringListEditor::Add | PimCommon::SimpleStringListEditor::Remove | PimCommon::SimpleStringListEditor::Modify);
    mReplyListEditor
        = new PimCommon::SimpleStringListEditor(group, buttonCode,
                                                i18n("A&dd..."), i18n("Re&move"),
                                                i18n("Mod&ify..."),
                                                i18n("Enter new reply prefix:"));
    mReplyListEditor->setRemoveDialogLabel(i18n("Do you want to remove reply prefix?"));
    connect(mReplyListEditor, &PimCommon::SimpleStringListEditor::changed, this, &ConfigModuleTab::slotEmitChanged);

    // row 2: "replace [...]" check box:
    mReplaceReplyPrefixCheck = new QCheckBox(
        MessageCore::MessageCoreSettings::self()->replaceReplyPrefixItem()->label(),
        group);
    connect(mReplaceReplyPrefixCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    layout->addWidget(label);
    layout->addWidget(mReplyListEditor);
    layout->addWidget(mReplaceReplyPrefixCheck);

    vlay->addWidget(group);

    group = new QGroupBox(i18n("For&ward Subject Prefixes"), this);
    layout = new QVBoxLayout(group);

    // row 0: help text:
    label = new QLabel(i18n("Recognize any sequence of the following prefixes\n"
                            "(entries are case-insensitive regular expressions):"), group);
    label->setAlignment(Qt::AlignLeft);
    label->setWordWrap(true);

    // row 1: string list editor
    mForwardListEditor
        = new PimCommon::SimpleStringListEditor(group, buttonCode,
                                                i18n("Add..."),
                                                i18n("Remo&ve"),
                                                i18n("Modify..."),
                                                i18n("Enter new forward prefix:"));
    mForwardListEditor->setRemoveDialogLabel(i18n("Do you want to remove forward prefix?"));
    connect(mForwardListEditor, &PimCommon::SimpleStringListEditor::changed, this, &ConfigModuleTab::slotEmitChanged);

    // row 3: "replace [...]" check box:
    mReplaceForwardPrefixCheck = new QCheckBox(
        MessageCore::MessageCoreSettings::self()->replaceForwardPrefixItem()->label(),
        group);
    connect(mReplaceForwardPrefixCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    layout->addWidget(label);
    layout->addWidget(mForwardListEditor);
    layout->addWidget(mReplaceForwardPrefixCheck);
    vlay->addWidget(group);
}

void ComposerPage::SubjectTab::doLoadFromGlobalSettings()
{
    loadWidget(mReplyListEditor, MessageCore::MessageCoreSettings::self()->replyPrefixesItem());
    loadWidget(mForwardListEditor, MessageCore::MessageCoreSettings::self()->forwardPrefixesItem());
    loadWidget(mReplaceForwardPrefixCheck, MessageCore::MessageCoreSettings::self()->replaceForwardPrefixItem());
    loadWidget(mReplaceReplyPrefixCheck, MessageCore::MessageCoreSettings::self()->replaceReplyPrefixItem());
}

void ComposerPage::SubjectTab::save()
{
    saveSimpleStringListEditor(mReplyListEditor, MessageCore::MessageCoreSettings::self()->replyPrefixesItem());
    saveSimpleStringListEditor(mForwardListEditor, MessageCore::MessageCoreSettings::self()->forwardPrefixesItem());
    saveCheckBox(mReplaceForwardPrefixCheck, MessageCore::MessageCoreSettings::self()->replaceForwardPrefixItem());
    saveCheckBox(mReplaceReplyPrefixCheck, MessageCore::MessageCoreSettings::self()->replaceReplyPrefixItem());
}

void ComposerPage::SubjectTab::doResetToDefaultsOther()
{
    const bool bUseDefaults = MessageComposer::MessageComposerSettings::self()->useDefaults(true);
    loadWidget(mReplyListEditor, MessageCore::MessageCoreSettings::self()->replyPrefixesItem());
    loadWidget(mForwardListEditor, MessageCore::MessageCoreSettings::self()->forwardPrefixesItem());
    loadWidget(mReplaceForwardPrefixCheck, MessageCore::MessageCoreSettings::self()->replaceForwardPrefixItem());
    loadWidget(mReplaceReplyPrefixCheck, MessageCore::MessageCoreSettings::self()->replaceReplyPrefixItem());
    MessageComposer::MessageComposerSettings::self()->useDefaults(bUseDefaults);
}

QString ComposerPage::CharsetTab::helpAnchor() const
{
    return QStringLiteral("configure-composer-charset");
}

ComposerPageCharsetTab::ComposerPageCharsetTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    QVBoxLayout *vlay = new QVBoxLayout(this);

    QLabel *label = new QLabel(i18n("This list is checked for every outgoing message "
                                    "from the top to the bottom for a charset that "
                                    "contains all required characters."), this);
    label->setWordWrap(true);
    vlay->addWidget(label);

    mCharsetListEditor
        = new PimCommon::SimpleStringListEditor(this, PimCommon::SimpleStringListEditor::All,
                                                i18n("A&dd..."), i18n("Remo&ve"),
                                                i18n("&Modify..."), i18n("Enter charset:"));
    mCharsetListEditor->setRemoveDialogLabel(i18n("Do you want to remove this selected charset?"));
    mCharsetListEditor->setUpDownAutoRepeat(true);
    connect(mCharsetListEditor, &PimCommon::SimpleStringListEditor::changed, this, &ConfigModuleTab::slotEmitChanged);

    vlay->addWidget(mCharsetListEditor, 1);

    mKeepReplyCharsetCheck = new QCheckBox(i18n("&Keep original charset when "
                                                "replying or forwarding (if "
                                                "possible)"), this);
    connect(mKeepReplyCharsetCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    vlay->addWidget(mKeepReplyCharsetCheck);

    connect(mCharsetListEditor, &PimCommon::SimpleStringListEditor::aboutToAdd, this, &ComposerPageCharsetTab::slotVerifyCharset);
    setEnabled(kmkernel);
}

void ComposerPage::CharsetTab::slotVerifyCharset(QString &charset)
{
    if (charset.isEmpty()) {
        return;
    }

    // KCharsets::codecForName("us-ascii") returns "iso-8859-1" (cf. Bug #49812)
    // therefore we have to treat this case specially
    const QString charsetLower = charset.toLower();
    if (charsetLower == QLatin1String("us-ascii")) {
        charset = QStringLiteral("us-ascii");
        return;
    } else if (charsetLower == QLatin1String("locale")) {
        charset = QStringLiteral("%1 (locale)")
                  .arg(QString::fromLatin1(kmkernel->networkCodec()->name()).toLower());
        return;
    }

    bool ok = false;
    QTextCodec *codec = KCharsets::charsets()->codecForName(charset, ok);
    if (ok && codec) {
        charset = QString::fromLatin1(codec->name()).toLower();
        return;
    }

    KMessageBox::sorry(this, i18n("This charset is not supported."));
    charset.clear();
}

void ComposerPage::CharsetTab::doLoadOther()
{
    if (!kmkernel) {
        return;
    }
    QStringList charsets = MessageComposer::MessageComposerSettings::preferredCharsets();
    QStringList::Iterator end(charsets.end());
    for (QStringList::Iterator it = charsets.begin();
         it != end; ++it) {
        if ((*it) == QLatin1String("locale")) {
            QByteArray cset = kmkernel->networkCodec()->name();
            cset = cset.toLower();
            (*it) = QStringLiteral("%1 (locale)").arg(QString::fromLatin1(cset));
        }
    }

    mCharsetListEditor->setStringList(charsets);
    loadWidget(mKeepReplyCharsetCheck, MessageComposer::MessageComposerSettings::self()->forceReplyCharsetItem());
}

void ComposerPage::CharsetTab::doResetToDefaultsOther()
{
    const bool bUseDefaults = MessageComposer::MessageComposerSettings::self()->useDefaults(true);
    mCharsetListEditor->setStringList(MessageComposer::MessageComposerSettings::preferredCharsets());
    mKeepReplyCharsetCheck->setChecked(MessageComposer::MessageComposerSettings::forceReplyCharset());
    saveCheckBox(mKeepReplyCharsetCheck, MessageComposer::MessageComposerSettings::self()->forceReplyCharsetItem());

    MessageComposer::MessageComposerSettings::self()->useDefaults(bUseDefaults);
    slotEmitChanged();
}

void ComposerPage::CharsetTab::save()
{
    if (!kmkernel) {
        return;
    }
    QStringList charsetList = mCharsetListEditor->stringList();
    QStringList::Iterator it = charsetList.begin();
    QStringList::Iterator end = charsetList.end();

    for (; it != end; ++it) {
        if ((*it).endsWith(QLatin1String("(locale)"))) {
            (*it) = QStringLiteral("locale");
        }
    }
    MessageComposer::MessageComposerSettings::setPreferredCharsets(charsetList);
    saveCheckBox(mKeepReplyCharsetCheck, MessageComposer::MessageComposerSettings::self()->forceReplyCharsetItem());
}

QString ComposerPage::HeadersTab::helpAnchor() const
{
    return QStringLiteral("configure-composer-headers");
}

ComposerPageHeadersTab::ComposerPageHeadersTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    QVBoxLayout *vlay = new QVBoxLayout(this);

    // "Use custom Message-Id suffix" checkbox:
    mCreateOwnMessageIdCheck
        = new QCheckBox(i18n("&Use custom message-id suffix"), this);
    connect(mCreateOwnMessageIdCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    vlay->addWidget(mCreateOwnMessageIdCheck);

    // "Message-Id suffix" line edit and label:
    QHBoxLayout *hlay = new QHBoxLayout(); // inherits spacing
    vlay->addLayout(hlay);
    mMessageIdSuffixEdit = new QLineEdit(this);
    mMessageIdSuffixEdit->setClearButtonEnabled(true);
    // only ASCII letters, digits, plus, minus and dots are allowed
    QRegularExpressionValidator *messageIdSuffixValidator
        = new QRegularExpressionValidator(QRegularExpression(QStringLiteral("[a-zA-Z0-9+-]+(?:\\.[a-zA-Z0-9+-]+)*")), this);
    mMessageIdSuffixEdit->setValidator(messageIdSuffixValidator);
    QLabel *label = new QLabel(i18n("Custom message-&id suffix:"), this);
    label->setBuddy(mMessageIdSuffixEdit);
    label->setEnabled(false);   // since !mCreateOwnMessageIdCheck->isChecked()
    mMessageIdSuffixEdit->setEnabled(false);
    hlay->addWidget(label);
    hlay->addWidget(mMessageIdSuffixEdit, 1);
    connect(mCreateOwnMessageIdCheck, &QAbstractButton::toggled, label, &QWidget::setEnabled);
    connect(mCreateOwnMessageIdCheck, &QAbstractButton::toggled, mMessageIdSuffixEdit, &QWidget::setEnabled);
    connect(mMessageIdSuffixEdit, &QLineEdit::textChanged, this, &ConfigModuleTab::slotEmitChanged);

    // horizontal rule and "custom header fields" label:
    vlay->addWidget(new KSeparator(Qt::Horizontal, this));
    vlay->addWidget(new QLabel(i18n("Define custom mime header fields:"), this));

    // "custom header fields" listbox:
    QGridLayout *glay = new QGridLayout(); // inherits spacing
    vlay->addLayout(glay);
    glay->setRowStretch(2, 1);
    glay->setColumnStretch(1, 1);
    mHeaderList = new ListView(this);
    mHeaderList->setHeaderLabels(QStringList() << i18nc("@title:column Name of the mime header.", "Name")
                                               << i18nc("@title:column Value of the mimeheader.", "Value"));
    mHeaderList->setSortingEnabled(false);
    connect(mHeaderList, &QTreeWidget::currentItemChanged, this, &ComposerPageHeadersTab::slotMimeHeaderSelectionChanged);
    connect(mHeaderList, &ListView::addHeader, this, &ComposerPageHeadersTab::slotNewMimeHeader);
    connect(mHeaderList, &ListView::removeHeader, this, &ComposerPageHeadersTab::slotRemoveMimeHeader);
    glay->addWidget(mHeaderList, 0, 0, 3, 2);

    // "new" and "remove" buttons:
    QPushButton *button = new QPushButton(i18nc("@action:button Add new mime header field.", "Ne&w"), this);
    connect(button, &QAbstractButton::clicked, this, &ComposerPageHeadersTab::slotNewMimeHeader);
    button->setAutoDefault(false);
    glay->addWidget(button, 0, 2);
    mRemoveHeaderButton = new QPushButton(i18n("Re&move"), this);
    connect(mRemoveHeaderButton, &QAbstractButton::clicked, this, &ComposerPageHeadersTab::slotRemoveMimeHeader);
    button->setAutoDefault(false);
    glay->addWidget(mRemoveHeaderButton, 1, 2);

    // "name" and "value" line edits and labels:
    mTagNameEdit = new QLineEdit(this);
    mTagNameEdit->setClearButtonEnabled(true);
    mTagNameEdit->setEnabled(false);
    mTagNameLabel = new QLabel(i18nc("@label:textbox Name of the mime header.", "&Name:"), this);
    mTagNameLabel->setBuddy(mTagNameEdit);
    mTagNameLabel->setEnabled(false);
    glay->addWidget(mTagNameLabel, 3, 0);
    glay->addWidget(mTagNameEdit, 3, 1);
    connect(mTagNameEdit, &QLineEdit::textChanged, this, &ComposerPageHeadersTab::slotMimeHeaderNameChanged);

    mTagValueEdit = new QLineEdit(this);
    mTagValueEdit->setClearButtonEnabled(true);
    mTagValueEdit->setEnabled(false);
    mTagValueLabel = new QLabel(i18n("&Value:"), this);
    mTagValueLabel->setBuddy(mTagValueEdit);
    mTagValueLabel->setEnabled(false);
    glay->addWidget(mTagValueLabel, 4, 0);
    glay->addWidget(mTagValueEdit, 4, 1);
    connect(mTagValueEdit, &QLineEdit::textChanged, this, &ComposerPageHeadersTab::slotMimeHeaderValueChanged);
}

void ComposerPage::HeadersTab::slotMimeHeaderSelectionChanged()
{
    mEmitChanges = false;
    QTreeWidgetItem *item = mHeaderList->currentItem();

    if (item) {
        mTagNameEdit->setText(item->text(0));
        mTagValueEdit->setText(item->text(1));
    } else {
        mTagNameEdit->clear();
        mTagValueEdit->clear();
    }
    mRemoveHeaderButton->setEnabled(item);
    mTagNameEdit->setEnabled(item);
    mTagValueEdit->setEnabled(item);
    mTagNameLabel->setEnabled(item);
    mTagValueLabel->setEnabled(item);
    mEmitChanges = true;
}

void ComposerPage::HeadersTab::slotMimeHeaderNameChanged(const QString &text)
{
    // is called on ::setup(), when clearing the line edits. So be
    // prepared to not find a selection:
    QTreeWidgetItem *item = mHeaderList->currentItem();
    if (item) {
        item->setText(0, text);
    }
    slotEmitChanged();
}

void ComposerPage::HeadersTab::slotMimeHeaderValueChanged(const QString &text)
{
    // is called on ::setup(), when clearing the line edits. So be
    // prepared to not find a selection:
    QTreeWidgetItem *item = mHeaderList->currentItem();
    if (item) {
        item->setText(1, text);
    }
    slotEmitChanged();
}

void ComposerPage::HeadersTab::slotNewMimeHeader()
{
    QTreeWidgetItem *listItem = new QTreeWidgetItem(mHeaderList);
    mHeaderList->setCurrentItem(listItem);
    slotEmitChanged();
}

void ComposerPage::HeadersTab::slotRemoveMimeHeader()
{
    // calling this w/o selection is a programming error:
    QTreeWidgetItem *item = mHeaderList->currentItem();
    if (!item) {
        qCDebug(KMAIL_LOG) << "=================================================="
                           << "Error: Remove button was pressed although no custom header was selected\n"
                           << "==================================================\n";
        return;
    }

    QTreeWidgetItem *below = mHeaderList->itemBelow(item);

    if (below) {
        qCDebug(KMAIL_LOG) << "below";
        mHeaderList->setCurrentItem(below);
        delete item;
        item = nullptr;
    } else if (mHeaderList->topLevelItemCount() > 0) {
        delete item;
        item = nullptr;
        mHeaderList->setCurrentItem(
            mHeaderList->topLevelItem(mHeaderList->topLevelItemCount() - 1)
            );
    }

    slotEmitChanged();
}

void ComposerPage::HeadersTab::doLoadOther()
{
    mMessageIdSuffixEdit->setText(MessageComposer::MessageComposerSettings::customMsgIDSuffix());
    const bool state = (!MessageComposer::MessageComposerSettings::customMsgIDSuffix().isEmpty()
                        && MessageComposer::MessageComposerSettings::useCustomMessageIdSuffix());
    mCreateOwnMessageIdCheck->setChecked(state);

    mHeaderList->clear();
    mTagNameEdit->clear();
    mTagValueEdit->clear();

    QTreeWidgetItem *item = nullptr;

    const int count = KMailSettings::self()->customMessageHeadersCount();
    for (int i = 0; i < count; ++i) {
        KConfigGroup config(KMKernel::self()->config(),
                            QLatin1String("Mime #") + QString::number(i));
        const QString name = config.readEntry("name");
        const QString value = config.readEntry("value");
        if (!name.isEmpty()) {
            item = new QTreeWidgetItem(mHeaderList, item);
            item->setText(0, name);
            item->setText(1, value);
        }
    }
    if (mHeaderList->topLevelItemCount() > 0) {
        mHeaderList->setCurrentItem(mHeaderList->topLevelItem(0));
    } else {
        // disable the "Remove" button
        mRemoveHeaderButton->setEnabled(false);
    }
}

void ComposerPage::HeadersTab::save()
{
    MessageComposer::MessageComposerSettings::self()->setCustomMsgIDSuffix(mMessageIdSuffixEdit->text());
    MessageComposer::MessageComposerSettings::self()->setUseCustomMessageIdSuffix(mCreateOwnMessageIdCheck->isChecked());

    //Clean config
    const int oldHeadersCount = KMailSettings::self()->customMessageHeadersCount();
    for (int i = 0; i < oldHeadersCount; ++i) {
        const QString groupMimeName = QStringLiteral("Mime #%1").arg(i);
        if (KMKernel::self()->config()->hasGroup(groupMimeName)) {
            KConfigGroup config(KMKernel::self()->config(), groupMimeName);
            config.deleteGroup();
        }
    }

    int numValidEntries = 0;
    QTreeWidgetItem *item = nullptr;
    const int numberOfEntry = mHeaderList->topLevelItemCount();
    for (int i = 0; i < numberOfEntry; ++i) {
        item = mHeaderList->topLevelItem(i);
        const QString str = item->text(0).trimmed();
        if (!str.isEmpty()) {
            if (str == QLatin1String("Content-Type")) {
                KMessageBox::error(this, i18n("\'Content-Type\' is not an authorized string. This header will be not saved."), i18n("Invalid header"));
                continue;
            }
            KConfigGroup config(KMKernel::self()->config(), QStringLiteral("Mime #%1").arg(numValidEntries));
            config.writeEntry("name", str);
            config.writeEntry("value", item->text(1));
            numValidEntries++;
        }
    }
    KMailSettings::self()->setCustomMessageHeadersCount(numValidEntries);
}

void ComposerPage::HeadersTab::doResetToDefaultsOther()
{
    const bool bUseDefaults = MessageComposer::MessageComposerSettings::self()->useDefaults(true);
    const QString messageIdSuffix = MessageComposer::MessageComposerSettings::customMsgIDSuffix();
    const bool useCustomMessageIdSuffix = MessageComposer::MessageComposerSettings::useCustomMessageIdSuffix();
    MessageComposer::MessageComposerSettings::self()->useDefaults(bUseDefaults);

    mMessageIdSuffixEdit->setText(messageIdSuffix);
    const bool state = (!messageIdSuffix.isEmpty() && useCustomMessageIdSuffix);
    mCreateOwnMessageIdCheck->setChecked(state);

    mHeaderList->clear();
    mTagNameEdit->clear();
    mTagValueEdit->clear();
    // disable the "Remove" button
    mRemoveHeaderButton->setEnabled(false);
}

QString ComposerPage::AttachmentsTab::helpAnchor() const
{
    return QStringLiteral("configure-composer-attachments");
}

ComposerPageAttachmentsTab::ComposerPageAttachmentsTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    QVBoxLayout *vlay = new QVBoxLayout(this);

    // "Outlook compatible attachment naming" check box
    mOutlookCompatibleCheck
        = new QCheckBox(i18n("Outlook-compatible attachment naming"), this);
    mOutlookCompatibleCheck->setChecked(false);
    mOutlookCompatibleCheck->setToolTip(i18n(
                                            "Turn this option on to make Outlook(tm) understand attachment names "
                                            "containing non-English characters"));
    connect(mOutlookCompatibleCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    connect(mOutlookCompatibleCheck, &QAbstractButton::clicked, this, &ComposerPageAttachmentsTab::slotOutlookCompatibleClicked);
    vlay->addWidget(mOutlookCompatibleCheck);
    vlay->addSpacing(5);

    // "Enable detection of missing attachments" check box
    mMissingAttachmentDetectionCheck
        = new QCheckBox(i18n("E&nable detection of missing attachments"), this);
    mMissingAttachmentDetectionCheck->setChecked(true);
    connect(mMissingAttachmentDetectionCheck, &QCheckBox::stateChanged, this, &ConfigModuleTab::slotEmitChanged);
    vlay->addWidget(mMissingAttachmentDetectionCheck);

    // "Attachment key words" label and string list editor
    QLabel *label = new QLabel(i18n("Recognize any of the following key words as "
                                    "intention to attach a file:"), this);
    label->setAlignment(Qt::AlignLeft);
    label->setWordWrap(true);

    vlay->addWidget(label);

    PimCommon::SimpleStringListEditor::ButtonCode buttonCode
        = static_cast<PimCommon::SimpleStringListEditor::ButtonCode>(PimCommon::SimpleStringListEditor::Add | PimCommon::SimpleStringListEditor::Remove | PimCommon::SimpleStringListEditor::Modify);
    mAttachWordsListEditor
        = new PimCommon::SimpleStringListEditor(this, buttonCode,
                                                i18n("A&dd..."), i18n("Re&move"),
                                                i18n("Mod&ify..."),
                                                i18n("Enter new key word:"));
    mAttachWordsListEditor->setRemoveDialogLabel(i18n("Do you want to remove this attachment word?"));
    connect(mAttachWordsListEditor, &PimCommon::SimpleStringListEditor::changed, this, &ConfigModuleTab::slotEmitChanged);
    vlay->addWidget(mAttachWordsListEditor);

    connect(mMissingAttachmentDetectionCheck, &QAbstractButton::toggled, label, &QWidget::setEnabled);
    connect(mMissingAttachmentDetectionCheck, &QAbstractButton::toggled, mAttachWordsListEditor, &QWidget::setEnabled);

    QHBoxLayout *layAttachment = new QHBoxLayout;
    label = new QLabel(i18n("Maximum Attachment Size:"), this);
    label->setAlignment(Qt::AlignLeft);
    layAttachment->addWidget(label);

    mMaximumAttachmentSize = new QSpinBox(this);
    mMaximumAttachmentSize->setRange(-1, 99999);
    mMaximumAttachmentSize->setSingleStep(100);
    mMaximumAttachmentSize->setSuffix(i18nc("spinbox suffix: unit for kilobyte", " kB"));
    connect(mMaximumAttachmentSize, qOverload<int>(&QSpinBox::valueChanged), this, &ConfigModuleTab::slotEmitChanged);
    mMaximumAttachmentSize->setSpecialValueText(i18n("No limit"));
    layAttachment->addWidget(mMaximumAttachmentSize);
    vlay->addLayout(layAttachment);
}

void ComposerPage::AttachmentsTab::doLoadFromGlobalSettings()
{
    loadWidget(mOutlookCompatibleCheck, MessageComposer::MessageComposerSettings::self()->outlookCompatibleAttachmentsItem());
    loadWidget(mMissingAttachmentDetectionCheck, KMailSettings::self()->showForgottenAttachmentWarningItem());
    loadWidget(mAttachWordsListEditor, KMailSettings::self()->attachmentKeywordsItem());
    const int maximumAttachmentSize(MessageCore::MessageCoreSettings::self()->maximumAttachmentSize());
    mMaximumAttachmentSize->setValue(maximumAttachmentSize == -1 ? -1 : MessageCore::MessageCoreSettings::self()->maximumAttachmentSize() / 1024);
}

void ComposerPage::AttachmentsTab::save()
{
    saveCheckBox(mOutlookCompatibleCheck, MessageComposer::MessageComposerSettings::self()->outlookCompatibleAttachmentsItem());
    saveCheckBox(mMissingAttachmentDetectionCheck, KMailSettings::self()->showForgottenAttachmentWarningItem());
    saveSimpleStringListEditor(mAttachWordsListEditor, KMailSettings::self()->attachmentKeywordsItem());

    KMime::setUseOutlookAttachmentEncoding(mOutlookCompatibleCheck->isChecked());
    const int maximumAttachmentSize(mMaximumAttachmentSize->value());
    MessageCore::MessageCoreSettings::self()->setMaximumAttachmentSize(maximumAttachmentSize == -1 ? -1 : maximumAttachmentSize * 1024);
}

void ComposerPageAttachmentsTab::slotOutlookCompatibleClicked()
{
    if (mOutlookCompatibleCheck->isChecked()) {
        KMessageBox::information(nullptr, i18n("You have chosen to "
                                               "encode attachment names containing non-English characters in a way that "
                                               "is understood by Outlook(tm) and other mail clients that do not "
                                               "support standard-compliant encoded attachment names.\n"
                                               "Note that KMail may create non-standard compliant messages, "
                                               "and consequently it is possible that your messages will not be "
                                               "understood by standard-compliant mail clients; so, unless you have no "
                                               "other choice, you should not enable this option."));
    }
}

ComposerPageAutoCorrectionTab::ComposerPageAutoCorrectionTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    QVBoxLayout *vlay = new QVBoxLayout(this);
    vlay->setSpacing(0);
    vlay->setContentsMargins(0, 0, 0, 0);
    autocorrectionWidget = new PimCommon::AutoCorrectionWidget(this);
    if (KMKernel::self()) {
        autocorrectionWidget->setAutoCorrection(KMKernel::self()->composerAutoCorrection());
    }
    vlay->addWidget(autocorrectionWidget);
    connect(autocorrectionWidget, &PimCommon::AutoCorrectionWidget::changed, this, &ConfigModuleTab::slotEmitChanged);
}

QString ComposerPageAutoCorrectionTab::helpAnchor() const
{
    return QStringLiteral("configure-autocorrection");
}

void ComposerPageAutoCorrectionTab::save()
{
    autocorrectionWidget->writeConfig();
}

void ComposerPageAutoCorrectionTab::doLoadFromGlobalSettings()
{
    autocorrectionWidget->loadConfig();
}

void ComposerPageAutoCorrectionTab::doResetToDefaultsOther()
{
    autocorrectionWidget->resetToDefault();
}

ComposerPageAutoImageResizeTab::ComposerPageAutoImageResizeTab(QWidget *parent)
    : ConfigModuleTab(parent)
{
    QVBoxLayout *vlay = new QVBoxLayout(this);
    vlay->setSpacing(0);
    vlay->setContentsMargins(0, 0, 0, 0);
    autoResizeWidget = new MessageComposer::ImageScalingWidget(this);
    vlay->addWidget(autoResizeWidget);
    connect(autoResizeWidget, &MessageComposer::ImageScalingWidget::changed, this, &ConfigModuleTab::slotEmitChanged);
}

QString ComposerPageAutoImageResizeTab::helpAnchor() const
{
    return QStringLiteral("configure-image-resize");
}

void ComposerPageAutoImageResizeTab::save()
{
    autoResizeWidget->writeConfig();
}

void ComposerPageAutoImageResizeTab::doLoadFromGlobalSettings()
{
    autoResizeWidget->loadConfig();
}

void ComposerPageAutoImageResizeTab::doResetToDefaultsOther()
{
    autoResizeWidget->resetToDefault();
}
