Пример #1
0
    def loadSettings(self):
        """Load the options dictionary from the project settings file.
        """
        if self.theProject.projMeta is None:
            return False

        stateFile = os.path.join(self.theProject.projMeta, nwFiles.OPTS_FILE)
        theState = {}

        if os.path.isfile(stateFile):
            logger.debug("Loading GUI options file")
            try:
                with open(stateFile, mode="r", encoding="utf8") as inFile:
                    theState = json.load(inFile)
            except Exception:
                logger.error("Failed to load GUI options file")
                nw.logException()
                return False

        # Filter out unused variables
        for aGroup in theState:
            if aGroup in self.validMap:
                self.theState[aGroup] = {}
                for anOpt in theState[aGroup]:
                    if anOpt in self.validMap[aGroup]:
                        self.theState[aGroup][anOpt] = theState[aGroup][anOpt]

        return True
Пример #2
0
    def setLanguage(self, theLang, projectDict=None):
        """Load a dictionary as a list from the app assets folder.
        """
        self.theLang = theLang
        self.theWords = set()
        dictFile = os.path.join(self.mainConf.dictPath, theLang + ".dict")
        try:
            with open(dictFile, mode="r", encoding="utf-8") as wordsFile:
                for theLine in wordsFile:
                    if len(theLine) == 0 or theLine.startswith("#"):
                        continue
                    self.theWords.add(theLine.strip().lower())

            logger.debug("Spell check dictionary for language %s loaded" %
                         theLang)
            logger.debug("Dictionary contains %d words" % len(self.theWords))
            self.spellLanguage = theLang

        except Exception:
            logger.error(
                "Failed to load spell check word list for language %s" %
                theLang)
            nw.logException()
            self.spellLanguage = None

        self._readProjectDictionary(projectDict)
        for pWord in self.projDict:
            self.theWords.add(pWord)

        return
Пример #3
0
    def loadIndex(self):
        """Load index from last session from the project meta folder.
        """
        theData   = {}
        indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)

        if os.path.isfile(indexFile):
            logger.debug("Loading index file")
            try:
                with open(indexFile, mode="r", encoding="utf8") as inFile:
                    theData = json.load(inFile)
            except Exception:
                logger.error("Failed to load index file")
                nw.logException()
                self.indexBroken = True
                return False

            self._tagIndex   = theData.get("tagIndex", {})
            self._refIndex   = theData.get("refIndex", {})
            self._novelIndex = theData.get("novelIndex", {})
            self._noteIndex  = theData.get("noteIndex", {})
            self._textCounts = theData.get("textCounts", {})

            nowTime = round(time())
            self._timeNovel = nowTime
            self._timeNotes = nowTime
            self._timeIndex = nowTime

        self.checkIndex()

        return True
Пример #4
0
    def _loadCache(self):
        """Save the current data to cache.
        """
        buildCache = os.path.join(self.theProject.projCache,
                                  nwFiles.BUILD_CACHE)
        dataCount = 0
        if os.path.isfile(buildCache):

            logger.debug("Loading build cache")
            try:
                with open(buildCache, mode="r", encoding="utf8") as inFile:
                    theJson = inFile.read()
                theData = json.loads(theJson)
            except Exception:
                logger.error("Failed to load build cache")
                nw.logException()
                return False

            if "buildTime" in theData.keys():
                self.buildTime = theData["buildTime"]
            if "htmlStyle" in theData.keys():
                self.htmlStyle = theData["htmlStyle"]
                dataCount += 1
            if "htmlText" in theData.keys():
                self.htmlText = theData["htmlText"]
                dataCount += 1

        return dataCount == 2
Пример #5
0
    def checkIndex(self):
        """Check that the entries in the index are valid and contain the
        elements it should.
        """
        logger.debug("Checking index")
        tStart = time()

        try:
            self._checkTagIndex()
            self._checkRefIndex()
            self._checkNovelNoteIndex("novelIndex")
            self._checkNovelNoteIndex("noteIndex")
            self._checkTextCounts()
            self.indexBroken = False

        except Exception:
            logger.error("Error while checking index")
            nw.logException()
            self.indexBroken = True

        tEnd = time()
        logger.debug("Index check took %.3f ms" % ((tEnd - tStart)*1000))
        logger.debug("Index check complete")

        if self.indexBroken:
            self.clearIndex()

        return
