Files
novelWriter/novelwriter/dialogs/preferences.py
T
2024-04-03 20:25:54 +02:00

967 lines
36 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
novelWriter GUI Preferences
=============================
File History:
Created: 2019-06-10 [0.1.5] GuiPreferences
Rewritten: 2024-01-08 [2.3b1] GuiPreferences
This file is a part of novelWriter
Copyright 20182024, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QFont, QKeyEvent, QKeySequence
from PyQt5.QtWidgets import (
QAbstractButton, QApplication, QCompleter, QDialog, QDialogButtonBox,
QFileDialog, QFontDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout,
QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwConst, nwUnicode
from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NIconToolButton, NSpinBox
from novelwriter.extensions.pagedsidebar import NPagedSideBar
from novelwriter.extensions.switch import NSwitch
from novelwriter.types import (
QtAlignCenter, QtDialogApply, QtDialogClose, QtDialogSave, QtRoleAccept,
QtRoleApply, QtRoleReject
)
logger = logging.getLogger(__name__)
class GuiPreferences(QDialog):
newPreferencesReady = pyqtSignal(bool, bool, bool, bool)
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
logger.debug("Create: GuiPreferences")
self.setObjectName("GuiPreferences")
self.setWindowTitle(self.tr("Preferences"))
self.setMinimumSize(CONFIG.pxInt(600), CONFIG.pxInt(500))
self.resize(*CONFIG.preferencesWinSize)
# Title
self.titleLabel = NColourLabel(
self.tr("Preferences"), SHARED.theme.helpText,
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
)
# Search Box
self.searchText = QLineEdit(self)
self.searchText.setPlaceholderText(self.tr("Search"))
self.searchText.setMinimumWidth(CONFIG.pxInt(200))
self.searchAction = self.searchText.addAction(
SHARED.theme.getIcon("search"), QLineEdit.ActionPosition.TrailingPosition
)
self.searchAction.triggered.connect(self._gotoSearch)
# SideBar
self.sidebar = NPagedSideBar(self)
self.sidebar.setLabelColor(SHARED.theme.helpText)
self.sidebar.buttonClicked.connect(self._sidebarClicked)
# Form
self.mainForm = NScrollableForm(self)
self.mainForm.setHelpTextStyle(SHARED.theme.helpText)
# Buttons
self.buttonBox = QDialogButtonBox(QtDialogApply | QtDialogSave | QtDialogClose)
self.buttonBox.clicked.connect(self._dialogButtonClicked)
# Assemble
self.searchBox = QHBoxLayout()
self.searchBox.addWidget(self.titleLabel)
self.searchBox.addStretch(1)
self.searchBox.addWidget(self.searchText, 1)
self.mainBox = QHBoxLayout()
self.mainBox.addWidget(self.sidebar)
self.mainBox.addWidget(self.mainForm)
self.mainBox.setContentsMargins(0, 0, 0, 0)
self.outerBox = QVBoxLayout()
self.outerBox.addLayout(self.searchBox)
self.outerBox.addLayout(self.mainBox)
self.outerBox.addWidget(self.buttonBox)
self.outerBox.setSpacing(CONFIG.pxInt(8))
self.setLayout(self.outerBox)
self.setSizeGripEnabled(True)
# Build Form
self.buildForm()
# Populate Search
self.searchCompleter = QCompleter(self.mainForm.labels, self)
self.searchCompleter.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
self.searchCompleter.setFilterMode(Qt.MatchFlag.MatchContains)
self.searchCompleter.activated.connect(self._gotoSearch)
self.searchText.setCompleter(self.searchCompleter)
logger.debug("Ready: GuiPreferences")
return
def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: GuiPreferences")
return
def buildForm(self) -> None:
"""Build the settings form."""
section = 0
iSz = SHARED.theme.baseIconSize
minWidth = CONFIG.pxInt(200)
# Label
self.sidebar.addLabel(self.tr("General"))
# Appearance
# ==========
title = self.tr("Appearance")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Display Language
self.guiLocale = NComboBox(self)
self.guiLocale.setMinimumWidth(minWidth)
for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW):
self.guiLocale.addItem(name, lang)
self.guiLocale.setCurrentData(CONFIG.guiLocale, "en_GB")
self.mainForm.addRow(
self.tr("Display language"), self.guiLocale,
self.tr("Requires restart to take effect."), stretch=(3, 2)
)
# Colour Theme
self.guiTheme = NComboBox(self)
self.guiTheme.setMinimumWidth(minWidth)
for theme, name in SHARED.theme.listThemes():
self.guiTheme.addItem(name, theme)
self.guiTheme.setCurrentData(CONFIG.guiTheme, "default")
self.mainForm.addRow(
self.tr("Colour theme"), self.guiTheme,
self.tr("General colour theme and icons."), stretch=(3, 2)
)
# Application Font Family
self.guiFont = QLineEdit(self)
self.guiFont.setReadOnly(True)
self.guiFont.setMinimumWidth(CONFIG.pxInt(162))
self.guiFont.setText(CONFIG.guiFont)
self.guiFontButton = NIconToolButton(self, iSz, "more")
self.guiFontButton.clicked.connect(self._selectGuiFont)
self.mainForm.addRow(
self.tr("Application font family"), self.guiFont,
self.tr("Requires restart to take effect."), stretch=(3, 2),
button=self.guiFontButton
)
# Application Font Size
self.guiFontSize = NSpinBox(self)
self.guiFontSize.setMinimum(8)
self.guiFontSize.setMaximum(60)
self.guiFontSize.setSingleStep(1)
self.guiFontSize.setValue(CONFIG.guiFontSize)
self.mainForm.addRow(
self.tr("Application font size"), self.guiFontSize,
self.tr("Requires restart to take effect."), unit=self.tr("pt")
)
# Vertical Scrollbars
self.hideVScroll = NSwitch(self)
self.hideVScroll.setChecked(CONFIG.hideVScroll)
self.mainForm.addRow(
self.tr("Hide vertical scroll bars in main windows"), self.hideVScroll,
self.tr("Scrolling available with mouse wheel and keys only.")
)
# Horizontal Scrollbars
self.hideHScroll = NSwitch(self)
self.hideHScroll.setChecked(CONFIG.hideHScroll)
self.mainForm.addRow(
self.tr("Hide horizontal scroll bars in main windows"), self.hideHScroll,
self.tr("Scrolling available with mouse wheel and keys only.")
)
# Document Style
# ==============
title = self.tr("Document Style")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Document Colour Theme
self.guiSyntax = NComboBox(self)
self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200))
for syntax, name in SHARED.theme.listSyntax():
self.guiSyntax.addItem(name, syntax)
self.guiSyntax.setCurrentData(CONFIG.guiSyntax, "default_light")
self.mainForm.addRow(
self.tr("Document colour theme"), self.guiSyntax,
self.tr("Colour theme for the editor and viewer."), stretch=(3, 2)
)
# Document Font Family
self.textFont = QLineEdit(self)
self.textFont.setReadOnly(True)
self.textFont.setMinimumWidth(CONFIG.pxInt(162))
self.textFont.setText(CONFIG.textFont)
self.textFontButton = NIconToolButton(self, iSz, "more")
self.textFontButton.clicked.connect(self._selectTextFont)
self.mainForm.addRow(
self.tr("Document font family"), self.textFont,
self.tr("Applies to both document editor and viewer."), stretch=(3, 2),
button=self.textFontButton
)
# Document Font Size
self.textSize = NSpinBox(self)
self.textSize.setMinimum(8)
self.textSize.setMaximum(60)
self.textSize.setSingleStep(1)
self.textSize.setValue(CONFIG.textSize)
self.mainForm.addRow(
self.tr("Document font size"), self.textSize,
self.tr("Applies to both document editor and viewer."), unit=self.tr("pt")
)
# Emphasise Labels
self.emphLabels = NSwitch(self)
self.emphLabels.setChecked(CONFIG.emphLabels)
self.mainForm.addRow(
self.tr("Emphasise partition and chapter labels"), self.emphLabels,
self.tr("Makes them stand out in the project tree."),
)
# Document Path
self.showFullPath = NSwitch(self)
self.showFullPath.setChecked(CONFIG.showFullPath)
self.mainForm.addRow(
self.tr("Show full path in document header"), self.showFullPath,
self.tr("Add the parent folder names to the header.")
)
# Include Notes in Word Count
self.incNotesWCount = NSwitch(self)
self.incNotesWCount.setChecked(CONFIG.incNotesWCount)
self.mainForm.addRow(
self.tr("Include project notes in status bar word count"), self.incNotesWCount
)
# Auto Save
# =========
title = self.tr("Auto Save")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Document Save Timer
self.autoSaveDoc = NSpinBox(self)
self.autoSaveDoc.setMinimum(5)
self.autoSaveDoc.setMaximum(600)
self.autoSaveDoc.setSingleStep(1)
self.autoSaveDoc.setValue(CONFIG.autoSaveDoc)
self.mainForm.addRow(
self.tr("Save document interval"), self.autoSaveDoc,
self.tr("How often the document is automatically saved."), unit=self.tr("seconds")
)
# Project Save Timer
self.autoSaveProj = NSpinBox(self)
self.autoSaveProj.setMinimum(5)
self.autoSaveProj.setMaximum(600)
self.autoSaveProj.setSingleStep(1)
self.autoSaveProj.setValue(CONFIG.autoSaveProj)
self.mainForm.addRow(
self.tr("Save project interval"), self.autoSaveProj,
self.tr("How often the project is automatically saved."), unit=self.tr("seconds")
)
# Project Backup
# ==============
title = self.tr("Project Backup")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Backup Path
self.backupPath = CONFIG.backupPath()
self.backupGetPath = QPushButton(SHARED.theme.getIcon("browse"), self.tr("Browse"), self)
self.backupGetPath.setIconSize(iSz)
self.backupGetPath.clicked.connect(self._backupFolder)
self.mainForm.addRow(
self.tr("Backup storage location"), self.backupGetPath,
self.tr("Path: {0}").format(self.backupPath), editable="backupPath"
)
# Run When Closing
self.backupOnClose = NSwitch(self)
self.backupOnClose.setChecked(CONFIG.backupOnClose)
self.backupOnClose.toggled.connect(self._toggledBackupOnClose)
self.mainForm.addRow(
self.tr("Run backup when the project is closed"), self.backupOnClose,
self.tr("Can be overridden for individual projects in Project Settings.")
)
# Ask Before Backup
# Only enabled when "Run when closing" is checked
self.askBeforeBackup = NSwitch(self)
self.askBeforeBackup.setChecked(CONFIG.askBeforeBackup)
self.askBeforeBackup.setEnabled(CONFIG.backupOnClose)
self.mainForm.addRow(
self.tr("Ask before running backup"), self.askBeforeBackup,
self.tr("If off, backups will run in the background.")
)
# Session Timer
# =============
title = self.tr("Session Timer")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Pause When Idle
self.stopWhenIdle = NSwitch(self)
self.stopWhenIdle.setChecked(CONFIG.stopWhenIdle)
self.mainForm.addRow(
self.tr("Pause the session timer when not writing"), self.stopWhenIdle,
self.tr("Also pauses when the application window does not have focus.")
)
# Inactive Time for Idle
self.userIdleTime = NDoubleSpinBox(self)
self.userIdleTime.setMinimum(0.5)
self.userIdleTime.setMaximum(600.0)
self.userIdleTime.setSingleStep(0.5)
self.userIdleTime.setDecimals(1)
self.userIdleTime.setValue(CONFIG.userIdleTime/60.0)
self.mainForm.addRow(
self.tr("Editor inactive time before pausing timer"), self.userIdleTime,
self.tr("User activity includes typing and changing the content."),
unit=self.tr("minutes")
)
# Label
self.sidebar.addLabel(self.tr("Writing"))
# Text Flow
# =========
title = self.tr("Text Flow")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Max Text Width in Normal Mode
self.textWidth = NSpinBox(self)
self.textWidth.setMinimum(0)
self.textWidth.setMaximum(10000)
self.textWidth.setSingleStep(10)
self.textWidth.setValue(CONFIG.textWidth)
self.mainForm.addRow(
self.tr("Maximum text width in \"Normal Mode\""), self.textWidth,
self.tr("Set to 0 to disable this feature."), unit=self.tr("px")
)
# Max Text Width in Focus Mode
self.focusWidth = NSpinBox(self)
self.focusWidth.setMinimum(200)
self.focusWidth.setMaximum(10000)
self.focusWidth.setSingleStep(10)
self.focusWidth.setValue(CONFIG.focusWidth)
self.mainForm.addRow(
self.tr("Maximum text width in \"Focus Mode\""), self.focusWidth,
self.tr("The maximum width cannot be disabled."), unit=self.tr("px")
)
# Focus Mode Footer
self.hideFocusFooter = NSwitch(self)
self.hideFocusFooter.setChecked(CONFIG.hideFocusFooter)
self.mainForm.addRow(
self.tr("Hide document footer in \"Focus Mode\""), self.hideFocusFooter,
self.tr("Hide the information bar in the document editor.")
)
# Justify Text
self.doJustify = NSwitch(self)
self.doJustify.setChecked(CONFIG.doJustify)
self.mainForm.addRow(
self.tr("Justify the text margins"), self.doJustify,
self.tr("Applies to both document editor and viewer."),
)
# Document Margins
self.textMargin = NSpinBox(self)
self.textMargin.setMinimum(0)
self.textMargin.setMaximum(900)
self.textMargin.setSingleStep(1)
self.textMargin.setValue(CONFIG.textMargin)
self.mainForm.addRow(
self.tr("Minimum text margin"), self.textMargin,
self.tr("Applies to both document editor and viewer."),
unit=self.tr("px")
)
# Tab Width
self.tabWidth = NSpinBox(self)
self.tabWidth.setMinimum(0)
self.tabWidth.setMaximum(200)
self.tabWidth.setSingleStep(1)
self.tabWidth.setValue(CONFIG.tabWidth)
self.mainForm.addRow(
self.tr("Tab width"), self.tabWidth,
self.tr("The width of a tab key press in the editor and viewer."),
unit=self.tr("px")
)
# Text Editing
# ============
title = self.tr("Text Editing")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Spell Checking
self.spellLanguage = NComboBox(self)
self.spellLanguage.setMinimumWidth(minWidth)
if CONFIG.hasEnchant:
for tag, language in SHARED.spelling.listDictionaries():
self.spellLanguage.addItem(language, tag)
else:
self.spellLanguage.addItem(nwUnicode.U_EMDASH, "")
self.spellLanguage.setEnabled(False)
if (idx := self.spellLanguage.findData(CONFIG.spellLanguage)) != -1:
self.spellLanguage.setCurrentIndex(idx)
self.mainForm.addRow(
self.tr("Spell check language"), self.spellLanguage,
self.tr("Available languages are determined by your system."), stretch=(3, 2)
)
# Auto-Select Word Under Cursor
self.autoSelect = NSwitch(self)
self.autoSelect.setChecked(CONFIG.autoSelect)
self.mainForm.addRow(
self.tr("Auto-select word under cursor"), self.autoSelect,
self.tr("Apply formatting to word under cursor if no selection is made.")
)
# Show Tabs and Spaces
self.showTabsNSpaces = NSwitch(self)
self.showTabsNSpaces.setChecked(CONFIG.showTabsNSpaces)
self.mainForm.addRow(
self.tr("Show tabs and spaces"), self.showTabsNSpaces
)
# Show Line Endings
self.showLineEndings = NSwitch(self)
self.showLineEndings.setChecked(CONFIG.showLineEndings)
self.mainForm.addRow(
self.tr("Show line endings"), self.showLineEndings
)
# Editor Scrolling
# ================
title = self.tr("Editor Scrolling")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Scroll Past End
self.scrollPastEnd = NSwitch(self)
self.scrollPastEnd.setChecked(CONFIG.scrollPastEnd)
self.mainForm.addRow(
self.tr("Scroll past end of the document"), self.scrollPastEnd,
self.tr("Also centres the cursor when scrolling.")
)
# Typewriter Scrolling
self.autoScroll = NSwitch(self)
self.autoScroll.setChecked(CONFIG.autoScroll)
self.mainForm.addRow(
self.tr("Typewriter style scrolling when you type"), self.autoScroll,
self.tr("Keeps the cursor at a fixed vertical position.")
)
# Typewriter Position
self.autoScrollPos = NSpinBox(self)
self.autoScrollPos.setMinimum(10)
self.autoScrollPos.setMaximum(90)
self.autoScrollPos.setSingleStep(1)
self.autoScrollPos.setValue(int(CONFIG.autoScrollPos))
self.mainForm.addRow(
self.tr("Minimum position for Typewriter scrolling"), self.autoScrollPos,
self.tr("Percentage of the editor height from the top."), unit="%"
)
# Text Highlighting
# =================
title = self.tr("Text Highlighting")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
self.highlightQuotes = NSwitch(self)
self.highlightQuotes.setChecked(CONFIG.highlightQuotes)
self.highlightQuotes.toggled.connect(self._toggleHighlightQuotes)
self.mainForm.addRow(
self.tr("Highlight text wrapped in quotes"), self.highlightQuotes,
self.tr("Applies to the document editor only.")
)
self.allowOpenSQuote = NSwitch(self)
self.allowOpenSQuote.setChecked(CONFIG.allowOpenSQuote)
self.mainForm.addRow(
self.tr("Allow open-ended single quotes"), self.allowOpenSQuote,
self.tr("Highlight single-quoted line with no closing quote.")
)
self.allowOpenDQuote = NSwitch(self)
self.allowOpenDQuote.setChecked(CONFIG.allowOpenDQuote)
self.mainForm.addRow(
self.tr("Allow open-ended double quotes"), self.allowOpenDQuote,
self.tr("Highlight double-quoted line with no closing quote.")
)
self.highlightEmph = NSwitch(self)
self.highlightEmph.setChecked(CONFIG.highlightEmph)
self.mainForm.addRow(
self.tr("Add highlight colour to emphasised text"), self.highlightEmph,
self.tr("Applies to the document editor only.")
)
self.showMultiSpaces = NSwitch(self)
self.showMultiSpaces.setChecked(CONFIG.showMultiSpaces)
self.mainForm.addRow(
self.tr("Highlight multiple or trailing spaces"), self.showMultiSpaces,
self.tr("Applies to the document editor only.")
)
# Text Automation
# ===============
title = self.tr("Text Automation")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
boxWidth = CONFIG.pxInt(150)
# Auto-Replace as You Type Main Switch
self.doReplace = NSwitch(self)
self.doReplace.setChecked(CONFIG.doReplace)
self.doReplace.toggled.connect(self._toggleAutoReplaceMain)
self.mainForm.addRow(
self.tr("Auto-replace text as you type"), self.doReplace,
self.tr("Allow the editor to replace symbols as you type.")
)
# Auto-Replace Single Quotes
self.doReplaceSQuote = NSwitch(self)
self.doReplaceSQuote.setChecked(CONFIG.doReplaceSQuote)
self.doReplaceSQuote.setEnabled(CONFIG.doReplace)
self.mainForm.addRow(
self.tr("Auto-replace single quotes"), self.doReplaceSQuote,
self.tr("Try to guess which is an opening or a closing quote.")
)
# Auto-Replace Double Quotes
self.doReplaceDQuote = NSwitch(self)
self.doReplaceDQuote.setChecked(CONFIG.doReplaceDQuote)
self.doReplaceDQuote.setEnabled(CONFIG.doReplace)
self.mainForm.addRow(
self.tr("Auto-replace double quotes"), self.doReplaceDQuote,
self.tr("Try to guess which is an opening or a closing quote.")
)
# Auto-Replace Hyphens
self.doReplaceDash = NSwitch(self)
self.doReplaceDash.setChecked(CONFIG.doReplaceDash)
self.doReplaceDash.setEnabled(CONFIG.doReplace)
self.mainForm.addRow(
self.tr("Auto-replace dashes"), self.doReplaceDash,
self.tr("Double and triple hyphens become short and long dashes.")
)
# Auto-Replace Dots
self.doReplaceDots = NSwitch(self)
self.doReplaceDots.setChecked(CONFIG.doReplaceDots)
self.doReplaceDots.setEnabled(CONFIG.doReplace)
self.mainForm.addRow(
self.tr("Auto-replace dots"), self.doReplaceDots,
self.tr("Three consecutive dots become ellipsis.")
)
# Pad Before
self.fmtPadBefore = QLineEdit(self)
self.fmtPadBefore.setMaxLength(32)
self.fmtPadBefore.setMinimumWidth(boxWidth)
self.fmtPadBefore.setText(CONFIG.fmtPadBefore)
self.mainForm.addRow(
self.tr("Insert non-breaking space before"), self.fmtPadBefore,
self.tr("Automatically add space before any of these symbols."), stretch=(2, 1)
)
# Pad After
self.fmtPadAfter = QLineEdit(self)
self.fmtPadAfter.setMaxLength(32)
self.fmtPadAfter.setMinimumWidth(boxWidth)
self.fmtPadAfter.setText(CONFIG.fmtPadAfter)
self.mainForm.addRow(
self.tr("Insert non-breaking space after"), self.fmtPadAfter,
self.tr("Automatically add space after any of these symbols."), stretch=(2, 1)
)
# Use Thin Space
self.fmtPadThin = NSwitch(self)
self.fmtPadThin.setChecked(CONFIG.fmtPadThin)
self.fmtPadThin.setEnabled(CONFIG.doReplace)
self.mainForm.addRow(
self.tr("Use thin space instead"), self.fmtPadThin,
self.tr("Inserts a thin space instead of a regular space.")
)
# Quotation Style
# ===============
title = self.tr("Quotation Style")
section += 1
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
self.quoteSym = {}
qWidth = CONFIG.pxInt(40)
# Single Quote Style
self.quoteSym["SO"] = QLineEdit(self)
self.quoteSym["SO"].setMaxLength(1)
self.quoteSym["SO"].setReadOnly(True)
self.quoteSym["SO"].setFixedWidth(qWidth)
self.quoteSym["SO"].setAlignment(QtAlignCenter)
self.quoteSym["SO"].setText(CONFIG.fmtSQuoteOpen)
self.btnSingleStyleO = NIconToolButton(self, iSz, "more")
self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
self.mainForm.addRow(
self.tr("Single quote open style"), self.quoteSym["SO"],
self.tr("The symbol to use for a leading single quote."),
button=self.btnSingleStyleO
)
self.quoteSym["SC"] = QLineEdit(self)
self.quoteSym["SC"].setMaxLength(1)
self.quoteSym["SC"].setReadOnly(True)
self.quoteSym["SC"].setFixedWidth(qWidth)
self.quoteSym["SC"].setAlignment(QtAlignCenter)
self.quoteSym["SC"].setText(CONFIG.fmtSQuoteClose)
self.btnSingleStyleC = NIconToolButton(self, iSz, "more")
self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
self.mainForm.addRow(
self.tr("Single quote close style"), self.quoteSym["SC"],
self.tr("The symbol to use for a trailing single quote."),
button=self.btnSingleStyleC
)
# Double Quote Style
self.quoteSym["DO"] = QLineEdit(self)
self.quoteSym["DO"].setMaxLength(1)
self.quoteSym["DO"].setReadOnly(True)
self.quoteSym["DO"].setFixedWidth(qWidth)
self.quoteSym["DO"].setAlignment(QtAlignCenter)
self.quoteSym["DO"].setText(CONFIG.fmtDQuoteOpen)
self.btnDoubleStyleO = NIconToolButton(self, iSz, "more")
self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
self.mainForm.addRow(
self.tr("Double quote open style"), self.quoteSym["DO"],
self.tr("The symbol to use for a leading double quote."),
button=self.btnDoubleStyleO
)
self.quoteSym["DC"] = QLineEdit(self)
self.quoteSym["DC"].setMaxLength(1)
self.quoteSym["DC"].setReadOnly(True)
self.quoteSym["DC"].setFixedWidth(qWidth)
self.quoteSym["DC"].setAlignment(QtAlignCenter)
self.quoteSym["DC"].setText(CONFIG.fmtDQuoteClose)
self.btnDoubleStyleC = NIconToolButton(self, iSz, "more")
self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
self.mainForm.addRow(
self.tr("Double quote close style"), self.quoteSym["DC"],
self.tr("The symbol to use for a trailing double quote."),
button=self.btnDoubleStyleC
)
self.mainForm.finalise()
self.sidebar.setSelected(1)
return
##
# Events
##
def closeEvent(self, event: QCloseEvent) -> None:
"""Capture the close event and perform cleanup."""
logger.debug("Close: GuiPreferences")
self._saveWindowSize()
event.accept()
QApplication.processEvents()
self.done(nwConst.DLG_FINISHED)
self.deleteLater()
return
def keyPressEvent(self, event: QKeyEvent) -> None:
"""Overload keyPressEvent to block enter key to save."""
if event.matches(QKeySequence.StandardKey.Cancel):
self.close()
event.ignore()
return
##
# Private Slots
##
@pyqtSlot("QAbstractButton*")
def _dialogButtonClicked(self, button: QAbstractButton) -> None:
"""Handle button clicks from the dialog button box."""
role = self.buttonBox.buttonRole(button)
if role == QtRoleApply:
self._saveValues()
elif role == QtRoleAccept:
self._saveValues()
self.close()
elif role == QtRoleReject:
self.close()
return
@pyqtSlot(int)
def _sidebarClicked(self, section: int) -> None:
"""Process a user request to switch page."""
self.mainForm.scrollToSection(section)
return
@pyqtSlot()
def _gotoSearch(self) -> None:
"""Go to the setting indicated by the search text."""
self.mainForm.scrollToLabel(self.searchText.text().strip())
return
@pyqtSlot()
def _selectGuiFont(self) -> None:
"""Open the QFontDialog and set a font for the font style."""
current = QFont()
current.setFamily(CONFIG.guiFont)
current.setPointSize(CONFIG.guiFontSize)
font, status = QFontDialog.getFont(current, self)
if status:
self.guiFont.setText(font.family())
self.guiFontSize.setValue(font.pointSize())
return
@pyqtSlot()
def _selectTextFont(self):
"""Open the QFontDialog and set a font for the font style."""
current = QFont()
current.setFamily(CONFIG.textFont)
current.setPointSize(CONFIG.textSize)
font, status = QFontDialog.getFont(current, self)
if status:
self.textFont.setText(font.family())
self.textSize.setValue(font.pointSize())
return
@pyqtSlot()
def _backupFolder(self) -> None:
"""Open a dialog to select the backup folder."""
if path := QFileDialog.getExistingDirectory(
self, self.tr("Backup Directory"), str(self.backupPath) or "",
options=QFileDialog.ShowDirsOnly
):
self.backupPath = path
self.mainForm.setHelpText("backupPath", self.tr("Path: {0}").format(path))
return
@pyqtSlot(bool)
def _toggledBackupOnClose(self, state: bool) -> None:
"""Toggle switch that depends on the backup on close switch."""
self.askBeforeBackup.setEnabled(state)
return
@pyqtSlot(bool)
def _toggleHighlightQuotes(self, state: bool) -> None:
"""Toggle switches controlled by the highlight quotes switch."""
self.allowOpenSQuote.setEnabled(state)
self.allowOpenDQuote.setEnabled(state)
return
@pyqtSlot(bool)
def _toggleAutoReplaceMain(self, state: bool) -> None:
"""Toggle switches controlled by the auto replace switch."""
self.doReplaceSQuote.setEnabled(state)
self.doReplaceDQuote.setEnabled(state)
self.doReplaceDash.setEnabled(state)
self.doReplaceDots.setEnabled(state)
self.fmtPadThin.setEnabled(state)
return
def _getQuote(self, qType: str) -> None:
"""Dialog for single quote open."""
quote, status = GuiQuoteSelect.getQuote(self, current=self.quoteSym[qType].text())
if status:
self.quoteSym[qType].setText(quote)
return
##
# Internal Functions
##
def _saveWindowSize(self) -> None:
"""Save the dialog window size."""
CONFIG.setPreferencesWinSize(self.width(), self.height())
return
def _saveValues(self) -> None:
"""Save the values set in the form."""
updateTheme = False
needsRestart = False
updateSyntax = False
refreshTree = False
# Appearance
guiLocale = self.guiLocale.currentData()
guiTheme = self.guiTheme.currentData()
guiFont = self.guiFont.text()
guiFontSize = self.guiFontSize.value()
updateTheme |= CONFIG.guiTheme != guiTheme
needsRestart |= CONFIG.guiLocale != guiLocale
needsRestart |= CONFIG.guiFont != guiFont
needsRestart |= CONFIG.guiFontSize != guiFontSize
CONFIG.guiLocale = guiLocale
CONFIG.guiTheme = guiTheme
CONFIG.guiFont = guiFont
CONFIG.guiFontSize = guiFontSize
CONFIG.hideVScroll = self.hideVScroll.isChecked()
CONFIG.hideHScroll = self.hideHScroll.isChecked()
# Document Style
guiSyntax = self.guiSyntax.currentData()
emphLabels = self.emphLabels.isChecked()
updateSyntax |= CONFIG.guiSyntax != guiSyntax
refreshTree |= CONFIG.emphLabels != emphLabels
CONFIG.guiSyntax = guiSyntax
CONFIG.emphLabels = emphLabels
CONFIG.showFullPath = self.showFullPath.isChecked()
CONFIG.incNotesWCount = self.incNotesWCount.isChecked()
CONFIG.setTextFont(self.textFont.text(), self.textSize.value())
# Auto Save
CONFIG.autoSaveDoc = self.autoSaveDoc.value()
CONFIG.autoSaveProj = self.autoSaveProj.value()
# Project Backup
CONFIG.setBackupPath(self.backupPath)
CONFIG.backupOnClose = self.backupOnClose.isChecked()
CONFIG.askBeforeBackup = self.askBeforeBackup.isChecked()
# Session Timer
CONFIG.stopWhenIdle = self.stopWhenIdle.isChecked()
CONFIG.userIdleTime = round(self.userIdleTime.value() * 60)
# Text Flow
CONFIG.textWidth = self.textWidth.value()
CONFIG.focusWidth = self.focusWidth.value()
CONFIG.hideFocusFooter = self.hideFocusFooter.isChecked()
CONFIG.doJustify = self.doJustify.isChecked()
CONFIG.textMargin = self.textMargin.value()
CONFIG.tabWidth = self.tabWidth.value()
# Text Editing
CONFIG.spellLanguage = self.spellLanguage.currentData()
CONFIG.autoSelect = self.autoSelect.isChecked()
CONFIG.showTabsNSpaces = self.showTabsNSpaces.isChecked()
CONFIG.showLineEndings = self.showLineEndings.isChecked()
# Editor Scrolling
CONFIG.autoScroll = self.autoScroll.isChecked()
CONFIG.autoScrollPos = self.autoScrollPos.value()
CONFIG.scrollPastEnd = self.scrollPastEnd.isChecked()
# Text Highlighting
highlightQuotes = self.highlightQuotes.isChecked()
highlightEmph = self.highlightEmph.isChecked()
showMultiSpaces = self.showMultiSpaces.isChecked()
updateSyntax |= CONFIG.highlightQuotes != highlightQuotes
updateSyntax |= CONFIG.highlightEmph != highlightEmph
updateSyntax |= CONFIG.showMultiSpaces != showMultiSpaces
CONFIG.highlightQuotes = highlightQuotes
CONFIG.highlightEmph = highlightEmph
CONFIG.showMultiSpaces = showMultiSpaces
CONFIG.allowOpenSQuote = self.allowOpenSQuote.isChecked()
CONFIG.allowOpenDQuote = self.allowOpenDQuote.isChecked()
# Text Automation
CONFIG.doReplace = self.doReplace.isChecked()
CONFIG.doReplaceSQuote = self.doReplaceSQuote.isChecked()
CONFIG.doReplaceDQuote = self.doReplaceDQuote.isChecked()
CONFIG.doReplaceDash = self.doReplaceDash.isChecked()
CONFIG.doReplaceDots = self.doReplaceDots.isChecked()
CONFIG.fmtPadBefore = self.fmtPadBefore.text().strip()
CONFIG.fmtPadAfter = self.fmtPadAfter.text().strip()
CONFIG.fmtPadThin = self.fmtPadThin.isChecked()
# Quotation Style
CONFIG.fmtSQuoteOpen = self.quoteSym["SO"].text()
CONFIG.fmtSQuoteClose = self.quoteSym["SC"].text()
CONFIG.fmtDQuoteOpen = self.quoteSym["DO"].text()
CONFIG.fmtDQuoteClose = self.quoteSym["DC"].text()
# Finalise
CONFIG.saveConfig()
self.newPreferencesReady.emit(needsRestart, refreshTree, updateTheme, updateSyntax)
return
# END Class GuiPreferences