#include "vsettingsdialog.h"
#include <QtWidgets>
#include <QRegExp>
#include <QToolTip>

#include "vconfigmanager.h"
#include "utils/vutils.h"
#include "vconstants.h"
#include "vlineedit.h"
#include "vplantumlhelper.h"
#include "vgraphvizhelper.h"
#include "utils/vkeyboardlayoutmanager.h"
#include "dialog/vkeyboardlayoutmappingdialog.h"

extern VConfigManager *g_config;

VSettingsDialog::VSettingsDialog(QWidget *p_parent)
    : QDialog(p_parent),
      m_needUpdateEditorFont(false)
{
    m_tabList = new QListWidget(this);
    m_tabList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);

    m_tabs = new QStackedLayout();

    // Reset VNote.
    m_resetVNoteBtn = new QPushButton(tr("Reset VNote"), this);
    m_resetVNoteBtn->setProperty("DangerBtn", true);
    m_resetVNoteBtn->setToolTip(tr("Reset all the configurations of VNote"));
    connect(m_resetVNoteBtn, &QPushButton::clicked,
            this, &VSettingsDialog::resetVNote);

    // Reset Layout.
    m_resetLayoutBtn = new QPushButton(tr("Reset Layout"), this);
    m_resetLayoutBtn->setToolTip(tr("Reset layout of VNote"));
    connect(m_resetLayoutBtn, &QPushButton::clicked,
            this, &VSettingsDialog::resetLayout);

    m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    connect(m_btnBox, &QDialogButtonBox::accepted, this, &VSettingsDialog::saveConfiguration);
    connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
    m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true);

    m_btnBox->addButton(m_resetVNoteBtn, QDialogButtonBox::ResetRole);
    m_btnBox->addButton(m_resetLayoutBtn, QDialogButtonBox::ResetRole);

    QHBoxLayout *tabLayout = new QHBoxLayout();
    tabLayout->addWidget(m_tabList);
    tabLayout->addLayout(m_tabs);
    tabLayout->setContentsMargins(0, 0, 0, 0);
    tabLayout->setSpacing(0);
    tabLayout->setStretch(0, 0);
    tabLayout->setStretch(1, 5);

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addLayout(tabLayout);
    mainLayout->addWidget(m_btnBox);
    setLayout(mainLayout);

    setWindowTitle(tr("Settings"));

    // Add tabs.
    addTab(new VGeneralTab(), tr("General"));
    addTab(new VLookTab(), tr("Appearance"));
    addTab(new VReadEditTab(this), tr("Read/Edit"));
    addTab(new VNoteManagementTab(), tr("Note Management"));
    addTab(new VMarkdownTab(), tr("Markdown"));
    addTab(new VMiscTab(), tr("Misc"));

    m_tabList->setMaximumWidth(m_tabList->sizeHintForColumn(0) + 5);

    connect(m_tabList, &QListWidget::currentItemChanged,
            this, [this](QListWidgetItem *p_cur, QListWidgetItem *p_pre) {
                Q_UNUSED(p_pre);
                Q_ASSERT(p_cur);
                int idx = p_cur->data(Qt::UserRole).toInt();
                Q_ASSERT(idx >= 0);
                m_tabs->setCurrentWidget(m_tabs->widget(idx));
            });

    m_tabList->setCurrentRow(0);

    loadConfiguration();
}

void VSettingsDialog::resetVNote()
{
    int ret = VUtils::showMessage(QMessageBox::Warning,
                                  tr("Warning"),
                                  tr("Are you sure to reset VNote?"),
                                  tr("All configurations (except notebooks information) "
                                     "will be reset to default values. "
                                     "It is UNRECOVERABLE!"),
                                  QMessageBox::Ok | QMessageBox::Cancel,
                                  QMessageBox::Cancel,
                                  this,
                                  MessageBoxType::Danger);

    if (ret == QMessageBox::Cancel) {
        return;
    }

    g_config->resetConfigurations();

    VUtils::showMessage(QMessageBox::Information,
                        tr("Information"),
                        tr("Please restart VNote to make it work."),
                        tr("Any change to VNote before restart will be lost!"),
                        QMessageBox::Ok,
                        QMessageBox::Ok,
                        this);

    reject();
}

void VSettingsDialog::resetLayout()
{
    int ret = VUtils::showMessage(QMessageBox::Warning,
                                  tr("Warning"),
                                  tr("Are you sure to reset the layout of VNote?"),
                                  tr("The view and layout mode will be reset. "
                                     "It is UNRECOVERABLE!"),
                                  QMessageBox::Ok | QMessageBox::Cancel,
                                  QMessageBox::Cancel,
                                  this,
                                  MessageBoxType::Danger);

    if (ret == QMessageBox::Cancel) {
        return;
    }

    g_config->resetLayoutConfigurations();

    VUtils::showMessage(QMessageBox::Information,
                        tr("Information"),
                        tr("Please restart VNote to make it work."),
                        tr("Any change to VNote before restart will be lost!"),
                        QMessageBox::Ok,
                        QMessageBox::Ok,
                        this);

    reject();
}

void VSettingsDialog::addTab(QWidget *p_widget, const QString &p_label)
{
    int idx = m_tabs->addWidget(p_widget);
    QListWidgetItem *item = new QListWidgetItem(p_label, m_tabList);
    item->setData(Qt::UserRole, idx);
}

void VSettingsDialog::loadConfiguration()
{
    int idx = 0;
    // General Tab.
    {
        VGeneralTab *generalTab = dynamic_cast<VGeneralTab *>(m_tabs->widget(idx++));
        Q_ASSERT(generalTab);
        if (!generalTab->loadConfiguration()) {
            goto err;
        }
    }

    // Appearance Tab.
    {
        VLookTab *lookTab = dynamic_cast<VLookTab *>(m_tabs->widget(idx++));
        Q_ASSERT(lookTab);
        if (!lookTab->loadConfiguration()) {
            goto err;
        }
    }

    // Read/Edit Tab.
    {
        VReadEditTab *readEditTab = dynamic_cast<VReadEditTab *>(m_tabs->widget(idx++));
        Q_ASSERT(readEditTab);
        if (!readEditTab->loadConfiguration()) {
            goto err;
        }
    }

    // Note Management Tab.
    {
        VNoteManagementTab *noteManagementTab = dynamic_cast<VNoteManagementTab *>(m_tabs->widget(idx++));
        Q_ASSERT(noteManagementTab);
        if (!noteManagementTab->loadConfiguration()) {
            goto err;
        }
    }

    // Markdown Tab.
    {
        VMarkdownTab *markdownTab = dynamic_cast<VMarkdownTab *>(m_tabs->widget(idx++));
        Q_ASSERT(markdownTab);
        if (!markdownTab->loadConfiguration()) {
            goto err;
        }
    }

    // Misc Tab.
    {
        VMiscTab *miscTab = dynamic_cast<VMiscTab *>(m_tabs->widget(idx++));
        Q_ASSERT(miscTab);
        if (!miscTab->loadConfiguration()) {
            goto err;
        }
    }

    return;
err:
    VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
                        tr("Fail to load configuration."), "",
                        QMessageBox::Ok, QMessageBox::Ok, NULL);
    QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
}