Пример #6
0
    def _readProjectDictionary(self, projectDict):
        """Read the content of the project dictionary, and add it to the
        lookup lists.
        """
        self.projDict = []
        self.projectDict = projectDict

        if projectDict is None:
            return False

        if not os.path.isfile(projectDict):
            return False

        try:
            logger.debug("Loading project word list")
            with open(projectDict, mode="r", encoding="utf-8") as wordsFile:
                for theLine in wordsFile:
                    theLine = theLine.strip()
                    if len(theLine) > 0 and theLine not in self.projDict:
                        self.projDict.append(theLine)
            logger.debug("Project word list contains %d words" %
                         len(self.projDict))

        except Exception:
            logger.error("Failed to load project word list")
            nw.logException()
            return False

        return True
Пример #7
0
    def _doSave(self):
        """Save the new word list and close.
        """
        self._saveGuiSettings()

        dctFile = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
        tmpFile = dctFile + "~"

        try:
            with open(tmpFile, mode="w", encoding="utf8") as outFile:
                for i in range(self.listBox.count()):
                    outFile.write(self.listBox.item(i).text() + "\n")

        except Exception:
            logger.error("Could not save new word list")
            nw.logException()
            self.reject()
            return False

        if os.path.isfile(dctFile):
            os.unlink(dctFile)
        os.rename(tmpFile, dctFile)
        self.accept()

        return True
Пример #8
0
    def loadSyntax(self):
        """Load the currently specified syntax highlighter theme.
        """
        logger.debug("Loading syntax theme files")

        confParser = configparser.ConfigParser()
        try:
            with open(self.syntaxFile, mode="r", encoding="utf8") as inFile:
                confParser.read_file(inFile)
        except Exception:
            logger.error("Could not load syntax colours from: %s" %
                         self.syntaxFile)
            nw.logException()
            return False

        ## Main
        cnfSec = "Main"
        if confParser.has_section(cnfSec):
            self.syntaxName = self._parseLine(confParser, cnfSec, "name", "")
            self.syntaxDescription = self._parseLine(confParser, cnfSec,
                                                     "description", "")
            self.syntaxAuthor = self._parseLine(confParser, cnfSec, "author",
                                                "")
            self.syntaxCredit = self._parseLine(confParser, cnfSec, "credit",
                                                "")
            self.syntaxUrl = self._parseLine(confParser, cnfSec, "url", "")
            self.syntaxLicense = self._parseLine(confParser, cnfSec, "license",
                                                 "")
            self.syntaxLicenseUrl = self._parseLine(confParser, cnfSec,
                                                    "licenseurl", "")

        ## Syntax
        cnfSec = "Syntax"
        if confParser.has_section(cnfSec):
            self.colBack = self._loadColour(confParser, cnfSec, "background")
            self.colText = self._loadColour(confParser, cnfSec, "text")
            self.colLink = self._loadColour(confParser, cnfSec, "link")
            self.colHead = self._loadColour(confParser, cnfSec, "headertext")
            self.colHeadH = self._loadColour(confParser, cnfSec, "headertag")
            self.colEmph = self._loadColour(confParser, cnfSec, "emphasis")
            self.colDialN = self._loadColour(confParser, cnfSec,
                                             "straightquotes")
            self.colDialD = self._loadColour(confParser, cnfSec,
                                             "doublequotes")
            self.colDialS = self._loadColour(confParser, cnfSec,
                                             "singlequotes")
            self.colHidden = self._loadColour(confParser, cnfSec, "hidden")
            self.colKey = self._loadColour(confParser, cnfSec, "keyword")
            self.colVal = self._loadColour(confParser, cnfSec, "value")
            self.colSpell = self._loadColour(confParser, cnfSec,
                                             "spellcheckline")
            self.colTagErr = self._loadColour(confParser, cnfSec, "tagerror")
            self.colRepTag = self._loadColour(confParser, cnfSec, "replacetag")
            self.colMod = self._loadColour(confParser, cnfSec, "modifier")

        logger.info("Loaded syntax theme '%s'" % self.guiSyntax)

        return True
