Merge branch 'main' into release_1.0rc1

This commit is contained in:
Veronica K. B. Olsen
2020-11-01 17:08:44 +01:00
20 changed files with 166 additions and 113 deletions
+2 -2
View File
@@ -165,9 +165,9 @@ def formatTimeStamp(theTime, fileSafe=False):
it to a timestamp string. it to a timestamp string.
""" """
if fileSafe: if fileSafe:
return datetime.fromtimestamp(theTime).strftime(nwConst.fStampFmt) return datetime.fromtimestamp(theTime).strftime(nwConst.FMT_FSTAMP)
else: else:
return datetime.fromtimestamp(theTime).strftime(nwConst.tStampFmt) return datetime.fromtimestamp(theTime).strftime(nwConst.FMT_TSTAMP)
def formatTime(tS): def formatTime(tS):
"""Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format """Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format
+7 -7
View File
@@ -37,7 +37,7 @@ from shutil import which
from PyQt5.Qt import PYQT_VERSION_STR from PyQt5.Qt import PYQT_VERSION_STR
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
from nw.constants import nwFiles, nwUnicode from nw.constants import nwConst, nwFiles, nwUnicode
from nw.common import splitVersionNumber, formatTimeStamp from nw.common import splitVersionNumber, formatTimeStamp
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -88,14 +88,14 @@ class Config:
self.guiIcons = "typicons_colour_light" self.guiIcons = "typicons_colour_light"
self.guiDark = False # Load icons for dark backgrounds, if available self.guiDark = False # Load icons for dark backgrounds, if available
self.guiLang = "en" # Hardcoded for now since the GUI is only in English self.guiLang = "en" # Hardcoded for now since the GUI is only in English
self.guiFont = "" # Defaults to system defualt font self.guiFont = "" # Defaults to system default font
self.guiFontSize = 11 self.guiFontSize = 11
self.guiScale = 1.0 # Set automatically by Theme class self.guiScale = 1.0 # Set automatically by Theme class
## Sizes ## Sizes
self.winGeometry = [1100, 650] self.winGeometry = [1200, 650]
self.treeColWidth = [120, 30, 50] self.treeColWidth = [200, 50, 30]
self.projColWidth = [140, 55, 140] self.projColWidth = [200, 60, 140]
self.mainPanePos = [300, 800] self.mainPanePos = [300, 800]
self.docPanePos = [400, 400] self.docPanePos = [400, 400]
self.viewPanePos = [500, 150] self.viewPanePos = [500, 150]
@@ -284,7 +284,7 @@ class Config:
logger.verbose("App path: %s" % self.appPath) logger.verbose("App path: %s" % self.appPath)
logger.verbose("Last path: %s" % self.lastPath) logger.verbose("Last path: %s" % self.lastPath)
# If config folder does not exist, make it. # If config folder does not exist, create it.
# This assumes that the os config folder itself exists. # This assumes that the os config folder itself exists.
if not os.path.isdir(self.confPath): if not os.path.isdir(self.confPath):
try: try:
@@ -327,7 +327,7 @@ class Config:
self._checkOptionalPackages() self._checkOptionalPackages()
if self.spellTool is None: if self.spellTool is None:
self.spellTool = "internal" self.spellTool = nwConst.SP_INTERNAL
if self.spellLanguage is None: if self.spellLanguage is None:
self.spellLanguage = "en" self.spellLanguage = "en"
+30 -6
View File
@@ -29,13 +29,19 @@ from nw.constants.enum import nwItemClass, nwItemLayout, nwOutline
class nwConst(): class nwConst():
tStampFmt = "%Y-%m-%d %H:%M:%S" # Default format # Date and Time Formats
fStampFmt = "%Y-%m-%d %H.%M.%S" # FileName safe format FMT_TSTAMP = "%Y-%m-%d %H:%M:%S" # Default format
dStampFmt = "%Y-%m-%d" # Date only format FMT_FSTAMP = "%Y-%m-%d %H.%M.%S" # FileName safe format
FMT_DSTAMP = "%Y-%m-%d" # Date only format
maxDepth = 30 # Maximum folder depth of a project # Various Hard Limits
maxDocSize = 5000000 # Maxium size of a single document MAX_DEPTH = 30 # Maximum folder depth of a project
maxBuildSize = 10000000 # Maxium size of a project build MAX_DOCSIZE = 5000000 # Maxium size of a single document
MAX_BUILDSIZE = 10000000 # Maxium size of a project build
# Spell Check Providers
SP_INTERNAL = "internal"
SP_ENCHANT = "enchant"
# END Class nwConst # END Class nwConst
@@ -74,6 +80,24 @@ class nwKeyWords:
ENTITY_KEY = "@entity" ENTITY_KEY = "@entity"
CUSTOM_KEY = "@custom" CUSTOM_KEY = "@custom"
# Set of Valid Keys
VALID_KEYS = {
TAG_KEY, POV_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY,
WORLD_KEY, OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY
}
# Map from Keys to Item Class
KEY_CLASS = {
CHAR_KEY : nwItemClass.CHARACTER,
POV_KEY : nwItemClass.CHARACTER,
PLOT_KEY : nwItemClass.PLOT,
TIME_KEY : nwItemClass.TIMELINE,
WORLD_KEY : nwItemClass.WORLD,
OBJECT_KEY : nwItemClass.OBJECT,
ENTITY_KEY : nwItemClass.ENTITY,
CUSTOM_KEY : nwItemClass.CUSTOM,
}
# END Class nwKeyWords # END Class nwKeyWords
class nwLabels(): class nwLabels():
+1 -1
View File
@@ -251,7 +251,7 @@ class NWDoc():
self._docMeta["layout"] = nwItemLayout[metaBits[1]] self._docMeta["layout"] = nwItemLayout[metaBits[1]]
else: else:
logger.debug("Ignoring meta data: '%s'" % metaLine) logger.debug("Ignoring meta data: '%s'" % metaLine.strip())
return return
+3 -25
View File
@@ -42,28 +42,6 @@ logger = logging.getLogger(__name__)
class NWIndex(): class NWIndex():
VALID_KEYS = {
nwKeyWords.TAG_KEY,
nwKeyWords.PLOT_KEY,
nwKeyWords.POV_KEY,
nwKeyWords.CHAR_KEY,
nwKeyWords.WORLD_KEY,
nwKeyWords.TIME_KEY,
nwKeyWords.OBJECT_KEY,
nwKeyWords.ENTITY_KEY,
nwKeyWords.CUSTOM_KEY
}
TAG_CLASS = {
nwKeyWords.CHAR_KEY : nwItemClass.CHARACTER,
nwKeyWords.POV_KEY : nwItemClass.CHARACTER,
nwKeyWords.PLOT_KEY : nwItemClass.PLOT,
nwKeyWords.TIME_KEY : nwItemClass.TIMELINE,
nwKeyWords.WORLD_KEY : nwItemClass.WORLD,
nwKeyWords.OBJECT_KEY : nwItemClass.OBJECT,
nwKeyWords.ENTITY_KEY : nwItemClass.ENTITY,
nwKeyWords.CUSTOM_KEY : nwItemClass.CUSTOM,
}
def __init__(self, theProject, theParent): def __init__(self, theProject, theParent):
# Internal # Internal
@@ -530,7 +508,7 @@ class NWIndex():
return [] return []
# Check that the key is valid # Check that the key is valid
isGood[0] = theBits[0] in self.VALID_KEYS isGood[0] = theBits[0] in nwKeyWords.VALID_KEYS
if not isGood[0] or nBits == 1: if not isGood[0] or nBits == 1:
return isGood return isGood
@@ -550,7 +528,7 @@ class NWIndex():
# If we're still here, we better check that the references exist # If we're still here, we better check that the references exist
for n in range(1, nBits): for n in range(1, nBits):
if theBits[n] in self.tagIndex: if theBits[n] in self.tagIndex:
isGood[n] = self.TAG_CLASS[theBits[0]].name == self.tagIndex[theBits[n]][2] isGood[n] = nwKeyWords.KEY_CLASS[theBits[0]].name == self.tagIndex[theBits[n]][2]
return isGood return isGood
@@ -608,7 +586,7 @@ class NWIndex():
section. sTitle must be a string. section. sTitle must be a string.
""" """
theRefs = {} theRefs = {}
for tKey in self.TAG_CLASS: for tKey in nwKeyWords.KEY_CLASS:
theRefs[tKey] = [] theRefs[tKey] = []
if tHandle not in self.refIndex: if tHandle not in self.refIndex:
+12 -16
View File
@@ -31,7 +31,7 @@ import os
from difflib import get_close_matches from difflib import get_close_matches
from nw.constants import isoLanguage from nw.constants import nwConst, isoLanguage
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -41,11 +41,8 @@ logger = logging.getLogger(__name__)
class NWSpellCheck(): class NWSpellCheck():
SP_INTERNAL = "internal"
SP_ENCHANT = "enchant"
theDict = None theDict = None
PROJW = [] projDict = []
def __init__(self): def __init__(self):
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
@@ -71,9 +68,9 @@ class NWSpellCheck():
def addWord(self, newWord): def addWord(self, newWord):
"""Add a word to the project dictionary. """Add a word to the project dictionary.
""" """
if self.projectDict is not None and newWord not in self.PROJW: if self.projectDict is not None and newWord not in self.projDict:
newWord = newWord.strip() newWord = newWord.strip()
self.PROJW.append(newWord) self.projDict.append(newWord)
try: try:
with open(self.projectDict, mode="a+", encoding="utf-8") as outFile: with open(self.projectDict, mode="a+", encoding="utf-8") as outFile:
outFile.write("%s\n" % newWord) outFile.write("%s\n" % newWord)
@@ -110,7 +107,7 @@ class NWSpellCheck():
"""Read the content of the project dictionary, and add it to the """Read the content of the project dictionary, and add it to the
lookup lists. lookup lists.
""" """
self.PROJW = [] self.projDict = []
if projectDict is not None: if projectDict is not None:
self.projectDict = projectDict self.projectDict = projectDict
if not os.path.isfile(projectDict): if not os.path.isfile(projectDict):
@@ -120,9 +117,9 @@ class NWSpellCheck():
with open(projectDict, mode="r", encoding="utf-8") as wordsFile: with open(projectDict, mode="r", encoding="utf-8") as wordsFile:
for theLine in wordsFile: for theLine in wordsFile:
theLine = theLine.strip() theLine = theLine.strip()
if len(theLine) > 0 and theLine not in self.PROJW: if len(theLine) > 0 and theLine not in self.projDict:
self.PROJW.append(theLine) self.projDict.append(theLine)
logger.debug("Project word list contains %d words" % len(self.PROJW)) logger.debug("Project word list contains %d words" % len(self.projDict))
except Exception as e: except Exception as e:
logger.error("Failed to load project word list") logger.error("Failed to load project word list")
logger.error(str(e)) logger.error(str(e))
@@ -157,7 +154,7 @@ class NWSpellEnchant(NWSpellCheck):
self.spellLanguage = None self.spellLanguage = None
self._readProjectDictionary(projectDict) self._readProjectDictionary(projectDict)
for pWord in self.PROJW: for pWord in self.projDict:
self.theDict.add_to_session(pWord) self.theDict.add_to_session(pWord)
return return
@@ -236,7 +233,6 @@ class NWSpellSimple(NWSpellCheck):
when no other is available. This method is fairly slow compared to when no other is available. This method is fairly slow compared to
other implementations. other implementations.
""" """
WORDS = [] WORDS = []
def __init__(self): def __init__(self):
@@ -266,7 +262,7 @@ class NWSpellSimple(NWSpellCheck):
self.spellLanguage = None self.spellLanguage = None
self._readProjectDictionary(projectDict) self._readProjectDictionary(projectDict)
for pWord in self.PROJW: for pWord in self.projDict:
if pWord not in self.WORDS: if pWord not in self.WORDS:
self.WORDS.append(pWord) self.WORDS.append(pWord)
@@ -324,7 +320,7 @@ class NWSpellSimple(NWSpellCheck):
if theBits[1] != ".dict": if theBits[1] != ".dict":
continue continue
spName = "%s [internal]" % self.expandLanguage(theBits[0]) spName = "%s [%s]" % (self.expandLanguage(theBits[0]), nwConst.SP_INTERNAL)
retList.append((theBits[0], spName)) retList.append((theBits[0], spName))
return retList return retList
@@ -333,6 +329,6 @@ class NWSpellSimple(NWSpellCheck):
"""Return the tag and provider of the currently loaded """Return the tag and provider of the currently loaded
dictionary. dictionary.
""" """
return self.theLang, "internal" return self.theLang, nwConst.SP_INTERNAL
# END Class NWSpellSimple # END Class NWSpellSimple
+1 -1
View File
@@ -216,7 +216,7 @@ class Tokenizer():
self.theText = theDocument.openDocument(theHandle) self.theText = theDocument.openDocument(theHandle)
docSize = len(self.theText) docSize = len(self.theText)
if docSize > nwConst.maxDocSize: if docSize > nwConst.MAX_DOCSIZE:
errVal = "Document '%s' is too big (%.2f MB). Skipping." % ( errVal = "Document '%s' is too big (%.2f MB). Skipping." % (
self.theItem.itemName, docSize/1.0e6 self.theItem.itemName, docSize/1.0e6
) )
+2 -2
View File
@@ -260,7 +260,7 @@ class NWTree():
""" """
tItem = self.__getitem__(tHandle) tItem = self.__getitem__(tHandle)
if tItem is not None: if tItem is not None:
for i in range(nwConst.maxDepth + 1): for i in range(nwConst.MAX_DEPTH + 1):
if tItem.itemParent is None: if tItem.itemParent is None:
return tItem return tItem
else: else:
@@ -278,7 +278,7 @@ class NWTree():
tItem = self.__getitem__(tHandle) tItem = self.__getitem__(tHandle)
if tItem is not None: if tItem is not None:
tTree.append(tHandle) tTree.append(tHandle)
for i in range(nwConst.maxDepth + 1): for i in range(nwConst.MAX_DEPTH + 1):
if tItem.itemParent is None: if tItem.itemParent is None:
return tTree return tTree
else: else:
+12 -5
View File
@@ -422,7 +422,7 @@ class GuiBuildNovel(QDialog):
self.buttonBox.addWidget(self.btnSave) self.buttonBox.addWidget(self.btnSave)
self.buttonBox.addWidget(self.btnPrint) self.buttonBox.addWidget(self.btnPrint)
self.buttonBox.addWidget(self.btnClose) self.buttonBox.addWidget(self.btnClose)
self.buttonBox.setSpacing(4) self.buttonBox.setSpacing(self.mainConf.pxInt(4))
# Assemble GUI # Assemble GUI
# ============ # ============
@@ -464,12 +464,13 @@ class GuiBuildNovel(QDialog):
self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
# Tools and Buttons Layout # Tools and Buttons Layout
tSp = self.mainConf.pxInt(8)
self.innerBox = QVBoxLayout() self.innerBox = QVBoxLayout()
self.innerBox.addWidget(self.toolsArea) self.innerBox.addWidget(self.toolsArea)
self.innerBox.addSpacing(8) self.innerBox.addSpacing(tSp)
self.innerBox.addWidget(self.buildProgress) self.innerBox.addWidget(self.buildProgress)
self.innerBox.addWidget(self.buildNovel) self.innerBox.addWidget(self.buildNovel)
self.innerBox.addSpacing(8) self.innerBox.addSpacing(tSp)
self.innerBox.addLayout(self.buttonBox) self.innerBox.addLayout(self.buttonBox)
# Tools and Buttons Wrapper Widget # Tools and Buttons Wrapper Widget
@@ -482,6 +483,12 @@ class GuiBuildNovel(QDialog):
self.mainSplit.addWidget(self.docView) self.mainSplit.addWidget(self.docView)
self.mainSplit.setSizes([boxWidth, docWidth]) self.mainSplit.setSizes([boxWidth, docWidth])
self.idxSettings = self.mainSplit.indexOf(self.innerWidget)
self.idxDocument = self.mainSplit.indexOf(self.docView)
self.mainSplit.setCollapsible(self.idxSettings, False)
self.mainSplit.setCollapsible(self.idxDocument, False)
# Outer Layout # Outer Layout
self.outerBox = QHBoxLayout() self.outerBox = QHBoxLayout()
self.outerBox.addWidget(self.mainSplit) self.outerBox.addWidget(self.mainSplit)
@@ -508,7 +515,7 @@ class GuiBuildNovel(QDialog):
self.docView.setStyleSheet(self.htmlStyle) self.docView.setStyleSheet(self.htmlStyle)
htmlSize = sum([len(x) for x in self.htmlText]) htmlSize = sum([len(x) for x in self.htmlText])
if htmlSize < nwConst.maxBuildSize: if htmlSize < nwConst.MAX_BUILDSIZE:
qApp.processEvents() qApp.processEvents()
self.docView.setContent(self.htmlText, self.buildTime) self.docView.setContent(self.htmlText, self.buildTime)
else: else:
@@ -649,7 +656,7 @@ class GuiBuildNovel(QDialog):
else: else:
self.docView.setStyleSheet(self.htmlStyle) self.docView.setStyleSheet(self.htmlStyle)
if htmlSize < nwConst.maxBuildSize: if htmlSize < nwConst.MAX_BUILDSIZE:
self.docView.setContent(self.htmlText, self.buildTime) self.docView.setContent(self.htmlText, self.buildTime)
self._enableQtSave(True) self._enableQtSave(True)
else: else:
+11 -8
View File
@@ -50,7 +50,7 @@ from PyQt5.QtWidgets import (
QFrame QFrame
) )
from nw.core import NWDoc, NWSpellCheck, NWSpellSimple, countWords from nw.core import NWDoc, NWSpellSimple, countWords
from nw.gui.dochighlight import GuiDocHighlighter from nw.gui.dochighlight import GuiDocHighlighter
from nw.common import transferCase from nw.common import transferCase
from nw.constants import ( from nw.constants import (
@@ -284,12 +284,12 @@ class GuiDocEditor(QTextEdit):
return False return False
docSize = len(theDoc) docSize = len(theDoc)
if docSize > nwConst.maxDocSize: if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(( self.theParent.makeAlert((
"The document you are trying to open is too big. " "The document you are trying to open is too big. "
"The document size is %.2f\u202fMB. " "The document size is %.2f\u202fMB. "
"The maximum size allowed is %.2f\u202fMB." "The maximum size allowed is %.2f\u202fMB."
) % (docSize/1.0e6, nwConst.maxDocSize/1.0e6), nwAlert.ERROR) ) % (docSize/1.0e6, nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR)
self.clearEditor() self.clearEditor()
return False return False
@@ -361,12 +361,12 @@ class GuiDocEditor(QTextEdit):
text. This also clears undo history. text. This also clears undo history.
""" """
docSize = len(theText) docSize = len(theText)
if docSize > nwConst.maxDocSize: if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(( self.theParent.makeAlert((
"The text you are trying to add is too big. " "The text you are trying to add is too big. "
"The text size is %.2f\u202fMB. " "The text size is %.2f\u202fMB. "
"The maximum size allowed is %.2f\u202fMB." "The maximum size allowed is %.2f\u202fMB."
) % (docSize/1.0e6, nwConst.maxDocSize/1.0e6), nwAlert.ERROR) ) % (docSize/1.0e6, nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR)
return False return False
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
@@ -859,11 +859,11 @@ class GuiDocEditor(QTextEdit):
""" """
self.lastEdit = time() self.lastEdit = time()
self.lastFind = None self.lastFind = None
if self.qDocument.characterCount() > nwConst.maxDocSize: if self.qDocument.characterCount() > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(( self.theParent.makeAlert((
"The document has grown too big and you cannot add more text to it. " "The document has grown too big and you cannot add more text to it. "
"The maximum size of a single novelWriter document is %.2f\u202fMB." "The maximum size of a single novelWriter document is %.2f\u202fMB."
) % (nwConst.maxDocSize/1.0e6), nwAlert.ERROR) ) % (nwConst.MAX_DOCSIZE/1.0e6), nwAlert.ERROR)
self.undo() self.undo()
return return
if not self.docChanged: if not self.docChanged:
@@ -1004,6 +1004,9 @@ class GuiDocEditor(QTextEdit):
"""Decide whether to run the word counter, or not due to """Decide whether to run the word counter, or not due to
inactivity. inactivity.
""" """
if self.theHandle is None:
return
if self.wCounter.isRunning(): if self.wCounter.isRunning():
logger.verbose("Word counter is busy") logger.verbose("Word counter is busy")
return return
@@ -1611,7 +1614,7 @@ class GuiDocEditor(QTextEdit):
"""Create the spell checking object based on the spellTool """Create the spell checking object based on the spellTool
setting in config. setting in config.
""" """
if self.mainConf.spellTool == NWSpellCheck.SP_ENCHANT: if self.mainConf.spellTool == nwConst.SP_ENCHANT:
from nw.core.spellcheck import NWSpellEnchant from nw.core.spellcheck import NWSpellEnchant
self.theDict = NWSpellEnchant() self.theDict = NWSpellEnchant()
else: else:
+1 -1
View File
@@ -155,7 +155,7 @@ class GuiDocSplit(QDialog):
# Check that another folder can be created # Check that another folder can be created
parTree = self.theProject.projTree.getItemPath(srcItem.itemParent) parTree = self.theProject.projTree.getItemPath(srcItem.itemParent)
if len(parTree) >= nwConst.maxDepth - 1: if len(parTree) >= nwConst.MAX_DEPTH - 1:
self.theParent.makeAlert(( self.theParent.makeAlert((
"Cannot add new folder for the document split. " "Cannot add new folder for the document split. "
"Maximum folder depth has been reached. " "Maximum folder depth has been reached. "
+21
View File
@@ -246,6 +246,27 @@ class GuiOutlineDetails(QScrollArea):
return return
def clearDetails(self):
"""Clear all the data labels.
"""
self.titleLabel.setText("<b>Title</b>")
self.titleValue.setText("")
self.fileValue.setText("")
self.itemValue.setText("")
self.cCValue.setText("")
self.wCValue.setText("")
self.pCValue.setText("")
self.synopValue.setText("")
self.povKeyValue.setText("")
self.chrKeyValue.setText("")
self.pltKeyValue.setText("")
self.timKeyValue.setText("")
self.wldKeyValue.setText("")
self.objKeyValue.setText("")
self.entKeyValue.setText("")
self.cstKeyValue.setText("")
return
def showItem(self, tHandle, sTitle): def showItem(self, tHandle, sTitle):
"""Update the content of the tree with the given handle and line """Update the content of the tree with the given handle and line
number pointing to a header. number pointing to a header.
+6 -5
View File
@@ -37,7 +37,8 @@ from PyQt5.QtWidgets import (
) )
from nw.gui.custom import QSwitch, QConfigLayout, PagedDialog, QuotesDialog from nw.gui.custom import QSwitch, QConfigLayout, PagedDialog, QuotesDialog
from nw.core import NWSpellCheck, NWSpellSimple, NWSpellEnchant from nw.core import NWSpellSimple, NWSpellEnchant
from nw.constants import nwConst
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -703,11 +704,11 @@ class GuiConfigEditEditingTab(QWidget):
## Spell Check Provider and Language ## Spell Check Provider and Language
self.spellLangList = QComboBox(self) self.spellLangList = QComboBox(self)
self.spellToolList = QComboBox(self) self.spellToolList = QComboBox(self)
self.spellToolList.addItem("Internal (difflib)", NWSpellCheck.SP_INTERNAL) self.spellToolList.addItem("Internal (difflib)", nwConst.SP_INTERNAL)
self.spellToolList.addItem("Spell Enchant (pyenchant)", NWSpellCheck.SP_ENCHANT) self.spellToolList.addItem("Spell Enchant (pyenchant)", nwConst.SP_ENCHANT)
theModel = self.spellToolList.model() theModel = self.spellToolList.model()
idEnchant = self.spellToolList.findData(NWSpellCheck.SP_ENCHANT) idEnchant = self.spellToolList.findData(nwConst.SP_ENCHANT)
theModel.item(idEnchant).setEnabled(self.mainConf.hasEnchant) theModel.item(idEnchant).setEnabled(self.mainConf.hasEnchant)
self.spellToolList.currentIndexChanged.connect(self._doUpdateSpellTool) self.spellToolList.currentIndexChanged.connect(self._doUpdateSpellTool)
@@ -814,7 +815,7 @@ class GuiConfigEditEditingTab(QWidget):
preserve the language choice, if the language exists in the preserve the language choice, if the language exists in the
updated list. updated list.
""" """
if spellTool == NWSpellCheck.SP_ENCHANT: if spellTool == nwConst.SP_ENCHANT:
theDict = NWSpellEnchant() theDict = NWSpellEnchant()
else: else:
theDict = NWSpellSimple() theDict = NWSpellSimple()
+3 -3
View File
@@ -243,8 +243,8 @@ class GuiProjectTree(QTreeWidget):
tHandle = self.theProject.newFile("New File", itemClass, pHandle) tHandle = self.theProject.newFile("New File", itemClass, pHandle)
elif itemType == nwItemType.FOLDER: elif itemType == nwItemType.FOLDER:
if len(parTree) >= nwConst.maxDepth - 1: if len(parTree) >= nwConst.MAX_DEPTH - 1:
# Folders cannot be deeper than maxDepth - 1, leaving room # Folders cannot be deeper than MAX_DEPTH - 1, leaving room
# for one more level of files. # for one more level of files.
self.makeAlert(( self.makeAlert((
"Cannot add new folder to this item. " "Cannot add new folder to this item. "
@@ -579,7 +579,7 @@ class GuiProjectTree(QTreeWidget):
pCount += int(pItem.child(i).data(self.C_COUNT, Qt.UserRole)) pCount += int(pItem.child(i).data(self.C_COUNT, Qt.UserRole))
pHandle = pItem.data(self.C_NAME, Qt.UserRole) pHandle = pItem.data(self.C_NAME, Qt.UserRole)
if not nDepth > nwConst.maxDepth + 1 and pHandle != "": if not nDepth > nwConst.MAX_DEPTH + 1 and pHandle != "":
self.propagateCount(pHandle, pCount, nDepth+1) self.propagateCount(pHandle, pCount, nDepth+1)
return return
+4 -4
View File
@@ -428,10 +428,10 @@ class GuiWritingStats(QDialog):
continue continue
dStart = datetime.strptime( dStart = datetime.strptime(
"%s %s" % (inData[0], inData[1]), nwConst.tStampFmt "%s %s" % (inData[0], inData[1]), nwConst.FMT_TSTAMP
) )
dEnd = datetime.strptime( dEnd = datetime.strptime(
"%s %s" % (inData[2], inData[3]), nwConst.tStampFmt "%s %s" % (inData[2], inData[3]), nwConst.FMT_TSTAMP
) )
tDiff = dEnd - dStart tDiff = dEnd - dStart
@@ -528,9 +528,9 @@ class GuiWritingStats(QDialog):
isFirst = False isFirst = False
if groupByDay: if groupByDay:
sStart = dStart.strftime(nwConst.dStampFmt) sStart = dStart.strftime(nwConst.FMT_DSTAMP)
else: else:
sStart = dStart.strftime(nwConst.tStampFmt) sStart = dStart.strftime(nwConst.FMT_TSTAMP)
self.filterData.append((dStart, sStart, sDiff, dwTotal, wcNovel, wcNotes)) self.filterData.append((dStart, sStart, sDiff, dwTotal, wcNovel, wcNotes))
listMax = min(max(listMax, dwTotal), histMax) listMax = min(max(listMax, dwTotal), histMax)
+1
View File
@@ -345,6 +345,7 @@ class GuiMain(QMainWindow):
self.closeDocument() self.closeDocument()
self.docViewer.clearNavHistory() self.docViewer.clearNavHistory()
self.projView.closeOutline() self.projView.closeOutline()
self.projMeta.clearDetails()
self.theProject.closeProject() self.theProject.closeProject()
self.theIndex.clearIndex() self.theIndex.clearIndex()
self.clearGUI() self.clearGUI()
+38 -16
View File
@@ -5,23 +5,20 @@ The main setup script for novelWeiter.
It runs the standard setuptool.setup() with all options taken from the It runs the standard setuptool.setup() with all options taken from the
setup.cfg file. setup.cfg file.
In addtion, a few speicalised commands are available: In addtion, a few speicalised commands are available. These are
described in the help text in the main section.
* sample: Will build a sample.zip file, which is the way the sample project is
included into distributable packages.
* qthelp: Will build a QtAssistant readable version of the novelWriter
documentation. This should also be a part of distributed packages. It allows
for reading the help offline. Otherwise, the F1 button redirects to the
online documentation only.
* launcher: Will attempt to install novelWriter icons, mime type and create a
launcher for the application.
""" """
import os import os
import sys import sys
import shutil import shutil
import subprocess import subprocess
OS_NONE = 0
OS_LINUX = 1
OS_WIN = 2
OS_DARWIN = 3
# =============================================================================================== # # =============================================================================================== #
# Qt Assistant Documentation Builder # Qt Assistant Documentation Builder
# =============================================================================================== # # =============================================================================================== #
@@ -285,6 +282,19 @@ def xdgInstall():
# =============================================================================================== # # =============================================================================================== #
if __name__ == "__main__": if __name__ == "__main__":
"""Parse command line options and run the commands.
"""
# Detect OS
if sys.platform.startswith("linux"):
hostOS = OS_LINUX
elif sys.platform.startswith("darwin"):
hostOS = OS_DARWIN
elif sys.platform.startswith("win32"):
hostOS = OS_WIN
elif sys.platform.startswith("cygwin"):
hostOS = OS_WIN
else:
hostOS = OS_NONE
helpMsg = ( helpMsg = (
"\n" "\n"
@@ -292,10 +302,17 @@ if __name__ == "__main__":
"======================\n" "======================\n"
"This tool provides some additional setup commands for novelWriter.\n" "This tool provides some additional setup commands for novelWriter.\n"
"\n" "\n"
"help Print the help message.\n" "help Print this help message.\n"
"qthelp Build the help documentation for use with the QtAssistant.\n" "qthelp Build the help documentation for use with the Qt Assistant.\n"
" Run before install to enable in the the installed version.\n"
"sample Build the sample project as a zip file.\n" "sample Build the sample project as a zip file.\n"
" Run before install to enable creating sample projects.\n"
"install Installs novelWriter to the system's Python install location.\n"
" Run as root or with sudo for system-wide install, or as\n"
" user for single user install.\n"
"xdg-install Install launcher and icons for freedesktop systems.\n" "xdg-install Install launcher and icons for freedesktop systems.\n"
" Run as root or with sudo for system-wide install, or as\n"
" user for single user install.\n"
) )
if "help" in sys.argv: if "help" in sys.argv:
@@ -303,6 +320,11 @@ if __name__ == "__main__":
print(helpMsg) print(helpMsg)
sys.exit(0) sys.exit(0)
if "launcher" in sys.argv:
sys.argv.remove("launcher")
print("The 'launcher' option has been replaced by 'xdg-install'.")
sys.exit(1)
if "qthelp" in sys.argv: if "qthelp" in sys.argv:
sys.argv.remove("qthelp") sys.argv.remove("qthelp")
buildQtDocs() buildQtDocs()
@@ -313,11 +335,11 @@ if __name__ == "__main__":
if "xdg-install" in sys.argv: if "xdg-install" in sys.argv:
sys.argv.remove("xdg-install") sys.argv.remove("xdg-install")
if not sys.platform.startswith("win32"): if hostOS == OS_WIN:
xdgInstall()
else:
print("ERROR: xdg-install cannot be used on Windows") print("ERROR: xdg-install cannot be used on Windows")
sys.exit(1) sys.exit(1)
else:
xdgInstall()
if len(sys.argv) <= 1: if len(sys.argv) <= 1:
# Nothing more to do # Nothing more to do
+3 -3
View File
@@ -8,9 +8,9 @@ guifont =
guifontsize = 11 guifontsize = 11
[Sizes] [Sizes]
geometry = 1100, 650 geometry = 1200, 650
treecols = 120, 30, 50 treecols = 200, 50, 30
projcols = 140, 55, 140 projcols = 200, 60, 140
mainpane = 300, 800 mainpane = 300, 800
docpane = 400, 400 docpane = 400, 400
viewpane = 500, 150 viewpane = 500, 150
+6 -6
View File
@@ -44,11 +44,11 @@ def testConfigSetWinSize(tmpConf, nwTemp, nwRef):
tmpConf.guiScale = 1.0 tmpConf.guiScale = 1.0
assert tmpConf.confPath == nwTemp assert tmpConf.confPath == nwTemp
assert tmpConf.setWinSize(1105, 655) assert tmpConf.setWinSize(1205, 655)
assert not tmpConf.confChanged assert not tmpConf.confChanged
assert tmpConf.setWinSize(70, 70) assert tmpConf.setWinSize(70, 70)
assert tmpConf.confChanged assert tmpConf.confChanged
assert tmpConf.setWinSize(1100, 650) assert tmpConf.setWinSize(1200, 650)
assert tmpConf.saveConfig() assert tmpConf.saveConfig()
assert cmpFiles(testConf, refConf, [2]) assert cmpFiles(testConf, refConf, [2])
@@ -62,13 +62,13 @@ def testConfigSetTreeColWidths(tmpConf, nwTemp, nwRef):
assert tmpConf.confPath == nwTemp assert tmpConf.confPath == nwTemp
tmpConf.guiScale = 1.0 tmpConf.guiScale = 1.0
assert tmpConf.setTreeColWidths([10, 20, 30]) assert tmpConf.setTreeColWidths([10, 20, 25])
assert tmpConf.treeColWidth == [10, 20, 30] assert tmpConf.treeColWidth == [10, 20, 25]
assert tmpConf.setTreeColWidths([120, 30, 50]) assert tmpConf.setTreeColWidths([200, 50, 30])
assert tmpConf.setProjColWidths([10, 20, 30]) assert tmpConf.setProjColWidths([10, 20, 30])
assert tmpConf.projColWidth == [10, 20, 30] assert tmpConf.projColWidth == [10, 20, 30]
assert tmpConf.setProjColWidths([140, 55, 140]) assert tmpConf.setProjColWidths([200, 60, 140])
assert tmpConf.confChanged assert tmpConf.confChanged
assert tmpConf.saveConfig() assert tmpConf.saveConfig()
+2 -2
View File
@@ -13,7 +13,7 @@ from nwtools import cmpFiles
from nw.core.project import NWProject from nw.core.project import NWProject
from nw.core.document import NWDoc from nw.core.document import NWDoc
from nw.core.spellcheck import NWSpellEnchant, NWSpellSimple from nw.core.spellcheck import NWSpellEnchant, NWSpellSimple
from nw.constants import nwItemClass, nwItemType, nwItemLayout, nwFiles from nw.constants import nwConst, nwItemClass, nwItemType, nwItemLayout, nwFiles
@pytest.mark.project @pytest.mark.project
def testProjectNewOpenSave(nwFuncTemp, nwTempProj, nwRef, nwTemp, nwDummy): def testProjectNewOpenSave(nwFuncTemp, nwTempProj, nwRef, nwTemp, nwDummy):
@@ -405,7 +405,7 @@ def testSpellSimple(nwTemp, nwConf):
aTag, aName = spChk.describeDict() aTag, aName = spChk.describeDict()
assert aTag == "en" assert aTag == "en"
assert aName == "internal" assert aName == nwConst.SP_INTERNAL
@pytest.mark.project @pytest.mark.project
def testProjectOptions(nwDummy, nwLipsum): def testProjectOptions(nwDummy, nwLipsum):