void VSettingsDialog::saveConfiguration()
{
    int idx = 0;
    // General Tab.
    {
        VGeneralTab *generalTab = dynamic_cast<VGeneralTab *>(m_tabs->widget(idx++));
        Q_ASSERT(generalTab);
        if (!generalTab->saveConfiguration()) {
            goto err;
        }
    }

    // Appearance Tab.
    {
        VLookTab *lookTab = dynamic_cast<VLookTab *>(m_tabs->widget(idx++));
        Q_ASSERT(lookTab);
        if (!lookTab->saveConfiguration()) {
            goto err;
        }
    }

    // Read/Edit Tab.
    {
        VReadEditTab *readEditTab = dynamic_cast<VReadEditTab *>(m_tabs->widget(idx++));
        Q_ASSERT(readEditTab);
        if (!readEditTab->saveConfiguration()) {
            goto err;
        }
    }

    // Note Management Tab.
    {
        VNoteManagementTab *noteManagementTab = dynamic_cast<VNoteManagementTab *>(m_tabs->widget(idx++));
        Q_ASSERT(noteManagementTab);
        if (!noteManagementTab->saveConfiguration()) {
            goto err;
        }
    }

    // Markdown Tab.
    {
        VMarkdownTab *markdownTab = dynamic_cast<VMarkdownTab *>(m_tabs->widget(idx++));
        Q_ASSERT(markdownTab);
        if (!markdownTab->saveConfiguration()) {
            goto err;
        }
    }

    // Misc Tab.
    {
        VMiscTab *miscTab = dynamic_cast<VMiscTab *>(m_tabs->widget(idx++));
        Q_ASSERT(miscTab);
        if (!miscTab->saveConfiguration()) {
            goto err;
        }
    }

    accept();
    return;
err:
    VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
                        tr("Fail to save configuration. Please try it again."), "",
                        QMessageBox::Ok, QMessageBox::Ok, NULL);
}

const QVector<QString> VGeneralTab::c_availableLangs = { "System", "English", "Chinese", "Japanese" };

VGeneralTab::VGeneralTab(QWidget *p_parent)
    : QWidget(p_parent)
{
    // Language combo.
    m_langCombo = VUtils::getComboBox(this);
    m_langCombo->setToolTip(tr("Choose the language of VNote interface"));
    m_langCombo->addItem(tr("System"), "System");
    auto langs = VUtils::getAvailableLanguages();
    for (auto const &lang : langs) {
        m_langCombo->addItem(lang.second, lang.first);
    }

    // System tray checkbox.
    m_systemTray = new QCheckBox(tr("System tray"), this);
    m_systemTray->setToolTip(tr("Minimized to the system tray after closing VNote"
                                " (not supported in macOS)"));
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
    // Do not support minimized to tray on macOS.
    m_systemTray->setEnabled(false);
#endif

    // Startup pages.
    QLayout *startupLayout = setupStartupPagesLayout();

    // Quick access.
    m_quickAccessEdit = new VLineEdit(this);
    m_quickAccessEdit->setPlaceholderText(tr("Path of file to quick access"));
    m_quickAccessEdit->setToolTip(tr("Set the path of a file to quick access "
                                     "(absolute or relative to configuration folder)"));

    QPushButton *browseBtn = new QPushButton(tr("Browse"), this);
    connect(browseBtn, &QPushButton::clicked,
            this, [this]() {
                QString filePath = QFileDialog::getOpenFileName(this,
                                                                tr("Select File To Quick Access"),
                                                                g_config->getDocumentPathOrHomePath());

                if (!filePath.isEmpty()) {
                    m_quickAccessEdit->setText(filePath);
                }
            });

    QHBoxLayout *qaLayout = new QHBoxLayout();
    qaLayout->addWidget(m_quickAccessEdit);
    qaLayout->addWidget(browseBtn);

    // Keyboard layout mappings.
    m_keyboardLayoutCombo = VUtils::getComboBox(this);
    m_keyboardLayoutCombo->setToolTip(tr("Choose the keyboard layout mapping to use in shortcuts"));

    // OpenGL option.
#if defined(Q_OS_WIN)
    m_openGLCombo = VUtils::getComboBox(this);
    m_openGLCombo->setToolTip(tr("Choose the OpenGL implementation to load (restart VNote to make it work)"));
#endif

    QPushButton *editLayoutBtn = new QPushButton(tr("Edit"), this);
    connect(editLayoutBtn, &QPushButton::clicked,
            this, [this]() {
                VKeyboardLayoutMappingDialog dialog(this);
                dialog.exec();
                loadKeyboardLayoutMapping();
            });

    QHBoxLayout *klLayout = new QHBoxLayout();
    klLayout->addWidget(m_keyboardLayoutCombo);
    klLayout->addWidget(editLayoutBtn);
    klLayout->addStretch();

    QFormLayout *optionLayout = new QFormLayout();
    optionLayout->addRow(tr("Language:"), m_langCombo);
    optionLayout->addRow(m_systemTray);
    optionLayout->addRow(tr("Startup pages:"), startupLayout);
    optionLayout->addRow(tr("Quick access:"), qaLayout);
    optionLayout->addRow(tr("Keyboard layout mapping:"), klLayout);

#if defined(Q_OS_WIN)
    optionLayout->addRow(tr("OpenGL:"), m_openGLCombo);
#endif

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addLayout(optionLayout);

    setLayout(mainLayout);
}

