Exemple #1
0
    def writeDocument(self, docText, forceWrite=False):
        """Write the document specified by the handle attribute. Handle
        any IO errors in the process  Returns True if successful, False
        if not.
        """
        self._docError = ""
        if self._docHandle is None:
            logger.error("No document handle set")
            return False

        self.theProject.ensureFolderStructure()

        docFile = self._docHandle+".nwd"
        logger.debug("Saving document: %s", docFile)

        docPath = os.path.join(self.theProject.projContent, docFile)
        docTemp = os.path.join(self.theProject.projContent, docFile+"~")

        if self._prevHash is not None and not forceWrite:
            self._currHash = sha256sum(docPath)
            if self._currHash is not None and self._currHash != self._prevHash:
                logger.error("File has been altered on disk since opened")
                return False

        # DocMeta Line
        if self._theItem is None:
            docMeta = ""
        else:
            docMeta = (
                f"%%~name: {self._theItem.itemName}\n"
                f"%%~path: {self._theItem.itemParent}/{self._theItem.itemHandle}\n"
                f"%%~kind: {self._theItem.itemClass.name}/{self._theItem.itemLayout.name}\n"
            )

        try:
            with open(docTemp, mode="w", encoding="utf-8") as outFile:
                outFile.write(docMeta)
                outFile.write(docText)
        except Exception as exc:
            self._docError = formatException(exc)
            return False

        # If we're here, the file was successfully saved, so we can
        # replace the temp file with the actual file
        try:
            os.replace(docTemp, docPath)
        except OSError as exc:
            self._docError = formatException(exc)
            return False

        self._prevHash = sha256sum(docPath)
        self._currHash = self._prevHash

        return True
Exemple #2
0
    def loadRecentCache(self):
        """Load the cache file for recent projects.
        """
        if self.dataPath is None:
            return False

        self.recentProj = {}

        cacheFile = os.path.join(self.dataPath, nwFiles.RECENT_FILE)
        if not os.path.isfile(cacheFile):
            return True

        try:
            with open(cacheFile, mode="r", encoding="utf-8") as inFile:
                theData = json.load(inFile)

            for projPath, theEntry in theData.items():
                self.recentProj[projPath] = {
                    "title": theEntry.get("title", ""),
                    "time": theEntry.get("time", 0),
                    "words": theEntry.get("words", 0),
                }

        except Exception as exc:
            self.hasError = True
            self.errData.append("Could not load recent project cache")
            self.errData.append(formatException(exc))
            return False

        return True
Exemple #3
0
    def readDocument(self, isOrphan=False):
        """Read the document specified by the handle set in the
        contructor, capturing potential file system errors and parse
        meta data. If the document doesn't exist on disk, return an
        empty string. If something went wrong, return None.
        """
        self._docError = ""
        if self._docHandle is None:
            logger.error("No document handle set")
            return None

        if self._theItem is None and not isOrphan:
            logger.error("Unknown novelWriter document")
            return None

        docFile = self._docHandle+".nwd"
        logger.debug("Opening document: %s", docFile)

        docPath = os.path.join(self.theProject.projContent, docFile)
        self._fileLoc = docPath

        theText = ""
        self._docMeta = {}
        self._prevHash = None

        if os.path.isfile(docPath):
            self._prevHash = sha256sum(docPath)
            try:
                with open(docPath, mode="r", encoding="utf-8") as inFile:
                    # Check the first <= 10 lines for metadata
                    for i in range(10):
                        inLine = inFile.readline()
                        if inLine.startswith(r"%%~"):
                            self._parseMeta(inLine)
                        else:
                            theText = inLine
                            break

                    # Load the rest of the file
                    theText += inFile.read()

            except Exception as exc:
                self._docError = formatException(exc)
                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 ""

        return theText
