#include "vsettingsdialog.h" #include #include #include #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(m_tabs->widget(idx++)); Q_ASSERT(generalTab); if (!generalTab->loadConfiguration()) { goto err; } } // Appearance Tab. { VLookTab *lookTab = dynamic_cast(m_tabs->widget(idx++)); Q_ASSERT(lookTab); if (!lookTab->loadConfiguration()) { goto err; } } // Read/Edit Tab. { VReadEditTab *readEditTab = dynamic_cast(m_tabs->widget(idx++)); Q_ASSERT(readEditTab); if (!readEditTab->loadConfiguration()) { goto err; } } // Note Management Tab. { VNoteManagementTab *noteManagementTab = dynamic_cast(m_tabs->widget(idx++)); Q_ASSERT(noteManagementTab); if (!noteManagementTab->loadConfiguration()) { goto err; } } // Markdown Tab. { VMarkdownTab *markdownTab = dynamic_cast(m_tabs->widget(idx++)); Q_ASSERT(markdownTab); if (!markdownTab->loadConfiguration()) { goto err; } } // Misc Tab. { VMiscTab *miscTab = dynamic_cast(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(m_tabs->widget(idx++)); Q_ASSERT(generalTab); if (!generalTab->saveConfiguration()) { goto err; } } // Appearance Tab. { VLookTab *lookTab = dynamic_cast(m_tabs->widget(idx++)); Q_ASSERT(lookTab); if (!lookTab->saveConfiguration()) { goto err; } } // Read/Edit Tab. { VReadEditTab *readEditTab = dynamic_cast(m_tabs->widget(idx++)); Q_ASSERT(readEditTab); if (!readEditTab->saveConfiguration()) { goto err; } } // Note Management Tab. { VNoteManagementTab *noteManagementTab = dynamic_cast(m_tabs->widget(idx++)); Q_ASSERT(noteManagementTab); if (!noteManagementTab->saveConfiguration()) { goto err; } } // Markdown Tab. { VMarkdownTab *markdownTab = dynamic_cast(m_tabs->widget(idx++)); Q_ASSERT(markdownTab); if (!markdownTab->saveConfiguration()) { goto err; } } // Misc Tab. { VMiscTab *miscTab = dynamic_cast(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 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(&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(&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(&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; }