QLayout *VGeneralTab::setupStartupPagesLayout()
{
    m_startupPageTypeCombo = VUtils::getComboBox(this);
    m_startupPageTypeCombo->setToolTip(tr("Restore tabs or open specific notes on startup"));
    m_startupPageTypeCombo->addItem(tr("None"), (int)StartupPageType::None);
    m_startupPageTypeCombo->addItem(tr("Continue where you left off"), (int)StartupPageType::ContinueLeftOff);
    m_startupPageTypeCombo->addItem(tr("Open specific pages"), (int)StartupPageType::SpecificPages);
    connect(m_startupPageTypeCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
            this, [this](int p_index) {
                int type = m_startupPageTypeCombo->itemData(p_index).toInt();
                bool pagesEditVisible = type == (int)StartupPageType::SpecificPages;
                m_startupPagesEdit->setVisible(pagesEditVisible);
                m_startupPagesAddBtn->setVisible(pagesEditVisible);
            });

    m_startupPagesEdit = new QPlainTextEdit(this);
    m_startupPagesEdit->setProperty("LineEdit", true);
    m_startupPagesEdit->setToolTip(tr("Absolute path of the notes to open on startup (one note per line)"));

    m_startupPagesAddBtn = new QPushButton(tr("Browse"), this);
    m_startupPagesAddBtn->setToolTip(tr("Select files to add as startup pages"));
    connect(m_startupPagesAddBtn, &QPushButton::clicked,
            this, [this]() {
                static QString lastPath = QDir::homePath();
                QStringList files = QFileDialog::getOpenFileNames(this,
                                                                  tr("Select Files As Startup Pages"),
                                                                  lastPath);
                if (files.isEmpty()) {
                    return;
                }

                // Update lastPath
                lastPath = QFileInfo(files[0]).path();

                m_startupPagesEdit->appendPlainText(files.join("\n"));
            });

    QHBoxLayout *startupPagesBtnLayout = new QHBoxLayout();
    startupPagesBtnLayout->addStretch();
    startupPagesBtnLayout->addWidget(m_startupPagesAddBtn);

    QVBoxLayout *startupPagesLayout = new QVBoxLayout();
    startupPagesLayout->addWidget(m_startupPagesEdit);
    startupPagesLayout->addLayout(startupPagesBtnLayout);

    QVBoxLayout *startupLayout = new QVBoxLayout();
    startupLayout->addWidget(m_startupPageTypeCombo);
    startupLayout->addLayout(startupPagesLayout);

    m_startupPagesEdit->hide();
    m_startupPagesAddBtn->hide();

    return startupLayout;
}

bool VGeneralTab::loadConfiguration()
{
    if (!loadLanguage()) {
        return false;
    }

    if (!loadSystemTray()) {
        return false;
    }

    if (!loadStartupPageType()) {
        return false;
    }

    if (!loadQuickAccess()) {
        return false;
    }

    if (!loadKeyboardLayoutMapping()) {
        return false;
    }

    if (!loadOpenGL()) {
        return false;
    }

    return true;
}

bool VGeneralTab::saveConfiguration()
{
    if (!saveLanguage()) {
        return false;
    }

    if (!saveSystemTray()) {
        return false;
    }

    if (!saveStartupPageType()) {
        return false;
    }

    if (!saveQuickAccess()) {
        return false;
    }

    if (!saveKeyboardLayoutMapping()) {
        return false;
    }

    if (!saveOpenGL()) {
        return false;
    }

    return true;
}

bool VGeneralTab::loadLanguage()
{
    QString lang = g_config->getLanguage();
    if (lang.isNull()) {
        return false;
    } else if (lang == "System") {
        m_langCombo->setCurrentIndex(0);
        return true;
    }
    bool found = false;
    // lang is the value, not name.
    for (int i = 0; i < m_langCombo->count(); ++i) {
        if (m_langCombo->itemData(i).toString() == lang) {
            found = true;
            m_langCombo->setCurrentIndex(i);
            break;
        }
    }
    if (!found) {
        qWarning() << "invalid language configuration (using default value)";
        m_langCombo->setCurrentIndex(0);
    }
    return true;
}

bool VGeneralTab::saveLanguage()
{
    QString curLang = m_langCombo->currentData().toString();
    g_config->setLanguage(curLang);
    return true;
}

bool VGeneralTab::loadSystemTray()
{
    m_systemTray->setChecked(g_config->getMinimizeToStystemTray() != 0);
    return true;
}

bool VGeneralTab::saveSystemTray()
{
    if (m_systemTray->isEnabled()) {
        g_config->setMinimizeToSystemTray(m_systemTray->isChecked() ? 1 : 0);
    }

    return true;
}

bool VGeneralTab::loadStartupPageType()
{
    StartupPageType type = g_config->getStartupPageType();
    bool found = false;
    for (int i = 0; i < m_startupPageTypeCombo->count(); ++i) {
        if (m_startupPageTypeCombo->itemData(i).toInt() == (int)type) {
            found = true;
            m_startupPageTypeCombo->setCurrentIndex(i);
        }
    }

    Q_ASSERT(found);

    const QStringList &pages = g_config->getStartupPages();
    m_startupPagesEdit->setPlainText(pages.join("\n"));

    bool pagesEditVisible = type == StartupPageType::SpecificPages;
    m_startupPagesEdit->setVisible(pagesEditVisible);
    m_startupPagesAddBtn->setVisible(pagesEditVisible);

    return true;
}

bool VGeneralTab::saveStartupPageType()
{
    StartupPageType type = (StartupPageType)m_startupPageTypeCombo->currentData().toInt();
    g_config->setStartupPageType(type);

    if (type == StartupPageType::SpecificPages) {
        QStringList pages = m_startupPagesEdit->toPlainText().split("\n");
        g_config->setStartupPages(pages);
    }

    return true;
}

bool VGeneralTab::loadQuickAccess()
{
    m_quickAccessEdit->setText(g_config->getQuickAccess());
    return true;
}

bool VGeneralTab::saveQuickAccess()
{
    g_config->setQuickAccess(m_quickAccessEdit->text());
    return true;
}

bool VGeneralTab::loadKeyboardLayoutMapping()
{
    m_keyboardLayoutCombo->clear();

    m_keyboardLayoutCombo->addItem(tr("None"), "");

    QStringList layouts = VKeyboardLayoutManager::availableLayouts();
    for (auto const & layout : layouts) {
        m_keyboardLayoutCombo->addItem(layout, layout);
    }

    int idx = 0;
    const auto &cur = VKeyboardLayoutManager::currentLayout();
    if (!cur.m_name.isEmpty()) {
        idx = m_keyboardLayoutCombo->findData(cur.m_name);
        if (idx == -1) {
            idx = 0;
            VKeyboardLayoutManager::setCurrentLayout("");
        }
    }

    m_keyboardLayoutCombo->setCurrentIndex(idx);
    return true;
}

bool VGeneralTab::saveKeyboardLayoutMapping()
{
    g_config->setKeyboardLayout(m_keyboardLayoutCombo->currentData().toString());
    VKeyboardLayoutManager::update();
    return true;
}

