diff --git a/nw/config.py b/nw/config.py index 6baf30a2..fbaf30fb 100644 --- a/nw/config.py +++ b/nw/config.py @@ -15,7 +15,7 @@ import configparser import sys import nw -from os import path, mkdir, getcwd +from os import path, mkdir, makedirs, getcwd from appdirs import user_config_dir from datetime import datetime @@ -166,9 +166,12 @@ class Config: # If config folder does not exist, make it. # This assumes that the os config folder itself exists. - # TODO: This does not work on Windows - if not path.isdir(self.confPath): - mkdir(self.confPath) + if self.osWindows: + if not path.isdir(self.confPath): + makedirs(self.confPath) + else: + if not path.isdir(self.confPath): + mkdir(self.confPath) # Check if config file exists if path.isfile(path.join(self.confPath,self.confFile)): @@ -190,7 +193,7 @@ class Config: logger.debug("Loading config file") cnfParse = configparser.ConfigParser() try: - cnfParse.read_file(open(path.join(self.confPath,self.confFile))) + cnfParse.read_file(open(path.join(self.confPath,self.confFile),mode="r",encoding="utf8")) except Exception as e: logger.error("Could not load config file") return False @@ -312,7 +315,7 @@ class Config: # Write config file try: - cnfParse.write(open(path.join(self.confPath,self.confFile),"w")) + cnfParse.write(open(path.join(self.confPath,self.confFile),mode="w",encoding="utf8")) self.confChanged = False except Exception as e: logger.error("Could not save config file") diff --git a/nw/convert/file/concat.py b/nw/convert/file/concat.py index cca2769b..5d22b0fa 100644 --- a/nw/convert/file/concat.py +++ b/nw/convert/file/concat.py @@ -63,7 +63,7 @@ class ConcatFile(TextFile): def _doOpenFile(self, filePath): try: - self.outFile = open(filePath,mode="wt+") + self.outFile = open(filePath,mode="wt+",encoding="utf8") except Exception as e: self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR) return False diff --git a/nw/convert/file/html.py b/nw/convert/file/html.py index d9893c00..278acab7 100644 --- a/nw/convert/file/html.py +++ b/nw/convert/file/html.py @@ -34,7 +34,7 @@ class HtmlFile(TextFile): def _doOpenFile(self, filePath): try: - self.outFile = open(filePath,mode="wt+") + self.outFile = open(filePath,mode="wt+",encoding="utf8") self.outFile.write("\n") self.outFile.write("\n") self.outFile.write("\n") diff --git a/nw/convert/file/latex.py b/nw/convert/file/latex.py index ebb052c8..962731e6 100644 --- a/nw/convert/file/latex.py +++ b/nw/convert/file/latex.py @@ -34,7 +34,7 @@ class LaTeXFile(TextFile): def _doOpenFile(self, filePath): try: - self.outFile = open(filePath,mode="wt+") + self.outFile = open(filePath,mode="wt+",encoding="utf8") self.outFile.write("\\documentclass[12pt]{report}\n") self.outFile.write("\\usepackage[utf8]{inputenc}\n") self.outFile.write("\n") diff --git a/nw/convert/file/markdown.py b/nw/convert/file/markdown.py index ea4bf5c6..360f1a64 100644 --- a/nw/convert/file/markdown.py +++ b/nw/convert/file/markdown.py @@ -34,7 +34,7 @@ class MarkdownFile(TextFile): def _doOpenFile(self, filePath): try: - self.outFile = open(filePath,mode="wt+") + self.outFile = open(filePath,mode="wt+",encoding="utf8") except Exception as e: self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR) return False diff --git a/nw/convert/file/text.py b/nw/convert/file/text.py index e04036c1..e62e5729 100644 --- a/nw/convert/file/text.py +++ b/nw/convert/file/text.py @@ -153,7 +153,7 @@ class TextFile(): that uses a different file format that requires a different approach. """ try: - self.outFile = open(filePath,mode="wt+") + self.outFile = open(filePath,mode="wt+",encoding="utf8") self.outFile.write("\n\n") except Exception as e: self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 3ecc9f3c..9c247ae5 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -162,9 +162,21 @@ class GuiDocEditor(QTextEdit): return True def loadText(self, tHandle): + """Load text from a document into the editor. If we have an io error, we must handle this + and clear the editor so that we don't risk overwriting the file if it exists. This can for + instance happen of the file contains binary elements or an encoding that novelWriter does + not support. If load is successful, ot the document is new (empty string) we set up the + editor for editing the file. + """ + + theDoc = self.nwDocument.openDocument(tHandle) + if theDoc is None: + # There was an io error + self.clearEditor() + return False self.hLight.setHandle(tHandle) - self.setPlainText(self.nwDocument.openDocument(tHandle)) + self.setPlainText(theDoc) self.setCursorPosition(self.nwDocument.theItem.cursorPos) self.lastEdit = time() self._runCounter() diff --git a/nw/gui/sessionlog.py b/nw/gui/sessionlog.py index e46feb36..3c400acf 100644 --- a/nw/gui/sessionlog.py +++ b/nw/gui/sessionlog.py @@ -147,7 +147,7 @@ class GuiSessionLogView(QDialog): logger.debug("Loading session log file") try: - with open(logFile,mode="r") as inFile: + with open(logFile,mode="r",encoding="utf8") as inFile: for inLine in inFile: inData = inLine.split() if len(inData) != 8: diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 8e37a61d..53a882d9 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -332,10 +332,12 @@ class GuiMain(QMainWindow): def openDocument(self, tHandle): if self.hasProject: self.closeDocument() - self.docEditor.loadText(tHandle) - self.docEditor.setFocus() - self.docEditor.changeWidth() - self.theProject.setLastEdited(tHandle) + if self.docEditor.loadText(tHandle): + self.docEditor.setFocus() + self.docEditor.changeWidth() + self.theProject.setLastEdited(tHandle) + else: + return False return True def saveDocument(self): @@ -390,7 +392,7 @@ class GuiMain(QMainWindow): theText = None try: - with open(loadFile,mode="rt") as inFile: + with open(loadFile,mode="rt",encoding="utf8") as inFile: theText = inFile.read() except Exception as e: self.makeAlert(["Could not read file. The file cannot be a binary file.",str(e)], nwAlert.ERROR) diff --git a/nw/project/document.py b/nw/project/document.py index 5305c01b..7f5b550a 100644 --- a/nw/project/document.py +++ b/nw/project/document.py @@ -53,12 +53,17 @@ class NWDoc(): if path.isfile(docPath): try: - with open(docPath,mode="r") as inFile: + with open(docPath,mode="r",encoding="utf8") as inFile: theDoc = inFile.read() except Exception as e: self.makeAlert(["Failed to open document file.",str(e)], nwAlert.ERROR) - return "" + # Note: Document must be cleared in case of an io error, or else the auto-save or + # save will try to overwrite it with an empty file. Return None to alert the caller. + self.clearDocument() + return None else: + # The document file does not exist, so we assume it's a new document and initialise an + # empty text string. logger.debug("The requested document does not exist.") return "" @@ -89,7 +94,7 @@ class NWDoc(): if path.isfile(docPath): rename(docPath,docBack) try: - with open(docPath,mode="w") as outFile: + with open(docPath,mode="w",encoding="utf8") as outFile: outFile.write(docText) except Exception as e: self.makeAlert(["Could not save document.",str(e)], nwAlert.ERROR) diff --git a/nw/project/index.py b/nw/project/index.py index 622b26c3..a55bf14e 100644 --- a/nw/project/index.py +++ b/nw/project/index.py @@ -91,7 +91,7 @@ class NWIndex(): if path.isfile(indexFile): logger.debug("Loading index file") try: - with open(indexFile,mode="r") as inFile: + with open(indexFile,mode="r",encoding="utf8") as inFile: theJson = inFile.read() theData = json.loads(theJson) except Exception as e: @@ -119,7 +119,7 @@ class NWIndex(): else: nIndent = None try: - with open(indexFile,mode="w+") as outFile: + with open(indexFile,mode="w+",encoding="utf8") as outFile: outFile.write(json.dumps({ "tagIndex" : self.tagIndex, "refIndex" : self.refIndex, diff --git a/nw/project/project.py b/nw/project/project.py index 603113dd..62345a63 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -328,7 +328,7 @@ class NWProject(): # Write the xml tree to file saveFile = path.join(self.projPath,self.projFile) try: - with open(saveFile,"wb") as outFile: + with open(saveFile,mode="wb") as outFile: outFile.write(etree.tostring( nwXML, pretty_print = True, @@ -657,7 +657,7 @@ class NWProject(): if self.projMeta is None: return False - with open(path.join(self.projMeta, nwFiles.SESS_INFO), mode="a+") as outFile: + with open(path.join(self.projMeta, nwFiles.SESS_INFO),mode="a+",encoding="utf8") as outFile: print(( "Start: {opened:s} " "End: {closed:s} " diff --git a/nw/theme.py b/nw/theme.py index d6958df6..f1a3b0c7 100644 --- a/nw/theme.py +++ b/nw/theme.py @@ -137,7 +137,7 @@ class Theme: cssData = "" try: if path.isfile(self.cssFile): - with open(self.cssFile,mode="r") as inFile: + with open(self.cssFile,mode="r",encoding="utf8") as inFile: cssData = inFile.read() except Exception as e: logger.error("Could not load theme css file") @@ -154,7 +154,7 @@ class Theme: # Config File confParser = configparser.ConfigParser() try: - confParser.read_file(open(self.confFile)) + confParser.read_file(open(self.confFile,mode="r",encoding="utf8")) except Exception as e: logger.error("Could not load theme settings from: %s" % self.confFile) return False @@ -205,7 +205,7 @@ class Theme: confParser = configparser.ConfigParser() try: - confParser.read_file(open(self.syntaxFile)) + confParser.read_file(open(self.syntaxFile,mode="r",encoding="utf8")) except Exception as e: logger.error("Could not load syntax colours from: %s" % self.syntaxFile) return False @@ -251,7 +251,7 @@ class Theme: themeConf = path.join(self.mainConf.themeRoot, self.guiPath, themeDir, self.confName) logger.verbose("Checking theme config for '%s'" % themeDir) try: - confParser.read_file(open(themeConf)) + confParser.read_file(open(themeConf,mode="r",encoding="utf8")) except Exception as e: self.theParent.makeAlert(["Could not load theme config file",str(e)],nwAlert.ERROR) continue @@ -279,7 +279,7 @@ class Theme: continue logger.verbose("Checking theme syntax for '%s'" % syntaxFile) try: - confParser.read_file(open(syntaxPath)) + confParser.read_file(open(syntaxPath,moe="r",encoding="utf8")) except Exception as e: self.theParent.makeAlert(["Could not load syntax file",str(e)],nwAlert.ERROR) return [] diff --git a/nw/tools/optlaststate.py b/nw/tools/optlaststate.py index a3fd0c50..0df6ff68 100644 --- a/nw/tools/optlaststate.py +++ b/nw/tools/optlaststate.py @@ -37,7 +37,7 @@ class OptLastState(): if path.isfile(stateFile): logger.debug("Loading options file") try: - with open(stateFile,mode="r") as inFile: + with open(stateFile,mode="r",encoding="utf8") as inFile: theJson = inFile.read() theState = json.loads(theJson) except Exception as e: @@ -52,7 +52,7 @@ class OptLastState(): stateFile = path.join(self.theProject.projMeta,self.theFile) logger.debug("Saving options file") try: - with open(stateFile,mode="w+") as outFile: + with open(stateFile,mode="w+",encoding="utf8") as outFile: outFile.write(json.dumps(self.theState, indent=2)) except Exception as e: logger.error("Failed to save options file") diff --git a/tests/nwtools.py b/tests/nwtools.py index 9ae3e6ee..fecb1df9 100644 --- a/tests/nwtools.py +++ b/tests/nwtools.py @@ -13,13 +13,13 @@ def ensureDir(theDir): def cmpFiles(fileOne, fileTwo, ignoreLines=[]): try: - foOne = open(fileOne,mode="r") + foOne = open(fileOne,mode="r",encoding="utf8") except Exception as e: print(str(e)) return False try: - foTwo = open(fileTwo,mode="r") + foTwo = open(fileTwo,mode="r",encoding="utf8") except Exception as e: print(str(e)) return False