Config class improvements (#826)
* Subclass ConfigParser * Remove redundant variables and update tests * Update file description * Move NWConfigParser class to common.py * Improve coverage of config class * Use NWConfigParser also in themes.py * Update config class file header * Fix comment * Update the usage of ConfigParser to recommended practice and extend tests * Improve logging for info level a bit
This commit is contained in:
committed by
GitHub
parent
e92506dc07
commit
e5c715695e
@@ -28,7 +28,6 @@ version = "1.5"
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = "1.5-alpha0"
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
os.environ["TZ"] = "Europe/Oslo"
|
||||
|
||||
+174
-63
@@ -26,24 +26,27 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
from configparser import ConfigParser
|
||||
|
||||
from PyQt5.QtWidgets import qApp
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
from nw.error import logException
|
||||
from nw.constants import nwConst, nwUnicode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Checker Functions
|
||||
# =============================================================================================== #
|
||||
|
||||
def checkString(value, default, allowNone=False):
|
||||
"""Check if a variable is a string or a none.
|
||||
"""
|
||||
if allowNone:
|
||||
if value is None:
|
||||
return None
|
||||
if value == "None":
|
||||
return None
|
||||
if allowNone and (value is None or value == "None"):
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
return str(value)
|
||||
return default
|
||||
@@ -52,11 +55,8 @@ def checkString(value, default, allowNone=False):
|
||||
def checkInt(value, default, allowNone=False):
|
||||
"""Check if a variable is an integer or a none.
|
||||
"""
|
||||
if allowNone:
|
||||
if value is None:
|
||||
return None
|
||||
if value == "None":
|
||||
return None
|
||||
if allowNone and (value is None or value == "None"):
|
||||
return None
|
||||
try:
|
||||
return int(value)
|
||||
except Exception:
|
||||
@@ -66,11 +66,9 @@ def checkInt(value, default, allowNone=False):
|
||||
def checkBool(value, default, allowNone=False):
|
||||
"""Check if a variable is a boolean or a none.
|
||||
"""
|
||||
if allowNone:
|
||||
if value is None:
|
||||
return None
|
||||
if value == "None":
|
||||
return None
|
||||
if allowNone and (value is None or value == "None"):
|
||||
return None
|
||||
|
||||
if isinstance(value, str):
|
||||
if value == "True":
|
||||
return True
|
||||
@@ -78,6 +76,7 @@ def checkBool(value, default, allowNone=False):
|
||||
return False
|
||||
else:
|
||||
return default
|
||||
|
||||
elif isinstance(value, int):
|
||||
if value == 1:
|
||||
return True
|
||||
@@ -85,67 +84,69 @@ def checkBool(value, default, allowNone=False):
|
||||
return False
|
||||
else:
|
||||
return default
|
||||
|
||||
return default
|
||||
|
||||
|
||||
def checkHandle(value, default, allowNone=False):
|
||||
"""Check if a value is a handle.
|
||||
"""
|
||||
if allowNone:
|
||||
if value is None:
|
||||
return None
|
||||
if value == "None":
|
||||
return None
|
||||
if allowNone and (value is None or value == "None"):
|
||||
return None
|
||||
if isHandle(value):
|
||||
return str(value)
|
||||
return default
|
||||
|
||||
|
||||
def isHandle(theString):
|
||||
# =============================================================================================== #
|
||||
# Validator Functions
|
||||
# =============================================================================================== #
|
||||
|
||||
def isHandle(value):
|
||||
"""Check if a string is a valid novelWriter handle.
|
||||
Note: This is case sensitive. Must be lower case!
|
||||
"""
|
||||
if not isinstance(theString, str):
|
||||
if not isinstance(value, str):
|
||||
return False
|
||||
if len(theString) != 13:
|
||||
if len(value) != 13:
|
||||
return False
|
||||
for c in theString:
|
||||
for c in value:
|
||||
if c not in "0123456789abcdef":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def isTitleTag(theString):
|
||||
def isTitleTag(value):
|
||||
"""Check if a string is a valid title string.
|
||||
"""
|
||||
if not isinstance(theString, str):
|
||||
if not isinstance(value, str):
|
||||
return False
|
||||
if len(theString) != 7:
|
||||
if len(value) != 7:
|
||||
return False
|
||||
if not theString.startswith("T"):
|
||||
if not value.startswith("T"):
|
||||
return False
|
||||
for c in theString[1:]:
|
||||
for c in value[1:]:
|
||||
if c not in "0123456789":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def isItemClass(theString):
|
||||
"""Check if an item is a calid nwItemClass identifier.
|
||||
def isItemClass(value):
|
||||
"""Check if a string is a valid nwItemClass identifier.
|
||||
"""
|
||||
return theString in nwItemClass.__members__
|
||||
return value in nwItemClass.__members__
|
||||
|
||||
|
||||
def isItemType(theString):
|
||||
"""Check if an item is a calid nwItemType identifier.
|
||||
def isItemType(value):
|
||||
"""Check if a string is a valid nwItemType identifier.
|
||||
"""
|
||||
return theString in nwItemType.__members__
|
||||
return value in nwItemType.__members__
|
||||
|
||||
|
||||
def isItemLayout(theString):
|
||||
"""Check if an item is a calid nwItemLayout identifier.
|
||||
def isItemLayout(value):
|
||||
"""Check if a string is a valid nwItemLayout identifier.
|
||||
"""
|
||||
return theString in nwItemLayout.__members__
|
||||
return value in nwItemLayout.__members__
|
||||
|
||||
|
||||
def hexToInt(value, default=0):
|
||||
@@ -159,14 +160,19 @@ def hexToInt(value, default=0):
|
||||
return default
|
||||
|
||||
|
||||
def formatInt(theInt):
|
||||
# =============================================================================================== #
|
||||
# Formatting Functions
|
||||
# =============================================================================================== #
|
||||
|
||||
def formatInt(value):
|
||||
"""Formats an integer with k, M, G etc.
|
||||
"""
|
||||
postFix = ["k", "M", "G", "T", "P", "E"]
|
||||
theVal = float(theInt)
|
||||
if not isinstance(value, int):
|
||||
return "ERR"
|
||||
|
||||
theVal = float(value)
|
||||
if theVal > 1000.0:
|
||||
for pF in postFix:
|
||||
for pF in ["k", "M", "G", "T", "P", "E"]:
|
||||
theVal /= 1000.0
|
||||
if theVal < 1000.0:
|
||||
if theVal < 10.0:
|
||||
@@ -176,7 +182,7 @@ def formatInt(theInt):
|
||||
else:
|
||||
return f"{theVal:3.0f}{nwUnicode.U_THSP}{pF}"
|
||||
|
||||
return str(theInt)
|
||||
return str(value)
|
||||
|
||||
|
||||
def formatTimeStamp(theTime, fileSafe=False):
|
||||
@@ -201,6 +207,23 @@ def formatTime(tS):
|
||||
return "ERROR"
|
||||
|
||||
|
||||
def parseTimeStamp(theStamp, default, allowNone=False):
|
||||
"""Parses a text representation of a time stamp and converts it into
|
||||
a float. Note that negative timestamps cause an OSError on Windows.
|
||||
See https://bugs.python.org/issue29097
|
||||
"""
|
||||
if str(theStamp).lower() == "none" and allowNone:
|
||||
return None
|
||||
try:
|
||||
return datetime.strptime(theStamp, nwConst.FMT_TSTAMP).timestamp()
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# String Functions
|
||||
# =============================================================================================== #
|
||||
|
||||
def splitVersionNumber(vString):
|
||||
""" Splits a version string on the form aa.bb.cc into major, minor
|
||||
and patch, and computes an integer value aabbcc.
|
||||
@@ -308,27 +331,8 @@ def fuzzyTime(secDiff):
|
||||
).format(int(round(secDiff/31557600)))
|
||||
|
||||
|
||||
def makeFileNameSafe(theText):
|
||||
"""Returns a filename safe version of the text.
|
||||
"""
|
||||
cleanName = ""
|
||||
for c in theText.strip():
|
||||
if c.isalpha() or c.isdigit() or c == " ":
|
||||
cleanName += c
|
||||
return cleanName
|
||||
|
||||
|
||||
def getGuiItem(theName):
|
||||
"""Returns a QtWidget based on its objectName.
|
||||
"""
|
||||
for qWidget in qApp.topLevelWidgets():
|
||||
if qWidget.objectName() == theName:
|
||||
return qWidget
|
||||
return None
|
||||
|
||||
|
||||
def numberToRoman(numVal, isLower=False):
|
||||
"""Convert an integer to a roman number.
|
||||
"""Convert an integer to a Roman number.
|
||||
"""
|
||||
if not isinstance(numVal, int):
|
||||
return "NAN"
|
||||
@@ -349,3 +353,110 @@ def numberToRoman(numVal, isLower=False):
|
||||
break
|
||||
|
||||
return romNum.lower() if isLower else romNum
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Other Functions
|
||||
# =============================================================================================== #
|
||||
|
||||
def makeFileNameSafe(theText):
|
||||
"""Returns a filename safe version of the text.
|
||||
"""
|
||||
cleanName = ""
|
||||
for c in theText.strip():
|
||||
if c.isalpha() or c.isdigit() or c == " ":
|
||||
cleanName += c
|
||||
return cleanName
|
||||
|
||||
|
||||
def getGuiItem(theName):
|
||||
"""Returns a QtWidget based on its objectName.
|
||||
"""
|
||||
for qWidget in qApp.topLevelWidgets():
|
||||
if qWidget.objectName() == theName:
|
||||
return qWidget
|
||||
return None
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Classes
|
||||
# =============================================================================================== #
|
||||
|
||||
class NWConfigParser(ConfigParser):
|
||||
|
||||
CNF_STR = 0
|
||||
CNF_INT = 1
|
||||
CNF_BOOL = 2
|
||||
CNF_S_LST = 3
|
||||
CNF_I_LST = 4
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def rdStr(self, section, option, default):
|
||||
"""Read string value.
|
||||
"""
|
||||
return self._parseLine(section, option, default, self.CNF_STR)
|
||||
|
||||
def rdInt(self, section, option, default):
|
||||
"""Read integer value.
|
||||
"""
|
||||
return self._parseLine(section, option, default, self.CNF_INT)
|
||||
|
||||
def rdBool(self, section, option, default):
|
||||
"""Read boolean value.
|
||||
"""
|
||||
return self._parseLine(section, option, default, self.CNF_BOOL)
|
||||
|
||||
def rdStrList(self, section, option, default):
|
||||
"""Read string list.
|
||||
"""
|
||||
return self._parseLine(section, option, default, self.CNF_S_LST)
|
||||
|
||||
def rdIntList(self, section, option, default):
|
||||
"""Read integer list.
|
||||
"""
|
||||
return self._parseLine(section, option, default, self.CNF_I_LST)
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _unpackList(self, value, default, type):
|
||||
"""Unpack a comma-separated string of items into a list.
|
||||
"""
|
||||
inList = value.split(",")
|
||||
outList = []
|
||||
if isinstance(default, list):
|
||||
outList = default.copy()
|
||||
for i in range(min(len(inList), len(outList))):
|
||||
try:
|
||||
if type == self.CNF_S_LST:
|
||||
outList[i] = inList[i].strip()
|
||||
elif type == self.CNF_I_LST:
|
||||
outList[i] = int(inList[i].strip())
|
||||
except Exception:
|
||||
continue
|
||||
return outList
|
||||
|
||||
def _parseLine(self, section, option, default, type):
|
||||
"""Parse a line and return the correct datatype.
|
||||
"""
|
||||
if self.has_option(section, option):
|
||||
try:
|
||||
if type == self.CNF_STR:
|
||||
return self.get(section, option)
|
||||
elif type == self.CNF_INT:
|
||||
return self.getint(section, option)
|
||||
elif type == self.CNF_BOOL:
|
||||
return self.getboolean(section, option)
|
||||
elif type in (self.CNF_I_LST, self.CNF_S_LST):
|
||||
return self._unpackList(self.get(section, option), default, type)
|
||||
except ValueError:
|
||||
logger.error("Could not read '%s':'%s' from config", str(section), str(option))
|
||||
logException()
|
||||
return default
|
||||
|
||||
return default
|
||||
|
||||
# END Class NWConfigParser
|
||||
|
||||
+170
-372
@@ -28,7 +28,6 @@ import sys
|
||||
import json
|
||||
import shutil
|
||||
import logging
|
||||
import configparser
|
||||
|
||||
from time import time
|
||||
|
||||
@@ -39,7 +38,7 @@ from PyQt5.QtCore import (
|
||||
)
|
||||
|
||||
from nw.error import logException
|
||||
from nw.common import splitVersionNumber, formatTimeStamp
|
||||
from nw.common import splitVersionNumber, formatTimeStamp, NWConfigParser
|
||||
from nw.constants import nwConst, nwFiles, nwUnicode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -47,12 +46,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class Config:
|
||||
|
||||
CNF_STR = 0
|
||||
CNF_INT = 1
|
||||
CNF_BOOL = 2
|
||||
CNF_S_LST = 3
|
||||
CNF_I_LST = 4
|
||||
|
||||
LANG_NW = 1
|
||||
LANG_PROJ = 2
|
||||
|
||||
@@ -432,11 +425,11 @@ class Config:
|
||||
if self.confPath is None:
|
||||
return False
|
||||
|
||||
cnfParse = configparser.ConfigParser()
|
||||
theConf = NWConfigParser()
|
||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||
try:
|
||||
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
||||
cnfParse.read_file(inFile)
|
||||
theConf.read_file(inFile)
|
||||
except Exception as e:
|
||||
logger.error("Could not load config file")
|
||||
logException()
|
||||
@@ -447,237 +440,95 @@ class Config:
|
||||
|
||||
# Main
|
||||
cnfSec = "Main"
|
||||
self.guiTheme = self._parseLine(
|
||||
cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme
|
||||
)
|
||||
self.guiSyntax = self._parseLine(
|
||||
cnfParse, cnfSec, "syntax", self.CNF_STR, self.guiSyntax
|
||||
)
|
||||
self.guiIcons = self._parseLine(
|
||||
cnfParse, cnfSec, "icons", self.CNF_STR, self.guiIcons
|
||||
)
|
||||
self.guiDark = self._parseLine(
|
||||
cnfParse, cnfSec, "guidark", self.CNF_BOOL, self.guiDark
|
||||
)
|
||||
self.guiFont = self._parseLine(
|
||||
cnfParse, cnfSec, "guifont", self.CNF_STR, self.guiFont
|
||||
)
|
||||
self.guiFontSize = self._parseLine(
|
||||
cnfParse, cnfSec, "guifontsize", self.CNF_INT, self.guiFontSize
|
||||
)
|
||||
self.lastNotes = self._parseLine(
|
||||
cnfParse, cnfSec, "lastnotes", self.CNF_STR, self.lastNotes
|
||||
)
|
||||
self.guiLang = self._parseLine(
|
||||
cnfParse, cnfSec, "guilang", self.CNF_STR, self.guiLang
|
||||
)
|
||||
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
|
||||
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
|
||||
self.guiIcons = theConf.rdStr(cnfSec, "icons", self.guiIcons)
|
||||
self.guiDark = theConf.rdBool(cnfSec, "guidark", self.guiDark)
|
||||
self.guiFont = theConf.rdStr(cnfSec, "guifont", self.guiFont)
|
||||
self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize", self.guiFontSize)
|
||||
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
|
||||
self.guiLang = theConf.rdStr(cnfSec, "guilang", self.guiLang)
|
||||
|
||||
# Sizes
|
||||
cnfSec = "Sizes"
|
||||
self.winGeometry = self._parseLine(
|
||||
cnfParse, cnfSec, "geometry", self.CNF_I_LST, self.winGeometry
|
||||
)
|
||||
self.prefGeometry = self._parseLine(
|
||||
cnfParse, cnfSec, "preferences", self.CNF_I_LST, self.prefGeometry
|
||||
)
|
||||
self.treeColWidth = self._parseLine(
|
||||
cnfParse, cnfSec, "treecols", self.CNF_I_LST, self.treeColWidth
|
||||
)
|
||||
self.novelColWidth = self._parseLine(
|
||||
cnfParse, cnfSec, "novelcols", self.CNF_I_LST, self.novelColWidth
|
||||
)
|
||||
self.projColWidth = self._parseLine(
|
||||
cnfParse, cnfSec, "projcols", self.CNF_I_LST, self.projColWidth
|
||||
)
|
||||
self.mainPanePos = self._parseLine(
|
||||
cnfParse, cnfSec, "mainpane", self.CNF_I_LST, self.mainPanePos
|
||||
)
|
||||
self.docPanePos = self._parseLine(
|
||||
cnfParse, cnfSec, "docpane", self.CNF_I_LST, self.docPanePos
|
||||
)
|
||||
self.viewPanePos = self._parseLine(
|
||||
cnfParse, cnfSec, "viewpane", self.CNF_I_LST, self.viewPanePos
|
||||
)
|
||||
self.outlnPanePos = self._parseLine(
|
||||
cnfParse, cnfSec, "outlinepane", self.CNF_I_LST, self.outlnPanePos
|
||||
)
|
||||
self.isFullScreen = self._parseLine(
|
||||
cnfParse, cnfSec, "fullscreen", self.CNF_BOOL, self.isFullScreen
|
||||
)
|
||||
self.hideVScroll = self._parseLine(
|
||||
cnfParse, cnfSec, "hidevscroll", self.CNF_BOOL, self.hideVScroll
|
||||
)
|
||||
self.hideHScroll = self._parseLine(
|
||||
cnfParse, cnfSec, "hidehscroll", self.CNF_BOOL, self.hideHScroll
|
||||
)
|
||||
self.winGeometry = theConf.rdIntList(cnfSec, "geometry", self.winGeometry)
|
||||
self.prefGeometry = theConf.rdIntList(cnfSec, "preferences", self.prefGeometry)
|
||||
self.treeColWidth = theConf.rdIntList(cnfSec, "treecols", self.treeColWidth)
|
||||
self.novelColWidth = theConf.rdIntList(cnfSec, "novelcols", self.novelColWidth)
|
||||
self.projColWidth = theConf.rdIntList(cnfSec, "projcols", self.projColWidth)
|
||||
self.mainPanePos = theConf.rdIntList(cnfSec, "mainpane", self.mainPanePos)
|
||||
self.docPanePos = theConf.rdIntList(cnfSec, "docpane", self.docPanePos)
|
||||
self.viewPanePos = theConf.rdIntList(cnfSec, "viewpane", self.viewPanePos)
|
||||
self.outlnPanePos = theConf.rdIntList(cnfSec, "outlinepane", self.outlnPanePos)
|
||||
self.isFullScreen = theConf.rdBool(cnfSec, "fullscreen", self.isFullScreen)
|
||||
self.hideVScroll = theConf.rdBool(cnfSec, "hidevscroll", self.hideVScroll)
|
||||
self.hideHScroll = theConf.rdBool(cnfSec, "hidehscroll", self.hideHScroll)
|
||||
|
||||
# Project
|
||||
cnfSec = "Project"
|
||||
self.autoSaveProj = self._parseLine(
|
||||
cnfParse, cnfSec, "autosaveproject", self.CNF_INT, self.autoSaveProj
|
||||
)
|
||||
self.autoSaveDoc = self._parseLine(
|
||||
cnfParse, cnfSec, "autosavedoc", self.CNF_INT, self.autoSaveDoc
|
||||
)
|
||||
self.autoSaveProj = theConf.rdInt(cnfSec, "autosaveproject", self.autoSaveProj)
|
||||
self.autoSaveDoc = theConf.rdInt(cnfSec, "autosavedoc", self.autoSaveDoc)
|
||||
|
||||
# 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, "margin", self.CNF_INT, self.textMargin
|
||||
)
|
||||
self.tabWidth = self._parseLine(
|
||||
cnfParse, cnfSec, "tabwidth", self.CNF_INT, self.tabWidth
|
||||
)
|
||||
self.focusWidth = self._parseLine(
|
||||
cnfParse, cnfSec, "focuswidth", self.CNF_INT, self.focusWidth
|
||||
)
|
||||
self.hideFocusFooter = self._parseLine(
|
||||
cnfParse, cnfSec, "hidefocusfooter", self.CNF_BOOL, self.hideFocusFooter
|
||||
)
|
||||
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.scrollPastEnd = self._parseLine(
|
||||
cnfParse, cnfSec, "scrollpastend", self.CNF_BOOL, self.scrollPastEnd
|
||||
)
|
||||
self.autoScroll = self._parseLine(
|
||||
cnfParse, cnfSec, "autoscroll", self.CNF_BOOL, self.autoScroll
|
||||
)
|
||||
self.autoScrollPos = self._parseLine(
|
||||
cnfParse, cnfSec, "autoscrollpos", self.CNF_INT, self.autoScrollPos
|
||||
)
|
||||
self.fmtSingleQuotes = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtsinglequote", self.CNF_S_LST, self.fmtSingleQuotes
|
||||
)
|
||||
self.fmtDoubleQuotes = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtdoublequote", self.CNF_S_LST, self.fmtDoubleQuotes
|
||||
)
|
||||
self.fmtPadBefore = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtpadbefore", self.CNF_STR, self.fmtPadBefore
|
||||
)
|
||||
self.fmtPadAfter = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtpadafter", self.CNF_STR, self.fmtPadAfter
|
||||
)
|
||||
self.fmtPadThin = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtpadthin", self.CNF_BOOL, self.fmtPadThin
|
||||
)
|
||||
self.spellTool = self._parseLine(
|
||||
cnfParse, cnfSec, "spelltool", self.CNF_STR, self.spellTool
|
||||
)
|
||||
self.spellLanguage = self._parseLine(
|
||||
cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage
|
||||
)
|
||||
self.showTabsNSpaces = self._parseLine(
|
||||
cnfParse, cnfSec, "showtabsnspaces", self.CNF_BOOL, self.showTabsNSpaces
|
||||
)
|
||||
self.showLineEndings = self._parseLine(
|
||||
cnfParse, cnfSec, "showlineendings", self.CNF_BOOL, self.showLineEndings
|
||||
)
|
||||
self.showMultiSpaces = self._parseLine(
|
||||
cnfParse, cnfSec, "showmultispaces", self.CNF_BOOL, self.showMultiSpaces
|
||||
)
|
||||
self.bigDocLimit = self._parseLine(
|
||||
cnfParse, cnfSec, "bigdoclimit", self.CNF_INT, self.bigDocLimit
|
||||
)
|
||||
self.showFullPath = self._parseLine(
|
||||
cnfParse, cnfSec, "showfullpath", self.CNF_BOOL, self.showFullPath
|
||||
)
|
||||
self.highlightQuotes = self._parseLine(
|
||||
cnfParse, cnfSec, "highlightquotes", self.CNF_BOOL, self.highlightQuotes
|
||||
)
|
||||
self.allowOpenSQuote = self._parseLine(
|
||||
cnfParse, cnfSec, "allowopensquote", self.CNF_BOOL, self.allowOpenSQuote
|
||||
)
|
||||
self.allowOpenDQuote = self._parseLine(
|
||||
cnfParse, cnfSec, "allowopendquote", self.CNF_BOOL, self.allowOpenDQuote
|
||||
)
|
||||
self.highlightEmph = self._parseLine(
|
||||
cnfParse, cnfSec, "highlightemph", self.CNF_BOOL, self.highlightEmph
|
||||
)
|
||||
self.stopWhenIdle = self._parseLine(
|
||||
cnfParse, cnfSec, "stopwhenidle", self.CNF_BOOL, self.stopWhenIdle
|
||||
)
|
||||
self.userIdleTime = self._parseLine(
|
||||
cnfParse, cnfSec, "useridletime", self.CNF_INT, self.userIdleTime
|
||||
)
|
||||
self.textFont = theConf.rdStr(cnfSec, "textfont", self.textFont)
|
||||
self.textSize = theConf.rdInt(cnfSec, "textsize", self.textSize)
|
||||
self.textFixedW = theConf.rdBool(cnfSec, "fixedwidth", self.textFixedW)
|
||||
self.textWidth = theConf.rdInt(cnfSec, "width", self.textWidth)
|
||||
self.textMargin = theConf.rdInt(cnfSec, "margin", self.textMargin)
|
||||
self.tabWidth = theConf.rdInt(cnfSec, "tabwidth", self.tabWidth)
|
||||
self.focusWidth = theConf.rdInt(cnfSec, "focuswidth", self.focusWidth)
|
||||
self.hideFocusFooter = theConf.rdBool(cnfSec, "hidefocusfooter", self.hideFocusFooter)
|
||||
self.doJustify = theConf.rdBool(cnfSec, "justify", self.doJustify)
|
||||
self.autoSelect = theConf.rdBool(cnfSec, "autoselect", self.autoSelect)
|
||||
self.doReplace = theConf.rdBool(cnfSec, "autoreplace", self.doReplace)
|
||||
self.doReplaceSQuote = theConf.rdBool(cnfSec, "repsquotes", self.doReplaceSQuote)
|
||||
self.doReplaceDQuote = theConf.rdBool(cnfSec, "repdquotes", self.doReplaceDQuote)
|
||||
self.doReplaceDash = theConf.rdBool(cnfSec, "repdash", self.doReplaceDash)
|
||||
self.doReplaceDots = theConf.rdBool(cnfSec, "repdots", self.doReplaceDots)
|
||||
self.scrollPastEnd = theConf.rdBool(cnfSec, "scrollpastend", self.scrollPastEnd)
|
||||
self.autoScroll = theConf.rdBool(cnfSec, "autoscroll", self.autoScroll)
|
||||
self.autoScrollPos = theConf.rdInt(cnfSec, "autoscrollpos", self.autoScrollPos)
|
||||
self.fmtSingleQuotes = theConf.rdStrList(cnfSec, "fmtsinglequote", self.fmtSingleQuotes)
|
||||
self.fmtDoubleQuotes = theConf.rdStrList(cnfSec, "fmtdoublequote", self.fmtDoubleQuotes)
|
||||
self.fmtPadBefore = theConf.rdStr(cnfSec, "fmtpadbefore", self.fmtPadBefore)
|
||||
self.fmtPadAfter = theConf.rdStr(cnfSec, "fmtpadafter", self.fmtPadAfter)
|
||||
self.fmtPadThin = theConf.rdBool(cnfSec, "fmtpadthin", self.fmtPadThin)
|
||||
self.spellTool = theConf.rdStr(cnfSec, "spelltool", self.spellTool)
|
||||
self.spellLanguage = theConf.rdStr(cnfSec, "spellcheck", self.spellLanguage)
|
||||
self.showTabsNSpaces = theConf.rdBool(cnfSec, "showtabsnspaces", self.showTabsNSpaces)
|
||||
self.showLineEndings = theConf.rdBool(cnfSec, "showlineendings", self.showLineEndings)
|
||||
self.showMultiSpaces = theConf.rdBool(cnfSec, "showmultispaces", self.showMultiSpaces)
|
||||
self.bigDocLimit = theConf.rdInt(cnfSec, "bigdoclimit", self.bigDocLimit)
|
||||
self.showFullPath = theConf.rdBool(cnfSec, "showfullpath", self.showFullPath)
|
||||
self.highlightQuotes = theConf.rdBool(cnfSec, "highlightquotes", self.highlightQuotes)
|
||||
self.allowOpenSQuote = theConf.rdBool(cnfSec, "allowopensquote", self.allowOpenSQuote)
|
||||
self.allowOpenDQuote = theConf.rdBool(cnfSec, "allowopendquote", self.allowOpenDQuote)
|
||||
self.highlightEmph = theConf.rdBool(cnfSec, "highlightemph", self.highlightEmph)
|
||||
self.stopWhenIdle = theConf.rdBool(cnfSec, "stopwhenidle", self.stopWhenIdle)
|
||||
self.userIdleTime = theConf.rdInt(cnfSec, "useridletime", self.userIdleTime)
|
||||
|
||||
# Backup
|
||||
cnfSec = "Backup"
|
||||
self.backupPath = self._parseLine(
|
||||
cnfParse, cnfSec, "backuppath", self.CNF_STR, self.backupPath
|
||||
)
|
||||
self.backupOnClose = self._parseLine(
|
||||
cnfParse, cnfSec, "backuponclose", self.CNF_BOOL, self.backupOnClose
|
||||
)
|
||||
self.askBeforeBackup = self._parseLine(
|
||||
cnfParse, cnfSec, "askbeforebackup", self.CNF_BOOL, self.askBeforeBackup
|
||||
)
|
||||
self.backupPath = theConf.rdStr(cnfSec, "backuppath", self.backupPath)
|
||||
self.backupOnClose = theConf.rdBool(cnfSec, "backuponclose", self.backupOnClose)
|
||||
self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup", self.askBeforeBackup)
|
||||
|
||||
# State
|
||||
cnfSec = "State"
|
||||
self.showRefPanel = self._parseLine(
|
||||
cnfParse, cnfSec, "showrefpanel", self.CNF_BOOL, self.showRefPanel
|
||||
)
|
||||
self.viewComments = self._parseLine(
|
||||
cnfParse, cnfSec, "viewcomments", self.CNF_BOOL, self.viewComments
|
||||
)
|
||||
self.viewSynopsis = self._parseLine(
|
||||
cnfParse, cnfSec, "viewsynopsis", self.CNF_BOOL, self.viewSynopsis
|
||||
)
|
||||
self.searchCase = self._parseLine(
|
||||
cnfParse, cnfSec, "searchcase", self.CNF_BOOL, self.searchCase
|
||||
)
|
||||
self.searchWord = self._parseLine(
|
||||
cnfParse, cnfSec, "searchword", self.CNF_BOOL, self.searchWord
|
||||
)
|
||||
self.searchRegEx = self._parseLine(
|
||||
cnfParse, cnfSec, "searchregex", self.CNF_BOOL, self.searchRegEx
|
||||
)
|
||||
self.searchLoop = self._parseLine(
|
||||
cnfParse, cnfSec, "searchloop", self.CNF_BOOL, self.searchLoop
|
||||
)
|
||||
self.searchNextFile = self._parseLine(
|
||||
cnfParse, cnfSec, "searchnextfile", self.CNF_BOOL, self.searchNextFile
|
||||
)
|
||||
self.searchMatchCap = self._parseLine(
|
||||
cnfParse, cnfSec, "searchmatchcap", self.CNF_BOOL, self.searchMatchCap
|
||||
)
|
||||
self.showRefPanel = theConf.rdBool(cnfSec, "showrefpanel", self.showRefPanel)
|
||||
self.viewComments = theConf.rdBool(cnfSec, "viewcomments", self.viewComments)
|
||||
self.viewSynopsis = theConf.rdBool(cnfSec, "viewsynopsis", self.viewSynopsis)
|
||||
self.searchCase = theConf.rdBool(cnfSec, "searchcase", self.searchCase)
|
||||
self.searchWord = theConf.rdBool(cnfSec, "searchword", self.searchWord)
|
||||
self.searchRegEx = theConf.rdBool(cnfSec, "searchregex", self.searchRegEx)
|
||||
self.searchLoop = theConf.rdBool(cnfSec, "searchloop", self.searchLoop)
|
||||
self.searchNextFile = theConf.rdBool(cnfSec, "searchnextfile", self.searchNextFile)
|
||||
self.searchMatchCap = theConf.rdBool(cnfSec, "searchmatchcap", self.searchMatchCap)
|
||||
|
||||
# Path
|
||||
cnfSec = "Path"
|
||||
self.lastPath = self._parseLine(
|
||||
cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath
|
||||
)
|
||||
self.lastPath = theConf.rdStr(cnfSec, "lastpath", self.lastPath)
|
||||
|
||||
# Check Certain Values for None
|
||||
self.spellLanguage = self._checkNone(self.spellLanguage)
|
||||
@@ -700,115 +551,106 @@ class Config:
|
||||
if self.confPath is None:
|
||||
return False
|
||||
|
||||
cnfParse = configparser.ConfigParser()
|
||||
theConf = NWConfigParser()
|
||||
|
||||
# Set options
|
||||
theConf["Main"] = {
|
||||
"timestamp": formatTimeStamp(time()),
|
||||
"theme": str(self.guiTheme),
|
||||
"syntax": str(self.guiSyntax),
|
||||
"icons": str(self.guiIcons),
|
||||
"guidark": str(self.guiDark),
|
||||
"guifont": str(self.guiFont),
|
||||
"guifontsize": str(self.guiFontSize),
|
||||
"lastnotes": str(self.lastNotes),
|
||||
"guilang": str(self.guiLang),
|
||||
}
|
||||
|
||||
# Main
|
||||
cnfSec = "Main"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "timestamp", formatTimeStamp(time()))
|
||||
cnfParse.set(cnfSec, "theme", str(self.guiTheme))
|
||||
cnfParse.set(cnfSec, "syntax", str(self.guiSyntax))
|
||||
cnfParse.set(cnfSec, "icons", str(self.guiIcons))
|
||||
cnfParse.set(cnfSec, "guidark", str(self.guiDark))
|
||||
cnfParse.set(cnfSec, "guifont", str(self.guiFont))
|
||||
cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize))
|
||||
cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes))
|
||||
cnfParse.set(cnfSec, "guilang", str(self.guiLang))
|
||||
theConf["Sizes"] = {
|
||||
"geometry": self._packList(self.winGeometry),
|
||||
"preferences": self._packList(self.prefGeometry),
|
||||
"treecols": self._packList(self.treeColWidth),
|
||||
"novelcols": self._packList(self.novelColWidth),
|
||||
"projcols": self._packList(self.projColWidth),
|
||||
"mainpane": self._packList(self.mainPanePos),
|
||||
"docpane": self._packList(self.docPanePos),
|
||||
"viewpane": self._packList(self.viewPanePos),
|
||||
"outlinepane": self._packList(self.outlnPanePos),
|
||||
"fullscreen": str(self.isFullScreen),
|
||||
"hidevscroll": str(self.hideVScroll),
|
||||
"hidehscroll": str(self.hideHScroll),
|
||||
}
|
||||
|
||||
# Sizes
|
||||
cnfSec = "Sizes"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "geometry", self._packList(self.winGeometry))
|
||||
cnfParse.set(cnfSec, "preferences", self._packList(self.prefGeometry))
|
||||
cnfParse.set(cnfSec, "treecols", self._packList(self.treeColWidth))
|
||||
cnfParse.set(cnfSec, "novelcols", self._packList(self.novelColWidth))
|
||||
cnfParse.set(cnfSec, "projcols", self._packList(self.projColWidth))
|
||||
cnfParse.set(cnfSec, "mainpane", self._packList(self.mainPanePos))
|
||||
cnfParse.set(cnfSec, "docpane", self._packList(self.docPanePos))
|
||||
cnfParse.set(cnfSec, "viewpane", self._packList(self.viewPanePos))
|
||||
cnfParse.set(cnfSec, "outlinepane", self._packList(self.outlnPanePos))
|
||||
cnfParse.set(cnfSec, "fullscreen", str(self.isFullScreen))
|
||||
cnfParse.set(cnfSec, "hidevscroll", str(self.hideVScroll))
|
||||
cnfParse.set(cnfSec, "hidehscroll", str(self.hideHScroll))
|
||||
theConf["Project"] = {
|
||||
"autosaveproject": str(self.autoSaveProj),
|
||||
"autosavedoc": str(self.autoSaveDoc),
|
||||
}
|
||||
|
||||
# Project
|
||||
cnfSec = "Project"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "autosaveproject", str(self.autoSaveProj))
|
||||
cnfParse.set(cnfSec, "autosavedoc", str(self.autoSaveDoc))
|
||||
theConf["Editor"] = {
|
||||
"textfont": str(self.textFont),
|
||||
"textsize": str(self.textSize),
|
||||
"fixedwidth": str(self.textFixedW),
|
||||
"width": str(self.textWidth),
|
||||
"margin": str(self.textMargin),
|
||||
"tabwidth": str(self.tabWidth),
|
||||
"focuswidth": str(self.focusWidth),
|
||||
"hidefocusfooter": str(self.hideFocusFooter),
|
||||
"justify": str(self.doJustify),
|
||||
"autoselect": str(self.autoSelect),
|
||||
"autoreplace": str(self.doReplace),
|
||||
"repsquotes": str(self.doReplaceSQuote),
|
||||
"repdquotes": str(self.doReplaceDQuote),
|
||||
"repdash": str(self.doReplaceDash),
|
||||
"repdots": str(self.doReplaceDots),
|
||||
"scrollpastend": str(self.scrollPastEnd),
|
||||
"autoscroll": str(self.autoScroll),
|
||||
"autoscrollpos": str(self.autoScrollPos),
|
||||
"fmtsinglequote": self._packList(self.fmtSingleQuotes),
|
||||
"fmtdoublequote": self._packList(self.fmtDoubleQuotes),
|
||||
"fmtpadbefore": str(self.fmtPadBefore),
|
||||
"fmtpadafter": str(self.fmtPadAfter),
|
||||
"fmtpadthin": str(self.fmtPadThin),
|
||||
"spelltool": str(self.spellTool),
|
||||
"spellcheck": str(self.spellLanguage),
|
||||
"showtabsnspaces": str(self.showTabsNSpaces),
|
||||
"showlineendings": str(self.showLineEndings),
|
||||
"showmultispaces": str(self.showMultiSpaces),
|
||||
"bigdoclimit": str(self.bigDocLimit),
|
||||
"showfullpath": str(self.showFullPath),
|
||||
"highlightquotes": str(self.highlightQuotes),
|
||||
"allowopensquote": str(self.allowOpenSQuote),
|
||||
"allowopendquote": str(self.allowOpenDQuote),
|
||||
"highlightemph": str(self.highlightEmph),
|
||||
"stopwhenidle": str(self.stopWhenIdle),
|
||||
"useridletime": str(self.userIdleTime),
|
||||
}
|
||||
|
||||
# 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, "margin", str(self.textMargin))
|
||||
cnfParse.set(cnfSec, "tabwidth", str(self.tabWidth))
|
||||
cnfParse.set(cnfSec, "focuswidth", str(self.focusWidth))
|
||||
cnfParse.set(cnfSec, "hidefocusfooter", str(self.hideFocusFooter))
|
||||
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, "scrollpastend", str(self.scrollPastEnd))
|
||||
cnfParse.set(cnfSec, "autoscroll", str(self.autoScroll))
|
||||
cnfParse.set(cnfSec, "autoscrollpos", str(self.autoScrollPos))
|
||||
cnfParse.set(cnfSec, "fmtsinglequote", self._packList(self.fmtSingleQuotes))
|
||||
cnfParse.set(cnfSec, "fmtdoublequote", self._packList(self.fmtDoubleQuotes))
|
||||
cnfParse.set(cnfSec, "fmtpadbefore", str(self.fmtPadBefore))
|
||||
cnfParse.set(cnfSec, "fmtpadafter", str(self.fmtPadAfter))
|
||||
cnfParse.set(cnfSec, "fmtpadthin", str(self.fmtPadThin))
|
||||
cnfParse.set(cnfSec, "spelltool", str(self.spellTool))
|
||||
cnfParse.set(cnfSec, "spellcheck", str(self.spellLanguage))
|
||||
cnfParse.set(cnfSec, "showtabsnspaces", str(self.showTabsNSpaces))
|
||||
cnfParse.set(cnfSec, "showlineendings", str(self.showLineEndings))
|
||||
cnfParse.set(cnfSec, "showmultispaces", str(self.showMultiSpaces))
|
||||
cnfParse.set(cnfSec, "bigdoclimit", str(self.bigDocLimit))
|
||||
cnfParse.set(cnfSec, "showfullpath", str(self.showFullPath))
|
||||
cnfParse.set(cnfSec, "highlightquotes", str(self.highlightQuotes))
|
||||
cnfParse.set(cnfSec, "allowopensquote", str(self.allowOpenSQuote))
|
||||
cnfParse.set(cnfSec, "allowopendquote", str(self.allowOpenDQuote))
|
||||
cnfParse.set(cnfSec, "highlightemph", str(self.highlightEmph))
|
||||
cnfParse.set(cnfSec, "stopwhenidle", str(self.stopWhenIdle))
|
||||
cnfParse.set(cnfSec, "useridletime", str(self.userIdleTime))
|
||||
theConf["Backup"] = {
|
||||
"backuppath": str(self.backupPath),
|
||||
"backuponclose": str(self.backupOnClose),
|
||||
"askbeforebackup": str(self.askBeforeBackup),
|
||||
}
|
||||
|
||||
# Backup
|
||||
cnfSec = "Backup"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "backuppath", str(self.backupPath))
|
||||
cnfParse.set(cnfSec, "backuponclose", str(self.backupOnClose))
|
||||
cnfParse.set(cnfSec, "askbeforebackup", str(self.askBeforeBackup))
|
||||
theConf["State"] = {
|
||||
"showrefpanel": str(self.showRefPanel),
|
||||
"viewcomments": str(self.viewComments),
|
||||
"viewsynopsis": str(self.viewSynopsis),
|
||||
"searchcase": str(self.searchCase),
|
||||
"searchword": str(self.searchWord),
|
||||
"searchregex": str(self.searchRegEx),
|
||||
"searchloop": str(self.searchLoop),
|
||||
"searchnextfile": str(self.searchNextFile),
|
||||
"searchmatchcap": str(self.searchMatchCap),
|
||||
}
|
||||
|
||||
# State
|
||||
cnfSec = "State"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "showrefpanel", str(self.showRefPanel))
|
||||
cnfParse.set(cnfSec, "viewcomments", str(self.viewComments))
|
||||
cnfParse.set(cnfSec, "viewsynopsis", str(self.viewSynopsis))
|
||||
cnfParse.set(cnfSec, "searchcase", str(self.searchCase))
|
||||
cnfParse.set(cnfSec, "searchword", str(self.searchWord))
|
||||
cnfParse.set(cnfSec, "searchregex", str(self.searchRegEx))
|
||||
cnfParse.set(cnfSec, "searchloop", str(self.searchLoop))
|
||||
cnfParse.set(cnfSec, "searchnextfile", str(self.searchNextFile))
|
||||
cnfParse.set(cnfSec, "searchmatchcap", str(self.searchMatchCap))
|
||||
|
||||
# Path
|
||||
cnfSec = "Path"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "lastpath", str(self.lastPath))
|
||||
theConf["Path"] = {
|
||||
"lastpath": str(self.lastPath),
|
||||
}
|
||||
|
||||
# Write config file
|
||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||
try:
|
||||
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
|
||||
cnfParse.write(outFile)
|
||||
theConf.write(outFile)
|
||||
self.confChanged = False
|
||||
except Exception as e:
|
||||
logger.error("Could not save config file")
|
||||
@@ -884,7 +726,7 @@ class Config:
|
||||
return True
|
||||
|
||||
def updateRecentCache(self, projPath, projTitle, wordCount, saveTime):
|
||||
"""Add or update recent cache information o9n a given project.
|
||||
"""Add or update recent cache information on a given project.
|
||||
"""
|
||||
self.recentProj[os.path.abspath(projPath)] = {
|
||||
"title": projTitle,
|
||||
@@ -992,12 +834,6 @@ class Config:
|
||||
self.confChanged = True
|
||||
return self.showRefPanel
|
||||
|
||||
def getErrData(self):
|
||||
errMessage = "<br>".join(self.errData)
|
||||
self.hasError = False
|
||||
self.errData = []
|
||||
return errMessage
|
||||
|
||||
def setViewComments(self, viewState):
|
||||
self.viewComments = viewState
|
||||
self.confChanged = True
|
||||
@@ -1051,6 +887,12 @@ class Config:
|
||||
def getFocusWidth(self):
|
||||
return self.pxInt(self.focusWidth)
|
||||
|
||||
def getErrData(self):
|
||||
errMessage = "<br>".join(self.errData)
|
||||
self.hasError = False
|
||||
self.errData = []
|
||||
return errMessage
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -1060,52 +902,8 @@ class Config:
|
||||
"""
|
||||
return ", ".join([str(inVal) for inVal in inData])
|
||||
|
||||
def _unpackList(self, inStr, listDefault, cnfType):
|
||||
"""Unpack a comma-separated string of items into a list.
|
||||
"""
|
||||
inData = inStr.split(",")
|
||||
outData = listDefault.copy()
|
||||
for i in range(min(len(inData), len(listDefault))):
|
||||
try:
|
||||
if cnfType == self.CNF_S_LST:
|
||||
outData[i] = inData[i].strip()
|
||||
elif cnfType == self.CNF_I_LST:
|
||||
outData[i] = int(inData[i].strip())
|
||||
else:
|
||||
continue
|
||||
except Exception:
|
||||
continue
|
||||
return outData
|
||||
|
||||
def _parseLine(self, cnfParse, cnfSec, cnfName, cnfType, cnfDefault):
|
||||
"""Parse a line and return the correct datatype.
|
||||
"""
|
||||
if cnfParse.has_section(cnfSec):
|
||||
if cnfParse.has_option(cnfSec, cnfName):
|
||||
try:
|
||||
if cnfType == self.CNF_STR:
|
||||
return cnfParse.get(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_INT:
|
||||
return cnfParse.getint(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_BOOL:
|
||||
return cnfParse.getboolean(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_I_LST:
|
||||
return self._unpackList(
|
||||
cnfParse.get(cnfSec, cnfName), cnfDefault, self.CNF_I_LST
|
||||
)
|
||||
elif cnfType == self.CNF_S_LST:
|
||||
return self._unpackList(
|
||||
cnfParse.get(cnfSec, cnfName), cnfDefault, self.CNF_S_LST
|
||||
)
|
||||
except ValueError:
|
||||
logger.error("Failed to load value from config file.")
|
||||
logException()
|
||||
return cnfDefault
|
||||
|
||||
return cnfDefault
|
||||
|
||||
def _checkNone(self, checkVal):
|
||||
"""Return a NoneType if the value correspomds to None, otherwise
|
||||
"""Return a NoneType if the value corresponds to None, otherwise
|
||||
return the value unchanged.
|
||||
"""
|
||||
if checkVal is None:
|
||||
|
||||
+2
-2
@@ -257,10 +257,10 @@ class NWIndex():
|
||||
|
||||
# If the file is archived or trashed, we don't index the file itself
|
||||
if self.theProject.projTree.isTrashRoot(theItem.itemParent):
|
||||
logger.info("Not indexing trash item '%s'", tHandle)
|
||||
logger.debug("Not indexing trash item '%s'", tHandle)
|
||||
return False
|
||||
if theRoot.itemClass == nwItemClass.ARCHIVE:
|
||||
logger.info("Not indexing archived item '%s'", tHandle)
|
||||
logger.debug("Not indexing archived item '%s'", tHandle)
|
||||
return False
|
||||
|
||||
itemClass = theItem.itemClass
|
||||
|
||||
+2
-2
@@ -251,7 +251,7 @@ class NWItem():
|
||||
if isinstance(expState, str):
|
||||
self.isExpanded = (expState == str(True))
|
||||
else:
|
||||
self.isExpanded = (expState == True) # noqa: E712
|
||||
self.isExpanded = (expState is True)
|
||||
return
|
||||
|
||||
def setExported(self, expState):
|
||||
@@ -260,7 +260,7 @@ class NWItem():
|
||||
if isinstance(expState, str):
|
||||
self.isExported = (expState == str(True))
|
||||
else:
|
||||
self.isExported = (expState == True) # noqa: E712
|
||||
self.isExported = (expState is True)
|
||||
return
|
||||
|
||||
##
|
||||
|
||||
+3
-2
@@ -369,7 +369,7 @@ class NWProject():
|
||||
|
||||
self.clearProject()
|
||||
self.projPath = os.path.abspath(os.path.dirname(fileName))
|
||||
logger.debug("Opening project: %s", self.projPath)
|
||||
logger.info("Opening project: %s", self.projPath)
|
||||
|
||||
# Standard Folders and Files
|
||||
# ==========================
|
||||
@@ -611,7 +611,7 @@ class NWProject():
|
||||
if not self.ensureFolderStructure():
|
||||
return False
|
||||
|
||||
logger.debug("Saving project: %s", self.projPath)
|
||||
logger.info("Saving project: %s", self.projPath)
|
||||
|
||||
if autoSave:
|
||||
self.autoCount += 1
|
||||
@@ -712,6 +712,7 @@ class NWProject():
|
||||
def closeProject(self, idleTime=0):
|
||||
"""Close the current project and clear all meta data.
|
||||
"""
|
||||
logger.info("Closing project: %s", self.projPath)
|
||||
self.optState.saveSettings()
|
||||
self.projTree.writeToCFile()
|
||||
self._appendSessionStats(idleTime)
|
||||
|
||||
+28
-46
@@ -27,7 +27,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import nw
|
||||
import os
|
||||
import logging
|
||||
import configparser
|
||||
|
||||
from math import ceil
|
||||
from functools import partial
|
||||
@@ -39,6 +38,7 @@ from PyQt5.QtGui import (
|
||||
)
|
||||
|
||||
from nw.enum import nwAlert
|
||||
from nw.common import NWConfigParser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -280,7 +280,7 @@ class GuiTheme:
|
||||
return False
|
||||
|
||||
# Config File
|
||||
confParser = configparser.ConfigParser()
|
||||
confParser = NWConfigParser()
|
||||
try:
|
||||
with open(self.confFile, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
@@ -292,13 +292,13 @@ class GuiTheme:
|
||||
# Main
|
||||
cnfSec = "Main"
|
||||
if confParser.has_section(cnfSec):
|
||||
self.themeName = self._parseLine(confParser, cnfSec, "name", "")
|
||||
self.themeDescription = self._parseLine(confParser, cnfSec, "description", "N/A")
|
||||
self.themeAuthor = self._parseLine(confParser, cnfSec, "author", "N/A")
|
||||
self.themeCredit = self._parseLine(confParser, cnfSec, "credit", "N/A")
|
||||
self.themeUrl = self._parseLine(confParser, cnfSec, "url", "")
|
||||
self.themeLicense = self._parseLine(confParser, cnfSec, "license", "N/A")
|
||||
self.themeLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
|
||||
self.themeName = confParser.rdStr(cnfSec, "name", "")
|
||||
self.themeDescription = confParser.rdStr(cnfSec, "description", "N/A")
|
||||
self.themeAuthor = confParser.rdStr(cnfSec, "author", "N/A")
|
||||
self.themeCredit = confParser.rdStr(cnfSec, "credit", "N/A")
|
||||
self.themeUrl = confParser.rdStr(cnfSec, "url", "")
|
||||
self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A")
|
||||
self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
|
||||
|
||||
# Palette
|
||||
cnfSec = "Palette"
|
||||
@@ -338,7 +338,7 @@ class GuiTheme:
|
||||
"""
|
||||
logger.debug("Loading syntax theme files")
|
||||
|
||||
confParser = configparser.ConfigParser()
|
||||
confParser = NWConfigParser()
|
||||
try:
|
||||
with open(self.syntaxFile, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
@@ -350,13 +350,13 @@ class GuiTheme:
|
||||
# Main
|
||||
cnfSec = "Main"
|
||||
if confParser.has_section(cnfSec):
|
||||
self.syntaxName = self._parseLine(confParser, cnfSec, "name", "")
|
||||
self.syntaxDescription = self._parseLine(confParser, cnfSec, "description", "")
|
||||
self.syntaxAuthor = self._parseLine(confParser, cnfSec, "author", "")
|
||||
self.syntaxCredit = self._parseLine(confParser, cnfSec, "credit", "")
|
||||
self.syntaxUrl = self._parseLine(confParser, cnfSec, "url", "")
|
||||
self.syntaxLicense = self._parseLine(confParser, cnfSec, "license", "")
|
||||
self.syntaxLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
|
||||
self.syntaxName = confParser.rdStr(cnfSec, "name", "")
|
||||
self.syntaxDescription = confParser.rdStr(cnfSec, "description", "")
|
||||
self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "")
|
||||
self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "")
|
||||
self.syntaxUrl = confParser.rdStr(cnfSec, "url", "")
|
||||
self.syntaxLicense = confParser.rdStr(cnfSec, "license", "")
|
||||
self.syntaxLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
|
||||
|
||||
# Syntax
|
||||
cnfSec = "Syntax"
|
||||
@@ -388,7 +388,7 @@ class GuiTheme:
|
||||
if self.themeList:
|
||||
return self.themeList
|
||||
|
||||
confParser = configparser.ConfigParser()
|
||||
confParser = NWConfigParser()
|
||||
for themeDir in os.listdir(os.path.join(self.mainConf.themeRoot, self.guiPath)):
|
||||
themeConf = os.path.join(
|
||||
self.mainConf.themeRoot, self.guiPath, themeDir, self.confName
|
||||
@@ -420,7 +420,7 @@ class GuiTheme:
|
||||
if self.syntaxList:
|
||||
return self.syntaxList
|
||||
|
||||
confParser = configparser.ConfigParser()
|
||||
confParser = NWConfigParser()
|
||||
syntaxDir = os.path.join(self.mainConf.themeRoot, self.syntaxPath)
|
||||
for syntaxFile in os.listdir(syntaxDir):
|
||||
syntaxPath = os.path.join(syntaxDir, syntaxFile)
|
||||
@@ -486,15 +486,6 @@ class GuiTheme:
|
||||
self.guiPalette.setColor(paletteVal, QColor(*readCol))
|
||||
return
|
||||
|
||||
def _parseLine(self, confParser, cnfSec, cnfName, cnfDefault):
|
||||
"""Simple wrapper for the config parser to check that the entry
|
||||
exists before attempting to load it.
|
||||
"""
|
||||
if confParser.has_section(cnfSec):
|
||||
if confParser.has_option(cnfSec, cnfName):
|
||||
return confParser.get(cnfSec, cnfName)
|
||||
return cnfDefault
|
||||
|
||||
# End Class GuiTheme
|
||||
|
||||
|
||||
@@ -644,7 +635,7 @@ class GuiIcons:
|
||||
return False
|
||||
|
||||
# Config File
|
||||
confParser = configparser.ConfigParser()
|
||||
confParser = NWConfigParser()
|
||||
try:
|
||||
with open(self.confFile, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
@@ -656,13 +647,13 @@ class GuiIcons:
|
||||
# Main
|
||||
cnfSec = "Main"
|
||||
if confParser.has_section(cnfSec):
|
||||
self.themeName = self._parseLine(confParser, cnfSec, "name", "")
|
||||
self.themeDescription = self._parseLine(confParser, cnfSec, "description", "")
|
||||
self.themeAuthor = self._parseLine(confParser, cnfSec, "author", "N/A")
|
||||
self.themeCredit = self._parseLine(confParser, cnfSec, "credit", "N/A")
|
||||
self.themeUrl = self._parseLine(confParser, cnfSec, "url", "")
|
||||
self.themeLicense = self._parseLine(confParser, cnfSec, "license", "N/A")
|
||||
self.themeLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
|
||||
self.themeName = confParser.rdStr(cnfSec, "name", "")
|
||||
self.themeDescription = confParser.rdStr(cnfSec, "description", "")
|
||||
self.themeAuthor = confParser.rdStr(cnfSec, "author", "N/A")
|
||||
self.themeCredit = confParser.rdStr(cnfSec, "credit", "N/A")
|
||||
self.themeUrl = confParser.rdStr(cnfSec, "url", "")
|
||||
self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A")
|
||||
self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
|
||||
|
||||
# Palette
|
||||
cnfSec = "Map"
|
||||
@@ -736,7 +727,7 @@ class GuiIcons:
|
||||
if self.themeList:
|
||||
return self.themeList
|
||||
|
||||
confParser = configparser.ConfigParser()
|
||||
confParser = NWConfigParser()
|
||||
for themeDir in os.listdir(self.mainConf.iconPath):
|
||||
themePath = os.path.join(self.mainConf.iconPath, themeDir)
|
||||
if not os.path.isdir(themePath) or themeDir == self.fbackName:
|
||||
@@ -818,13 +809,4 @@ class GuiIcons:
|
||||
|
||||
return QIcon()
|
||||
|
||||
def _parseLine(self, confParser, cnfSec, cnfName, cnfDefault):
|
||||
"""Simple wrapper for the config parser to check that the entry
|
||||
exists before attempting to load it.
|
||||
"""
|
||||
if confParser.has_section(cnfSec):
|
||||
if confParser.has_option(cnfSec, cnfName):
|
||||
return confParser.get(cnfSec, cnfName)
|
||||
return cnfDefault
|
||||
|
||||
# END Class GuiIcons
|
||||
|
||||
+5
-8
@@ -71,12 +71,9 @@ class GuiMain(QMainWindow):
|
||||
logger.info("OS: %s", self.mainConf.osType)
|
||||
logger.info("Kernel: %s", self.mainConf.kernelVer)
|
||||
logger.info("Host: %s", self.mainConf.hostName)
|
||||
logger.info("Qt5 Version: %s (%d)",
|
||||
self.mainConf.verQtString, self.mainConf.verQtValue)
|
||||
logger.info("PyQt5 Version: %s (%d)",
|
||||
self.mainConf.verPyQtString, self.mainConf.verPyQtValue)
|
||||
logger.info("Python Version: %s (0x%x)",
|
||||
self.mainConf.verPyString, self.mainConf.verPyHexVal)
|
||||
logger.info("Qt5: %s (%d)", self.mainConf.verQtString, self.mainConf.verQtValue)
|
||||
logger.info("PyQt5: %s (%d)", self.mainConf.verPyQtString, self.mainConf.verPyQtValue)
|
||||
logger.info("Python: %s (0x%x)", self.mainConf.verPyString, self.mainConf.verPyHexVal)
|
||||
logger.info("GUI Language: %s", self.mainConf.guiLang)
|
||||
|
||||
# Core Classes
|
||||
@@ -297,7 +294,7 @@ class GuiMain(QMainWindow):
|
||||
logger.debug("Opening project from additional command line option")
|
||||
self.openProject(self.mainConf.cmdOpen)
|
||||
|
||||
logger.debug("novelWriter is ready ...")
|
||||
logger.info("novelWriter is ready ...")
|
||||
self.setStatus(self.tr("novelWriter is ready ..."))
|
||||
|
||||
return
|
||||
@@ -872,7 +869,7 @@ class GuiMain(QMainWindow):
|
||||
logger.error("No project open")
|
||||
return False
|
||||
|
||||
logger.debug("Rebuilding index ...")
|
||||
logger.info("Rebuilding index ...")
|
||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
tStart = time()
|
||||
|
||||
|
||||
+1
-4
@@ -91,10 +91,7 @@ def fncDir(tmpDir):
|
||||
shutil.rmtree(fncDir)
|
||||
if not os.path.isdir(fncDir):
|
||||
os.mkdir(fncDir)
|
||||
yield fncDir
|
||||
if os.path.isdir(fncDir):
|
||||
shutil.rmtree(fncDir)
|
||||
return
|
||||
return fncDir
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
|
||||
@@ -19,14 +19,19 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import pytest
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from tools import writeFile
|
||||
|
||||
from nw.common import (
|
||||
checkString, checkBool, checkInt, formatInt, transferCase,
|
||||
fuzzyTime, checkHandle, formatTimeStamp, formatTime, hexToInt,
|
||||
checkString, checkBool, checkInt, formatInt, transferCase, fuzzyTime,
|
||||
checkHandle, formatTimeStamp, parseTimeStamp, formatTime, hexToInt,
|
||||
makeFileNameSafe, isHandle, isTitleTag, isItemClass, isItemType,
|
||||
isItemLayout, numberToRoman
|
||||
isItemLayout, numberToRoman, NWConfigParser
|
||||
)
|
||||
|
||||
|
||||
@@ -65,11 +70,11 @@ def testBaseCommon_CheckBool():
|
||||
"""
|
||||
assert checkBool(None, 3, True) is None
|
||||
assert checkBool("None", 3, True) is None
|
||||
assert checkBool("True", False, False)
|
||||
assert not checkBool("False", True, False)
|
||||
assert checkBool("True", False, False) is True
|
||||
assert checkBool("False", True, False) is False
|
||||
assert checkBool("Boo", None, False) is None
|
||||
assert not checkBool(0, None, False)
|
||||
assert checkBool(1, None, False)
|
||||
assert checkBool(0, None, False) is False
|
||||
assert checkBool(1, None, False) is True
|
||||
assert checkBool(2, None, False) is None
|
||||
assert checkBool(0.0, None, False) is None
|
||||
assert checkBool(1.0, None, False) is None
|
||||
@@ -96,13 +101,12 @@ def testBaseCommon_CheckHandle():
|
||||
def testBaseCommon_IsHandle():
|
||||
"""Test the isHandle function.
|
||||
"""
|
||||
assert isHandle("47666c91c7ccf")
|
||||
|
||||
assert not isHandle("47666C91C7CCF")
|
||||
assert not isHandle("h7666c91c7ccf")
|
||||
assert not isHandle("None")
|
||||
assert not isHandle(None)
|
||||
assert not isHandle("STUFF")
|
||||
assert isHandle("47666c91c7ccf") is True
|
||||
assert isHandle("47666C91C7CCF") is False
|
||||
assert isHandle("h7666c91c7ccf") is False
|
||||
assert isHandle("None") is False
|
||||
assert isHandle(None) is False
|
||||
assert isHandle("STUFF") is False
|
||||
|
||||
# END Test testBaseCommon_IsHandle
|
||||
|
||||
@@ -111,16 +115,16 @@ def testBaseCommon_IsHandle():
|
||||
def testBaseCommon_IsTitleTag():
|
||||
"""Test the isItemClass function.
|
||||
"""
|
||||
assert isTitleTag("T123456")
|
||||
assert isTitleTag("T123456") is True
|
||||
|
||||
assert not isTitleTag("t123456")
|
||||
assert not isTitleTag("S123456")
|
||||
assert not isTitleTag("T12345A")
|
||||
assert not isTitleTag("T1234567")
|
||||
assert isTitleTag("t123456") is False
|
||||
assert isTitleTag("S123456") is False
|
||||
assert isTitleTag("T12345A") is False
|
||||
assert isTitleTag("T1234567") is False
|
||||
|
||||
assert not isTitleTag("None")
|
||||
assert not isTitleTag(None)
|
||||
assert not isTitleTag("STUFF")
|
||||
assert isTitleTag("None") is False
|
||||
assert isTitleTag(None) is False
|
||||
assert isTitleTag("STUFF") is False
|
||||
|
||||
# END Test testBaseCommon_IsTitleTag
|
||||
|
||||
@@ -129,21 +133,21 @@ def testBaseCommon_IsTitleTag():
|
||||
def testBaseCommon_IsItemClass():
|
||||
"""Test the isItemClass function.
|
||||
"""
|
||||
assert isItemClass("NO_CLASS")
|
||||
assert isItemClass("NOVEL")
|
||||
assert isItemClass("PLOT")
|
||||
assert isItemClass("CHARACTER")
|
||||
assert isItemClass("WORLD")
|
||||
assert isItemClass("TIMELINE")
|
||||
assert isItemClass("OBJECT")
|
||||
assert isItemClass("ENTITY")
|
||||
assert isItemClass("CUSTOM")
|
||||
assert isItemClass("ARCHIVE")
|
||||
assert isItemClass("TRASH")
|
||||
assert isItemClass("NO_CLASS") is True
|
||||
assert isItemClass("NOVEL") is True
|
||||
assert isItemClass("PLOT") is True
|
||||
assert isItemClass("CHARACTER") is True
|
||||
assert isItemClass("WORLD") is True
|
||||
assert isItemClass("TIMELINE") is True
|
||||
assert isItemClass("OBJECT") is True
|
||||
assert isItemClass("ENTITY") is True
|
||||
assert isItemClass("CUSTOM") is True
|
||||
assert isItemClass("ARCHIVE") is True
|
||||
assert isItemClass("TRASH") is True
|
||||
|
||||
assert not isItemClass("None")
|
||||
assert not isItemClass(None)
|
||||
assert not isItemClass("STUFF")
|
||||
assert isItemClass("None") is False
|
||||
assert isItemClass(None) is False
|
||||
assert isItemClass("STUFF") is False
|
||||
|
||||
# END Test testBaseCommon_IsItemClass
|
||||
|
||||
@@ -152,15 +156,15 @@ def testBaseCommon_IsItemClass():
|
||||
def testBaseCommon_IsItemType():
|
||||
"""Test the isItemType function.
|
||||
"""
|
||||
assert isItemType("NO_TYPE")
|
||||
assert isItemType("ROOT")
|
||||
assert isItemType("FOLDER")
|
||||
assert isItemType("FILE")
|
||||
assert isItemType("TRASH")
|
||||
assert isItemType("NO_TYPE") is True
|
||||
assert isItemType("ROOT") is True
|
||||
assert isItemType("FOLDER") is True
|
||||
assert isItemType("FILE") is True
|
||||
assert isItemType("TRASH") is True
|
||||
|
||||
assert not isItemType("None")
|
||||
assert not isItemType(None)
|
||||
assert not isItemType("STUFF")
|
||||
assert isItemType("None") is False
|
||||
assert isItemType(None) is False
|
||||
assert isItemType("STUFF") is False
|
||||
|
||||
# END Test testBaseCommon_IsItemType
|
||||
|
||||
@@ -169,19 +173,19 @@ def testBaseCommon_IsItemType():
|
||||
def testBaseCommon_IsItemLayout():
|
||||
"""Test the isItemLayout function.
|
||||
"""
|
||||
assert isItemLayout("NO_LAYOUT")
|
||||
assert isItemLayout("TITLE")
|
||||
assert isItemLayout("BOOK")
|
||||
assert isItemLayout("PAGE")
|
||||
assert isItemLayout("PARTITION")
|
||||
assert isItemLayout("UNNUMBERED")
|
||||
assert isItemLayout("CHAPTER")
|
||||
assert isItemLayout("SCENE")
|
||||
assert isItemLayout("NOTE")
|
||||
assert isItemLayout("NO_LAYOUT") is True
|
||||
assert isItemLayout("TITLE") is True
|
||||
assert isItemLayout("BOOK") is True
|
||||
assert isItemLayout("PAGE") is True
|
||||
assert isItemLayout("PARTITION") is True
|
||||
assert isItemLayout("UNNUMBERED") is True
|
||||
assert isItemLayout("CHAPTER") is True
|
||||
assert isItemLayout("SCENE") is True
|
||||
assert isItemLayout("NOTE") is True
|
||||
|
||||
assert not isItemLayout("None")
|
||||
assert not isItemLayout(None)
|
||||
assert not isItemLayout("STUFF")
|
||||
assert isItemLayout("None") is False
|
||||
assert isItemLayout(None) is False
|
||||
assert isItemLayout("STUFF") is False
|
||||
|
||||
# END Test testBaseCommon_IsItemLayout
|
||||
|
||||
@@ -234,11 +238,29 @@ def testBaseCommon_FormatTime():
|
||||
# END Test testBaseCommon_FormatTime
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_ParseTimeStamp():
|
||||
"""Test the parseTimeStamp function.
|
||||
"""
|
||||
localEpoch = datetime(2000, 1, 1).timestamp()
|
||||
assert parseTimeStamp(None, 0.0, allowNone=True) is None
|
||||
assert parseTimeStamp("None", 0.0, allowNone=True) is None
|
||||
assert parseTimeStamp("None", 0.0) == 0.0
|
||||
assert parseTimeStamp("2000-01-01 00:00:00", 123.0) == localEpoch
|
||||
assert parseTimeStamp("2000-13-01 00:00:00", 123.0) == 123.0
|
||||
assert parseTimeStamp("2000-01-32 00:00:00", 123.0) == 123.0
|
||||
|
||||
# END Test testBaseCommon_ParseTimeStamp
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_FormatInt():
|
||||
"""Test the formatInt function.
|
||||
"""
|
||||
assert formatInt(1000) == "1000"
|
||||
# Normal Cases
|
||||
assert formatInt(1) == "1"
|
||||
assert formatInt(12) == "12"
|
||||
assert formatInt(123) == "123"
|
||||
assert formatInt(1234) == "1.23\u2009k"
|
||||
assert formatInt(12345) == "12.3\u2009k"
|
||||
assert formatInt(123456) == "123\u2009k"
|
||||
@@ -247,6 +269,11 @@ def testBaseCommon_FormatInt():
|
||||
assert formatInt(123456789) == "123\u2009M"
|
||||
assert formatInt(1234567890) == "1.23\u2009G"
|
||||
|
||||
# Exceptions
|
||||
assert formatInt(12.3) == "ERR"
|
||||
assert formatInt(None) == "ERR"
|
||||
assert formatInt("42") == "ERR"
|
||||
|
||||
# END Test testBaseCommon_FormatInt
|
||||
|
||||
|
||||
@@ -339,3 +366,87 @@ def testBaseCommon_RomanNumbers():
|
||||
assert numberToRoman(999, True) == "cmxcix"
|
||||
|
||||
# END Test testBaseCommon_RomanNumbers
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_NWConfigParser(fncDir):
|
||||
"""Test the NWConfigParser subclass.
|
||||
"""
|
||||
tstConf = os.path.join(fncDir, "test.cfg")
|
||||
writeFile(tstConf, (
|
||||
"[main]\n"
|
||||
"stropt = value\n"
|
||||
"intopt1 = 42\n"
|
||||
"intopt2 = 42.43\n"
|
||||
"boolopt1 = true\n"
|
||||
"boolopt2 = TRUE\n"
|
||||
"boolopt3 = 1\n"
|
||||
"boolopt4 = 0\n"
|
||||
"list1 = a, b, c\n"
|
||||
"list2 = 17, 18, 19\n"
|
||||
))
|
||||
|
||||
cfgParser = NWConfigParser()
|
||||
cfgParser.read(tstConf)
|
||||
|
||||
# Readers
|
||||
# =======
|
||||
|
||||
# Read String
|
||||
assert cfgParser.rdStr("main", "stropt", "stuff") == "value"
|
||||
assert cfgParser.rdStr("main", "boolopt1", "stuff") == "true"
|
||||
assert cfgParser.rdStr("main", "intopt1", "stuff") == "42"
|
||||
|
||||
assert cfgParser.rdStr("nope", "stropt", "stuff") == "stuff"
|
||||
assert cfgParser.rdStr("main", "blabla", "stuff") == "stuff"
|
||||
|
||||
# Read Boolean
|
||||
assert cfgParser.rdBool("main", "boolopt1", None) is True
|
||||
assert cfgParser.rdBool("main", "boolopt2", None) is True
|
||||
assert cfgParser.rdBool("main", "boolopt3", None) is True
|
||||
assert cfgParser.rdBool("main", "boolopt4", None) is False
|
||||
assert cfgParser.rdBool("main", "intopt1", None) is None
|
||||
|
||||
assert cfgParser.rdBool("nope", "boolopt1", None) is None
|
||||
assert cfgParser.rdBool("main", "blabla", None) is None
|
||||
|
||||
# Read Integer
|
||||
assert cfgParser.rdInt("main", "intopt1", 13) == 42
|
||||
assert cfgParser.rdInt("main", "intopt2", 13) == 13
|
||||
assert cfgParser.rdInt("main", "stropt", 13) == 13
|
||||
|
||||
assert cfgParser.rdInt("nope", "intopt1", 13) == 13
|
||||
assert cfgParser.rdInt("main", "blabla", 13) == 13
|
||||
|
||||
# Read String List
|
||||
assert cfgParser.rdStrList("main", "list1", []) == []
|
||||
assert cfgParser.rdStrList("main", "list1", ["x"]) == ["a"]
|
||||
assert cfgParser.rdStrList("main", "list1", ["x", "y"]) == ["a", "b"]
|
||||
assert cfgParser.rdStrList("main", "list1", ["x", "y", "z"]) == ["a", "b", "c"]
|
||||
assert cfgParser.rdStrList("main", "list1", ["x", "y", "z", "w"]) == ["a", "b", "c", "w"]
|
||||
|
||||
assert cfgParser.rdStrList("main", "stropt", ["x"]) == ["value"]
|
||||
assert cfgParser.rdStrList("main", "intopt1", ["x"]) == ["42"]
|
||||
|
||||
assert cfgParser.rdStrList("nope", "list1", ["x"]) == ["x"]
|
||||
assert cfgParser.rdStrList("main", "blabla", ["x"]) == ["x"]
|
||||
|
||||
# Read Integer List
|
||||
assert cfgParser.rdIntList("main", "list2", []) == []
|
||||
assert cfgParser.rdIntList("main", "list2", [1]) == [17]
|
||||
assert cfgParser.rdIntList("main", "list2", [1, 2]) == [17, 18]
|
||||
assert cfgParser.rdIntList("main", "list2", [1, 2, 3]) == [17, 18, 19]
|
||||
assert cfgParser.rdIntList("main", "list2", [1, 2, 3, 4]) == [17, 18, 19, 4]
|
||||
|
||||
assert cfgParser.rdIntList("main", "stropt", [1]) == [1]
|
||||
assert cfgParser.rdIntList("main", "boolopt1", [1]) == [1]
|
||||
|
||||
assert cfgParser.rdIntList("nope", "list2", [1]) == [1]
|
||||
assert cfgParser.rdIntList("main", "blabla", [1]) == [1]
|
||||
|
||||
# Internal
|
||||
# ========
|
||||
|
||||
assert cfgParser._parseLine("main", "stropt", None, 999) is None
|
||||
|
||||
# END Test testBaseCommon_NWConfigParser
|
||||
|
||||
@@ -19,10 +19,9 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import configparser
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
from shutil import copyfile
|
||||
|
||||
@@ -161,6 +160,20 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
|
||||
assert tstConf.hasError is False
|
||||
assert tstConf.errData == []
|
||||
|
||||
# Check handling of novelWriter as a package
|
||||
with monkeypatch.context() as mp:
|
||||
tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir)
|
||||
assert tstConf.confPath == tmpDir
|
||||
assert tstConf.dataPath == tmpDir
|
||||
appRoot = tstConf.appRoot
|
||||
|
||||
mp.setattr("os.path.isfile", lambda *a: True)
|
||||
tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir)
|
||||
assert tstConf.confPath == tmpDir
|
||||
assert tstConf.dataPath == tmpDir
|
||||
assert tstConf.appRoot == os.path.dirname(appRoot)
|
||||
assert tstConf.appPath == os.path.dirname(appRoot)
|
||||
|
||||
assert tstConf.loadConfig()
|
||||
assert tstConf.saveConfig()
|
||||
|
||||
@@ -187,19 +200,34 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
|
||||
assert tstConf.saveConfig()
|
||||
|
||||
# Localisation
|
||||
# ============
|
||||
|
||||
i18nDir = os.path.join(fncDir, "i18n")
|
||||
os.mkdir(i18nDir)
|
||||
os.mkdir(os.path.join(i18nDir, "stuff"))
|
||||
tstConf.nwLangPath = i18nDir
|
||||
|
||||
copyfile(os.path.join(filesDir, "nw_en_GB.qm"), os.path.join(fncDir, "nw_en_GB.qm"))
|
||||
copyfile(os.path.join(filesDir, "nw_en_GB.qm"), os.path.join(i18nDir, "nw_en_GB.qm"))
|
||||
writeFile(os.path.join(i18nDir, "nw_en_GB.ts"), "")
|
||||
writeFile(os.path.join(i18nDir, "nw_abcd.qm"), "")
|
||||
|
||||
tstApp = MockApp()
|
||||
tstConf.initLocalisation(tstApp)
|
||||
|
||||
# Check Lists
|
||||
theList = tstConf.listLanguages(tstConf.LANG_NW)
|
||||
assert theList == [("en_GB", "British English")]
|
||||
theList = tstConf.listLanguages(tstConf.LANG_PROJ)
|
||||
assert theList == [("en", "English")]
|
||||
theList = tstConf.listLanguages(None)
|
||||
assert theList == []
|
||||
|
||||
# Add Language
|
||||
copyfile(os.path.join(filesDir, "nw_en_GB.qm"), os.path.join(i18nDir, "nw_fr.qm"))
|
||||
writeFile(os.path.join(i18nDir, "nw_fr.ts"), "")
|
||||
|
||||
theList = tstConf.listLanguages(tstConf.LANG_NW)
|
||||
assert theList == [("en_GB", "British English"), ("fr", "Français")]
|
||||
|
||||
copyfile(confFile, testFile)
|
||||
assert cmpFiles(testFile, compFile, [2, 9, 10])
|
||||
@@ -311,6 +339,7 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
|
||||
|
||||
# GUI Scaling
|
||||
# ===========
|
||||
|
||||
tmpConf.guiScale = 1.0
|
||||
assert tmpConf.pxInt(10) == 10
|
||||
assert tmpConf.pxInt(13) == 13
|
||||
@@ -343,6 +372,19 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
|
||||
|
||||
assert tmpConf.setWinSize(1200, 650)
|
||||
|
||||
# Preferences Size
|
||||
tmpConf.guiScale = 2.0
|
||||
assert tmpConf.setPreferencesSize(70, 70)
|
||||
assert tmpConf.getPreferencesSize() == [70, 70]
|
||||
assert tmpConf.prefGeometry == [35, 35]
|
||||
|
||||
tmpConf.guiScale = 1.0
|
||||
assert tmpConf.setPreferencesSize(70, 70)
|
||||
assert tmpConf.getPreferencesSize() == [70, 70]
|
||||
assert tmpConf.prefGeometry == [70, 70]
|
||||
|
||||
assert tmpConf.setPreferencesSize(700, 615)
|
||||
|
||||
# Project Tree Columns
|
||||
tmpConf.guiScale = 2.0
|
||||
assert tmpConf.setTreeColWidths([10, 20, 25])
|
||||
@@ -436,6 +478,7 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
|
||||
|
||||
# Getters Only
|
||||
# ============
|
||||
|
||||
tmpConf.guiScale = 1.0
|
||||
assert tmpConf.getTextWidth() == 600
|
||||
assert tmpConf.getTextMargin() == 40
|
||||
@@ -450,6 +493,7 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
|
||||
|
||||
# Flag Setters
|
||||
# ============
|
||||
|
||||
assert not tmpConf.setShowRefPanel(False)
|
||||
assert not tmpConf.showRefPanel
|
||||
assert tmpConf.setShowRefPanel(True)
|
||||
@@ -482,74 +526,16 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
|
||||
# Function _packList
|
||||
assert tmpConf._packList(["A", 1, 2.0, None, False]) == "A, 1, 2.0, None, False"
|
||||
|
||||
# Function _unpackList
|
||||
assert tmpConf._unpackList("1, 2, 3", [0, 0, 0], tmpConf.CNF_I_LST) == [1, 2, 3]
|
||||
assert tmpConf._unpackList("1, 2 ", [0, 0, 0], tmpConf.CNF_I_LST) == [1, 2, 0]
|
||||
assert tmpConf._unpackList("A, B, C", [0, 0, 0], tmpConf.CNF_I_LST) == [0, 0, 0]
|
||||
assert tmpConf._unpackList("1, 2, 3", ["X", "Y", "Z"], tmpConf.CNF_S_LST) == ["1", "2", "3"]
|
||||
assert tmpConf._unpackList("A, B ", ["X", "Y", "Z"], tmpConf.CNF_S_LST) == ["A", "B", "Z"]
|
||||
assert tmpConf._unpackList("A, B, C", ["X", "Y", "Z"], tmpConf.CNF_S_LST) == ["A", "B", "C"]
|
||||
assert tmpConf._unpackList("A, B, C", ["X", "Y", "Z"], tmpConf.CNF_STR) == ["X", "Y", "Z"]
|
||||
|
||||
# Function _parseLine
|
||||
cnfParse = configparser.ConfigParser()
|
||||
cnfParse.read_string(
|
||||
"[Main]\n"
|
||||
"val_string = stuff\n"
|
||||
"val_int = 123\n"
|
||||
"val_bool = True\n"
|
||||
"val_list_string = A, B, C\n"
|
||||
"val_list_int = 1, 2, 3\n"
|
||||
)
|
||||
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "val_string", tmpConf.CNF_STR, "default"
|
||||
) == "stuff"
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "nope", tmpConf.CNF_STR, "default"
|
||||
) == "default"
|
||||
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "val_int", tmpConf.CNF_INT, "0"
|
||||
) == 123
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "nope", tmpConf.CNF_INT, 0
|
||||
) == 0
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "val_string", tmpConf.CNF_INT, 0
|
||||
) == 0
|
||||
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "val_bool", tmpConf.CNF_BOOL, False
|
||||
) is True
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "nope", tmpConf.CNF_BOOL, False
|
||||
) is False
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "val_string", tmpConf.CNF_BOOL, False
|
||||
) is False
|
||||
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "val_list_string", tmpConf.CNF_S_LST, ["W", "X", "Y", "Z"]
|
||||
) == ["A", "B", "C", "Z"]
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "nope", tmpConf.CNF_S_LST, ["W", "X", "Y", "Z"]
|
||||
) == ["W", "X", "Y", "Z"]
|
||||
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "val_list_int", tmpConf.CNF_I_LST, [6, 7, 8, 9]
|
||||
) == [1, 2, 3, 9]
|
||||
assert tmpConf._parseLine(
|
||||
cnfParse, "Main", "nope", tmpConf.CNF_S_LST, [6, 7, 8, 9]
|
||||
) == [6, 7, 8, 9]
|
||||
|
||||
# Function _checkNone
|
||||
assert tmpConf._checkNone(None) is None
|
||||
assert tmpConf._checkNone("None") is None
|
||||
assert tmpConf._checkNone("stuff") == "stuff"
|
||||
assert tmpConf._checkNone("none") is None
|
||||
assert tmpConf._checkNone("NONE") is None
|
||||
assert tmpConf._checkNone("NoNe") is None
|
||||
assert tmpConf._checkNone(123456) == 123456
|
||||
|
||||
# Function _checkOptionalPackages
|
||||
# (Assumes enchant package exists ans is importable)
|
||||
# (Assumes enchant package exists and is importable)
|
||||
tmpConf._checkOptionalPackages()
|
||||
assert tmpConf.hasEnchant is True
|
||||
|
||||
@@ -559,12 +545,12 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
|
||||
assert tmpConf.hasEnchant is False
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("shutil.which", lambda *args: "stuff")
|
||||
mp.setattr("shutil.which", lambda *a: "stuff")
|
||||
tmpConf._checkOptionalPackages()
|
||||
assert tmpConf.hasAssistant is True
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("shutil.which", lambda *args: None)
|
||||
mp.setattr("shutil.which", lambda *a: None)
|
||||
tmpConf._checkOptionalPackages()
|
||||
assert tmpConf.hasAssistant is False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user