bool VGeneralTab::loadOpenGL()
{
#if defined(Q_OS_WIN)
    m_openGLCombo->clear();

    m_openGLCombo->addItem(tr("System"), OpenGLDefault);
    m_openGLCombo->addItem(tr("DesktopOpenGL"), OpenGLDesktop);
    m_openGLCombo->addItem(tr("OpenGLES"), OpenGLAngle);
    m_openGLCombo->addItem(tr("SoftwareOpenGL"), OpenGLSoftware);

    int opt = VConfigManager::getWindowsOpenGL();
    int idx = m_openGLCombo->findData(opt);
    if (idx == -1) {
        idx = 0;
    }
    m_openGLCombo->setCurrentIndex(idx);
#endif
    return true;
}

bool VGeneralTab::saveOpenGL()
{
#if defined(Q_OS_WIN)
    VConfigManager::setWindowsOpenGL(m_openGLCombo->currentData().toInt());
#endif
    return true;
}

VLookTab::VLookTab(QWidget *p_parent)
    : QWidget(p_parent)
{
    m_tbIconSizeSpin = new QSpinBox(this);
    m_tbIconSizeSpin->setToolTip(tr("Icon size in pixel of tool bar (restart VNote to make it work)"));
    m_tbIconSizeSpin->setMaximum(100);
    m_tbIconSizeSpin->setMinimum(5);

    QFormLayout *layout = new QFormLayout();
    layout->addRow(tr("Tool bar icon size:"), m_tbIconSizeSpin);

    setLayout(layout);
}

bool VLookTab::loadConfiguration()
{
    if (!loadToolBarIconSize()) {
        return false;
    }

    return true;
}

bool VLookTab::saveConfiguration()
{
    if (!saveToolBarIconSize()) {
        return false;
    }

    return true;
}

bool VLookTab::loadToolBarIconSize()
{
    int sz = g_config->getToolBarIconSize();
    m_tbIconSizeSpin->setValue(sz);
    return true;
}

bool VLookTab::saveToolBarIconSize()
{
    g_config->setToolBarIconSize(m_tbIconSizeSpin->value());
    return true;
}

VReadEditTab::VReadEditTab(VSettingsDialog *p_dlg, QWidget *p_parent)
    : QWidget(p_parent),
      m_settingsDlg(p_dlg)
{
    m_readBox = new QGroupBox(tr("Read Mode (For Markdown Only)"));
    m_editBox = new QGroupBox(tr("Edit Mode"));

    // Web Zoom Factor.
    m_customWebZoom = new QCheckBox(tr("Custom Web zoom factor"));
    m_customWebZoom->setToolTip(tr("Set the zoom factor of the Web page when reading"));
    m_webZoomFactorSpin = new QDoubleSpinBox();
    m_webZoomFactorSpin->setMaximum(c_webZoomFactorMax);
    m_webZoomFactorSpin->setMinimum(c_webZoomFactorMin);
    m_webZoomFactorSpin->setSingleStep(0.25);
    connect(m_customWebZoom, &QCheckBox::stateChanged,
            this, [this](int p_state) {
                this->m_webZoomFactorSpin->setEnabled(p_state == Qt::Checked);
            });
    QHBoxLayout *zoomFactorLayout = new QHBoxLayout();
    zoomFactorLayout->addWidget(m_customWebZoom);
    zoomFactorLayout->addWidget(m_webZoomFactorSpin);

    // Web flash anchor.
    m_flashAnchor = new QCheckBox(tr("Flash current heading"));
    m_flashAnchor->setToolTip(tr("Flash current heading on change"));

    // Swap file.
    m_swapFile = new QCheckBox(tr("Swap file"));
    m_swapFile->setToolTip(tr("Automatically save changes to a swap file for backup"));
    connect(m_swapFile, &QCheckBox::stateChanged,
            this, &VReadEditTab::showTipsAboutAutoSave);

    // Auto save.
    m_autoSave = new QCheckBox(tr("Auto save"));
    m_autoSave->setToolTip(tr("Automatically save the note when editing"));
    connect(m_autoSave, &QCheckBox::stateChanged,
            this, &VReadEditTab::showTipsAboutAutoSave);

    // Key mode.
    m_keyModeCB = VUtils::getComboBox();
    m_keyModeCB->setToolTip(tr("Choose the key mode in editor"));
    m_keyModeCB->addItem(tr("Normal"), (int)KeyMode::Normal);
    m_keyModeCB->addItem(tr("Vim"), (int)KeyMode::Vim);
    connect(m_keyModeCB, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            this, [this](int p_index) {
                int mode = m_keyModeCB->itemData(p_index).toInt();
                m_smartIM->setVisible(mode == (int)KeyMode::Vim);
            });

    // Smart IM in Vim.
    m_smartIM = new QCheckBox(tr("Smart input method in Vim mode"));
    m_smartIM->setToolTip(tr("Disable input method when leaving Insert mode in Vim mode"));

    // Editor font family.
    m_customEditorFont = new QCheckBox(tr("Custom editor font"));
    m_customEditorFont->setToolTip(tr("Set the font of editor to override style configuration"));
    connect(m_customEditorFont, &QCheckBox::stateChanged,
            this, [this](int p_state) {
                m_editorFontFamilyCB->setEnabled(p_state == Qt::Checked);
            });
    m_editorFontFamilyCB = new QFontComboBox();
    QHBoxLayout *editorFontLayout = new QHBoxLayout();
    editorFontLayout->addWidget(m_customEditorFont);
    editorFontLayout->addWidget(m_editorFontFamilyCB);

    // Editor zoom delta.
    m_editorZoomDeltaSpin = new QSpinBox();
    m_editorZoomDeltaSpin->setToolTip(tr("Set the zoom delta of the editor font"));
    m_editorZoomDeltaSpin->setMaximum(c_editorZoomDeltaMax);
    m_editorZoomDeltaSpin->setMinimum(c_editorZoomDeltaMin);
    m_editorZoomDeltaSpin->setSingleStep(1);

    QVBoxLayout *readLayout = new QVBoxLayout();
    readLayout->addLayout(zoomFactorLayout);
    readLayout->addWidget(m_flashAnchor);
    m_readBox->setLayout(readLayout);

    QFormLayout *editLayout = new QFormLayout();
    editLayout->addRow(m_swapFile);
    editLayout->addRow(m_autoSave);
    editLayout->addRow(tr("Key mode:"), m_keyModeCB);
    editLayout->addWidget(m_smartIM);
    editLayout->addRow(tr("Editor zoom delta:"), m_editorZoomDeltaSpin);
    editLayout->addRow(editorFontLayout);
    m_editBox->setLayout(editLayout);

    m_smartIM->hide();
    m_keyModeCB->setCurrentIndex(0);

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addWidget(m_readBox);
    mainLayout->addWidget(m_editBox);
    setLayout(mainLayout);
}

