diff --git a/nw/core/project.py b/nw/core/project.py index f8e45194..b646934a 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -28,12 +28,10 @@ import nw import logging import os +import shutil from lxml import etree from time import time -from shutil import make_archive, unpack_archive, copyfile - -from PyQt5.QtWidgets import QMessageBox from nw.core.tree import NWTree from nw.core.item import NWItem @@ -436,26 +434,15 @@ class NWProject(): xRoot = nwXML.getroot() nwxRoot = xRoot.tag - appVersion = "Unknown" - hexVersion = "0x0" - fileVersion = "Unknown" - self.saveCount = 0 - self.autoCount = 0 - - if "appVersion" in xRoot.attrib: - appVersion = xRoot.attrib["appVersion"] - if "hexVersion" in xRoot.attrib: - hexVersion = xRoot.attrib["hexVersion"] - if "fileVersion" in xRoot.attrib: - fileVersion = xRoot.attrib["fileVersion"] + appVersion = xRoot.attrib.get("appVersion", "Unknown") + hexVersion = xRoot.attrib.get("hexVersion", "0x0") + fileVersion = xRoot.attrib.get("fileVersion", "Unknown") # The following are deprecated and will be removed - if "saveCount" in xRoot.attrib: - self.saveCount = checkInt(xRoot.attrib["saveCount"], 0, False) - if "autoCount" in xRoot.attrib: - self.autoCount = checkInt(xRoot.attrib["autoCount"], 0, False) - if "editTime" in xRoot.attrib: - self.editTime = checkInt(xRoot.attrib["editTime"], 0, False) + # The settings have been moved to the tag + self.saveCount = checkInt(xRoot.attrib.get("saveCount", 0), 0, False) + self.autoCount = checkInt(xRoot.attrib.get("autoCount", 0), 0, False) + self.editTime = checkInt(xRoot.attrib.get("editTime", 0), 0, False) logger.verbose("XML root is %s" % nwxRoot) logger.verbose("File version is %s" % fileVersion) @@ -483,20 +470,19 @@ class NWProject(): # parser will lose the autoReplace settings if allowed to # read the file. Introduced in version 0.10. - if fileVersion == "1.0" and self.mainConf.showGUI: - msgBox = QMessageBox() - msgRes = msgBox.question(self.theParent, "Old Project Version", ( + if fileVersion == "1.0": + msgRes = self.theParent.askQuestion("Old Project Version", ( "The project file and data is created by a novelWriter version " "lower than 0.7. Do you want to upgrade the project to the " "most recent format?