Пример #9
0
    def describeDict(self):
        """Return the tag and provider of the currently loaded
        dictionary.
        """
        try:
            spTag = self.theDict.tag
            spName = self.theDict.provider.name
        except Exception:
            logger.error("Failed to extract information about the dictionary")
            nw.logException()
            spTag = ""
            spName = ""

        return spTag, spName
Пример #10
0
 def addWord(self, newWord):
     """Add a word to the project dictionary.
     """
     if self.projectDict is not None and newWord not in self.projDict:
         newWord = newWord.strip()
         try:
             with open(self.projectDict, mode="a+",
                       encoding="utf-8") as outFile:
                 outFile.write("%s\n" % newWord)
             self.projDict.append(newWord)
         except Exception:
             logger.error("Failed to add word to project word list %s" %
                          str(self.projectDict))
             nw.logException()
             return False
         return True
     return False
Пример #11
0
    def saveSettings(self):
        """Save the options dictionary to the project settings file.
        """
        if self.theProject.projMeta is None:
            return False

        stateFile = os.path.join(self.theProject.projMeta, nwFiles.OPTS_FILE)
        logger.debug("Saving GUI options file")

        try:
            with open(stateFile, mode="w+", encoding="utf8") as outFile:
                json.dump(self.theState, outFile, indent=2)
        except Exception:
            logger.error("Failed to save GUI options file")
            nw.logException()
            return False

        return True
Пример #12
0
    def writeToCFile(self):
        """Write the convenience table of contents file in the root of
        the project directory.
        """
        tocList = []
        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))

        try:
            # Dump the text
            tocText = os.path.join(self.theProject.projPath, nwFiles.TOC_TXT)
            with open(tocText, mode="w", encoding="utf8") as outFile:
                outFile.write("\n")
                outFile.write("Table of Contents\n")
                outFile.write("=================\n")
                outFile.write("\n")
                outFile.write(
                    "%-25s  %-9s  %-10s  %s\n" %
                    ("File Name", "Class", "Layout", "Document Label"))
                outFile.write("-" * tocLen + "\n")
                outFile.write("\n".join(tocList))
                outFile.write("\n")

        except Exception:
            logger.error("Could not write ToC file")
            nw.logException()
            return False

        return True
Пример #13
0
    def saveIndex(self):
        """Save the current index as a json file in the project meta
        data folder.
        """
        logger.debug("Saving index file")
        indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)

        try:
            with open(indexFile, mode="w+", encoding="utf8") as outFile:
                json.dump({
                    "tagIndex"   : self._tagIndex,
                    "refIndex"   : self._refIndex,
                    "novelIndex" : self._novelIndex,
                    "noteIndex"  : self._noteIndex,
                    "textCounts" : self._textCounts,
                }, outFile, indent=2)
        except Exception:
            logger.error("Failed to save index file")
            nw.logException()
            return False

        return True
Пример #14
0
    def _saveCache(self):
        """Save the current data to cache.
        """
        buildCache = os.path.join(self.theProject.projCache,
                                  nwFiles.BUILD_CACHE)

        logger.debug("Saving build cache")
        try:
            with open(buildCache, mode="w+", encoding="utf8") as outFile:
                outFile.write(
                    json.dumps(
                        {
                            "buildTime": self.buildTime,
                            "htmlStyle": self.htmlStyle,
                            "htmlText": self.htmlText,
                        },
                        indent=2))
        except Exception:
            logger.error("Failed to save build cache")
            nw.logException()
            return False

        return True