void VReadEditTab::showTipsAboutAutoSave()
{
    if (m_autoSave->isChecked() && m_swapFile->isChecked()) {
        // Show a tooltip.
        QPoint pos = m_editBox->mapToGlobal(QPoint(0, 0));
        QToolTip::showText(pos,
                           tr("It's recommended to enable \"Swap file\" "
                              "or \"Auto save\", not both"),
                            m_editBox);
    }
}

bool VReadEditTab::loadConfiguration()
{
    if (!loadWebZoomFactor()) {
        return false;
    }

    if (!loadFlashAnchor()) {
        return false;
    }

    if (!loadSwapFile()) {
        return false;
    }

    if (!loadAutoSave()) {
        return false;
    }

    if (!loadEditorZoomDelta()) {
        return false;
    }

    if (!loadEditorFontFamily()) {
        return false;
    }

    if (!loadKeyMode()) {
        return false;
    }

    return true;
}

bool VReadEditTab::saveConfiguration()
{
    if (!saveWebZoomFactor()) {
        return false;
    }

    if (!saveFlashAnchor()) {
        return false;
    }

    if (!saveSwapFile()) {
        return false;
    }

    if (!saveAutoSave()) {
        return false;
    }

    if (!saveEditorZoomDelta()) {
        return false;
    }

    if (!saveEditorFontFamily()) {
        return false;
    }

    if (!saveKeyMode()) {
        return false;
    }

    return true;
}

bool VReadEditTab::loadWebZoomFactor()
{
    qreal factor = g_config->getWebZoomFactor();
    bool customFactor = g_config->isCustomWebZoomFactor();
    if (customFactor) {
        if (factor < c_webZoomFactorMin || factor > c_webZoomFactorMax) {
            factor = 1;
        }
        m_customWebZoom->setChecked(true);
        m_webZoomFactorSpin->setValue(factor);
    } else {
        m_customWebZoom->setChecked(false);
        m_webZoomFactorSpin->setValue(factor);
        m_webZoomFactorSpin->setEnabled(false);
    }

    return true;
}

bool VReadEditTab::saveWebZoomFactor()
{
    if (m_customWebZoom->isChecked()) {
        g_config->setWebZoomFactor(m_webZoomFactorSpin->value());
    } else {
        g_config->setWebZoomFactor(-1);
    }

    return true;
}

bool VReadEditTab::loadEditorZoomDelta()
{
    m_editorZoomDeltaSpin->setValue(g_config->getEditorZoomDelta());
    return true;
}

bool VReadEditTab::saveEditorZoomDelta()
{
    g_config->setEditorZoomDelta(m_editorZoomDeltaSpin->value());
    return true;
}

bool VReadEditTab::loadEditorFontFamily()
{
    const QString &family = g_config->getEditorFontFamily();
    m_customEditorFont->setChecked(!family.isEmpty());

    m_editorFontFamilyCB->setCurrentFont(g_config->getMdEditFont());
    m_editorFontFamilyCB->setEnabled(m_customEditorFont->isChecked());
    return true;
}

bool VReadEditTab::saveEditorFontFamily()
{
    QString family;
    if (m_customEditorFont->isChecked()) {
        QFont font = m_editorFontFamilyCB->currentFont();
        family = font.family();
    }

    if (family != g_config->getEditorFontFamily()) {
        g_config->setEditorFontFamily(family);
        m_settingsDlg->setNeedUpdateEditorFont(true);
    }

    return true;
}

bool VReadEditTab::loadFlashAnchor()
{
    m_flashAnchor->setChecked(g_config->getEnableFlashAnchor());
    return true;
}

bool VReadEditTab::saveFlashAnchor()
{
    g_config->setEnableFlashAnchor(m_flashAnchor->isChecked());
    return true;
}

bool VReadEditTab::loadSwapFile()
{
    m_swapFile->setChecked(g_config->getEnableBackupFile());
    return true;
}

bool VReadEditTab::saveSwapFile()
{
    g_config->setEnableBackupFile(m_swapFile->isChecked());
    return true;
}

bool VReadEditTab::loadAutoSave()
{
    m_autoSave->setChecked(g_config->getEnableAutoSave());
    return true;
}

bool VReadEditTab::saveAutoSave()
{
    g_config->setEnableAutoSave(m_autoSave->isChecked());
    return true;
}

bool VReadEditTab::loadKeyMode()
{
    m_keyModeCB->setCurrentIndex(m_keyModeCB->findData((int)g_config->getKeyMode()));
    m_smartIM->setChecked(g_config->getEnableSmartImInVimMode());
    return true;
}

bool VReadEditTab::saveKeyMode()
{
    g_config->setKeyMode((KeyMode)m_keyModeCB->currentData().toInt());
    g_config->setEnableSmartImInVimMode(m_smartIM->isChecked());
    return true;
}