Note that after the upgrade, you " "cannot open the project with an older version of novelWriter " "any more, so make sure you have a recent backup." )) - if msgRes != QMessageBox.Yes: + if not msgRes: self.clearProject() return False - elif fileVersion != "1.1" and fileVersion != "1.2" and self.mainConf.showGUI: + elif fileVersion != "1.1" and fileVersion != "1.2": self.makeAlert(( "Unknown or unsupported novelWriter project file format. " "The project cannot be opened by this version of novelWriter. " @@ -510,9 +496,8 @@ class NWProject(): # Check novelWriter Version # ========================= - if int(hexVersion, 16) > int(nw.__hexversion__, 16) and self.mainConf.showGUI: - msgBox = QMessageBox() - msgRes = msgBox.question(self.theParent, "Version Conflict", ( + if int(hexVersion, 16) > int(nw.__hexversion__, 16): + msgRes = self.theParent.askQuestion("Version Conflict", ( "This project was saved by a newer version of novelWriter, version %s. " "This is version %s. If you continue to open the project, some attributes " "and settings may not be preserved, but the overall project should be fine. " @@ -520,7 +505,7 @@ class NWProject(): ) % ( appVersion, nw.__version__ )) - if msgRes != QMessageBox.Yes: + if not msgRes: self.clearProject() return False @@ -835,15 +820,15 @@ class NWProject(): try: self._clearLockFile() - make_archive(baseName, "zip", self.projPath, ".") + shutil.make_archive(baseName, "zip", self.projPath, ".") self._writeLockFile() + logger.info("Backup written to: %s" % archName) if doNotify: self.theParent.makeAlert( "Backup archive file written to: %s.zip" % os.path.join(cleanName, archName), nwAlert.INFO ) - else: - logger.info("Backup written to: %s" % archName) + except Exception as e: self.theParent.makeAlert( ["Could not write backup archive.", str(e)], @@ -874,7 +859,7 @@ class NWProject(): self.setProjectPath(projPath, newProject=True) try: - unpack_archive(pkgSample, projPath) + shutil.unpack_archive(pkgSample, projPath) isSuccess = True except Exception as e: self.makeAlert( @@ -887,14 +872,14 @@ class NWProject(): try: srcProj = os.path.join(srcSample, nwFiles.PROJ_FILE) dstProj = os.path.join(projPath, nwFiles.PROJ_FILE) - copyfile(srcProj, dstProj) + shutil.copyfile(srcProj, dstProj) srcContent = os.path.join(srcSample, "content") dstContent = os.path.join(projPath, "content") for srcFile in os.listdir(srcContent): srcDoc = os.path.join(srcContent, srcFile) dstDoc = os.path.join(dstContent, srcFile) - copyfile(srcDoc, dstDoc) + shutil.copyfile(srcDoc, dstDoc) isSuccess = True @@ -998,11 +983,15 @@ class NWProject(): "You must set a valid backup path in preferences to use " "the automatic project backup feature." ), nwAlert.WARN) + return False + if self.projName == "": self.theParent.makeAlert(( "You must set a valid project name in project settings to " "use the automatic project backup feature." ), nwAlert.WARN) + return False + return True def setSpellCheck(self, theMode): @@ -1011,12 +1000,15 @@ class NWProject(): if self.spellCheck != theMode: self.spellCheck = theMode self.setProjectChanged(True) - return True + return self.spellCheck def setSpellLang(self, theLang): """Set the project-specific spell check language. """ - self.projLang = checkString(theLang, None, True) + theLang = checkString(theLang, None, True) + if self.projLang != theLang: + self.projLang = theLang + self.setProjectChanged(True) return True def setAutoOutline(self, theMode): @@ -1025,7 +1017,7 @@ class NWProject(): if self.autoOutline != theMode: self.autoOutline = theMode self.setProjectChanged(True) - return True + return self.autoOutline def setTreeOrder(self, newOrder): """A list representing the linear/flattened order of project @@ -1072,7 +1064,7 @@ class NWProject(): if nwItem.itemStatus in replaceMap.keys(): nwItem.setStatus(replaceMap[nwItem.itemStatus]) self.setProjectChanged(True) - return + return True def setImportColours(self, newCols): """Update the list of note file importance flags. Also iterate @@ -1084,14 +1076,15 @@ class NWProject(): if nwItem.itemStatus in replaceMap.keys(): nwItem.setStatus(replaceMap[nwItem.itemStatus]) self.setProjectChanged(True) - return + return True def setAutoReplace(self, autoReplace): """Update the auto-replace dictionary. This replaces the entire dictionary, so alterations have to be made in a copy. """ self.autoReplace = autoReplace - return + self.setProjectChanged(True) + return True def setTitleFormat(self, titleFormat): """Set the formatting of titles in the project. @@ -1099,7 +1092,7 @@ class NWProject(): for valKey, valEntry in titleFormat.items(): if valKey in self.titleFormat: self.titleFormat[valKey] = checkString(valEntry, self.titleFormat[valKey], False) - return + return True def setProjectChanged(self, bValue): """Toggle the project changed flag, and propagate the @@ -1292,7 +1285,7 @@ class NWProject(): back into the project tree. """ if self.projPath is None: - return + return False # Then check the files in the data folder logger.debug("Checking files in project content folder") @@ -1352,7 +1345,7 @@ class NWProject(): orphItem.setLayout(oLayout) self.projTree.append(oHandle, None, orphItem) - return + return True def _appendSessionStats(self): """Append session statistics to the sessions log file. @@ -1490,7 +1483,8 @@ class NWProject(): os.unlink(rmFile) except Exception as e: logger.error(str(e)) + return False - return + return True # END Class NWProject diff --git a/nw/core/status.py b/nw/core/status.py index 2c6fe811..ccec2e73 100644 --- a/nw/core/status.py +++ b/nw/core/status.py @@ -109,7 +109,8 @@ class NWStatus(): return def countEntry(self, theLabel): - """Lookup the usage count of a given entry. + """Increment the counter for a given label. This should be used + together with resetCounts in a loop over project items. """ theIndex = self.lookupEntry(theLabel) if theIndex is not None: diff --git a/nw/guimain.py b/nw/guimain.py index 76b344fb..9efc9a2e 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -982,6 +982,13 @@ class GuiMain(QMainWindow): return + def askQuestion(self, theTitle, theQuestion): + """Ask the user a Yes/No question. + """ + msgBox = QMessageBox() + msgRes = msgBox.question(self, theTitle, theQuestion) + return msgRes == QMessageBox.Yes + def reportConfErr(self): """Checks if the Config module has any errors to report, and let the user know if this is the case. The Config module caches