Exemple #4
0
    def saveRecentCache(self):
        """Save the cache dictionary of recent projects.
        """
        if self.dataPath is None:
            return False

        cacheFile = os.path.join(self.dataPath, nwFiles.RECENT_FILE)
        cacheTemp = os.path.join(self.dataPath, nwFiles.RECENT_FILE + "~")

        try:
            with open(cacheTemp, mode="w+", encoding="utf-8") as outFile:
                json.dump(self.recentProj, outFile, indent=2)
        except Exception as exc:
            self.hasError = True
            self.errData.append("Could not save recent project cache")
            self.errData.append(formatException(exc))
            return False

        if os.path.isfile(cacheFile):
            os.unlink(cacheFile)
        os.rename(cacheTemp, cacheFile)

        return True
Exemple #5
0
    def deleteDocument(self):
        """Permanently delete a document source file and related files
        from the project data folder.
        """
        self._docError = ""
        if self._docHandle is None:
            logger.error("No document handle set")
            return False

        chkList = [
            os.path.join(self.theProject.projContent, f"{self._docHandle}.nwd"),
            os.path.join(self.theProject.projContent, f"{self._docHandle}.nwd~"),
        ]

        for chkFile in chkList:
            if os.path.isfile(chkFile):
                try:
                    os.unlink(chkFile)
                    logger.debug("Deleted: %s", chkFile)
                except Exception as exc:
                    self._docError = formatException(exc)
                    return False

        return True
Exemple #6
0
    def saveConfig(self):
        """Save the current preferences to file.
        """
        logger.debug("Saving config file")
        if self.confPath is None:
            return False

        theConf = NWConfigParser()

        theConf["Main"] = {
            "timestamp": formatTimeStamp(time()),
            "theme": str(self.guiTheme),
            "syntax": str(self.guiSyntax),
            "icons": str(self.guiIcons),
            "guifont": str(self.guiFont),
            "guifontsize": str(self.guiFontSize),
            "lastnotes": str(self.lastNotes),
            "guilang": str(self.guiLang),
            "hidevscroll": str(self.hideVScroll),
            "hidehscroll": str(self.hideHScroll),
        }

        theConf["Sizes"] = {
            "geometry": self._packList(self.winGeometry),
            "preferences": self._packList(self.prefGeometry),
            "projcols": self._packList(self.projColWidth),
            "mainpane": self._packList(self.mainPanePos),
            "docpane": self._packList(self.docPanePos),
            "viewpane": self._packList(self.viewPanePos),
            "outlinepane": self._packList(self.outlnPanePos),
            "fullscreen": str(self.isFullScreen),
        }

        theConf["Project"] = {
            "autosaveproject": str(self.autoSaveProj),
            "autosavedoc": str(self.autoSaveDoc),
            "emphlabels": str(self.emphLabels),
        }

        theConf["Editor"] = {
            "textfont": str(self.textFont),
            "textsize": str(self.textSize),
            "width": str(self.textWidth),
            "margin": str(self.textMargin),
            "tabwidth": str(self.tabWidth),
            "focuswidth": str(self.focusWidth),
            "hidefocusfooter": str(self.hideFocusFooter),
            "justify": str(self.doJustify),
            "autoselect": str(self.autoSelect),
            "autoreplace": str(self.doReplace),
            "repsquotes": str(self.doReplaceSQuote),
            "repdquotes": str(self.doReplaceDQuote),
            "repdash": str(self.doReplaceDash),
            "repdots": str(self.doReplaceDots),
            "scrollpastend": str(self.scrollPastEnd),
            "autoscroll": str(self.autoScroll),
            "autoscrollpos": str(self.autoScrollPos),
            "fmtsinglequote": self._packList(self.fmtSingleQuotes),
            "fmtdoublequote": self._packList(self.fmtDoubleQuotes),
            "fmtpadbefore": str(self.fmtPadBefore),
            "fmtpadafter": str(self.fmtPadAfter),
            "fmtpadthin": str(self.fmtPadThin),
            "spellcheck": str(self.spellLanguage),
            "showtabsnspaces": str(self.showTabsNSpaces),
            "showlineendings": str(self.showLineEndings),
            "showmultispaces": str(self.showMultiSpaces),
            "wordcounttimer": str(self.wordCountTimer),
            "bigdoclimit": str(self.bigDocLimit),
            "incnoteswcount": str(self.incNotesWCount),
            "showfullpath": str(self.showFullPath),
            "highlightquotes": str(self.highlightQuotes),
            "allowopensquote": str(self.allowOpenSQuote),
            "allowopendquote": str(self.allowOpenDQuote),
            "highlightemph": str(self.highlightEmph),
            "stopwhenidle": str(self.stopWhenIdle),
            "useridletime": str(self.userIdleTime),
        }

        theConf["Backup"] = {
            "backuppath": str(self.backupPath),
            "backuponclose": str(self.backupOnClose),
            "askbeforebackup": str(self.askBeforeBackup),
        }

        theConf["State"] = {
            "showrefpanel": str(self.showRefPanel),
            "viewcomments": str(self.viewComments),
            "viewsynopsis": str(self.viewSynopsis),
            "searchcase": str(self.searchCase),
            "searchword": str(self.searchWord),
            "searchregex": str(self.searchRegEx),
            "searchloop": str(self.searchLoop),
            "searchnextfile": str(self.searchNextFile),
            "searchmatchcap": str(self.searchMatchCap),
        }

        theConf["Path"] = {
            "lastpath": str(self.lastPath),
        }

        # Write config file
        cnfPath = os.path.join(self.confPath, self.confFile)
        try:
            with open(cnfPath, mode="w", encoding="utf-8") as outFile:
                theConf.write(outFile)
            self.confChanged = False
        except Exception as exc:
            logger.error("Could not save config file")
            logException()
            self.hasError = True
            self.errData.append("Could not save config file")
            self.errData.append(formatException(exc))
            return False

        return True
