def listSyntax(self): """Scan the syntax themes folder and list all themes. """ if self._syntaxList: return self._syntaxList confParser = NWConfigParser() for syntaxKey, syntaxPath in self._availSyntax.items(): logger.verbose("Checking theme syntax for '%s'", syntaxKey) syntaxName = _loadInternalName(confParser, syntaxPath) if syntaxName: self._syntaxList.append((syntaxKey, syntaxName)) self._syntaxList = sorted(self._syntaxList, key=lambda x: x[1]) return self._syntaxList
def listThemes(self): """Scan the GUI themes folder and list all themes. """ if self._themeList: return self._themeList confParser = NWConfigParser() for themeKey, themePath in self._availThemes.items(): logger.verbose("Checking theme config for '%s'", themeKey) themeName = _loadInternalName(confParser, themePath) if themeName: self._themeList.append((themeKey, themeName)) self._themeList = sorted(self._themeList, key=lambda x: x[1]) return self._themeList
def listThemes(self): """Scan the icons themes folder and list all themes. """ if self._themeList: return self._themeList confParser = NWConfigParser() for themeDir in os.listdir(self._iconPath): themePath = os.path.join(self._iconPath, themeDir) if not os.path.isdir(themePath): continue logger.verbose("Checking icon theme config for '%s'", themeDir) themeConf = os.path.join(themePath, self._confName) themeName = _loadInternalName(confParser, themeConf) if themeName: self._themeList.append((themeDir, themeName)) self._themeList = sorted(self._themeList, key=lambda x: x[1]) return self._themeList
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
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
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. """ self._themeMap = {} themePath = self._getThemePath() if themePath is None: logger.warning("No icons loaded") return False self._themePath = themePath themeConf = os.path.join(themePath, self._confName) logger.info("Loading icon theme '%s'", self.mainConf.guiIcons) # Config File confParser = NWConfigParser() try: with open(themeConf, mode="r", encoding="utf-8") as inFile: confParser.read_file(inFile) except Exception: logger.error("Could not load icon theme settings from: %s", themeConf) logException() return False # Main cnfSec = "Main" if confParser.has_section(cnfSec): self.themeName = confParser.rdStr(cnfSec, "name", "") self.themeDescription = confParser.rdStr(cnfSec, "description", "") self.themeAuthor = confParser.rdStr(cnfSec, "author", "N/A") self.themeCredit = confParser.rdStr(cnfSec, "credit", "N/A") self.themeUrl = confParser.rdStr(cnfSec, "url", "") self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A") self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "") # Populate Icon Map cnfSec = "Map" if confParser.has_section(cnfSec): for iconName, iconFile in confParser.items(cnfSec): if iconName not in self.ICON_KEYS: logger.error("Unknown icon name '%s' in config file", iconName) else: iconPath = os.path.join(self._themePath, 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) # Check that icons have been defined logger.debug("Scanning theme icons") for iconKey in self.ICON_KEYS: if iconKey in ("novelwriter", "proj_nwx"): # These are not part of the theme itself continue if iconKey not in self._themeMap: logger.error("No icon file specified for '%s'", iconKey) return True
def loadSyntax(self): """Load the currently specified syntax highlighter theme. """ logger.info("Loading syntax theme '%s'", self.guiSyntax) confParser = NWConfigParser() try: with open(self.syntaxFile, mode="r", encoding="utf-8") as inFile: confParser.read_file(inFile) except Exception: logger.error("Could not load syntax colours from: %s", self.syntaxFile) logException() return False # Main cnfSec = "Main" if confParser.has_section(cnfSec): self.syntaxName = confParser.rdStr(cnfSec, "name", "") self.syntaxDescription = confParser.rdStr(cnfSec, "description", "") self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "") self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "") self.syntaxUrl = confParser.rdStr(cnfSec, "url", "") self.syntaxLicense = confParser.rdStr(cnfSec, "license", "") self.syntaxLicenseUrl = confParser.rdStr(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.colError = self._loadColour(confParser, cnfSec, "errorline") self.colRepTag = self._loadColour(confParser, cnfSec, "replacetag") self.colMod = self._loadColour(confParser, cnfSec, "modifier") return True
def loadTheme(self): """Load the currently specified GUI theme. """ logger.info("Loading GUI theme '%s'", self.guiTheme) # Config File confParser = NWConfigParser() try: with open(self.themeFile, mode="r", encoding="utf-8") as inFile: confParser.read_file(inFile) except Exception: logger.error("Could not load theme settings from: %s", self.themeFile) logException() return False # Main cnfSec = "Main" if confParser.has_section(cnfSec): self.themeName = confParser.rdStr(cnfSec, "name", "") self.themeDescription = confParser.rdStr(cnfSec, "description", "N/A") self.themeAuthor = confParser.rdStr(cnfSec, "author", "N/A") self.themeCredit = confParser.rdStr(cnfSec, "credit", "N/A") self.themeUrl = confParser.rdStr(cnfSec, "url", "") self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A") self.themeLicenseUrl = confParser.rdStr(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") # CSS File cssData = readTextFile(self.cssFile) if cssData: qApp.setStyleSheet(cssData) # Apply Styles qApp.setPalette(self._guiPalette) return True
def testBaseCommon_NWConfigParser(fncDir): """Test the NWConfigParser subclass. """ tstConf = os.path.join(fncDir, "test.cfg") writeFile(tstConf, ("[main]\n" "stropt = value\n" "intopt1 = 42\n" "intopt2 = 42.43\n" "boolopt1 = true\n" "boolopt2 = TRUE\n" "boolopt3 = 1\n" "boolopt4 = 0\n" "list1 = a, b, c\n" "list2 = 17, 18, 19\n" "float1 = 4.2\n")) cfgParser = NWConfigParser() cfgParser.read(tstConf) # Readers # ======= # Read String assert cfgParser.rdStr("main", "stropt", "stuff") == "value" assert cfgParser.rdStr("main", "boolopt1", "stuff") == "true" assert cfgParser.rdStr("main", "intopt1", "stuff") == "42" assert cfgParser.rdStr("nope", "stropt", "stuff") == "stuff" assert cfgParser.rdStr("main", "blabla", "stuff") == "stuff" # Read Boolean assert cfgParser.rdBool("main", "boolopt1", None) is True assert cfgParser.rdBool("main", "boolopt2", None) is True assert cfgParser.rdBool("main", "boolopt3", None) is True assert cfgParser.rdBool("main", "boolopt4", None) is False assert cfgParser.rdBool("main", "intopt1", None) is None assert cfgParser.rdBool("nope", "boolopt1", None) is None assert cfgParser.rdBool("main", "blabla", None) is None # Read Integer assert cfgParser.rdInt("main", "intopt1", 13) == 42 assert cfgParser.rdInt("main", "intopt2", 13) == 13 assert cfgParser.rdInt("main", "stropt", 13) == 13 assert cfgParser.rdInt("nope", "intopt1", 13) == 13 assert cfgParser.rdInt("main", "blabla", 13) == 13 # Read Float assert cfgParser.rdFlt("main", "intopt1", 13.0) == 42.0 assert cfgParser.rdFlt("main", "float1", 13.0) == 4.2 assert cfgParser.rdInt("main", "stropt", 13.0) == 13.0 assert cfgParser.rdInt("nope", "intopt1", 13.0) == 13.0 assert cfgParser.rdInt("main", "blabla", 13.0) == 13.0 # Read String List assert cfgParser.rdStrList("main", "list1", []) == [] assert cfgParser.rdStrList("main", "list1", ["x"]) == ["a"] assert cfgParser.rdStrList("main", "list1", ["x", "y"]) == ["a", "b"] assert cfgParser.rdStrList("main", "list1", ["x", "y", "z"]) == ["a", "b", "c"] assert cfgParser.rdStrList("main", "list1", ["x", "y", "z", "w"]) == ["a", "b", "c", "w"] assert cfgParser.rdStrList("main", "stropt", ["x"]) == ["value"] assert cfgParser.rdStrList("main", "intopt1", ["x"]) == ["42"] assert cfgParser.rdStrList("nope", "list1", ["x"]) == ["x"] assert cfgParser.rdStrList("main", "blabla", ["x"]) == ["x"] # Read Integer List assert cfgParser.rdIntList("main", "list2", []) == [] assert cfgParser.rdIntList("main", "list2", [1]) == [17] assert cfgParser.rdIntList("main", "list2", [1, 2]) == [17, 18] assert cfgParser.rdIntList("main", "list2", [1, 2, 3]) == [17, 18, 19] assert cfgParser.rdIntList("main", "list2", [1, 2, 3, 4]) == [17, 18, 19, 4] assert cfgParser.rdIntList("main", "stropt", [1]) == [1] assert cfgParser.rdIntList("main", "boolopt1", [1]) == [1] assert cfgParser.rdIntList("nope", "list2", [1]) == [1] assert cfgParser.rdIntList("main", "blabla", [1]) == [1] # Internal # ======== assert cfgParser._parseLine("main", "stropt", None, 999) is None