Пример #15
0
    def updateTheme(self):
        """Update the theme map. This is more of an init, since many of
        the GUI icons cannot really be replaced without writing specific
        update functions for the classes where they're used.
        """
        logger.debug("Loading icon theme files")

        self.themeMap = {}
        checkPath = os.path.join(self.mainConf.iconPath,
                                 self.mainConf.guiIcons)
        if os.path.isdir(checkPath):
            logger.debug("Loading icon theme '%s'" % self.mainConf.guiIcons)
            self.iconPath = checkPath
            self.confFile = os.path.join(checkPath, self.confName)
        else:
            return False

        # Config File
        confParser = configparser.ConfigParser()
        try:
            with open(self.confFile, mode="r", encoding="utf8") as inFile:
                confParser.read_file(inFile)
        except Exception:
            logger.error("Could not load icon theme settings from: %s" %
                         self.confFile)
            nw.logException()
            return False

        ## Main
        cnfSec = "Main"
        if confParser.has_section(cnfSec):
            self.themeName = self._parseLine(confParser, cnfSec, "name", "")
            self.themeDescription = self._parseLine(confParser, cnfSec,
                                                    "description", "")
            self.themeAuthor = self._parseLine(confParser, cnfSec, "author",
                                               "N/A")
            self.themeCredit = self._parseLine(confParser, cnfSec, "credit",
                                               "N/A")
            self.themeUrl = self._parseLine(confParser, cnfSec, "url", "")
            self.themeLicense = self._parseLine(confParser, cnfSec, "license",
                                                "N/A")
            self.themeLicenseUrl = self._parseLine(confParser, cnfSec,
                                                   "licenseurl", "")

        ## Palette
        cnfSec = "Map"
        if confParser.has_section(cnfSec):
            for iconName, iconFile in confParser.items(cnfSec):
                if iconName not in self.ICON_MAP:
                    logger.error("Unknown icon name '%s' in config file" %
                                 iconName)
                else:
                    iconPath = os.path.join(self.iconPath, iconFile)
                    if os.path.isfile(iconPath):
                        self.themeMap[iconName] = iconPath
                        logger.verbose("Icon slot '%s' using file '%s'" %
                                       (iconName, iconFile))
                    else:
                        logger.error("Icon file '%s' not in theme folder" %
                                     iconFile)

        logger.info("Loaded icon theme '%s'" % self.mainConf.guiIcons)

        return True
Пример #16
0
    def loadTheme(self):
        """Load the currently specified GUI theme.
        """
        logger.debug("Loading theme files")
        logger.debug("System icon theme is '%s'" % str(QIcon.themeName()))

        # CSS File
        cssData = ""
        try:
            if os.path.isfile(self.cssFile):
                with open(self.cssFile, mode="r", encoding="utf8") as inFile:
                    cssData = inFile.read()
        except Exception:
            logger.error("Could not load theme css file")
            nw.logException()
            return False

        # Config File
        confParser = configparser.ConfigParser()
        try:
            with open(self.confFile, mode="r", encoding="utf8") as inFile:
                confParser.read_file(inFile)
        except Exception:
            logger.error("Could not load theme settings from: %s" %
                         self.confFile)
            nw.logException()
            return False

        ## Main
        cnfSec = "Main"
        if confParser.has_section(cnfSec):
            self.themeName = self._parseLine(confParser, cnfSec, "name", "")
            self.themeDescription = self._parseLine(confParser, cnfSec,
                                                    "description", "N/A")
            self.themeAuthor = self._parseLine(confParser, cnfSec, "author",
                                               "N/A")
            self.themeCredit = self._parseLine(confParser, cnfSec, "credit",
                                               "N/A")
            self.themeUrl = self._parseLine(confParser, cnfSec, "url", "")
            self.themeLicense = self._parseLine(confParser, cnfSec, "license",
                                                "N/A")
            self.themeLicenseUrl = self._parseLine(confParser, cnfSec,
                                                   "licenseurl", "")

        ## Palette
        cnfSec = "Palette"
        if confParser.has_section(cnfSec):
            self._setPalette(confParser, cnfSec, "window", QPalette.Window)
            self._setPalette(confParser, cnfSec, "windowtext",
                             QPalette.WindowText)
            self._setPalette(confParser, cnfSec, "base", QPalette.Base)
            self._setPalette(confParser, cnfSec, "alternatebase",
                             QPalette.AlternateBase)
            self._setPalette(confParser, cnfSec, "text", QPalette.Text)
            self._setPalette(confParser, cnfSec, "tooltipbase",
                             QPalette.ToolTipBase)
            self._setPalette(confParser, cnfSec, "tooltiptext",
                             QPalette.ToolTipText)
            self._setPalette(confParser, cnfSec, "button", QPalette.Button)
            self._setPalette(confParser, cnfSec, "buttontext",
                             QPalette.ButtonText)
            self._setPalette(confParser, cnfSec, "brighttext",
                             QPalette.BrightText)
            self._setPalette(confParser, cnfSec, "highlight",
                             QPalette.Highlight)
            self._setPalette(confParser, cnfSec, "highlightedtext",
                             QPalette.HighlightedText)
            self._setPalette(confParser, cnfSec, "link", QPalette.Link)
            self._setPalette(confParser, cnfSec, "linkvisited",
                             QPalette.LinkVisited)

        ## GUI
        cnfSec = "GUI"
        if confParser.has_section(cnfSec):
            self.statNone = self._loadColour(confParser, cnfSec, "statusnone")
            self.statUnsaved = self._loadColour(confParser, cnfSec,
                                                "statusunsaved")
            self.statSaved = self._loadColour(confParser, cnfSec,
                                              "statussaved")

        # Apply Styles
        qApp.setStyleSheet(cssData)
        qApp.setPalette(self.guiPalette)

        logger.info("Loaded theme '%s'" % self.guiTheme)

        return True