Exemple #7
0
    def loadConfig(self):
        """Load preferences from file and replace default settings.
        """
        logger.debug("Loading config file")
        if self.confPath is None:
            return False

        theConf = NWConfigParser()
        cnfPath = os.path.join(self.confPath, self.confFile)
        try:
            with open(cnfPath, mode="r", encoding="utf-8") as inFile:
                theConf.read_file(inFile)
        except Exception as exc:
            logger.error("Could not load config file")
            logException()
            self.hasError = True
            self.errData.append("Could not load config file")
            self.errData.append(formatException(exc))
            return False

        # Main
        cnfSec = "Main"
        self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
        self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
        self.guiIcons = theConf.rdStr(cnfSec, "icons", self.guiIcons)
        self.guiFont = theConf.rdStr(cnfSec, "guifont", self.guiFont)
        self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize",
                                         self.guiFontSize)
        self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
        self.guiLang = theConf.rdStr(cnfSec, "guilang", self.guiLang)
        self.hideVScroll = theConf.rdBool(cnfSec, "hidevscroll",
                                          self.hideVScroll)
        self.hideHScroll = theConf.rdBool(cnfSec, "hidehscroll",
                                          self.hideHScroll)

        # Sizes
        cnfSec = "Sizes"
        self.winGeometry = theConf.rdIntList(cnfSec, "geometry",
                                             self.winGeometry)
        self.prefGeometry = theConf.rdIntList(cnfSec, "preferences",
                                              self.prefGeometry)
        self.projColWidth = theConf.rdIntList(cnfSec, "projcols",
                                              self.projColWidth)
        self.mainPanePos = theConf.rdIntList(cnfSec, "mainpane",
                                             self.mainPanePos)
        self.docPanePos = theConf.rdIntList(cnfSec, "docpane", self.docPanePos)
        self.viewPanePos = theConf.rdIntList(cnfSec, "viewpane",
                                             self.viewPanePos)
        self.outlnPanePos = theConf.rdIntList(cnfSec, "outlinepane",
                                              self.outlnPanePos)
        self.isFullScreen = theConf.rdBool(cnfSec, "fullscreen",
                                           self.isFullScreen)

        # Project
        cnfSec = "Project"
        self.autoSaveProj = theConf.rdInt(cnfSec, "autosaveproject",
                                          self.autoSaveProj)
        self.autoSaveDoc = theConf.rdInt(cnfSec, "autosavedoc",
                                         self.autoSaveDoc)
        self.emphLabels = theConf.rdBool(cnfSec, "emphlabels", self.emphLabels)

        # Editor
        cnfSec = "Editor"
        self.textFont = theConf.rdStr(cnfSec, "textfont", self.textFont)
        self.textSize = theConf.rdInt(cnfSec, "textsize", self.textSize)
        self.textWidth = theConf.rdInt(cnfSec, "width", self.textWidth)
        self.textMargin = theConf.rdInt(cnfSec, "margin", self.textMargin)
        self.tabWidth = theConf.rdInt(cnfSec, "tabwidth", self.tabWidth)
        self.focusWidth = theConf.rdInt(cnfSec, "focuswidth", self.focusWidth)
        self.hideFocusFooter = theConf.rdBool(cnfSec, "hidefocusfooter",
                                              self.hideFocusFooter)
        self.doJustify = theConf.rdBool(cnfSec, "justify", self.doJustify)
        self.autoSelect = theConf.rdBool(cnfSec, "autoselect", self.autoSelect)
        self.doReplace = theConf.rdBool(cnfSec, "autoreplace", self.doReplace)
        self.doReplaceSQuote = theConf.rdBool(cnfSec, "repsquotes",
                                              self.doReplaceSQuote)
        self.doReplaceDQuote = theConf.rdBool(cnfSec, "repdquotes",
                                              self.doReplaceDQuote)
        self.doReplaceDash = theConf.rdBool(cnfSec, "repdash",
                                            self.doReplaceDash)
        self.doReplaceDots = theConf.rdBool(cnfSec, "repdots",
                                            self.doReplaceDots)
        self.scrollPastEnd = theConf.rdInt(cnfSec, "scrollpastend",
                                           self.scrollPastEnd)
        self.autoScroll = theConf.rdBool(cnfSec, "autoscroll", self.autoScroll)
        self.autoScrollPos = theConf.rdInt(cnfSec, "autoscrollpos",
                                           self.autoScrollPos)
        self.fmtSingleQuotes = theConf.rdStrList(cnfSec, "fmtsinglequote",
                                                 self.fmtSingleQuotes)
        self.fmtDoubleQuotes = theConf.rdStrList(cnfSec, "fmtdoublequote",
                                                 self.fmtDoubleQuotes)
        self.fmtPadBefore = theConf.rdStr(cnfSec, "fmtpadbefore",
                                          self.fmtPadBefore)
        self.fmtPadAfter = theConf.rdStr(cnfSec, "fmtpadafter",
                                         self.fmtPadAfter)
        self.fmtPadThin = theConf.rdBool(cnfSec, "fmtpadthin", self.fmtPadThin)
        self.spellLanguage = theConf.rdStr(cnfSec, "spellcheck",
                                           self.spellLanguage)
        self.showTabsNSpaces = theConf.rdBool(cnfSec, "showtabsnspaces",
                                              self.showTabsNSpaces)
        self.showLineEndings = theConf.rdBool(cnfSec, "showlineendings",
                                              self.showLineEndings)
        self.showMultiSpaces = theConf.rdBool(cnfSec, "showmultispaces",
                                              self.showMultiSpaces)
        self.wordCountTimer = theConf.rdFlt(cnfSec, "wordcounttimer",
                                            self.wordCountTimer)
        self.bigDocLimit = theConf.rdInt(cnfSec, "bigdoclimit",
                                         self.bigDocLimit)
        self.incNotesWCount = theConf.rdBool(cnfSec, "incnoteswcount",
                                             self.incNotesWCount)
        self.showFullPath = theConf.rdBool(cnfSec, "showfullpath",
                                           self.showFullPath)
        self.highlightQuotes = theConf.rdBool(cnfSec, "highlightquotes",
                                              self.highlightQuotes)
        self.allowOpenSQuote = theConf.rdBool(cnfSec, "allowopensquote",
                                              self.allowOpenSQuote)
        self.allowOpenDQuote = theConf.rdBool(cnfSec, "allowopendquote",
                                              self.allowOpenDQuote)
        self.highlightEmph = theConf.rdBool(cnfSec, "highlightemph",
                                            self.highlightEmph)
        self.stopWhenIdle = theConf.rdBool(cnfSec, "stopwhenidle",
                                           self.stopWhenIdle)
        self.userIdleTime = theConf.rdInt(cnfSec, "useridletime",
                                          self.userIdleTime)

        # Backup
        cnfSec = "Backup"
        self.backupPath = theConf.rdStr(cnfSec, "backuppath", self.backupPath)
        self.backupOnClose = theConf.rdBool(cnfSec, "backuponclose",
                                            self.backupOnClose)
        self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup",
                                              self.askBeforeBackup)

        # State
        cnfSec = "State"
        self.showRefPanel = theConf.rdBool(cnfSec, "showrefpanel",
                                           self.showRefPanel)
        self.viewComments = theConf.rdBool(cnfSec, "viewcomments",
                                           self.viewComments)
        self.viewSynopsis = theConf.rdBool(cnfSec, "viewsynopsis",
                                           self.viewSynopsis)
        self.searchCase = theConf.rdBool(cnfSec, "searchcase", self.searchCase)
        self.searchWord = theConf.rdBool(cnfSec, "searchword", self.searchWord)
        self.searchRegEx = theConf.rdBool(cnfSec, "searchregex",
                                          self.searchRegEx)
        self.searchLoop = theConf.rdBool(cnfSec, "searchloop", self.searchLoop)
        self.searchNextFile = theConf.rdBool(cnfSec, "searchnextfile",
                                             self.searchNextFile)
        self.searchMatchCap = theConf.rdBool(cnfSec, "searchmatchcap",
                                             self.searchMatchCap)

        # Path
        cnfSec = "Path"
        self.lastPath = theConf.rdStr(cnfSec, "lastpath", self.lastPath)

        # Check Certain Values for None
        self.spellLanguage = self._checkNone(self.spellLanguage)

        # If we're using straight quotes, disable auto-replace
        if self.fmtSingleQuotes == ["'", "'"] and self.doReplaceSQuote:
            logger.info(
                "Using straight single quotes, so disabling auto-replace")
            self.doReplaceSQuote = False

        if self.fmtDoubleQuotes == ['"', '"'] and self.doReplaceDQuote:
            logger.info(
                "Using straight double quotes, so disabling auto-replace")
            self.doReplaceDQuote = False

        # Check deprecated settings
        if self.guiIcons in ("typicons_colour_dark", "typicons_grey_dark"):
            self.guiIcons = "typicons_dark"
        elif self.guiIcons in ("typicons_colour_light", "typicons_grey_light"):
            self.guiIcons = "typicons_light"

        return True
