From e0ce894c4f8f9d30b9d0e32e2bc81a77a51263c2 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Mon, 10 Jun 2019 12:48:36 +0200 Subject: [PATCH 01/11] Added a config dialog --- nw/gui/configeditor.py | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 nw/gui/configeditor.py diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py new file mode 100644 index 00000000..bd440272 --- /dev/null +++ b/nw/gui/configeditor.py @@ -0,0 +1,97 @@ +# -*- 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 nw + +from os import path + +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel +from PyQt5.QtSvg import QSvgWidget +from PyQt5.QtWidgets import ( + QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel, + QWidget, QTabWidget, QDialogButtonBox, QListWidget, QListWidgetItem, QPushButton, + QColorDialog, QAbstractItemView, QTreeWidget, QTreeWidgetItem +) +from nw.enum import nwAlert + +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("%s Configuration" % nw.__package__) + + 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.theProject) + + self.tabWidget = QTabWidget() + self.tabWidget.addTab(self.tabMain, "General") + + 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") + self.close() + return + + def _doClose(self): + logger.verbose("ConfigEditor close button clicked") + self.close() + return + +# END Class GuiConfigEditor + +class GuiConfigEditGeneral(QWidget): + + def __init__(self, theParent, theProject): + QWidget.__init__(self, theParent) + + self.theParent = theParent + self.theProject = theProject + self.mainForm = QFormLayout() + + self.setLayout(self.mainForm) + + return + +# END Class GuiConfigEditGeneral From c30886732a474d0a455155e50476de688a803b7a Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Mon, 10 Jun 2019 12:51:37 +0200 Subject: [PATCH 02/11] Connected the preferences dialog to the menu and main gui --- nw/gui/mainmenu.py | 10 ++++++---- nw/gui/winmain.py | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) 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..dad460ef 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -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 @@ -454,6 +455,11 @@ class GuiMain(QMainWindow): return projPath return None + def editConfigDialog(self): + dlgProj = GuiConfigEditor(self, self.theProject) + dlgProj.exec_() + return True + def editProjectDialog(self): if self.hasProject: dlgProj = GuiProjectEditor(self, self.theProject) From ca0a756fe5fbb32294a47993462c3520539f6e12 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Mon, 10 Jun 2019 14:41:32 +0200 Subject: [PATCH 03/11] Added autosave and theme to preferences dialog --- nw/config.py | 6 +- nw/gui/configeditor.py | 81 +++++++++++++++++-- nw/gui/winmain.py | 23 ++++-- nw/project/project.py | 20 +++-- nw/theme.py | 43 +++++++++- .../{default => default_dark}/styles.css | 4 + .../{default => default_dark}/theme.conf | 3 + nw/themes/default_light/styles.css | 7 ++ nw/themes/default_light/theme.conf | 18 +++++ 9 files changed, 177 insertions(+), 28 deletions(-) rename nw/themes/{default => default_dark}/styles.css (87%) rename nw/themes/{default => default_dark}/theme.conf (92%) create mode 100644 nw/themes/default_light/styles.css create mode 100644 nw/themes/default_light/theme.conf diff --git a/nw/config.py b/nw/config.py index f65f9554..8db01cfd 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] @@ -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. diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py index bd440272..bd67eb72 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -20,8 +20,7 @@ from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel from PyQt5.QtSvg import QSvgWidget from PyQt5.QtWidgets import ( QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel, - QWidget, QTabWidget, QDialogButtonBox, QListWidget, QListWidgetItem, QPushButton, - QColorDialog, QAbstractItemView, QTreeWidget, QTreeWidgetItem + QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox ) from nw.enum import nwAlert @@ -40,7 +39,7 @@ class GuiConfigEditor(QDialog): self.outerBox = QHBoxLayout() self.innerBox = QVBoxLayout() - self.setWindowTitle("%s Configuration" % nw.__package__) + self.setWindowTitle("Preferences") self.gradPath = path.abspath(path.join(self.mainConf.appPath,"graphics","block.svg")) self.svgGradient = QSvgWidget(self.gradPath) @@ -70,8 +69,21 @@ class GuiConfigEditor(QDialog): return def _doSave(self): + logger.verbose("ConfigEditor save button clicked") - self.close() + + needsRestart = False + needsRestart |= self.tabMain.saveValues() + + if needsRestart: + msgBox = QMessageBox() + msgBox.information( + self, "Preferences", + "Some changes will not be applied until
%s has been restarted." % nw.__package__ + ) + + self.accept() + return def _doClose(self): @@ -86,12 +98,65 @@ class GuiConfigEditGeneral(QWidget): def __init__(self, theParent, theProject): QWidget.__init__(self, theParent) - self.theParent = theParent - self.theProject = theProject - self.mainForm = QFormLayout() + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theProject = theProject + self.outerBox = QVBoxLayout() - self.setLayout(self.mainForm) + # User Interface + self.guiLook = QGroupBox("User Interface", self) + self.guiLookForm = QFormLayout(self) + self.guiLook.setLayout(self.guiLookForm) + + self.theThemes = self.theParent.theTheme.listThemes() + self.guiLookTheme = QComboBox() + 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.addRow("Theme", self.guiLookTheme) + + # AutoSave + self.autoSave = QGroupBox("Auto-Save", self) + self.autoSaveForm = QFormLayout(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.autoSaveForm.addRow("Save Document (seconds)", self.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.addRow("Save Project (seconds)", self.autoSaveProj) + + self.outerBox.addWidget(self.guiLook) + self.outerBox.addWidget(self.autoSave) + self.setLayout(self.outerBox) return + def saveValues(self): + + autoSaveDoc = self.autoSaveDoc.value() + autoSaveProj = self.autoSaveProj.value() + guiTheme = self.guiLookTheme.currentData() + + # Check if restart is needed + needsRestart = False + needsRestart |= self.mainConf.guiTheme != guiTheme + + self.mainConf.autoSaveDoc = autoSaveDoc + self.mainConf.autoSaveProj = autoSaveProj + self.mainConf.guiTheme = guiTheme + self.mainConf.confChanged = True + + return needsRestart + # END Class GuiConfigEditGeneral diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index dad460ef..8e2400c4 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 @@ -50,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 @@ -119,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) @@ -139,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 @@ -150,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 ## @@ -456,8 +461,10 @@ class GuiMain(QMainWindow): return None def editConfigDialog(self): - dlgProj = GuiConfigEditor(self, self.theProject) - dlgProj.exec_() + dlgConf = GuiConfigEditor(self, self.theProject) + if dlgConf.exec_() == QDialog.Accepted: + logger.debug("Applying new preferences") + self.initMain() return True def editProjectDialog(self): 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 From 30f25fcb2b08d4039dbd4d58c2e4ba3f55d0f9b9 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Mon, 10 Jun 2019 21:55:17 +0200 Subject: [PATCH 04/11] Added a number of options to the config gui --- nw/config.py | 9 ++-- nw/gui/configeditor.py | 115 +++++++++++++++++++++++++++++++++++++++-- nw/gui/doceditor.py | 20 ++++++- nw/gui/dochighlight.py | 14 +++-- nw/gui/winmain.py | 1 + 5 files changed, 147 insertions(+), 12 deletions(-) diff --git a/nw/config.py b/nw/config.py index 8db01cfd..8faa22d1 100644 --- a/nw/config.py +++ b/nw/config.py @@ -64,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 @@ -155,10 +156,11 @@ class Config: ## Editor cnfSec = "Editor" + 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.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) @@ -207,10 +209,11 @@ class Config: ## Editor cnfSec = "Editor" cnfParse.add_section(cnfSec) + 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,"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)) diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py index bd67eb72..05df251d 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -16,11 +16,12 @@ import nw from os import path from PyQt5.QtCore import Qt -from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel +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 + QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox, + QCheckBox, QGridLayout, QFontComboBox ) from nw.enum import nwAlert @@ -46,10 +47,12 @@ class GuiConfigEditor(QDialog): self.svgGradient.setFixedWidth(80) self.theProject.countStatus() - self.tabMain = GuiConfigEditGeneral(self.theParent, self.theProject) + 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) @@ -74,6 +77,7 @@ class GuiConfigEditor(QDialog): needsRestart = False needsRestart |= self.tabMain.saveValues() + needsRestart |= self.tabEditor.saveValues() if needsRestart: msgBox = QMessageBox() @@ -95,12 +99,11 @@ class GuiConfigEditor(QDialog): class GuiConfigEditGeneral(QWidget): - def __init__(self, theParent, theProject): + def __init__(self, theParent): QWidget.__init__(self, theParent) self.mainConf = nw.CONFIG self.theParent = theParent - self.theProject = theProject self.outerBox = QVBoxLayout() # User Interface @@ -160,3 +163,105 @@ class GuiConfigEditGeneral(QWidget): return needsRestart # END Class GuiConfigEditGeneral + +class GuiConfigEditEditor(QWidget): + + def __init__(self, theParent): + QWidget.__init__(self, theParent) + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.outerBox = QVBoxLayout() + + # 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"), 0, 0) + self.textStyleForm.addWidget(self.textStyleFont, 0, 1, 1, 2) + self.textStyleForm.addWidget(QLabel("Font Size"), 1, 0) + self.textStyleForm.addWidget(self.textStyleSize, 1, 1) + self.textStyleForm.setColumnStretch(3, 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("pixles"), 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) + + # Assemble + self.outerBox.addWidget(self.textStyle) + self.outerBox.addWidget(self.textFlow) + self.outerBox.addStretch(0) + self.setLayout(self.outerBox) + + return + + def saveValues(self): + + 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 + self.mainConf.confChanged = True + + return False + +# END Class GuiConfigEditEditor diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index b955acb1..307cafce 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 @@ -113,7 +113,18 @@ class GuiDocEditor(QTextEdit): return True def initEditor(self): + + tHandle = self.theHandle + + # 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) + if self.mainConf.textFixedW: self.setLineWrapMode(QTextEdit.FixedPixelWidth) self.setLineWrapColumnOrWidth(self.mainConf.textWidth) @@ -121,12 +132,19 @@ class GuiDocEditor(QTextEdit): mTB = self.mainConf.textMargin[0] mLR = self.mainConf.textMargin[1] self.setViewportMargins(mLR,mTB,mLR,mTB) + 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 tHandle is not None: + 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/winmain.py b/nw/gui/winmain.py index 8e2400c4..0c8e8d76 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -465,6 +465,7 @@ class GuiMain(QMainWindow): if dlgConf.exec_() == QDialog.Accepted: logger.debug("Applying new preferences") self.initMain() + self.docEditor.initEditor() return True def editProjectDialog(self): From 8fd7d8c666ba65b67355e1f204b1be76e660305d Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Mon, 10 Jun 2019 22:46:15 +0200 Subject: [PATCH 05/11] Added gui elements for setting margins and auto-replace --- nw/config.py | 21 ++++++- nw/constants.py | 15 +++++ nw/gui/configeditor.py | 123 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 145 insertions(+), 14 deletions(-) diff --git a/nw/config.py b/nw/config.py index 8faa22d1..db9bfe59 100644 --- a/nw/config.py +++ b/nw/config.py @@ -14,9 +14,11 @@ import logging import configparser import nw -from os import path, mkdir, getcwd -from appdirs import user_config_dir -from datetime import datetime +from os import path, mkdir, getcwd +from appdirs import user_config_dir +from datetime import datetime + +from nw.constants import nwQuotes logger = logging.getLogger(__name__) @@ -77,6 +79,8 @@ class Config: self.doReplaceDQuote = True self.doReplaceDash = True self.doReplaceDots = True + self.styleSQuote = 1 + self.styleDQuote = 1 self.wordCountTimer = 5.0 self.fmtDoubleQuotes = ["“","”"] @@ -169,6 +173,8 @@ class Config: 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.styleSQuote = self._parseLine(cnfParse, cnfSec, "stylesquote", self.CNF_INT, self.styleSQuote) + self.styleDQuote = self._parseLine(cnfParse, cnfSec, "styledquote", self.CNF_INT, self.styleDQuote) self.spellLanguage = self._parseLine(cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage) ## Path @@ -176,6 +182,13 @@ class Config: for i in range(10): self.recentList[i] = self._parseLine(cnfParse, cnfSec, "recent%d" % i,self.CNF_STR, self.recentList[i]) + if self.styleSQuote >= 0 and self.styleSQuote < len(nwQuotes.SINGLE): + self.fmtSingleQuotes[0] = nwQuotes.SINGLE[self.styleSQuote][0] + self.fmtSingleQuotes[1] = nwQuotes.SINGLE[self.styleSQuote][1] + if self.styleDQuote >= 0 and self.styleDQuote < len(nwQuotes.DOUBLE): + self.fmtDoubleQuotes[0] = nwQuotes.DOUBLE[self.styleDQuote][0] + self.fmtDoubleQuotes[1] = nwQuotes.DOUBLE[self.styleDQuote][1] + return True def saveConfig(self): @@ -222,6 +235,8 @@ class Config: cnfParse.set(cnfSec,"repdquotes", str(self.doReplaceDQuote)) cnfParse.set(cnfSec,"repdash", str(self.doReplaceDash)) cnfParse.set(cnfSec,"repdots", str(self.doReplaceDots)) + cnfParse.set(cnfSec,"stylesquote",str(self.styleSQuote)) + cnfParse.set(cnfSec,"styledquote",str(self.styleDQuote)) cnfParse.set(cnfSec,"spellcheck", str(self.spellLanguage)) ## Path diff --git a/nw/constants.py b/nw/constants.py index 8a545d0c..79ec0c66 100644 --- a/nw/constants.py +++ b/nw/constants.py @@ -70,3 +70,18 @@ class nwLabels(): } # END Class nwLabels + +class nwQuotes(): + + SINGLE = [ + ("'", "'", "Straight"), + ("‘", "’", "English"), + ] + DOUBLE = [ + ("\"", "\"", "Straight"), + ("“", "”", "English"), + ("«", "»", "Frensh"), + ("»", "«", "Danish"), + ] + +# END Class nwQuotes diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py index 05df251d..66193319 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -23,7 +23,8 @@ from PyQt5.QtWidgets import ( QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox, QCheckBox, QGridLayout, QFontComboBox ) -from nw.enum import nwAlert +from nw.enum import nwAlert +from nw.constants import nwQuotes logger = logging.getLogger(__name__) @@ -141,6 +142,7 @@ class GuiConfigEditGeneral(QWidget): self.outerBox.addWidget(self.guiLook) self.outerBox.addWidget(self.autoSave) + self.outerBox.addStretch(1) self.setLayout(self.outerBox) return @@ -171,7 +173,7 @@ class GuiConfigEditEditor(QWidget): self.mainConf = nw.CONFIG self.theParent = theParent - self.outerBox = QVBoxLayout() + self.outerBox = QGridLayout() # Text Style self.textStyle = QGroupBox("Text Style", self) @@ -179,7 +181,7 @@ class GuiConfigEditEditor(QWidget): self.textStyle.setLayout(self.textStyleForm) self.textStyleFont = QFontComboBox() - self.textStyleFont.setMaximumWidth(250) + self.textStyleFont.setMaximumWidth(300) self.textStyleFont.setCurrentFont(QFont(self.mainConf.textFont)) self.textStyleSize = QSpinBox(self) @@ -188,11 +190,11 @@ class GuiConfigEditEditor(QWidget): self.textStyleSize.setSingleStep(1) self.textStyleSize.setValue(self.mainConf.textSize) - self.textStyleForm.addWidget(QLabel("Font"), 0, 0) - self.textStyleForm.addWidget(self.textStyleFont, 0, 1, 1, 2) - self.textStyleForm.addWidget(QLabel("Font Size"), 1, 0) - self.textStyleForm.addWidget(self.textStyleSize, 1, 1) - self.textStyleForm.setColumnStretch(3, 1) + 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) @@ -235,10 +237,100 @@ class GuiConfigEditEditor(QWidget): 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("pixles"), 0, 2) + self.textMarginForm.addWidget(QLabel("Vertical"), 1, 0) + self.textMarginForm.addWidget(self.textMarginVer, 1, 1) + self.textMarginForm.addWidget(QLabel("pixles"), 1, 2) + self.textMarginForm.addWidget(QLabel("Tab Width"), 2, 0) + self.textMarginForm.addWidget(self.textMarginTab, 2, 1) + self.textMarginForm.addWidget(QLabel("pixles"), 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.autoReplaceSStyle = QComboBox() + for n, qTup in enumerate(nwQuotes.SINGLE): + qA, qB, qStr = qTup + self.autoReplaceSStyle.addItem("%s: %stext%s" % (qStr, qA, qB), n) + singleIdx = self.autoReplaceSStyle.findData(self.mainConf.styleSQuote) + if singleIdx != -1: + self.autoReplaceSStyle.setCurrentIndex(singleIdx) + + 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.autoReplaceDStyle = QComboBox() + for n, qTup in enumerate(nwQuotes.DOUBLE): + qA, qB, qStr = qTup + self.autoReplaceDStyle.addItem("%s: %stext%s" % (qStr, qA, qB), n) + doubleIdx = self.autoReplaceDStyle.findData(self.mainConf.styleSQuote) + if doubleIdx != -1: + self.autoReplaceDStyle.setCurrentIndex(doubleIdx) + + 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("Style"), 1, 2) + self.autoReplaceForm.addWidget(self.autoReplaceSStyle, 1, 3) + self.autoReplaceForm.addWidget(QLabel("Double Quotes"), 2, 0) + self.autoReplaceForm.addWidget(self.autoReplaceDQ, 2, 1) + self.autoReplaceForm.addWidget(QLabel("Style"), 2, 2) + self.autoReplaceForm.addWidget(self.autoReplaceDStyle, 2, 3) + self.autoReplaceForm.setColumnStretch(4, 1) + # Assemble - self.outerBox.addWidget(self.textStyle) - self.outerBox.addWidget(self.textFlow) - self.outerBox.addStretch(0) + 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 @@ -260,6 +352,15 @@ class GuiConfigEditEditor(QWidget): 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 + self.mainConf.confChanged = True return False From 4aa6e25acd48425b3faa771c9d808ffc5feaff91 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Mon, 10 Jun 2019 23:14:53 +0200 Subject: [PATCH 06/11] Connected the last items added to preferences gui to config --- nw/config.py | 24 ++++++++++++++++++------ nw/constants.py | 10 +++++++++- nw/gui/configeditor.py | 12 ++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/nw/config.py b/nw/config.py index db9bfe59..8cb91674 100644 --- a/nw/config.py +++ b/nw/config.py @@ -182,12 +182,8 @@ class Config: for i in range(10): self.recentList[i] = self._parseLine(cnfParse, cnfSec, "recent%d" % i,self.CNF_STR, self.recentList[i]) - if self.styleSQuote >= 0 and self.styleSQuote < len(nwQuotes.SINGLE): - self.fmtSingleQuotes[0] = nwQuotes.SINGLE[self.styleSQuote][0] - self.fmtSingleQuotes[1] = nwQuotes.SINGLE[self.styleSQuote][1] - if self.styleDQuote >= 0 and self.styleDQuote < len(nwQuotes.DOUBLE): - self.fmtDoubleQuotes[0] = nwQuotes.DOUBLE[self.styleDQuote][0] - self.fmtDoubleQuotes[1] = nwQuotes.DOUBLE[self.styleDQuote][1] + self.setSingleQuotes(self.styleSQuote) + self.setDoubleQuotes(self.styleDQuote) return True @@ -309,6 +305,22 @@ class Config: self.confChanged = True return True + def setSingleQuotes(self, quoteStyle): + if quoteStyle >= 0 and quoteStyle < len(nwQuotes.SINGLE): + self.fmtSingleQuotes[0] = nwQuotes.SINGLE[quoteStyle][0] + self.fmtSingleQuotes[1] = nwQuotes.SINGLE[quoteStyle][1] + self.styleSQuote = quoteStyle + return True + return False + + def setDoubleQuotes(self, quoteStyle): + if quoteStyle >= 0 and quoteStyle < len(nwQuotes.DOUBLE): + self.fmtDoubleQuotes[0] = nwQuotes.DOUBLE[quoteStyle][0] + self.fmtDoubleQuotes[1] = nwQuotes.DOUBLE[quoteStyle][1] + self.styleDQuote = quoteStyle + return True + return False + ## # Internal Functions ## diff --git a/nw/constants.py b/nw/constants.py index 79ec0c66..9c93901c 100644 --- a/nw/constants.py +++ b/nw/constants.py @@ -76,12 +76,20 @@ class nwQuotes(): SINGLE = [ ("'", "'", "Straight"), ("‘", "’", "English"), + ("‹", "›", "French"), + ("›", "‹", "Danish"), + ("’", "’", "Swedish"), + ("‚", "‘", "German"), + ("’", "‚", "Dutch"), ] DOUBLE = [ ("\"", "\"", "Straight"), ("“", "”", "English"), - ("«", "»", "Frensh"), + ("«", "»", "French"), ("»", "«", "Danish"), + ("”", "”", "Swedish"), + ("„", "“", "German"), + ("„", "”", "Dutch"), ] # END Class nwQuotes diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py index 66193319..9ba93299 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -361,6 +361,18 @@ class GuiConfigEditEditor(QWidget): self.mainConf.textMargin[1] = textMarginV self.mainConf.tabWidth = tabWidth + doReplace = self.autoReplaceMain.isChecked() + doReplaceSQuote = self.autoReplaceSQ.isChecked() + doReplaceDQuote = self.autoReplaceDQ.isChecked() + styleSQuote = self.autoReplaceSStyle.currentData() + styleDQuote = self.autoReplaceDStyle.currentData() + + self.mainConf.doReplace = doReplace + self.mainConf.doReplaceSQuote = doReplaceSQuote + self.mainConf.doReplaceDQuote = doReplaceDQuote + self.mainConf.setSingleQuotes(styleSQuote) + self.mainConf.setDoubleQuotes(styleDQuote) + self.mainConf.confChanged = True return False From f9f8d292d1ff580950fcb72804d1174c455f5a6c Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Tue, 11 Jun 2019 15:43:04 +0200 Subject: [PATCH 07/11] Finished all the autoreplace options in the config gui --- nw/config.py | 96 ++++++++------------- nw/constants.py | 23 ------ nw/gui/configeditor.py | 184 ++++++++++++++++++++++++++++++----------- nw/gui/doceditor.py | 13 ++- 4 files changed, 181 insertions(+), 135 deletions(-) diff --git a/nw/config.py b/nw/config.py index 8cb91674..e7da0533 100644 --- a/nw/config.py +++ b/nw/config.py @@ -14,11 +14,9 @@ import logging import configparser import nw -from os import path, mkdir, getcwd -from appdirs import user_config_dir -from datetime import datetime - -from nw.constants import nwQuotes +from os import path, mkdir, getcwd +from appdirs import user_config_dir +from datetime import datetime logger = logging.getLogger(__name__) @@ -79,13 +77,10 @@ class Config: self.doReplaceDQuote = True self.doReplaceDash = True self.doReplaceDots = True - self.styleSQuote = 1 - self.styleDQuote = 1 self.wordCountTimer = 5.0 - self.fmtDoubleQuotes = ["“","”"] self.fmtSingleQuotes = ["‘","’"] - self.fmtApostrophe = "’" + self.fmtDoubleQuotes = ["“","”"] self.spellLanguage = "en_GB" @@ -160,31 +155,28 @@ class Config: ## Editor cnfSec = "Editor" - 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.styleSQuote = self._parseLine(cnfParse, cnfSec, "stylesquote", self.CNF_INT, self.styleSQuote) - self.styleDQuote = self._parseLine(cnfParse, cnfSec, "styledquote", self.CNF_INT, self.styleDQuote) - 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" for i in range(10): self.recentList[i] = self._parseLine(cnfParse, cnfSec, "recent%d" % i,self.CNF_STR, self.recentList[i]) - self.setSingleQuotes(self.styleSQuote) - self.setDoubleQuotes(self.styleDQuote) - return True def saveConfig(self): @@ -218,22 +210,22 @@ class Config: ## Editor cnfSec = "Editor" cnfParse.add_section(cnfSec) - 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,"stylesquote",str(self.styleSQuote)) - cnfParse.set(cnfSec,"styledquote",str(self.styleDQuote)) - 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" @@ -305,22 +297,6 @@ class Config: self.confChanged = True return True - def setSingleQuotes(self, quoteStyle): - if quoteStyle >= 0 and quoteStyle < len(nwQuotes.SINGLE): - self.fmtSingleQuotes[0] = nwQuotes.SINGLE[quoteStyle][0] - self.fmtSingleQuotes[1] = nwQuotes.SINGLE[quoteStyle][1] - self.styleSQuote = quoteStyle - return True - return False - - def setDoubleQuotes(self, quoteStyle): - if quoteStyle >= 0 and quoteStyle < len(nwQuotes.DOUBLE): - self.fmtDoubleQuotes[0] = nwQuotes.DOUBLE[quoteStyle][0] - self.fmtDoubleQuotes[1] = nwQuotes.DOUBLE[quoteStyle][1] - self.styleDQuote = quoteStyle - return True - return False - ## # Internal Functions ## diff --git a/nw/constants.py b/nw/constants.py index 9c93901c..8a545d0c 100644 --- a/nw/constants.py +++ b/nw/constants.py @@ -70,26 +70,3 @@ class nwLabels(): } # END Class nwLabels - -class nwQuotes(): - - SINGLE = [ - ("'", "'", "Straight"), - ("‘", "’", "English"), - ("‹", "›", "French"), - ("›", "‹", "Danish"), - ("’", "’", "Swedish"), - ("‚", "‘", "German"), - ("’", "‚", "Dutch"), - ] - DOUBLE = [ - ("\"", "\"", "Straight"), - ("“", "”", "English"), - ("«", "»", "French"), - ("»", "«", "Danish"), - ("”", "”", "Swedish"), - ("„", "“", "German"), - ("„", "”", "Dutch"), - ] - -# END Class nwQuotes diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py index 9ba93299..f08a5385 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -23,8 +23,7 @@ from PyQt5.QtWidgets import ( QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox, QCheckBox, QGridLayout, QFontComboBox ) -from nw.enum import nwAlert -from nw.constants import nwQuotes +from nw.enum import nwAlert logger = logging.getLogger(__name__) @@ -76,9 +75,16 @@ class GuiConfigEditor(QDialog): logger.verbose("ConfigEditor save button clicked") + validEntries = True needsRestart = False - needsRestart |= self.tabMain.saveValues() - needsRestart |= self.tabEditor.saveValues() + + retA, retB = self.tabMain.saveValues() + validEntries &= retA + needsRestart |= retB + + retA, retB = self.tabEditor.saveValues() + validEntries &= retA + needsRestart |= retB if needsRestart: msgBox = QMessageBox() @@ -87,7 +93,8 @@ class GuiConfigEditor(QDialog): "Some changes will not be applied until
%s has been restarted." % nw.__package__ ) - self.accept() + if validEntries: + self.accept() return @@ -149,12 +156,14 @@ class GuiConfigEditGeneral(QWidget): def saveValues(self): + validEntries = True + needsRestart = False + autoSaveDoc = self.autoSaveDoc.value() autoSaveProj = self.autoSaveProj.value() guiTheme = self.guiLookTheme.currentData() # Check if restart is needed - needsRestart = False needsRestart |= self.mainConf.guiTheme != guiTheme self.mainConf.autoSaveDoc = autoSaveDoc @@ -162,7 +171,7 @@ class GuiConfigEditGeneral(QWidget): self.mainConf.guiTheme = guiTheme self.mainConf.confChanged = True - return needsRestart + return validEntries, needsRestart # END Class GuiConfigEditGeneral @@ -181,7 +190,7 @@ class GuiConfigEditEditor(QWidget): self.textStyle.setLayout(self.textStyleForm) self.textStyleFont = QFontComboBox() - self.textStyleFont.setMaximumWidth(300) + self.textStyleFont.setMaximumWidth(250) self.textStyleFont.setCurrentFont(QFont(self.mainConf.textFont)) self.textStyleSize = QSpinBox(self) @@ -230,7 +239,7 @@ class GuiConfigEditEditor(QWidget): 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("pixles"), 0, 3) + 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) @@ -262,13 +271,13 @@ class GuiConfigEditEditor(QWidget): self.textMarginForm.addWidget(QLabel("Horizontal"), 0, 0) self.textMarginForm.addWidget(self.textMarginHor, 0, 1) - self.textMarginForm.addWidget(QLabel("pixles"), 0, 2) + 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("pixles"), 1, 2) + 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("pixles"), 2, 2) + self.textMarginForm.addWidget(QLabel("px"), 2, 2) self.textMarginForm.setColumnStretch(4, 1) # Auto-Replace @@ -290,13 +299,17 @@ class GuiConfigEditEditor(QWidget): else: self.autoReplaceSQ.setCheckState(Qt.Unchecked) - self.autoReplaceSStyle = QComboBox() - for n, qTup in enumerate(nwQuotes.SINGLE): - qA, qB, qStr = qTup - self.autoReplaceSStyle.addItem("%s: %stext%s" % (qStr, qA, qB), n) - singleIdx = self.autoReplaceSStyle.findData(self.mainConf.styleSQuote) - if singleIdx != -1: - self.autoReplaceSStyle.setCurrentIndex(singleIdx) + 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.") @@ -305,25 +318,51 @@ class GuiConfigEditEditor(QWidget): else: self.autoReplaceDQ.setCheckState(Qt.Unchecked) - self.autoReplaceDStyle = QComboBox() - for n, qTup in enumerate(nwQuotes.DOUBLE): - qA, qB, qStr = qTup - self.autoReplaceDStyle.addItem("%s: %stext%s" % (qStr, qA, qB), n) - doubleIdx = self.autoReplaceDStyle.findData(self.mainConf.styleSQuote) - if doubleIdx != -1: - self.autoReplaceDStyle.setCurrentIndex(doubleIdx) + self.autoReplaceDStyleO = QLineEdit() + self.autoReplaceDStyleO.setMaxLength(1) + self.autoReplaceDStyleO.setFixedWidth(30) + self.autoReplaceDStyleO.setAlignment(Qt.AlignCenter) + self.autoReplaceDStyleO.setText(self.mainConf.fmtDoubleQuotes[0]) - 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("Style"), 1, 2) - self.autoReplaceForm.addWidget(self.autoReplaceSStyle, 1, 3) - self.autoReplaceForm.addWidget(QLabel("Double Quotes"), 2, 0) - self.autoReplaceForm.addWidget(self.autoReplaceDQ, 2, 1) - self.autoReplaceForm.addWidget(QLabel("Style"), 2, 2) - self.autoReplaceForm.addWidget(self.autoReplaceDStyle, 2, 3) - self.autoReplaceForm.setColumnStretch(4, 1) + 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) @@ -337,6 +376,8 @@ class GuiConfigEditEditor(QWidget): def saveValues(self): + validEntries = True + textFont = self.textStyleFont.currentFont().family() textSize = self.textStyleSize.value() @@ -348,10 +389,10 @@ class GuiConfigEditEditor(QWidget): doJustify = self.textFlowJustify.isChecked() autoSelect = self.testFlowAutoSelect.isChecked() - self.mainConf.textWidth = textWidth - self.mainConf.textFixedW = textFixedW - self.mainConf.doJustify = doJustify - self.mainConf.autoSelect = autoSelect + self.mainConf.textWidth = textWidth + self.mainConf.textFixedW = textFixedW + self.mainConf.doJustify = doJustify + self.mainConf.autoSelect = autoSelect textMarginH = self.textMarginHor.value() textMarginV = self.textMarginVer.value() @@ -361,20 +402,67 @@ class GuiConfigEditEditor(QWidget): self.mainConf.textMargin[1] = textMarginV self.mainConf.tabWidth = tabWidth - doReplace = self.autoReplaceMain.isChecked() - doReplaceSQuote = self.autoReplaceSQ.isChecked() - doReplaceDQuote = self.autoReplaceDQ.isChecked() - styleSQuote = self.autoReplaceSStyle.currentData() - styleDQuote = self.autoReplaceDStyle.currentData() + 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.setSingleQuotes(styleSQuote) - self.mainConf.setDoubleQuotes(styleDQuote) + 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): + validOnes = [ + "\"","'", + "“","”","„", + "‘","’","‚", + "«","»","‹","›", + "『","』","「","」", + "《","》","〈","〉" + ] + if len(toCheck) != 1: + return False + if toCheck in validOnes: + return True return False # END Class GuiConfigEditEditor diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 307cafce..9b36790a 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -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,8 +112,10 @@ class GuiDocEditor(QTextEdit): return True def initEditor(self): - - tHandle = self.theHandle + """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: @@ -125,6 +126,7 @@ class GuiDocEditor(QTextEdit): 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) @@ -133,6 +135,7 @@ class GuiDocEditor(QTextEdit): 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) @@ -140,7 +143,9 @@ class GuiDocEditor(QTextEdit): theOpt.setAlignment(Qt.AlignJustify) self.theQDoc.setDefaultTextOption(theOpt) - if tHandle is not None: + # 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) From 213bafe61edf82853c329b18d26f1e73d4ef55c8 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Tue, 11 Jun 2019 15:50:47 +0200 Subject: [PATCH 08/11] Cleaned up the alignment of the general tab --- nw/gui/configeditor.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py index f08a5385..d78e9990 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -112,25 +112,28 @@ class GuiConfigEditGeneral(QWidget): self.mainConf = nw.CONFIG self.theParent = theParent - self.outerBox = QVBoxLayout() + self.outerBox = QGridLayout() # User Interface self.guiLook = QGroupBox("User Interface", self) - self.guiLookForm = QFormLayout(self) + self.guiLookForm = QGridLayout(self) self.guiLook.setLayout(self.guiLookForm) - self.theThemes = self.theParent.theTheme.listThemes() 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.addRow("Theme", self.guiLookTheme) + + self.guiLookForm.addWidget(QLabel("Theme"), 0, 0) + self.guiLookForm.addWidget(self.guiLookTheme, 0, 1) # AutoSave self.autoSave = QGroupBox("Auto-Save", self) - self.autoSaveForm = QFormLayout(self) + self.autoSaveForm = QGridLayout(self) self.autoSave.setLayout(self.autoSaveForm) self.autoSaveDoc = QSpinBox(self) @@ -138,18 +141,24 @@ class GuiConfigEditGeneral(QWidget): self.autoSaveDoc.setMaximum(600) self.autoSaveDoc.setSingleStep(1) self.autoSaveDoc.setValue(self.mainConf.autoSaveDoc) - self.autoSaveForm.addRow("Save Document (seconds)", self.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.addRow("Save Project (seconds)", self.autoSaveProj) - self.outerBox.addWidget(self.guiLook) - self.outerBox.addWidget(self.autoSave) - self.outerBox.addStretch(1) + 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) + + self.outerBox.addWidget(self.guiLook, 0, 0) + self.outerBox.addWidget(self.autoSave, 1, 0) + self.outerBox.setColumnStretch(2, 1) + self.outerBox.setRowStretch(2, 1) self.setLayout(self.outerBox) return From 4b72c856c84b4e0d2e4992164e88241b66e3f8d5 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Tue, 11 Jun 2019 16:14:33 +0200 Subject: [PATCH 09/11] Added the complete list of horizontal text quote marks in unicode format from wikipedia --- nw/constants.py | 50 ++++++++++++++++++++++++++++++++++++++++++ nw/gui/configeditor.py | 13 +++-------- 2 files changed, 53 insertions(+), 10 deletions(-) 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 index d78e9990..de5dc88b 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -23,7 +23,8 @@ from PyQt5.QtWidgets import ( QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox, QCheckBox, QGridLayout, QFontComboBox ) -from nw.enum import nwAlert +from nw.enum import nwAlert +from nw.constants import nwQuotes logger = logging.getLogger(__name__) @@ -460,17 +461,9 @@ class GuiConfigEditEditor(QWidget): ## def _checkQuoteSymbol(self, toCheck): - validOnes = [ - "\"","'", - "“","”","„", - "‘","’","‚", - "«","»","‹","›", - "『","』","「","」", - "《","》","〈","〉" - ] if len(toCheck) != 1: return False - if toCheck in validOnes: + if toCheck in nwQuotes.SYMBOLS: return True return False From 48da536bc0d05d3ee3d61189b57b2a89222b0a8b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Tue, 11 Jun 2019 18:38:51 +0200 Subject: [PATCH 10/11] Added option for selecting spell check dictionary --- nw/gui/configeditor.py | 44 +++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py index de5dc88b..fa1d63f4 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -11,6 +11,7 @@ """ import logging +import enchant import nw from os import path @@ -52,7 +53,7 @@ class GuiConfigEditor(QDialog): self.tabEditor = GuiConfigEditEditor(self.theParent) self.tabWidget = QTabWidget() - self.tabWidget.addTab(self.tabMain, "General") + self.tabWidget.addTab(self.tabMain, "General") self.tabWidget.addTab(self.tabEditor, "Editor") self.setLayout(self.outerBox) @@ -132,6 +133,22 @@ class GuiConfigEditGeneral(QWidget): 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) @@ -156,10 +173,12 @@ class GuiConfigEditGeneral(QWidget): self.autoSaveForm.addWidget(self.autoSaveProj, 1, 1) self.autoSaveForm.addWidget(QLabel("seconds"), 1, 2) - self.outerBox.addWidget(self.guiLook, 0, 0) - self.outerBox.addWidget(self.autoSave, 1, 0) + # 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(2, 1) + self.outerBox.setRowStretch(3, 1) self.setLayout(self.outerBox) return @@ -169,17 +188,20 @@ class GuiConfigEditGeneral(QWidget): validEntries = True needsRestart = False - autoSaveDoc = self.autoSaveDoc.value() - autoSaveProj = self.autoSaveProj.value() - guiTheme = self.guiLookTheme.currentData() + 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.autoSaveDoc = autoSaveDoc - self.mainConf.autoSaveProj = autoSaveProj - self.mainConf.guiTheme = guiTheme - self.mainConf.confChanged = True + self.mainConf.guiTheme = guiTheme + self.mainConf.spellLanguage = spellLanguage + self.mainConf.autoSaveDoc = autoSaveDoc + self.mainConf.autoSaveProj = autoSaveProj + + self.mainConf.confChanged = True return validEntries, needsRestart From 22ae5fdaf01ec1360b231c04af5e210f2942c801 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Tue, 11 Jun 2019 18:46:13 +0200 Subject: [PATCH 11/11] Updated tests --- sample/sampleNovel/meta/sessionInfo.log | 23 +++++++++++++++++++++++ sample/sampleNovel/nwProject.nwx | 4 ++-- tests/reference/novelwriter.conf | 9 ++++++--- 3 files changed, 31 insertions(+), 5 deletions(-) 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]