Пример #17
0
    def _doBuild(self, bldObj, isPreview=False, doConvert=True):
        """Rund the build with a specific build object.
        """
        tStart = int(time())

        # Get Settings
        fmtTitle = self.fmtTitle.text().strip()
        fmtChapter = self.fmtChapter.text().strip()
        fmtUnnumbered = self.fmtUnnumbered.text().strip()
        fmtScene = self.fmtScene.text().strip()
        fmtSection = self.fmtSection.text().strip()
        textFont = self.textFont.text()
        textSize = self.textSize.value()
        lineHeight = self.lineHeight.value()
        justifyText = self.justifyText.isChecked()
        noStyling = self.noStyling.isChecked()
        incSynopsis = self.includeSynopsis.isChecked()
        incComments = self.includeComments.isChecked()
        incKeywords = self.includeKeywords.isChecked()
        novelFiles = self.novelFiles.isChecked()
        noteFiles = self.noteFiles.isChecked()
        ignoreFlag = self.ignoreFlag.isChecked()
        includeBody = self.includeBody.isChecked()
        replaceUCode = self.replaceUCode.isChecked()

        # The language lookup dict is reloaded if needed
        self.theProject.setProjectLang(self.buildLang.currentData())

        # Get font information
        fontInfo = QFontInfo(QFont(textFont, textSize))
        textFixed = fontInfo.fixedPitch()

        isHtml = isinstance(bldObj, ToHtml)
        isOdt = isinstance(bldObj, ToOdt)

        bldObj.setTitleFormat(fmtTitle)
        bldObj.setChapterFormat(fmtChapter)
        bldObj.setUnNumberedFormat(fmtUnnumbered)
        bldObj.setSceneFormat(fmtScene, fmtScene == "")
        bldObj.setSectionFormat(fmtSection, fmtSection == "")

        bldObj.setFont(textFont, textSize, textFixed)
        bldObj.setJustify(justifyText)
        bldObj.setLineHeight(lineHeight)

        bldObj.setSynopsis(incSynopsis)
        bldObj.setComments(incComments)
        bldObj.setKeywords(incKeywords)
        bldObj.setBodyText(includeBody)

        if isHtml:
            bldObj.setStyles(not noStyling)
            bldObj.setReplaceUnicode(replaceUCode)

        if isOdt:
            bldObj.setColourHeaders(not noStyling)
            bldObj.initDocument()

        # Make sure the project and document is up to date
        self.theParent.treeView.flushTreeOrder()
        self.theParent.saveDocument()

        self.buildProgress.setMaximum(len(self.theProject.projTree))
        self.buildProgress.setValue(0)

        for nItt, tItem in enumerate(self.theProject.projTree):

            noteRoot = noteFiles
            noteRoot &= tItem.itemType == nwItemType.ROOT
            noteRoot &= tItem.itemClass != nwItemClass.NOVEL
            noteRoot &= tItem.itemClass != nwItemClass.ARCHIVE

            try:
                if noteRoot:
                    # Add headers for root folders of notes
                    bldObj.addRootHeading(tItem.itemHandle)
                    if doConvert:
                        bldObj.doConvert()

                elif self._checkInclude(tItem, noteFiles, novelFiles,
                                        ignoreFlag):
                    bldObj.setText(tItem.itemHandle)
                    bldObj.doPreProcessing()
                    bldObj.tokenizeText()
                    bldObj.doHeaders()
                    if doConvert:
                        bldObj.doConvert()
                    bldObj.doPostProcessing()

            except Exception:
                logger.error("Failed to build document '%s'" %
                             tItem.itemHandle)
                nw.logException()
                if isPreview:
                    self.docView.setText(
                        ("Failed to generate preview. "
                         "Document with title '%s' could not be parsed.") %
                        tItem.itemName)

                return False

            # Update progress bar, also for skipped items
            self.buildProgress.setValue(nItt + 1)

        if isOdt:
            bldObj.closeDocument()

        tEnd = int(time())
        logger.debug("Built project in %.3f ms" % (1000 * (tEnd - tStart)))

        if bldObj.errData:
            self.theParent.makeAlert(
                "%s:<br>-&nbsp;%s" %
                (self.tr("There were problems when building the project"),
                 "<br>-&nbsp;".join(bldObj.errData)), nwAlert.ERROR)

        return