VNoteManagementTab::VNoteManagementTab(QWidget *p_parent)
    : QWidget(p_parent)
{
    m_noteBox = new QGroupBox(tr("Notes"));
    m_externalBox = new QGroupBox(tr("External Files"));

    // Note.
    // Image folder.
    m_customImageFolder = new QCheckBox(tr("Custom image folder"), this);
    m_customImageFolder->setToolTip(tr("Set the global name of the image folder to hold images "
                                       "of notes (restart VNote to make it work)"));
    connect(m_customImageFolder, &QCheckBox::stateChanged,
            this, &VNoteManagementTab::customImageFolderChanged);

    m_imageFolderEdit = new VLineEdit(this);
    m_imageFolderEdit->setPlaceholderText(tr("Name of the image folder"));
    m_imageFolderEdit->setToolTip(m_customImageFolder->toolTip());
    QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), this);
    m_imageFolderEdit->setValidator(validator);

    QHBoxLayout *imageFolderLayout = new QHBoxLayout();
    imageFolderLayout->addWidget(m_customImageFolder);
    imageFolderLayout->addWidget(m_imageFolderEdit);

    // Attachment folder.
    m_customAttachmentFolder = new QCheckBox(tr("Custom attachment folder"), this);
    m_customAttachmentFolder->setToolTip(tr("Set the global name of the attachment folder to hold attachments "
                                            "of notes (restart VNote to make it work)"));
    connect(m_customAttachmentFolder, &QCheckBox::stateChanged,
            this, &VNoteManagementTab::customAttachmentFolderChanged);

    m_attachmentFolderEdit = new VLineEdit(this);
    m_attachmentFolderEdit->setPlaceholderText(tr("Name of the attachment folder"));
    m_attachmentFolderEdit->setToolTip(m_customAttachmentFolder->toolTip());
    validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), this);
    m_attachmentFolderEdit->setValidator(validator);

    QHBoxLayout *attachmentFolderLayout = new QHBoxLayout();
    attachmentFolderLayout->addWidget(m_customAttachmentFolder);
    attachmentFolderLayout->addWidget(m_attachmentFolderEdit);

    // Single click open.
    m_singleClickOpen = new QCheckBox(tr("Single click to open a note in current tab"), this);
    m_singleClickOpen->setToolTip(tr("Single click a note in the notes list to open it in current tab, "
                                     "double click to open it in a new tab"));

    QFormLayout *noteLayout = new QFormLayout();
    noteLayout->addRow(imageFolderLayout);
    noteLayout->addRow(attachmentFolderLayout);
    noteLayout->addRow(m_singleClickOpen);
    m_noteBox->setLayout(noteLayout);

    // External File.
    // Image folder.
    m_customImageFolderExt = new QCheckBox(tr("Custom image folder"), this);
    m_customImageFolderExt->setToolTip(tr("Set the path of the global image folder to hold images "
                                          "of external files (restart VNote to make it work).\nYou "
                                          "could use both absolute or relative path here. If "
                                          "absolute path is used, VNote will not manage\nthose images, "
                                          "so you need to clean up unused images manually."));
    connect(m_customImageFolderExt, &QCheckBox::stateChanged,
            this, &VNoteManagementTab::customImageFolderExtChanged);

    m_imageFolderEditExt = new VLineEdit(this);
    m_imageFolderEditExt->setToolTip(m_customImageFolderExt->toolTip());
    m_imageFolderEditExt->setPlaceholderText(tr("Name of the image folder"));

    QHBoxLayout *imageFolderExtLayout = new QHBoxLayout();
    imageFolderExtLayout->addWidget(m_customImageFolderExt);
    imageFolderExtLayout->addWidget(m_imageFolderEditExt);

    QFormLayout *externalLayout = new QFormLayout();
    externalLayout->addRow(imageFolderExtLayout);
    m_externalBox->setLayout(externalLayout);

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addWidget(m_noteBox);
    mainLayout->addWidget(m_externalBox);

    setLayout(mainLayout);
}

bool VNoteManagementTab::loadConfiguration()
{
    if (!loadImageFolder()) {
        return false;
    }

    if (!loadAttachmentFolder()) {
        return false;
    }

    if (!loadImageFolderExt()) {
        return false;
    }

    if (!loadSingleClickOpen()) {
        return false;
    }

    return true;
}

bool VNoteManagementTab::saveConfiguration()
{
    if (!saveImageFolder()) {
        return false;
    }

    if (!saveAttachmentFolder()) {
        return false;
    }

    if (!saveImageFolderExt()) {
        return false;
    }

    if (!saveSingleClickOpen()) {
        return false;
    }

    return true;
}

bool VNoteManagementTab::loadImageFolder()
{
    bool isCustom = g_config->isCustomImageFolder();

    m_customImageFolder->setChecked(isCustom);
    m_imageFolderEdit->setText(g_config->getImageFolder());
    m_imageFolderEdit->setEnabled(isCustom);

    return true;
}

bool VNoteManagementTab::saveImageFolder()
{
    if (m_customImageFolder->isChecked()) {
        g_config->setImageFolder(m_imageFolderEdit->text());
    } else {
        g_config->setImageFolder("");
    }

    return true;
}

void VNoteManagementTab::customImageFolderChanged(int p_state)
{
    if (p_state == Qt::Checked) {
        m_imageFolderEdit->setEnabled(true);
        m_imageFolderEdit->selectAll();
        m_imageFolderEdit->setFocus();
    } else {
        m_imageFolderEdit->setEnabled(false);
    }
}

bool VNoteManagementTab::loadAttachmentFolder()
{
    bool isCustom = g_config->isCustomAttachmentFolder();

    m_customAttachmentFolder->setChecked(isCustom);
    m_attachmentFolderEdit->setText(g_config->getAttachmentFolder());
    m_attachmentFolderEdit->setEnabled(isCustom);

    return true;
}

bool VNoteManagementTab::saveAttachmentFolder()
{
    if (m_customAttachmentFolder->isChecked()) {
        g_config->setAttachmentFolder(m_attachmentFolderEdit->text());
    } else {
        g_config->setAttachmentFolder("");
    }

    return true;
}

void VNoteManagementTab::customAttachmentFolderChanged(int p_state)
{
    if (p_state == Qt::Checked) {
        m_attachmentFolderEdit->setEnabled(true);
        m_attachmentFolderEdit->selectAll();
        m_attachmentFolderEdit->setFocus();
    } else {
        m_attachmentFolderEdit->setEnabled(false);
    }
}

bool VNoteManagementTab::loadImageFolderExt()
{
    bool isCustom = g_config->isCustomImageFolderExt();

    m_customImageFolderExt->setChecked(isCustom);
    m_imageFolderEditExt->setText(g_config->getImageFolderExt());
    m_imageFolderEditExt->setEnabled(isCustom);

    return true;
}

bool VNoteManagementTab::saveImageFolderExt()
{
    if (m_customImageFolderExt->isChecked()) {
        g_config->setImageFolderExt(QDir::fromNativeSeparators(m_imageFolderEditExt->text()));
    } else {
        g_config->setImageFolderExt("");
    }

    return true;
}

void VNoteManagementTab::customImageFolderExtChanged(int p_state)
{
    if (p_state == Qt::Checked) {
        m_imageFolderEditExt->setEnabled(true);
        m_imageFolderEditExt->selectAll();
        m_imageFolderEditExt->setFocus();
    } else {
        m_imageFolderEditExt->setEnabled(false);
    }
}

bool VNoteManagementTab::loadSingleClickOpen()
{
    m_singleClickOpen->setChecked(g_config->getSingleClickClosePreviousTab());
    return true;
}

bool VNoteManagementTab::saveSingleClickOpen()
{
    g_config->setSingleClickClosePreviousTab(m_singleClickOpen->isChecked());
    return true;
}

