Fix merge conflicts

This commit is contained in:
Veronica K. B. Olsen
2019-11-08 19:09:29 +01:00
41 changed files with 205 additions and 88 deletions
+4
View File
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
# Main Window Elements
from nw.gui.icons import GuiIcons
from nw.gui.mainmenu import GuiMainMenu
from nw.gui.statusbar import GuiMainStatus
from nw.gui.theme import GuiTheme
# Dialogs
from nw.gui.dialogs.configeditor import GuiConfigEditor
@@ -26,8 +28,10 @@ from nw.gui.tools.dochighlight import GuiDocHighlighter
from nw.gui.tools.wordcounter import WordCounter
__all__ = [
"GuiIcons",
"GuiMainMenu",
"GuiMainStatus",
"GuiTheme",
"GuiConfigEditor",
"GuiExport",
"GuiItemEditor",
+13 -16
View File
@@ -15,8 +15,7 @@ import nw
from os import path
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import (
QIcon, QPixmap, QColor, QBrush, QStandardItemModel, QFont
)
@@ -46,10 +45,7 @@ class GuiConfigEditor(QDialog):
self.innerBox = QVBoxLayout()
self.setWindowTitle("Preferences")
self.gradPath = path.abspath(path.join(self.mainConf.graphPath,"gear.svg"))
self.svgGradient = QSvgWidget(path.join(self.gradPath))
self.svgGradient.setFixedSize(QSize(64,64))
self.guiDeco = self.theParent.theTheme.loadDecoration("settings",(64,64))
self.theProject.countStatus()
self.tabMain = GuiConfigEditGeneral(self.theParent)
@@ -60,7 +56,7 @@ class GuiConfigEditor(QDialog):
self.tabWidget.addTab(self.tabEditor, "Editor")
self.setLayout(self.outerBox)
self.outerBox.addWidget(self.svgGradient, 0, Qt.AlignTop)
self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop)
self.outerBox.addLayout(self.innerBox)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
@@ -147,11 +143,16 @@ class GuiConfigEditGeneral(QWidget):
if syntaxIdx != -1:
self.guiLookSyntax.setCurrentIndex(syntaxIdx)
self.guiDarkIcons = QCheckBox("Prefer icons for dark backgrounds", self)
self.guiDarkIcons.setToolTip("This may improve the look of icons if the system theme is dark.")
self.guiDarkIcons.setChecked(self.mainConf.guiDark)
self.guiLookForm.addWidget(QLabel("Theme"), 0, 0)
self.guiLookForm.addWidget(self.guiLookTheme, 0, 1)
self.guiLookForm.addWidget(QLabel("Syntax"), 1, 0)
self.guiLookForm.addWidget(self.guiLookSyntax, 1, 1)
self.guiLookForm.setColumnStretch(2, 1)
self.guiLookForm.addWidget(self.guiDarkIcons, 2, 0, 1, 2)
self.guiLookForm.setColumnStretch(3, 1)
# Spell Checking
self.spellLang = QGroupBox("Spell Checker", self)
@@ -221,17 +222,11 @@ class GuiConfigEditGeneral(QWidget):
self.projBackupClose = QCheckBox("Run on close",self)
self.projBackupClose.setToolTip("Backup automatically on project close.")
if self.mainConf.backupOnClose:
self.projBackupClose.setCheckState(Qt.Checked)
else:
self.projBackupClose.setCheckState(Qt.Unchecked)
self.projBackupClose.setChecked(self.mainConf.backupOnClose)
self.projBackupAsk = QCheckBox("Ask before backup",self)
self.projBackupAsk.setToolTip("Ask before backup.")
if self.mainConf.askBeforeBackup:
self.projBackupAsk.setCheckState(Qt.Checked)
else:
self.projBackupAsk.setCheckState(Qt.Unchecked)
self.projBackupAsk.setChecked(self.mainConf.askBeforeBackup)
self.projBackupForm.addWidget(self.projBackupPath, 0, 0, 1, 2)
self.projBackupForm.addWidget(self.projBackupGetPath, 0, 2)
@@ -257,6 +252,7 @@ class GuiConfigEditGeneral(QWidget):
guiTheme = self.guiLookTheme.currentData()
guiSyntax = self.guiLookSyntax.currentData()
guiDark = self.guiDarkIcons.isChecked()
spellTool = self.spellToolList.currentData()
spellLanguage = self.spellLangList.currentData()
autoSaveDoc = self.autoSaveDoc.value()
@@ -270,6 +266,7 @@ class GuiConfigEditGeneral(QWidget):
self.mainConf.guiTheme = guiTheme
self.mainConf.guiSyntax = guiSyntax
self.mainConf.guiDark = guiDark
self.mainConf.spellTool = spellTool
self.mainConf.spellLanguage = spellLanguage
self.mainConf.autoSaveDoc = autoSaveDoc
+3 -6
View File
@@ -16,8 +16,7 @@ import nw
from os import path
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialog, QHBoxLayout, QVBoxLayout, QWidget, QTabWidget, QGridLayout,
QGroupBox, QCheckBox, QLabel, QComboBox, QLineEdit, QPushButton,
@@ -50,9 +49,7 @@ class GuiExport(QDialog):
self.setWindowTitle("Export Project")
self.setLayout(self.outerBox)
self.gradPath = path.abspath(path.join(self.mainConf.graphPath,"export.svg"))
self.svgGradient = QSvgWidget(self.gradPath)
self.svgGradient.setFixedSize(QSize(64,64))
self.guiDeco = self.theParent.theTheme.loadDecoration("export",(64,64))
self.theProject.countStatus()
self.tabMain = GuiExportMain(self.theParent, self.theProject, self.optState)
@@ -62,7 +59,7 @@ class GuiExport(QDialog):
self.tabWidget.addTab(self.tabMain, "Settings")
self.tabWidget.addTab(self.tabPandoc, "Pandoc")
self.outerBox.addWidget(self.svgGradient, 0, Qt.AlignTop)
self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop)
self.outerBox.addLayout(self.innerBox)
self.doExportForm = QGridLayout()
+3 -7
View File
@@ -15,8 +15,7 @@ import nw
from os import path
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialog, QHBoxLayout, QVBoxLayout, QGroupBox, QFormLayout,
QLineEdit, QPushButton, QComboBox
@@ -42,13 +41,10 @@ class GuiItemEditor(QDialog):
self.innerBox = QVBoxLayout()
self.setWindowTitle("Item Settings")
self.gradPath = path.abspath(path.join(self.mainConf.graphPath,"gear.svg"))
self.svgGradient = QSvgWidget(self.gradPath)
self.svgGradient.setFixedSize(QSize(64,64))
self.guiDeco = self.theParent.theTheme.loadDecoration("settings",(64,64))
self.setLayout(self.outerBox)
self.outerBox.addWidget(self.svgGradient, 0, Qt.AlignTop)
self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop)
self.outerBox.addLayout(self.innerBox)
self.mainGroup = QGroupBox("Item Settings")
+4 -7
View File
@@ -15,9 +15,8 @@ import nw
from os import path
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush
from PyQt5.QtWidgets import (
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit,
QLabel, QWidget, QTabWidget, QDialogButtonBox, QListWidget,
@@ -45,9 +44,7 @@ class GuiProjectEditor(QDialog):
self.setWindowTitle("Project Settings")
self.setLayout(self.outerBox)
self.gradPath = path.abspath(path.join(self.mainConf.graphPath,"gear.svg"))
self.svgGradient = QSvgWidget(self.gradPath)
self.svgGradient.setFixedSize(QSize(64,64))
self.guiDeco = self.theParent.theTheme.loadDecoration("settings",(64,64))
self.theProject.countStatus()
self.tabMain = GuiProjectEditMain(self.theParent, self.theProject)
@@ -61,7 +58,7 @@ class GuiProjectEditor(QDialog):
self.tabWidget.addTab(self.tabImport, "Importance")
self.tabWidget.addTab(self.tabReplace,"Auto-Replace")
self.outerBox.addWidget(self.svgGradient, 0, Qt.AlignTop)
self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop)
self.outerBox.addLayout(self.innerBox)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+151
View File
@@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-
"""novelWriter Icons Class
novelWriter Icons Class
===========================
This class manages the GUI icons
File History:
Created: 2019-11-08 [0.4.0]
"""
import logging
import nw
from os import path
from PyQt5.QtCore import QSize
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtGui import QIcon
logger = logging.getLogger(__name__)
class GuiIcons:
# Icon keys should either be a .svg or .png file under the gui
# theme folder, or have a fallback that is either compatible with
# QIcon.fromTheme, as specified here:
# https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
# or, if there is no fallback, the variable should be None.
ICON_MAP = {
"root" : "drive-harddisk",
"folder" : "folder",
"document" : "x-office-document",
"trash" : "user-trash",
"orphan" : "dialog-warning",
"save" : "document-save",
"add" : "list-add",
"remove" : "list-remove",
"close" : "edit-delete",
"search" : "edit-find",
"replace" : "edit-find-replace",
"time" : None,
}
DECO_MAP = {
"export" : "export.svg",
"settings" : "settings.svg",
}
def __init__(self, theParent):
self.mainConf = nw.CONFIG
self.theParent = theParent
# Storage
self.qIcons = {}
# Look for files in
self.priPath = "" # The gui theme's icon folder
self.secPath = "" # The main assets icon folder
self.prefDark = False # Load dark icons, if available
return
def initIcons(self, priPath):
self.priPath = priPath
self.secPath = self.mainConf.iconPath
self.prefDark = self.mainConf.guiDark
for iconKey in self.ICON_MAP.keys():
self.qIcons[iconKey] = self._loadIcon(iconKey)
return
def loadDecoration(self, decoKey, decoSize=None):
if decoKey not in self.DECO_MAP:
logger.error("Decoration with name '%s' does not exist" % decoKey)
return QSvgWidget()
svgPath = path.join(self.mainConf.graphPath, self.DECO_MAP[decoKey])
if not path.isfile(svgPath):
logger.error("Decoration file '%s' not in assets folder" % self.DECO_MAP[decoKey])
return QSvgWidget()
svgDeco = QSvgWidget(svgPath)
if decoSize is not None:
svgDeco.setFixedSize(QSize(decoSize[0],decoSize[1]))
return svgDeco
def getIcon(self, iconKey, iconSize=None):
if iconKey in self.qIcons:
return self.qIcons[iconKey]
return QIcon()
##
# Internal Functions
##
def _loadIcon(self, iconKey):
if iconKey not in self.ICON_MAP:
logger.error("Icon with name '%s' does not exist" % iconKey)
return QIcon()
if self.prefDark:
iconSuffix = "dark"
else:
iconSuffix = "light"
iconNames = []
iconNames.append("%s-%s.svg" % (iconKey, iconSuffix))
iconNames.append("%s-%s.png" % (iconKey, iconSuffix))
iconNames.append("%s.svg" % iconKey)
iconNames.append("%s.png" % iconKey)
# Check theme folder
loadFrom = None
for iconName in iconNames:
checkPath = path.join(self.priPath, iconName)
if path.isfile(checkPath):
loadFrom = checkPath
logger.verbose("Loading icon '%s' from theme" % iconName)
break
if loadFrom is not None:
return QIcon(loadFrom)
# Check assets folder
for iconName in iconNames:
checkPath = path.join(self.secPath, iconName)
if path.isfile(checkPath):
loadFrom = checkPath
logger.verbose("Loading icon '%s' from assets" % iconName)
break
if loadFrom is not None:
return QIcon(loadFrom)
# If we're still here, try to set from system theme
if self.ICON_MAP[iconKey] is not None:
logger.verbose("Loading icon '%s' from system theme" % iconKey)
return QIcon().fromTheme(self.ICON_MAP[iconKey])
# Give up and return an empty icomn
logger.warning("Did not load an icon for '%s'" % iconKey)
return QIcon()
# END Class GuiIcons
+318
View File
@@ -0,0 +1,318 @@
# -*- coding: utf-8 -*-
"""novelWriter Theme Class
novelWriter Theme Class
===========================
This class reads and store the main theme
File History:
Created: 2019-05-18 [0.1.3]
"""
import logging
import configparser
import nw
from os import path, listdir
from PyQt5.QtWidgets import qApp
from PyQt5.QtGui import QPalette, QColor, QIcon, QPixmap
from nw.constants import nwAlert
from nw.gui.icons import GuiIcons
logger = logging.getLogger(__name__)
class GuiTheme:
def __init__(self, theParent):
self.mainConf = nw.CONFIG
self.theParent = theParent
self.theIcons = GuiIcons(self.theParent)
self.guiPalette = QPalette()
self.guiPath = "gui"
self.iconPath = "icons"
self.syntaxPath = "syntax"
self.cssName = "style.qss"
self.confName = "theme.conf"
self.themeList = []
self.syntaxList = []
# Loaded Theme Settings
## Theme
self.themeName = ""
self.themeAuthor = ""
self.themeCredit = ""
self.themeUrl = ""
## GUI
self.treeWCount = [ 0, 0, 0]
self.statNone = [120,120,120]
self.statUnsaved = [120,120, 40]
self.statSaved = [ 40,120, 0]
# Loaded Syntax Settings
## Main
self.syntaxName = ""
self.syntaxAuthor = ""
self.syntaxCredit = ""
self.syntaxUrl = ""
## Colours
self.colBack = [255,255,255]
self.colText = [ 0, 0, 0]
self.colLink = [ 0, 0, 0]
self.colHead = [ 0, 0, 0]
self.colHeadH = [ 0, 0, 0]
self.colEmph = [ 0, 0, 0]
self.colDialN = [ 0, 0, 0]
self.colDialD = [ 0, 0, 0]
self.colDialS = [ 0, 0, 0]
self.colComm = [ 0, 0, 0]
self.colKey = [ 0, 0, 0]
self.colVal = [ 0, 0, 0]
self.colSpell = [ 0, 0, 0]
self.colTagErr = [ 0, 0, 0]
self.colRepTag = [ 0, 0, 0]
# Changeable Settings
self.guiTheme = None
self.guiSyntax = None
self.themeRoot = None
self.themePath = None
self.syntaxFile = None
self.confFile = None
self.cssFile = None
self.updateTheme()
self.getIcon = self.theIcons.getIcon
self.loadDecoration = self.theIcons.loadDecoration
return
##
# Actions
##
def updateTheme(self):
self.guiTheme = self.mainConf.guiTheme
self.guiSyntax = self.mainConf.guiSyntax
self.themeRoot = self.mainConf.themeRoot
self.themePath = path.join(self.mainConf.themeRoot,self.guiPath,self.guiTheme)
self.syntaxFile = path.join(self.themeRoot,self.syntaxPath,self.guiSyntax+".conf")
self.confFile = path.join(self.themePath,self.confName)
self.cssFile = path.join(self.themePath,self.cssName)
self.theIcons.initIcons(path.join(self.themePath,self.iconPath))
self.loadTheme()
self.loadSyntax()
return True
def loadTheme(self):
logger.debug("Loading theme files")
logger.debug("System icon theme is '%s'" % str(QIcon.themeName()))
# CSS File
cssData = ""
try:
if path.isfile(self.cssFile):
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")
return False
# Config File
confParser = configparser.ConfigParser()
try:
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
## Main
cnfSec = "Main"
if confParser.has_section(cnfSec):
self.themeName = self._parseLine( confParser, cnfSec, "name", "")
self.themeAuthor = self._parseLine( confParser, cnfSec, "author", "")
self.themeCredit = self._parseLine( confParser, cnfSec, "credit", "")
self.themeUrl = self._parseLine( confParser, cnfSec, "url", "")
## 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.treeWCount = self._loadColour(confParser, cnfSec, "treewordcount")
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
def loadSyntax(self):
confParser = configparser.ConfigParser()
try:
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
## Main
cnfSec = "Main"
if confParser.has_section(cnfSec):
self.syntaxName = self._parseLine(confParser, cnfSec, "name", "")
self.syntaxAuthor = self._parseLine(confParser, cnfSec, "author", "")
self.syntaxCredit = self._parseLine(confParser, cnfSec, "credit", "")
self.syntaxUrl = self._parseLine(confParser, cnfSec, "url", "")
## 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.colComm = 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")
logger.info("Loaded syntax theme '%s'" % self.guiSyntax)
return True
def listThemes(self):
if len(self.themeList) > 0:
return self.themeList
confParser = configparser.ConfigParser()
for themeDir in listdir(path.join(self.mainConf.themeRoot, self.guiPath)):
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, mode="r", encoding="utf8"))
except Exception as e:
self.theParent.makeAlert(["Could not load theme config file.",str(e)],nwAlert.ERROR)
continue
themeName = ""
if confParser.has_section("Main"):
themeName = confParser.get("Main","name")
logger.verbose("Theme name is '%s'" % themeName)
if themeName != "":
self.themeList.append((themeDir, themeName))
self.themeList = sorted(self.themeList, key=lambda x: x[1])
return self.themeList
def listSyntax(self):
if len(self.syntaxList) > 0:
return self.syntaxList
confParser = configparser.ConfigParser()
syntaxDir = path.join(self.mainConf.themeRoot, self.syntaxPath)
for syntaxFile in listdir(syntaxDir):
syntaxPath = path.join(syntaxDir, syntaxFile)
if not path.isfile(syntaxPath):
continue
logger.verbose("Checking theme syntax for '%s'" % syntaxFile)
try:
confParser.read_file(open(syntaxPath, mode="r", encoding="utf8"))
except Exception as e:
self.theParent.makeAlert(["Could not load syntax file.",str(e)],nwAlert.ERROR)
return []
syntaxName = ""
if confParser.has_section("Main"):
syntaxName = confParser.get("Main","name")
if len(syntaxFile) > 5 and syntaxName != "":
self.syntaxList.append((syntaxFile[:-5], syntaxName))
logger.verbose("Syntax name is '%s'" % syntaxName)
self.syntaxList = sorted(self.syntaxList, key=lambda x: x[1])
return self.syntaxList
##
# Internal Functions
##
def _loadColour(self, confParser, cnfSec, cnfName):
if confParser.has_option(cnfSec,cnfName):
inData = confParser.get(cnfSec,cnfName).split(",")
outData = []
try:
outData.append(int(inData[0]))
outData.append(int(inData[1]))
outData.append(int(inData[2]))
except:
logger.error("Could not load theme colours for '%s' from config file" % cnfName)
outData = [0,0,0]
else:
logger.warning("Could not find theme colours for '%s' in config file" % cnfName)
outData = [0,0,0]
return outData
def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
readCol = []
if confParser.has_option(cnfSec,cnfName):
inData = confParser.get(cnfSec,cnfName).split(",")
try:
readCol.append(int(inData[0]))
readCol.append(int(inData[1]))
readCol.append(int(inData[2]))
except:
logger.error("Could not load theme colours for '%s' from config file" % cnfName)
return
if len(readCol) == 3:
self.guiPalette.setColor(paletteVal, QColor(*readCol))
return
def _parseLine(self, confParser, cnfSec, cnfName, cnfDefault):
if confParser.has_section(cnfSec):
if confParser.has_option(cnfSec, cnfName):
return confParser.get(cnfSec, cnfName)
return cnfDefault
# End Class GuiTheme