Exemple #8
0
    def initConfig(self, confPath=None, dataPath=None):
        """Initialise the config class. The manual setting of confPath
        and dataPath is mainly intended for the test suite.
        """
        logger.debug("Initialising Config ...")
        if confPath is None:
            confRoot = QStandardPaths.writableLocation(
                QStandardPaths.ConfigLocation)
            self.confPath = os.path.join(os.path.abspath(confRoot),
                                         self.appHandle)
        else:
            logger.info("Setting config from alternative path: %s", confPath)
            self.confPath = confPath

        if dataPath is None:
            if self.verQtValue >= 50400:
                dataRoot = QStandardPaths.writableLocation(
                    QStandardPaths.AppDataLocation)
            else:
                dataRoot = QStandardPaths.writableLocation(
                    QStandardPaths.DataLocation)
            self.dataPath = os.path.join(os.path.abspath(dataRoot),
                                         self.appHandle)
        else:
            logger.info("Setting data path from alternative path: %s",
                        dataPath)
            self.dataPath = dataPath

        logger.verbose("Config path: %s", self.confPath)
        logger.verbose("Data path: %s", self.dataPath)

        # Check Data Path Subdirs
        dataDirs = ["syntax", "themes"]
        for dataDir in dataDirs:
            dirPath = os.path.join(self.dataPath, dataDir)
            if not os.path.isdir(dirPath):
                try:
                    os.mkdir(dirPath)
                    logger.info("Created folder: %s", dirPath)
                except Exception:
                    logger.error("Could not create folder: %s", dirPath)
                    logException()

        self.confFile = self.appHandle + ".conf"
        self.lastPath = os.path.expanduser("~")
        self.appPath = getattr(sys, "_MEIPASS",
                               os.path.abspath(os.path.dirname(__file__)))
        self.appRoot = os.path.abspath(
            os.path.join(self.appPath, os.path.pardir))

        if os.path.isfile(self.appRoot):
            # novelWriter is packaged as a single file, so the app and
            # root paths are the same, and equal to the folder that
            # contains the single executable.
            self.appRoot = os.path.dirname(self.appRoot)
            self.appPath = self.appRoot

        # Assets
        self.assetPath = os.path.join(self.appPath, "assets")
        self.appIcon = os.path.join(self.assetPath, "icons", "novelwriter.svg")

        # Internationalisation
        self.nwLangPath = os.path.join(self.assetPath, "i18n")

        logger.debug("Assets: %s", self.assetPath)
        logger.verbose("App path: %s", self.appPath)
        logger.verbose("Last path: %s", self.lastPath)

        # If the config folder does not exist, create it.
        # This assumes that the os config folder itself exists.
        if not os.path.isdir(self.confPath):
            try:
                os.mkdir(self.confPath)
            except Exception as exc:
                logger.error("Could not create folder: %s", self.confPath)
                logException()
                self.hasError = True
                self.errData.append("Could not create folder: %s" %
                                    self.confPath)
                self.errData.append(formatException(exc))
                self.confPath = None

        # Check if config file exists
        if self.confPath is not None:
            if os.path.isfile(os.path.join(self.confPath, self.confFile)):
                # If it exists, load it
                self.loadConfig()
            else:
                # If it does not exist, save a copy of the default values
                self.saveConfig()

        # If the data folder does not exist, create it.
        # This assumes that the os data folder itself exists.
        if self.dataPath is not None:
            if not os.path.isdir(self.dataPath):
                try:
                    os.mkdir(self.dataPath)
                except Exception as exc:
                    logger.error("Could not create folder: %s", self.dataPath)
                    logException()
                    self.hasError = True
                    self.errData.append("Could not create folder: %s" %
                                        self.dataPath)
                    self.errData.append(formatException(exc))
                    self.dataPath = None

        # Host and Kernel
        if self.verQtValue >= 50600:
            self.hostName = QSysInfo.machineHostName()
            self.kernelVer = QSysInfo.kernelVersion()

        # Load recent projects cache
        self.loadRecentCache()

        # Check the availability of optional packages
        self._checkOptionalPackages()

        if self.spellLanguage is None:
            self.spellLanguage = "en"

        # Look for a PDF version of the manual
        pdfDocs = os.path.join(self.assetPath, "manual.pdf")
        if os.path.isfile(pdfDocs):
            logger.debug("Found manual: %s", pdfDocs)
            self.pdfDocs = pdfDocs

        logger.debug("Config initialisation complete")

        return True