VMarkdownTab::VMarkdownTab(QWidget *p_parent)
    : QWidget(p_parent)
{
    // Default note open mode.
    m_openModeCombo = VUtils::getComboBox();
    m_openModeCombo->setToolTip(tr("Default mode to open a file"));
    m_openModeCombo->addItem(tr("Read Mode"), (int)OpenFileMode::Read);
    m_openModeCombo->addItem(tr("Edit Mode"), (int)OpenFileMode::Edit);

    // Heading sequence.
    m_headingSequenceTypeCombo = VUtils::getComboBox();
    m_headingSequenceTypeCombo->setToolTip(tr("Enable auto sequence for all headings (in the form like 1.2.3.4.)"));
    m_headingSequenceTypeCombo->addItem(tr("Disabled"), (int)HeadingSequenceType::Disabled);
    m_headingSequenceTypeCombo->addItem(tr("Enabled"), (int)HeadingSequenceType::Enabled);
    m_headingSequenceTypeCombo->addItem(tr("Enabled for internal notes only"), (int)HeadingSequenceType::EnabledNoteOnly);

    m_headingSequenceLevelCombo = VUtils::getComboBox();
    m_headingSequenceLevelCombo->setToolTip(tr("Base level to start heading sequence"));
    m_headingSequenceLevelCombo->addItem(tr("1"), 1);
    m_headingSequenceLevelCombo->addItem(tr("2"), 2);
    m_headingSequenceLevelCombo->addItem(tr("3"), 3);
    m_headingSequenceLevelCombo->addItem(tr("4"), 4);
    m_headingSequenceLevelCombo->addItem(tr("5"), 5);
    m_headingSequenceLevelCombo->addItem(tr("6"), 6);
    m_headingSequenceLevelCombo->setEnabled(false);

    connect(m_headingSequenceTypeCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
            this, [this](int p_index){
                if (p_index > -1) {
                    HeadingSequenceType type = (HeadingSequenceType)m_headingSequenceTypeCombo->itemData(p_index).toInt();
                    m_headingSequenceLevelCombo->setEnabled(type != HeadingSequenceType::Disabled);
                }
            });

    QHBoxLayout *headingSequenceLayout = new QHBoxLayout();
    headingSequenceLayout->addWidget(m_headingSequenceTypeCombo);
    headingSequenceLayout->addWidget(m_headingSequenceLevelCombo);

    // Color column.
    m_colorColumnEdit = new VLineEdit();
    m_colorColumnEdit->setToolTip(tr("Specify the screen column in fenced code block "
                                     "which will be highlighted"));
    QValidator *validator = new QRegExpValidator(QRegExp("\\d+"), this);
    m_colorColumnEdit->setValidator(validator);

    QLabel *colorColumnLabel = new QLabel(tr("Color column:"));
    colorColumnLabel->setToolTip(m_colorColumnEdit->toolTip());

    // MathJax.
    m_mathjaxConfigEdit = new VLineEdit();
    m_mathjaxConfigEdit->setToolTip(tr("Location of MathJax JavaScript and its configuration "
                                       "(restart VNote to make it work in in-place preview)"));
    m_mathjaxConfigEdit->setPlaceholderText(tr("Need to prepend \"file://\" to local path"));

    // PlantUML.
    m_plantUMLModeCombo = VUtils::getComboBox();
    m_plantUMLModeCombo->setToolTip(tr("Enable PlantUML support in Markdown"));
    m_plantUMLModeCombo->addItem(tr("Disabled"), PlantUMLMode::DisablePlantUML);
    m_plantUMLModeCombo->addItem(tr("Online Service"), PlantUMLMode::OnlinePlantUML);
    m_plantUMLModeCombo->addItem(tr("Local JAR"), PlantUMLMode::LocalPlantUML);

    m_plantUMLServerEdit = new VLineEdit();
    m_plantUMLServerEdit->setToolTip(tr("Server address for online PlantUML"));

    m_plantUMLJarEdit = new VLineEdit();
    m_plantUMLJarEdit->setToolTip(tr("Location to the PlantUML JAR executable for local PlantUML"));

    QPushButton *plantUMLJarTestBtn = new QPushButton(tr("Test"));
    plantUMLJarTestBtn->setToolTip(tr("Test PlantUML JAR configuration"));
    connect(plantUMLJarTestBtn, &QPushButton::clicked,
            this, [this]() {
                QString jar = m_plantUMLJarEdit->text();
                if (jar.isEmpty() || !QFileInfo::exists(jar)) {
                    VUtils::showMessage(QMessageBox::Warning,
                                        tr("Warning"),
                                        tr("The JAR file specified does not exist."),
                                        tr("Please input the right absolute file path to the JAR file."),
                                        QMessageBox::Ok,
                                        QMessageBox::Ok,
                                        this);
                    return;
                }

                if (!jar.trimmed().toLower().endsWith(".jar")) {
                    VUtils::showMessage(QMessageBox::Warning,
                                        tr("Warning"),
                                        tr("Please specify the absolute file path to the JAR file."),
                                        tr("It should be something like \"/path/to/plantuml.jar\"."),
                                        QMessageBox::Ok,
                                        QMessageBox::Ok,
                                        this);
                    return;
                }

                QString msg;
                bool ret = VPlantUMLHelper::testPlantUMLJar(jar, msg);
                VUtils::showMessage(QMessageBox::Information,
                                    tr("Information"),
                                    tr("Test %1.").arg((ret ? tr("succeeded") : tr("failed"))),
                                    msg,
                                    QMessageBox::Ok,
                                    QMessageBox::Ok,
                                    this);
            });

    QHBoxLayout *plantUMLLayout = new QHBoxLayout();
    plantUMLLayout->addWidget(m_plantUMLJarEdit);
    plantUMLLayout->addWidget(plantUMLJarTestBtn);

    // Graphviz.
    m_graphvizCB = new QCheckBox(tr("Graphviz"));
    m_graphvizCB->setToolTip(tr("Enable Graphviz for drawing graph"));

    m_graphvizDotEdit = new VLineEdit();
    m_graphvizDotEdit->setPlaceholderText(tr("Empty to detect automatically"));
    m_graphvizDotEdit->setToolTip(tr("Location to the GraphViz dot executable"));

    QPushButton *graphvizTestBtn = new QPushButton(tr("Test"));
    graphvizTestBtn->setToolTip(tr("Test Graphviz executable configuration"));
    connect(graphvizTestBtn, &QPushButton::clicked,
            this, [this]() {
                QString dot = m_graphvizDotEdit->text();
                if (dot.isEmpty()) {
                    dot = "dot";
                }

                QString msg;
                bool ret = VGraphvizHelper::testGraphviz(dot, msg);
                VUtils::showMessage(QMessageBox::Information,
                                    tr("Information"),
                                    tr("Test %1.").arg((ret ? tr("succeeded") : tr("failed"))),
                                    msg,
                                    QMessageBox::Ok,
                                    QMessageBox::Ok,
                                    this);
            });

    QHBoxLayout *graphvizLayout = new QHBoxLayout();
    graphvizLayout->addWidget(m_graphvizDotEdit);
    graphvizLayout->addWidget(graphvizTestBtn);

    QFormLayout *mainLayout = new QFormLayout();
    mainLayout->addRow(tr("Open mode:"), m_openModeCombo);
    mainLayout->addRow(tr("Heading sequence:"), headingSequenceLayout);
    mainLayout->addRow(colorColumnLabel, m_colorColumnEdit);
    mainLayout->addRow(tr("MathJax configuration:"), m_mathjaxConfigEdit);
    mainLayout->addRow(tr("PlantUML:"), m_plantUMLModeCombo);
    mainLayout->addRow(tr("PlantUML server:"), m_plantUMLServerEdit);
    mainLayout->addRow(tr("PlantUML JAR:"), plantUMLLayout);
    mainLayout->addRow(m_graphvizCB);
    mainLayout->addRow(tr("Graphviz executable:"), graphvizLayout);

    setLayout(mainLayout);
}