Пример #18
0
    def loadText(self, tHandle, updateHistory=True):
        """Load text into the viewer from an item handle.
        """
        tItem = self.theProject.projTree[tHandle]
        if tItem is None:
            logger.warning("Item not found")
            return False

        if tItem.itemType != nwItemType.FILE:
            return False

        logger.debug("Generating preview for item %s" % tHandle)
        qApp.setOverrideCursor(QCursor(Qt.WaitCursor))

        sPos = self.verticalScrollBar().value()
        aDoc = ToHtml(self.theProject, self.theParent)
        aDoc.setPreview(self.mainConf.viewComments, self.mainConf.viewSynopsis)
        aDoc.setLinkHeaders(True)

        # Be extra careful here to prevent crashes when first opening a
        # project as a crash here leaves no way of recovering.
        # See issue #298
        try:
            aDoc.setText(tHandle)
            aDoc.doPreProcessing()
            aDoc.tokenizeText()
            aDoc.doConvert()
            aDoc.doPostProcessing()
        except Exception:
            logger.error(
                "Failed to generate preview for document with handle '%s'" %
                tHandle)
            nw.logException()
            self.setText(
                self.tr("An error occurred while generating the preview."))
            return False

        # Refresh the tab stops
        if self.mainConf.verQtValue >= 51000:
            self.setTabStopDistance(self.mainConf.getTabWidth())
        else:
            self.setTabStopWidth(self.mainConf.getTabWidth())

        # Must be before setHtml
        if updateHistory:
            self.docHistory.append(tHandle)

        self.setHtml(aDoc.theResult.replace("\t", "!!tab!!"))
        self.setDocumentTitle(tHandle)

        # Loop through the text and put back in the tabs. Tabs are removed by
        # the setHtml function, so the ToHtml class puts in a placeholder.
        while self.find("!!tab!!"):
            theCursor = self.textCursor()
            theCursor.insertText("\t")

        if self.theHandle == tHandle:
            self.verticalScrollBar().setValue(sPos)
        self.theHandle = tHandle
        self.theProject.setLastViewed(tHandle)
        self.docHeader.setTitleFromHandle(self.theHandle)
        self.updateDocMargins()

        # Make sure the main GUI knows we changed the content
        self.theParent.viewMeta.refreshReferences(tHandle)

        # Since we change the content while it may still be rendering, we mark
        # the document dirty again to make sure it's re-rendered properly.
        self.redrawText()
        qApp.restoreOverrideCursor()

        return True