Merge branch 'main' into release_1.0rc1
This commit is contained in:
@@ -49,8 +49,8 @@ and the file extension ``.nwd``.
|
|||||||
|
|
||||||
If you wish to find the physical location of a file in the project, you can either look it up in the
|
If you wish to find the physical location of a file in the project, you can either look it up in the
|
||||||
project XML file, select :guilabel:`Show File Details` from the :guilabel:`Document` menu when
|
project XML file, select :guilabel:`Show File Details` from the :guilabel:`Document` menu when
|
||||||
having the document open, or look in one of the ``ToC`` files in the root of the project folder.
|
having the document open, or look in the ``ToC.txt`` file in the root of the project folder.
|
||||||
The ``ToC`` files have a list of all document files in the project and where they are saved.
|
The ``ToC.txt`` file has a list of all document files in the project and where they are saved.
|
||||||
|
|
||||||
The reason for this cryptic file naming is to avoid issues with file naming conventions and
|
The reason for this cryptic file naming is to avoid issues with file naming conventions and
|
||||||
restrictions on different operating systems, and also to have a file name that does not depend on
|
restrictions on different operating systems, and also to have a file name that does not depend on
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ class nwFiles():
|
|||||||
PROJ_DICT = "wordlist.txt"
|
PROJ_DICT = "wordlist.txt"
|
||||||
PROJ_LOCK = "nwProject.lock"
|
PROJ_LOCK = "nwProject.lock"
|
||||||
TOC_TXT = "ToC.txt"
|
TOC_TXT = "ToC.txt"
|
||||||
TOC_JSON = "ToC.json"
|
|
||||||
SESS_STATS = "sessionStats.log"
|
SESS_STATS = "sessionStats.log"
|
||||||
INDEX_FILE = "tagsIndex.json"
|
INDEX_FILE = "tagsIndex.json"
|
||||||
OPTS_FILE = "guiOptions.json"
|
OPTS_FILE = "guiOptions.json"
|
||||||
|
|||||||
+6
-1
@@ -743,7 +743,7 @@ class NWProject():
|
|||||||
"""Close the current project and clear all meta data.
|
"""Close the current project and clear all meta data.
|
||||||
"""
|
"""
|
||||||
self.optState.saveSettings()
|
self.optState.saveSettings()
|
||||||
self.projTree.writeToCFiles()
|
self.projTree.writeToCFile()
|
||||||
self._appendSessionStats()
|
self._appendSessionStats()
|
||||||
self._clearLockFile()
|
self._clearLockFile()
|
||||||
self.clearProject()
|
self.clearProject()
|
||||||
@@ -781,6 +781,10 @@ class NWProject():
|
|||||||
def zipIt(self, doNotify):
|
def zipIt(self, doNotify):
|
||||||
"""Create a zip file of the entire project.
|
"""Create a zip file of the entire project.
|
||||||
"""
|
"""
|
||||||
|
if not self.theParent.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
logger.info("Backing up project")
|
logger.info("Backing up project")
|
||||||
self.theParent.setStatus("Backing up project ...")
|
self.theParent.setStatus("Backing up project ...")
|
||||||
|
|
||||||
@@ -1476,6 +1480,7 @@ class NWProject():
|
|||||||
os.path.join(self.projMeta, "timelineOptions.json"),
|
os.path.join(self.projMeta, "timelineOptions.json"),
|
||||||
os.path.join(self.projMeta, "docMergeOptions.json"),
|
os.path.join(self.projMeta, "docMergeOptions.json"),
|
||||||
os.path.join(self.projMeta, "sessionLogOptions.json"),
|
os.path.join(self.projMeta, "sessionLogOptions.json"),
|
||||||
|
os.path.join(self.projPath, "ToC.json"),
|
||||||
]
|
]
|
||||||
|
|
||||||
for rmFile in rmList:
|
for rmFile in rmList:
|
||||||
|
|||||||
+27
-32
@@ -26,7 +26,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
@@ -139,46 +138,42 @@ class NWTree():
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def writeToCFiles(self):
|
def writeToCFile(self):
|
||||||
"""Write the convenience table of contents files in the root of
|
"""Write the convenience table of contents file in the root of
|
||||||
the project directory. These files are there to assist the user
|
the project directory.
|
||||||
if they wish to browse the stored files.
|
|
||||||
"""
|
"""
|
||||||
tocText = os.path.join(self.theProject.projPath, nwFiles.TOC_TXT)
|
tocList = []
|
||||||
tocJson = os.path.join(self.theProject.projPath, nwFiles.TOC_JSON)
|
tocLen = 0
|
||||||
|
for tHandle in self._treeOrder:
|
||||||
|
tItem = self.__getitem__(tHandle)
|
||||||
|
if tItem is None:
|
||||||
|
continue
|
||||||
|
tFile = tHandle+".nwd"
|
||||||
|
if os.path.isfile(os.path.join(self.theProject.projContent, tFile)):
|
||||||
|
tocLine = "%-25s %-9s %-10s %s" % (
|
||||||
|
os.path.join("content", tFile),
|
||||||
|
tItem.itemClass.name,
|
||||||
|
tItem.itemLayout.name,
|
||||||
|
tItem.itemName,
|
||||||
|
)
|
||||||
|
tocList.append(tocLine)
|
||||||
|
tocLen = max(tocLen, len(tocLine))
|
||||||
|
|
||||||
jsonData = []
|
|
||||||
try:
|
try:
|
||||||
# Dump the text
|
# Dump the text
|
||||||
|
tocText = os.path.join(self.theProject.projPath, nwFiles.TOC_TXT)
|
||||||
with open(tocText, mode="w", encoding="utf8") as outFile:
|
with open(tocText, mode="w", encoding="utf8") as outFile:
|
||||||
outFile.write("\n")
|
outFile.write("\n")
|
||||||
outFile.write(" Table of Contents\n")
|
outFile.write("Table of Contents\n")
|
||||||
outFile.write("===================\n")
|
outFile.write("=================\n")
|
||||||
outFile.write("\n")
|
outFile.write("\n")
|
||||||
outFile.write(" %-25s %-9s %s\n" % ("File Name", "Class", "Document Label"))
|
outFile.write("%-25s %-9s %-10s %s\n" % (
|
||||||
outFile.write("-"*80+"\n")
|
"File Name", "Class", "Layout", "Document Label"
|
||||||
for tHandle in sorted(self._treeOrder):
|
))
|
||||||
tItem = self.__getitem__(tHandle)
|
outFile.write("-"*tocLen + "\n")
|
||||||
if tItem is None:
|
outFile.write("\n".join(tocList))
|
||||||
continue
|
|
||||||
tFile = tHandle+".nwd"
|
|
||||||
if os.path.isfile(os.path.join(self.theProject.projContent, tFile)):
|
|
||||||
outFile.write(" %-25s %-9s %s\n" % (
|
|
||||||
os.path.join("content", tFile),
|
|
||||||
tItem.itemClass.name,
|
|
||||||
tItem.itemName,
|
|
||||||
))
|
|
||||||
jsonData.append([
|
|
||||||
os.path.join("content", tFile),
|
|
||||||
tItem.itemClass.name,
|
|
||||||
tItem.itemName,
|
|
||||||
])
|
|
||||||
outFile.write("\n")
|
outFile.write("\n")
|
||||||
|
|
||||||
# Dump the JSON
|
|
||||||
with open(tocJson, mode="w+", encoding="utf8") as outFile:
|
|
||||||
json.dump(jsonData, outFile, indent=2)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
|
|
||||||
|
|||||||
+23
-12
@@ -620,9 +620,10 @@ class GuiDocEditor(QTextEdit):
|
|||||||
this class when calling these actions from other classes.
|
this class when calling these actions from other classes.
|
||||||
"""
|
"""
|
||||||
logger.verbose("Requesting action: %s" % theAction.name)
|
logger.verbose("Requesting action: %s" % theAction.name)
|
||||||
if not self.theParent.hasProject:
|
if self.theHandle is None:
|
||||||
logger.error("No project open")
|
logger.error("No document open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._allowAutoReplace(False)
|
self._allowAutoReplace(False)
|
||||||
if theAction == nwDocAction.UNDO:
|
if theAction == nwDocAction.UNDO:
|
||||||
self.undo()
|
self.undo()
|
||||||
@@ -678,7 +679,9 @@ class GuiDocEditor(QTextEdit):
|
|||||||
logger.debug("Unknown or unsupported document action %s" % str(theAction))
|
logger.debug("Unknown or unsupported document action %s" % str(theAction))
|
||||||
self._allowAutoReplace(True)
|
self._allowAutoReplace(True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._allowAutoReplace(True)
|
self._allowAutoReplace(True)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def isEmpty(self):
|
def isEmpty(self):
|
||||||
@@ -690,21 +693,29 @@ class GuiDocEditor(QTextEdit):
|
|||||||
"""Tell the user where on the file system the file in the editor
|
"""Tell the user where on the file system the file in the editor
|
||||||
is saved.
|
is saved.
|
||||||
"""
|
"""
|
||||||
if self.theHandle is not None:
|
if self.theHandle is None:
|
||||||
msgBox = QMessageBox()
|
logger.error("No document open")
|
||||||
msgBox.information(self, "File Location", (
|
return False
|
||||||
"File details for the currently open file<br>"
|
|
||||||
"Handle: {handle:s}<br>"
|
msgBox = QMessageBox()
|
||||||
"Location: {fileLoc:s}"
|
msgBox.information(self, "File Location", (
|
||||||
).format(
|
"File details for the currently open file<br>"
|
||||||
handle = self.theHandle,
|
"Handle: {handle:s}<br>"
|
||||||
fileLoc = str(self.nwDocument.getFileLocation())
|
"Location: {fileLoc:s}"
|
||||||
))
|
).format(
|
||||||
|
handle = self.theHandle,
|
||||||
|
fileLoc = str(self.nwDocument.getFileLocation())
|
||||||
|
))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def insertText(self, theInsert):
|
def insertText(self, theInsert):
|
||||||
"""Insert a specific type of text at the cursor position.
|
"""Insert a specific type of text at the cursor position.
|
||||||
"""
|
"""
|
||||||
|
if self.theHandle is None:
|
||||||
|
logger.error("No document open")
|
||||||
|
return False
|
||||||
|
|
||||||
if isinstance(theInsert, str):
|
if isinstance(theInsert, str):
|
||||||
theText = theInsert
|
theText = theInsert
|
||||||
elif isinstance(theInsert, nwDocInsert):
|
elif isinstance(theInsert, nwDocInsert):
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
nHandle = None
|
nHandle = None
|
||||||
|
|
||||||
if not self.theParent.hasProject:
|
if not self.theParent.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# The item needs to be assigned an item class, so one must be
|
# The item needs to be assigned an item class, so one must be
|
||||||
@@ -281,6 +282,10 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
"""Move an item up or down in the tree, but only if the treeView
|
"""Move an item up or down in the tree, but only if the treeView
|
||||||
has focus. This also applies when the menu is used.
|
has focus. This also applies when the menu is used.
|
||||||
"""
|
"""
|
||||||
|
if not self.theParent.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
hasFocus = qApp.focusWidget() == self or not self.mainConf.showGUI
|
hasFocus = qApp.focusWidget() == self or not self.mainConf.showGUI
|
||||||
if hasFocus and self.theParent.hasProject:
|
if hasFocus and self.theParent.hasProject:
|
||||||
|
|
||||||
@@ -364,6 +369,10 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
function only asks for confirmation once, and calls the regular
|
function only asks for confirmation once, and calls the regular
|
||||||
deleteItem function for each document in the Trash folder.
|
deleteItem function for each document in the Trash folder.
|
||||||
"""
|
"""
|
||||||
|
if not self.theParent.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
trashHandle = self.theProject.projTree.trashRoot()
|
trashHandle = self.theProject.projTree.trashRoot()
|
||||||
|
|
||||||
logger.debug("Emptying Trash folder")
|
logger.debug("Emptying Trash folder")
|
||||||
@@ -409,6 +418,10 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
delete the files on disk. Folders are deleted if they're empty only,
|
delete the files on disk. Folders are deleted if they're empty only,
|
||||||
and the deletion is always permanent.
|
and the deletion is always permanent.
|
||||||
"""
|
"""
|
||||||
|
if not self.theParent.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
tHandle = self.getSelectedHandle()
|
tHandle = self.getSelectedHandle()
|
||||||
|
|
||||||
|
|||||||
+93
-45
@@ -316,7 +316,8 @@ class GuiMain(QMainWindow):
|
|||||||
if not isYes:
|
if not isYes:
|
||||||
msgBox = QMessageBox()
|
msgBox = QMessageBox()
|
||||||
msgRes = msgBox.question(
|
msgRes = msgBox.question(
|
||||||
self, "Close Project", "Save changes and close the current project?"
|
self, "Close Project",
|
||||||
|
"Close the current project?<br>Changes are saved automatically."
|
||||||
)
|
)
|
||||||
if msgRes != QMessageBox.Yes:
|
if msgRes != QMessageBox.Yes:
|
||||||
return False
|
return False
|
||||||
@@ -442,6 +443,7 @@ class GuiMain(QMainWindow):
|
|||||||
"""Save the current project.
|
"""Save the current project.
|
||||||
"""
|
"""
|
||||||
if not self.hasProject:
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# If the project is new, it may not have a path, so we need one
|
# If the project is new, it may not have a path, so we need one
|
||||||
@@ -465,27 +467,33 @@ class GuiMain(QMainWindow):
|
|||||||
def closeDocument(self):
|
def closeDocument(self):
|
||||||
"""Close the document and clear the editor and title field.
|
"""Close the document and clear the editor and title field.
|
||||||
"""
|
"""
|
||||||
if self.hasProject:
|
if not self.hasProject:
|
||||||
self.docEditor.saveCursorPosition()
|
logger.error("No project open")
|
||||||
if self.docEditor.docChanged:
|
return False
|
||||||
self.saveDocument()
|
|
||||||
self.docEditor.clearEditor()
|
self.docEditor.saveCursorPosition()
|
||||||
|
if self.docEditor.docChanged:
|
||||||
|
self.saveDocument()
|
||||||
|
self.docEditor.clearEditor()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def openDocument(self, tHandle, tLine=None, changeFocus=True, doScroll=False):
|
def openDocument(self, tHandle, tLine=None, changeFocus=True, doScroll=False):
|
||||||
"""Open a specific document, optionally at a given line.
|
"""Open a specific document, optionally at a given line.
|
||||||
"""
|
"""
|
||||||
if self.hasProject:
|
if not self.hasProject:
|
||||||
self.closeDocument()
|
logger.error("No project open")
|
||||||
self.tabWidget.setCurrentWidget(self.splitDocs)
|
return False
|
||||||
if self.docEditor.loadText(tHandle, tLine):
|
|
||||||
if changeFocus:
|
self.closeDocument()
|
||||||
self.docEditor.setFocus()
|
self.tabWidget.setCurrentWidget(self.splitDocs)
|
||||||
self.theProject.setLastEdited(tHandle)
|
if self.docEditor.loadText(tHandle, tLine):
|
||||||
self.treeView.setSelectedHandle(tHandle, doScroll=doScroll)
|
if changeFocus:
|
||||||
else:
|
self.docEditor.setFocus()
|
||||||
return False
|
self.theProject.setLastEdited(tHandle)
|
||||||
|
self.treeView.setSelectedHandle(tHandle, doScroll=doScroll)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -493,43 +501,54 @@ class GuiMain(QMainWindow):
|
|||||||
"""Opens the next document in the project tree, following the
|
"""Opens the next document in the project tree, following the
|
||||||
document with the given handle. Stops when reaching the end.
|
document with the given handle. Stops when reaching the end.
|
||||||
"""
|
"""
|
||||||
if self.hasProject:
|
if not self.hasProject:
|
||||||
self.treeView.flushTreeOrder()
|
logger.error("No project open")
|
||||||
nHandle = None # The next handle after tHandle
|
return False
|
||||||
fHandle = None # The first file handle we encounter
|
|
||||||
foundIt = False # We've found tHandle, pick the next we see
|
|
||||||
for tItem in self.theProject.projTree:
|
|
||||||
if tItem is None:
|
|
||||||
continue
|
|
||||||
if tItem.itemType != nwItemType.FILE:
|
|
||||||
continue
|
|
||||||
if fHandle is None:
|
|
||||||
fHandle = tItem.itemHandle
|
|
||||||
if tItem.itemHandle == tHandle:
|
|
||||||
foundIt = True
|
|
||||||
elif foundIt:
|
|
||||||
nHandle = tItem.itemHandle
|
|
||||||
break
|
|
||||||
|
|
||||||
if nHandle is not None:
|
self.treeView.flushTreeOrder()
|
||||||
self.openDocument(nHandle, tLine=0, doScroll=True)
|
nHandle = None # The next handle after tHandle
|
||||||
return True
|
fHandle = None # The first file handle we encounter
|
||||||
elif wrapAround:
|
foundIt = False # We've found tHandle, pick the next we see
|
||||||
self.openDocument(fHandle, tLine=0, doScroll=True)
|
for tItem in self.theProject.projTree:
|
||||||
return False
|
if tItem is None:
|
||||||
|
continue
|
||||||
|
if tItem.itemType != nwItemType.FILE:
|
||||||
|
continue
|
||||||
|
if fHandle is None:
|
||||||
|
fHandle = tItem.itemHandle
|
||||||
|
if tItem.itemHandle == tHandle:
|
||||||
|
foundIt = True
|
||||||
|
elif foundIt:
|
||||||
|
nHandle = tItem.itemHandle
|
||||||
|
break
|
||||||
|
|
||||||
|
if nHandle is not None:
|
||||||
|
self.openDocument(nHandle, tLine=0, doScroll=True)
|
||||||
|
return True
|
||||||
|
elif wrapAround:
|
||||||
|
self.openDocument(fHandle, tLine=0, doScroll=True)
|
||||||
|
return False
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def saveDocument(self):
|
def saveDocument(self):
|
||||||
"""Save the current documents.
|
"""Save the current documents.
|
||||||
"""
|
"""
|
||||||
if self.hasProject:
|
if not self.hasProject:
|
||||||
self.docEditor.saveText()
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.docEditor.saveText()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def viewDocument(self, tHandle=None, tAnchor=None):
|
def viewDocument(self, tHandle=None, tAnchor=None):
|
||||||
"""Load a document for viewing in the view panel.
|
"""Load a document for viewing in the view panel.
|
||||||
"""
|
"""
|
||||||
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
logger.debug("Viewing document, but no handle provided")
|
logger.debug("Viewing document, but no handle provided")
|
||||||
|
|
||||||
@@ -573,8 +592,11 @@ class GuiMain(QMainWindow):
|
|||||||
"""Import the text contained in an out-of-project text file, and
|
"""Import the text contained in an out-of-project text file, and
|
||||||
insert the text into the currently open document.
|
insert the text into the currently open document.
|
||||||
"""
|
"""
|
||||||
lastPath = self.mainConf.lastPath
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
|
lastPath = self.mainConf.lastPath
|
||||||
extFilter = [
|
extFilter = [
|
||||||
"Text files (*.txt)",
|
"Text files (*.txt)",
|
||||||
"Markdown files (*.md)",
|
"Markdown files (*.md)",
|
||||||
@@ -627,16 +649,26 @@ class GuiMain(QMainWindow):
|
|||||||
def mergeDocuments(self):
|
def mergeDocuments(self):
|
||||||
"""Merge multiple documents to one single new document.
|
"""Merge multiple documents to one single new document.
|
||||||
"""
|
"""
|
||||||
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
dlgMerge = GuiDocMerge(self, self.theProject)
|
dlgMerge = GuiDocMerge(self, self.theProject)
|
||||||
dlgMerge.exec_()
|
dlgMerge.exec_()
|
||||||
return
|
|
||||||
|
return True
|
||||||
|
|
||||||
def splitDocument(self):
|
def splitDocument(self):
|
||||||
"""Split a single document into multiple documents.
|
"""Split a single document into multiple documents.
|
||||||
"""
|
"""
|
||||||
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
dlgSplit = GuiDocSplit(self, self.theProject)
|
dlgSplit = GuiDocSplit(self, self.theProject)
|
||||||
dlgSplit.exec_()
|
dlgSplit.exec_()
|
||||||
return
|
|
||||||
|
return True
|
||||||
|
|
||||||
def passDocumentAction(self, theAction):
|
def passDocumentAction(self, theAction):
|
||||||
"""Pass on document action theAction to the document viewer if
|
"""Pass on document action theAction to the document viewer if
|
||||||
@@ -655,6 +687,10 @@ class GuiMain(QMainWindow):
|
|||||||
def openSelectedItem(self):
|
def openSelectedItem(self):
|
||||||
"""Open the selected documents.
|
"""Open the selected documents.
|
||||||
"""
|
"""
|
||||||
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
tHandle = self.treeView.getSelectedHandle()
|
tHandle = self.treeView.getSelectedHandle()
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
logger.warning("No item selected")
|
logger.warning("No item selected")
|
||||||
@@ -673,6 +709,10 @@ class GuiMain(QMainWindow):
|
|||||||
def editItem(self, tHandle=None):
|
def editItem(self, tHandle=None):
|
||||||
"""Open the edit item dialog.
|
"""Open the edit item dialog.
|
||||||
"""
|
"""
|
||||||
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
tHandle = self.treeView.getSelectedHandle()
|
tHandle = self.treeView.getSelectedHandle()
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
@@ -703,6 +743,7 @@ class GuiMain(QMainWindow):
|
|||||||
"""Rebuild the entire index.
|
"""Rebuild the entire index.
|
||||||
"""
|
"""
|
||||||
if not self.hasProject:
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.debug("Rebuilding index ...")
|
logger.debug("Rebuilding index ...")
|
||||||
@@ -748,9 +789,14 @@ class GuiMain(QMainWindow):
|
|||||||
def rebuildOutline(self):
|
def rebuildOutline(self):
|
||||||
"""Force a rebuild of the Outline view.
|
"""Force a rebuild of the Outline view.
|
||||||
"""
|
"""
|
||||||
|
if not self.hasProject:
|
||||||
|
logger.error("No project open")
|
||||||
|
return False
|
||||||
|
|
||||||
logger.verbose("Forcing a rebuild of the Project Outline")
|
logger.verbose("Forcing a rebuild of the Project Outline")
|
||||||
self.tabWidget.setCurrentWidget(self.splitOutline)
|
self.tabWidget.setCurrentWidget(self.splitOutline)
|
||||||
self.projView.refreshTree(overRide=True)
|
self.projView.refreshTree(overRide=True)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -943,7 +989,8 @@ class GuiMain(QMainWindow):
|
|||||||
if self.hasProject:
|
if self.hasProject:
|
||||||
msgBox = QMessageBox()
|
msgBox = QMessageBox()
|
||||||
msgRes = msgBox.question(
|
msgRes = msgBox.question(
|
||||||
self, "Exit", "Do you want to save changes and exit?"
|
self, "Exit",
|
||||||
|
"Do you want to exit novelWriter?<br>Changes are saved automatically."
|
||||||
)
|
)
|
||||||
if msgRes != QMessageBox.Yes:
|
if msgRes != QMessageBox.Yes:
|
||||||
return False
|
return False
|
||||||
@@ -1008,6 +1055,7 @@ class GuiMain(QMainWindow):
|
|||||||
"""
|
"""
|
||||||
if self.docEditor.theHandle is None:
|
if self.docEditor.theHandle is None:
|
||||||
logger.error("No document open, so not activating Focus Mode")
|
logger.error("No document open, so not activating Focus Mode")
|
||||||
|
self.mainMenu.aFocusMode.setChecked(self.isFocusMode)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.isFocusMode = not self.isFocusMode
|
self.isFocusMode = not self.isFocusMode
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
[
|
|
||||||
[
|
|
||||||
"content/14298de4d9524.nwd",
|
|
||||||
"CHARACTER",
|
|
||||||
"John Smith"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/53b69b83cdafc.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Title Page"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/5eaea4e8cdee8.nwd",
|
|
||||||
"WORLD",
|
|
||||||
"Mars"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/636b6aa9b697b.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Making a Scene"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/6a2d6d5f4f401.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Chapter One"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/88706ddc78b1b.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Chapter Two"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/8a5deb88c0e97.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Old File"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/96b68994dfa3d.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"A Note on Structure"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/974e400180a99.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Page"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/ae7339df26ded.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"We Found John!"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/b3e74dbc1f584.nwd",
|
|
||||||
"WORLD",
|
|
||||||
"Earth"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/b8136a5a774a0.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Delete Me!"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/ba8a28a246524.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Interlude"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/bb2c23b3c42cc.nwd",
|
|
||||||
"CHARACTER",
|
|
||||||
"Jane Smith"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/bc0cbd2a407f3.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Another Scene"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/edca4be2fcaf8.nwd",
|
|
||||||
"NOVEL",
|
|
||||||
"Part One"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"content/f1471bef9f2ae.nwd",
|
|
||||||
"WORLD",
|
|
||||||
"Space"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
+21
-22
@@ -1,24 +1,23 @@
|
|||||||
|
|
||||||
Table of Contents
|
Table of Contents
|
||||||
===================
|
=================
|
||||||
|
|
||||||
File Name Class Document Label
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
content/14298de4d9524.nwd CHARACTER John Smith
|
|
||||||
content/53b69b83cdafc.nwd NOVEL Title Page
|
|
||||||
content/5eaea4e8cdee8.nwd WORLD Mars
|
|
||||||
content/636b6aa9b697b.nwd NOVEL Making a Scene
|
|
||||||
content/6a2d6d5f4f401.nwd NOVEL Chapter One
|
|
||||||
content/88706ddc78b1b.nwd NOVEL Chapter Two
|
|
||||||
content/8a5deb88c0e97.nwd NOVEL Old File
|
|
||||||
content/96b68994dfa3d.nwd NOVEL A Note on Structure
|
|
||||||
content/974e400180a99.nwd NOVEL Page
|
|
||||||
content/ae7339df26ded.nwd NOVEL We Found John!
|
|
||||||
content/b3e74dbc1f584.nwd WORLD Earth
|
|
||||||
content/b8136a5a774a0.nwd NOVEL Delete Me!
|
|
||||||
content/ba8a28a246524.nwd NOVEL Interlude
|
|
||||||
content/bb2c23b3c42cc.nwd CHARACTER Jane Smith
|
|
||||||
content/bc0cbd2a407f3.nwd NOVEL Another Scene
|
|
||||||
content/edca4be2fcaf8.nwd NOVEL Part One
|
|
||||||
content/f1471bef9f2ae.nwd WORLD Space
|
|
||||||
|
|
||||||
|
File Name Class Layout Document Label
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
content/53b69b83cdafc.nwd NOVEL TITLE Title Page
|
||||||
|
content/974e400180a99.nwd NOVEL PAGE Page
|
||||||
|
content/edca4be2fcaf8.nwd NOVEL PARTITION Part One
|
||||||
|
content/6a2d6d5f4f401.nwd NOVEL CHAPTER Chapter One
|
||||||
|
content/636b6aa9b697b.nwd NOVEL SCENE Making a Scene
|
||||||
|
content/bc0cbd2a407f3.nwd NOVEL SCENE Another Scene
|
||||||
|
content/ba8a28a246524.nwd NOVEL UNNUMBERED Interlude
|
||||||
|
content/96b68994dfa3d.nwd NOVEL NOTE A Note on Structure
|
||||||
|
content/88706ddc78b1b.nwd NOVEL CHAPTER Chapter Two
|
||||||
|
content/ae7339df26ded.nwd NOVEL SCENE We Found John!
|
||||||
|
content/14298de4d9524.nwd CHARACTER NOTE John Smith
|
||||||
|
content/bb2c23b3c42cc.nwd CHARACTER NOTE Jane Smith
|
||||||
|
content/b3e74dbc1f584.nwd WORLD NOTE Earth
|
||||||
|
content/f1471bef9f2ae.nwd WORLD NOTE Space
|
||||||
|
content/5eaea4e8cdee8.nwd WORLD NOTE Mars
|
||||||
|
content/8a5deb88c0e97.nwd NOVEL SCENE Old File
|
||||||
|
content/b8136a5a774a0.nwd NOVEL SCENE Delete Me!
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class DummyMain():
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mainConf = None
|
self.mainConf = None
|
||||||
|
self.hasProject = True
|
||||||
self.statusBar = StatusBar()
|
self.statusBar = StatusBar()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -627,7 +627,6 @@ def testProjectOldFormat(nwDummy, nwOldProj):
|
|||||||
# Check that new files have been created
|
# Check that new files have been created
|
||||||
assert os.path.isfile(os.path.join(nwOldProj, "meta", "guiOptions.json"))
|
assert os.path.isfile(os.path.join(nwOldProj, "meta", "guiOptions.json"))
|
||||||
assert os.path.isfile(os.path.join(nwOldProj, "meta", "sessionStats.log"))
|
assert os.path.isfile(os.path.join(nwOldProj, "meta", "sessionStats.log"))
|
||||||
assert os.path.isfile(os.path.join(nwOldProj, "ToC.json"))
|
|
||||||
assert os.path.isfile(os.path.join(nwOldProj, "ToC.txt"))
|
assert os.path.isfile(os.path.join(nwOldProj, "ToC.txt"))
|
||||||
|
|
||||||
@pytest.mark.project
|
@pytest.mark.project
|
||||||
|
|||||||
Reference in New Issue
Block a user