bool VMarkdownTab::loadConfiguration()
{
    if (!loadOpenMode()) {
        return false;
    }

    if (!loadHeadingSequence()) {
        return false;
    }

    if (!loadColorColumn()) {
        return false;
    }

    if (!loadMathJax()) {
        return false;
    }

    if (!loadPlantUML()) {
        return false;
    }

    if (!loadGraphviz()) {
        return false;
    }

    return true;
}

bool VMarkdownTab::saveConfiguration()
{
    if (!saveOpenMode()) {
        return false;
    }

    if (!saveHeadingSequence()) {
        return false;
    }

    if (!saveColorColumn()) {
        return false;
    }

    if (!saveMathJax()) {
        return false;
    }

    if (!savePlantUML()) {
        return false;
    }

    if (!saveGraphviz()) {
        return false;
    }

    return true;
}

bool VMarkdownTab::loadOpenMode()
{
    int mode = (int)g_config->getNoteOpenMode();
    bool found = false;
    for (int i = 0; i < m_openModeCombo->count(); ++i) {
        if (m_openModeCombo->itemData(i).toInt() == mode) {
            m_openModeCombo->setCurrentIndex(i);
            found = true;
            break;
        }
    }

    Q_ASSERT(found);
    return true;
}

bool VMarkdownTab::saveOpenMode()
{
    int mode = m_openModeCombo->currentData().toInt();
    g_config->setNoteOpenMode((OpenFileMode)mode);
    return true;
}

bool VMarkdownTab::loadHeadingSequence()
{
    HeadingSequenceType type = g_config->getHeadingSequenceType();
    int level = g_config->getHeadingSequenceBaseLevel();
    if (level < 1 || level > 6) {
        level = 1;
    }

    int idx = m_headingSequenceTypeCombo->findData((int)type);
    Q_ASSERT(idx > -1);
    m_headingSequenceTypeCombo->setCurrentIndex(idx);
    m_headingSequenceLevelCombo->setCurrentIndex(level - 1);
    m_headingSequenceLevelCombo->setEnabled(type != HeadingSequenceType::Disabled);

    return true;
}

bool VMarkdownTab::saveHeadingSequence()
{
    QVariant typeData = m_headingSequenceTypeCombo->currentData();
    Q_ASSERT(typeData.isValid());
    g_config->setHeadingSequenceType((HeadingSequenceType)typeData.toInt());
    g_config->setHeadingSequenceBaseLevel(m_headingSequenceLevelCombo->currentData().toInt());

    return true;
}

bool VMarkdownTab::loadColorColumn()
{
    int colorColumn = g_config->getColorColumn();
    m_colorColumnEdit->setText(QString::number(colorColumn <= 0 ? 0 : colorColumn));
    return true;
}

bool VMarkdownTab::saveColorColumn()
{
    bool ok = false;
    int colorColumn = m_colorColumnEdit->text().toInt(&ok);
    if (ok && colorColumn >= 0) {
        g_config->setColorColumn(colorColumn);
    }

    return true;
}

bool VMarkdownTab::loadMathJax()
{
    m_mathjaxConfigEdit->setText(g_config->getMathjaxJavascript());
    return true;
}

bool VMarkdownTab::saveMathJax()
{
    g_config->setMathjaxJavascript(m_mathjaxConfigEdit->text());
    return true;
}

bool VMarkdownTab::loadPlantUML()
{
    m_plantUMLModeCombo->setCurrentIndex(m_plantUMLModeCombo->findData(g_config->getPlantUMLMode()));
    m_plantUMLServerEdit->setText(g_config->getPlantUMLServer());
    m_plantUMLJarEdit->setText(g_config->getPlantUMLJar());
    return true;
}

bool VMarkdownTab::savePlantUML()
{
    g_config->setPlantUMLMode(m_plantUMLModeCombo->currentData().toInt());
    g_config->setPlantUMLServer(m_plantUMLServerEdit->text());
    g_config->setPlantUMLJar(m_plantUMLJarEdit->text());
    return true;
}

bool VMarkdownTab::loadGraphviz()
{
    m_graphvizCB->setChecked(g_config->getEnableGraphviz());
    m_graphvizDotEdit->setText(g_config->getGraphvizDot());
    return true;
}

bool VMarkdownTab::saveGraphviz()
{
    g_config->setEnableGraphviz(m_graphvizCB->isChecked());
    g_config->setGraphvizDot(m_graphvizDotEdit->text());
    return true;
}

VMiscTab::VMiscTab(QWidget *p_parent)
    : QWidget(p_parent)
{
    m_matchesInPageCB = new QCheckBox(tr("Highlight matches of a full-text search in page"),
                                      this);

    QFormLayout *mainLayout = new QFormLayout();
    mainLayout->addRow(m_matchesInPageCB);

    setLayout(mainLayout);
}

bool VMiscTab::loadConfiguration()
{
    if (!loadMatchesInPage()) {
        return false;
    }

    return true;
}

bool VMiscTab::saveConfiguration()
{
    if (!saveMatchesInPage()) {
        return false;
    }

    return true;
}

bool VMiscTab::loadMatchesInPage()
{
    m_matchesInPageCB->setChecked(g_config->getHighlightMatchesInPage());
    return true;
}

bool VMiscTab::saveMatchesInPage()
{
    g_config->setHighlightMatchesInPage(m_matchesInPageCB->isChecked());
    return true;
}