Exemple #9
0
    def _saveDocument(self, theFmt):
        """Save the document to various formats.
        """
        replaceTabs = self.replaceTabs.isChecked()

        fileExt = ""
        textFmt = ""

        # Settings
        # ========

        if theFmt == self.FMT_ODT:
            fileExt = "odt"
            textFmt = self.tr("Open Document")

        elif theFmt == self.FMT_FODT:
            fileExt = "fodt"
            textFmt = self.tr("Flat Open Document")

        elif theFmt == self.FMT_HTM:
            fileExt = "htm"
            textFmt = self.tr("Plain HTML")

        elif theFmt == self.FMT_NWD:
            fileExt = "nwd"
            textFmt = self.tr("novelWriter Markdown")

        elif theFmt == self.FMT_MD:
            fileExt = "md"
            textFmt = self.tr("Standard Markdown")

        elif theFmt == self.FMT_GH:
            fileExt = "md"
            textFmt = self.tr("GitHub Markdown")

        elif theFmt == self.FMT_JSON_H:
            fileExt = "json"
            textFmt = self.tr("JSON + novelWriter HTML")

        elif theFmt == self.FMT_JSON_M:
            fileExt = "json"
            textFmt = self.tr("JSON + novelWriter Markdown")

        elif theFmt == self.FMT_PDF:
            fileExt = "pdf"
            textFmt = self.tr("PDF")

        else:
            return False

        # Generate File Name
        # ==================

        cleanName = makeFileNameSafe(self.theProject.projName)
        fileName = "%s.%s" % (cleanName, fileExt)
        saveDir = self.mainConf.lastPath
        if not os.path.isdir(saveDir):
            saveDir = os.path.expanduser("~")

        savePath = os.path.join(saveDir, fileName)
        savePath, _ = QFileDialog.getSaveFileName(
            self, self.tr("Save Document As"), savePath
        )
        if not savePath:
            return False

        self.mainConf.setLastPath(savePath)

        # Build and Write
        # ===============

        errMsg = ""
        wSuccess = False

        if theFmt == self.FMT_ODT:
            makeOdt = ToOdt(self.theProject, isFlat=False)
            self._doBuild(makeOdt)
            try:
                makeOdt.saveOpenDocText(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_FODT:
            makeOdt = ToOdt(self.theProject, isFlat=True)
            self._doBuild(makeOdt)
            try:
                makeOdt.saveFlatXML(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_HTM:
            makeHtml = ToHtml(self.theProject)
            self._doBuild(makeHtml)
            if replaceTabs:
                makeHtml.replaceTabs()

            try:
                makeHtml.saveHTML5(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_NWD:
            makeNwd = ToMarkdown(self.theProject)
            makeNwd.setKeepMarkdown(True)
            self._doBuild(makeNwd, doConvert=False)
            if replaceTabs:
                makeNwd.replaceTabs(spaceChar=" ")

            try:
                makeNwd.saveRawMarkdown(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt in (self.FMT_MD, self.FMT_GH):
            makeMd = ToMarkdown(self.theProject)
            if theFmt == self.FMT_GH:
                makeMd.setGitHubMarkdown()
            else:
                makeMd.setStandardMarkdown()

            self._doBuild(makeMd)
            if replaceTabs:
                makeMd.replaceTabs(nSpaces=4, spaceChar=" ")

            try:
                makeMd.saveMarkdown(savePath)
                wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_JSON_H or theFmt == self.FMT_JSON_M:
            jsonData = {
                "meta": {
                    "workingTitle": self.theProject.projName,
                    "novelTitle": self.theProject.bookTitle,
                    "authors": self.theProject.bookAuthors,
                    "buildTime": self.buildTime,
                }
            }

            if theFmt == self.FMT_JSON_H:
                makeHtml = ToHtml(self.theProject)
                self._doBuild(makeHtml)
                if replaceTabs:
                    makeHtml.replaceTabs()

                theBody = []
                for htmlPage in makeHtml.fullHTML:
                    theBody.append(htmlPage.rstrip("\n").split("\n"))
                jsonData["text"] = {
                    "css": self.htmlStyle,
                    "html": theBody,
                }

            elif theFmt == self.FMT_JSON_M:
                makeMd = ToHtml(self.theProject)
                makeMd.setKeepMarkdown(True)
                self._doBuild(makeMd, doConvert=False)
                if replaceTabs:
                    makeMd.replaceTabs(spaceChar=" ")

                theBody = []
                for nwdPage in makeMd.theMarkdown:
                    theBody.append(nwdPage.split("\n"))
                jsonData["text"] = {
                    "nwd": theBody,
                }

            try:
                with open(savePath, mode="w", encoding="utf-8") as outFile:
                    outFile.write(json.dumps(jsonData, indent=2))
                    wSuccess = True
            except Exception as exc:
                errMsg = formatException(exc)

        elif theFmt == self.FMT_PDF:
            try:
                thePrinter = QPrinter()
                thePrinter.setOutputFormat(QPrinter.PdfFormat)
                thePrinter.setOrientation(QPrinter.Portrait)
                thePrinter.setDuplex(QPrinter.DuplexLongSide)
                thePrinter.setFontEmbeddingEnabled(True)
                thePrinter.setColorMode(QPrinter.Color)
                thePrinter.setOutputFileName(savePath)
                self.docView.document().print(thePrinter)
                wSuccess = True

            except Exception as exc:
                errMsg = formatException(exc)

        else:
            # If the if statements above and here match, it should not
            # be possible to reach this else statement.
            return False  # pragma: no cover

        # Report to User
        # ==============

        if wSuccess:
            self.mainGui.makeAlert([
                self.tr("{0} file successfully written to:").format(textFmt), savePath
            ], nwAlert.INFO)
        else:
            self.mainGui.makeAlert(self.tr(
                "Failed to write {0} file. {1}"
            ).format(textFmt, errMsg), nwAlert.ERROR)

        return wSuccess
Exemple #10
0
    def _saveData(self, dataFmt):
        """Save the content of the list box to a file.
        """
        fileExt = ""
        textFmt = ""

        if dataFmt == self.FMT_JSON:
            fileExt = "json"
            textFmt = self.tr("JSON Data File")
        elif dataFmt == self.FMT_CSV:
            fileExt = "csv"
            textFmt = self.tr("CSV Data File")
        else:
            return False

        # Generate the file name
        saveDir = self.mainConf.lastPath
        if not os.path.isdir(saveDir):
            saveDir = os.path.expanduser("~")

        fileName = "sessionStats.%s" % fileExt
        savePath = os.path.join(saveDir, fileName)

        savePath, _ = QFileDialog.getSaveFileName(
            self, self.tr("Save Data As"), savePath,
            "%s (*.%s)" % (textFmt, fileExt))
        if not savePath:
            return False

        self.mainConf.setLastPath(savePath)

        # Do the actual writing
        wSuccess = False
        errMsg = ""

        try:
            with open(savePath, mode="w", encoding="utf-8") as outFile:
                if dataFmt == self.FMT_JSON:
                    jsonData = []
                    for _, sD, tT, wD, wA, wB, tI in self.filterData:
                        jsonData.append({
                            "date": sD,
                            "length": tT,
                            "newWords": wD,
                            "novelWords": wA,
                            "noteWords": wB,
                            "idleTime": tI,
                        })
                    json.dump(jsonData, outFile, indent=2)
                    wSuccess = True

                if dataFmt == self.FMT_CSV:
                    outFile.write(
                        '"Date","Length (sec)","Words Changed",'
                        '"Novel Words","Note Words","Idle Time (sec)"\n')
                    for _, sD, tT, wD, wA, wB, tI in self.filterData:
                        outFile.write(f'"{sD}",{tT:.0f},{wD},{wA},{wB},{tI}\n')
                    wSuccess = True

        except Exception as exc:
            errMsg = formatException(exc)
            wSuccess = False

        # Report to user
        if wSuccess:
            self.theParent.makeAlert([
                self.tr("{0} file successfully written to:").format(textFmt),
                savePath
            ], nwAlert.INFO)
        else:
            self.theParent.makeAlert(
                [self.tr("Failed to write {0} file.").format(textFmt), errMsg],
                nwAlert.ERROR)

        return wSuccess