diff --git a/nw/config.py b/nw/config.py
index f65f9554..e7da0533 100644
--- a/nw/config.py
+++ b/nw/config.py
@@ -43,13 +43,14 @@ class Config:
self.appPath = None
self.appRoot = None
self.guiPath = None
+ self.themeRoot = None
self.themePath = None
# Set default values
self.confChanged = False
## General
- self.guiTheme = "default"
+ self.guiTheme = "default_dark"
self.winGeometry = [1100, 650]
self.treeColWidth = [120, 30, 50]
self.mainPanePos = [300, 800]
@@ -63,10 +64,11 @@ class Config:
self.autoSaveDoc = 30
## Text Editor
+ self.textFont = None
+ self.textSize = 12
self.textFixedW = True
self.textWidth = 600
self.textMargin = [40, 40]
- self.textSize = 13
self.tabWidth = 40
self.doJustify = True
self.autoSelect = True
@@ -77,9 +79,8 @@ class Config:
self.doReplaceDots = True
self.wordCountTimer = 5.0
- self.fmtDoubleQuotes = ["“","”"]
self.fmtSingleQuotes = ["‘","’"]
- self.fmtApostrophe = "’"
+ self.fmtDoubleQuotes = ["“","”"]
self.spellLanguage = "en_GB"
@@ -106,7 +107,8 @@ class Config:
self.appRoot = path.join(self.appPath,path.pardir)
self.helpPath = path.join(self.appRoot,"help","en_GB")
self.guiPath = path.join(self.appPath,"gui")
- self.themePath = path.join(self.appPath,"themes")
+ self.themeRoot = path.join(self.appPath,"themes")
+ self.themePath = path.join(self.themeRoot)
# If config folder does not exist, make it.
# This assumes that the os config folder itself exists.
@@ -153,19 +155,22 @@ class Config:
## Editor
cnfSec = "Editor"
- self.textFixedW = self._parseLine(cnfParse, cnfSec, "fixedwidth", self.CNF_BOOL, self.textFixedW)
- self.textWidth = self._parseLine(cnfParse, cnfSec, "width", self.CNF_INT, self.textWidth)
- self.textMargin = self._parseLine(cnfParse, cnfSec, "margins", self.CNF_LIST, self.textMargin)
- self.textSize = self._parseLine(cnfParse, cnfSec, "textsize", self.CNF_INT, self.textSize)
- self.tabWidth = self._parseLine(cnfParse, cnfSec, "tabwidth", self.CNF_INT, self.tabWidth)
- self.doJustify = self._parseLine(cnfParse, cnfSec, "justify", self.CNF_BOOL, self.doJustify)
- self.autoSelect = self._parseLine(cnfParse, cnfSec, "autoselect", self.CNF_BOOL, self.autoSelect)
- self.doReplace = self._parseLine(cnfParse, cnfSec, "autoreplace", self.CNF_BOOL, self.doReplace)
- self.doReplaceSQuote = self._parseLine(cnfParse, cnfSec, "repsquotes", self.CNF_BOOL, self.doReplaceSQuote)
- self.doReplaceDQuote = self._parseLine(cnfParse, cnfSec, "repdquotes", self.CNF_BOOL, self.doReplaceDQuote)
- self.doReplaceDash = self._parseLine(cnfParse, cnfSec, "repdash", self.CNF_BOOL, self.doReplaceDash)
- self.doReplaceDots = self._parseLine(cnfParse, cnfSec, "repdots", self.CNF_BOOL, self.doReplaceDots)
- self.spellLanguage = self._parseLine(cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage)
+ self.textFont = self._parseLine(cnfParse, cnfSec, "textfont", self.CNF_STR, self.textFont)
+ self.textSize = self._parseLine(cnfParse, cnfSec, "textsize", self.CNF_INT, self.textSize)
+ self.textFixedW = self._parseLine(cnfParse, cnfSec, "fixedwidth", self.CNF_BOOL, self.textFixedW)
+ self.textWidth = self._parseLine(cnfParse, cnfSec, "width", self.CNF_INT, self.textWidth)
+ self.textMargin = self._parseLine(cnfParse, cnfSec, "margins", self.CNF_LIST, self.textMargin)
+ self.tabWidth = self._parseLine(cnfParse, cnfSec, "tabwidth", self.CNF_INT, self.tabWidth)
+ self.doJustify = self._parseLine(cnfParse, cnfSec, "justify", self.CNF_BOOL, self.doJustify)
+ self.autoSelect = self._parseLine(cnfParse, cnfSec, "autoselect", self.CNF_BOOL, self.autoSelect)
+ self.doReplace = self._parseLine(cnfParse, cnfSec, "autoreplace", self.CNF_BOOL, self.doReplace)
+ self.doReplaceSQuote = self._parseLine(cnfParse, cnfSec, "repsquotes", self.CNF_BOOL, self.doReplaceSQuote)
+ self.doReplaceDQuote = self._parseLine(cnfParse, cnfSec, "repdquotes", self.CNF_BOOL, self.doReplaceDQuote)
+ self.doReplaceDash = self._parseLine(cnfParse, cnfSec, "repdash", self.CNF_BOOL, self.doReplaceDash)
+ self.doReplaceDots = self._parseLine(cnfParse, cnfSec, "repdots", self.CNF_BOOL, self.doReplaceDots)
+ self.fmtSingleQuotes = self._parseLine(cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes)
+ self.fmtDoubleQuotes = self._parseLine(cnfParse, cnfSec, "fmtdoublequote", self.CNF_LIST, self.fmtDoubleQuotes)
+ self.spellLanguage = self._parseLine(cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage)
## Path
cnfSec = "Path"
@@ -205,19 +210,22 @@ class Config:
## Editor
cnfSec = "Editor"
cnfParse.add_section(cnfSec)
- cnfParse.set(cnfSec,"fixedwidth", str(self.textFixedW))
- cnfParse.set(cnfSec,"width", str(self.textWidth))
- cnfParse.set(cnfSec,"margins", self._packList(self.textMargin))
- cnfParse.set(cnfSec,"textsize", str(self.textSize))
- cnfParse.set(cnfSec,"tabwidth", str(self.tabWidth))
- cnfParse.set(cnfSec,"justify", str(self.doJustify))
- cnfParse.set(cnfSec,"autoselect", str(self.autoSelect))
- cnfParse.set(cnfSec,"autoreplace",str(self.doReplace))
- cnfParse.set(cnfSec,"repsquotes", str(self.doReplaceSQuote))
- cnfParse.set(cnfSec,"repdquotes", str(self.doReplaceDQuote))
- cnfParse.set(cnfSec,"repdash", str(self.doReplaceDash))
- cnfParse.set(cnfSec,"repdots", str(self.doReplaceDots))
- cnfParse.set(cnfSec,"spellcheck", str(self.spellLanguage))
+ cnfParse.set(cnfSec,"textfont", str(self.textFont))
+ cnfParse.set(cnfSec,"textsize", str(self.textSize))
+ cnfParse.set(cnfSec,"fixedwidth", str(self.textFixedW))
+ cnfParse.set(cnfSec,"width", str(self.textWidth))
+ cnfParse.set(cnfSec,"margins", self._packList(self.textMargin))
+ cnfParse.set(cnfSec,"tabwidth", str(self.tabWidth))
+ cnfParse.set(cnfSec,"justify", str(self.doJustify))
+ cnfParse.set(cnfSec,"autoselect", str(self.autoSelect))
+ cnfParse.set(cnfSec,"autoreplace", str(self.doReplace))
+ cnfParse.set(cnfSec,"repsquotes", str(self.doReplaceSQuote))
+ cnfParse.set(cnfSec,"repdquotes", str(self.doReplaceDQuote))
+ cnfParse.set(cnfSec,"repdash", str(self.doReplaceDash))
+ cnfParse.set(cnfSec,"repdots", str(self.doReplaceDots))
+ cnfParse.set(cnfSec,"fmtsinglequote",self._packList(self.fmtSingleQuotes))
+ cnfParse.set(cnfSec,"fmtdoublequote",self._packList(self.fmtDoubleQuotes))
+ cnfParse.set(cnfSec,"spellcheck", str(self.spellLanguage))
## Path
cnfSec = "Path"
diff --git a/nw/constants.py b/nw/constants.py
index 8a545d0c..9c56b012 100644
--- a/nw/constants.py
+++ b/nw/constants.py
@@ -70,3 +70,53 @@ class nwLabels():
}
# END Class nwLabels
+
+class nwQuotes():
+ """Allowed quotation marks.
+ Source: https://en.wikipedia.org/wiki/Quotation_mark
+ """
+
+ SYMBOLS = [
+ "\u0022", # Quotation mark
+ "\u0027", # Apostrophe
+ "\u00ab", # Left-pointing double angle quotation mark
+ "\u00bb", # Right-pointing double angle quotation mark
+ "\u2018", # Left single quotation mark
+ "\u2019", # Right single quotation mark
+ "\u201a", # Single low-9 quotation mark
+ "\u201b", # Single high-reversed-9 quotation mark
+ "\u201c", # Left double quotation mark
+ "\u201d", # Right double quotation mark
+ "\u201e", # Double low-9 quotation mark
+ "\u201f", # Double high-reversed-9 quotation mark
+ "\u2039", # Single left-pointing angle quotation mark
+ "\u203a", # Single right-pointing angle quotation mark
+ "\u2e42", # Double low-reversed-9 quotation mark
+ "\u300c", # Left corner bracket
+ "\u300d", # Right corner bracket
+ "\u300e", # Left white corner bracket
+ "\u300f", # Right white corner bracket
+ ]
+ HTML = {
+ "\u0022" : """,
+ "\u0027" : "'",
+ "\u00ab" : "«",
+ "\u00bb" : "»",
+ "\u2018" : "‘",
+ "\u2019" : "’",
+ "\u201a" : "‚",
+ "\u201b" : "‛",
+ "\u201c" : "“",
+ "\u201d" : "”",
+ "\u201e" : "„",
+ "\u201f" : "‟",
+ "\u2039" : "‹",
+ "\u203a" : "›",
+ "\u2e42" : "⹂",
+ "\u300c" : "「",
+ "\u300d" : "」",
+ "\u300e" : "『",
+ "\u300f" : "』",
+ }
+
+# END Class nwQuotes
\ No newline at end of file
diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py
new file mode 100644
index 00000000..fa1d63f4
--- /dev/null
+++ b/nw/gui/configeditor.py
@@ -0,0 +1,492 @@
+# -*- coding: utf-8 -*-
+"""novelWriter GUI Config Editor
+
+ novelWriter – GUI Config Editor
+=================================
+ Class holding the config dialog
+
+ File History:
+ Created: 2019-06-10 [0.1.5]
+
+"""
+
+import logging
+import enchant
+import nw
+
+from os import path
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel, QFont
+from PyQt5.QtSvg import QSvgWidget
+from PyQt5.QtWidgets import (
+ QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel,
+ QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox,
+ QCheckBox, QGridLayout, QFontComboBox
+)
+from nw.enum import nwAlert
+from nw.constants import nwQuotes
+
+logger = logging.getLogger(__name__)
+
+class GuiConfigEditor(QDialog):
+
+ def __init__(self, theParent, theProject):
+ QDialog.__init__(self, theParent)
+
+ logger.debug("Initialising ConfigEditor ...")
+
+ self.mainConf = nw.CONFIG
+ self.theParent = theParent
+ self.theProject = theProject
+ self.outerBox = QHBoxLayout()
+ self.innerBox = QVBoxLayout()
+
+ self.setWindowTitle("Preferences")
+
+ self.gradPath = path.abspath(path.join(self.mainConf.appPath,"graphics","block.svg"))
+ self.svgGradient = QSvgWidget(self.gradPath)
+ self.svgGradient.setFixedWidth(80)
+
+ self.theProject.countStatus()
+ self.tabMain = GuiConfigEditGeneral(self.theParent)
+ self.tabEditor = GuiConfigEditEditor(self.theParent)
+
+ self.tabWidget = QTabWidget()
+ self.tabWidget.addTab(self.tabMain, "General")
+ self.tabWidget.addTab(self.tabEditor, "Editor")
+
+ self.setLayout(self.outerBox)
+ self.outerBox.addWidget(self.svgGradient)
+ self.outerBox.addLayout(self.innerBox)
+
+ self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.accepted.connect(self._doSave)
+ self.buttonBox.rejected.connect(self._doClose)
+
+ self.innerBox.addWidget(self.tabWidget)
+ self.innerBox.addWidget(self.buttonBox)
+
+ self.show()
+
+ logger.debug("ProjectEditor ConfigEditor complete")
+
+ return
+
+ def _doSave(self):
+
+ logger.verbose("ConfigEditor save button clicked")
+
+ validEntries = True
+ needsRestart = False
+
+ retA, retB = self.tabMain.saveValues()
+ validEntries &= retA
+ needsRestart |= retB
+
+ retA, retB = self.tabEditor.saveValues()
+ validEntries &= retA
+ needsRestart |= retB
+
+ if needsRestart:
+ msgBox = QMessageBox()
+ msgBox.information(
+ self, "Preferences",
+ "Some changes will not be applied until
%s has been restarted." % nw.__package__
+ )
+
+ if validEntries:
+ self.accept()
+
+ return
+
+ def _doClose(self):
+ logger.verbose("ConfigEditor close button clicked")
+ self.close()
+ return
+
+# END Class GuiConfigEditor
+
+class GuiConfigEditGeneral(QWidget):
+
+ def __init__(self, theParent):
+ QWidget.__init__(self, theParent)
+
+ self.mainConf = nw.CONFIG
+ self.theParent = theParent
+ self.outerBox = QGridLayout()
+
+ # User Interface
+ self.guiLook = QGroupBox("User Interface", self)
+ self.guiLookForm = QGridLayout(self)
+ self.guiLook.setLayout(self.guiLookForm)
+
+ self.guiLookTheme = QComboBox()
+ self.guiLookTheme.setMinimumWidth(200)
+ self.theThemes = self.theParent.theTheme.listThemes()
+ for themeDir, themeName in self.theThemes:
+ self.guiLookTheme.addItem(themeName, themeDir)
+ themeIdx = self.guiLookTheme.findData(self.mainConf.guiTheme)
+ if themeIdx != -1:
+ self.guiLookTheme.setCurrentIndex(themeIdx)
+
+ self.guiLookForm.addWidget(QLabel("Theme"), 0, 0)
+ self.guiLookForm.addWidget(self.guiLookTheme, 0, 1)
+
+ # Spell Checking
+ self.spellLang = QGroupBox("Spell Checker", self)
+ self.spellLangForm = QGridLayout(self)
+ self.spellLang.setLayout(self.spellLangForm)
+
+ self.spellLangList = QComboBox(self)
+ for spTag, spProvider in enchant.list_dicts():
+ self.spellLangList.addItem("%s [%s]" % (spTag, spProvider.name), spTag)
+ spellIdx = self.spellLangList.findData(self.mainConf.spellLanguage)
+ if spellIdx != -1:
+ self.spellLangList.setCurrentIndex(spellIdx)
+
+ self.spellLangForm.addWidget(QLabel("Language"), 0, 0)
+ self.spellLangForm.addWidget(self.spellLangList, 0, 1)
+ self.spellLangForm.setColumnStretch(1, 1)
+
+ # AutoSave
+ self.autoSave = QGroupBox("Auto-Save", self)
+ self.autoSaveForm = QGridLayout(self)
+ self.autoSave.setLayout(self.autoSaveForm)
+
+ self.autoSaveDoc = QSpinBox(self)
+ self.autoSaveDoc.setMinimum(5)
+ self.autoSaveDoc.setMaximum(600)
+ self.autoSaveDoc.setSingleStep(1)
+ self.autoSaveDoc.setValue(self.mainConf.autoSaveDoc)
+
+ self.autoSaveProj = QSpinBox(self)
+ self.autoSaveProj.setMinimum(5)
+ self.autoSaveProj.setMaximum(600)
+ self.autoSaveProj.setSingleStep(1)
+ self.autoSaveProj.setValue(self.mainConf.autoSaveProj)
+
+ self.autoSaveForm.addWidget(QLabel("Document"), 0, 0)
+ self.autoSaveForm.addWidget(self.autoSaveDoc, 0, 1)
+ self.autoSaveForm.addWidget(QLabel("seconds"), 0, 2)
+ self.autoSaveForm.addWidget(QLabel("Project"), 1, 0)
+ self.autoSaveForm.addWidget(self.autoSaveProj, 1, 1)
+ self.autoSaveForm.addWidget(QLabel("seconds"), 1, 2)
+
+ # Assemble
+ self.outerBox.addWidget(self.guiLook, 0, 0)
+ self.outerBox.addWidget(self.spellLang, 1, 0)
+ self.outerBox.addWidget(self.autoSave, 2, 0)
+ self.outerBox.setColumnStretch(2, 1)
+ self.outerBox.setRowStretch(3, 1)
+ self.setLayout(self.outerBox)
+
+ return
+
+ def saveValues(self):
+
+ validEntries = True
+ needsRestart = False
+
+ guiTheme = self.guiLookTheme.currentData()
+ spellLanguage = self.spellLangList.currentData()
+ autoSaveDoc = self.autoSaveDoc.value()
+ autoSaveProj = self.autoSaveProj.value()
+
+ # Check if restart is needed
+ needsRestart |= self.mainConf.guiTheme != guiTheme
+
+ self.mainConf.guiTheme = guiTheme
+ self.mainConf.spellLanguage = spellLanguage
+ self.mainConf.autoSaveDoc = autoSaveDoc
+ self.mainConf.autoSaveProj = autoSaveProj
+
+ self.mainConf.confChanged = True
+
+ return validEntries, needsRestart
+
+# END Class GuiConfigEditGeneral
+
+class GuiConfigEditEditor(QWidget):
+
+ def __init__(self, theParent):
+ QWidget.__init__(self, theParent)
+
+ self.mainConf = nw.CONFIG
+ self.theParent = theParent
+ self.outerBox = QGridLayout()
+
+ # Text Style
+ self.textStyle = QGroupBox("Text Style", self)
+ self.textStyleForm = QGridLayout(self)
+ self.textStyle.setLayout(self.textStyleForm)
+
+ self.textStyleFont = QFontComboBox()
+ self.textStyleFont.setMaximumWidth(250)
+ self.textStyleFont.setCurrentFont(QFont(self.mainConf.textFont))
+
+ self.textStyleSize = QSpinBox(self)
+ self.textStyleSize.setMinimum(5)
+ self.textStyleSize.setMaximum(120)
+ self.textStyleSize.setSingleStep(1)
+ self.textStyleSize.setValue(self.mainConf.textSize)
+
+ self.textStyleForm.addWidget(QLabel("Font Family"), 0, 0)
+ self.textStyleForm.addWidget(self.textStyleFont, 0, 1)
+ self.textStyleForm.addWidget(QLabel("Size"), 0, 2)
+ self.textStyleForm.addWidget(self.textStyleSize, 0, 3)
+ self.textStyleForm.setColumnStretch(4, 1)
+
+ # Text Flow
+ self.textFlow = QGroupBox("Text Flow", self)
+ self.textFlowForm = QGridLayout(self)
+ self.textFlow.setLayout(self.textFlowForm)
+
+ self.textFlowFixed = QCheckBox(self)
+ self.textFlowFixed.setToolTip("Make text in editor fixed width and scale margins instead.")
+ if self.mainConf.textFixedW:
+ self.textFlowFixed.setCheckState(Qt.Checked)
+ else:
+ self.textFlowFixed.setCheckState(Qt.Unchecked)
+ self.textFlowWidth = QSpinBox(self)
+ self.textFlowWidth.setMinimum(300)
+ self.textFlowWidth.setMaximum(10000)
+ self.textFlowWidth.setSingleStep(10)
+ self.textFlowWidth.setValue(self.mainConf.textWidth)
+
+ self.textFlowJustify = QCheckBox(self)
+ self.textFlowJustify.setToolTip("Justify text in main document editor.")
+ if self.mainConf.doJustify:
+ self.textFlowJustify.setCheckState(Qt.Checked)
+ else:
+ self.textFlowJustify.setCheckState(Qt.Unchecked)
+
+ self.testFlowAutoSelect = QCheckBox(self)
+ self.testFlowAutoSelect.setToolTip("Auto-select word under cursor when applying formatting.")
+ if self.mainConf.autoSelect:
+ self.testFlowAutoSelect.setCheckState(Qt.Checked)
+ else:
+ self.testFlowAutoSelect.setCheckState(Qt.Unchecked)
+
+ self.textFlowForm.addWidget(QLabel("Fixed Width"), 0, 0)
+ self.textFlowForm.addWidget(self.textFlowFixed, 0, 1)
+ self.textFlowForm.addWidget(self.textFlowWidth, 0, 2)
+ self.textFlowForm.addWidget(QLabel("px"), 0, 3)
+ self.textFlowForm.addWidget(QLabel("Justify Text"), 1, 0)
+ self.textFlowForm.addWidget(self.textFlowJustify, 1, 1)
+ self.textFlowForm.addWidget(QLabel("Auto-Select Text"), 2, 0)
+ self.textFlowForm.addWidget(self.testFlowAutoSelect, 2, 1)
+ self.textFlowForm.setColumnStretch(4, 1)
+
+ # Text Margins
+ self.textMargin = QGroupBox("Margins", self)
+ self.textMarginForm = QGridLayout(self)
+ self.textMargin.setLayout(self.textMarginForm)
+
+ self.textMarginHor = QSpinBox(self)
+ self.textMarginHor.setMinimum(0)
+ self.textMarginHor.setMaximum(2000)
+ self.textMarginHor.setSingleStep(1)
+ self.textMarginHor.setValue(self.mainConf.textMargin[0])
+
+ self.textMarginVer = QSpinBox(self)
+ self.textMarginVer.setMinimum(0)
+ self.textMarginVer.setMaximum(2000)
+ self.textMarginVer.setSingleStep(1)
+ self.textMarginVer.setValue(self.mainConf.textMargin[1])
+
+ self.textMarginTab = QSpinBox(self)
+ self.textMarginTab.setMinimum(0)
+ self.textMarginTab.setMaximum(200)
+ self.textMarginTab.setSingleStep(1)
+ self.textMarginTab.setValue(self.mainConf.tabWidth)
+
+ self.textMarginForm.addWidget(QLabel("Horizontal"), 0, 0)
+ self.textMarginForm.addWidget(self.textMarginHor, 0, 1)
+ self.textMarginForm.addWidget(QLabel("px"), 0, 2)
+ self.textMarginForm.addWidget(QLabel("Vertical"), 1, 0)
+ self.textMarginForm.addWidget(self.textMarginVer, 1, 1)
+ self.textMarginForm.addWidget(QLabel("px"), 1, 2)
+ self.textMarginForm.addWidget(QLabel("Tab Width"), 2, 0)
+ self.textMarginForm.addWidget(self.textMarginTab, 2, 1)
+ self.textMarginForm.addWidget(QLabel("px"), 2, 2)
+ self.textMarginForm.setColumnStretch(4, 1)
+
+ # Auto-Replace
+ self.autoReplace = QGroupBox("Auto-Replace", self)
+ self.autoReplaceForm = QGridLayout(self)
+ self.autoReplace.setLayout(self.autoReplaceForm)
+
+ self.autoReplaceMain = QCheckBox(self)
+ self.autoReplaceMain.setToolTip("Auto-replace text as you type.")
+ if self.mainConf.doReplace:
+ self.autoReplaceMain.setCheckState(Qt.Checked)
+ else:
+ self.autoReplaceMain.setCheckState(Qt.Unchecked)
+
+ self.autoReplaceSQ = QCheckBox(self)
+ self.autoReplaceSQ.setToolTip("Auto-replace single quotes.")
+ if self.mainConf.doReplaceSQuote:
+ self.autoReplaceSQ.setCheckState(Qt.Checked)
+ else:
+ self.autoReplaceSQ.setCheckState(Qt.Unchecked)
+
+ self.autoReplaceSStyleO = QLineEdit()
+ self.autoReplaceSStyleO.setMaxLength(1)
+ self.autoReplaceSStyleO.setFixedWidth(30)
+ self.autoReplaceSStyleO.setAlignment(Qt.AlignCenter)
+ self.autoReplaceSStyleO.setText(self.mainConf.fmtSingleQuotes[0])
+
+ self.autoReplaceSStyleC = QLineEdit()
+ self.autoReplaceSStyleC.setMaxLength(1)
+ self.autoReplaceSStyleC.setFixedWidth(30)
+ self.autoReplaceSStyleC.setAlignment(Qt.AlignCenter)
+ self.autoReplaceSStyleC.setText(self.mainConf.fmtSingleQuotes[1])
+
+ self.autoReplaceDQ = QCheckBox(self)
+ self.autoReplaceDQ.setToolTip("Auto-replace double quotes.")
+ if self.mainConf.doReplaceDQuote:
+ self.autoReplaceDQ.setCheckState(Qt.Checked)
+ else:
+ self.autoReplaceDQ.setCheckState(Qt.Unchecked)
+
+ self.autoReplaceDStyleO = QLineEdit()
+ self.autoReplaceDStyleO.setMaxLength(1)
+ self.autoReplaceDStyleO.setFixedWidth(30)
+ self.autoReplaceDStyleO.setAlignment(Qt.AlignCenter)
+ self.autoReplaceDStyleO.setText(self.mainConf.fmtDoubleQuotes[0])
+
+ self.autoReplaceDStyleC = QLineEdit()
+ self.autoReplaceDStyleC.setMaxLength(1)
+ self.autoReplaceDStyleC.setFixedWidth(30)
+ self.autoReplaceDStyleC.setAlignment(Qt.AlignCenter)
+ self.autoReplaceDStyleC.setText(self.mainConf.fmtDoubleQuotes[1])
+
+ self.autoReplaceDash = QCheckBox(self)
+ self.autoReplaceDash.setToolTip("Auto-replace double and triple hyphens with short and long dash.")
+ if self.mainConf.doReplaceDash:
+ self.autoReplaceDash.setCheckState(Qt.Checked)
+ else:
+ self.autoReplaceDash.setCheckState(Qt.Unchecked)
+
+ self.autoReplaceDots = QCheckBox(self)
+ self.autoReplaceDots.setToolTip("Auto-replace three dots with ellipsis.")
+ if self.mainConf.doReplaceDots:
+ self.autoReplaceDots.setCheckState(Qt.Checked)
+ else:
+ self.autoReplaceDots.setCheckState(Qt.Unchecked)
+
+ self.autoReplaceForm.addWidget(QLabel("Enable Feature"), 0, 0)
+ self.autoReplaceForm.addWidget(self.autoReplaceMain, 0, 1)
+ self.autoReplaceForm.addWidget(QLabel("Single Quotes"), 1, 0)
+ self.autoReplaceForm.addWidget(self.autoReplaceSQ, 1, 1)
+ self.autoReplaceForm.addWidget(QLabel("Open"), 1, 2)
+ self.autoReplaceForm.addWidget(self.autoReplaceSStyleO, 1, 3)
+ self.autoReplaceForm.addWidget(QLabel("Close"), 1, 4)
+ self.autoReplaceForm.addWidget(self.autoReplaceSStyleC, 1, 5)
+ self.autoReplaceForm.addWidget(QLabel("Double Quotes"), 2, 0)
+ self.autoReplaceForm.addWidget(self.autoReplaceDQ, 2, 1)
+ self.autoReplaceForm.addWidget(QLabel("Open"), 2, 2)
+ self.autoReplaceForm.addWidget(self.autoReplaceDStyleO, 2, 3)
+ self.autoReplaceForm.addWidget(QLabel("Close"), 2, 4)
+ self.autoReplaceForm.addWidget(self.autoReplaceDStyleC, 2, 5)
+ self.autoReplaceForm.addWidget(QLabel("Hyphens with Dash"), 3, 0)
+ self.autoReplaceForm.addWidget(self.autoReplaceDash, 3, 1)
+ self.autoReplaceForm.addWidget(QLabel("Dots with Ellipsis"), 4, 0)
+ self.autoReplaceForm.addWidget(self.autoReplaceDots, 4, 1)
+ self.autoReplaceForm.setColumnStretch(6, 1)
+
+ # Assemble
+ self.outerBox.addWidget(self.textStyle, 0, 0, 1, 2)
+ self.outerBox.addWidget(self.textFlow, 1, 0)
+ self.outerBox.addWidget(self.textMargin, 1, 1)
+ self.outerBox.addWidget(self.autoReplace, 2, 0, 1, 2)
+ self.outerBox.setColumnStretch(2, 1)
+ self.setLayout(self.outerBox)
+
+ return
+
+ def saveValues(self):
+
+ validEntries = True
+
+ textFont = self.textStyleFont.currentFont().family()
+ textSize = self.textStyleSize.value()
+
+ self.mainConf.textFont = textFont
+ self.mainConf.textSize = textSize
+
+ textWidth = self.textFlowWidth.value()
+ textFixedW = self.textFlowFixed.isChecked()
+ doJustify = self.textFlowJustify.isChecked()
+ autoSelect = self.testFlowAutoSelect.isChecked()
+
+ self.mainConf.textWidth = textWidth
+ self.mainConf.textFixedW = textFixedW
+ self.mainConf.doJustify = doJustify
+ self.mainConf.autoSelect = autoSelect
+
+ textMarginH = self.textMarginHor.value()
+ textMarginV = self.textMarginVer.value()
+ tabWidth = self.textMarginTab.value()
+
+ self.mainConf.textMargin[0] = textMarginH
+ self.mainConf.textMargin[1] = textMarginV
+ self.mainConf.tabWidth = tabWidth
+
+ doReplace = self.autoReplaceMain.isChecked()
+ doReplaceSQuote = self.autoReplaceSQ.isChecked()
+ doReplaceDQuote = self.autoReplaceDQ.isChecked()
+ doReplaceDash = self.autoReplaceDash.isChecked()
+ doReplaceDots = self.autoReplaceDash.isChecked()
+ fmtSingleQuotesO = self.autoReplaceSStyleO.text()
+ fmtSingleQuotesC = self.autoReplaceSStyleC.text()
+ fmtDoubleQuotesO = self.autoReplaceDStyleO.text()
+ fmtDoubleQuotesC = self.autoReplaceDStyleC.text()
+
+ self.mainConf.doReplace = doReplace
+ self.mainConf.doReplaceSQuote = doReplaceSQuote
+ self.mainConf.doReplaceDQuote = doReplaceDQuote
+ self.mainConf.doReplaceDash = doReplaceDash
+ self.mainConf.doReplaceDots = doReplaceDots
+
+ if self._checkQuoteSymbol(fmtSingleQuotesO):
+ self.mainConf.fmtSingleQuotes[0] = fmtSingleQuotesO
+ else:
+ self.theParent.makeAlert("Invalid quote symbol: %s" % fmtSingleQuotesO, nwAlert.ERROR)
+ validEntries = False
+
+ if self._checkQuoteSymbol(fmtSingleQuotesC):
+ self.mainConf.fmtSingleQuotes[1] = fmtSingleQuotesC
+ else:
+ self.theParent.makeAlert("Invalid quote symbol: %s" % fmtSingleQuotesC, nwAlert.ERROR)
+ validEntries = False
+
+ if self._checkQuoteSymbol(fmtDoubleQuotesO):
+ self.mainConf.fmtDoubleQuotes[0] = fmtDoubleQuotesO
+ else:
+ self.theParent.makeAlert("Invalid quote symbol: %s" % fmtDoubleQuotesO, nwAlert.ERROR)
+ validEntries = False
+
+ if self._checkQuoteSymbol(fmtDoubleQuotesC):
+ self.mainConf.fmtDoubleQuotes[1] = fmtDoubleQuotesC
+ else:
+ self.theParent.makeAlert("Invalid quote symbol: %s" % fmtDoubleQuotesC, nwAlert.ERROR)
+ validEntries = False
+
+ self.mainConf.confChanged = True
+
+ return validEntries, False
+
+ ##
+ # Internal Functions
+ ##
+
+ def _checkQuoteSymbol(self, toCheck):
+ if len(toCheck) != 1:
+ return False
+ if toCheck in nwQuotes.SYMBOLS:
+ return True
+ return False
+
+# END Class GuiConfigEditEditor
diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py
index b955acb1..9b36790a 100644
--- a/nw/gui/doceditor.py
+++ b/nw/gui/doceditor.py
@@ -18,7 +18,7 @@ from time import time
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import QTextEdit, QAction, QMenu, QShortcut
-from PyQt5.QtGui import QTextCursor, QTextOption, QIcon, QKeySequence
+from PyQt5.QtGui import QTextCursor, QTextOption, QIcon, QKeySequence, QFont
from nw.project.document import NWDoc
from nw.gui.dochighlight import GuiDocHighlighter
@@ -55,7 +55,6 @@ class GuiDocEditor(QTextEdit):
self.typDQClose = self.mainConf.fmtDoubleQuotes[1]
self.typSQOpen = self.mainConf.fmtSingleQuotes[0]
self.typSQClose = self.mainConf.fmtSingleQuotes[1]
- self.typApos = self.mainConf.fmtApostrophe
# Core Elements
self.theQDoc = self.document()
@@ -113,7 +112,21 @@ class GuiDocEditor(QTextEdit):
return True
def initEditor(self):
+ """Initialise or re-initialise the editor with the user's settings.
+ This function is both called when the editor is created, and when the user changes the
+ main editor preferences.
+ """
+
+ # Set Font
+ if self.mainConf.textFont is not None:
+ self.setFontFamily(self.mainConf.textFont)
+ else:
+ # If none is defined, set the default back to config
+ theFont = self.theQDoc.defaultFont().family()
+ self.mainConf.textFont = theFont
self.setFontPointSize(self.mainConf.textSize)
+
+ # Set text fixed width, or alternatively, just margins
if self.mainConf.textFixedW:
self.setLineWrapMode(QTextEdit.FixedPixelWidth)
self.setLineWrapColumnOrWidth(self.mainConf.textWidth)
@@ -121,12 +134,22 @@ class GuiDocEditor(QTextEdit):
mTB = self.mainConf.textMargin[0]
mLR = self.mainConf.textMargin[1]
self.setViewportMargins(mLR,mTB,mLR,mTB)
+
+ # Also set the document text options for the document text flow
theOpt = QTextOption()
if self.mainConf.tabWidth is not None:
theOpt.setTabStopDistance(self.mainConf.tabWidth)
if self.mainConf.doJustify:
theOpt.setAlignment(Qt.AlignJustify)
self.theQDoc.setDefaultTextOption(theOpt)
+
+ # If we have a document open, we should reload it in case the font changed
+ if self.theHandle is not None:
+ tHandle = self.theHandle
+ self.hLight.initHighlighter()
+ self.clearEditor()
+ self.loadText(tHandle)
+
return True
def loadText(self, tHandle):
diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py
index 848f60a3..55ae8b05 100644
--- a/nw/gui/dochighlight.py
+++ b/nw/gui/dochighlight.py
@@ -32,7 +32,9 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.theDict = None
self.theHandle = None
self.spellCheck = False
+ self.spellRx = None
self.hRules = []
+ self.hStyles = {}
self.colHead = QColor(*self.theTheme.colHead)
self.colHeadH = QColor(*self.theTheme.colHeadH)
@@ -47,6 +49,14 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.colTagErr = QColor(*self.theTheme.colTagErr)
self.colRepTag = QColor(*self.theTheme.colRepTag)
+ self.initHighlighter()
+
+ logger.debug("DocHighlighter initialisation complete")
+
+ return
+
+ def initHighlighter(self):
+
self.hStyles = {
"header1" : self._makeFormat(self.colHead, "bold",1.8),
"header2" : self._makeFormat(self.colHead, "bold",1.6),
@@ -160,9 +170,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.rules = [(QRegularExpression(a),b) for (a,b) in self.hRules]
self.spellRx = QRegularExpression(r"\b[^\s]+\b")
- logger.debug("DocHighlighter initialisation complete")
-
- return
+ return True
##
# Setters
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index e170b408..5c91f71c 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -492,10 +492,12 @@ class GuiMainMenu(QMenuBar):
menuItem.triggered.connect(self.theParent.rebuildIndex)
self.toolsMenu.addAction(menuItem)
- # # Tools > Settings
- # menuItem = QAction(QIcon.fromTheme("preferences-system"), "Preferences", self)
- # menuItem.setStatusTip("Preferences")
- # self.toolsMenu.addAction(menuItem)
+ # Tools > Settings
+ menuItem = QAction(QIcon.fromTheme("preferences-system"), "Preferences", self)
+ menuItem.setStatusTip("Preferences")
+ menuItem.setShortcut("Ctrl+,")
+ menuItem.triggered.connect(self.theParent.editConfigDialog)
+ self.toolsMenu.addAction(menuItem)
return
diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py
index 4d17b36e..0c8e8d76 100644
--- a/nw/gui/winmain.py
+++ b/nw/gui/winmain.py
@@ -19,7 +19,7 @@ from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QIcon, QPixmap, QColor
from PyQt5.QtWidgets import (
qApp, QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog,
- QShortcut, QMessageBox, QProgressDialog
+ QShortcut, QMessageBox, QProgressDialog, QDialog
)
from nw.gui.doctree import GuiDocTree
@@ -27,6 +27,7 @@ from nw.gui.doceditor import GuiDocEditor
from nw.gui.docviewer import GuiDocViewer
from nw.gui.docdetails import GuiDocDetails
from nw.gui.mainmenu import GuiMainMenu
+from nw.gui.configeditor import GuiConfigEditor
from nw.gui.projecteditor import GuiProjectEditor
from nw.gui.itemeditor import GuiItemEditor
from nw.gui.statusbar import GuiMainStatus
@@ -49,7 +50,7 @@ class GuiMain(QMainWindow):
logger.debug("Initialising GUI ...")
self.mainConf = nw.CONFIG
- self.theTheme = Theme()
+ self.theTheme = Theme(self)
self.theProject = NWProject(self)
self.theIndex = NWIndex(self.theProject, self)
self.hasProject = False
@@ -118,15 +119,11 @@ class GuiMain(QMainWindow):
# Set Up Autosaving Project Timer
self.asProjTimer = QTimer()
- self.asProjTimer.setInterval(int(self.mainConf.autoSaveProj*1000))
self.asProjTimer.timeout.connect(self._autoSaveProject)
- self.asProjTimer.start()
# Set Up Autosaving Document Timer
self.asDocTimer = QTimer()
- self.asDocTimer.setInterval(int(self.mainConf.autoSaveDoc*1000))
self.asDocTimer.timeout.connect(self._autoSaveDocument)
- self.asDocTimer.start()
# Keyboard Shortcuts
QShortcut(Qt.Key_Return, self.treeView, context=Qt.WidgetShortcut, activated=self._treeKeyPressReturn)
@@ -138,6 +135,10 @@ class GuiMain(QMainWindow):
if self.mainConf.showGUI:
self.show()
+ self.initMain()
+ self.asProjTimer.start()
+ self.asDocTimer.start()
+
logger.debug("GUI initialisation complete")
return
@@ -149,6 +150,11 @@ class GuiMain(QMainWindow):
self.statusBar.clearStatus()
return True
+ def initMain(self):
+ self.asProjTimer.setInterval(int(self.mainConf.autoSaveProj*1000))
+ self.asDocTimer.setInterval(int(self.mainConf.autoSaveDoc*1000))
+ return True
+
##
# Project Actions
##
@@ -454,6 +460,14 @@ class GuiMain(QMainWindow):
return projPath
return None
+ def editConfigDialog(self):
+ dlgConf = GuiConfigEditor(self, self.theProject)
+ if dlgConf.exec_() == QDialog.Accepted:
+ logger.debug("Applying new preferences")
+ self.initMain()
+ self.docEditor.initEditor()
+ return True
+
def editProjectDialog(self):
if self.hasProject:
dlgProj = GuiProjectEditor(self, self.theProject)
diff --git a/nw/project/project.py b/nw/project/project.py
index 81979f2e..d4035aa2 100644
--- a/nw/project/project.py
+++ b/nw/project/project.py
@@ -373,8 +373,9 @@ class NWProject():
return True
def setSpellCheck(self, theMode):
- self.spellCheck = theMode
- self.setProjectChanged(True)
+ if self.spellCheck != theMode:
+ self.spellCheck = theMode
+ self.setProjectChanged(True)
return True
def setTreeOrder(self, newOrder):
@@ -385,18 +386,21 @@ class NWProject():
return True
def setLastEdited(self, tHandle):
- self.lastEdited = tHandle
- self.setProjectChanged(True)
+ if self.lastEdited != tHandle:
+ self.lastEdited = tHandle
+ self.setProjectChanged(True)
return True
def setLastViewed(self, tHandle):
- self.lastViewed = tHandle
- self.setProjectChanged(True)
+ if self.lastViewed != tHandle:
+ self.lastViewed = tHandle
+ self.setProjectChanged(True)
return True
def setProjectWordCount(self, theCount):
- self.currWCount = theCount
- self.setProjectChanged(True)
+ if self.currWCount != theCount:
+ self.currWCount = theCount
+ self.setProjectChanged(True)
return True
def getSessionWordCount(self):
diff --git a/nw/theme.py b/nw/theme.py
index 806e7b46..f0a8db3c 100644
--- a/nw/theme.py
+++ b/nw/theme.py
@@ -14,19 +14,30 @@ import logging
import configparser
import nw
-from os import path
+from os import path, listdir
+
+from nw.enum import nwAlert
logger = logging.getLogger(__name__)
class Theme:
- def __init__(self):
+ def __init__(self, theParent):
self.mainConf = nw.CONFIG
+ self.theParent = theParent
self.cssName = "styles.css"
self.confName = "theme.conf"
+ self.themeList = []
# Loaded Theme Settings
+
+ ## Main
+ self.themeName = "No Name"
+
+ ## Syntax
+ self.colText = [0,0,0]
+ self.colLink = [0,0,0]
self.colHead = [0,0,0]
self.colHeadH = [0,0,0]
self.colEmph = [0,0,0]
@@ -83,6 +94,11 @@ class Theme:
logger.error("Could not load theme config file")
return False
+ ## Main
+ cnfSec = "Main"
+ if confParser.has_section(cnfSec):
+ self.themeName = confParser.get(cnfSec,"name")
+
## Syntax
cnfSec = "Syntax"
if confParser.has_section(cnfSec):
@@ -103,6 +119,29 @@ class Theme:
return True
+ def listThemes(self):
+
+ if len(self.themeList) > 0:
+ return self.themeList
+
+ confParser = configparser.ConfigParser()
+ for themeDir in listdir(self.mainConf.themeRoot):
+ themeConf = path.join(self.mainConf.themeRoot, themeDir, self.confName)
+ logger.verbose("Checking theme config for '%s'" % themeDir)
+ try:
+ confParser.read_file(open(themeConf))
+ except Exception as e:
+ self.theParent.makeAlert(["Could not load theme config file",str(e)],nwAlert.ERROR)
+ return []
+ themeName = ""
+ if confParser.has_section("Main"):
+ themeName = confParser.get("Main","name")
+ logger.verbose("Theme name is '%s'" % themeName)
+ if themeName != "":
+ self.themeList.append((themeDir, themeName))
+
+ return self.themeList
+
##
# Internal Functions
##
diff --git a/nw/themes/default/styles.css b/nw/themes/default_dark/styles.css
similarity index 87%
rename from nw/themes/default/styles.css
rename to nw/themes/default_dark/styles.css
index ddf008ae..c81aecf3 100644
--- a/nw/themes/default/styles.css
+++ b/nw/themes/default_dark/styles.css
@@ -1,3 +1,7 @@
+/**
+ * Default Theme: Dark
+ */
+
* {
background-color: #999999;
}
diff --git a/nw/themes/default/theme.conf b/nw/themes/default_dark/theme.conf
similarity index 92%
rename from nw/themes/default/theme.conf
rename to nw/themes/default_dark/theme.conf
index 7f4729e8..86b04b45 100644
--- a/nw/themes/default/theme.conf
+++ b/nw/themes/default_dark/theme.conf
@@ -1,3 +1,6 @@
+[Main]
+name = Default Dark
+
[Syntax]
text = 199, 207, 208
link = 184, 200, 0
diff --git a/nw/themes/default_light/styles.css b/nw/themes/default_light/styles.css
new file mode 100644
index 00000000..90ef623f
--- /dev/null
+++ b/nw/themes/default_light/styles.css
@@ -0,0 +1,7 @@
+/**
+ * Default Theme: Light
+ */
+
+QTreeView, QHeaderView {
+ font-size: 13px;
+}
diff --git a/nw/themes/default_light/theme.conf b/nw/themes/default_light/theme.conf
new file mode 100644
index 00000000..b3d0d5be
--- /dev/null
+++ b/nw/themes/default_light/theme.conf
@@ -0,0 +1,18 @@
+[Main]
+name = Default Light
+
+[Syntax]
+text = 0, 0, 0
+link = 0, 0, 0
+headertext = 0, 0, 0
+headertag = 0, 0, 0
+emphasis = 0, 0, 0
+straightquotes = 0, 0, 0
+doublequotes = 0, 0, 0
+singlequotes = 0, 0, 0
+hidden = 50, 50, 50
+keyword = 0, 0, 0
+value = 0, 0, 0
+spellcheckline = 0, 0, 0
+tagerror = 0, 0, 0
+replacetag = 0, 0, 0
diff --git a/sample/sampleNovel/meta/sessionInfo.log b/sample/sampleNovel/meta/sessionInfo.log
index 4111f856..1defe611 100644
--- a/sample/sampleNovel/meta/sessionInfo.log
+++ b/sample/sampleNovel/meta/sessionInfo.log
@@ -187,3 +187,26 @@ Start: 2019-06-08 20:30:24 End: 2019-06-08 20:42:10 Words: 9
Start: 2019-06-08 20:42:14 End: 2019-06-08 20:42:37 Words: 0
Start: 2019-06-08 20:42:41 End: 2019-06-08 20:43:25 Words: 0
Start: 2019-06-08 20:43:29 End: 2019-06-08 20:43:42 Words: 0
+Start: 2019-06-10 13:26:52 End: 2019-06-10 13:30:22 Words: 0
+Start: 2019-06-10 13:30:28 End: 2019-06-10 13:31:44 Words: 0
+Start: 2019-06-10 14:17:18 End: 2019-06-10 14:17:48 Words: 0
+Start: 2019-06-10 14:20:17 End: 2019-06-10 14:21:11 Words: 0
+Start: 2019-06-10 20:41:35 End: 2019-06-10 20:41:48 Words: 0
+Start: 2019-06-10 20:42:15 End: 2019-06-10 20:42:27 Words: 0
+Start: 2019-06-10 20:42:43 End: 2019-06-10 20:42:47 Words: 0
+Start: 2019-06-10 20:45:02 End: 2019-06-10 20:45:07 Words: 0
+Start: 2019-06-10 20:45:44 End: 2019-06-10 20:46:03 Words: 0
+Start: 2019-06-10 20:46:08 End: 2019-06-10 20:47:15 Words: 0
+Start: 2019-06-10 20:47:24 End: 2019-06-10 20:47:28 Words: 0
+Start: 2019-06-10 20:47:46 End: 2019-06-10 20:47:59 Words: 0
+Start: 2019-06-10 20:48:02 End: 2019-06-10 20:48:08 Words: 0
+Start: 2019-06-10 20:57:47 End: 2019-06-10 20:58:01 Words: 0
+Start: 2019-06-10 21:29:47 End: 2019-06-10 21:33:22 Words: 0
+Start: 2019-06-10 21:33:26 End: 2019-06-10 21:34:59 Words: 0
+Start: 2019-06-10 21:39:54 End: 2019-06-10 21:45:25 Words: 0
+Start: 2019-06-10 21:45:29 End: 2019-06-10 21:47:40 Words: 0
+Start: 2019-06-10 21:48:06 End: 2019-06-10 21:48:47 Words: 0
+Start: 2019-06-10 21:48:51 End: 2019-06-10 21:49:07 Words: 0
+Start: 2019-06-10 22:17:58 End: 2019-06-10 22:18:44 Words: 0
+Start: 2019-06-10 22:46:35 End: 2019-06-10 22:49:34 Words: 0
+Start: 2019-06-11 14:44:55 End: 2019-06-11 14:53:47 Words: 0
diff --git a/sample/sampleNovel/nwProject.nwx b/sample/sampleNovel/nwProject.nwx
index 7fc31a8e..b7bbbada 100644
--- a/sample/sampleNovel/nwProject.nwx
+++ b/sample/sampleNovel/nwProject.nwx
@@ -1,5 +1,5 @@
-
+
Sample Project
Sample Project
@@ -80,7 +80,7 @@
736
137
6
- 725
+ 738
-
New File
diff --git a/tests/reference/novelwriter.conf b/tests/reference/novelwriter.conf
index a7df1756..7c827a0c 100644
--- a/tests/reference/novelwriter.conf
+++ b/tests/reference/novelwriter.conf
@@ -1,6 +1,6 @@
[Main]
-timestamp = 2019-05-25 23:33:23
-theme = default
+timestamp = 2019-06-11 18:39:59
+theme = default_dark
[Sizes]
geometry = 1100, 650
@@ -14,10 +14,11 @@ autosaveproject = 60
autosavedoc = 30
[Editor]
+textfont = None
+textsize = 12
fixedwidth = True
width = 600
margins = 40, 40
-textsize = 13
tabwidth = 40
justify = True
autoselect = True
@@ -26,6 +27,8 @@ repsquotes = True
repdquotes = True
repdash = True
repdots = True
+fmtsinglequote = ‘, ’
+fmtdoublequote = “, ”
spellcheck = en_GB
[Path]