From d998900a3a4b24856c0bdc1b7083bac3bc2eaa84 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 24 May 2020 13:44:32 +0200 Subject: [PATCH 1/6] Pin some dependency versions now that some of the limits are known --- README.md | 6 ++++++ docs/source/started.txt | 5 +++++ requirements.txt | 6 +++--- setup.py | 6 +++--- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a69a866b..bf1efa1c 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,12 @@ python3 -m pip install lxml python3 -m pip install pyenchant ``` +PyQt/Qt should be at least 5.2.1, but ideally 5.10 or higher for nearly all features to work. +Exporting to markdown requires PyQt/Qt 5.14. +There are no known minimum for lxml, but the code was originally written with 4.2. +The optional spell check library must be at least 3.0.0 to work with Windows. +On Linux, 2.0.0 also works fine. + If no external spell checking tool is installed, novelWriter will use a basic spell checker based on standard Python package `difflib`. Currently, only English dictionaries are available for this spell checker, but more can be added to the `nw/assets/dict` folder. See the [nw/assets/dict/README.md](README.md) file in that folder for how to generate more dictionaries. diff --git a/docs/source/started.txt b/docs/source/started.txt index 5c279e14..c27ff27e 100644 --- a/docs/source/started.txt +++ b/docs/source/started.txt @@ -35,6 +35,11 @@ The following are optional, but recommended: * ``pyenchant`` for spell checking +PyQt/Qt should be at least 5.2.1, but ideally 5.10 or higher for nearly all features to work. +Exporting to markdown requires PyQt/Qt 5.14. +There are no known minimum for lxml, but the code was originally written with 4.2. +The optional spell check library must be at least 3.0.0 to work with Windows. +On Linux, 2.0.0 also works fine. Running novelWriter =================== diff --git a/requirements.txt b/requirements.txt index 08fc42c3..c50e7b68 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -pyqt5 -lxml -pyenchant +pyqt5>=5.2.1 +lxml>=4.2.0 +pyenchant>=3.0.0 diff --git a/setup.py b/setup.py index f9736fee..9a59bccf 100755 --- a/setup.py +++ b/setup.py @@ -37,8 +37,8 @@ setuptools.setup( ], python_requires = ">=3.6", install_requires = [ - "pyqt5", - "lxml", - "pyenchant", + "pyqt5>=5.2.1", + "lxml>=4.2.0", + "pyenchant>=3.0.0", ], ) From e1ce93416c9c09f79b44b5d60cfc502cf5bac91c Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 24 May 2020 15:38:58 +0200 Subject: [PATCH 2/6] Outline should not show non-exported items --- nw/core/index.py | 9 +++++++-- nw/gui/elements/outline.py | 4 +--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/nw/core/index.py b/nw/core/index.py index 60129b35..346fdb36 100644 --- a/nw/core/index.py +++ b/nw/core/index.py @@ -529,13 +529,18 @@ class NWIndex(): # Extract Data ## - def getNovelStructure(self): + def getNovelStructure(self, skipExcluded=True): """Builds a list of all titles in the novel, in the correct order as they appear in the tree view and in the respective document files, but skipping all note files. """ theStructure = [] - for tHandle in self.theProject.projTree.handles(): + for tItem in self.theProject.projTree: + if tItem is None: + continue + if not tItem.isExported and skipExcluded: + continue + tHandle = tItem.itemHandle if tHandle not in self.novelIndex: continue for sTitle in sorted(self.novelIndex[tHandle].keys()): diff --git a/nw/gui/elements/outline.py b/nw/gui/elements/outline.py index 54cfbf5a..408dc126 100644 --- a/nw/gui/elements/outline.py +++ b/nw/gui/elements/outline.py @@ -319,7 +319,6 @@ class GuiProjectOutline(QTreeWidget): if they are hidden. This ensures that showing and hiding columns is fast and doesn't require a rebuild of the tree. """ - self.clear() if self.firstView: @@ -347,7 +346,7 @@ class GuiProjectOutline(QTreeWidget): currChapter = None currScene = None - for titleKey in self.theIndex.getNovelStructure(): + for titleKey in self.theIndex.getNovelStructure(skipExcluded=True): if len(titleKey) < 16: continue @@ -404,7 +403,6 @@ class GuiProjectOutline(QTreeWidget): def _createTreeItem(self, tHandle, sTitle): """Populate a tree item with all the column values. """ - nwItem = self.theProject.projTree[tHandle] novIdx = self.theIndex.novelIndex[tHandle][sTitle] From c865dbd59bbc5e6873ac8eff6442f99febb0251b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 24 May 2020 16:42:28 +0200 Subject: [PATCH 3/6] Added a lot of comments to functions in the source code --- nw/core/document.py | 4 +- nw/core/index.py | 1 - nw/core/project.py | 87 +++++++++++++++++++++++++------ nw/core/spellcheck.py | 38 +++++++++++++- nw/core/tools.py | 3 -- nw/gui/additions/pageddialog.py | 10 +++- nw/gui/additions/qconfiglayout.py | 6 ++- nw/gui/additions/qswitch.py | 1 - nw/gui/elements/docdetails.py | 39 +++++++------- nw/gui/elements/doceditor.py | 40 +++++++++----- nw/gui/elements/doctitlebar.py | 1 - nw/gui/elements/doctree.py | 47 +++++++++++++++-- nw/gui/elements/docviewer.py | 22 ++++++-- nw/gui/elements/noticebar.py | 4 ++ nw/gui/elements/outline.py | 4 -- nw/gui/elements/searchbar.py | 17 ++++++ nw/gui/elements/viewdetails.py | 11 +++- nw/gui/tools/dochighlight.py | 12 ++++- nw/gui/tools/optionstate.py | 2 - nw/gui/tools/wordcounter.py | 4 +- nw/guimain.py | 2 +- 21 files changed, 273 insertions(+), 82 deletions(-) diff --git a/nw/core/document.py b/nw/core/document.py index 6bcba588..4c502bea 100644 --- a/nw/core/document.py +++ b/nw/core/document.py @@ -136,7 +136,6 @@ class NWDoc(): """Save the document via temp file in case of save failure, and in any case keep a backup of the file. """ - if self.docHandle is None or not self.docEditable: return False @@ -202,7 +201,6 @@ class NWDoc(): """Parses the document meta tag and returns the path and name as a list and a string. """ - if len(self.docMeta) < 14: # Not enough information return "", [] @@ -232,6 +230,8 @@ class NWDoc(): @staticmethod def _assemblePath(tHandle, docExt): + """Assemble the file path for a given handle. + """ if tHandle is None: return None, None docDir = "data_"+tHandle[0] diff --git a/nw/core/index.py b/nw/core/index.py index 346fdb36..3633c841 100644 --- a/nw/core/index.py +++ b/nw/core/index.py @@ -244,7 +244,6 @@ class NWIndex(): and text as separate inputs as we want to primarily scan the files before we save them, unless we're rebuilding the index. """ - theItem = self.theProject.projTree[tHandle] if theItem is None: return False diff --git a/nw/core/project.py b/nw/core/project.py index 7838d5fd..4516d1aa 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -181,7 +181,6 @@ class NWProject(): """Clear the data for the current project, and set them to default values. """ - # Project Status self.projOpened = 0 self.projChanged = False @@ -236,7 +235,6 @@ class NWProject(): parse the XML of the file and populate the project variables and build the tree of project items. """ - if not path.isfile(fileName): fileName = path.join(fileName, nwFiles.PROJ_FILE) if not path.isfile(fileName): @@ -404,7 +402,6 @@ class NWProject(): make sure if the save fails, we're not left with a truncated file. """ - if self.projPath is None: self.makeAlert("Project path not set, cannot save.", nwAlert.ERROR) return False @@ -522,7 +519,6 @@ class NWProject(): def zipIt(self, doNotify): """Create a zip file of the entire project. """ - logger.info("Backing up project") self.theParent.statusBar.setStatus("Backing up project ...") @@ -836,7 +832,6 @@ class NWProject(): def _readLockFile(self): """Reads the lock file in the project folder. """ - if self.projPath is None: return ["ERROR"] @@ -863,7 +858,6 @@ class NWProject(): def _writeLockFile(self): """Writes a lock file to the project folder. """ - if self.projPath is None: return False @@ -901,6 +895,8 @@ class NWProject(): return None def _checkFolder(self, thePath): + """Check if a folder exists, and if it doesn't, create it. + """ if not path.isdir(thePath): try: mkdir(thePath) @@ -911,6 +907,8 @@ class NWProject(): return True def _packProjectValue(self, xParent, theName, theValue, allowNone=True): + """Pack a list of values into an xml element. + """ if not isinstance(theValue, list): theValue = [theValue] for aValue in theValue: @@ -927,7 +925,6 @@ class NWProject(): orphaned files so the user can either delete them, or put them back into the project tree. """ - if self.projPath is None: return @@ -992,7 +989,6 @@ class NWProject(): def _appendSessionStats(self): """Append session statistics to the sessions log file. """ - if self.projMeta is None: return False @@ -1234,9 +1230,13 @@ class NWTree(): ## def __len__(self): + """Return the length counter. Does not check that it is correct! + """ return self._theLength def __bool__(self): + """Returns True if the tree has any entries. + """ return self._theLength > 0 ## @@ -1382,7 +1382,6 @@ class NWItem(): def unpackXML(self, xItem): """Sets the values from an XML entry of type 'item'. """ - if xItem.tag != "item": logger.error("XML entry is not an NWItem") return False @@ -1420,9 +1419,11 @@ class NWItem(): @staticmethod def _subPack(xParent, name, attrib=None, text=None, none=True): + """Packs the values into an xml element. + """ if not none and (text == None or text == "None"): return None - xSub = etree.SubElement(xParent,name,attrib=attrib) + xSub = etree.SubElement(xParent, name, attrib=attrib) if text is not None: xSub.text = text return xSub @@ -1432,10 +1433,14 @@ class NWItem(): ## def setName(self, theName): + """Set the item name. + """ self.itemName = theName.strip() return def setHandle(self, theHandle): + """Set the item handle, and ensure it is valid. + """ if isinstance(theHandle, str): if len(theHandle) == 13: self.itemHandle = theHandle @@ -1446,6 +1451,8 @@ class NWItem(): return def setParent(self, theParent): + """Set the parent handle, and ensure that it is valid. + """ if theParent is None: self.parHandle = None elif isinstance(theParent, str): @@ -1458,10 +1465,16 @@ class NWItem(): return def setOrder(self, theOrder): + """Set the item order, and ensure that it is valid. This value + is purely a meta value, not actually used by novelWriter. + """ self.itemOrder = checkInt(theOrder, 0) return def setType(self, theType): + """Set the item type from either a proper nwItemType, or set it + from a string representing a nwItemType. + """ if isinstance(theType, nwItemType): self.itemType = theType elif theType in nwItemType.__members__: @@ -1472,6 +1485,9 @@ class NWItem(): return def setClass(self, theClass): + """Set the item class from either a proper nwItemClass, or set + it from a string representing a nwItemClass. + """ if isinstance(theClass, nwItemClass): self.itemClass = theClass elif theClass in nwItemClass.__members__: @@ -1482,6 +1498,9 @@ class NWItem(): return def setLayout(self, theLayout): + """Set the item layout from either a proper nwItemLayout, or set + it from a string representing a nwItemLayout. + """ if isinstance(theLayout, nwItemLayout): self.itemLayout = theLayout elif theLayout in nwItemLayout.__members__: @@ -1492,6 +1511,9 @@ class NWItem(): return def setStatus(self, theStatus): + """Set the item status by looking it up in the valid status + items of the current project. + """ if self.itemClass == nwItemClass.NOVEL: self.itemStatus = self.theProject.statusItems.checkEntry(theStatus) else: @@ -1499,6 +1521,8 @@ class NWItem(): return def setExpanded(self, expState): + """Save the expanded status of an item in the project tree. + """ if isinstance(expState, str): self.isExpanded = expState == str(True) else: @@ -1506,6 +1530,8 @@ class NWItem(): return def setExported(self, expState): + """Save the export flag. + """ if isinstance(expState, str): self.isExported = expState == str(True) else: @@ -1517,19 +1543,27 @@ class NWItem(): ## def setCharCount(self, theCount): - self.charCount = checkInt(theCount,0) + """Set the character count, and ensure that it is an integer. + """ + self.charCount = checkInt(theCount, 0) return def setWordCount(self, theCount): - self.wordCount = checkInt(theCount,0) + """Set the word count, and ensure that it is an integer. + """ + self.wordCount = checkInt(theCount, 0) return def setParaCount(self, theCount): - self.paraCount = checkInt(theCount,0) + """Set the paragraph count, and ensure that it is an integer. + """ + self.paraCount = checkInt(theCount, 0) return def setCursorPos(self, thePosition): - self.cursorPos = checkInt(thePosition,0) + """Set the cursor position, and ensure that it is an integer. + """ + self.cursorPos = checkInt(thePosition, 0) return # END Class NWItem @@ -1551,6 +1585,9 @@ class NWStatus(): return def addEntry(self, theLabel, theColours): + """Add a status entry to the status object, but ensure it isn't + a duplicate. + """ theLabel = theLabel.strip() if self.lookupEntry(theLabel) is None: self.theLabels.append(theLabel) @@ -1561,6 +1598,9 @@ class NWStatus(): return True def lookupEntry(self, theLabel): + """Look up a status entry in the object lists, and return it if + it exists. + """ if theLabel is None: return None theLabel = theLabel.strip() @@ -1569,6 +1609,9 @@ class NWStatus(): return None def checkEntry(self, theStatus): + """Check if a status value is valid, and returns the safe + reference to be used internally. + """ if isinstance(theStatus, str): theStatus = theStatus.strip() if self.lookupEntry(theStatus) is not None: @@ -1578,11 +1621,12 @@ class NWStatus(): return self.theLabels[theStatus] def setNewEntries(self, newList): - + """Update the list of entries after they have been modified by + the GUI tool. + """ replaceMap = {} if newList is not None: - self.theLabels = [] self.theColours = [] self.theCounts = [] @@ -1598,10 +1642,14 @@ class NWStatus(): return replaceMap def resetCounts(self): + """Clear the counts of references to the status entries. + """ self.theCounts = [0]*self.theLength return def countEntry(self, theLabel): + """Lookup the usage count of a given entry. + """ theIndex = self.lookupEntry(theLabel) if theIndex is not None: self.theCounts[theIndex] += 1 @@ -1623,7 +1671,6 @@ class NWStatus(): def unpackEntries(self, xParent): """Unpack an XML tree and set the class values. """ - theLabels = [] theColours = [] @@ -1661,15 +1708,21 @@ class NWStatus(): ## def __getitem__(self, n): + """Return an entry by its index. + """ if n >= 0 and n < self.theLength: return self.theLabels[n], self.theColours[n], self.theCounts[n] return None, None, None def __iter__(self): + """Initialise the iterator. + """ self.theIndex = 0 return self def __next__(self): + """Return the next entry for the iterator. + """ if self.theIndex < self.theLength: theLabel, theColour, theCount = self.__getitem__(self.theIndex) self.theIndex += 1 diff --git a/nw/core/spellcheck.py b/nw/core/spellcheck.py index fda4022d..2af06f42 100644 --- a/nw/core/spellcheck.py +++ b/nw/core/spellcheck.py @@ -55,15 +55,23 @@ class NWSpellCheck(): return def setLanguage(self, theLang, projectDict=None): + """Dummy function. + """ return def checkWord(self, theWord): + """Dummy function. + """ return True def suggestWords(self, theWord): + """Dummy function. + """ return [] def addWord(self, newWord): + """Add a word to the project dictionary. + """ if self.projectDict is not None and newWord not in self.PROJW: newWord = newWord.strip() self.PROJW.append(newWord) @@ -76,10 +84,14 @@ class NWSpellCheck(): return def listDictionaries(self): + """Dummy function. + """ return [] @staticmethod def expandLanguage(spTag): + """Translate a language tag to something more suer friendly. + """ spBits = spTag.split("_") if spBits[0] in isoLanguage.ISO_639_1: spLang = isoLanguage.ISO_639_1[spBits[0]] @@ -94,6 +106,9 @@ class NWSpellCheck(): ## def _readProjectDictionary(self, projectDict): + """Read the content of the project dictionary, and add it to the + lookup lists. + """ self.PROJW = [] if projectDict is not None: self.projectDict = projectDict @@ -147,17 +162,25 @@ class NWSpellEnchant(NWSpellCheck): return def checkWord(self, theWord): + """Wrapper function for pyenchant. + """ return self.theDict.check(theWord) def suggestWords(self, theWord): + """Wrapper function for pyenchant. + """ return self.theDict.suggest(theWord) def addWord(self, newWord): + """Wrapper function for pyenchant. + """ self.theDict.add_to_session(newWord) NWSpellCheck.addWord(self, newWord) return def listDictionaries(self): + """Wrapper function for pyenchant. + """ retList = [] try: import enchant @@ -171,6 +194,8 @@ class NWSpellEnchant(NWSpellCheck): # END Class NWSpellEnchant class NWSpellEnchantDummy: + """Fallback for when Enchant is selected, but not installed. + """ def __init__(self): return @@ -191,6 +216,11 @@ class NWSpellEnchantDummy: # ================================================================================================ # class NWSpellSimple(NWSpellCheck): + """Internal spell check tool that uses standard Python packages with + no other external dependencies. This is the fallback spell checker + when no other is available. This method is fairly slow compared to + other implementations. + """ WORDS = [] @@ -200,7 +230,8 @@ class NWSpellSimple(NWSpellCheck): return def setLanguage(self, theLang, projectDict=None): - + """Load a dictionary as a list from the app assets folder. + """ self.WORDS = [] dictFile = path.join(self.mainConf.dictPath,theLang+".dict") try: @@ -259,6 +290,8 @@ class NWSpellSimple(NWSpellCheck): return theOptions def addWord(self, newWord): + """Wrapper for the internal project dictionary feature. + """ newWord = newWord.strip().lower() if newWord not in self.WORDS: self.WORDS.append(newWord) @@ -266,7 +299,8 @@ class NWSpellSimple(NWSpellCheck): return def listDictionaries(self): - + """Lists the dictionary files in the app assets folder. + """ retList = [] for dictFile in listdir(self.mainConf.dictPath): diff --git a/nw/core/tools.py b/nw/core/tools.py index 2c96cee6..a6eb244b 100644 --- a/nw/core/tools.py +++ b/nw/core/tools.py @@ -39,7 +39,6 @@ def countWords(theText): """Count words in a piece of text, skipping special syntax and comments. """ - charCount = 0 wordCount = 0 paraCount = 0 @@ -86,7 +85,6 @@ def projectMaintenance(theProject): """Wrapper class for handling various tasks related to managing old projects with content from older versions of novelWriter. """ - # Remove no longer used project cache folder if path.isdir(theProject.projPath): cacheDir = path.join(theProject.projPath, "cache") @@ -141,7 +139,6 @@ def numberToWord(numVal, theLanguage): def _numberToWordEN(numVal): """Convert numbers to English words. """ - numWord = "" oneWord = "" tenWord = "" diff --git a/nw/gui/additions/pageddialog.py b/nw/gui/additions/pageddialog.py index 6e5c2e23..2b65cbb9 100644 --- a/nw/gui/additions/pageddialog.py +++ b/nw/gui/additions/pageddialog.py @@ -69,10 +69,14 @@ class PagedDialog(QDialog): return def addTab(self, tabWidget, tabLabel): + """Forwards the adding of tabs to the QTabWidget. + """ self._tabBox.addTab(tabWidget, tabLabel) return def addControls(self, buttonBar): + """Adds a button bar to the dialog. + """ self._buttonBox.addWidget(buttonBar) return @@ -85,12 +89,16 @@ class VerticalTabBar(QTabBar): return def tabSizeHint(self, theIndex): + """Returns a transposed size hint for the rotated bar. + """ tSize = QTabBar.tabSizeHint(self, theIndex) tSize.transpose() return tSize def paintEvent(self, theEvent): - + """Custom implementation of the label painter that rotates the + label 90 degrees. + """ pObj = QStylePainter(self) oObj = QStyleOptionTab() diff --git a/nw/gui/additions/qconfiglayout.py b/nw/gui/additions/qconfiglayout.py index 032d0e6b..f4eb1ca7 100644 --- a/nw/gui/additions/qconfiglayout.py +++ b/nw/gui/additions/qconfiglayout.py @@ -69,11 +69,15 @@ class QConfigLayout(QGridLayout): return def setHelpText(self, intRow, theText): + """Set the text for the help label. + """ if intRow in self._itemMap: self._itemMap[intRow]["help"].setText(theText) return def setLabelText(self, intRow, theText): + """Set the text for the main label. + """ if intRow in self._itemMap: self._itemMap[intRow]["label"].setText(theText) return @@ -85,7 +89,6 @@ class QConfigLayout(QGridLayout): def addGroupLabel(self, theLabel): """Adds a text label to separate groups of settings. """ - if isinstance(theLabel, QLabel): qLabel = theLabel elif isinstance(theLabel, str): @@ -107,7 +110,6 @@ class QConfigLayout(QGridLayout): def addRow(self, theLabel, theWidget, helpText=None, theUnit=None): """Add a label and a widget as a new row of the grid. """ - thisEntry = { "label" : None, "help" : None, diff --git a/nw/gui/additions/qswitch.py b/nw/gui/additions/qswitch.py index 39fc5b8a..7eb32830 100644 --- a/nw/gui/additions/qswitch.py +++ b/nw/gui/additions/qswitch.py @@ -97,7 +97,6 @@ class QSwitch(QAbstractButton): def paintEvent(self, event): """Drawing the switch itself. """ - qPaint = QPainter(self) qPaint.setRenderHint(QPainter.Antialiasing, True) qPaint.setPen(Qt.NoPen) diff --git a/nw/gui/elements/docdetails.py b/nw/gui/elements/docdetails.py index 12eb6030..6a12f0b3 100644 --- a/nw/gui/elements/docdetails.py +++ b/nw/gui/elements/docdetails.py @@ -143,31 +143,33 @@ class GuiDocDetails(QFrame): self.pCountData.setAlignment(Qt.AlignRight) # Assemble - self.mainBox.addWidget(self.labelName, 0, 0, 1, 1) - self.mainBox.addWidget(self.labelFlag, 0, 1, 1, 1) - self.mainBox.addWidget(self.labelData, 0, 2, 1, 3) + self.mainBox.addWidget(self.labelName, 0, 0, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.labelFlag, 0, 1, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.labelData, 0, 2, 1, 3, Qt.AlignTop) - self.mainBox.addWidget(self.statusName, 1, 0, 1, 1) - self.mainBox.addWidget(self.statusFlag, 1, 1, 1, 1) - self.mainBox.addWidget(self.statusData, 1, 2, 1, 1) - self.mainBox.addWidget(self.cCountName, 1, 3, 1, 1) - self.mainBox.addWidget(self.cCountData, 1, 4, 1, 1) + self.mainBox.addWidget(self.statusName, 1, 0, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.statusFlag, 1, 1, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.statusData, 1, 2, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.cCountName, 1, 3, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.cCountData, 1, 4, 1, 1, Qt.AlignTop) - self.mainBox.addWidget(self.className, 2, 0, 1, 1) - self.mainBox.addWidget(self.classFlag, 2, 1, 1, 1) - self.mainBox.addWidget(self.classData, 2, 2, 1, 1) - self.mainBox.addWidget(self.wCountName, 2, 3, 1, 1) - self.mainBox.addWidget(self.wCountData, 2, 4, 1, 1) + self.mainBox.addWidget(self.className, 2, 0, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.classFlag, 2, 1, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.classData, 2, 2, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.wCountName, 2, 3, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.wCountData, 2, 4, 1, 1, Qt.AlignTop) - self.mainBox.addWidget(self.layoutName, 3, 0, 1, 1) - self.mainBox.addWidget(self.layoutFlag, 3, 1, 1, 1) - self.mainBox.addWidget(self.layoutData, 3, 2, 1, 1) - self.mainBox.addWidget(self.pCountName, 3, 3, 1, 1) - self.mainBox.addWidget(self.pCountData, 3, 4, 1, 1) + self.mainBox.addWidget(self.layoutName, 3, 0, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.layoutFlag, 3, 1, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.layoutData, 3, 2, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.pCountName, 3, 3, 1, 1, Qt.AlignTop) + self.mainBox.addWidget(self.pCountData, 3, 4, 1, 1, Qt.AlignTop) self.mainBox.setColumnStretch(0,0) self.mainBox.setColumnStretch(1,0) self.mainBox.setColumnStretch(2,1) + self.mainBox.setColumnStretch(3,0) + self.mainBox.setColumnStretch(4,0) logger.debug("DocDetails initialisation complete") @@ -180,7 +182,6 @@ class GuiDocDetails(QFrame): def updateViewBox(self, tHandle): """Populate the details box from a given handle. """ - nwItem = self.theProject.projTree[tHandle] if nwItem is None: diff --git a/nw/gui/elements/doceditor.py b/nw/gui/elements/doceditor.py index 5e5e6228..906d4c65 100644 --- a/nw/gui/elements/doceditor.py +++ b/nw/gui/elements/doceditor.py @@ -141,7 +141,6 @@ class GuiDocEditor(QTextEdit): """Clear the current document and reset all document related flags and counters. """ - self.nwDocument.clearDocument() self.setReadOnly(True) self.clear() @@ -167,7 +166,6 @@ class GuiDocEditor(QTextEdit): settings. This function is both called when the editor is created, and when the user changes the main editor preferences. """ - # Some Constants self.nonWord = "\"'" self.nonWord += "".join(self.mainConf.fmtDoubleQuotes) @@ -241,7 +239,6 @@ class GuiDocEditor(QTextEdit): document is new (empty string), we set up the editor for editing the file. """ - theDoc = self.nwDocument.openDocument(tHandle, showStatus=showStatus) if theDoc is None: # There was an io error @@ -319,7 +316,6 @@ class GuiDocEditor(QTextEdit): Config.textFixedW is enabled or we're in Zen mode. Otherwise, just ensure the margins are set correctly. """ - if self.mainConf.textFixedW or self.theParent.isZenMode: vBar = self.verticalScrollBar() if vBar.isVisible(): @@ -376,6 +372,10 @@ class GuiDocEditor(QTextEdit): ## def setDocumentChanged(self, bValue): + """Keeps track of the document changed variable, and ensures + that the corresponding icon on the status bar shows the same + status. + """ self.docChanged = bValue self.theParent.statusBar.setDocumentStatus(self.docChanged) return self.docChanged @@ -437,7 +437,6 @@ class GuiDocEditor(QTextEdit): If the spell check mode (theMode) is not defined (None), then toggle the current status saved in this class. """ - if theMode is None: theMode = not self.spellCheck @@ -460,7 +459,6 @@ class GuiDocEditor(QTextEdit): currently loaded text. The fastest way to do this, at least as of Qt 5.13, is to clear the text and put it back. """ - logger.verbose("Running spell checker") if self.spellCheck: bfTime = time() @@ -481,6 +479,12 @@ class GuiDocEditor(QTextEdit): ## def docAction(self, theAction): + """Perform an action on the current document based on an action + flag. This is just a single entry point wrapper function to + ensure all the feature functions get the correct information + passed to it without having to consider the internal logic of + this class when calling these actions from other classes. + """ logger.verbose("Requesting action: %s" % theAction.name) if not self.theParent.hasProject: logger.error("No project open") @@ -537,9 +541,14 @@ class GuiDocEditor(QTextEdit): return True def isEmpty(self): + """Wrapper function to check if the current document is empty. + """ return self.qDocument.isEmpty() def revealLocation(self): + """Tell the user where on the file system the file in the editor + is saved. + """ if self.theHandle is not None: msgBox = QMessageBox() msgBox.information(self, "File Location", ( @@ -568,7 +577,6 @@ class GuiDocEditor(QTextEdit): However, we don't want to spend a lot of time in this function as it is triggered on every keypress when typing. """ - self.hasSelection = self.textCursor().hasSelection() if keyEvent.modifiers() == Qt.ShiftModifier: @@ -628,7 +636,6 @@ class GuiDocEditor(QTextEdit): """Triggered by right click to open the context menu. Also triggered by the Ctrl+. shortcut. """ - if not self.spellCheck: return @@ -667,7 +674,11 @@ class GuiDocEditor(QTextEdit): return + @pyqtSlot("QTextCursor", str) def _correctWord(self, theCursor, theWord): + """Slot for the spell check context menu triggering the + replacement of a word with the word from the dictionary. + """ xPos = theCursor.selectionStart() theCursor.beginEditBlock() theCursor.removeSelectedText() @@ -677,7 +688,11 @@ class GuiDocEditor(QTextEdit): self.setTextCursor(theCursor) return + @pyqtSlot("QTextCursor") def _addWord(self, theCursor): + """Slot for the spell check context menu triggered when the user + wants to add a word to the project dictionary. + """ theWord = theCursor.selectedText().strip().strip(self.nonWord) logger.debug("Added '%s' to project dictionary" % theWord) self.theDict.addWord(theWord) @@ -729,7 +744,6 @@ class GuiDocEditor(QTextEdit): tag and can tell the document viewer to try and find and load the file where the tag is defined. """ - if theCursor is None: theCursor = self.textCursor() @@ -772,13 +786,15 @@ class GuiDocEditor(QTextEdit): return def _openSpellContext(self): + """Opens the spell check context menu at the current point of + the cursor. + """ self._openContextMenu(self.cursorRect().center()) return def _docAutoReplace(self, theBlock): """Autoreplace text elements based on main configuration. """ - if not theBlock.isValid(): return @@ -870,7 +886,6 @@ class GuiDocEditor(QTextEdit): def _formatBlock(self, docAction): """Changes the block format of the block under the cursor. """ - theCursor = self.textCursor() theBlock = theCursor.block() if not theBlock.isValid(): @@ -949,6 +964,8 @@ class GuiDocEditor(QTextEdit): return def _makeSelection(self, selMode): + """Wrapper function to select a word based on a selection mode. + """ theCursor = self.textCursor() theCursor.clearSelection() theCursor.select(selMode) @@ -1025,7 +1042,6 @@ class GuiDocEditor(QTextEdit): """Create the spell checking object based on the spellTool setting in config. """ - if self.mainConf.spellTool == "enchant": from nw.core.spellcheck import NWSpellEnchant self.theDict = NWSpellEnchant() diff --git a/nw/gui/elements/doctitlebar.py b/nw/gui/elements/doctitlebar.py index 9e05f479..38425961 100644 --- a/nw/gui/elements/doctitlebar.py +++ b/nw/gui/elements/doctitlebar.py @@ -79,7 +79,6 @@ class GuiDocTitleBar(QLabel): """Sets the document title from the handle, or alternatively, set the whole document path. """ - self.setText("") self.theHandle = tHandle if tHandle is None: diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py index a91ee10e..2c81cf91 100644 --- a/nw/gui/elements/doctree.py +++ b/nw/gui/elements/doctree.py @@ -105,13 +105,19 @@ class GuiDocTree(QTreeWidget): ## def clearTree(self): + """Clear the GUI content and the related maps. + """ self.clear() self.theMap = {} self.orphRoot = None return def newTreeItem(self, itemType, itemClass): - + """Add new item to the tree, with a given itemType and + itemClass, and attach it to the selected handle. Also make sure + the item is added in a place it can be added, and that other + meta data is set correctly to ensure a valid project tree. + """ pHandle = self.getSelectedHandle() if not self.theParent.hasProject: @@ -274,7 +280,6 @@ class GuiDocTree(QTreeWidget): function only asks for confirmation once, and calls the regular deleteItem function for each document in the Trash folder. """ - trashHandle = self.theProject.projTree.trashRoot() logger.debug("Emptying Trash folder") @@ -315,7 +320,6 @@ class GuiDocTree(QTreeWidget): that to save memory. Items not in the tree are not saved to the project file, so a loaded project will be clean anyway. """ - if tHandle is None: tHandle = self.getSelectedHandle() @@ -446,6 +450,13 @@ class GuiDocTree(QTreeWidget): return def propagateCount(self, tHandle, theCount, nDepth=0): + """Recursive function setting the word count for a given item, + and propagating that count upwards in the tree until reaching a + root item. This function is more efficient than recalculating + everything each time the word count is updated, but is also + prone to diverging from the true values if the counts are not + properly reported to the function. + """ tItem = self._getTreeItem(tHandle) if tItem is not None: tItem.setText(self.C_COUNT,str(theCount)) @@ -460,6 +471,12 @@ class GuiDocTree(QTreeWidget): return def projectWordCount(self): + """Sum up the word counts for all root items and set the + relevant values in the project and on the status bar. This call + is a fast way of getting this number, and depends on the + propagateCount function being called when it should to maintain + the correct count. + """ nWords = 0 for n in range(self.topLevelItemCount()): tItem = self.topLevelItem(n) @@ -472,6 +489,11 @@ class GuiDocTree(QTreeWidget): return def buildTree(self): + """Build the entire project tree from scratch. This depends on + the save project item iterator in the project class which will + always make sure items with a parent have had their parent item + sent first. + """ self.clear() for nwItem in self.theProject.getProjectItems(): self._addTreeItem(nwItem) @@ -517,11 +539,16 @@ class GuiDocTree(QTreeWidget): ## def _getTreeItem(self, tHandle): + """Returns the QTreeWidgetItem of a given item handle. + """ if tHandle in self.theMap.keys(): return self.theMap[tHandle] return None def _scanChildren(self, theList, theItem, theIndex): + """This is a recursive function returning all items in a tree + starting at a given QTreeWidgetItem. + """ tHandle = theItem.text(self.C_HANDLE) nwItem = self.theProject.projTree[tHandle] nwItem.setExpanded(theItem.isExpanded()) @@ -532,7 +559,9 @@ class GuiDocTree(QTreeWidget): return theList def _addTreeItem(self, nwItem): - + """Create a QTreeWidgetItem from an NWItem and add it to the + project tree. + """ tHandle = nwItem.itemHandle pHandle = nwItem.parHandle tClass = nwItem.itemClass @@ -591,6 +620,9 @@ class GuiDocTree(QTreeWidget): return trItem def _addOrphanedRoot(self): + """Add the special Orphaned Files root item to hold non-root + items with no parent set. + """ if self.orphRoot is None: newItem = QTreeWidgetItem([""]*4) newItem.setText(self.C_NAME, "Orphaned Files") @@ -604,6 +636,8 @@ class GuiDocTree(QTreeWidget): return def _cleanOrphanedRoot(self): + """Remove the special Orphaned Files root folder if it is empty. + """ if self.orphRoot is not None: if self.orphRoot.childCount() == 0: self.takeTopLevelItem(self.indexOfTopLevelItem(self.orphRoot)) @@ -615,7 +649,6 @@ class GuiDocTree(QTreeWidget): in the project is consistent with the treeView. Also move the word count over to the new parent tree. """ - trItemS = self._getTreeItem(tHandle) nwItemS = self.theProject.projTree[tHandle] trItemP = trItemS.parent() @@ -636,6 +669,10 @@ class GuiDocTree(QTreeWidget): return True def _moveOrphanedItem(self, tHandle, dHandle): + """Move an Orphaned Item to a new dHandle parent item. This + function will set all the missing meta data based on the meta + data of the destination item. + """ trItemS = self._getTreeItem(tHandle) nwItemS = self.theProject.projTree[tHandle] nwItemD = self.theProject.projTree[dHandle] diff --git a/nw/gui/elements/docviewer.py b/nw/gui/elements/docviewer.py index c99b1de1..7fdf22e4 100644 --- a/nw/gui/elements/docviewer.py +++ b/nw/gui/elements/docviewer.py @@ -88,7 +88,6 @@ class GuiDocViewer(QTextBrowser): def initViewer(self): """Set editor settings from main config. """ - self._makeStyleSheet() # Set Font @@ -122,7 +121,6 @@ class GuiDocViewer(QTextBrowser): def loadText(self, tHandle): """Load text into the viewer from an item handle. """ - tItem = self.theProject.projTree[tHandle] if tItem is None: logger.warning("Item not found") @@ -153,11 +151,16 @@ class GuiDocViewer(QTextBrowser): return True def reloadText(self): + """Reload the text in the current document. + """ self.loadText(self.theHandle) return def loadFromTag(self, theTag): - + """Load text in the document from a reference given by a meta + tag rather than a known handle. This function depends on the + index being up to date. + """ logger.debug("Loading document from tag '%s'" % theTag) if theTag in self.theParent.theIndex.tagIndex.keys(): @@ -175,6 +178,9 @@ class GuiDocViewer(QTextBrowser): return True def docAction(self, theAction): + """Wrapper function for various document actions on the current + document. + """ logger.verbose("Requesting action: %s" % theAction.name) if self.theHandle is None: logger.error("No document open") @@ -225,6 +231,9 @@ class GuiDocViewer(QTextBrowser): ## def _makeSelection(self, selMode): + """Wrapper function for making a selection based on a specific + selection mode. + """ theCursor = self.textCursor() theCursor.clearSelection() theCursor.select(selMode) @@ -232,7 +241,8 @@ class GuiDocViewer(QTextBrowser): return def _linkClicked(self, theURL): - + """Slot for a link in the document being clicked. + """ theLink = theURL.url() tHandle = None onLine = 0 @@ -256,7 +266,9 @@ class GuiDocViewer(QTextBrowser): return def _makeStyleSheet(self): - + """Generate an appropriate style sheet for the document viewer, + based on the current syntax highlighter theme, + """ styleSheet = ( "body {{" " color: rgb({tColR},{tColG},{tColB});" diff --git a/nw/gui/elements/noticebar.py b/nw/gui/elements/noticebar.py index b5580984..4b6d57aa 100644 --- a/nw/gui/elements/noticebar.py +++ b/nw/gui/elements/noticebar.py @@ -67,11 +67,15 @@ class GuiNoticeBar(QFrame): return def showNote(self, theNote): + """Show the note on the noticebar. + """ self.noteLabel.setText("Note: %s" % theNote) self.setVisible(True) return def hideNote(self): + """Clear the noticebar and hide it. + """ self.noteLabel.setText("") self.setVisible(False) return diff --git a/nw/gui/elements/outline.py b/nw/gui/elements/outline.py index 408dc126..f09a38b6 100644 --- a/nw/gui/elements/outline.py +++ b/nw/gui/elements/outline.py @@ -125,7 +125,6 @@ class GuiProjectOutline(QTreeWidget): """Clear the tree and header and set the default values for the columns arrays. """ - self.clear() self.setColumnCount(1) self.setHeaderLabel(nwLabels.OUTLINE_COLS[nwOutline.TITLE]) @@ -150,7 +149,6 @@ class GuiProjectOutline(QTreeWidget): what data to load, and if necessary, force a rebuild of the tree. """ - # If it's the first time, we always build if self.firstView or self.firstView and overRide: self._loadHeaderState() @@ -229,7 +227,6 @@ class GuiProjectOutline(QTreeWidget): """Load the state of the main tree header, that is, column order and column width. """ - # Load whatever we saved last time, regardless of wether it # contains the correct names or number of columns. The names # must be valid though. @@ -280,7 +277,6 @@ class GuiProjectOutline(QTreeWidget): save the current width of hidden columns though. This preserves the last known width in case they're unhidden again. """ - # If we haven't built the tree, there is nothing to save. if self.lastBuild == 0: return diff --git a/nw/gui/elements/searchbar.py b/nw/gui/elements/searchbar.py index 431bddad..590d8d0a 100644 --- a/nw/gui/elements/searchbar.py +++ b/nw/gui/elements/searchbar.py @@ -98,6 +98,9 @@ class GuiSearchBar(QFrame): ## def setSearchText(self, theText): + """Open the search bar and set the search text to the text + provided, if any. + """ if not self.isVisible(): self.setVisible(True) self.searchBox.setText(theText) @@ -106,15 +109,21 @@ class GuiSearchBar(QFrame): return True def setReplaceText(self, theText): + """Set the replace text. + """ self._replaceVisible(True) self.replaceBox.setFocus() self.replaceBox.setText(theText) return True def getSearchText(self): + """Return the current search text. + """ return self.searchBox.text() def getReplaceText(self): + """Return the current replace text. + """ return self.replaceBox.text() ## @@ -122,11 +131,15 @@ class GuiSearchBar(QFrame): ## def _doClose(self): + """Hide the search/replace bar. + """ self._replaceVisible(False) self.setVisible(False) return def _doSearch(self): + """Call the search action function for the document editor. + """ modKey = qApp.keyboardModifiers() if modKey == Qt.ShiftModifier: self.theParent.docEditor.docAction(nwDocAction.GO_PREV) @@ -135,10 +148,14 @@ class GuiSearchBar(QFrame): return def _doReplace(self): + """Call the replace action function for the document editor. + """ self.theParent.docEditor.docAction(nwDocAction.REPL_NEXT) return def _replaceVisible(self, isVisible): + """Set the visibility of all the replace widgets. + """ self.replaceLabel.setVisible(isVisible) self.replaceBox.setVisible(isVisible) self.replaceButton.setVisible(isVisible) diff --git a/nw/gui/elements/viewdetails.py b/nw/gui/elements/viewdetails.py index cb0d6af3..589ffee5 100644 --- a/nw/gui/elements/viewdetails.py +++ b/nw/gui/elements/viewdetails.py @@ -94,7 +94,9 @@ class GuiDocViewDetails(QWidget): return def refreshReferences(self, tHandle): - + """Update the current list of document references from the + project index. + """ self.currHandle = tHandle if self.isSticky.isChecked(): @@ -117,12 +119,17 @@ class GuiDocViewDetails(QWidget): ## def _linkClicked(self, theLink): + """Capture the link-click and forward it to the document viewer + class for handling. + """ if len(theLink) == 18: tHandle = theLink[-13:] self.theParent.viewDocument(tHandle) return def _doShowHide(self, chState): + """Toggle the expand/collapse of the panel. + """ self.scrollBox.setVisible(chState) self.mainConf.setShowRefPanel(chState) if chState: @@ -132,6 +139,8 @@ class GuiDocViewDetails(QWidget): return def _doSticky(self, chState): + """Toggle the sticky feature of the references. + """ if not chState and self.currHandle is not None: self.refreshReferences(self.currHandle) return diff --git a/nw/gui/tools/dochighlight.py b/nw/gui/tools/dochighlight.py index 9d57b612..cda6ac03 100644 --- a/nw/gui/tools/dochighlight.py +++ b/nw/gui/tools/dochighlight.py @@ -75,6 +75,9 @@ class GuiDocHighlighter(QSyntaxHighlighter): return def initHighlighter(self): + """Initialise the syntax highlighter, setting all the colour + rules and building the regexes. + """ logger.debug("Setting up highlighting rules") @@ -205,14 +208,21 @@ class GuiDocHighlighter(QSyntaxHighlighter): ## def setDict(self, theDict): + """Set the dictionary object for spell check underlines lookup. + """ self.theDict = theDict return True def setSpellCheck(self, theMode): + """Enable/disable the real time spell checker. + """ self.spellCheck = theMode return True def setHandle(self, theHandle): + """Set the handle of the currently highlighted document. This is + needed for the index lookup for validating tags and references. + """ self.theHandle = theHandle return True @@ -226,7 +236,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): is significantly faster than running the regex checks we use for text paragraphs. """ - if self.theHandle is None or not theText: return @@ -317,7 +326,6 @@ class GuiDocHighlighter(QSyntaxHighlighter): """Generate a valid character format to be applied to the text that is to be highlighted. """ - theFormat = QTextCharFormat() if fmtCol is not None: diff --git a/nw/gui/tools/optionstate.py b/nw/gui/tools/optionstate.py index ed45e7cf..5f265622 100644 --- a/nw/gui/tools/optionstate.py +++ b/nw/gui/tools/optionstate.py @@ -51,7 +51,6 @@ class OptionState(): def loadSettings(self): """Load the options dictionary from the project settings file. """ - if self.theProject.projMeta is None: return False @@ -76,7 +75,6 @@ class OptionState(): def saveSettings(self): """Save the options dictionary to the project settings file. """ - if self.theProject.projMeta is None: return False diff --git a/nw/gui/tools/wordcounter.py b/nw/gui/tools/wordcounter.py index 24a0b9ca..49255040 100644 --- a/nw/gui/tools/wordcounter.py +++ b/nw/gui/tools/wordcounter.py @@ -45,7 +45,9 @@ class WordCounter(QThread): return def run(self): - + """Overloaded run function for the word counter, forwarding the + call to the function that does the actual counting. + """ theText = self.theParent.getText() cC, wC, pC = countWords(theText) diff --git a/nw/guimain.py b/nw/guimain.py index 558712ed..b15edbe9 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -57,7 +57,7 @@ class GuiMain(QMainWindow): logger.info("Starting %s" % nw.__package__) logger.debug("Initialising GUI ...") - self.mainConf = nw.CONFIG + self.mainConf = nw.CONFIG # Some runtime info useful for debugging logger.info("OS: %s" % self.mainConf.osType) From 52269faaddff9c503d2de2fe2a7f4f286330bb7e Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 24 May 2020 18:26:06 +0200 Subject: [PATCH 4/6] Drop the double folder for the sample project --- sample/{sampleNovel => }/data_1/4298de4d9524_main.nwd | 0 sample/{sampleNovel => }/data_5/3b69b83cdafc_main.nwd | 0 sample/{sampleNovel => }/data_5/eaea4e8cdee8_main.nwd | 0 sample/{sampleNovel => }/data_6/36b6aa9b697b_main.nwd | 0 sample/{sampleNovel => }/data_6/a2d6d5f4f401_main.nwd | 0 sample/{sampleNovel => }/data_8/8706ddc78b1b_main.nwd | 0 sample/{sampleNovel => }/data_9/6b68994dfa3d_main.nwd | 0 sample/{sampleNovel => }/data_a/e7339df26ded_main.nwd | 0 sample/{sampleNovel => }/data_b/3e74dbc1f584_main.nwd | 0 sample/{sampleNovel => }/data_b/8136a5a774a0_main.nwd | 0 sample/{sampleNovel => }/data_b/a8a28a246524_main.nwd | 0 sample/{sampleNovel => }/data_b/b2c23b3c42cc_main.nwd | 0 sample/{sampleNovel => }/data_b/c0cbd2a407f3_main.nwd | 0 sample/{sampleNovel => }/data_e/dca4be2fcaf8_main.nwd | 0 sample/{sampleNovel => }/data_f/1471bef9f2ae_main.nwd | 0 sample/{sampleNovel => }/nwProject.nwx | 4 ++-- 16 files changed, 2 insertions(+), 2 deletions(-) rename sample/{sampleNovel => }/data_1/4298de4d9524_main.nwd (100%) rename sample/{sampleNovel => }/data_5/3b69b83cdafc_main.nwd (100%) rename sample/{sampleNovel => }/data_5/eaea4e8cdee8_main.nwd (100%) rename sample/{sampleNovel => }/data_6/36b6aa9b697b_main.nwd (100%) rename sample/{sampleNovel => }/data_6/a2d6d5f4f401_main.nwd (100%) rename sample/{sampleNovel => }/data_8/8706ddc78b1b_main.nwd (100%) rename sample/{sampleNovel => }/data_9/6b68994dfa3d_main.nwd (100%) rename sample/{sampleNovel => }/data_a/e7339df26ded_main.nwd (100%) rename sample/{sampleNovel => }/data_b/3e74dbc1f584_main.nwd (100%) rename sample/{sampleNovel => }/data_b/8136a5a774a0_main.nwd (100%) rename sample/{sampleNovel => }/data_b/a8a28a246524_main.nwd (100%) rename sample/{sampleNovel => }/data_b/b2c23b3c42cc_main.nwd (100%) rename sample/{sampleNovel => }/data_b/c0cbd2a407f3_main.nwd (100%) rename sample/{sampleNovel => }/data_e/dca4be2fcaf8_main.nwd (100%) rename sample/{sampleNovel => }/data_f/1471bef9f2ae_main.nwd (100%) rename sample/{sampleNovel => }/nwProject.nwx (98%) diff --git a/sample/sampleNovel/data_1/4298de4d9524_main.nwd b/sample/data_1/4298de4d9524_main.nwd similarity index 100% rename from sample/sampleNovel/data_1/4298de4d9524_main.nwd rename to sample/data_1/4298de4d9524_main.nwd diff --git a/sample/sampleNovel/data_5/3b69b83cdafc_main.nwd b/sample/data_5/3b69b83cdafc_main.nwd similarity index 100% rename from sample/sampleNovel/data_5/3b69b83cdafc_main.nwd rename to sample/data_5/3b69b83cdafc_main.nwd diff --git a/sample/sampleNovel/data_5/eaea4e8cdee8_main.nwd b/sample/data_5/eaea4e8cdee8_main.nwd similarity index 100% rename from sample/sampleNovel/data_5/eaea4e8cdee8_main.nwd rename to sample/data_5/eaea4e8cdee8_main.nwd diff --git a/sample/sampleNovel/data_6/36b6aa9b697b_main.nwd b/sample/data_6/36b6aa9b697b_main.nwd similarity index 100% rename from sample/sampleNovel/data_6/36b6aa9b697b_main.nwd rename to sample/data_6/36b6aa9b697b_main.nwd diff --git a/sample/sampleNovel/data_6/a2d6d5f4f401_main.nwd b/sample/data_6/a2d6d5f4f401_main.nwd similarity index 100% rename from sample/sampleNovel/data_6/a2d6d5f4f401_main.nwd rename to sample/data_6/a2d6d5f4f401_main.nwd diff --git a/sample/sampleNovel/data_8/8706ddc78b1b_main.nwd b/sample/data_8/8706ddc78b1b_main.nwd similarity index 100% rename from sample/sampleNovel/data_8/8706ddc78b1b_main.nwd rename to sample/data_8/8706ddc78b1b_main.nwd diff --git a/sample/sampleNovel/data_9/6b68994dfa3d_main.nwd b/sample/data_9/6b68994dfa3d_main.nwd similarity index 100% rename from sample/sampleNovel/data_9/6b68994dfa3d_main.nwd rename to sample/data_9/6b68994dfa3d_main.nwd diff --git a/sample/sampleNovel/data_a/e7339df26ded_main.nwd b/sample/data_a/e7339df26ded_main.nwd similarity index 100% rename from sample/sampleNovel/data_a/e7339df26ded_main.nwd rename to sample/data_a/e7339df26ded_main.nwd diff --git a/sample/sampleNovel/data_b/3e74dbc1f584_main.nwd b/sample/data_b/3e74dbc1f584_main.nwd similarity index 100% rename from sample/sampleNovel/data_b/3e74dbc1f584_main.nwd rename to sample/data_b/3e74dbc1f584_main.nwd diff --git a/sample/sampleNovel/data_b/8136a5a774a0_main.nwd b/sample/data_b/8136a5a774a0_main.nwd similarity index 100% rename from sample/sampleNovel/data_b/8136a5a774a0_main.nwd rename to sample/data_b/8136a5a774a0_main.nwd diff --git a/sample/sampleNovel/data_b/a8a28a246524_main.nwd b/sample/data_b/a8a28a246524_main.nwd similarity index 100% rename from sample/sampleNovel/data_b/a8a28a246524_main.nwd rename to sample/data_b/a8a28a246524_main.nwd diff --git a/sample/sampleNovel/data_b/b2c23b3c42cc_main.nwd b/sample/data_b/b2c23b3c42cc_main.nwd similarity index 100% rename from sample/sampleNovel/data_b/b2c23b3c42cc_main.nwd rename to sample/data_b/b2c23b3c42cc_main.nwd diff --git a/sample/sampleNovel/data_b/c0cbd2a407f3_main.nwd b/sample/data_b/c0cbd2a407f3_main.nwd similarity index 100% rename from sample/sampleNovel/data_b/c0cbd2a407f3_main.nwd rename to sample/data_b/c0cbd2a407f3_main.nwd diff --git a/sample/sampleNovel/data_e/dca4be2fcaf8_main.nwd b/sample/data_e/dca4be2fcaf8_main.nwd similarity index 100% rename from sample/sampleNovel/data_e/dca4be2fcaf8_main.nwd rename to sample/data_e/dca4be2fcaf8_main.nwd diff --git a/sample/sampleNovel/data_f/1471bef9f2ae_main.nwd b/sample/data_f/1471bef9f2ae_main.nwd similarity index 100% rename from sample/sampleNovel/data_f/1471bef9f2ae_main.nwd rename to sample/data_f/1471bef9f2ae_main.nwd diff --git a/sample/sampleNovel/nwProject.nwx b/sample/nwProject.nwx similarity index 98% rename from sample/sampleNovel/nwProject.nwx rename to sample/nwProject.nwx index b4b7b117..45b7503b 100644 --- a/sample/sampleNovel/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -109,7 +109,7 @@ 1199 216 7 - 0 + 527 Another Scene From fb6e3f692eb4edb5532af0a684eea4f6d2dc2d36 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 24 May 2020 18:33:58 +0200 Subject: [PATCH 5/6] Fixed the formatting of novel page layout --- nw/core/tokenizer.py | 14 +++++++++++ sample/data_9/74e400180a99_main.nwd | 4 +++ sample/nwProject.nwx | 39 +++++++++++++++++++---------- 3 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 sample/data_9/74e400180a99_main.nwd diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index c94f418f..efa2e906 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -540,6 +540,20 @@ class Tokenizer(): tToken[0], tToken[1], tToken[2], tToken[3] | self.A_PBA ) + if self.isPage: + for n, tToken in enumerate(self.theTokens): + tType = tToken[0] + tText = tToken[1] + tFormat = tToken[2] + if n == 0: + self.theTokens[n] = ( + tType, tText, tFormat, self.A_LEFT | self.A_PBB + ) + else: + self.theTokens[n] = ( + tType, tText, tFormat, self.A_LEFT + ) + return ## diff --git a/sample/data_9/74e400180a99_main.nwd b/sample/data_9/74e400180a99_main.nwd new file mode 100644 index 00000000..303d5232 --- /dev/null +++ b/sample/data_9/74e400180a99_main.nwd @@ -0,0 +1,4 @@ +%%~ 974e400180a99:7031beac91f75:Page +This is a plain page with some text on it. + +This file should receive no special formatting, but the text will always be left aligned and the content will always start on a fresh page when the project is exported. diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 45b7503b..ad832035 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -11,8 +11,8 @@ True True 636b6aa9b697b - 6a2d6d5f4f401 - 875 + ba8a28a246524 + 914 B E @@ -44,7 +44,7 @@ Main - + Novel ROOT @@ -65,20 +65,33 @@ 2 78 - - Part 1 + + Page + FILE + NOVEL + New + False + True + PAGE + 208 + 40 + 2 + 213 + + + Part One FILE NOVEL New False True PARTITION - 0 - 0 - 0 + 23 + 5 + 1 0 - + A Folder FOLDER NOVEL @@ -277,9 +290,9 @@ False True SCENE - 30 - 6 - 1 + 0 + 0 + 0 36 From 0dbcaacaeba520394e205f5fd604e45022638f31 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 24 May 2020 18:37:32 +0200 Subject: [PATCH 6/6] Added comment --- nw/core/tokenizer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index efa2e906..2fa784f5 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -540,6 +540,8 @@ class Tokenizer(): tToken[0], tToken[1], tToken[2], tToken[3] | self.A_PBA ) + # A single page is always left-aligned and starts on a fresh + # page, unless it's empty. if self.isPage: for n, tToken in enumerate(self.theTokens): tType = tToken[0]