Clean up core structure (#1502)

This commit is contained in:
Veronica Berglyd Olsen
2023-08-10 11:29:25 +01:00
committed by GitHub
77 changed files with 1379 additions and 1516 deletions
+6 -7
View File
@@ -121,8 +121,7 @@ def checkUuid(value: Any, default: str) -> str:
def checkPath(value: Any, default: Path) -> Path: def checkPath(value: Any, default: Path) -> Path:
"""Check if a value is a valid path. Non-empty strings are accepted. """Check if a value is a valid path."""
"""
if isinstance(value, Path): if isinstance(value, Path):
return value return value
elif isinstance(value, str): elif isinstance(value, str):
@@ -289,8 +288,7 @@ def transferCase(source: str, target: str) -> str:
def fuzzyTime(seconds: int) -> str: def fuzzyTime(seconds: int) -> str:
"""Converts a time difference in seconds into a fuzzy time string. """Convert a time difference in seconds into a fuzzy time string."""
"""
if seconds < 0: if seconds < 0:
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "in the future" "Common", "in the future"
@@ -350,8 +348,7 @@ def fuzzyTime(seconds: int) -> str:
def numberToRoman(value: int, toLower: bool = False) -> str: def numberToRoman(value: int, toLower: bool = False) -> str:
"""Convert an integer to a Roman number. """Convert an integer to a Roman number."""
"""
if not isinstance(value, int): if not isinstance(value, int):
return "NAN" return "NAN"
if value < 1 or value > 4999: if value < 1 or value > 4999:
@@ -424,12 +421,14 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
return "".join(buffer) return "".join(buffer)
def xmlIndent(tree: ET.Element | ET.ElementTree): def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
"""A modified version of the XML indent function in the standard """A modified version of the XML indent function in the standard
library. It behaves more closely to how the one from lxml does. library. It behaves more closely to how the one from lxml does.
""" """
if isinstance(tree, ET.ElementTree): if isinstance(tree, ET.ElementTree):
tree = tree.getroot() tree = tree.getroot()
if not isinstance(tree, ET.Element):
return
indentations = ["\n"] indentations = ["\n"]
+198 -201
View File
@@ -28,18 +28,23 @@ import json
import logging import logging
from time import time from time import time
from typing import TYPE_CHECKING
from pathlib import Path from pathlib import Path
from PyQt5.QtGui import QFontDatabase from PyQt5.QtGui import QFontDatabase
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QT_VERSION, QT_VERSION_STR, PYQT_VERSION, PYQT_VERSION_STR, QStandardPaths, PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
QSysInfo, QLocale, QLibraryInfo, QTranslator QLocale, QStandardPaths, QSysInfo, QTranslator
) )
from PyQt5.QtWidgets import QApplication
from novelwriter.error import logException, formatException from novelwriter.error import formatException, logException
from novelwriter.common import checkPath, formatTimeStamp, NWConfigParser from novelwriter.common import NWConfigParser, checkInt, checkPath, formatTimeStamp
from novelwriter.constants import nwFiles, nwUnicode from novelwriter.constants import nwFiles, nwUnicode
if TYPE_CHECKING: # pragma: no cover
from novelwriter.gui.theme import GuiTheme
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -48,7 +53,7 @@ class Config:
LANG_NW = 1 LANG_NW = 1
LANG_PROJ = 2 LANG_PROJ = 2
def __init__(self): def __init__(self) -> None:
# Initialisation # Initialisation
# ============== # ==============
@@ -64,6 +69,7 @@ class Config:
self._confPath = confRoot.absolute() / self.appHandle # The user config location self._confPath = confRoot.absolute() / self.appHandle # The user config location
self._dataPath = dataRoot.absolute() / self.appHandle # The user data location self._dataPath = dataRoot.absolute() / self.appHandle # The user data location
self._homePath = Path.home().absolute() # The user's home directory self._homePath = Path.home().absolute() # The user's home directory
self._backPath = self._homePath / "Backups"
self._appPath = Path(__file__).parent.absolute() self._appPath = Path(__file__).parent.absolute()
self._appRoot = self._appPath.parent self._appRoot = self._appPath.parent
@@ -90,7 +96,8 @@ class Config:
# User Settings # User Settings
# ============= # =============
self._recentProj = RecentProjects(self) self._themeObj = None
self._recentObj = RecentProjects(self)
# General GUI Settings # General GUI Settings
self.guiLocale = self._qLocale.name() self.guiLocale = self._qLocale.name()
@@ -102,7 +109,6 @@ class Config:
self.hideVScroll = False # Hide vertical scroll bars on main widgets self.hideVScroll = False # Hide vertical scroll bars on main widgets
self.hideHScroll = False # Hide horizontal scroll bars on main widgets self.hideHScroll = False # Hide horizontal scroll bars on main widgets
self.lastNotes = "0x0" # The latest release notes that have been shown self.lastNotes = "0x0" # The latest release notes that have been shown
self._lastPath = self._homePath # The user's last used path
# Size Settings # Size Settings
self._mainWinSize = [1200, 650] # Last size of the main GUI window self._mainWinSize = [1200, 650] # Last size of the main GUI window
@@ -116,7 +122,6 @@ class Config:
self.autoSaveProj = 60 # Interval for auto-saving project, in seconds self.autoSaveProj = 60 # Interval for auto-saving project, in seconds
self.autoSaveDoc = 30 # Interval for auto-saving document, in seconds self.autoSaveDoc = 30 # Interval for auto-saving document, in seconds
self.emphLabels = True # Add emphasis to H1 and H2 item labels self.emphLabels = True # Add emphasis to H1 and H2 item labels
self._backupPath = None # Backup path to use, can be none
self.backupOnClose = False # Flag for running automatic backups self.backupOnClose = False # Flag for running automatic backups
self.askBeforeBackup = True # Flag for asking before running automatic backup self.askBeforeBackup = True # Flag for asking before running automatic backup
@@ -169,6 +174,10 @@ class Config:
self.fmtPadAfter = "" self.fmtPadAfter = ""
self.fmtPadThin = False self.fmtPadThin = False
# User Paths
self._lastPath = self._homePath # The user's last used path
self._backupPath = self._backPath # Backup path to use, can be none
# Spell Checking Settings # Spell Checking Settings
self.spellLanguage = "en" self.spellLanguage = "en"
@@ -228,53 +237,59 @@ class Config:
## ##
@property @property
def hasError(self): def hasError(self) -> bool:
return self._hasError return self._hasError
@property @property
def recentProjects(self): def recentProjects(self) -> RecentProjects:
return self._recentProj return self._recentObj
@property @property
def mainWinSize(self): def theme(self) -> GuiTheme:
if self._themeObj is None:
raise Exception("Cannot access GUI theme before it is initialised")
return self._themeObj
@property
def mainWinSize(self) -> list[int]:
return [int(x*self.guiScale) for x in self._mainWinSize] return [int(x*self.guiScale) for x in self._mainWinSize]
@property @property
def preferencesWinSize(self): def preferencesWinSize(self) -> list[int]:
return [int(x*self.guiScale) for x in self._prefsWinSize] return [int(x*self.guiScale) for x in self._prefsWinSize]
@property @property
def projLoadColWidths(self): def projLoadColWidths(self) -> list[int]:
return [int(x*self.guiScale) for x in self._projLoadCols] return [int(x*self.guiScale) for x in self._projLoadCols]
@property @property
def mainPanePos(self): def mainPanePos(self) -> list[int]:
return [int(x*self.guiScale) for x in self._mainPanePos] return [int(x*self.guiScale) for x in self._mainPanePos]
@property @property
def viewPanePos(self): def viewPanePos(self) -> list[int]:
return [int(x*self.guiScale) for x in self._viewPanePos] return [int(x*self.guiScale) for x in self._viewPanePos]
@property @property
def outlinePanePos(self): def outlinePanePos(self) -> list[int]:
return [int(x*self.guiScale) for x in self._outlnPanePos] return [int(x*self.guiScale) for x in self._outlnPanePos]
## ##
# Getters # Getters
## ##
def getTextWidth(self, focusMode=False): def getTextWidth(self, focusMode: bool = False) -> int:
"""Get the text with for the correct editor mode.""" """Get the text with for the correct editor mode."""
if focusMode: if focusMode:
return self.pxInt(max(self.focusWidth, 200)) return self.pxInt(max(self.focusWidth, 200))
else: else:
return self.pxInt(max(self.textWidth, 200)) return self.pxInt(max(self.textWidth, 200))
def getTextMargin(self): def getTextMargin(self) -> int:
"""Get the scaled text margin.""" """Get the scaled text margin."""
return self.pxInt(max(self.textMargin, 0)) return self.pxInt(max(self.textMargin, 0))
def getTabWidth(self): def getTabWidth(self) -> int:
"""Get the scaled tab width.""" """Get the scaled tab width."""
return self.pxInt(max(self.tabWidth, 0)) return self.pxInt(max(self.tabWidth, 0))
@@ -282,65 +297,70 @@ class Config:
# Setters # Setters
## ##
def setMainWinSize(self, newWidth, newHeight): def setThemeInstance(self, theme: GuiTheme) -> None:
"""Set the applications theme instance."""
self._themeObj = theme
return
def setMainWinSize(self, width: int, height: int) -> None:
"""Set the size of the main window, but only if the change is """Set the size of the main window, but only if the change is
larger than 5 pixels. The OS window manager will sometimes larger than 5 pixels. The OS window manager will sometimes
adjust it a bit, and we don't want the main window to shrink or adjust it a bit, and we don't want the main window to shrink or
grow each time the app is opened. grow each time the app is opened.
""" """
newWidth = int(newWidth/self.guiScale) width = int(width/self.guiScale)
newHeight = int(newHeight/self.guiScale) height = int(height/self.guiScale)
if abs(self._mainWinSize[0] - newWidth) > 5: if abs(self._mainWinSize[0] - width) > 5:
self._mainWinSize[0] = newWidth self._mainWinSize[0] = width
if abs(self._mainWinSize[1] - newHeight) > 5: if abs(self._mainWinSize[1] - height) > 5:
self._mainWinSize[1] = newHeight self._mainWinSize[1] = height
return return
def setPreferencesWinSize(self, newWidth, newHeight): def setPreferencesWinSize(self, width: int, height: int) -> None:
"""Set the size of the Preferences dialog window.""" """Set the size of the Preferences dialog window."""
self._prefsWinSize[0] = int(newWidth/self.guiScale) self._prefsWinSize[0] = int(width/self.guiScale)
self._prefsWinSize[1] = int(newHeight/self.guiScale) self._prefsWinSize[1] = int(height/self.guiScale)
return return
def setProjLoadColWidths(self, colWidths): def setProjLoadColWidths(self, widths: list[int]) -> None:
"""Set the column widths of the Load Project dialog.""" """Set the column widths of the Load Project dialog."""
self._projLoadCols = [int(x/self.guiScale) for x in colWidths] self._projLoadCols = [int(x/self.guiScale) for x in widths]
return return
def setMainPanePos(self, panePos): def setMainPanePos(self, pos: list[int]) -> None:
"""Set the position of the main GUI splitter.""" """Set the position of the main GUI splitter."""
self._mainPanePos = [int(x/self.guiScale) for x in panePos] self._mainPanePos = [int(x/self.guiScale) for x in pos]
return return
def setViewPanePos(self, panePos): def setViewPanePos(self, pos: list[int]) -> None:
"""Set the position of the viewer meta data splitter.""" """Set the position of the viewer meta data splitter."""
self._viewPanePos = [int(x/self.guiScale) for x in panePos] self._viewPanePos = [int(x/self.guiScale) for x in pos]
return return
def setOutlinePanePos(self, panePos): def setOutlinePanePos(self, pos: list[int]) -> None:
"""Set the position of the outline details splitter.""" """Set the position of the outline details splitter."""
self._outlnPanePos = [int(x/self.guiScale) for x in panePos] self._outlnPanePos = [int(x/self.guiScale) for x in pos]
return return
def setLastPath(self, lastPath): def setLastPath(self, path: str | Path) -> None:
"""Set the last used path. Only the folder is saved, so if the """Set the last used path. Only the folder is saved, so if the
path is not a folder, the parent of the path is used instead. path is not a folder, the parent of the path is used instead.
""" """
if isinstance(lastPath, (str, Path)): if isinstance(path, (str, Path)):
lastPath = checkPath(lastPath, self._homePath) path = checkPath(path, self._homePath)
if not lastPath.is_dir(): if not path.is_dir():
lastPath = lastPath.parent path = path.parent
if lastPath.is_dir(): if path.is_dir():
self._lastPath = lastPath self._lastPath = path
logger.debug("Last path updated: %s" % self._lastPath) logger.debug("Last path updated: %s" % self._lastPath)
return return
def setBackupPath(self, backupPath: Path | None): def setBackupPath(self, path: Path | str) -> None:
"""Set the current backup path.""" """Set the current backup path."""
self._backupPath = checkPath(backupPath, None) self._backupPath = checkPath(path, self._backPath)
return return
def setTextFont(self, family: str | None, pointSize: int = 12): def setTextFont(self, family: str | None, pointSize: int = 12) -> None:
"""Set the text font if it exists. If it doesn't, or is None, """Set the text font if it exists. If it doesn't, or is None,
set to default font. set to default font.
""" """
@@ -364,15 +384,11 @@ class Config:
## ##
def pxInt(self, value: int) -> int: def pxInt(self, value: int) -> int:
"""Used to scale fixed gui sizes by the screen scale factor. """Scale fixed gui sizes by the screen scale factor."""
This function returns an int, which is always rounded down.
"""
return int(value*self.guiScale) return int(value*self.guiScale)
def rpxInt(self, value: int) -> int: def rpxInt(self, value: int) -> int:
"""Used to un-scale fixed gui sizes by the screen scale factor. """Un-scale fixed gui sizes by the screen scale factor."""
This function returns an int, which is always rounded down.
"""
return int(value/self.guiScale) return int(value/self.guiScale)
def dataPath(self, target: str | None = None) -> Path: def dataPath(self, target: str | None = None) -> Path:
@@ -395,12 +411,12 @@ class Config:
return self._lastPath return self._lastPath
return self._homePath return self._homePath
def backupPath(self) -> Path | None: def backupPath(self) -> Path:
"""Return the backup path.""" """Return the backup path."""
if isinstance(self._backupPath, Path): if isinstance(self._backupPath, Path):
if self._backupPath.is_dir(): if self._backupPath.is_dir():
return self._backupPath return self._backupPath
return None return self._backPath
def errorText(self) -> str: def errorText(self) -> str:
"""Compile and return error messages from the initialisation of """Compile and return error messages from the initialisation of
@@ -442,7 +458,8 @@ class Config:
# Config Actions # Config Actions
## ##
def initConfig(self, confPath: str | Path | None = None, dataPath: str | Path | None = None): def initConfig(self, confPath: str | Path | None = None,
dataPath: str | Path | None = None) -> None:
"""Initialise the config class. The manual setting of confPath """Initialise the config class. The manual setting of confPath
and dataPath is mainly intended for the test suite. and dataPath is mainly intended for the test suite.
""" """
@@ -479,16 +496,15 @@ class Config:
else: else:
self.saveConfig() self.saveConfig()
self._recentProj.loadCache() self._recentObj.loadCache()
self._checkOptionalPackages() self._checkOptionalPackages()
logger.debug("Config initialisation complete") logger.debug("Config initialisation complete")
return return
def initLocalisation(self, nwApp): def initLocalisation(self, nwApp: QApplication) -> None:
"""Initialise the localisation of the GUI. """Initialise the localisation of the GUI."""
"""
self._qLocale = QLocale(self.guiLocale) self._qLocale = QLocale(self.guiLocale)
QLocale.setDefault(self._qLocale) QLocale.setDefault(self._qLocale)
self._qtTrans = {} self._qtTrans = {}
@@ -509,16 +525,15 @@ class Config:
return return
def loadConfig(self): def loadConfig(self) -> bool:
"""Load preferences from file and replace default settings. """Load preferences from file and replace default settings."""
"""
logger.debug("Loading config file") logger.debug("Loading config file")
theConf = NWConfigParser() conf = NWConfigParser()
cnfPath = self._confPath / nwFiles.CONF_FILE cnfPath = self._confPath / nwFiles.CONF_FILE
try: try:
with open(cnfPath, mode="r", encoding="utf-8") as inFile: with open(cnfPath, mode="r", encoding="utf-8") as inFile:
theConf.read_file(inFile) conf.read_file(inFile)
except Exception as exc: except Exception as exc:
logger.error("Could not load config file") logger.error("Could not load config file")
logException() logException()
@@ -528,98 +543,98 @@ class Config:
return False return False
# Main # Main
cnfSec = "Main" sec = "Main"
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme) self.guiTheme = conf.rdStr(sec, "theme", self.guiTheme)
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax) self.guiSyntax = conf.rdStr(sec, "syntax", self.guiSyntax)
self.guiFont = theConf.rdStr(cnfSec, "font", self.guiFont) self.guiFont = conf.rdStr(sec, "font", self.guiFont)
self.guiFontSize = theConf.rdInt(cnfSec, "fontsize", self.guiFontSize) self.guiFontSize = conf.rdInt(sec, "fontsize", self.guiFontSize)
self.guiLocale = theConf.rdStr(cnfSec, "localisation", self.guiLocale) self.guiLocale = conf.rdStr(sec, "localisation", self.guiLocale)
self.hideVScroll = theConf.rdBool(cnfSec, "hidevscroll", self.hideVScroll) self.hideVScroll = conf.rdBool(sec, "hidevscroll", self.hideVScroll)
self.hideHScroll = theConf.rdBool(cnfSec, "hidehscroll", self.hideHScroll) self.hideHScroll = conf.rdBool(sec, "hidehscroll", self.hideHScroll)
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes) self.lastNotes = conf.rdStr(sec, "lastnotes", self.lastNotes)
self._lastPath = theConf.rdPath(cnfSec, "lastpath", self._lastPath) self._lastPath = conf.rdPath(sec, "lastpath", self._lastPath)
# Sizes # Sizes
cnfSec = "Sizes" sec = "Sizes"
self._mainWinSize = theConf.rdIntList(cnfSec, "mainwindow", self._mainWinSize) self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize)
self._prefsWinSize = theConf.rdIntList(cnfSec, "preferences", self._prefsWinSize) self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize)
self._projLoadCols = theConf.rdIntList(cnfSec, "projloadcols", self._projLoadCols) self._projLoadCols = conf.rdIntList(sec, "projloadcols", self._projLoadCols)
self._mainPanePos = theConf.rdIntList(cnfSec, "mainpane", self._mainPanePos) self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos)
self._viewPanePos = theConf.rdIntList(cnfSec, "viewpane", self._viewPanePos) self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos)
self._outlnPanePos = theConf.rdIntList(cnfSec, "outlinepane", self._outlnPanePos) self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos)
# Project # Project
cnfSec = "Project" sec = "Project"
self.autoSaveProj = theConf.rdInt(cnfSec, "autosaveproject", self.autoSaveProj) self.autoSaveProj = conf.rdInt(sec, "autosaveproject", self.autoSaveProj)
self.autoSaveDoc = theConf.rdInt(cnfSec, "autosavedoc", self.autoSaveDoc) self.autoSaveDoc = conf.rdInt(sec, "autosavedoc", self.autoSaveDoc)
self.emphLabels = theConf.rdBool(cnfSec, "emphlabels", self.emphLabels) self.emphLabels = conf.rdBool(sec, "emphlabels", self.emphLabels)
self._backupPath = theConf.rdPath(cnfSec, "backuppath", self._backupPath) self._backupPath = conf.rdPath(sec, "backuppath", self._backupPath)
self.backupOnClose = theConf.rdBool(cnfSec, "backuponclose", self.backupOnClose) self.backupOnClose = conf.rdBool(sec, "backuponclose", self.backupOnClose)
self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup", self.askBeforeBackup) self.askBeforeBackup = conf.rdBool(sec, "askbeforebackup", self.askBeforeBackup)
# Editor # Editor
cnfSec = "Editor" sec = "Editor"
self.textFont = theConf.rdStr(cnfSec, "textfont", self.textFont) self.textFont = conf.rdStr(sec, "textfont", self.textFont)
self.textSize = theConf.rdInt(cnfSec, "textsize", self.textSize) self.textSize = conf.rdInt(sec, "textsize", self.textSize)
self.textWidth = theConf.rdInt(cnfSec, "width", self.textWidth) self.textWidth = conf.rdInt(sec, "width", self.textWidth)
self.textMargin = theConf.rdInt(cnfSec, "margin", self.textMargin) self.textMargin = conf.rdInt(sec, "margin", self.textMargin)
self.tabWidth = theConf.rdInt(cnfSec, "tabwidth", self.tabWidth) self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth)
self.focusWidth = theConf.rdInt(cnfSec, "focuswidth", self.focusWidth) self.focusWidth = conf.rdInt(sec, "focuswidth", self.focusWidth)
self.hideFocusFooter = theConf.rdBool(cnfSec, "hidefocusfooter", self.hideFocusFooter) self.hideFocusFooter = conf.rdBool(sec, "hidefocusfooter", self.hideFocusFooter)
self.doJustify = theConf.rdBool(cnfSec, "justify", self.doJustify) self.doJustify = conf.rdBool(sec, "justify", self.doJustify)
self.autoSelect = theConf.rdBool(cnfSec, "autoselect", self.autoSelect) self.autoSelect = conf.rdBool(sec, "autoselect", self.autoSelect)
self.doReplace = theConf.rdBool(cnfSec, "autoreplace", self.doReplace) self.doReplace = conf.rdBool(sec, "autoreplace", self.doReplace)
self.doReplaceSQuote = theConf.rdBool(cnfSec, "repsquotes", self.doReplaceSQuote) self.doReplaceSQuote = conf.rdBool(sec, "repsquotes", self.doReplaceSQuote)
self.doReplaceDQuote = theConf.rdBool(cnfSec, "repdquotes", self.doReplaceDQuote) self.doReplaceDQuote = conf.rdBool(sec, "repdquotes", self.doReplaceDQuote)
self.doReplaceDash = theConf.rdBool(cnfSec, "repdash", self.doReplaceDash) self.doReplaceDash = conf.rdBool(sec, "repdash", self.doReplaceDash)
self.doReplaceDots = theConf.rdBool(cnfSec, "repdots", self.doReplaceDots) self.doReplaceDots = conf.rdBool(sec, "repdots", self.doReplaceDots)
self.scrollPastEnd = theConf.rdInt(cnfSec, "scrollpastend", self.scrollPastEnd) self.scrollPastEnd = conf.rdInt(sec, "scrollpastend", self.scrollPastEnd)
self.autoScroll = theConf.rdBool(cnfSec, "autoscroll", self.autoScroll) self.autoScroll = conf.rdBool(sec, "autoscroll", self.autoScroll)
self.autoScrollPos = theConf.rdInt(cnfSec, "autoscrollpos", self.autoScrollPos) self.autoScrollPos = conf.rdInt(sec, "autoscrollpos", self.autoScrollPos)
self.fmtSQuoteOpen = theConf.rdStr(cnfSec, "fmtsquoteopen", self.fmtSQuoteOpen) self.fmtSQuoteOpen = conf.rdStr(sec, "fmtsquoteopen", self.fmtSQuoteOpen)
self.fmtSQuoteClose = theConf.rdStr(cnfSec, "fmtsquoteclose", self.fmtSQuoteClose) self.fmtSQuoteClose = conf.rdStr(sec, "fmtsquoteclose", self.fmtSQuoteClose)
self.fmtDQuoteOpen = theConf.rdStr(cnfSec, "fmtdquoteopen", self.fmtDQuoteOpen) self.fmtDQuoteOpen = conf.rdStr(sec, "fmtdquoteopen", self.fmtDQuoteOpen)
self.fmtDQuoteClose = theConf.rdStr(cnfSec, "fmtdquoteclose", self.fmtDQuoteClose) self.fmtDQuoteClose = conf.rdStr(sec, "fmtdquoteclose", self.fmtDQuoteClose)
self.fmtPadBefore = theConf.rdStr(cnfSec, "fmtpadbefore", self.fmtPadBefore) self.fmtPadBefore = conf.rdStr(sec, "fmtpadbefore", self.fmtPadBefore)
self.fmtPadAfter = theConf.rdStr(cnfSec, "fmtpadafter", self.fmtPadAfter) self.fmtPadAfter = conf.rdStr(sec, "fmtpadafter", self.fmtPadAfter)
self.fmtPadThin = theConf.rdBool(cnfSec, "fmtpadthin", self.fmtPadThin) self.fmtPadThin = conf.rdBool(sec, "fmtpadthin", self.fmtPadThin)
self.spellLanguage = theConf.rdStr(cnfSec, "spellcheck", self.spellLanguage) self.spellLanguage = conf.rdStr(sec, "spellcheck", self.spellLanguage)
self.showTabsNSpaces = theConf.rdBool(cnfSec, "showtabsnspaces", self.showTabsNSpaces) self.showTabsNSpaces = conf.rdBool(sec, "showtabsnspaces", self.showTabsNSpaces)
self.showLineEndings = theConf.rdBool(cnfSec, "showlineendings", self.showLineEndings) self.showLineEndings = conf.rdBool(sec, "showlineendings", self.showLineEndings)
self.showMultiSpaces = theConf.rdBool(cnfSec, "showmultispaces", self.showMultiSpaces) self.showMultiSpaces = conf.rdBool(sec, "showmultispaces", self.showMultiSpaces)
self.wordCountTimer = theConf.rdFlt(cnfSec, "wordcounttimer", self.wordCountTimer) self.wordCountTimer = conf.rdFlt(sec, "wordcounttimer", self.wordCountTimer)
self.bigDocLimit = theConf.rdInt(cnfSec, "bigdoclimit", self.bigDocLimit) self.bigDocLimit = conf.rdInt(sec, "bigdoclimit", self.bigDocLimit)
self.incNotesWCount = theConf.rdBool(cnfSec, "incnoteswcount", self.incNotesWCount) self.incNotesWCount = conf.rdBool(sec, "incnoteswcount", self.incNotesWCount)
self.showFullPath = theConf.rdBool(cnfSec, "showfullpath", self.showFullPath) self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
self.highlightQuotes = theConf.rdBool(cnfSec, "highlightquotes", self.highlightQuotes) self.highlightQuotes = conf.rdBool(sec, "highlightquotes", self.highlightQuotes)
self.allowOpenSQuote = theConf.rdBool(cnfSec, "allowopensquote", self.allowOpenSQuote) self.allowOpenSQuote = conf.rdBool(sec, "allowopensquote", self.allowOpenSQuote)
self.allowOpenDQuote = theConf.rdBool(cnfSec, "allowopendquote", self.allowOpenDQuote) self.allowOpenDQuote = conf.rdBool(sec, "allowopendquote", self.allowOpenDQuote)
self.highlightEmph = theConf.rdBool(cnfSec, "highlightemph", self.highlightEmph) self.highlightEmph = conf.rdBool(sec, "highlightemph", self.highlightEmph)
self.stopWhenIdle = theConf.rdBool(cnfSec, "stopwhenidle", self.stopWhenIdle) self.stopWhenIdle = conf.rdBool(sec, "stopwhenidle", self.stopWhenIdle)
self.userIdleTime = theConf.rdInt(cnfSec, "useridletime", self.userIdleTime) self.userIdleTime = conf.rdInt(sec, "useridletime", self.userIdleTime)
# State # State
cnfSec = "State" sec = "State"
self.showRefPanel = theConf.rdBool(cnfSec, "showrefpanel", self.showRefPanel) self.showRefPanel = conf.rdBool(sec, "showrefpanel", self.showRefPanel)
self.viewComments = theConf.rdBool(cnfSec, "viewcomments", self.viewComments) self.viewComments = conf.rdBool(sec, "viewcomments", self.viewComments)
self.viewSynopsis = theConf.rdBool(cnfSec, "viewsynopsis", self.viewSynopsis) self.viewSynopsis = conf.rdBool(sec, "viewsynopsis", self.viewSynopsis)
self.searchCase = theConf.rdBool(cnfSec, "searchcase", self.searchCase) self.searchCase = conf.rdBool(sec, "searchcase", self.searchCase)
self.searchWord = theConf.rdBool(cnfSec, "searchword", self.searchWord) self.searchWord = conf.rdBool(sec, "searchword", self.searchWord)
self.searchRegEx = theConf.rdBool(cnfSec, "searchregex", self.searchRegEx) self.searchRegEx = conf.rdBool(sec, "searchregex", self.searchRegEx)
self.searchLoop = theConf.rdBool(cnfSec, "searchloop", self.searchLoop) self.searchLoop = conf.rdBool(sec, "searchloop", self.searchLoop)
self.searchNextFile = theConf.rdBool(cnfSec, "searchnextfile", self.searchNextFile) self.searchNextFile = conf.rdBool(sec, "searchnextfile", self.searchNextFile)
self.searchMatchCap = theConf.rdBool(cnfSec, "searchmatchcap", self.searchMatchCap) self.searchMatchCap = conf.rdBool(sec, "searchmatchcap", self.searchMatchCap)
# Deprecated Settings or Locations as of 2.0 # Deprecated Settings or Locations as of 2.0
# ToDo: These will be loaded for a few minor releases until the users have converted them # ToDo: These will be loaded for a few minor releases until the users have converted them
self.guiFont = theConf.rdStr("Main", "guifont", self.guiFont) self.guiFont = conf.rdStr("Main", "guifont", self.guiFont)
self.guiFontSize = theConf.rdInt("Main", "guifontsize", self.guiFontSize) self.guiFontSize = conf.rdInt("Main", "guifontsize", self.guiFontSize)
self.guiLocale = theConf.rdStr("Main", "guilang", self.guiLocale) self.guiLocale = conf.rdStr("Main", "guilang", self.guiLocale)
self._backupPath = theConf.rdPath("Backup", "backuppath", self._backupPath) self._backupPath = conf.rdPath("Backup", "backuppath", self._backupPath)
self.backupOnClose = theConf.rdBool("Backup", "backuponclose", self.backupOnClose) self.backupOnClose = conf.rdBool("Backup", "backuponclose", self.backupOnClose)
self.askBeforeBackup = theConf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup) self.askBeforeBackup = conf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup)
fmtSingleQuotes = theConf.rdStrList(cnfSec, "fmtsinglequote", []) fmtSingleQuotes = conf.rdStrList(sec, "fmtsinglequote", [])
fmtDoubleQuotes = theConf.rdStrList(cnfSec, "fmtdoublequote", []) fmtDoubleQuotes = conf.rdStrList(sec, "fmtdoublequote", [])
if isinstance(fmtSingleQuotes, list) and len(fmtSingleQuotes) == 2: if isinstance(fmtSingleQuotes, list) and len(fmtSingleQuotes) == 2:
self.fmtSQuoteOpen = fmtSingleQuotes[0] self.fmtSQuoteOpen = fmtSingleQuotes[0]
@@ -631,9 +646,6 @@ class Config:
# Check Values # Check Values
# ============ # ============
# Check Certain Values for None
self.spellLanguage = self._checkNone(self.spellLanguage)
# If we're using straight quotes, disable auto-replace # If we're using straight quotes, disable auto-replace
if self.fmtSQuoteOpen == self.fmtSQuoteClose == "'" and self.doReplaceSQuote: if self.fmtSQuoteOpen == self.fmtSQuoteClose == "'" and self.doReplaceSQuote:
logger.info("Using straight single quotes, so disabling auto-replace") logger.info("Using straight single quotes, so disabling auto-replace")
@@ -645,18 +657,17 @@ class Config:
return True return True
def saveConfig(self): def saveConfig(self) -> bool:
"""Save the current preferences to file. """Save the current preferences to file."""
"""
logger.debug("Saving config file") logger.debug("Saving config file")
theConf = NWConfigParser() conf = NWConfigParser()
theConf["Meta"] = { conf["Meta"] = {
"timestamp": formatTimeStamp(time()), "timestamp": formatTimeStamp(time()),
} }
theConf["Main"] = { conf["Main"] = {
"theme": str(self.guiTheme), "theme": str(self.guiTheme),
"syntax": str(self.guiSyntax), "syntax": str(self.guiSyntax),
"font": str(self.guiFont), "font": str(self.guiFont),
@@ -668,7 +679,7 @@ class Config:
"lastpath": str(self._lastPath), "lastpath": str(self._lastPath),
} }
theConf["Sizes"] = { conf["Sizes"] = {
"mainwindow": self._packList(self._mainWinSize), "mainwindow": self._packList(self._mainWinSize),
"preferences": self._packList(self._prefsWinSize), "preferences": self._packList(self._prefsWinSize),
"projloadcols": self._packList(self._projLoadCols), "projloadcols": self._packList(self._projLoadCols),
@@ -677,16 +688,16 @@ class Config:
"outlinepane": self._packList(self._outlnPanePos), "outlinepane": self._packList(self._outlnPanePos),
} }
theConf["Project"] = { conf["Project"] = {
"autosaveproject": str(self.autoSaveProj), "autosaveproject": str(self.autoSaveProj),
"autosavedoc": str(self.autoSaveDoc), "autosavedoc": str(self.autoSaveDoc),
"emphlabels": str(self.emphLabels), "emphlabels": str(self.emphLabels),
"backuppath": str(self._backupPath or ""), "backuppath": str(self._backupPath),
"backuponclose": str(self.backupOnClose), "backuponclose": str(self.backupOnClose),
"askbeforebackup": str(self.askBeforeBackup), "askbeforebackup": str(self.askBeforeBackup),
} }
theConf["Editor"] = { conf["Editor"] = {
"textfont": str(self.textFont), "textfont": str(self.textFont),
"textsize": str(self.textSize), "textsize": str(self.textSize),
"width": str(self.textWidth), "width": str(self.textWidth),
@@ -727,7 +738,7 @@ class Config:
"useridletime": str(self.userIdleTime), "useridletime": str(self.userIdleTime),
} }
theConf["State"] = { conf["State"] = {
"showrefpanel": str(self.showRefPanel), "showrefpanel": str(self.showRefPanel),
"viewcomments": str(self.viewComments), "viewcomments": str(self.viewComments),
"viewsynopsis": str(self.viewSynopsis), "viewsynopsis": str(self.viewSynopsis),
@@ -743,7 +754,7 @@ class Config:
cnfPath = self._confPath / nwFiles.CONF_FILE cnfPath = self._confPath / nwFiles.CONF_FILE
try: try:
with open(cnfPath, mode="w", encoding="utf-8") as outFile: with open(cnfPath, mode="w", encoding="utf-8") as outFile:
theConf.write(outFile) conf.write(outFile)
except Exception as exc: except Exception as exc:
logger.error("Could not save config file") logger.error("Could not save config file")
logException() logException()
@@ -758,26 +769,14 @@ class Config:
# Internal Functions # Internal Functions
## ##
def _packList(self, inData): def _packList(self, data: list) -> str:
"""Pack a list of items into a comma-separated string for saving """Pack a list of items into a comma-separated string for saving
to the config file. to the config file.
""" """
return ", ".join([str(inVal) for inVal in inData]) return ", ".join([str(inVal) for inVal in data])
def _checkNone(self, checkVal): def _checkOptionalPackages(self) -> None:
"""Return a NoneType if the value corresponds to None, otherwise """Check optional packages used by some features."""
return the value unchanged.
"""
if checkVal is None:
return None
if isinstance(checkVal, str):
if checkVal.lower() == "none":
return None
return checkVal
def _checkOptionalPackages(self):
"""Check if we have the optional packages used by some features.
"""
try: try:
import enchant # noqa: F401 import enchant # noqa: F401
except ImportError: except ImportError:
@@ -793,14 +792,13 @@ class Config:
class RecentProjects: class RecentProjects:
def __init__(self, config): def __init__(self, config: Config) -> None:
self._conf = config self._conf = config
self._data = {} self._data = {}
return return
def loadCache(self): def loadCache(self) -> bool:
"""Load the cache file for recent projects. """Load the cache file for recent projects."""
"""
self._data = {} self._data = {}
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE) cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
@@ -823,9 +821,8 @@ class RecentProjects:
return True return True
def saveCache(self): def saveCache(self) -> bool:
"""Save the cache dictionary of recent projects. """Save the cache dictionary of recent projects."""
"""
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE) cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
cacheTemp = cacheFile.with_suffix(".tmp") cacheTemp = cacheFile.with_suffix(".tmp")
try: try:
@@ -839,27 +836,27 @@ class RecentProjects:
return True return True
def listEntries(self): def listEntries(self) -> list[tuple[str, str, int, int]]:
"""List all items in the cache. """List all items in the cache."""
""" return [
return [(k, e["title"], e["words"], e["time"]) for k, e in self._data.items()] (str(k), str(e["title"]), checkInt(e["words"], 0), checkInt(e["time"], 0))
for k, e in self._data.items()
]
def update(self, projPath, projTitle, wordCount, saveTime): def update(self, path: str | Path, title: str, words: int, saved: float | int) -> None:
"""Add or update recent cache information on a given project. """Add or update recent cache information on a given project."""
""" self._data[str(path)] = {
self._data[str(projPath)] = { "title": title,
"title": projTitle, "words": int(words),
"words": int(wordCount), "time": int(saved),
"time": int(saveTime),
} }
self.saveCache() self.saveCache()
return return
def remove(self, projPath): def remove(self, path: str | Path) -> None:
"""Try to remove a path from the recent projects cache. """Try to remove a path from the recent projects cache."""
""" if self._data.pop(str(path), None) is not None:
if self._data.pop(str(projPath), None) is not None: logger.debug("Removed recent: %s", path)
logger.debug("Removed recent: %s", projPath)
self.saveCache() self.saveCache()
return return
+16 -16
View File
@@ -153,7 +153,7 @@ class BuildSettings:
The settings can be packed/unpacked to/from a dictionary for JSON. The settings can be packed/unpacked to/from a dictionary for JSON.
""" """
def __init__(self): def __init__(self) -> None:
self._name = "" self._name = ""
self._uuid = str(uuid.uuid4()) self._uuid = str(uuid.uuid4())
self._path = Path.home() self._path = Path.home()
@@ -239,12 +239,12 @@ class BuildSettings:
# Setters # Setters
## ##
def setName(self, name: str): def setName(self, name: str) -> None:
"""Set the build setting display name.""" """Set the build setting display name."""
self._name = str(name) self._name = str(name)
return return
def setBuildID(self, value: str | uuid.UUID): def setBuildID(self, value: str | uuid.UUID) -> None:
"""Set a UUID build ID.""" """Set a UUID build ID."""
value = checkUuid(value, "") value = checkUuid(value, "")
if not value: if not value:
@@ -253,7 +253,7 @@ class BuildSettings:
self._uuid = value self._uuid = value
return return
def setLastPath(self, path: Path | str | None): def setLastPath(self, path: Path | str | None) -> None:
"""Set the last used build path.""" """Set the last used build path."""
if isinstance(path, str): if isinstance(path, str):
path = Path(path) path = Path(path)
@@ -264,41 +264,41 @@ class BuildSettings:
self._changed = True self._changed = True
return return
def setLastBuildName(self, name: str): def setLastBuildName(self, name: str) -> None:
"""Set the last used build name.""" """Set the last used build name."""
self._build = str(name).strip() self._build = str(name).strip()
self._changed = True self._changed = True
return return
def setLastFormat(self, value: nwBuildFmt): def setLastFormat(self, value: nwBuildFmt) -> None:
"""Set the last used build format.""" """Set the last used build format."""
if isinstance(value, nwBuildFmt): if isinstance(value, nwBuildFmt):
self._format = value self._format = value
self._changed = True self._changed = True
return return
def setFiltered(self, tHandle: str): def setFiltered(self, tHandle: str) -> None:
"""Set an item as filtered.""" """Set an item as filtered."""
self._excluded.discard(tHandle) self._excluded.discard(tHandle)
self._included.discard(tHandle) self._included.discard(tHandle)
self._changed = True self._changed = True
return return
def setIncluded(self, tHandle: str): def setIncluded(self, tHandle: str) -> None:
"""Set an item as explicitly included.""" """Set an item as explicitly included."""
self._excluded.discard(tHandle) self._excluded.discard(tHandle)
self._included.add(tHandle) self._included.add(tHandle)
self._changed = True self._changed = True
return return
def setExcluded(self, tHandle: str): def setExcluded(self, tHandle: str) -> None:
"""Set an item as explicitly excluded.""" """Set an item as explicitly excluded."""
self._excluded.add(tHandle) self._excluded.add(tHandle)
self._included.discard(tHandle) self._included.discard(tHandle)
self._changed = True self._changed = True
return return
def setAllowRoot(self, tHandle: str, state: bool): def setAllowRoot(self, tHandle: str, state: bool) -> None:
"""Set a specific root folder as allowed or not.""" """Set a specific root folder as allowed or not."""
if state is True: if state is True:
self._skipRoot.discard(tHandle) self._skipRoot.discard(tHandle)
@@ -386,7 +386,7 @@ class BuildSettings:
return result return result
def resetChangedState(self): def resetChangedState(self) -> None:
"""Reset the changed status of the settings object. This must be """Reset the changed status of the settings object. This must be
called when the changes have been safely saved or passed on. called when the changes have been safely saved or passed on.
""" """
@@ -410,7 +410,7 @@ class BuildSettings:
} }
} }
def unpack(self, data: dict): def unpack(self, data: dict) -> None:
"""Unpack a dictionary and populate the class.""" """Unpack a dictionary and populate the class."""
settings = data.get("settings", {}) settings = data.get("settings", {})
content = data.get("content", {}) content = data.get("content", {})
@@ -454,13 +454,13 @@ class BuildCollection:
project folder. project folder.
""" """
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
self._builds = {} self._builds = {}
self._loadCollection() self._loadCollection()
return return
def __len__(self): def __len__(self) -> int:
"""Return the number of builds.""" """Return the number of builds."""
return len(self._builds) return len(self._builds)
@@ -476,7 +476,7 @@ class BuildCollection:
build.unpack(self._builds[buildID]) build.unpack(self._builds[buildID])
return build return build
def setBuild(self, build: BuildSettings): def setBuild(self, build: BuildSettings) -> None:
"""Set build settings data in the collection.""" """Set build settings data in the collection."""
if isinstance(build, BuildSettings): if isinstance(build, BuildSettings):
buildID = build.buildID buildID = build.buildID
@@ -484,7 +484,7 @@ class BuildCollection:
self._saveCollection() self._saveCollection()
return return
def removeBuild(self, buildID: str): def removeBuild(self, buildID: str) -> None:
"""Remove the a build from the collection.""" """Remove the a build from the collection."""
self._builds.pop(buildID, None) self._builds.pop(buildID, None)
self._saveCollection() self._saveCollection()
+7 -6
View File
@@ -80,7 +80,7 @@ class DocMerger:
and a new doc label. Calling this function resets the class. and a new doc label. Calling this function resets the class.
""" """
srcItem = self._project.tree[srcHandle] srcItem = self._project.tree[srcHandle]
if srcItem is None: if srcItem is None or srcItem.itemParent is None:
return None return None
newHandle = self._project.newFile(docLabel, srcItem.itemParent) newHandle = self._project.newFile(docLabel, srcItem.itemParent)
@@ -210,7 +210,7 @@ class DocSplitter:
"""An iterator that will write each document in the buffer, and """An iterator that will write each document in the buffer, and
return its new handle, parent handle, and sibling handle. return its new handle, parent handle, and sibling handle.
""" """
if self._srcHandle is None or self._srcItem is None: if self._srcHandle is None or self._srcItem is None or self._parHandle is None:
return return
pHandle = self._parHandle pHandle = self._parHandle
@@ -385,9 +385,10 @@ class ProjectBuilder:
aDoc = project.storage.getDocument(hChapter) aDoc = project.storage.getDocument(hChapter)
aDoc.writeDocument(f"## {lblNewChapter}\n\n") aDoc.writeDocument(f"## {lblNewChapter}\n\n")
hScene = project.newFile(lblNewScene, hChapter) if hChapter:
aDoc = project.storage.getDocument(hScene) hScene = project.newFile(lblNewScene, hChapter)
aDoc.writeDocument(f"### {lblNewScene}\n\n") aDoc = project.storage.getDocument(hScene)
aDoc.writeDocument(f"### {lblNewScene}\n\n")
project.newRoot(nwItemClass.PLOT) project.newRoot(nwItemClass.PLOT)
project.newRoot(nwItemClass.CHARACTER) project.newRoot(nwItemClass.CHARACTER)
@@ -418,7 +419,7 @@ class ProjectBuilder:
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n") aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
# Create chapter scenes # Create chapter scenes
if numScenes > 0: if numScenes > 0 and cHandle:
for sc in range(numScenes): for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}") scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = project.newFile(scTitle, cHandle) sHandle = project.newFile(scTitle, cHandle)
+3 -3
View File
@@ -54,7 +54,7 @@ class NWBuildDocument:
__slots__ = ("_project", "_build", "_queue", "_error", "_cache") __slots__ = ("_project", "_build", "_queue", "_error", "_cache")
def __init__(self, project: NWProject, build: BuildSettings): def __init__(self, project: NWProject, build: BuildSettings) -> None:
self._project = project self._project = project
self._build = build self._build = build
self._queue = [] self._queue = []
@@ -91,12 +91,12 @@ class NWBuildDocument:
# Methods # Methods
## ##
def addDocument(self, tHandle: str): def addDocument(self, tHandle: str) -> None:
"""Add a document to the build queue manually.""" """Add a document to the build queue manually."""
self._queue.append(tHandle) self._queue.append(tHandle)
return return
def queueAll(self): def queueAll(self) -> None:
"""Queue all document as defined by the build settings.""" """Queue all document as defined by the build settings."""
self._queue = [] self._queue = []
filtered = self._build.buildItemFilter(self._project) filtered = self._build.buildItemFilter(self._project)
+7 -14
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Project Wrapper novelWriter Project Wrapper
============================= =============================
The parent class for a novelWriter project
File History: File History:
Created: 2018-09-29 [0.0.1] Created: 2018-09-29 [0.0.1]
@@ -416,14 +415,6 @@ class NWProject(QObject):
logger.info("Backing up project") logger.info("Backing up project")
self.mainGui.setStatus(self.tr("Backing up project ...")) self.mainGui.setStatus(self.tr("Backing up project ..."))
backupPath = CONFIG.backupPath()
if not isinstance(backupPath, Path):
self.mainGui.makeAlert(self.tr(
"Cannot backup project because no valid backup path is set. "
"Please set a valid backup location in Preferences."
), level=nwAlert.ERROR)
return False
if not self._data.name: if not self._data.name:
self.mainGui.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Cannot backup project because no project name is set. " "Cannot backup project because no project name is set. "
@@ -432,9 +423,10 @@ class NWProject(QObject):
return False return False
cleanName = makeFileNameSafe(self._data.name) cleanName = makeFileNameSafe(self._data.name)
backupPath = CONFIG.backupPath()
baseDir = backupPath / cleanName baseDir = backupPath / cleanName
try: try:
baseDir.mkdir(exist_ok=True) baseDir.mkdir(exist_ok=True, parents=True)
except Exception as exc: except Exception as exc:
self.mainGui.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Could not create backup folder." "Could not create backup folder."
@@ -444,11 +436,12 @@ class NWProject(QObject):
timeStamp = formatTimeStamp(time(), fileSafe=True) timeStamp = formatTimeStamp(time(), fileSafe=True)
archName = baseDir / f"{cleanName} {timeStamp}.zip" archName = baseDir / f"{cleanName} {timeStamp}.zip"
if self._storage.zipIt(archName, compression=2): if self._storage.zipIt(archName, compression=2):
size = archName.stat().st_size size = formatInt(archName.stat().st_size)
if doNotify: if doNotify:
self.mainGui.makeAlert(self.tr( self.mainGui.makeAlert(
"Backup archive file written to: {0} [{1}B]" self.tr("Created a backup of your project of size {0}B.").format(size),
).format(str(archName), formatInt(size))) info=self.tr("Path: {0}").format(str(backupPath))
)
else: else:
self.mainGui.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Could not write backup archive." "Could not write backup archive."
+40 -37
View File
@@ -26,13 +26,16 @@ from __future__ import annotations
import uuid import uuid
import logging import logging
from typing import Any from typing import TYPE_CHECKING, Any
from novelwriter.common import ( from novelwriter.common import (
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
) )
from novelwriter.core.status import NWStatus from novelwriter.core.status import NWStatus
if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -43,9 +46,9 @@ class NWProjectData:
the list of project items. the list of project items.
""" """
def __init__(self, theProject): def __init__(self, project: NWProject) -> None:
self.theProject = theProject self._project = project
# Project Meta # Project Meta
self._uuid = "" self._uuid = ""
@@ -184,16 +187,16 @@ class NWProjectData:
# Methods # Methods
## ##
def incSaveCount(self): def incSaveCount(self) -> None:
"""Increment the save count by one.""" """Increment the save count by one."""
self._saveCount += 1 self._saveCount += 1
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def incAutoCount(self): def incAutoCount(self) -> None:
"""Increment the auto save count by one.""" """Increment the auto save count by one."""
self._autoCount += 1 self._autoCount += 1
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
## ##
@@ -208,93 +211,93 @@ class NWProjectData:
# Setters # Setters
## ##
def setUuid(self, value: Any): def setUuid(self, value: Any) -> None:
"""Set the project id.""" """Set the project id."""
value = checkUuid(value, "") value = checkUuid(value, "")
if not value: if not value:
self._uuid = str(uuid.uuid4()) self._uuid = str(uuid.uuid4())
elif value != self._uuid: elif value != self._uuid:
self._uuid = value self._uuid = value
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setName(self, value: str | None): def setName(self, value: str | None) -> None:
"""Set a new project name.""" """Set a new project name."""
if value != self._name: if value != self._name:
self._name = simplified(str(value or "")) self._name = simplified(str(value or ""))
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setTitle(self, value: str | None): def setTitle(self, value: str | None) -> None:
"""Set a new novel title.""" """Set a new novel title."""
if value != self._title: if value != self._title:
self._title = simplified(str(value or "")) self._title = simplified(str(value or ""))
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setAuthor(self, value: str | None): def setAuthor(self, value: str | None) -> None:
"""Set the author value.""" """Set the author value."""
if value != self._title: if value != self._title:
self._author = simplified(str(value or "")) self._author = simplified(str(value or ""))
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setSaveCount(self, value: Any): def setSaveCount(self, value: Any) -> None:
"""Set the save count from last session.""" """Set the save count from last session."""
self._saveCount = checkInt(value, 0) self._saveCount = checkInt(value, 0)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setAutoCount(self, value: Any): def setAutoCount(self, value: Any) -> None:
"""Set the auto save count from last session.""" """Set the auto save count from last session."""
self._autoCount = checkInt(value, 0) self._autoCount = checkInt(value, 0)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setEditTime(self, value: Any): def setEditTime(self, value: Any) -> None:
"""Set the edit time from last session.""" """Set the edit time from last session."""
self._editTime = checkInt(value, 0) self._editTime = checkInt(value, 0)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setDoBackup(self, value: Any): def setDoBackup(self, value: Any) -> None:
"""Set the do write backup flag.""" """Set the do write backup flag."""
if value != self._doBackup: if value != self._doBackup:
self._doBackup = checkBool(value, False) self._doBackup = checkBool(value, False)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setLanguage(self, value: str | None): def setLanguage(self, value: str | None) -> None:
"""Set the project language.""" """Set the project language."""
if value != self._language: if value != self._language:
self._language = checkStringNone(value, None) self._language = checkStringNone(value, None)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setSpellCheck(self, value: Any): def setSpellCheck(self, value: Any) -> None:
"""Set the spell check flag.""" """Set the spell check flag."""
if value != self._spellCheck: if value != self._spellCheck:
self._spellCheck = checkBool(value, False) self._spellCheck = checkBool(value, False)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setSpellLang(self, value: str | None): def setSpellLang(self, value: str | None) -> None:
"""Set the spell check language.""" """Set the spell check language."""
if value != self._spellLang: if value != self._spellLang:
self._spellLang = checkStringNone(value, None) self._spellLang = checkStringNone(value, None)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setLastHandle(self, value: str | None, component: str): def setLastHandle(self, value: str | None, component: str) -> None:
"""Set a last used handle into the handle registry for a given """Set a last used handle into the handle registry for a given
component. component.
""" """
if isinstance(component, str): if isinstance(component, str):
self._lastHandle[component] = checkStringNone(value, None) self._lastHandle[component] = checkStringNone(value, None)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setLastHandles(self, value: dict): def setLastHandles(self, value: dict) -> None:
"""Set the full last handles dictionary to a new set of values. """Set the full last handles dictionary to a new set of values.
This is intended to be used at project load. This is intended to be used at project load.
""" """
@@ -302,10 +305,10 @@ class NWProjectData:
for key, entry in value.items(): for key, entry in value.items():
if key in self._lastHandle: if key in self._lastHandle:
self._lastHandle[key] = str(entry) if isHandle(entry) else None self._lastHandle[key] = str(entry) if isHandle(entry) else None
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
def setInitCounts(self, novel: Any = None, notes: Any = None): def setInitCounts(self, novel: Any = None, notes: Any = None) -> None:
"""Set the word count totals for novel and note files.""" """Set the word count totals for novel and note files."""
if novel is not None: if novel is not None:
self._initCounts[0] = checkInt(novel, 0) self._initCounts[0] = checkInt(novel, 0)
@@ -315,7 +318,7 @@ class NWProjectData:
self._currCounts[1] = checkInt(notes, 0) self._currCounts[1] = checkInt(notes, 0)
return return
def setCurrCounts(self, novel: Any = None, notes: Any = None): def setCurrCounts(self, novel: Any = None, notes: Any = None) -> None:
"""Set the word count totals for novel and note files.""" """Set the word count totals for novel and note files."""
if novel is not None: if novel is not None:
self._currCounts[0] = checkInt(novel, 0) self._currCounts[0] = checkInt(novel, 0)
@@ -323,14 +326,14 @@ class NWProjectData:
self._currCounts[1] = checkInt(notes, 0) self._currCounts[1] = checkInt(notes, 0)
return return
def setAutoReplace(self, value: dict): def setAutoReplace(self, value: dict) -> None:
"""Set the auto-replace dictionary.""" """Set the auto-replace dictionary."""
if isinstance(value, dict): if isinstance(value, dict):
self._autoReplace = {} self._autoReplace = {}
for key, entry in value.items(): for key, entry in value.items():
if isinstance(entry, str): if isinstance(entry, str):
self._autoReplace[key] = simplified(entry) self._autoReplace[key] = simplified(entry)
self.theProject.setProjectChanged(True) self._project.setProjectChanged(True)
return return
# END Class NWProjectData # END Class NWProjectData
+15 -11
View File
@@ -110,7 +110,7 @@ class ProjectXMLReader:
Rev 1: Drops the titleFormat section of settings. Rev 1: Drops the titleFormat section of settings.
""" """
def __init__(self, path): def __init__(self, path: str | Path) -> None:
self._path = Path(path) self._path = Path(path)
self._state = XMLReadState.NO_ACTION self._state = XMLReadState.NO_ACTION
self._root = "" self._root = ""
@@ -236,7 +236,7 @@ class ProjectXMLReader:
# Internal Functions # Internal Functions
## ##
def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData): def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData) -> None:
"""Parse the project section of the XML file.""" """Parse the project section of the XML file."""
logger.debug("Parsing <project> section") logger.debug("Parsing <project> section")
@@ -267,7 +267,7 @@ class ProjectXMLReader:
return return
def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData): def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData) -> None:
"""Parse the settings section of the XML file.""" """Parse the settings section of the XML file."""
logger.debug("Parsing <settings> section") logger.debug("Parsing <settings> section")
@@ -307,7 +307,9 @@ class ProjectXMLReader:
return return
def _parseProjectContent(self, xSection: ET.Element, data: NWProjectData, content: list): def _parseProjectContent(
self, xSection: ET.Element, data: NWProjectData, content: list
) -> None:
"""Parse the content section of the XML file.""" """Parse the content section of the XML file."""
logger.debug("Parsing <content> section") logger.debug("Parsing <content> section")
@@ -362,7 +364,9 @@ class ProjectXMLReader:
return return
def _parseProjectContentLegacy(self, xSection: ET.Element, data: NWProjectData, content: list): def _parseProjectContentLegacy(
self, xSection: ET.Element, data: NWProjectData, content: list
) -> None:
"""Parse the content section of the XML file for older versions.""" """Parse the content section of the XML file for older versions."""
logger.debug("Parsing <content> section (legacy format)") logger.debug("Parsing <content> section (legacy format)")
@@ -438,7 +442,7 @@ class ProjectXMLReader:
return return
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus): def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus) -> None:
"""Parse a status or importance entry.""" """Parse a status or importance entry."""
for xEntry in xItem: for xEntry in xItem:
if xEntry.tag == "entry": if xEntry.tag == "entry":
@@ -447,7 +451,7 @@ class ProjectXMLReader:
green = checkInt(xEntry.attrib.get("green", 0), 0) green = checkInt(xEntry.attrib.get("green", 0), 0)
blue = checkInt(xEntry.attrib.get("blue", 0), 0) blue = checkInt(xEntry.attrib.get("blue", 0), 0)
count = checkInt(xEntry.attrib.get("count", 0), 0) count = checkInt(xEntry.attrib.get("count", 0), 0)
sObject.write(key, xEntry.text, (red, green, blue), count) sObject.write(key, xEntry.text or "", (red, green, blue), count)
return return
def _parseDictKeyText(self, xItem: ET.Element) -> dict: def _parseDictKeyText(self, xItem: ET.Element) -> dict:
@@ -460,7 +464,7 @@ class ProjectXMLReader:
result[xEntry.attrib["key"]] = checkString(xEntry.text, "") result[xEntry.attrib["key"]] = checkString(xEntry.text, "")
return result return result
def _parseDictTagText(self, xItem): def _parseDictTagText(self, xItem) -> dict:
"""Parse a dictionary stored with key as the tag and the value """Parse a dictionary stored with key as the tag and the value
as the text property. as the text property.
""" """
@@ -476,7 +480,7 @@ class ProjectXMLWriter:
very latest spec. very latest spec.
""" """
def __init__(self, path): def __init__(self, path: str | Path) -> None:
self._path = Path(path) self._path = Path(path)
self._error = None self._error = None
return return
@@ -585,13 +589,13 @@ class ProjectXMLWriter:
def _packSingleValue( def _packSingleValue(
self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None
): ) -> None:
"""Pack a single value into an XML element.""" """Pack a single value into an XML element."""
xItem = ET.SubElement(xParent, name, attrib=attrib or {}) xItem = ET.SubElement(xParent, name, attrib=attrib or {})
xItem.text = str(value) or "" xItem.text = str(value) or ""
return return
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict): def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict) -> None:
"""Pack the entries of a dictionary into an XML element.""" """Pack the entries of a dictionary into an XML element."""
xItem = ET.SubElement(xParent, name) xItem = ET.SubElement(xParent, name)
for key, value in data.items(): for key, value in data.items():
+2 -2
View File
@@ -47,7 +47,7 @@ class NWSessionLog:
format. That is, one JSON object per line. format. That is, one JSON object per line.
""" """
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
self._start = 0.0 self._start = 0.0
return return
@@ -65,7 +65,7 @@ class NWSessionLog:
# Methods # Methods
## ##
def startSession(self): def startSession(self) -> None:
"""Start the writing session.""" """Start the writing session."""
self._start = time() self._start = time()
return return
+5 -5
View File
@@ -45,7 +45,7 @@ class NWSpellEnchant:
between spell check tools. between spell check tools.
""" """
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
self._dictObj = FakeEnchant() self._dictObj = FakeEnchant()
self._userDict = UserDictionary(project) self._userDict = UserDictionary(project)
@@ -165,7 +165,7 @@ class NWSpellEnchant:
class FakeEnchant: class FakeEnchant:
"""Fallback for when Enchant is selected, but not installed.""" """Fallback for when Enchant is selected, but not installed."""
def __init__(self): def __init__(self) -> None:
class FakeProvider: class FakeProvider:
name = "" name = ""
@@ -189,7 +189,7 @@ class FakeEnchant:
class UserDictionary: class UserDictionary:
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
self._words = set() self._words = set()
self._path = None self._path = None
@@ -210,7 +210,7 @@ class UserDictionary:
self._words.add(word) self._words.add(word)
return True return True
def load(self): def load(self) -> None:
"""Load the user's dictionary.""" """Load the user's dictionary."""
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE) self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
if not isinstance(self._path, Path): if not isinstance(self._path, Path):
@@ -224,7 +224,7 @@ class UserDictionary:
logException() logException()
return return
def save(self): def save(self) -> None:
"""Save the user's dictionary.""" """Save the user's dictionary."""
if self._path is None: if self._path is None:
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE) self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
+48 -64
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Project Item Status Class novelWriter Project Item Status Class
======================================= =======================================
Data class for the status/importance settings of a project item
File History: File History:
Created: 2019-05-19 [0.1.3] Created: 2019-05-19 [0.1.3]
@@ -23,16 +22,22 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import random import random
import logging import logging
from typing import TYPE_CHECKING, ItemsView, Iterator, KeysView, Literal, ValuesView
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
from PyQt5.QtCore import QRectF, Qt from PyQt5.QtCore import QRectF, Qt
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.common import minmax, simplified from novelwriter.common import minmax, simplified
if TYPE_CHECKING: # pragma: no cover
from typing import TypeGuard # Requires Python 3.10
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -41,9 +46,9 @@ class NWStatus:
STATUS = 1 STATUS = 1
IMPORT = 2 IMPORT = 2
def __init__(self, type): def __init__(self, kind: Literal[1, 2]) -> None:
self._type = type self._type = kind
self._store = {} self._store = {}
self._default = None self._default = None
@@ -66,7 +71,7 @@ class NWStatus:
return return
def write(self, key, name, col, count=None): def write(self, key: str | None, name: str, col: tuple, count: int | None = None) -> str:
"""Add or update a status entry. If the key is invalid, a new """Add or update a status entry. If the key is invalid, a new
key is generated. key is generated.
""" """
@@ -96,10 +101,8 @@ class NWStatus:
return key return key
def remove(self, key): def remove(self, key: str) -> bool:
"""Remove an entry in the list, but not if the count is larger """Remove an entry in the list, except if the count > 0."""
than 0.
"""
if key not in self._store: if key not in self._store:
return False return False
if self._store[key]["count"] > 0: if self._store[key]["count"] > 0:
@@ -116,59 +119,48 @@ class NWStatus:
return True return True
def check(self, value): def check(self, value: str) -> str:
"""Check the key against the stored status names. """Check the key against the stored status names."""
"""
if self._isKey(value) and value in self._store: if self._isKey(value) and value in self._store:
return value return value
elif self._default is not None: elif self._default is not None:
return self._default return self._default
else: return ""
return ""
def name(self, key): def name(self, key: str | None) -> str:
"""Return the name associated with a given key. """Return the name associated with a given key."""
"""
if key in self._store: if key in self._store:
return self._store[key]["name"] return self._store[key]["name"]
elif self._default is not None: elif self._default is not None:
return self._store[self._default]["name"] return self._store[self._default]["name"]
else: return ""
return ""
def cols(self, key): def cols(self, key: str | None) -> tuple[int, int, int]:
"""Return the colours associated with a given key. """Return the colours associated with a given key."""
"""
if key in self._store: if key in self._store:
return self._store[key]["cols"] return self._store[key]["cols"]
elif self._default is not None: elif self._default is not None:
return self._store[self._default]["cols"] return self._store[self._default]["cols"]
else: return 100, 100, 100
return (100, 100, 100)
def count(self, key): def count(self, key: str | None) -> int:
"""Return the count associated with a given key. """Return the count associated with a given key."""
"""
if key in self._store: if key in self._store:
return self._store[key]["count"] return self._store[key]["count"]
elif self._default is not None: elif self._default is not None:
return self._store[self._default]["count"] return self._store[self._default]["count"]
else: return 0
return 0
def icon(self, key): def icon(self, key: str | None) -> QIcon:
"""Return the icon associated with a given key. """Return the icon associated with a given key."""
"""
if key in self._store: if key in self._store:
return self._store[key]["icon"] return self._store[key]["icon"]
elif self._default is not None: elif self._default is not None:
return self._store[self._default]["icon"] return self._store[self._default]["icon"]
else: return self._defaultIcon
return self._defaultIcon
def reorder(self, order): def reorder(self, order: list[str]) -> bool:
"""Reorder the items according to list. """Reorder the items according to list."""
"""
if len(order) != len(self._store): if len(order) != len(self._store):
logger.error("Length mismatch between new and old order") logger.error("Length mismatch between new and old order")
return False return False
@@ -188,23 +180,20 @@ class NWStatus:
return True return True
def resetCounts(self): def resetCounts(self) -> None:
"""Clear the counts of references to the status entries. """Clear the counts of references to the status entries."""
"""
for key in self._store: for key in self._store:
self._store[key]["count"] = 0 self._store[key]["count"] = 0
return return
def increment(self, key): def increment(self, key: str) -> None:
"""Increment the counter for a given entry. """Increment the counter for a given entry."""
"""
if key in self._store: if key in self._store:
self._store[key]["count"] += 1 self._store[key]["count"] += 1
return return
def pack(self): def pack(self) -> Iterator[tuple[str, dict]]:
"""Pack the status entries into a dictionary. """Pack the status entries into a dictionary."""
"""
for key, data in self._store.items(): for key, data in self._store.items():
yield (data["name"], { yield (data["name"], {
"key": key, "key": key,
@@ -215,25 +204,22 @@ class NWStatus:
}) })
return return
def unpack(self, data): def unpack(self, data: dict) -> None:
"""Unpack a data dictionary and set the class values. """Unpack a data dictionary and set the class values."""
"""
self._store = {} self._store = {}
self._default = None self._default = None
for key, entry in data.items(): for key, entry in data.items():
label = entry.get("label", "") label = entry.get("label", "")
colour = entry.get("colour", (100, 100, 100)) colour = entry.get("colour", (100, 100, 100))
count = entry.get("count", 0) count = entry.get("count", 0)
self.write(key, label, colour, count) self.write(key, label, colour, count)
return
return True
## ##
# Internal Functions # Internal Functions
## ##
def _newKey(self): def _newKey(self) -> str:
"""Generate a new key for a status flag. This method is """Generate a new key for a status flag. This method is
recursive, but should only fail if there is an issue with the recursive, but should only fail if there is an issue with the
random number generator or the user has added a lot of status random number generator or the user has added a lot of status
@@ -245,9 +231,8 @@ class NWStatus:
key = self._newKey() key = self._newKey()
return key return key
def _isKey(self, value): def _isKey(self, value: str | None) -> TypeGuard[str]:
"""Check if a value is a key or not. """Check if a value is a key or not."""
"""
if not isinstance(value, str): if not isinstance(value, str):
return False return False
if len(value) != 7: if len(value) != 7:
@@ -259,9 +244,8 @@ class NWStatus:
return False return False
return True return True
def _createIcon(self, red, green, blue): def _createIcon(self, red: int, green: int, blue: int) -> QIcon:
"""Generate an icon for a status label. """Generate an icon for a status label."""
"""
pixmap = QPixmap(self._iPX, self._iPX) pixmap = QPixmap(self._iPX, self._iPX)
pixmap.fill(Qt.transparent) pixmap.fill(Qt.transparent)
@@ -276,22 +260,22 @@ class NWStatus:
# Iterator Bits # Iterator Bits
## ##
def __len__(self): def __len__(self) -> int:
return len(self._store) return len(self._store)
def __getitem__(self, key): def __getitem__(self, key: str) -> dict:
return self._store[key] return self._store[key]
def __iter__(self): def __iter__(self) -> Iterator[dict]:
return iter(self._store) return iter(self._store)
def keys(self): def keys(self) -> KeysView[str]:
return self._store.keys() return self._store.keys()
def items(self): def items(self) -> ItemsView[str, dict]:
return self._store.items() return self._store.items()
def values(self): def values(self) -> ValuesView[dict]:
return self._store.values() return self._store.values()
# END Class NWStatus # END Class NWStatus
+9 -9
View File
@@ -55,7 +55,7 @@ class NWStorage:
MODE_INPLACE = 1 MODE_INPLACE = 1
MODE_ARCHIVE = 2 MODE_ARCHIVE = 2
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
self._storagePath = None self._storagePath = None
self._runtimePath = None self._runtimePath = None
@@ -63,7 +63,7 @@ class NWStorage:
self._openMode = self.MODE_INACTIVE self._openMode = self.MODE_INACTIVE
return return
def clear(self): def clear(self) -> None:
"""Reset internal variables.""" """Reset internal variables."""
self._storagePath = None self._storagePath = None
self._runtimePath = None self._runtimePath = None
@@ -145,7 +145,7 @@ class NWStorage:
return True return True
return True return True
def closeSession(self): def closeSession(self) -> None:
"""Run tasks related to closing the session.""" """Run tasks related to closing the session."""
self.clearLockFile() self.clearLockFile()
self.clear() self.clear()
@@ -353,11 +353,11 @@ class _LegacyStorage:
file/folder layout to the current project format. file/folder layout to the current project format.
""" """
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
return return
def legacyDataFolder(self, path: Path, child: Path): def legacyDataFolder(self, path: Path, child: Path) -> None:
"""Handle the content of a legacy data folder from a version 1.0 """Handle the content of a legacy data folder from a version 1.0
project. project.
""" """
@@ -396,7 +396,7 @@ class _LegacyStorage:
return return
def deprecatedFiles(self, path: Path): def deprecatedFiles(self, path: Path) -> None:
"""Handle files that are no longer used by novelWriter.""" """Handle files that are no longer used by novelWriter."""
self._convertOldWordList( # Changed in 2.1 Beta 1 self._convertOldWordList( # Changed in 2.1 Beta 1
path / "meta" / "wordlist.txt", path / "meta" / "wordlist.txt",
@@ -440,7 +440,7 @@ class _LegacyStorage:
# Internal Functions # Internal Functions
## ##
def _convertOldWordList(self, wordList: Path, wordJson: Path): def _convertOldWordList(self, wordList: Path, wordJson: Path) -> None:
"""Convert the old word list plain text file to new format.""" """Convert the old word list plain text file to new format."""
if wordJson.exists() or not wordList.exists(): if wordJson.exists() or not wordList.exists():
# If the new file already exists, we won't overwrite it # If the new file already exists, we won't overwrite it
@@ -466,7 +466,7 @@ class _LegacyStorage:
return return
def _convertOldLogFile(self, sessLog: Path, sessJson: Path): def _convertOldLogFile(self, sessLog: Path, sessJson: Path) -> None:
"""Convert the old text log file format to the new JSON Lines """Convert the old text log file format to the new JSON Lines
format. format.
""" """
@@ -507,7 +507,7 @@ class _LegacyStorage:
return return
def _convertOldOptionsFile(self, optsOld: Path, optsNew: Path): def _convertOldOptionsFile(self, optsOld: Path, optsNew: Path) -> None:
"""Convert the old options state file format to the format.""" """Convert the old options state file format to the format."""
if optsNew.exists() or not optsOld.exists(): if optsNew.exists() or not optsOld.exists():
# If the new file already exists, we won't overwrite it # If the new file already exists, we won't overwrite it
+12 -12
View File
@@ -49,12 +49,12 @@ class ToHtml(Tokenizer):
M_EXPORT = 1 # Tweak output for saving to HTML or printing M_EXPORT = 1 # Tweak output for saving to HTML or printing
M_EBOOK = 2 # Tweak output for converting to epub M_EBOOK = 2 # Tweak output for converting to epub
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
super().__init__(project) super().__init__(project)
self._genMode = self.M_EXPORT self._genMode = self.M_EXPORT
self._cssStyles = True self._cssStyles = True
self._fullHTML = [] self._fullHTML: list[str] = []
# Internals # Internals
self._trMap = {} self._trMap = {}
@@ -67,14 +67,14 @@ class ToHtml(Tokenizer):
## ##
@property @property
def fullHTML(self): def fullHTML(self) -> list[str]:
return self._fullHTML return self._fullHTML
## ##
# Setters # Setters
## ##
def setPreview(self, doComments: bool, doSynopsis: bool): def setPreview(self, doComments: bool, doSynopsis: bool) -> None:
"""If we're using this class to generate markdown preview, we """If we're using this class to generate markdown preview, we
need to make a few changes to formatting, which is managed by need to make a few changes to formatting, which is managed by
these flags. these flags.
@@ -85,14 +85,14 @@ class ToHtml(Tokenizer):
self._doSynopsis = doSynopsis self._doSynopsis = doSynopsis
return return
def setStyles(self, cssStyles: bool): def setStyles(self, cssStyles: bool) -> None:
"""Enable or disable CSS styling. Some elements may still have """Enable or disable CSS styling. Some elements may still have
class tags. class tags.
""" """
self._cssStyles = cssStyles self._cssStyles = cssStyles
return return
def setReplaceUnicode(self, doReplace: bool): def setReplaceUnicode(self, doReplace: bool) -> None:
"""Set the translation map to either minimal or full unicode for """Set the translation map to either minimal or full unicode for
html entities replacement. html entities replacement.
""" """
@@ -113,7 +113,7 @@ class ToHtml(Tokenizer):
"""Return the size of the full HTML result.""" """Return the size of the full HTML result."""
return sum([len(x) for x in self._fullHTML]) return sum([len(x) for x in self._fullHTML])
def doPreProcessing(self): def doPreProcessing(self) -> None:
"""Extend the auto-replace to also properly encode some unicode """Extend the auto-replace to also properly encode some unicode
characters into their respective HTML entities. characters into their respective HTML entities.
""" """
@@ -121,7 +121,7 @@ class ToHtml(Tokenizer):
self._text = self._text.translate(self._trMap) self._text = self._text.translate(self._trMap)
return return
def doConvert(self): def doConvert(self) -> None:
"""Convert the list of text tokens into a HTML document saved """Convert the list of text tokens into a HTML document saved
to _result. to _result.
""" """
@@ -299,7 +299,7 @@ class ToHtml(Tokenizer):
return return
def saveHtml5(self, path: str | Path): def saveHtml5(self, path: str | Path) -> None:
"""Save the data to an HTML file.""" """Save the data to an HTML file."""
with open(path, mode="w", encoding="utf-8") as fObj: with open(path, mode="w", encoding="utf-8") as fObj:
fObj.write(( fObj.write((
@@ -326,7 +326,7 @@ class ToHtml(Tokenizer):
logger.info("Wrote file: %s", path) logger.info("Wrote file: %s", path)
return return
def saveHtmlJson(self, path: str | Path): def saveHtmlJson(self, path: str | Path) -> None:
"""Save the data to a JSON file.""" """Save the data to a JSON file."""
timeStamp = time() timeStamp = time()
data = { data = {
@@ -347,7 +347,7 @@ class ToHtml(Tokenizer):
logger.info("Wrote file: %s", path) logger.info("Wrote file: %s", path)
return return
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;"): def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;") -> None:
"""Replace tabs with spaces in the html.""" """Replace tabs with spaces in the html."""
htmlText = [] htmlText = []
tabSpace = spaceChar*nSpaces tabSpace = spaceChar*nSpaces
@@ -357,7 +357,7 @@ class ToHtml(Tokenizer):
self._fullHTML = htmlText self._fullHTML = htmlText
return return
def getStyleSheet(self) -> list: def getStyleSheet(self) -> list[str]:
"""Generate a stylesheet for the current settings.""" """Generate a stylesheet for the current settings."""
styles = [] styles = []
if not self._cssStyles: if not self._cssStyles:
+34 -34
View File
@@ -44,7 +44,7 @@ from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def stripEscape(text): def stripEscape(text) -> str:
"""Helper function to strip escaped Markdown characters from """Helper function to strip escaped Markdown characters from
paragraph text. paragraph text.
""" """
@@ -100,7 +100,7 @@ class Tokenizer(ABC):
A_IND_L = 0x0100 # Left indentation A_IND_L = 0x0100 # Left indentation
A_IND_R = 0x0200 # Right indentation A_IND_R = 0x0200 # Right indentation
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
@@ -191,116 +191,116 @@ class Tokenizer(ABC):
# Setters # Setters
## ##
def setTitleFormat(self, hFormat: str): def setTitleFormat(self, hFormat: str) -> None:
"""Set the title format pattern.""" """Set the title format pattern."""
self._fmtTitle = hFormat.strip() self._fmtTitle = hFormat.strip()
return return
def setChapterFormat(self, hFormat: str): def setChapterFormat(self, hFormat: str) -> None:
"""Set the chapert format pattern.""" """Set the chapert format pattern."""
self._fmtChapter = hFormat.strip() self._fmtChapter = hFormat.strip()
return return
def setUnNumberedFormat(self, hFormat: str): def setUnNumberedFormat(self, hFormat: str) -> None:
"""Set the unnumbered format pattern.""" """Set the unnumbered format pattern."""
self._fmtUnNum = hFormat.strip() self._fmtUnNum = hFormat.strip()
return return
def setSceneFormat(self, hFormat: str, hide: bool): def setSceneFormat(self, hFormat: str, hide: bool) -> None:
"""Set the scene format pattern and hidden status.""" """Set the scene format pattern and hidden status."""
self._fmtScene = hFormat.strip() self._fmtScene = hFormat.strip()
self._hideScene = hide self._hideScene = hide
return return
def setSectionFormat(self, hFormat: str, hide: bool): def setSectionFormat(self, hFormat: str, hide: bool) -> None:
"""Set the section format pattern and hidden status.""" """Set the section format pattern and hidden status."""
self._fmtSection = hFormat.strip() self._fmtSection = hFormat.strip()
self._hideSection = hide self._hideSection = hide
return return
def setFont(self, family: str, size: int, isFixed: bool = False): def setFont(self, family: str, size: int, isFixed: bool = False) -> None:
"""Set the build font.""" """Set the build font."""
self._textFont = family self._textFont = family
self._textSize = round(int(size)) self._textSize = round(int(size))
self._textFixed = isFixed self._textFixed = isFixed
return return
def setLineHeight(self, height: float): def setLineHeight(self, height: float) -> None:
"""Set the line height between 0.5 and 5.0.""" """Set the line height between 0.5 and 5.0."""
self._lineHeight = min(max(float(height), 0.5), 5.0) self._lineHeight = min(max(float(height), 0.5), 5.0)
return return
def setBlockIndent(self, indent: float): def setBlockIndent(self, indent: float) -> None:
"""Set the block indent between 0.0 and 10.0.""" """Set the block indent between 0.0 and 10.0."""
self._blockIndent = min(max(float(indent), 0.0), 10.0) self._blockIndent = min(max(float(indent), 0.0), 10.0)
return return
def setJustify(self, state: bool): def setJustify(self, state: bool) -> None:
"""Enable or disable text justification.""" """Enable or disable text justification."""
self._doJustify = state self._doJustify = state
return return
def setTitleMargins(self, upper: float, lower: float): def setTitleMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower title margin.""" """Set the upper and lower title margin."""
self._marginTitle = (float(upper), float(lower)) self._marginTitle = (float(upper), float(lower))
return return
def setHead1Margins(self, upper: float, lower: float): def setHead1Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 1 margin.""" """Set the upper and lower header 1 margin."""
self._marginHead1 = (float(upper), float(lower)) self._marginHead1 = (float(upper), float(lower))
return return
def setHead2Margins(self, upper: float, lower: float): def setHead2Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 2 margin.""" """Set the upper and lower header 2 margin."""
self._marginHead2 = (float(upper), float(lower)) self._marginHead2 = (float(upper), float(lower))
return return
def setHead3Margins(self, upper: float, lower: float): def setHead3Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 3 margin.""" """Set the upper and lower header 3 margin."""
self._marginHead3 = (float(upper), float(lower)) self._marginHead3 = (float(upper), float(lower))
return return
def setHead4Margins(self, upper: float, lower: float): def setHead4Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 4 margin.""" """Set the upper and lower header 4 margin."""
self._marginHead4 = (float(upper), float(lower)) self._marginHead4 = (float(upper), float(lower))
return return
def setTextMargins(self, upper: float, lower: float): def setTextMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower text margin.""" """Set the upper and lower text margin."""
self._marginText = (float(upper), float(lower)) self._marginText = (float(upper), float(lower))
return return
def setMetaMargins(self, upper: float, lower: float): def setMetaMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower meta text margin.""" """Set the upper and lower meta text margin."""
self._marginMeta = (float(upper), float(lower)) self._marginMeta = (float(upper), float(lower))
return return
def setLinkHeaders(self, state: bool): def setLinkHeaders(self, state: bool) -> None:
"""Enable or disable adding an anchor before headers.""" """Enable or disable adding an anchor before headers."""
self._linkHeaders = state self._linkHeaders = state
return return
def setBodyText(self, state: bool): def setBodyText(self, state: bool) -> None:
"""Include body text in build.""" """Include body text in build."""
self._doBodyText = state self._doBodyText = state
return return
def setSynopsis(self, state: bool): def setSynopsis(self, state: bool) -> None:
"""Include synopsis comments in build.""" """Include synopsis comments in build."""
self._doSynopsis = state self._doSynopsis = state
return return
def setComments(self, state: bool): def setComments(self, state: bool) -> None:
"""Include comments in build.""" """Include comments in build."""
self._doComments = state self._doComments = state
return return
def setKeywords(self, state: bool): def setKeywords(self, state: bool) -> None:
"""Include keywords in build.""" """Include keywords in build."""
self._doKeywords = state self._doKeywords = state
return return
def setKeepMarkdown(self, state: bool): def setKeepMarkdown(self, state: bool) -> None:
"""Keep original markdown during build.""" """Keep original markdown during build."""
self._keepMarkdown = state self._keepMarkdown = state
return return
@@ -310,7 +310,7 @@ class Tokenizer(ABC):
## ##
@abstractmethod @abstractmethod
def doConvert(self): def doConvert(self) -> None:
raise NotImplementedError raise NotImplementedError
def addRootHeading(self, tHandle: str) -> bool: def addRootHeading(self, tHandle: str) -> bool:
@@ -365,7 +365,7 @@ class Tokenizer(ABC):
return True return True
def doPreProcessing(self): def doPreProcessing(self) -> None:
"""Run trough the various replace dictionaries.""" """Run trough the various replace dictionaries."""
# Process the user's auto-replace dictionary # Process the user's auto-replace dictionary
autoReplace = self._project.data.autoReplace autoReplace = self._project.data.autoReplace
@@ -382,7 +382,7 @@ class Tokenizer(ABC):
return return
def tokenizeText(self): def tokenizeText(self) -> None:
"""Scan the text for either lines starting with specific """Scan the text for either lines starting with specific
characters that indicate headers, comments, commands etc, or characters that indicate headers, comments, commands etc, or
just contain plain text. In the case of plain text, apply the just contain plain text. In the case of plain text, apply the
@@ -742,14 +742,14 @@ class Tokenizer(ABC):
return True return True
def saveRawMarkdown(self, path: str | Path): def saveRawMarkdown(self, path: str | Path) -> None:
"""Save the raw text to a plain text file.""" """Save the raw text to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile: with open(path, mode="w", encoding="utf-8") as outFile:
for nwdPage in self._allMarkdown: for nwdPage in self._allMarkdown:
outFile.write(nwdPage) outFile.write(nwdPage)
return return
def saveRawMarkdownJSON(self, path: str | Path): def saveRawMarkdownJSON(self, path: str | Path) -> None:
"""Save the raw text to a JSON file.""" """Save the raw text to a JSON file."""
timeStamp = time() timeStamp = time()
data = { data = {
@@ -773,30 +773,30 @@ class Tokenizer(ABC):
class HeadingFormatter: class HeadingFormatter:
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
self._chCount = 0 self._chCount = 0
self._scChCount = 0 self._scChCount = 0
self._scAbsCount = 0 self._scAbsCount = 0
return return
def incChapter(self): def incChapter(self) -> None:
"""Increment the chapter counter.""" """Increment the chapter counter."""
self._chCount += 1 self._chCount += 1
return return
def incScene(self): def incScene(self) -> None:
"""Increment the scene counters.""" """Increment the scene counters."""
self._scChCount += 1 self._scChCount += 1
self._scAbsCount += 1 self._scAbsCount += 1
return return
def resetScene(self): def resetScene(self) -> None:
"""Reset the chapter scene counter.""" """Reset the chapter scene counter."""
self._scChCount = 0 self._scChCount = 0
return return
def apply(self, hFormat: str, text: str): def apply(self, hFormat: str, text: str) -> str:
"""Apply formatting to a specific heading.""" """Apply formatting to a specific heading."""
hFormat = hFormat.replace(nwHeadFmt.TITLE, text) hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount)) hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
+8 -10
View File
@@ -45,12 +45,10 @@ class ToMarkdown(Tokenizer):
M_STD = 0 # Standard Markdown M_STD = 0 # Standard Markdown
M_GH = 1 # GitHub Markdown M_GH = 1 # GitHub Markdown
def __init__(self, project: NWProject): def __init__(self, project: NWProject) -> None:
super().__init__(project) super().__init__(project)
self._genMode = self.M_STD self._genMode = self.M_STD
self._fullMD = [] self._fullMD: list[str] = []
return return
## ##
@@ -58,7 +56,7 @@ class ToMarkdown(Tokenizer):
## ##
@property @property
def fullMD(self) -> list: def fullMD(self) -> list[str]:
"""Return the markdown as a list.""" """Return the markdown as a list."""
return self._fullMD return self._fullMD
@@ -66,11 +64,11 @@ class ToMarkdown(Tokenizer):
# Setters # Setters
## ##
def setStandardMarkdown(self): def setStandardMarkdown(self) -> None:
self._genMode = self.M_STD self._genMode = self.M_STD
return return
def setGitHubMarkdown(self): def setGitHubMarkdown(self) -> None:
self._genMode = self.M_GH self._genMode = self.M_GH
return return
@@ -82,7 +80,7 @@ class ToMarkdown(Tokenizer):
"""Return the size of the full Markdown result.""" """Return the size of the full Markdown result."""
return sum([len(x) for x in self._fullMD]) return sum([len(x) for x in self._fullMD])
def doConvert(self): def doConvert(self) -> None:
"""Convert the list of text tokens into a HTML document saved """Convert the list of text tokens into a HTML document saved
to theResult. to theResult.
""" """
@@ -175,14 +173,14 @@ class ToMarkdown(Tokenizer):
return return
def saveMarkdown(self, path: str | Path): def saveMarkdown(self, path: str | Path) -> None:
"""Save the data to a plain text file.""" """Save the data to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile: with open(path, mode="w", encoding="utf-8") as outFile:
outFile.write("".join(self._fullMD)) outFile.write("".join(self._fullMD))
logger.info("Wrote file: %s", path) logger.info("Wrote file: %s", path)
return return
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "): def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
"""Replace tabs with spaces.""" """Replace tabs with spaces."""
spaces = spaceChar*nSpaces spaces = spaceChar*nSpaces
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD] self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
+44 -46
View File
@@ -98,7 +98,7 @@ class ToOdt(Tokenizer):
Test with: https://odfvalidator.org/ Test with: https://odfvalidator.org/
""" """
def __init__(self, project: NWProject, isFlat: bool): def __init__(self, project: NWProject, isFlat: bool) -> None:
super().__init__(project) super().__init__(project)
self._isFlat = isFlat # Flat: .fodt, otherwise .odt self._isFlat = isFlat # Flat: .fodt, otherwise .odt
@@ -188,7 +188,7 @@ class ToOdt(Tokenizer):
# Setters # Setters
## ##
def setLanguage(self, language: str): def setLanguage(self, language: str) -> None:
"""Set language for the document.""" """Set language for the document."""
if language: if language:
langBits = language.split("_") langBits = language.split("_")
@@ -197,7 +197,7 @@ class ToOdt(Tokenizer):
self._dCountry = langBits[1] self._dCountry = langBits[1]
return return
def setColourHeaders(self, state: bool): def setColourHeaders(self, state: bool) -> None:
"""Enable/disable coloured headings and comments.""" """Enable/disable coloured headings and comments."""
self._colourHead = state self._colourHead = state
return return
@@ -205,7 +205,7 @@ class ToOdt(Tokenizer):
def setPageLayout( def setPageLayout(
self, width: int | float, height: int | float, self, width: int | float, height: int | float,
top: int | float, bottom: int | float, left: int | float, right: int | float top: int | float, bottom: int | float, left: int | float, right: int | float
): ) -> None:
"""Set the document page size and margins in millimetres.""" """Set the document page size and margins in millimetres."""
self._mDocWidth = f"{width/10.0:.3f}cm" self._mDocWidth = f"{width/10.0:.3f}cm"
self._mDocHeight = f"{height/10.0:.3f}cm" self._mDocHeight = f"{height/10.0:.3f}cm"
@@ -219,7 +219,7 @@ class ToOdt(Tokenizer):
# Class Methods # Class Methods
## ##
def initDocument(self): def initDocument(self) -> None:
"""Initialises a new open document XML tree.""" """Initialises a new open document XML tree."""
# Initialise Variables # Initialise Variables
# ==================== # ====================
@@ -381,7 +381,7 @@ class ToOdt(Tokenizer):
return return
def doConvert(self): def doConvert(self) -> None:
"""Convert the list of text tokens into XML elements.""" """Convert the list of text tokens into XML elements."""
self._result = "" # Not used, but cleared just in case self._result = "" # Not used, but cleared just in case
@@ -599,7 +599,7 @@ class ToOdt(Tokenizer):
def _addTextPar( def _addTextPar(
self, styleName: str, oStyle: ODTParagraphStyle, tText: str, tFmt: str = "", self, styleName: str, oStyle: ODTParagraphStyle, tText: str, tFmt: str = "",
isHead: bool = False, oLevel: str | None = None isHead: bool = False, oLevel: str | None = None
): ) -> None:
"""Add a text paragraph to the text XML element.""" """Add a text paragraph to the text XML element."""
tAttr = {} tAttr = {}
tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle) tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
@@ -726,7 +726,7 @@ class ToOdt(Tokenizer):
# Style Elements # Style Elements
## ##
def _pageStyles(self): def _pageStyles(self) -> None:
"""Set the default page style.""" """Set the default page style."""
tAttr = {} tAttr = {}
tAttr[_mkTag("style", "name")] = "PM1" tAttr[_mkTag("style", "name")] = "PM1"
@@ -756,7 +756,7 @@ class ToOdt(Tokenizer):
return return
def _defaultStyles(self): def _defaultStyles(self) -> None:
"""Set the default styles.""" """Set the default styles."""
# Add Paragraph Family Style # Add Paragraph Family Style
# ========================== # ==========================
@@ -829,7 +829,7 @@ class ToOdt(Tokenizer):
return return
def _useableStyles(self): def _useableStyles(self) -> None:
"""Set the usable styles.""" """Set the usable styles."""
# Add Text Body Style # Add Text Body Style
# =================== # ===================
@@ -1002,7 +1002,7 @@ class ToOdt(Tokenizer):
return return
def _writeHeader(self): def _writeHeader(self) -> None:
"""Write the header elements.""" """Write the header elements."""
tAttr = {} tAttr = {}
tAttr[_mkTag("style", "name")] = "Standard" tAttr[_mkTag("style", "name")] = "Standard"
@@ -1048,7 +1048,7 @@ class ODTParagraphStyle:
VALID_CLASS = ["text", "chapter"] VALID_CLASS = ["text", "chapter"]
VALID_WEIGHT = ["normal", "inherit", "bold"] VALID_WEIGHT = ["normal", "inherit", "bold"]
def __init__(self): def __init__(self) -> None:
# Attributes # Attributes
self._mAttr = { self._mAttr = {
@@ -1087,26 +1087,26 @@ class ODTParagraphStyle:
# Attribute Setters # Attribute Setters
## ##
def setDisplayName(self, value: str | None): def setDisplayName(self, value: str | None) -> None:
self._mAttr["display-name"][1] = value self._mAttr["display-name"][1] = value
return return
def setParentStyleName(self, value: str | None): def setParentStyleName(self, value: str | None) -> None:
self._mAttr["parent-style-name"][1] = value self._mAttr["parent-style-name"][1] = value
return return
def setNextStyleName(self, value: str | None): def setNextStyleName(self, value: str | None) -> None:
self._mAttr["next-style-name"][1] = value self._mAttr["next-style-name"][1] = value
return return
def setOutlineLevel(self, value: str | None): def setOutlineLevel(self, value: str | None) -> None:
if value in self.VALID_LEVEL: if value in self.VALID_LEVEL:
self._mAttr["default-outline-level"][1] = value self._mAttr["default-outline-level"][1] = value
else: else:
self._mAttr["default-outline-level"][1] = None self._mAttr["default-outline-level"][1] = None
return return
def setClass(self, value: str | None): def setClass(self, value: str | None) -> None:
if value in self.VALID_CLASS: if value in self.VALID_CLASS:
self._mAttr["class"][1] = value self._mAttr["class"][1] = value
else: else:
@@ -1117,41 +1117,41 @@ class ODTParagraphStyle:
# Paragraph Setters # Paragraph Setters
## ##
def setMarginTop(self, value: str | None): def setMarginTop(self, value: str | None) -> None:
self._pAttr["margin-top"][1] = value self._pAttr["margin-top"][1] = value
return return
def setMarginBottom(self, value: str | None): def setMarginBottom(self, value: str | None) -> None:
self._pAttr["margin-bottom"][1] = value self._pAttr["margin-bottom"][1] = value
return return
def setMarginLeft(self, value: str | None): def setMarginLeft(self, value: str | None) -> None:
self._pAttr["margin-left"][1] = value self._pAttr["margin-left"][1] = value
return return
def setMarginRight(self, value: str | None): def setMarginRight(self, value: str | None) -> None:
self._pAttr["margin-right"][1] = value self._pAttr["margin-right"][1] = value
return return
def setLineHeight(self, value: str | None): def setLineHeight(self, value: str | None) -> None:
self._pAttr["line-height"][1] = value self._pAttr["line-height"][1] = value
return return
def setTextAlign(self, value: str | None): def setTextAlign(self, value: str | None) -> None:
if value in self.VALID_ALIGN: if value in self.VALID_ALIGN:
self._pAttr["text-align"][1] = value self._pAttr["text-align"][1] = value
else: else:
self._pAttr["text-align"][1] = None self._pAttr["text-align"][1] = None
return return
def setBreakBefore(self, value: str | None): def setBreakBefore(self, value: str | None) -> None:
if value in self.VALID_BREAK: if value in self.VALID_BREAK:
self._pAttr["break-before"][1] = value self._pAttr["break-before"][1] = value
else: else:
self._pAttr["break-before"][1] = None self._pAttr["break-before"][1] = None
return return
def setBreakAfter(self, value: str | None): def setBreakAfter(self, value: str | None) -> None:
if value in self.VALID_BREAK: if value in self.VALID_BREAK:
self._pAttr["break-after"][1] = value self._pAttr["break-after"][1] = value
else: else:
@@ -1162,30 +1162,30 @@ class ODTParagraphStyle:
# Text Setters # Text Setters
## ##
def setFontName(self, value: str | None): def setFontName(self, value: str | None) -> None:
self._tAttr["font-name"][1] = value self._tAttr["font-name"][1] = value
return return
def setFontFamily(self, value: str | None): def setFontFamily(self, value: str | None) -> None:
self._tAttr["font-family"][1] = value self._tAttr["font-family"][1] = value
return return
def setFontSize(self, value: str | None): def setFontSize(self, value: str | None) -> None:
self._tAttr["font-size"][1] = value self._tAttr["font-size"][1] = value
return return
def setFontWeight(self, value: str | None): def setFontWeight(self, value: str | None) -> None:
if value in self.VALID_WEIGHT: if value in self.VALID_WEIGHT:
self._tAttr["font-weight"][1] = value self._tAttr["font-weight"][1] = value
else: else:
self._tAttr["font-weight"][1] = None self._tAttr["font-weight"][1] = None
return return
def setColor(self, value: str | None): def setColor(self, value: str | None) -> None:
self._tAttr["color"][1] = value self._tAttr["color"][1] = value
return return
def setOpacity(self, value: str | None): def setOpacity(self, value: str | None) -> None:
self._tAttr["opacity"][1] = value self._tAttr["opacity"][1] = value
return return
@@ -1193,7 +1193,7 @@ class ODTParagraphStyle:
# Methods # Methods
## ##
def checkNew(self, refStyle: ODTParagraphStyle): def checkNew(self, refStyle: ODTParagraphStyle) -> bool:
"""Check if there are new settings in refStyle that differ from """Check if there are new settings in refStyle that differ from
those in the current object. those in the current object.
""" """
@@ -1217,7 +1217,7 @@ class ODTParagraphStyle:
) )
return sha256(theString.encode()).hexdigest() return sha256(theString.encode()).hexdigest()
def packXML(self, xParent: ET.Element, name: str): def packXML(self, xParent: ET.Element, name: str) -> None:
"""Pack the content into an xml element.""" """Pack the content into an xml element."""
theAttr = {} theAttr = {}
theAttr[_mkTag("style", "name")] = name theAttr[_mkTag("style", "name")] = name
@@ -1259,8 +1259,7 @@ class ODTTextStyle:
VALID_LSTYLE = ["none", "solid"] VALID_LSTYLE = ["none", "solid"]
VALID_LTYPE = ["none", "single", "double"] VALID_LTYPE = ["none", "single", "double"]
def __init__(self): def __init__(self) -> None:
# Text Attributes # Text Attributes
self._tAttr = { self._tAttr = {
"font-weight": ["fo", None], "font-weight": ["fo", None],
@@ -1268,35 +1267,34 @@ class ODTTextStyle:
"text-line-through-style": ["style", None], "text-line-through-style": ["style", None],
"text-line-through-type": ["style", None], "text-line-through-type": ["style", None],
} }
return return
## ##
# Setters # Setters
## ##
def setFontWeight(self, value: str | None): def setFontWeight(self, value: str | None) -> None:
if value in self.VALID_WEIGHT: if value in self.VALID_WEIGHT:
self._tAttr["font-weight"][1] = value self._tAttr["font-weight"][1] = value
else: else:
self._tAttr["font-weight"][1] = None self._tAttr["font-weight"][1] = None
return return
def setFontStyle(self, value: str | None): def setFontStyle(self, value: str | None) -> None:
if value in self.VALID_STYLE: if value in self.VALID_STYLE:
self._tAttr["font-style"][1] = value self._tAttr["font-style"][1] = value
else: else:
self._tAttr["font-style"][1] = None self._tAttr["font-style"][1] = None
return return
def setStrikeStyle(self, value: str | None): def setStrikeStyle(self, value: str | None) -> None:
if value in self.VALID_LSTYLE: if value in self.VALID_LSTYLE:
self._tAttr["text-line-through-style"][1] = value self._tAttr["text-line-through-style"][1] = value
else: else:
self._tAttr["text-line-through-style"][1] = None self._tAttr["text-line-through-style"][1] = None
return return
def setStrikeType(self, value: str | None): def setStrikeType(self, value: str | None) -> None:
if value in self.VALID_LTYPE: if value in self.VALID_LTYPE:
self._tAttr["text-line-through-type"][1] = value self._tAttr["text-line-through-type"][1] = value
else: else:
@@ -1307,7 +1305,7 @@ class ODTTextStyle:
# Methods # Methods
## ##
def packXML(self, xParent: ET.Element, name: str): def packXML(self, xParent: ET.Element, name: str) -> None:
"""Pack the content into an xml element.""" """Pack the content into an xml element."""
theAttr = {} theAttr = {}
theAttr[_mkTag("style", "name")] = name theAttr[_mkTag("style", "name")] = name
@@ -1357,7 +1355,7 @@ class XMLParagraph:
object and attribute is written to, object and attribute is written to,
""" """
def __init__(self, xRoot: ET.Element): def __init__(self, xRoot: ET.Element) -> None:
self._xRoot = xRoot self._xRoot = xRoot
self._xTail = ET.Element("") self._xTail = ET.Element("")
@@ -1370,7 +1368,7 @@ class XMLParagraph:
return return
def appendText(self, tText: str): def appendText(self, tText: str) -> None:
"""Append text to the XML element. We do this one character at """Append text to the XML element. We do this one character at
the time in order to be able to process line breaks, tabs and the time in order to be able to process line breaks, tabs and
spaces separately. Multiple spaces are concatenated into a spaces separately. Multiple spaces are concatenated into a
@@ -1435,7 +1433,7 @@ class XMLParagraph:
return return
def appendSpan(self, tText: str, tFmt: str): def appendSpan(self, tText: str, tFmt: str) -> None:
"""Append a text span to the XML element. The span is always """Append a text span to the XML element. The span is always
closed since we do not allow nested spans (like Libre Office). closed since we do not allow nested spans (like Libre Office).
Therefore we return to the root element level when we're done Therefore we return to the root element level when we're done
@@ -1449,7 +1447,7 @@ class XMLParagraph:
self._nState = X_ROOT_TAIL self._nState = X_ROOT_TAIL
return return
def checkError(self): def checkError(self) -> tuple[int, str]:
"""Check that the number of characters written matches the """Check that the number of characters written matches the
number of characters received. number of characters received.
""" """
@@ -1463,7 +1461,7 @@ class XMLParagraph:
# Internal Functions # Internal Functions
## ##
def _processSpaces(self, nSpaces: int): def _processSpaces(self, nSpaces: int) -> None:
"""Add spaces to paragraph. The first space is always written """Add spaces to paragraph. The first space is always written
as-is (unless it's the first character of the paragraph). The as-is (unless it's the first character of the paragraph). The
second space uses the dedicated tag for spaces, and from the second space uses the dedicated tag for spaces, and from the
+12 -15
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI About Box novelWriter GUI About Box
=========================== ===========================
The about novelWriter dialog box
File History: File History:
Created: 2020-05-21 [0.5.2] Created: 2020-05-21 [0.5.2]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
import novelwriter import novelwriter
@@ -31,8 +31,8 @@ from datetime import datetime
from PyQt5.QtGui import QCursor from PyQt5.QtGui import QCursor
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget, qApp, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QTabWidget,
QTextBrowser, QLabel QTextBrowser, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG from novelwriter import CONFIG
@@ -44,15 +44,12 @@ logger = logging.getLogger(__name__)
class GuiAbout(QDialog): class GuiAbout(QDialog):
def __init__(self, mainGui): def __init__(self, parent: QWidget):
super().__init__(parent=mainGui) super().__init__(parent=parent)
logger.debug("Create: GuiAbout") logger.debug("Create: GuiAbout")
self.setObjectName("GuiAbout") self.setObjectName("GuiAbout")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout() self.innerBox = QHBoxLayout()
self.innerBox.setSpacing(CONFIG.pxInt(16)) self.innerBox.setSpacing(CONFIG.pxInt(16))
@@ -63,7 +60,7 @@ class GuiAbout(QDialog):
nPx = CONFIG.pxInt(96) nPx = CONFIG.pxInt(96)
self.nwIcon = QLabel() self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx))) self.nwIcon.setPixmap(CONFIG.theme.getPixmap("novelwriter", (nPx, nPx)))
self.lblName = QLabel("<b>novelWriter</b>") self.lblName = QLabel("<b>novelWriter</b>")
self.lblVers = QLabel(f"v{novelwriter.__version__}") self.lblVers = QLabel(f"v{novelwriter.__version__}")
self.lblDate = QLabel(datetime.strptime(novelwriter.__date__, "%Y-%m-%d").strftime("%x")) self.lblDate = QLabel(datetime.strptime(novelwriter.__date__, "%Y-%m-%d").strftime("%x"))
@@ -231,12 +228,12 @@ class GuiAbout(QDialog):
" color: rgb({kColR},{kColG},{kColB});" " color: rgb({kColR},{kColG},{kColB});"
"}}\n" "}}\n"
).format( ).format(
hColR=self.mainGui.mainTheme.colHead[0], hColR=CONFIG.theme.colHead[0],
hColG=self.mainGui.mainTheme.colHead[1], hColG=CONFIG.theme.colHead[1],
hColB=self.mainGui.mainTheme.colHead[2], hColB=CONFIG.theme.colHead[2],
kColR=self.mainTheme.colKey[0], kColR=CONFIG.theme.colKey[0],
kColG=self.mainTheme.colKey[1], kColG=CONFIG.theme.colKey[1],
kColB=self.mainTheme.colKey[2], kColB=CONFIG.theme.colKey[2],
) )
self.pageAbout.document().setDefaultStyleSheet(styleSheet) self.pageAbout.document().setDefaultStyleSheet(styleSheet)
self.pageNotes.document().setDefaultStyleSheet(styleSheet) self.pageNotes.document().setDefaultStyleSheet(styleSheet)
+6 -8
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Doc Merge Dialog novelWriter GUI Doc Merge Dialog
================================== ==================================
Custom dialog class for merging documents.
File History: File History:
Created: 2020-01-23 [0.4.3] Created: 2020-01-23 [0.4.3]
@@ -23,6 +22,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -49,9 +49,7 @@ class GuiDocMerge(QDialog):
logger.debug("Create: GuiDocMerge") logger.debug("Create: GuiDocMerge")
self.setObjectName("GuiDocMerge") self.setObjectName("GuiDocMerge")
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._data = {} self._data = {}
@@ -60,9 +58,9 @@ class GuiDocMerge(QDialog):
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge"))) self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
self.helpLabel = NHelpLabel(self.tr( self.helpLabel = NHelpLabel(self.tr(
"Drag and drop items to change the order, or uncheck to exclude." "Drag and drop items to change the order, or uncheck to exclude."
), self.mainTheme.helpText) ), CONFIG.theme.helpText)
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
hSp = CONFIG.pxInt(12) hSp = CONFIG.pxInt(12)
vSp = CONFIG.pxInt(8) vSp = CONFIG.pxInt(8)
bSp = CONFIG.pxInt(12) bSp = CONFIG.pxInt(12)
@@ -157,11 +155,11 @@ class GuiDocMerge(QDialog):
self.listBox.clear() self.listBox.clear()
for tHandle in itemList: for tHandle in itemList:
nwItem = self.theProject.tree[tHandle] nwItem = self.mainGui.project.tree[tHandle]
if nwItem is None or not nwItem.isFileType(): if nwItem is None or not nwItem.isFileType():
continue continue
itemIcon = self.mainTheme.getItemIcon( itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
) )
+8 -10
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Doc Split Dialog novelWriter GUI Doc Split Dialog
================================== ==================================
Custom dialog class for splitting documents.
File History: File History:
Created: 2020-02-01 [0.4.3] Created: 2020-02-01 [0.4.3]
@@ -23,6 +22,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -51,9 +51,7 @@ class GuiDocSplit(QDialog):
logger.debug("Create: GuiDocSplit") logger.debug("Create: GuiDocSplit")
self.setObjectName("GuiDocSplit") self.setObjectName("GuiDocSplit")
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._data = {} self._data = {}
self._text = [] self._text = []
@@ -63,16 +61,16 @@ class GuiDocSplit(QDialog):
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers"))) self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
self.helpLabel = NHelpLabel( self.helpLabel = NHelpLabel(
self.tr("Select the maximum level to split into files."), self.tr("Select the maximum level to split into files."),
self.mainGui.mainTheme.helpText CONFIG.theme.helpText
) )
# Values # Values
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
hSp = CONFIG.pxInt(12) hSp = CONFIG.pxInt(12)
vSp = CONFIG.pxInt(8) vSp = CONFIG.pxInt(8)
bSp = CONFIG.pxInt(12) bSp = CONFIG.pxInt(12)
pOptions = self.theProject.options pOptions = self.mainGui.project.options
spLevel = pOptions.getInt("GuiDocSplit", "spLevel", 3) spLevel = pOptions.getInt("GuiDocSplit", "spLevel", 3)
intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True) intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True)
docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True) docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True)
@@ -171,7 +169,7 @@ class GuiDocSplit(QDialog):
self._data["docHierarchy"] = docHierarchy self._data["docHierarchy"] = docHierarchy
self._data["moveToTrash"] = moveToTrash self._data["moveToTrash"] = moveToTrash
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiDocSplit", "spLevel", spLevel) pOptions.setValue("GuiDocSplit", "spLevel", spLevel)
pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder) pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder)
pOptions.setValue("GuiDocSplit", "docHierarchy", docHierarchy) pOptions.setValue("GuiDocSplit", "docHierarchy", docHierarchy)
@@ -201,13 +199,13 @@ class GuiDocSplit(QDialog):
self.listBox.clear() self.listBox.clear()
nwItem = self.theProject.tree[sHandle] nwItem = self.mainGui.project.tree[sHandle]
if nwItem is None or not nwItem.isFileType(): if nwItem is None or not nwItem.isFileType():
return return
spLevel = self.splitLevel.currentData() spLevel = self.splitLevel.currentData()
if not self._text: if not self._text:
inDoc = self.theProject.storage.getDocument(sHandle) inDoc = self.mainGui.project.storage.getDocument(sHandle)
self._text = (inDoc.readDocument() or "").splitlines() self._text = (inDoc.readDocument() or "").splitlines()
for lineNo, aLine in enumerate(self._text): for lineNo, aLine in enumerate(self._text):
+1 -1
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Edit Label Dialog novelWriter Edit Label Dialog
=============================== ===============================
A simple dialog for editing a label
File History: File History:
Created: 2022-06-11 [2.0rc1] Created: 2022-06-11 [2.0rc1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
+17 -35
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Preferences novelWriter GUI Preferences
============================= =============================
GUI classes for the user preferences dialog
File History: File History:
Created: 2019-06-10 [0.1.5] Created: 2019-06-10 [0.1.5]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -49,8 +49,7 @@ class GuiPreferences(NPagedDialog):
logger.debug("Create: GuiPreferences") logger.debug("Create: GuiPreferences")
self.setObjectName("GuiPreferences") self.setObjectName("GuiPreferences")
self.mainGui = mainGui self.mainGui = mainGui
self.theProject = mainGui.theProject
self.setWindowTitle(self.tr("Preferences")) self.setWindowTitle(self.tr("Preferences"))
@@ -160,13 +159,11 @@ class GuiPreferencesGeneral(QWidget):
def __init__(self, prefsGui): def __init__(self, prefsGui):
super().__init__(parent=prefsGui) super().__init__(parent=prefsGui)
self.prefsGui = prefsGui self.prefsGui = prefsGui
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form # The Form
self.mainForm = NConfigLayout() self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText) self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Look and Feel # Look and Feel
@@ -193,7 +190,7 @@ class GuiPreferencesGeneral(QWidget):
# Select Theme # Select Theme
self.guiTheme = QComboBox() self.guiTheme = QComboBox()
self.guiTheme.setMinimumWidth(minWidth) self.guiTheme.setMinimumWidth(minWidth)
self.theThemes = self.mainTheme.listThemes() self.theThemes = CONFIG.theme.listThemes()
for themeDir, themeName in self.theThemes: for themeDir, themeName in self.theThemes:
self.guiTheme.addItem(themeName, themeDir) self.guiTheme.addItem(themeName, themeDir)
themeIdx = self.guiTheme.findData(CONFIG.guiTheme) themeIdx = self.guiTheme.findData(CONFIG.guiTheme)
@@ -209,7 +206,7 @@ class GuiPreferencesGeneral(QWidget):
# Editor Theme # Editor Theme
self.guiSyntax = QComboBox() self.guiSyntax = QComboBox()
self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200)) self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200))
self.theSyntaxes = self.mainTheme.listSyntax() self.theSyntaxes = CONFIG.theme.listSyntax()
for syntaxFile, syntaxName in self.theSyntaxes: for syntaxFile, syntaxName in self.theSyntaxes:
self.guiSyntax.addItem(syntaxName, syntaxFile) self.guiSyntax.addItem(syntaxName, syntaxFile)
syntaxIdx = self.guiSyntax.findData(CONFIG.guiSyntax) syntaxIdx = self.guiSyntax.findData(CONFIG.guiSyntax)
@@ -228,7 +225,7 @@ class GuiPreferencesGeneral(QWidget):
self.guiFont.setFixedWidth(CONFIG.pxInt(162)) self.guiFont.setFixedWidth(CONFIG.pxInt(162))
self.guiFont.setText(CONFIG.guiFont) self.guiFont.setText(CONFIG.guiFont)
self.fontButton = QPushButton("...") self.fontButton = QPushButton("...")
self.fontButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) self.fontButton.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont) self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Font family"), self.tr("Font family"),
@@ -342,12 +339,9 @@ class GuiPreferencesProjects(QWidget):
def __init__(self, prefsGui): def __init__(self, prefsGui):
super().__init__(parent=prefsGui) super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form # The Form
self.mainForm = NConfigLayout() self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText) self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Automatic Save # Automatic Save
@@ -497,12 +491,9 @@ class GuiPreferencesDocuments(QWidget):
def __init__(self, prefsGui): def __init__(self, prefsGui):
super().__init__(parent=prefsGui) super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form # The Form
self.mainForm = NConfigLayout() self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText) self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Text Style # Text Style
@@ -515,7 +506,7 @@ class GuiPreferencesDocuments(QWidget):
self.textFont.setFixedWidth(CONFIG.pxInt(162)) self.textFont.setFixedWidth(CONFIG.pxInt(162))
self.textFont.setText(CONFIG.textFont) self.textFont.setText(CONFIG.textFont)
self.fontButton = QPushButton("...") self.fontButton = QPushButton("...")
self.fontButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) self.fontButton.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont) self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Font family"), self.tr("Font family"),
@@ -654,12 +645,11 @@ class GuiPreferencesEditor(QWidget):
def __init__(self, prefsGui): def __init__(self, prefsGui):
super().__init__(parent=prefsGui) super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form # The Form
self.mainForm = NConfigLayout() self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText) self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
mW = CONFIG.pxInt(250) mW = CONFIG.pxInt(250)
@@ -825,13 +815,11 @@ class GuiPreferencesSyntax(QWidget):
def __init__(self, prefsGui): def __init__(self, prefsGui):
super().__init__(parent=prefsGui) super().__init__(parent=prefsGui)
self.prefsGui = prefsGui self.prefsGui = prefsGui
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form # The Form
self.mainForm = NConfigLayout() self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText) self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Quotes & Dialogue # Quotes & Dialogue
@@ -931,12 +919,9 @@ class GuiPreferencesAutomation(QWidget):
def __init__(self, prefsGui): def __init__(self, prefsGui):
super().__init__(parent=prefsGui) super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form # The Form
self.mainForm = NConfigLayout() self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText) self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Automatic Features # Automatic Features
@@ -1085,12 +1070,9 @@ class GuiPreferencesQuotes(QWidget):
def __init__(self, prefsGui): def __init__(self, prefsGui):
super().__init__(parent=prefsGui) super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form # The Form
self.mainForm = NConfigLayout() self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText) self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Quotation Style # Quotation Style
@@ -1098,7 +1080,7 @@ class GuiPreferencesQuotes(QWidget):
self.mainForm.addGroupLabel(self.tr("Quotation Style")) self.mainForm.addGroupLabel(self.tr("Quotation Style"))
qWidth = CONFIG.pxInt(40) qWidth = CONFIG.pxInt(40)
bWidth = int(2.5*self.mainTheme.getTextWidth("...")) bWidth = int(2.5*CONFIG.theme.getTextWidth("..."))
self.quoteSym = {} self.quoteSym = {}
# Single Quote Style # Single Quote Style
+26 -30
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Project Details novelWriter GUI Project Details
================================= =================================
Class holding the project details dialog
File History: File History:
Created: 2021-01-03 [1.1rc1] Created: 2021-01-03 [1.1rc1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import math import math
import logging import logging
@@ -36,9 +36,9 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.common import formatTime, numberToRoman from novelwriter.common import formatTime, numberToRoman
from novelwriter.constants import nwUnicode from novelwriter.constants import nwUnicode
from novelwriter.gui.components import NovelSelector
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
from novelwriter.extensions.pageddialog import NPagedDialog from novelwriter.extensions.pageddialog import NPagedDialog
from novelwriter.extensions.novelselector import NovelSelector
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -51,14 +51,13 @@ class GuiProjectDetails(NPagedDialog):
logger.debug("Create: GuiProjectDetails") logger.debug("Create: GuiProjectDetails")
self.setObjectName("GuiProjectDetails") self.setObjectName("GuiProjectDetails")
self.mainGui = mainGui self.mainGui = mainGui
self.theProject = mainGui.theProject
self.setWindowTitle(self.tr("Project Details")) self.setWindowTitle(self.tr("Project Details"))
wW = CONFIG.pxInt(600) wW = CONFIG.pxInt(600)
wH = CONFIG.pxInt(400) wH = CONFIG.pxInt(400)
pOptions = self.theProject.options pOptions = self.mainGui.project.options
self.setMinimumWidth(wW) self.setMinimumWidth(wW)
self.setMinimumHeight(wH) self.setMinimumHeight(wH)
@@ -67,8 +66,8 @@ class GuiProjectDetails(NPagedDialog):
CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "winHeight", wH)) CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "winHeight", wH))
) )
self.tabMain = GuiProjectDetailsMain(self.mainGui, self.theProject) self.tabMain = GuiProjectDetailsMain(self.mainGui)
self.tabContents = GuiProjectDetailsContents(self.mainGui, self.theProject) self.tabContents = GuiProjectDetailsContents(self.mainGui)
self.addTab(self.tabMain, self.tr("Overview")) self.addTab(self.tabMain, self.tr("Overview"))
self.addTab(self.tabContents, self.tr("Contents")) self.addTab(self.tabContents, self.tr("Contents"))
@@ -125,7 +124,7 @@ class GuiProjectDetails(NPagedDialog):
countFrom = self.tabContents.poValue.value() countFrom = self.tabContents.poValue.value()
clearDouble = self.tabContents.dblValue.isChecked() clearDouble = self.tabContents.dblValue.isChecked()
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiProjectDetails", "winWidth", winWidth) pOptions.setValue("GuiProjectDetails", "winWidth", winWidth)
pOptions.setValue("GuiProjectDetails", "winHeight", winHeight) pOptions.setValue("GuiProjectDetails", "winHeight", winHeight)
pOptions.setValue("GuiProjectDetails", "widthCol0", widthCol0) pOptions.setValue("GuiProjectDetails", "widthCol0", widthCol0)
@@ -144,15 +143,13 @@ class GuiProjectDetails(NPagedDialog):
class GuiProjectDetailsMain(QWidget): class GuiProjectDetailsMain(QWidget):
def __init__(self, mainGui, theProject): def __init__(self, mainGui):
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
self.theProject = theProject self.mainGui = mainGui
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
fPx = self.mainTheme.fontPixelSize fPx = CONFIG.theme.fontPixelSize
fPt = self.mainTheme.fontPointSize fPt = CONFIG.theme.fontPointSize
vPx = CONFIG.pxInt(4) vPx = CONFIG.pxInt(4)
hPx = CONFIG.pxInt(12) hPx = CONFIG.pxInt(12)
@@ -247,22 +244,23 @@ class GuiProjectDetailsMain(QWidget):
def updateValues(self): def updateValues(self):
"""Set all the values. """Set all the values.
""" """
pIndex = self.theProject.index project = self.mainGui.project
pIndex = project.index
hCounts = pIndex.getNovelTitleCounts() hCounts = pIndex.getNovelTitleCounts()
nwCount = pIndex.getNovelWordCount() nwCount = pIndex.getNovelWordCount()
edTime = self.theProject.getCurrentEditTime() edTime = project.getCurrentEditTime()
self.bookTitle.setText(self.theProject.data.title or self.theProject.data.name) self.bookTitle.setText(project.data.title or project.data.name)
self.projName.setText(self.tr("Project: {0}").format(self.theProject.data.name)) self.projName.setText(self.tr("Project: {0}").format(project.data.name))
self.bookAuthors.setText(self.tr("By {0}").format(self.theProject.data.author)) self.bookAuthors.setText(self.tr("By {0}").format(project.data.author))
self.wordCountVal.setText(f"{nwCount:n}") self.wordCountVal.setText(f"{nwCount:n}")
self.chapCountVal.setText(f"{hCounts[2]:n}") self.chapCountVal.setText(f"{hCounts[2]:n}")
self.sceneCountVal.setText(f"{hCounts[3]:n}") self.sceneCountVal.setText(f"{hCounts[3]:n}")
self.revCountVal.setText(f"{self.theProject.data.saveCount:n}") self.revCountVal.setText(f"{project.data.saveCount:n}")
self.editTimeVal.setText(formatTime(edTime)) self.editTimeVal.setText(formatTime(edTime))
self.projPathVal.setText(str(self.theProject.storage.storagePath)) self.projPathVal.setText(str(project.storage.storagePath))
return return
@@ -277,28 +275,26 @@ class GuiProjectDetailsContents(QWidget):
C_PAGE = 3 C_PAGE = 3
C_PROG = 4 C_PROG = 4
def __init__(self, mainGui, theProject): def __init__(self, mainGui):
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
self.theProject = theProject self.mainGui = mainGui
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
# Internal # Internal
self._theToC = [] self._theToC = []
self._currentRoot = None self._currentRoot = None
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
hPx = CONFIG.pxInt(12) hPx = CONFIG.pxInt(12)
vPx = CONFIG.pxInt(4) vPx = CONFIG.pxInt(4)
pOptions = self.theProject.options pOptions = self.mainGui.project.options
# Header # Header
# ====== # ======
self.tocLabel = QLabel("<b>%s</b>" % self.tr("Table of Contents")) self.tocLabel = QLabel("<b>%s</b>" % self.tr("Table of Contents"))
self.novelValue = NovelSelector(self, self.theProject, self.mainGui) self.novelValue = NovelSelector(self, self.mainGui)
self.novelValue.setMinimumWidth(CONFIG.pxInt(200)) self.novelValue.setMinimumWidth(CONFIG.pxInt(200))
self.novelValue.novelSelectionChanged.connect(self._novelValueChanged) self.novelValue.novelSelectionChanged.connect(self._novelValueChanged)
@@ -447,7 +443,7 @@ class GuiProjectDetailsContents(QWidget):
"""Extract the information from the project index. """Extract the information from the project index.
""" """
logger.debug("Populating ToC from handle '%s'", rootHandle) logger.debug("Populating ToC from handle '%s'", rootHandle)
self._theToC = self.theProject.index.getTableOfContents(rootHandle, 2) self._theToC = self.mainGui.project.index.getTableOfContents(rootHandle, 2)
self._theToC.append(("", 0, self.tr("END"), 0)) self._theToC.append(("", 0, self.tr("END"), 0))
return return
@@ -500,7 +496,7 @@ class GuiProjectDetailsContents(QWidget):
progPage = f"{cPage:n}" progPage = f"{cPage:n}"
progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%" progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"
hDec = self.mainTheme.getHeaderDecoration(tLevel) hDec = CONFIG.theme.getHeaderDecoration(tLevel)
if tTitle.strip() == "": if tTitle.strip() == "":
tTitle = self.tr("Untitled") tTitle = self.tr("Untitled")
+10 -11
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Open Project novelWriter GUI Open Project
============================== ==============================
GUI class for the load/browse/new project dialog
File History: File History:
Created: 2020-02-26 [0.4.5] Created: 2020-02-26 [0.4.5]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -62,13 +62,12 @@ class GuiProjectLoad(QDialog):
self.setObjectName("GuiProjectLoad") self.setObjectName("GuiProjectLoad")
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.openState = self.NONE_STATE self.openState = self.NONE_STATE
self.openPath = None self.openPath = None
sPx = CONFIG.pxInt(16) sPx = CONFIG.pxInt(16)
nPx = CONFIG.pxInt(96) nPx = CONFIG.pxInt(96)
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout() self.innerBox = QHBoxLayout()
@@ -80,7 +79,7 @@ class GuiProjectLoad(QDialog):
self.setMinimumHeight(CONFIG.pxInt(400)) self.setMinimumHeight(CONFIG.pxInt(400))
self.nwIcon = QLabel() self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx))) self.nwIcon.setPixmap(CONFIG.theme.getPixmap("novelwriter", (nPx, nPx)))
self.innerBox.addWidget(self.nwIcon, 0, Qt.AlignTop) self.innerBox.addWidget(self.nwIcon, 0, Qt.AlignTop)
self.projectForm = QGridLayout() self.projectForm = QGridLayout()
@@ -101,8 +100,9 @@ class GuiProjectLoad(QDialog):
self.listBox.setIconSize(QSize(iPx, iPx)) self.listBox.setIconSize(QSize(iPx, iPx))
treeHead = self.listBox.headerItem() treeHead = self.listBox.headerItem()
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight) if treeHead:
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight) treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects")) self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects"))
self.lblPath = QLabel("<b>%s</b>" % self.tr("Path")) self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
@@ -110,7 +110,7 @@ class GuiProjectLoad(QDialog):
self.selPath.setReadOnly(True) self.selPath.setReadOnly(True)
self.browseButton = QPushButton("...") self.browseButton = QPushButton("...")
self.browseButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) self.browseButton.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("...")))
self.browseButton.clicked.connect(self._doBrowse) self.browseButton.clicked.connect(self._doBrowse)
self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3) self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3)
@@ -268,7 +268,7 @@ class GuiProjectLoad(QDialog):
self.listBox.clear() self.listBox.clear()
dataList = CONFIG.recentProjects.listEntries() dataList = CONFIG.recentProjects.listEntries()
sortList = sorted(dataList, key=lambda x: x[3], reverse=True) sortList = sorted(dataList, key=lambda x: x[3], reverse=True)
nwxIcon = self.mainGui.mainTheme.getIcon("proj_nwx") nwxIcon = CONFIG.theme.getIcon("proj_nwx")
for path, title, words, time in sortList: for path, title, words, time in sortList:
newItem = QTreeWidgetItem([""]*4) newItem = QTreeWidgetItem([""]*4)
newItem.setIcon(self.C_NAME, nwxIcon) newItem.setIcon(self.C_NAME, nwxIcon)
@@ -279,11 +279,10 @@ class GuiProjectLoad(QDialog):
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter) newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter) newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter) newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed) newItem.setFont(self.C_TIME, CONFIG.theme.guiFontFixed)
self.listBox.addTopLevelItem(newItem) self.listBox.addTopLevelItem(newItem)
if self.listBox.topLevelItemCount() > 0: self.listBox.setCurrentItem(self.listBox.topLevelItem(0))
self.listBox.topLevelItem(0).setSelected(True)
projColWidth = CONFIG.projLoadColWidths projColWidth = CONFIG.projLoadColWidths
if len(projColWidth) == 3: if len(projColWidth) == 3:
+37 -42
View File
@@ -60,15 +60,13 @@ class GuiProjectSettings(NPagedDialog):
logger.debug("Create: GuiProjectSettings") logger.debug("Create: GuiProjectSettings")
self.setObjectName("GuiProjectSettings") self.setObjectName("GuiProjectSettings")
self.mainGui = mainGui self.mainGui = mainGui
self.theProject = mainGui.theProject self.mainGui.project.countStatus()
self.theProject.countStatus()
self.setWindowTitle(self.tr("Project Settings")) self.setWindowTitle(self.tr("Project Settings"))
wW = CONFIG.pxInt(570) wW = CONFIG.pxInt(570)
wH = CONFIG.pxInt(375) wH = CONFIG.pxInt(375)
pOptions = self.theProject.options pOptions = self.mainGui.project.options
self.setMinimumWidth(wW) self.setMinimumWidth(wW)
self.setMinimumHeight(wH) self.setMinimumHeight(wH)
@@ -117,34 +115,35 @@ class GuiProjectSettings(NPagedDialog):
def _doSave(self): def _doSave(self):
"""Save settings and close dialog. """Save settings and close dialog.
""" """
project = self.mainGui.project
projName = self.tabMain.editName.text() projName = self.tabMain.editName.text()
bookTitle = self.tabMain.editTitle.text() bookTitle = self.tabMain.editTitle.text()
bookAuthor = self.tabMain.editAuthor.text() bookAuthor = self.tabMain.editAuthor.text()
spellLang = self.tabMain.spellLang.currentData() spellLang = self.tabMain.spellLang.currentData()
doBackup = not self.tabMain.doBackup.isChecked() doBackup = not self.tabMain.doBackup.isChecked()
self.theProject.data.setName(projName) project.data.setName(projName)
self.theProject.data.setTitle(bookTitle) project.data.setTitle(bookTitle)
self.theProject.data.setAuthor(bookAuthor) project.data.setAuthor(bookAuthor)
self.theProject.data.setDoBackup(doBackup) project.data.setDoBackup(doBackup)
# Remember this as updating spell dictionary can be expensive # Remember this as updating spell dictionary can be expensive
self._spellChanged = self.theProject.data.setSpellLang(spellLang) self._spellChanged = project.data.setSpellLang(spellLang)
if self.tabStatus.colChanged: if self.tabStatus.colChanged:
newList, delList = self.tabStatus.getNewList() newList, delList = self.tabStatus.getNewList()
self.theProject.setStatusColours(newList, delList) project.setStatusColours(newList, delList)
if self.tabImport.colChanged: if self.tabImport.colChanged:
newList, delList = self.tabImport.getNewList() newList, delList = self.tabImport.getNewList()
self.theProject.setImportColours(newList, delList) project.setImportColours(newList, delList)
if self.tabStatus.colChanged or self.tabImport.colChanged: if self.tabStatus.colChanged or self.tabImport.colChanged:
self.mainGui.rebuildTrees() self.mainGui.rebuildTrees()
if self.tabReplace.arChanged: if self.tabReplace.arChanged:
newList = self.tabReplace.getNewList() newList = self.tabReplace.getNewList()
self.theProject.data.setAutoReplace(newList) project.data.setAutoReplace(newList)
self._saveGuiSettings() self._saveGuiSettings()
self.accept() self.accept()
@@ -184,7 +183,7 @@ class GuiProjectSettings(NPagedDialog):
statusColW = CONFIG.rpxInt(self.tabStatus.listBox.columnWidth(0)) statusColW = CONFIG.rpxInt(self.tabStatus.listBox.columnWidth(0))
importColW = CONFIG.rpxInt(self.tabImport.listBox.columnWidth(0)) importColW = CONFIG.rpxInt(self.tabImport.listBox.columnWidth(0))
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiProjectSettings", "winWidth", winWidth) pOptions.setValue("GuiProjectSettings", "winWidth", winWidth)
pOptions.setValue("GuiProjectSettings", "winHeight", winHeight) pOptions.setValue("GuiProjectSettings", "winHeight", winHeight)
pOptions.setValue("GuiProjectSettings", "replaceColW", replaceColW) pOptions.setValue("GuiProjectSettings", "replaceColW", replaceColW)
@@ -201,22 +200,22 @@ class GuiProjectEditMain(QWidget):
def __init__(self, projGui): def __init__(self, projGui):
super().__init__(parent=projGui) super().__init__(parent=projGui)
self.mainGui = projGui.mainGui self.mainGui = projGui.mainGui
self.theProject = projGui.theProject
# The Form # The Form
self.mainForm = NConfigLayout() self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainGui.mainTheme.helpText) self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
self.mainForm.addGroupLabel(self.tr("Project Settings")) self.mainForm.addGroupLabel(self.tr("Project Settings"))
xW = CONFIG.pxInt(250) xW = CONFIG.pxInt(250)
pData = self.mainGui.project.data
self.editName = QLineEdit() self.editName = QLineEdit()
self.editName.setMaxLength(200) self.editName.setMaxLength(200)
self.editName.setMaximumWidth(xW) self.editName.setMaximumWidth(xW)
self.editName.setText(self.theProject.data.name) self.editName.setText(pData.name)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Project name"), self.tr("Project name"),
self.editName, self.editName,
@@ -226,7 +225,7 @@ class GuiProjectEditMain(QWidget):
self.editTitle = QLineEdit() self.editTitle = QLineEdit()
self.editTitle.setMaxLength(200) self.editTitle.setMaxLength(200)
self.editTitle.setMaximumWidth(xW) self.editTitle.setMaximumWidth(xW)
self.editTitle.setText(self.theProject.data.title) self.editTitle.setText(pData.title)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Novel title"), self.tr("Novel title"),
self.editTitle, self.editTitle,
@@ -236,7 +235,7 @@ class GuiProjectEditMain(QWidget):
self.editAuthor = QLineEdit() self.editAuthor = QLineEdit()
self.editAuthor.setMaxLength(200) self.editAuthor.setMaxLength(200)
self.editAuthor.setMaximumWidth(xW) self.editAuthor.setMaximumWidth(xW)
self.editAuthor.setText(self.theProject.data.author) self.editAuthor.setText(pData.author)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Author(s)"), self.tr("Author(s)"),
self.editAuthor, self.editAuthor,
@@ -259,13 +258,13 @@ class GuiProjectEditMain(QWidget):
) )
spellIdx = 0 spellIdx = 0
if self.theProject.data.spellLang is not None: if pData.spellLang is not None:
spellIdx = self.spellLang.findData(self.theProject.data.spellLang) spellIdx = self.spellLang.findData(pData.spellLang)
if spellIdx != -1: if spellIdx != -1:
self.spellLang.setCurrentIndex(spellIdx) self.spellLang.setCurrentIndex(spellIdx)
self.doBackup = NSwitch(self) self.doBackup = NSwitch(self)
self.doBackup.setChecked(not self.theProject.data.doBackup) self.doBackup.setChecked(not pData.doBackup)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("No backup on close"), self.tr("No backup on close"),
self.doBackup, self.doBackup,
@@ -289,28 +288,26 @@ class GuiProjectEditStatus(QWidget):
def __init__(self, projGui, isStatus): def __init__(self, projGui, isStatus):
super().__init__(parent=projGui) super().__init__(parent=projGui)
self.mainGui = projGui.mainGui self.mainGui = projGui.mainGui
self.theProject = projGui.theProject
self.mainTheme = projGui.mainGui.mainTheme
if isStatus: if isStatus:
self.theStatus = self.theProject.data.itemStatus self.theStatus = self.mainGui.project.data.itemStatus
pageLabel = self.tr("Novel File Status Levels") pageLabel = self.tr("Novel File Status Levels")
colSetting = "statusColW" colSetting = "statusColW"
else: else:
self.theStatus = self.theProject.data.itemImport self.theStatus = self.mainGui.project.data.itemImport
pageLabel = self.tr("Note File Importance Levels") pageLabel = self.tr("Note File Importance Levels")
colSetting = "importColW" colSetting = "importColW"
wCol0 = CONFIG.pxInt( wCol0 = CONFIG.pxInt(
self.theProject.options.getInt("GuiProjectSettings", colSetting, 130) self.mainGui.project.options.getInt("GuiProjectSettings", colSetting, 130)
) )
self.colDeleted = [] self.colDeleted = []
self.colChanged = False self.colChanged = False
self.selColour = QColor(100, 100, 100) self.selColour = QColor(100, 100, 100)
self.iPx = self.mainTheme.baseIconSize self.iPx = CONFIG.theme.baseIconSize
# The List # The List
# ======== # ========
@@ -329,16 +326,16 @@ class GuiProjectEditStatus(QWidget):
# List Controls # List Controls
# ============= # =============
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "") self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "")
self.addButton.clicked.connect(self._newItem) self.addButton.clicked.connect(self._newItem)
self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "") self.delButton = QPushButton(CONFIG.theme.getIcon("remove"), "")
self.delButton.clicked.connect(self._delItem) self.delButton.clicked.connect(self._delItem)
self.upButton = QPushButton(self.mainTheme.getIcon("up"), "") self.upButton = QPushButton(CONFIG.theme.getIcon("up"), "")
self.upButton.clicked.connect(lambda: self._moveItem(-1)) self.upButton.clicked.connect(lambda: self._moveItem(-1))
self.dnButton = QPushButton(self.mainTheme.getIcon("down"), "") self.dnButton = QPushButton(CONFIG.theme.getIcon("down"), "")
self.dnButton.clicked.connect(lambda: self._moveItem(1)) self.dnButton.clicked.connect(lambda: self._moveItem(1))
# Edit Form # Edit Form
@@ -577,13 +574,11 @@ class GuiProjectEditReplace(QWidget):
def __init__(self, projGui): def __init__(self, projGui):
super().__init__(parent=projGui) super().__init__(parent=projGui)
self.mainGui = projGui.mainGui self.mainGui = projGui.mainGui
self.mainTheme = projGui.mainGui.mainTheme self.arChanged = False
self.theProject = projGui.theProject
self.arChanged = False
wCol0 = CONFIG.pxInt( wCol0 = CONFIG.pxInt(
self.theProject.options.getInt("GuiProjectSettings", "replaceColW", 130) self.mainGui.project.options.getInt("GuiProjectSettings", "replaceColW", 130)
) )
pageLabel = self.tr("Text Replace List for Preview and Export") pageLabel = self.tr("Text Replace List for Preview and Export")
@@ -599,7 +594,7 @@ class GuiProjectEditReplace(QWidget):
self.listBox.setColumnWidth(self.COL_KEY, wCol0) self.listBox.setColumnWidth(self.COL_KEY, wCol0)
self.listBox.setIndentation(0) self.listBox.setIndentation(0)
for aKey, aVal in self.theProject.data.autoReplace.items(): for aKey, aVal in self.mainGui.project.data.autoReplace.items():
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal]) newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
self.listBox.addTopLevelItem(newItem) self.listBox.addTopLevelItem(newItem)
@@ -609,10 +604,10 @@ class GuiProjectEditReplace(QWidget):
# List Controls # List Controls
# ============= # =============
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "") self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "")
self.addButton.clicked.connect(self._addEntry) self.addButton.clicked.connect(self._addEntry)
self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "") self.delButton = QPushButton(CONFIG.theme.getIcon("remove"), "")
self.delButton.clicked.connect(self._delEntry) self.delButton.clicked.connect(self._delEntry)
# Edit Form # Edit Form
+1 -1
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Quotes Dialog novelWriter GUI Quotes Dialog
=============================== ===============================
GUI class for quotes dialog
File History: File History:
Created: 2020-06-18 [0.9] Created: 2020-06-18 [0.9]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
+2 -5
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Updates novelWriter GUI Updates
========================= =========================
A dialog box for checking for latest updates
File History: File History:
Created: 2021-08-21 [1.5b1] Created: 2021-08-21 [1.5b1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import json import json
import logging import logging
@@ -49,9 +49,6 @@ class GuiUpdates(QDialog):
logger.debug("Create: GuiUpdates") logger.debug("Create: GuiUpdates")
self.setObjectName("GuiUpdates") self.setObjectName("GuiUpdates")
self.mainGui = mainGui
self.setWindowTitle(self.tr("Check for Updates")) self.setWindowTitle(self.tr("Check for Updates"))
nPx = CONFIG.pxInt(96) nPx = CONFIG.pxInt(96)
@@ -61,7 +58,7 @@ class GuiUpdates(QDialog):
# Left Box # Left Box
self.nwIcon = QLabel() self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx))) self.nwIcon.setPixmap(CONFIG.theme.getPixmap("novelwriter", (nPx, nPx)))
self.leftBox = QVBoxLayout() self.leftBox = QVBoxLayout()
self.leftBox.addWidget(self.nwIcon) self.leftBox.addWidget(self.nwIcon)
+8 -11
View File
@@ -50,17 +50,14 @@ class GuiWordList(QDialog):
logger.debug("Create: GuiWordList") logger.debug("Create: GuiWordList")
self.setObjectName("GuiWordList") self.setObjectName("GuiWordList")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.setWindowTitle(self.tr("Project Word List")) self.setWindowTitle(self.tr("Project Word List"))
self.mainGui = mainGui
mS = CONFIG.pxInt(250) mS = CONFIG.pxInt(250)
wW = CONFIG.pxInt(320) wW = CONFIG.pxInt(320)
wH = CONFIG.pxInt(340) wH = CONFIG.pxInt(340)
pOptions = self.theProject.options pOptions = self.mainGui.project.options
self.setMinimumWidth(mS) self.setMinimumWidth(mS)
self.setMinimumHeight(mS) self.setMinimumHeight(mS)
@@ -80,10 +77,10 @@ class GuiWordList(QDialog):
self.newEntry = QLineEdit() self.newEntry = QLineEdit()
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "") self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "")
self.addButton.clicked.connect(self._doAdd) self.addButton.clicked.connect(self._doAdd)
self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "") self.delButton = QPushButton(CONFIG.theme.getIcon("remove"), "")
self.delButton.clicked.connect(self._doDelete) self.delButton.clicked.connect(self._doDelete)
self.editBox = QHBoxLayout() self.editBox = QHBoxLayout()
@@ -152,7 +149,7 @@ class GuiWordList(QDialog):
def _doSave(self): def _doSave(self):
"""Save the new word list and close.""" """Save the new word list and close."""
self._saveGuiSettings() self._saveGuiSettings()
userDict = UserDictionary(self.theProject) userDict = UserDictionary(self.mainGui.project)
for i in range(self.listBox.count()): for i in range(self.listBox.count()):
item = self.listBox.item(i) item = self.listBox.item(i)
if isinstance(item, QListWidgetItem): if isinstance(item, QListWidgetItem):
@@ -175,7 +172,7 @@ class GuiWordList(QDialog):
def _loadWordList(self): def _loadWordList(self):
"""Load the project's word list, if it exists.""" """Load the project's word list, if it exists."""
userDict = UserDictionary(self.theProject) userDict = UserDictionary(self.mainGui.project)
userDict.load() userDict.load()
self.listBox.clear() self.listBox.clear()
for word in userDict: for word in userDict:
@@ -188,7 +185,7 @@ class GuiWordList(QDialog):
winWidth = CONFIG.rpxInt(self.width()) winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height()) winHeight = CONFIG.rpxInt(self.height())
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiWordList", "winWidth", winWidth) pOptions.setValue("GuiWordList", "winWidth", winWidth)
pOptions.setValue("GuiWordList", "winHeight", winHeight) pOptions.setValue("GuiWordList", "winHeight", winHeight)
-1
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Enums novelWriter Enums
=================== ===================
Global enum values
File History: File History:
Created: 2018-11-02 [0.0.1] Created: 2018-11-02 [0.0.1]
+5 -7
View File
@@ -42,7 +42,7 @@ class NProgressCircle(QProgressBar):
"_cPen", "_bPen", "_tColor" "_cPen", "_bPen", "_tColor"
) )
def __init__(self, parent: QWidget, size: int, point: int): def __init__(self, parent: QWidget, size: int, point: int) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
self._text = None self._text = None
self._point = point self._point = point
@@ -60,10 +60,8 @@ class NProgressCircle(QProgressBar):
self.setFixedHeight(size) self.setFixedHeight(size)
return return
def setColours( def setColours(self, back: QColor | None = None, track: QColor | None = None,
self, back: QColor | None = None, track: QColor | None = None, bar: QColor | None = None, text: QColor | None = None) -> None:
bar: QColor | None = None, text: QColor | None = None
):
"""Set the colours of the widget.""" """Set the colours of the widget."""
if isinstance(back, QColor): if isinstance(back, QColor):
self._dPen = QPen(back) self._dPen = QPen(back)
@@ -76,13 +74,13 @@ class NProgressCircle(QProgressBar):
self._tColor = text self._tColor = text
return return
def setCentreText(self, text: str | None): def setCentreText(self, text: str | None) -> None:
"""Replace the progress text with a custom string.""" """Replace the progress text with a custom string."""
self._text = text self._text = text
self.setValue(self.value()) # Triggers a redraw self.setValue(self.value()) # Triggers a redraw
return return
def paintEvent(self, event: QPaintEvent): def paintEvent(self, event: QPaintEvent) -> None:
"""Custom painter for the progress bar.""" """Custom painter for the progress bar."""
progress = 100.0*self.value()/self.maximum() progress = 100.0*self.value()/self.maximum()
angle = ceil(16*3.6*progress) angle = ceil(16*3.6*progress)
+13 -14
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Custom Widget: Config Layout novelWriter Custom Widget: Config Layout
========================================== ==========================================
A custom grid layout for config pages
File History: File History:
Created: 2020-05-03 [0.4.5] Created: 2020-05-03 [0.4.5]
@@ -38,7 +37,7 @@ FONT_SCALE = 0.9
class NConfigLayout(QGridLayout): class NConfigLayout(QGridLayout):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self._nextRow = 0 self._nextRow = 0
@@ -57,7 +56,8 @@ class NConfigLayout(QGridLayout):
# Getters and Setters # Getters and Setters
## ##
def setHelpTextStyle(self, color: QColor | list | tuple, fontScale: float = FONT_SCALE): def setHelpTextStyle(self, color: QColor | list | tuple,
fontScale: float = FONT_SCALE) -> None:
"""Set the text color for the help text.""" """Set the text color for the help text."""
if isinstance(color, QColor): if isinstance(color, QColor):
self._helpCol = color self._helpCol = color
@@ -66,7 +66,7 @@ class NConfigLayout(QGridLayout):
self._fontScale = fontScale self._fontScale = fontScale
return return
def setHelpText(self, row: int, text: str): def setHelpText(self, row: int, text: str) -> None:
"""Set the text for the help label.""" """Set the text for the help label."""
if row in self._itemMap: if row in self._itemMap:
qHelp = self._itemMap[row][1] qHelp = self._itemMap[row][1]
@@ -74,7 +74,7 @@ class NConfigLayout(QGridLayout):
qHelp.setText(text) qHelp.setText(text)
return return
def setLabelText(self, row: int, text: str): def setLabelText(self, row: int, text: str) -> None:
"""Set the text for the main label.""" """Set the text for the main label."""
if row in self._itemMap: if row in self._itemMap:
self._itemMap[row](0).setText(text) self._itemMap[row](0).setText(text)
@@ -84,7 +84,7 @@ class NConfigLayout(QGridLayout):
# Class Methods # Class Methods
## ##
def addGroupLabel(self, label: str): def addGroupLabel(self, label: str) -> None:
"""Add a text label to separate groups of settings.""" """Add a text label to separate groups of settings."""
hM = CONFIG.pxInt(4) hM = CONFIG.pxInt(4)
qLabel = QLabel("<b>%s</b>" % label) qLabel = QLabel("<b>%s</b>" % label)
@@ -95,10 +95,8 @@ class NConfigLayout(QGridLayout):
self._nextRow += 1 self._nextRow += 1
return return
def addRow( def addRow(self, label: str, widget: QWidget, helpText: str | None = None,
self, label: str, widget: QWidget, helpText: str | None = None, unit: str | None = None, button: QWidget | None = None) -> int:
unit: str | None = None, button: QWidget | None = None
) -> int:
"""Add a label and a widget as a new row of the grid.""" """Add a label and a widget as a new row of the grid."""
wSp = CONFIG.pxInt(8) wSp = CONFIG.pxInt(8)
qLabel = QLabel(label) qLabel = QLabel(label)
@@ -156,7 +154,7 @@ class NSimpleLayout(QGridLayout):
column layout. column layout.
""" """
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self._nextRow = 0 self._nextRow = 0
@@ -171,7 +169,7 @@ class NSimpleLayout(QGridLayout):
# Methods # Methods
## ##
def addGroupLabel(self, label: str): def addGroupLabel(self, label: str) -> None:
"""Add a text label to separate groups of settings.""" """Add a text label to separate groups of settings."""
hM = CONFIG.pxInt(4) hM = CONFIG.pxInt(4)
qLabel = QLabel("<b>%s</b>" % label) qLabel = QLabel("<b>%s</b>" % label)
@@ -182,7 +180,7 @@ class NSimpleLayout(QGridLayout):
self._nextRow += 1 self._nextRow += 1
return return
def addRow(self, label: str, widget: QWidget): def addRow(self, label: str, widget: QWidget) -> None:
"""Add a label and a widget as a new row of the grid.""" """Add a label and a widget as a new row of the grid."""
wSp = CONFIG.pxInt(8) wSp = CONFIG.pxInt(8)
qLabel = QLabel(label) qLabel = QLabel(label)
@@ -209,7 +207,8 @@ class NSimpleLayout(QGridLayout):
class NHelpLabel(QLabel): class NHelpLabel(QLabel):
def __init__(self, text: str, color: QColor | list | tuple, fontSize: float = FONT_SCALE): def __init__(self, text: str, color: QColor | list | tuple,
fontSize: float = FONT_SCALE) -> None:
super().__init__(text) super().__init__(text)
if isinstance(color, QColor): if isinstance(color, QColor):
@@ -1,11 +1,9 @@
""" """
novelWriter GUI Components Module novelWriter Custom Widget: Novel Selector
=================================== ===========================================
A module of various small GUI components
File History: File History:
Created: 2020-05-17 [0.5.1] StatusLED Created: 2022-11-17 [2.0]
Created: 2022-11-17 [2.0] NovelSelector
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -23,16 +21,22 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
from PyQt5.QtGui import QPainter from typing import TYPE_CHECKING
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QAbstractButton, QComboBox
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QComboBox, QWidget
from novelwriter import CONFIG
from novelwriter.enum import nwItemClass from novelwriter.enum import nwItemClass
from novelwriter.constants import nwLabels from novelwriter.constants import nwLabels
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -40,17 +44,12 @@ class NovelSelector(QComboBox):
novelSelectionChanged = pyqtSignal(str) novelSelectionChanged = pyqtSignal(str)
def __init__(self, parent, project, mainGui): def __init__(self, parent: QWidget, mainGui: GuiMain) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
self._mainGui = mainGui self._mainGui = mainGui
self._project = project
self._theme = mainGui.mainTheme
self._blockSignal = False self._blockSignal = False
self._firstHandle = None self._firstHandle = None
self.currentIndexChanged.connect(self._indexChanged) self.currentIndexChanged.connect(self._indexChanged)
return return
## ##
@@ -58,20 +57,19 @@ class NovelSelector(QComboBox):
## ##
@property @property
def handle(self): def handle(self) -> str:
return self.currentData() return self.currentData()
@property @property
def firstHandle(self): def firstHandle(self) -> str | None:
return self._firstHandle return self._firstHandle
## ##
# Methods # Methods
## ##
def setHandle(self, tHandle, blockSignal=True): def setHandle(self, tHandle: str, blockSignal: bool = True) -> None:
"""Set the currently selected handle. """Set the currently selected handle."""
"""
self._blockSignal = blockSignal self._blockSignal = blockSignal
if tHandle is None: if tHandle is None:
index = self.count() - 1 index = self.count() - 1
@@ -82,16 +80,15 @@ class NovelSelector(QComboBox):
self._blockSignal = False self._blockSignal = False
return return
def updateList(self, includeAll=False, prefix=None): def updateList(self, includeAll: bool = False, prefix: str | None = None) -> None:
"""Rebuild the list of novel items. """Rebuild the list of novel items."""
"""
self._blockSignal = True self._blockSignal = True
self._firstHandle = None self._firstHandle = None
self.clear() self.clear()
icon = self._theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]) icon = CONFIG.theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
handle = self.currentData() handle = self.currentData()
for tHandle, nwItem in self._project.tree.iterRoots(nwItemClass.NOVEL): for tHandle, nwItem in self._mainGui.project.tree.iterRoots(nwItemClass.NOVEL):
if prefix: if prefix:
name = prefix.format(nwItem.itemName) name = prefix.format(nwItem.itemName)
self.addItem(name, tHandle) self.addItem(name, tHandle)
@@ -116,67 +113,10 @@ class NovelSelector(QComboBox):
## ##
@pyqtSlot(int) @pyqtSlot(int)
def _indexChanged(self, index): def _indexChanged(self, index: int) -> None:
"""Re-emit the change of selected novel signal, unless blocked. """Re-emit the change of selection signal, unless blocked."""
"""
if not self._blockSignal: if not self._blockSignal:
self.novelSelectionChanged.emit(self.currentData()) self.novelSelectionChanged.emit(self.currentData())
return return
# END Class NovelSelector # END Class NovelSelector
class StatusLED(QAbstractButton):
S_NONE = 0
S_BAD = 1
S_GOOD = 2
def __init__(self, colNone, colGood, colBad, sW, sH, parent=None):
super().__init__(parent=parent)
self._colNone = colNone
self._colGood = colGood
self._colBad = colBad
self._theCol = colNone
self.setFixedWidth(sW)
self.setFixedHeight(sH)
return
##
# Setters
##
def setState(self, theState):
"""Set the colour state.
"""
if theState == self.S_GOOD:
self._theCol = self._colGood
elif theState == self.S_BAD:
self._theCol = self._colBad
else:
self._theCol = self._colNone
self.update()
return
##
# Events
##
def paintEvent(self, _):
"""Drawing the LED.
"""
qPalette = self.palette()
qPaint = QPainter(self)
qPaint.setRenderHint(QPainter.Antialiasing, True)
qPaint.setPen(qPalette.dark().color())
qPaint.setBrush(self._theCol)
qPaint.setOpacity(1.0)
qPaint.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
return
# END Class StatusLED
+15 -18
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Custom Widget: Paged Dialog novelWriter Custom Widget: Paged Dialog
========================================= =========================================
A custom dialog with tabs and a vertical tab bar
File History: File History:
Created: 2020-05-17 [0.5.1] Created: 2020-05-17 [0.5.1]
@@ -22,11 +21,13 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
from PyQt5.QtCore import QRect, QPoint from PyQt5.QtGui import QPaintEvent
from PyQt5.QtCore import QRect, QPoint, QSize
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar, QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar,
QTabWidget, QVBoxLayout QTabWidget, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG from novelwriter import CONFIG
@@ -34,7 +35,7 @@ from novelwriter import CONFIG
class NPagedDialog(QDialog): class NPagedDialog(QDialog):
def __init__(self, parent=None): def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
self._tabBar = NVerticalTabBar(self) self._tabBar = NVerticalTabBar(self)
@@ -67,21 +68,18 @@ class NPagedDialog(QDialog):
return return
def addTab(self, widget, label): def addTab(self, widget: QWidget, label: str) -> None:
"""Forward the adding of tabs to the QTabWidget. """Forward the adding of tabs to the QTabWidget."""
"""
self._tabBox.addTab(widget, label) self._tabBox.addTab(widget, label)
return return
def addControls(self, buttonBar): def addControls(self, buttonBar: QWidget) -> None:
"""Add a button bar to the dialog. """Add a button bar to the dialog."""
"""
self._buttonBox.addWidget(buttonBar) self._buttonBox.addWidget(buttonBar)
return return
def setCurrentWidget(self, widget): def setCurrentWidget(self, widget: QWidget) -> None:
"""Forward the changing of tab to the QTabWidget. """Forward the changing of tab to the QTabWidget."""
"""
self._tabBox.setCurrentWidget(widget) self._tabBox.setCurrentWidget(widget)
return return
@@ -90,20 +88,19 @@ class NPagedDialog(QDialog):
class NVerticalTabBar(QTabBar): class NVerticalTabBar(QTabBar):
def __init__(self, parent=None): def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
self._mW = CONFIG.pxInt(150) self._mW = CONFIG.pxInt(150)
return return
def tabSizeHint(self, index): def tabSizeHint(self, index: int) -> QSize:
"""Return a transposed size hint for the rotated bar. """Return a transposed size hint for the rotated bar."""
"""
tSize = super().tabSizeHint(index) tSize = super().tabSizeHint(index)
tSize.transpose() tSize.transpose()
tSize.setWidth(min(tSize.width(), self._mW)) tSize.setWidth(min(tSize.width(), self._mW))
return tSize return tSize
def paintEvent(self, event): def paintEvent(self, event: QPaintEvent) -> None:
"""Custom implementation of the label painter that rotates the """Custom implementation of the label painter that rotates the
label 90 degrees. label 90 degrees.
""" """
+2 -2
View File
@@ -35,11 +35,11 @@ class NProgressSimple(QProgressBar):
A custom widget that paints a plain bar with no other styling. A custom widget that paints a plain bar with no other styling.
""" """
def __init__(self, parent: QWidget): def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
return return
def paintEvent(self, event: QPaintEvent): def paintEvent(self, event: QPaintEvent) -> None:
"""Custom painter for the progress bar.""" """Custom painter for the progress bar."""
if self.value() == 0: if self.value() == 0:
return return
+77
View File
@@ -0,0 +1,77 @@
"""
novelWriter Custom Widget: Status LED
=======================================
File History:
Created: 2020-05-17 [0.5.1]
This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
from typing import Literal
from PyQt5.QtGui import QColor, QPaintEvent, QPainter
from PyQt5.QtWidgets import QAbstractButton, QWidget
logger = logging.getLogger(__name__)
class StatusLED(QAbstractButton):
S_NONE = 0
S_BAD = 1
S_GOOD = 2
def __init__(self, colNone: QColor, colGood: QColor, colBad: QColor,
sW: int, sH: int, parent: QWidget | None = None) -> None:
super().__init__(parent=parent)
self._colNone = colNone
self._colGood = colGood
self._colBad = colBad
self._theCol = colNone
self.setFixedWidth(sW)
self.setFixedHeight(sH)
return
def setState(self, state: Literal[0, 1, 2]) -> None:
"""Set the colour state."""
if state == self.S_GOOD:
self._theCol = self._colGood
elif state == self.S_BAD:
self._theCol = self._colBad
else:
self._theCol = self._colNone
self.update()
return
def paintEvent(self, event: QPaintEvent) -> None:
"""Drawing the LED."""
qPalette = self.palette()
qPaint = QPainter(self)
qPaint.setRenderHint(QPainter.Antialiasing, True)
qPaint.setPen(qPalette.dark().color())
qPaint.setBrush(self._theCol)
qPaint.setOpacity(1.0)
qPaint.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
return
# END Class StatusLED
+27 -31
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Custom Widget: Switch novelWriter Custom Widget: Switch
=================================== ===================================
A custom switch widget
File History: File History:
Created: 2020-05-03 [0.4.5] Created: 2020-05-03 [0.4.5]
@@ -22,10 +21,11 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
from PyQt5.QtGui import QPainter from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent
from PyQt5.QtCore import Qt, QRectF, QPropertyAnimation, pyqtProperty from PyQt5.QtCore import QEvent, QPropertyAnimation, QRectF, Qt, pyqtProperty
from PyQt5.QtWidgets import QSizePolicy, QAbstractButton from PyQt5.QtWidgets import QAbstractButton, QSizePolicy, QWidget
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwUnicode from novelwriter.constants import nwUnicode
@@ -33,7 +33,8 @@ from novelwriter.constants import nwUnicode
class NSwitch(QAbstractButton): class NSwitch(QAbstractButton):
def __init__(self, parent=None, width=None, height=None): def __init__(self, parent: QWidget | None = None,
width: int | None = None, height: int | None = None) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
if width is None: if width is None:
@@ -64,12 +65,12 @@ class NSwitch(QAbstractButton):
# Properties # Properties
## ##
@pyqtProperty(int) @pyqtProperty(int) # type: ignore
def offset(self): def offset(self) -> int: # type: ignore
return self._offset return self._offset
@offset.setter @offset.setter # type: ignore
def offset(self, offset): def offset(self, offset: int):
self._offset = offset self._offset = offset
self.update() self.update()
return return
@@ -78,33 +79,30 @@ class NSwitch(QAbstractButton):
# Getters and Setters # Getters and Setters
## ##
def setChecked(self, checked): def setChecked(self, checked: bool) -> None:
"""Overload setChecked to also alter the offset. """Overload setChecked to also alter the offset."""
"""
super().setChecked(checked) super().setChecked(checked)
if checked: if checked:
self.offset = self._xW - self._xR self._offset = self._xW - self._xR
else: else:
self.offset = self._xR self._offset = self._xR
return return
## ##
# Events # Events
## ##
def resizeEvent(self, event): def resizeEvent(self, event: QResizeEvent) -> None:
"""Overload resize to ensure correct offset. """Overload resize to ensure correct offset."""
"""
super().resizeEvent(event) super().resizeEvent(event)
if self.isChecked(): if self.isChecked():
self.offset = self._xW - self._xR self._offset = self._xW - self._xR
else: else:
self.offset = self._xR self._offset = self._xR
return return
def paintEvent(self, event): def paintEvent(self, event: QPaintEvent) -> None:
"""Drawing the switch itself. """Drawing the switch itself."""
"""
qPaint = QPainter(self) qPaint = QPainter(self)
qPaint.setRenderHint(QPainter.Antialiasing, True) qPaint.setRenderHint(QPainter.Antialiasing, True)
qPaint.setPen(Qt.NoPen) qPaint.setPen(Qt.NoPen)
@@ -134,27 +132,26 @@ class NSwitch(QAbstractButton):
qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR) qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
qPaint.setBrush(thumbBrush) qPaint.setBrush(thumbBrush)
qPaint.drawEllipse(self.offset - self._rR, self._rB, self._rH, self._rH) qPaint.drawEllipse(self._offset - self._rR, self._rB, self._rH, self._rH)
theFont = qPaint.font() theFont = qPaint.font()
theFont.setPixelSize(self._xT) theFont.setPixelSize(self._xT)
qPaint.setPen(textColor) qPaint.setPen(textColor)
qPaint.setFont(theFont) qPaint.setFont(theFont)
qPaint.drawText( qPaint.drawText(
QRectF(self.offset - self._rR, self._rB, self._rH, self._rH), QRectF(self._offset - self._rR, self._rB, self._rH, self._rH),
Qt.AlignCenter, thumbText Qt.AlignCenter, thumbText
) )
return return
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event: QMouseEvent) -> None:
"""Animate the switch on mouse release. """Animate the switch on mouse release."""
"""
super().mouseReleaseEvent(event) super().mouseReleaseEvent(event)
if event.button() == Qt.LeftButton: if event.button() == Qt.LeftButton:
doAnim = QPropertyAnimation(self, b"offset", self) doAnim = QPropertyAnimation(self, b"offset", self)
doAnim.setDuration(120) doAnim.setDuration(120)
doAnim.setStartValue(self.offset) doAnim.setStartValue(self._offset)
if self.isChecked(): if self.isChecked():
doAnim.setEndValue(self._xW - self._xR) doAnim.setEndValue(self._xW - self._xR)
else: else:
@@ -162,9 +159,8 @@ class NSwitch(QAbstractButton):
doAnim.start() doAnim.start()
return return
def enterEvent(self, event): def enterEvent(self, event: QEvent) -> None:
"""Change the cursor when hovering the button. """Change the cursor when hovering the button."""
"""
self.setCursor(Qt.PointingHandCursor) self.setCursor(Qt.PointingHandCursor)
super().enterEvent(event) super().enterEvent(event)
return return
+9 -9
View File
@@ -23,8 +23,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations from __future__ import annotations
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
@@ -39,7 +39,7 @@ class NSwitchBox(QScrollArea):
switchToggled = pyqtSignal(str, bool) switchToggled = pyqtSignal(str, bool)
def __init__(self, parent: QWidget, baseSize: int): def __init__(self, parent: QWidget, baseSize: int) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
self._index = 0 self._index = 0
self._hSwitch = baseSize self._hSwitch = baseSize
@@ -49,7 +49,7 @@ class NSwitchBox(QScrollArea):
self.clear() self.clear()
return return
def clear(self): def clear(self) -> None:
"""Rebuild the content of the core widget.""" """Rebuild the content of the core widget."""
self._index = 0 self._index = 0
self._widgets = [] self._widgets = []
@@ -66,7 +66,7 @@ class NSwitchBox(QScrollArea):
return return
def addLabel(self, text: str): def addLabel(self, text: str) -> None:
"""Add a header label to the content box.""" """Add a header label to the content box."""
label = QLabel(text) label = QLabel(text)
font = label.font() font = label.font()
@@ -77,7 +77,7 @@ class NSwitchBox(QScrollArea):
self._bumpIndex() self._bumpIndex()
return return
def addItem(self, qIcon: QIcon, text: str, identifier: str, default: bool = False): def addItem(self, qIcon: QIcon, text: str, identifier: str, default: bool = False) -> None:
"""Add an item to the content box.""" """Add an item to the content box."""
icon = QLabel("") icon = QLabel("")
icon.setAlignment(Qt.AlignRight | Qt.AlignVCenter) icon.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
@@ -97,7 +97,7 @@ class NSwitchBox(QScrollArea):
return return
def addSeparator(self): def addSeparator(self) -> None:
"""Add a blank entry in the content box.""" """Add a blank entry in the content box."""
spacer = QWidget() spacer = QWidget()
spacer.setFixedHeight(int(0.5*self._sIcon)) spacer.setFixedHeight(int(0.5*self._sIcon))
@@ -106,7 +106,7 @@ class NSwitchBox(QScrollArea):
self._bumpIndex() self._bumpIndex()
return return
def setInnerContentsMargins(self, left: int, top: int, right: int, bottom: int): def setInnerContentsMargins(self, left: int, top: int, right: int, bottom: int) -> None:
"""Set the contents margins of the inner layout.""" """Set the contents margins of the inner layout."""
self._content.setContentsMargins(left, top, right, bottom) self._content.setContentsMargins(left, top, right, bottom)
return return
@@ -115,12 +115,12 @@ class NSwitchBox(QScrollArea):
# Internal Functions # Internal Functions
## ##
def _emitSwitchSignal(self, identifier: str, state: bool): def _emitSwitchSignal(self, identifier: str, state: bool) -> None:
"""Emit a signal for a switch toggle.""" """Emit a signal for a switch toggle."""
self.switchToggled.emit(identifier, state) self.switchToggled.emit(identifier, state)
return return
def _bumpIndex(self): def _bumpIndex(self) -> None:
"""Increase the index counter and make sure only the last """Increase the index counter and make sure only the last
columns is stretching. columns is stretching.
""" """
+60 -67
View File
@@ -84,9 +84,7 @@ class GuiDocEditor(QTextEdit):
logger.debug("Create: GuiDocEditor") logger.debug("Create: GuiDocEditor")
# Class Variables # Class Variables
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._nwDocument = None self._nwDocument = None
self._nwItem = None self._nwItem = None
@@ -134,7 +132,7 @@ class GuiDocEditor(QTextEdit):
self.docSearch = GuiDocEditSearch(self) self.docSearch = GuiDocEditSearch(self)
# Syntax # Syntax
self.spEnchant = NWSpellEnchant(self.theProject) self.spEnchant = NWSpellEnchant(self.mainGui.project)
self.highLight = GuiDocHighlighter(qDoc, self.mainGui, self.spEnchant) self.highLight = GuiDocHighlighter(qDoc, self.mainGui, self.spEnchant)
# Context Menu # Context Menu
@@ -229,14 +227,14 @@ class GuiDocEditor(QTextEdit):
"""Update the syntax highlighting theme. """Update the syntax highlighting theme.
""" """
mainPalette = self.palette() mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) mainPalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack)) mainPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) mainPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(mainPalette) self.setPalette(mainPalette)
docPalette = self.viewport().palette() docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack)) docPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) docPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.viewport().setPalette(docPalette) self.viewport().setPalette(docPalette)
self.docHeader.matchColours() self.docHeader.matchColours()
@@ -342,7 +340,7 @@ class GuiDocEditor(QTextEdit):
document is new (empty string), we set up the editor for editing document is new (empty string), we set up the editor for editing
the file. the file.
""" """
self._nwDocument = self.theProject.storage.getDocument(tHandle) self._nwDocument = self.mainGui.project.storage.getDocument(tHandle)
self._nwItem = self._nwDocument.getCurrentItem() self._nwItem = self._nwDocument.getCurrentItem()
theDoc = self._nwDocument.readDocument() theDoc = self._nwDocument.readDocument()
@@ -518,10 +516,10 @@ class GuiDocEditor(QTextEdit):
self.setDocumentChanged(False) self.setDocumentChanged(False)
oldHeader = self._nwItem.mainHeading oldHeader = self._nwItem.mainHeading
oldCount = self.theProject.index.getHandleHeaderCount(tHandle) oldCount = self.mainGui.project.index.getHandleHeaderCount(tHandle)
self.theProject.index.scanText(tHandle, docText) self.mainGui.project.index.scanText(tHandle, docText)
newHeader = self._nwItem.mainHeading newHeader = self._nwItem.mainHeading
newCount = self.theProject.index.getHandleHeaderCount(tHandle) newCount = self.mainGui.project.index.getHandleHeaderCount(tHandle)
if self._nwItem.itemClass == nwItemClass.NOVEL: if self._nwItem.itemClass == nwItemClass.NOVEL:
if oldCount == newCount: if oldCount == newCount:
@@ -700,10 +698,10 @@ class GuiDocEditor(QTextEdit):
"""Set the spell checker dictionary language, and emit the """Set the spell checker dictionary language, and emit the
dictionary changed signal. dictionary changed signal.
""" """
if self.theProject.data.spellLang is None: if self.mainGui.project.data.spellLang is None:
theLang = CONFIG.spellLanguage theLang = CONFIG.spellLanguage
else: else:
theLang = self.theProject.data.spellLang theLang = self.mainGui.project.data.spellLang
self.spEnchant.setLanguage(theLang) self.spEnchant.setLanguage(theLang)
_, theProvider = self.spEnchant.describeDict() _, theProvider = self.spEnchant.describeDict()
@@ -736,7 +734,7 @@ class GuiDocEditor(QTextEdit):
self._spellCheck = theMode self._spellCheck = theMode
self.mainGui.mainMenu.setSpellCheck(theMode) self.mainGui.mainMenu.setSpellCheck(theMode)
self.theProject.data.setSpellCheck(theMode) self.mainGui.project.data.setSpellCheck(theMode)
self.highLight.setSpellCheck(theMode) self.highLight.setSpellCheck(theMode)
if not self._bigDoc or theMode is False: if not self._bigDoc or theMode is False:
# We don't run the spell checker automatically on big docs # We don't run the spell checker automatically on big docs
@@ -1918,7 +1916,7 @@ class GuiDocEditor(QTextEdit):
if theText.startswith("@"): if theText.startswith("@"):
isGood, tBits, tPos = self.theProject.index.scanThis(theText) isGood, tBits, tPos = self.mainGui.project.index.scanThis(theText)
if not isGood: if not isGood:
return False return False
@@ -2223,10 +2221,8 @@ class GuiDocEditSearch(QFrame):
logger.debug("Create: GuiDocEditSearch") logger.debug("Create: GuiDocEditSearch")
self.docEditor = docEditor self.docEditor = docEditor
self.mainGui = docEditor.mainGui self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject
self.mainTheme = docEditor.mainTheme
self.repVisible = False self.repVisible = False
self.isCaseSense = CONFIG.searchCase self.isCaseSense = CONFIG.searchCase
@@ -2237,9 +2233,9 @@ class GuiDocEditSearch(QFrame):
self.doMatchCap = CONFIG.searchMatchCap self.doMatchCap = CONFIG.searchMatchCap
mPx = CONFIG.pxInt(6) mPx = CONFIG.pxInt(6)
tPx = int(0.8*self.mainTheme.fontPixelSize) tPx = int(0.8*CONFIG.theme.fontPixelSize)
self.boxFont = self.mainTheme.guiFont self.boxFont = CONFIG.theme.guiFont
self.boxFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) self.boxFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
@@ -2272,7 +2268,7 @@ class GuiDocEditSearch(QFrame):
self.resultLabel = QLabel("?/?") self.resultLabel = QLabel("?/?")
self.resultLabel.setFont(self.boxFont) self.resultLabel.setFont(self.boxFont)
self.resultLabel.setMinimumWidth(self.mainTheme.getTextWidth("?/?", self.boxFont)) self.resultLabel.setMinimumWidth(CONFIG.theme.getTextWidth("?/?", self.boxFont))
self.toggleCase = QAction(self.tr("Case Sensitive"), self) self.toggleCase = QAction(self.tr("Case Sensitive"), self)
self.toggleCase.setCheckable(True) self.toggleCase.setCheckable(True)
@@ -2378,15 +2374,15 @@ class GuiDocEditSearch(QFrame):
self.replaceBox.setPalette(qPalette) self.replaceBox.setPalette(qPalette)
# Set icons # Set icons
self.toggleCase.setIcon(self.mainTheme.getIcon("search_case")) self.toggleCase.setIcon(CONFIG.theme.getIcon("search_case"))
self.toggleWord.setIcon(self.mainTheme.getIcon("search_word")) self.toggleWord.setIcon(CONFIG.theme.getIcon("search_word"))
self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex")) self.toggleRegEx.setIcon(CONFIG.theme.getIcon("search_regex"))
self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop")) self.toggleLoop.setIcon(CONFIG.theme.getIcon("search_loop"))
self.toggleProject.setIcon(self.mainTheme.getIcon("search_project")) self.toggleProject.setIcon(CONFIG.theme.getIcon("search_project"))
self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve")) self.toggleMatchCap.setIcon(CONFIG.theme.getIcon("search_preserve"))
self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel")) self.cancelSearch.setIcon(CONFIG.theme.getIcon("search_cancel"))
self.searchButton.setIcon(self.mainTheme.getIcon("search")) self.searchButton.setIcon(CONFIG.theme.getIcon("search"))
self.replaceButton.setIcon(self.mainTheme.getIcon("search_replace")) self.replaceButton.setIcon(CONFIG.theme.getIcon("search_replace"))
# Set stylesheets # Set stylesheets
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}") self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
@@ -2478,7 +2474,7 @@ class GuiDocEditSearch(QFrame):
""" """
currRes = "?" if currRes is None else currRes currRes = "?" if currRes is None else currRes
resCount = "?" if resCount is None else "1000+" if resCount > 1000 else resCount resCount = "?" if resCount is None else "1000+" if resCount > 1000 else resCount
minWidth = self.mainTheme.getTextWidth(f"{resCount}//{resCount}", self.boxFont) minWidth = CONFIG.theme.getTextWidth(f"{resCount}//{resCount}", self.boxFont)
self.resultLabel.setText(f"{currRes}/{resCount}") self.resultLabel.setText(f"{currRes}/{resCount}")
self.resultLabel.setMinimumWidth(minWidth) self.resultLabel.setMinimumWidth(minWidth)
self.adjustSize() self.adjustSize()
@@ -2638,14 +2634,12 @@ class GuiDocEditHeader(QWidget):
logger.debug("Create: GuiDocEditHeader") logger.debug("Create: GuiDocEditHeader")
self.docEditor = docEditor self.docEditor = docEditor
self.mainGui = docEditor.mainGui self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject
self.mainTheme = docEditor.mainTheme
self._docHandle = None self._docHandle = None
fPx = int(0.9*self.mainTheme.fontPixelSize) fPx = int(0.9*CONFIG.theme.fontPixelSize)
hSp = CONFIG.pxInt(6) hSp = CONFIG.pxInt(6)
# Main Widget Settings # Main Widget Settings
@@ -2662,7 +2656,7 @@ class GuiDocEditHeader(QWidget):
self.theTitle.setFixedHeight(fPx) self.theTitle.setFixedHeight(fPx)
lblFont = self.theTitle.font() lblFont = self.theTitle.font()
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.theTitle.setFont(lblFont) self.theTitle.setFont(lblFont)
# Buttons # Buttons
@@ -2732,15 +2726,15 @@ class GuiDocEditHeader(QWidget):
def updateTheme(self): def updateTheme(self):
"""Update theme elements. """Update theme elements.
""" """
self.editButton.setIcon(self.mainTheme.getIcon("edit")) self.editButton.setIcon(CONFIG.theme.getIcon("edit"))
self.searchButton.setIcon(self.mainTheme.getIcon("search")) self.searchButton.setIcon(CONFIG.theme.getIcon("search"))
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise")) self.minmaxButton.setIcon(CONFIG.theme.getIcon("maximise"))
self.closeButton.setIcon(self.mainTheme.getIcon("close")) self.closeButton.setIcon(CONFIG.theme.getIcon("close"))
buttonStyle = ( buttonStyle = (
"QToolButton {{border: none; background: transparent;}} " "QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText) ).format(*CONFIG.theme.colText)
self.editButton.setStyleSheet(buttonStyle) self.editButton.setStyleSheet(buttonStyle)
self.searchButton.setStyleSheet(buttonStyle) self.searchButton.setStyleSheet(buttonStyle)
@@ -2756,9 +2750,9 @@ class GuiDocEditHeader(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
thePalette = QPalette() thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText)) thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(thePalette) self.setPalette(thePalette)
self.theTitle.setPalette(thePalette) self.theTitle.setPalette(thePalette)
@@ -2778,17 +2772,18 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.setVisible(False) self.minmaxButton.setVisible(False)
return True return True
pTree = self.mainGui.project.tree
if CONFIG.showFullPath: if CONFIG.showFullPath:
tTitle = [] tTitle = []
tTree = self.theProject.tree.getItemPath(tHandle) tTree = pTree.getItemPath(tHandle)
for aHandle in reversed(tTree): for aHandle in reversed(tTree):
nwItem = self.theProject.tree[aHandle] nwItem = pTree[aHandle]
if nwItem is not None: if nwItem is not None:
tTitle.append(nwItem.itemName) tTitle.append(nwItem.itemName)
sSep = " %s " % nwUnicode.U_RSAQUO sSep = " %s " % nwUnicode.U_RSAQUO
self.theTitle.setText(sSep.join(tTitle)) self.theTitle.setText(sSep.join(tTitle))
else: else:
nwItem = self.theProject.tree[tHandle] nwItem = pTree[tHandle]
if nwItem is None: if nwItem is None:
return False return False
self.theTitle.setText(nwItem.itemName) self.theTitle.setText(nwItem.itemName)
@@ -2806,9 +2801,9 @@ class GuiDocEditHeader(QWidget):
toggleFocusMode function and should not be activated directly. toggleFocusMode function and should not be activated directly.
""" """
if self.mainGui.isFocusMode: if self.mainGui.isFocusMode:
self.minmaxButton.setIcon(self.mainTheme.getIcon("minimise")) self.minmaxButton.setIcon(CONFIG.theme.getIcon("minimise"))
else: else:
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise")) self.minmaxButton.setIcon(CONFIG.theme.getIcon("maximise"))
return return
## ##
@@ -2873,23 +2868,21 @@ class GuiDocEditFooter(QWidget):
logger.debug("Create: GuiDocEditFooter") logger.debug("Create: GuiDocEditFooter")
self.docEditor = docEditor self.docEditor = docEditor
self.mainGui = docEditor.mainGui self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject
self.mainTheme = docEditor.mainTheme
self._theItem = None self._theItem = None
self._docHandle = None self._docHandle = None
self._docSelection = False self._docSelection = False
self.sPx = int(round(0.9*self.mainTheme.baseIconSize)) self.sPx = int(round(0.9*CONFIG.theme.baseIconSize))
fPx = int(0.9*self.mainTheme.fontPixelSize) fPx = int(0.9*CONFIG.theme.fontPixelSize)
bSp = CONFIG.pxInt(4) bSp = CONFIG.pxInt(4)
hSp = CONFIG.pxInt(6) hSp = CONFIG.pxInt(6)
lblFont = self.font() lblFont = self.font()
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
# Main Widget Settings # Main Widget Settings
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
@@ -2976,8 +2969,8 @@ class GuiDocEditFooter(QWidget):
def updateTheme(self): def updateTheme(self):
"""Update theme elements. """Update theme elements.
""" """
self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx))) self.linesIcon.setPixmap(CONFIG.theme.getPixmap("status_lines", (self.sPx, self.sPx)))
self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx))) self.wordsIcon.setPixmap(CONFIG.theme.getPixmap("status_stats", (self.sPx, self.sPx)))
self.matchColours() self.matchColours()
@@ -2988,9 +2981,9 @@ class GuiDocEditFooter(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
thePalette = QPalette() thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText)) thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(thePalette) self.setPalette(thePalette)
self.statusText.setPalette(thePalette) self.statusText.setPalette(thePalette)
@@ -3007,7 +3000,7 @@ class GuiDocEditFooter(QWidget):
logger.debug("No handle set, so clearing the editor footer") logger.debug("No handle set, so clearing the editor footer")
self._theItem = None self._theItem = None
else: else:
self._theItem = self.theProject.tree[self._docHandle] self._theItem = self.mainGui.project.tree[self._docHandle]
self.setHasSelection(False) self.setHasSelection(False)
self.updateInfo() self.updateInfo()
+17 -19
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Syntax Highlighter novelWriter GUI Syntax Highlighter
==================================== ====================================
Class for the main document editor syntax highlighter
File History: File History:
Created: 2019-04-06 [0.0.1] Created: 2019-04-06 [0.0.1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -54,8 +54,6 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.theDoc = theDoc self.theDoc = theDoc
self.spEnchant = spEnchant self.spEnchant = spEnchant
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.theHandle = None self.theHandle = None
self.spellCheck = False self.spellCheck = False
self.spellRx = None self.spellRx = None
@@ -87,24 +85,24 @@ class GuiDocHighlighter(QSyntaxHighlighter):
""" """
logger.debug("Setting up highlighting rules") logger.debug("Setting up highlighting rules")
self.colHead = QColor(*self.mainTheme.colHead) self.colHead = QColor(*CONFIG.theme.colHead)
self.colHeadH = QColor(*self.mainTheme.colHeadH) self.colHeadH = QColor(*CONFIG.theme.colHeadH)
self.colDialN = QColor(*self.mainTheme.colDialN) self.colDialN = QColor(*CONFIG.theme.colDialN)
self.colDialD = QColor(*self.mainTheme.colDialD) self.colDialD = QColor(*CONFIG.theme.colDialD)
self.colDialS = QColor(*self.mainTheme.colDialS) self.colDialS = QColor(*CONFIG.theme.colDialS)
self.colHidden = QColor(*self.mainTheme.colHidden) self.colHidden = QColor(*CONFIG.theme.colHidden)
self.colKey = QColor(*self.mainTheme.colKey) self.colKey = QColor(*CONFIG.theme.colKey)
self.colVal = QColor(*self.mainTheme.colVal) self.colVal = QColor(*CONFIG.theme.colVal)
self.colSpell = QColor(*self.mainTheme.colSpell) self.colSpell = QColor(*CONFIG.theme.colSpell)
self.colError = QColor(*self.mainTheme.colError) self.colError = QColor(*CONFIG.theme.colError)
self.colRepTag = QColor(*self.mainTheme.colRepTag) self.colRepTag = QColor(*CONFIG.theme.colRepTag)
self.colMod = QColor(*self.mainTheme.colMod) self.colMod = QColor(*CONFIG.theme.colMod)
self.colBreak = QColor(*self.mainTheme.colEmph) self.colBreak = QColor(*CONFIG.theme.colEmph)
self.colBreak.setAlpha(64) self.colBreak.setAlpha(64)
self.colEmph = None self.colEmph = None
if CONFIG.highlightEmph: if CONFIG.highlightEmph:
self.colEmph = QColor(*self.mainTheme.colEmph) self.colEmph = QColor(*CONFIG.theme.colEmph)
self.hStyles = { self.hStyles = {
"header1": self._makeFormat(self.colHead, "bold", 1.8), "header1": self._makeFormat(self.colHead, "bold", 1.8),
@@ -287,8 +285,8 @@ class GuiDocHighlighter(QSyntaxHighlighter):
if theText.startswith("@"): # Keywords and commands if theText.startswith("@"): # Keywords and commands
self.setCurrentBlockState(self.BLOCK_META) self.setCurrentBlockState(self.BLOCK_META)
pIndex = self.theProject.index pIndex = self.mainGui.project.index
tItem = self.mainGui.theProject.tree[self.theHandle] tItem = self.mainGui.project.tree[self.theHandle]
isValid, theBits, thePos = pIndex.scanThis(theText) isValid, theBits, thePos = pIndex.scanThis(theText)
isGood = pIndex.checkThese(theBits, tItem) isGood = pIndex.checkThese(theBits, tItem)
if isValid: if isValid:
+63 -71
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Document Viewer novelWriter GUI Document Viewer
================================= =================================
GUI classes for the main document viewer
File History: File History:
Created: 2019-05-10 [0.0.1] GuiDocViewer Created: 2019-05-10 [0.0.1] GuiDocViewer
@@ -26,6 +25,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -59,9 +59,7 @@ class GuiDocViewer(QTextBrowser):
logger.debug("Create: GuiDocViewer") logger.debug("Create: GuiDocViewer")
# Class Variables # Class Variables
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
# Internal Variables # Internal Variables
self._docHandle = None self._docHandle = None
@@ -121,14 +119,14 @@ class GuiDocViewer(QTextBrowser):
# Set the widget colours to match syntax theme # Set the widget colours to match syntax theme
mainPalette = self.palette() mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) mainPalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack)) mainPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) mainPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(mainPalette) self.setPalette(mainPalette)
docPalette = self.viewport().palette() docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack)) docPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) docPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.viewport().setPalette(docPalette) self.viewport().setPalette(docPalette)
self.docHeader.matchColours() self.docHeader.matchColours()
@@ -164,7 +162,7 @@ class GuiDocViewer(QTextBrowser):
def loadText(self, tHandle, updateHistory=True): def loadText(self, tHandle, updateHistory=True):
"""Load text into the viewer from an item handle. """Load text into the viewer from an item handle.
""" """
if not self.theProject.tree.checkType(tHandle, nwItemType.FILE): if not self.mainGui.project.tree.checkType(tHandle, nwItemType.FILE):
logger.warning("Item not found") logger.warning("Item not found")
return False return False
@@ -172,7 +170,7 @@ class GuiDocViewer(QTextBrowser):
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
sPos = self.verticalScrollBar().value() sPos = self.verticalScrollBar().value()
aDoc = ToHtml(self.theProject) aDoc = ToHtml(self.mainGui.project)
aDoc.setPreview(CONFIG.viewComments, CONFIG.viewSynopsis) aDoc.setPreview(CONFIG.viewComments, CONFIG.viewSynopsis)
aDoc.setLinkHeaders(True) aDoc.setLinkHeaders(True)
@@ -212,7 +210,7 @@ class GuiDocViewer(QTextBrowser):
self.verticalScrollBar().setValue(sPos) self.verticalScrollBar().setValue(sPos)
self._docHandle = tHandle self._docHandle = tHandle
self.theProject._data.setLastHandle(tHandle, "viewer") self.mainGui.project.data.setLastHandle(tHandle, "viewer")
self.docHeader.setTitleFromHandle(self._docHandle) self.docHeader.setTitleFromHandle(self._docHandle)
self.updateDocMargins() self.updateDocMargins()
@@ -508,27 +506,27 @@ class GuiDocViewer(QTextBrowser):
" text-align: center;" " text-align: center;"
"}}\n" "}}\n"
).format( ).format(
tColR=self.mainTheme.colText[0], tColR=CONFIG.theme.colText[0],
tColG=self.mainTheme.colText[1], tColG=CONFIG.theme.colText[1],
tColB=self.mainTheme.colText[2], tColB=CONFIG.theme.colText[2],
hColR=self.mainTheme.colHead[0], hColR=CONFIG.theme.colHead[0],
hColG=self.mainTheme.colHead[1], hColG=CONFIG.theme.colHead[1],
hColB=self.mainTheme.colHead[2], hColB=CONFIG.theme.colHead[2],
aColR=self.mainTheme.colVal[0], aColR=CONFIG.theme.colVal[0],
aColG=self.mainTheme.colVal[1], aColG=CONFIG.theme.colVal[1],
aColB=self.mainTheme.colVal[2], aColB=CONFIG.theme.colVal[2],
eColR=self.mainTheme.colEmph[0], eColR=CONFIG.theme.colEmph[0],
eColG=self.mainTheme.colEmph[1], eColG=CONFIG.theme.colEmph[1],
eColB=self.mainTheme.colEmph[2], eColB=CONFIG.theme.colEmph[2],
kColR=self.mainTheme.colKey[0], kColR=CONFIG.theme.colKey[0],
kColG=self.mainTheme.colKey[1], kColG=CONFIG.theme.colKey[1],
kColB=self.mainTheme.colKey[2], kColB=CONFIG.theme.colKey[2],
cColR=self.mainTheme.colHidden[0], cColR=CONFIG.theme.colHidden[0],
cColG=self.mainTheme.colHidden[1], cColG=CONFIG.theme.colHidden[1],
cColB=self.mainTheme.colHidden[2], cColB=CONFIG.theme.colHidden[2],
mColR=self.mainTheme.colMod[0], mColR=CONFIG.theme.colMod[0],
mColG=self.mainTheme.colMod[1], mColG=CONFIG.theme.colMod[1],
mColB=self.mainTheme.colMod[2], mColB=CONFIG.theme.colMod[2],
) )
self.document().setDefaultStyleSheet(styleSheet) self.document().setDefaultStyleSheet(styleSheet)
@@ -681,15 +679,13 @@ class GuiDocViewHeader(QWidget):
logger.debug("Create: GuiDocViewHeader") logger.debug("Create: GuiDocViewHeader")
self.docViewer = docViewer self.docViewer = docViewer
self.mainGui = docViewer.mainGui self.mainGui = docViewer.mainGui
self.theProject = docViewer.theProject
self.mainTheme = docViewer.mainTheme
# Internal Variables # Internal Variables
self._docHandle = None self._docHandle = None
fPx = int(0.9*self.mainTheme.fontPixelSize) fPx = int(0.9*CONFIG.theme.fontPixelSize)
hSp = CONFIG.pxInt(6) hSp = CONFIG.pxInt(6)
# Main Widget Settings # Main Widget Settings
@@ -706,7 +702,7 @@ class GuiDocViewHeader(QWidget):
self.theTitle.setFixedHeight(fPx) self.theTitle.setFixedHeight(fPx)
lblFont = self.theTitle.font() lblFont = self.theTitle.font()
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.theTitle.setFont(lblFont) self.theTitle.setFont(lblFont)
# Buttons # Buttons
@@ -777,15 +773,15 @@ class GuiDocViewHeader(QWidget):
def updateTheme(self): def updateTheme(self):
"""Update theme elements. """Update theme elements.
""" """
self.backButton.setIcon(self.mainTheme.getIcon("backward")) self.backButton.setIcon(CONFIG.theme.getIcon("backward"))
self.forwardButton.setIcon(self.mainTheme.getIcon("forward")) self.forwardButton.setIcon(CONFIG.theme.getIcon("forward"))
self.refreshButton.setIcon(self.mainTheme.getIcon("refresh")) self.refreshButton.setIcon(CONFIG.theme.getIcon("refresh"))
self.closeButton.setIcon(self.mainTheme.getIcon("close")) self.closeButton.setIcon(CONFIG.theme.getIcon("close"))
buttonStyle = ( buttonStyle = (
"QToolButton {{border: none; background: transparent;}} " "QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText) ).format(*CONFIG.theme.colText)
self.backButton.setStyleSheet(buttonStyle) self.backButton.setStyleSheet(buttonStyle)
self.forwardButton.setStyleSheet(buttonStyle) self.forwardButton.setStyleSheet(buttonStyle)
@@ -801,9 +797,9 @@ class GuiDocViewHeader(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
thePalette = QPalette() thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText)) thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(thePalette) self.setPalette(thePalette)
self.theTitle.setPalette(thePalette) self.theTitle.setPalette(thePalette)
@@ -823,17 +819,18 @@ class GuiDocViewHeader(QWidget):
self.refreshButton.setVisible(False) self.refreshButton.setVisible(False)
return True return True
pTree = self.mainGui.project.tree
if CONFIG.showFullPath: if CONFIG.showFullPath:
tTitle = [] tTitle = []
tTree = self.theProject.tree.getItemPath(tHandle) tTree = pTree.getItemPath(tHandle)
for aHandle in reversed(tTree): for aHandle in reversed(tTree):
nwItem = self.theProject.tree[aHandle] nwItem = pTree[aHandle]
if nwItem is not None: if nwItem is not None:
tTitle.append(nwItem.itemName) tTitle.append(nwItem.itemName)
sSep = " %s " % nwUnicode.U_RSAQUO sSep = " %s " % nwUnicode.U_RSAQUO
self.theTitle.setText(sSep.join(tTitle)) self.theTitle.setText(sSep.join(tTitle))
else: else:
nwItem = self.theProject.tree[tHandle] nwItem = pTree[tHandle]
if nwItem is None: if nwItem is None:
return False return False
self.theTitle.setText(nwItem.itemName) self.theTitle.setText(nwItem.itemName)
@@ -900,13 +897,12 @@ class GuiDocViewFooter(QWidget):
self.docViewer = docViewer self.docViewer = docViewer
self.mainGui = docViewer.mainGui self.mainGui = docViewer.mainGui
self.mainTheme = docViewer.mainTheme
self.viewMeta = docViewer.mainGui.viewMeta self.viewMeta = docViewer.mainGui.viewMeta
# Internal Variables # Internal Variables
self._docHandle = None self._docHandle = None
fPx = int(0.9*self.mainTheme.fontPixelSize) fPx = int(0.9*CONFIG.theme.fontPixelSize)
bSp = CONFIG.pxInt(2) bSp = CONFIG.pxInt(2)
hSp = CONFIG.pxInt(8) hSp = CONFIG.pxInt(8)
@@ -991,7 +987,7 @@ class GuiDocViewFooter(QWidget):
self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop)
lblFont = self.font() lblFont = self.font()
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.lblRefs.setFont(lblFont) self.lblRefs.setFont(lblFont)
self.lblSticky.setFont(lblFont) self.lblSticky.setFont(lblFont)
self.lblComments.setFont(lblFont) self.lblComments.setFont(lblFont)
@@ -1036,21 +1032,21 @@ class GuiDocViewFooter(QWidget):
""" """
# Icons # Icons
fPx = int(0.9*self.mainTheme.fontPixelSize) fPx = int(0.9*CONFIG.theme.fontPixelSize)
stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx)) stickyOn = CONFIG.theme.getPixmap("sticky-on", (fPx, fPx))
stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx)) stickyOff = CONFIG.theme.getPixmap("sticky-off", (fPx, fPx))
stickyIcon = QIcon() stickyIcon = QIcon()
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On) stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off) stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx)) bulletOn = CONFIG.theme.getPixmap("bullet-on", (fPx, fPx))
bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx)) bulletOff = CONFIG.theme.getPixmap("bullet-off", (fPx, fPx))
bulletIcon = QIcon() bulletIcon = QIcon()
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On) bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off) bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
self.showHide.setIcon(self.mainTheme.getIcon("reference")) self.showHide.setIcon(CONFIG.theme.getIcon("reference"))
self.stickyRefs.setIcon(stickyIcon) self.stickyRefs.setIcon(stickyIcon)
self.showComments.setIcon(bulletIcon) self.showComments.setIcon(bulletIcon)
self.showSynopsis.setIcon(bulletIcon) self.showSynopsis.setIcon(bulletIcon)
@@ -1060,7 +1056,7 @@ class GuiDocViewFooter(QWidget):
buttonStyle = ( buttonStyle = (
"QToolButton {{border: none; background: transparent;}} " "QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText) ).format(*CONFIG.theme.colText)
self.showHide.setStyleSheet(buttonStyle) self.showHide.setStyleSheet(buttonStyle)
self.stickyRefs.setStyleSheet(buttonStyle) self.stickyRefs.setStyleSheet(buttonStyle)
@@ -1076,9 +1072,9 @@ class GuiDocViewFooter(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
thePalette = QPalette() thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack)) thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText)) thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText)) thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(thePalette) self.setPalette(thePalette)
self.lblRefs.setPalette(thePalette) self.lblRefs.setPalette(thePalette)
@@ -1141,9 +1137,7 @@ class GuiDocViewDetails(QScrollArea):
logger.debug("Create: GuiDocViewDetails") logger.debug("Create: GuiDocViewDetails")
self.mainGui = mainGui self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainTheme = mainGui.mainTheme
self.refList = QLabel("") self.refList = QLabel("")
self.refList.setWordWrap(True) self.refList.setWordWrap(True)
@@ -1151,9 +1145,7 @@ class GuiDocViewDetails(QScrollArea):
self.refList.setScaledContents(True) self.refList.setScaledContents(True)
self.refList.linkActivated.connect(self._linkClicked) self.refList.linkActivated.connect(self._linkClicked)
self.linkStyle = "style='color: rgb({0},{1},{2})'".format( self.linkStyle = "style='color: rgb({0},{1},{2})'".format(*CONFIG.theme.colLink)
*self.mainTheme.colLink
)
# Assemble # Assemble
self.outerWidget = QWidget() self.outerWidget = QWidget()
@@ -1180,10 +1172,10 @@ class GuiDocViewDetails(QScrollArea):
if self.mainGui.docViewer.stickyRef: if self.mainGui.docViewer.stickyRef:
return return
theRefs = self.theProject.index.getBackReferenceList(tHandle) theRefs = self.mainGui.project.index.getBackReferenceList(tHandle)
theList = [] theList = []
for tHandle in theRefs: for tHandle in theRefs:
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is not None: if tItem is not None:
theList.append("<a href='%s#%s' %s>%s</a>" % ( theList.append("<a href='%s#%s' %s>%s</a>" % (
tHandle, theRefs[tHandle], self.linkStyle, tItem.itemName tHandle, theRefs[tHandle], self.linkStyle, tItem.itemName
+12 -14
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Item Details Panel novelWriter GUI Item Details Panel
==================================== ====================================
GUI class for the project tree item details panel
File History: File History:
Created: 2019-04-24 [0.0.1] Created: 2019-04-24 [0.0.1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -42,9 +42,7 @@ class GuiItemDetails(QWidget):
logger.debug("Create: GuiItemDetails") logger.debug("Create: GuiItemDetails")
self.mainGui = mainGui self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainTheme = mainGui.mainTheme
# Internal Variables # Internal Variables
self._itemHandle = None self._itemHandle = None
@@ -53,7 +51,7 @@ class GuiItemDetails(QWidget):
hSp = CONFIG.pxInt(6) hSp = CONFIG.pxInt(6)
vSp = CONFIG.pxInt(1) vSp = CONFIG.pxInt(1)
mPx = CONFIG.pxInt(6) mPx = CONFIG.pxInt(6)
fPt = self.mainTheme.fontPointSize fPt = CONFIG.theme.fontPointSize
fntLabel = QFont() fntLabel = QFont()
fntLabel.setBold(True) fntLabel.setBold(True)
@@ -178,8 +176,8 @@ class GuiItemDetails(QWidget):
self.updateTheme() self.updateTheme()
# Make sure the columns for flags and counts don't resize too often # Make sure the columns for flags and counts don't resize too often
flagWidth = self.mainTheme.getTextWidth("Mm", fntValue) flagWidth = CONFIG.theme.getTextWidth("Mm", fntValue)
countWidth = self.mainTheme.getTextWidth("99,999", fntValue) countWidth = CONFIG.theme.getTextWidth("99,999", fntValue)
self.mainBox.setColumnMinimumWidth(1, flagWidth) self.mainBox.setColumnMinimumWidth(1, flagWidth)
self.mainBox.setColumnMinimumWidth(4, countWidth) self.mainBox.setColumnMinimumWidth(4, countWidth)
@@ -235,13 +233,13 @@ class GuiItemDetails(QWidget):
self.clearDetails() self.clearDetails()
return return
nwItem = self.theProject.tree[tHandle] nwItem = self.mainGui.project.tree[tHandle]
if nwItem is None: if nwItem is None:
self.clearDetails() self.clearDetails()
return return
self._itemHandle = tHandle self._itemHandle = tHandle
iPx = int(round(0.8*self.mainTheme.baseIconSize)) iPx = int(round(0.8*CONFIG.theme.baseIconSize))
# Label # Label
# ===== # =====
@@ -252,11 +250,11 @@ class GuiItemDetails(QWidget):
if nwItem.isFileType(): if nwItem.isFileType():
if nwItem.isActive: if nwItem.isActive:
self.labelIcon.setPixmap(self.mainTheme.getPixmap("checked", (iPx, iPx))) self.labelIcon.setPixmap(CONFIG.theme.getPixmap("checked", (iPx, iPx)))
else: else:
self.labelIcon.setPixmap(self.mainTheme.getPixmap("unchecked", (iPx, iPx))) self.labelIcon.setPixmap(CONFIG.theme.getPixmap("unchecked", (iPx, iPx)))
else: else:
self.labelIcon.setPixmap(self.mainTheme.getPixmap("noncheckable", (iPx, iPx))) self.labelIcon.setPixmap(CONFIG.theme.getPixmap("noncheckable", (iPx, iPx)))
self.labelData.setText(theLabel) self.labelData.setText(theLabel)
@@ -270,14 +268,14 @@ class GuiItemDetails(QWidget):
# Class # Class
# ===== # =====
classIcon = self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]) classIcon = CONFIG.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass])
self.classIcon.setPixmap(classIcon.pixmap(iPx, iPx)) self.classIcon.setPixmap(classIcon.pixmap(iPx, iPx))
self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass])) self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass]))
# Layout # Layout
# ====== # ======
usageIcon = self.mainTheme.getItemIcon( usageIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
) )
self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx)) self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx))
+14 -15
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Main Menu novelWriter GUI Main Menu
=========================== ===========================
GUI class for the main window menu
File History: File History:
Created: 2019-04-27 [0.0.1] Created: 2019-04-27 [0.0.1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -51,8 +51,7 @@ class GuiMainMenu(QMenuBar):
logger.debug("Create: GuiMainMenu") logger.debug("Create: GuiMainMenu")
self.mainGui = mainGui self.mainGui = mainGui
self.theProject = mainGui.theProject
# Build Menu # Build Menu
self._buildProjectMenu() self._buildProjectMenu()
@@ -380,10 +379,10 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Insert menu. """Assemble the Insert menu.
""" """
# Insert # Insert
self.insertMenu = self.addMenu(self.tr("&Insert")) self.insMenu = self.addMenu(self.tr("&Insert"))
# Insert > Dashes and Dots # Insert > Dashes and Dots
self.mInsDashes = self.insertMenu.addMenu(self.tr("Dashes")) self.mInsDashes = self.insMenu.addMenu(self.tr("Dashes"))
# Insert > Short Dash # Insert > Short Dash
self.aInsENDash = QAction(self.tr("Short Dash"), self) self.aInsENDash = QAction(self.tr("Short Dash"), self)
@@ -410,7 +409,7 @@ class GuiMainMenu(QMenuBar):
self.mInsDashes.addAction(self.aInsFigDash) self.mInsDashes.addAction(self.aInsFigDash)
# Insert > Quote Marks # Insert > Quote Marks
self.mInsQuotes = self.insertMenu.addMenu(self.tr("Quote Marks")) self.mInsQuotes = self.insMenu.addMenu(self.tr("Quote Marks"))
# Insert > Left Single Quote # Insert > Left Single Quote
self.aInsQuoteLS = QAction(self.tr("Left Single Quote"), self) self.aInsQuoteLS = QAction(self.tr("Left Single Quote"), self)
@@ -443,7 +442,7 @@ class GuiMainMenu(QMenuBar):
self.mInsQuotes.addAction(self.aInsMSApos) self.mInsQuotes.addAction(self.aInsMSApos)
# Insert > Symbols # Insert > Symbols
self.mInsPunct = self.insertMenu.addMenu(self.tr("General Punctuation")) self.mInsPunct = self.insMenu.addMenu(self.tr("General Punctuation"))
# Insert > Ellipsis # Insert > Ellipsis
self.aInsEllipsis = QAction(self.tr("Ellipsis"), self) self.aInsEllipsis = QAction(self.tr("Ellipsis"), self)
@@ -464,7 +463,7 @@ class GuiMainMenu(QMenuBar):
self.mInsPunct.addAction(self.aInsDPrime) self.mInsPunct.addAction(self.aInsDPrime)
# Insert > White Spaces # Insert > White Spaces
self.mInsSpace = self.insertMenu.addMenu(self.tr("White Spaces")) self.mInsSpace = self.insMenu.addMenu(self.tr("White Spaces"))
# Insert > Non-Breaking Space # Insert > Non-Breaking Space
self.aInsNBSpace = QAction(self.tr("Non-Breaking Space"), self) self.aInsNBSpace = QAction(self.tr("Non-Breaking Space"), self)
@@ -485,7 +484,7 @@ class GuiMainMenu(QMenuBar):
self.mInsSpace.addAction(self.aInsThinNBSpace) self.mInsSpace.addAction(self.aInsThinNBSpace)
# Insert > Symbols # Insert > Symbols
self.mInsSymbol = self.insertMenu.addMenu(self.tr("Other Symbols")) self.mInsSymbol = self.insMenu.addMenu(self.tr("Other Symbols"))
# Insert > List Bullet # Insert > List Bullet
self.aInsBullet = QAction(self.tr("List Bullet"), self) self.aInsBullet = QAction(self.tr("List Bullet"), self)
@@ -536,7 +535,7 @@ class GuiMainMenu(QMenuBar):
self.mInsSymbol.addAction(self.aInsDivide) self.mInsSymbol.addAction(self.aInsDivide)
# Insert > Tags and References # Insert > Tags and References
self.mInsKeywords = self.insertMenu.addMenu(self.tr("Tags and References")) self.mInsKeywords = self.insMenu.addMenu(self.tr("Tags and References"))
self.mInsKWItems = {} self.mInsKWItems = {}
self.mInsKWItems[nwKeyWords.TAG_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, G") self.mInsKWItems[nwKeyWords.TAG_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, G")
self.mInsKWItems[nwKeyWords.POV_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, V") self.mInsKWItems[nwKeyWords.POV_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, V")
@@ -557,7 +556,7 @@ class GuiMainMenu(QMenuBar):
self.mInsKeywords.addAction(self.mInsKWItems[keyWord][0]) self.mInsKeywords.addAction(self.mInsKWItems[keyWord][0])
# Insert > Special Comments # Insert > Special Comments
self.mInsComments = self.insertMenu.addMenu(self.tr("Special Comments")) self.mInsComments = self.insMenu.addMenu(self.tr("Special Comments"))
# Insert > Synopsis Comment # Insert > Synopsis Comment
self.aInsSynopsis = QAction(self.tr("Synopsis Comment"), self) self.aInsSynopsis = QAction(self.tr("Synopsis Comment"), self)
@@ -566,7 +565,7 @@ class GuiMainMenu(QMenuBar):
self.mInsComments.addAction(self.aInsSynopsis) self.mInsComments.addAction(self.aInsSynopsis)
# Insert > Symbols # Insert > Symbols
self.mInsBreaks = self.insertMenu.addMenu(self.tr("Page Break and Space")) self.mInsBreaks = self.insMenu.addMenu(self.tr("Page Break and Space"))
# Insert > New Page # Insert > New Page
self.aInsNewPage = QAction(self.tr("Page Break"), self) self.aInsNewPage = QAction(self.tr("Page Break"), self)
@@ -586,7 +585,7 @@ class GuiMainMenu(QMenuBar):
# Insert > Placeholder Text # Insert > Placeholder Text
self.aLipsumText = QAction(self.tr("Placeholder Text"), self) self.aLipsumText = QAction(self.tr("Placeholder Text"), self)
self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog()) self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog())
self.insertMenu.addAction(self.aLipsumText) self.insMenu.addAction(self.aLipsumText)
return return
@@ -796,7 +795,7 @@ class GuiMainMenu(QMenuBar):
# Tools > Check Spelling # Tools > Check Spelling
self.aSpellCheck = QAction(self.tr("Check Spelling"), self) self.aSpellCheck = QAction(self.tr("Check Spelling"), self)
self.aSpellCheck.setCheckable(True) self.aSpellCheck.setCheckable(True)
self.aSpellCheck.setChecked(self.theProject.data.spellCheck) self.aSpellCheck.setChecked(self.mainGui.project.data.spellCheck)
self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled! self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled!
self.aSpellCheck.setShortcut("Ctrl+F7") self.aSpellCheck.setShortcut("Ctrl+F7")
self.toolsMenu.addAction(self.aSpellCheck) self.toolsMenu.addAction(self.aSpellCheck)
@@ -826,7 +825,7 @@ class GuiMainMenu(QMenuBar):
# Tools > Backup Project # Tools > Backup Project
self.aBackupProject = QAction(self.tr("Backup Project"), self) self.aBackupProject = QAction(self.tr("Backup Project"), self)
self.aBackupProject.triggered.connect(lambda: self.theProject.backupProject(True)) self.aBackupProject.triggered.connect(lambda: self.mainGui.project.backupProject(True))
self.toolsMenu.addAction(self.aBackupProject) self.toolsMenu.addAction(self.aBackupProject)
# Tools > Build Manuscript # Tools > Build Manuscript
+34 -38
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Novel Tree novelWriter GUI Novel Tree
============================ ============================
GUI class for the main window novel tree
File History: File History:
Created: 2020-12-20 [1.1rc1] GuiNovelTree Created: 2020-12-20 [1.1rc1] GuiNovelTree
@@ -24,6 +23,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -42,7 +42,7 @@ from novelwriter import CONFIG
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
from novelwriter.common import minmax from novelwriter.common import minmax
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
from novelwriter.gui.components import NovelSelector from novelwriter.extensions.novelselector import NovelSelector
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -66,8 +66,7 @@ class GuiNovelView(QWidget):
def __init__(self, mainGui): def __init__(self, mainGui):
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
self.mainGui = mainGui self.mainGui = mainGui
self.theProject = mainGui.theProject
# Build GUI # Build GUI
self.novelTree = GuiNovelTree(self) self.novelTree = GuiNovelTree(self)
@@ -118,16 +117,16 @@ class GuiNovelView(QWidget):
def openProjectTasks(self): def openProjectTasks(self):
"""Run open project tasks. """Run open project tasks.
""" """
lastNovel = self.theProject.data.getLastHandle("novelTree") lastNovel = self.mainGui.project.data.getLastHandle("novelTree")
if lastNovel not in self.theProject.tree: if lastNovel not in self.mainGui.project.tree:
lastNovel = self.theProject.tree.findRoot(nwItemClass.NOVEL) lastNovel = self.mainGui.project.tree.findRoot(nwItemClass.NOVEL)
logger.debug("Setting novel tree to root item '%s'", lastNovel) logger.debug("Setting novel tree to root item '%s'", lastNovel)
lastCol = self.theProject.options.getEnum( lastCol = self.mainGui.project.options.getEnum(
"GuiNovelView", "lastCol", NovelTreeColumn, NovelTreeColumn.HIDDEN "GuiNovelView", "lastCol", NovelTreeColumn, NovelTreeColumn.HIDDEN
) )
lastColSize = self.theProject.options.getInt( lastColSize = self.mainGui.project.options.getInt(
"GuiNovelView", "lastColSize", 25 "GuiNovelView", "lastColSize", 25
) )
@@ -147,8 +146,9 @@ class GuiNovelView(QWidget):
""" """
lastColType = self.novelTree.lastColType lastColType = self.novelTree.lastColType
lastColSize = self.novelTree.lastColSize lastColSize = self.novelTree.lastColSize
self.theProject.options.setValue("GuiNovelView", "lastCol", lastColType) pOptions = self.mainGui.project.options
self.theProject.options.setValue("GuiNovelView", "lastColSize", lastColSize) pOptions.setValue("GuiNovelView", "lastCol", lastColType)
pOptions.setValue("GuiNovelView", "lastColSize", lastColSize)
return return
def setTreeFocus(self): def setTreeFocus(self):
@@ -170,7 +170,7 @@ class GuiNovelView(QWidget):
def refreshTree(self): def refreshTree(self):
"""Refresh the current tree. """Refresh the current tree.
""" """
self.novelTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("novelTree")) self.novelTree.refreshTree(rootHandle=self.mainGui.project.data.getLastHandle("novelTree"))
return return
@pyqtSlot(str) @pyqtSlot(str)
@@ -198,12 +198,10 @@ class GuiNovelToolBar(QWidget):
logger.debug("Create: GuiNovelToolBar") logger.debug("Create: GuiNovelToolBar")
self.novelView = novelView self.novelView = novelView
self.mainGui = novelView.mainGui self.mainGui = novelView.mainGui
self.theProject = novelView.mainGui.theProject
self.mainTheme = novelView.mainGui.mainTheme
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
mPx = CONFIG.pxInt(2) mPx = CONFIG.pxInt(2)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
@@ -213,7 +211,7 @@ class GuiNovelToolBar(QWidget):
selFont = self.font() selFont = self.font()
selFont.setWeight(QFont.Bold) selFont.setWeight(QFont.Bold)
self.novelPrefix = self.tr("Outline of {0}") self.novelPrefix = self.tr("Outline of {0}")
self.novelValue = NovelSelector(self, self.theProject, self.mainGui) self.novelValue = NovelSelector(self, self.mainGui)
self.novelValue.setFont(selFont) self.novelValue.setFont(selFont)
self.novelValue.setMinimumWidth(CONFIG.pxInt(150)) self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
self.novelValue.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.novelValue.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -276,9 +274,9 @@ class GuiNovelToolBar(QWidget):
"""Update theme elements. """Update theme elements.
""" """
# Icons # Icons
self.tbNovel.setIcon(self.mainTheme.getIcon("cls_novel")) self.tbNovel.setIcon(CONFIG.theme.getIcon("cls_novel"))
self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh")) self.tbRefresh.setIcon(CONFIG.theme.getIcon("refresh"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu")) self.tbMore.setIcon(CONFIG.theme.getIcon("menu"))
qPalette = self.palette() qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base()) qPalette.setBrush(QPalette.Window, qPalette.base())
@@ -347,7 +345,7 @@ class GuiNovelToolBar(QWidget):
def _refreshNovelTree(self): def _refreshNovelTree(self):
"""Rebuild the current tree. """Rebuild the current tree.
""" """
rootHandle = self.theProject.data.getLastHandle("novelTree") rootHandle = self.mainGui.project.data.getLastHandle("novelTree")
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True) self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
return return
@@ -399,10 +397,8 @@ class GuiNovelTree(QTreeWidget):
logger.debug("Create: GuiNovelTree") logger.debug("Create: GuiNovelTree")
self.novelView = novelView self.novelView = novelView
self.mainGui = novelView.mainGui self.mainGui = novelView.mainGui
self.mainTheme = novelView.mainGui.mainTheme
self.theProject = novelView.mainGui.theProject
# Internal Variables # Internal Variables
self._treeMap = {} self._treeMap = {}
@@ -419,7 +415,7 @@ class GuiNovelTree(QTreeWidget):
# Build GUI # Build GUI
# ========= # =========
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
cMg = CONFIG.pxInt(6) cMg = CONFIG.pxInt(6)
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
@@ -485,8 +481,8 @@ class GuiNovelTree(QTreeWidget):
def updateTheme(self): def updateTheme(self):
"""Update theme elements. """Update theme elements.
""" """
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx) self._pMore = CONFIG.theme.loadDecoration("deco_doc_more", pxH=iPx)
return return
## ##
@@ -518,10 +514,10 @@ class GuiNovelTree(QTreeWidget):
""" """
logger.debug("Requesting refresh of the novel tree") logger.debug("Requesting refresh of the novel tree")
if rootHandle is None: if rootHandle is None:
rootHandle = self.theProject.tree.findRoot(nwItemClass.NOVEL) rootHandle = self.mainGui.project.tree.findRoot(nwItemClass.NOVEL)
treeChanged = self.mainGui.projView.changedSince(self._lastBuild) treeChanged = self.mainGui.projView.changedSince(self._lastBuild)
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild) indexChanged = self.mainGui.project.index.rootChangedSince(rootHandle, self._lastBuild)
if not (treeChanged or indexChanged or overRide): if not (treeChanged or indexChanged or overRide):
logger.debug("No changes have been made to the novel index") logger.debug("No changes have been made to the novel index")
return return
@@ -532,7 +528,7 @@ class GuiNovelTree(QTreeWidget):
titleKey = selItem[0].data(self.C_DATA, self.D_KEY) titleKey = selItem[0].data(self.C_DATA, self.D_KEY)
self._populateTree(rootHandle) self._populateTree(rootHandle)
self.theProject.data.setLastHandle(rootHandle, "novelTree") self.mainGui.project.data.setLastHandle(rootHandle, "novelTree")
if titleKey is not None and titleKey in self._treeMap: if titleKey is not None and titleKey in self._treeMap:
self._treeMap[titleKey].setSelected(True) self._treeMap[titleKey].setSelected(True)
@@ -542,7 +538,7 @@ class GuiNovelTree(QTreeWidget):
def refreshHandle(self, tHandle): def refreshHandle(self, tHandle):
"""Refresh the data for a given handle. """Refresh the data for a given handle.
""" """
idxData = self.theProject.index.getItemData(tHandle) idxData = self.mainGui.project.index.getItemData(tHandle)
if idxData is None: if idxData is None:
return return
@@ -579,7 +575,7 @@ class GuiNovelTree(QTreeWidget):
self._lastCol = colType self._lastCol = colType
self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN) self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN)
if doRefresh: if doRefresh:
lastNovel = self.theProject.data.getLastHandle("novelTree") lastNovel = self.mainGui.project.data.getLastHandle("novelTree")
self.refreshTree(rootHandle=lastNovel, overRide=True) self.refreshTree(rootHandle=lastNovel, overRide=True)
return return
@@ -711,7 +707,7 @@ class GuiNovelTree(QTreeWidget):
tStart = time() tStart = time()
logger.debug("Building novel tree for root item '%s'", rootHandle) logger.debug("Building novel tree for root item '%s'", rootHandle)
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True) novStruct = self.mainGui.project.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
for tKey, tHandle, sTitle, novIdx in novStruct: for tKey, tHandle, sTitle, novIdx in novStruct:
if novIdx.level == "H0": if novIdx.level == "H0":
continue continue
@@ -737,7 +733,7 @@ class GuiNovelTree(QTreeWidget):
"""Set the tree item values from the index entry. """Set the tree item values from the index entry.
""" """
iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0) iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0)
hDec = self.mainTheme.getHeaderDecoration(iLevel) hDec = CONFIG.theme.getHeaderDecoration(iLevel)
trItem.setData(self.C_TITLE, Qt.DecorationRole, hDec) trItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
trItem.setText(self.C_TITLE, idxItem.title) trItem.setText(self.C_TITLE, idxItem.title)
@@ -763,7 +759,7 @@ class GuiNovelTree(QTreeWidget):
refData = [] refData = []
refName = "" refName = ""
theRefs = self.theProject.index.getReferences(tHandle, sTitle) theRefs = self.mainGui.project.index.getReferences(tHandle, sTitle)
if self._lastCol == NovelTreeColumn.POV: if self._lastCol == NovelTreeColumn.POV:
refData = theRefs[nwKeyWords.POV_KEY] refData = theRefs[nwKeyWords.POV_KEY]
refName = self._povLabel refName = self._povLabel
@@ -787,7 +783,7 @@ class GuiNovelTree(QTreeWidget):
""" """
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle) logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
pIndex = self.theProject.index pIndex = self.mainGui.project.index
novIdx = pIndex.getItemHeader(tHandle, sTitle) novIdx = pIndex.getItemHeader(tHandle, sTitle)
refTags = pIndex.getReferences(tHandle, sTitle) refTags = pIndex.getReferences(tHandle, sTitle)
+31 -38
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Project Outline novelWriter GUI Project Outline
================================= =================================
GUI class for the project outline view
File History: File History:
Created: 2022-05-15 [2.0rc1] GuiOutlineView Created: 2022-05-15 [2.0rc1] GuiOutlineView
@@ -26,6 +25,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -48,7 +48,7 @@ from novelwriter.enum import (
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.common import checkInt from novelwriter.common import checkInt
from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels
from novelwriter.gui.components import NovelSelector from novelwriter.extensions.novelselector import NovelSelector
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -62,8 +62,7 @@ class GuiOutlineView(QWidget):
def __init__(self, mainGui): def __init__(self, mainGui):
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
self.mainGui = mainGui self.mainGui = mainGui
self.theProject = mainGui.theProject
# Build GUI # Build GUI
self.outlineTree = GuiOutlineTree(self) self.outlineTree = GuiOutlineTree(self)
@@ -118,7 +117,7 @@ class GuiOutlineView(QWidget):
def refreshTree(self): def refreshTree(self):
"""Refresh the current tree. """Refresh the current tree.
""" """
self.outlineTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("outline")) self.outlineTree.refreshTree(rootHandle=self.mainGui.project.data.getLastHandle("outline"))
return return
def clearProject(self): def clearProject(self):
@@ -131,9 +130,9 @@ class GuiOutlineView(QWidget):
def openProjectTasks(self): def openProjectTasks(self):
"""Run open project tasks. """Run open project tasks.
""" """
lastOutline = self.theProject.data.getLastHandle("outline") lastOutline = self.mainGui.project.data.getLastHandle("outline")
if not (lastOutline in self.theProject.tree or lastOutline is None): if not (lastOutline in self.mainGui.project.tree or lastOutline is None):
lastOutline = self.theProject.tree.findRoot(nwItemClass.NOVEL) lastOutline = self.mainGui.project.tree.findRoot(nwItemClass.NOVEL)
logger.debug("Setting outline tree to root item '%s'", lastOutline) logger.debug("Setting outline tree to root item '%s'", lastOutline)
@@ -215,9 +214,7 @@ class GuiOutlineToolBar(QToolBar):
logger.debug("Create: GuiOutlineToolBar") logger.debug("Create: GuiOutlineToolBar")
self.mainGui = theOutline.mainGui self.mainGui = theOutline.mainGui
self.theProject = theOutline.mainGui.theProject
self.mainTheme = theOutline.mainGui.mainTheme
iPx = CONFIG.pxInt(22) iPx = CONFIG.pxInt(22)
mPx = CONFIG.pxInt(12) mPx = CONFIG.pxInt(12)
@@ -233,7 +230,7 @@ class GuiOutlineToolBar(QToolBar):
self.novelLabel = QLabel(self.tr("Outline of")) self.novelLabel = QLabel(self.tr("Outline of"))
self.novelLabel.setContentsMargins(0, 0, mPx, 0) self.novelLabel.setContentsMargins(0, 0, mPx, 0)
self.novelValue = NovelSelector(self, self.theProject, self.mainGui) self.novelValue = NovelSelector(self, self.mainGui)
self.novelValue.setMinimumWidth(CONFIG.pxInt(200)) self.novelValue.setMinimumWidth(CONFIG.pxInt(200))
self.novelValue.novelSelectionChanged.connect(self._novelValueChanged) self.novelValue.novelSelectionChanged.connect(self._novelValueChanged)
@@ -275,8 +272,8 @@ class GuiOutlineToolBar(QToolBar):
self.setStyleSheet("QToolBar {border: 0px;}") self.setStyleSheet("QToolBar {border: 0px;}")
self.novelValue.updateList(includeAll=True) self.novelValue.updateList(includeAll=True)
self.aRefresh.setIcon(self.mainTheme.getIcon("refresh")) self.aRefresh.setIcon(CONFIG.theme.getIcon("refresh"))
self.tbColumns.setIcon(self.mainTheme.getIcon("menu")) self.tbColumns.setIcon(CONFIG.theme.getIcon("menu"))
return return
@@ -374,8 +371,6 @@ class GuiOutlineTree(QTreeWidget):
self.outlineView = outlineView self.outlineView = outlineView
self.mainGui = outlineView.mainGui self.mainGui = outlineView.mainGui
self.theProject = outlineView.mainGui.theProject
self.mainTheme = outlineView.mainGui.mainTheme
self.setUniformRowHeights(True) self.setUniformRowHeights(True)
self.setFrameStyle(QFrame.NoFrame) self.setFrameStyle(QFrame.NoFrame)
@@ -386,7 +381,7 @@ class GuiOutlineTree(QTreeWidget):
self.itemDoubleClicked.connect(self._treeDoubleClick) self.itemDoubleClicked.connect(self._treeDoubleClick)
self.itemSelectionChanged.connect(self._itemSelected) self.itemSelectionChanged.connect(self._itemSelected)
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
self.setIndentation(0) self.setIndentation(0)
@@ -403,11 +398,11 @@ class GuiOutlineTree(QTreeWidget):
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()] self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
self._dIcon = { self._dIcon = {
"H0": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H0"), "H0": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H0"),
"H1": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H1"), "H1": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H1"),
"H2": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H2"), "H2": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H2"),
"H3": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H3"), "H3": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H3"),
"H4": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H4"), "H4": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H4"),
} }
# Internals # Internals
@@ -493,13 +488,13 @@ class GuiOutlineTree(QTreeWidget):
# If the novel index or novel tree has changed since the tree # If the novel index or novel tree has changed since the tree
# was last built, we rebuild the tree from the updated index. # was last built, we rebuild the tree from the updated index.
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild) indexChanged = self.mainGui.project.index.rootChangedSince(rootHandle, self._lastBuild)
if not (novelChanged or indexChanged or overRide): if not (novelChanged or indexChanged or overRide):
logger.debug("No changes have been made to the novel index") logger.debug("No changes have been made to the novel index")
return return
self._populateTree(rootHandle) self._populateTree(rootHandle)
self.theProject.data.setLastHandle(rootHandle or None, "outline") self.mainGui.project.data.setLastHandle(rootHandle or None, "outline")
return return
@@ -579,7 +574,7 @@ class GuiOutlineTree(QTreeWidget):
""" """
# Load whatever we saved last time, regardless of wether it # Load whatever we saved last time, regardless of wether it
# contains the correct names or number of columns. # contains the correct names or number of columns.
colState = self.theProject.options.getValue("GuiOutline", "columnState", {}) colState = self.mainGui.project.options.getValue("GuiOutline", "columnState", {})
tmpOrder = [] tmpOrder = []
tmpHidden = {} tmpHidden = {}
@@ -630,7 +625,7 @@ class GuiOutlineTree(QTreeWidget):
logHidden, orgWidth if logHidden and logWidth == 0 else logWidth logHidden, orgWidth if logHidden and logWidth == 0 else logWidth
] ]
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiOutline", "columnState", colState) pOptions.setValue("GuiOutline", "columnState", colState)
pOptions.saveSettings() pOptions.saveSettings()
@@ -666,7 +661,7 @@ class GuiOutlineTree(QTreeWidget):
headItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight) headItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight)
headItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight) headItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight)
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True) novStruct = self.mainGui.project.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
for _, tHandle, sTitle, novIdx in novStruct: for _, tHandle, sTitle, novIdx in novStruct:
iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0) iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0)
@@ -674,8 +669,8 @@ class GuiOutlineTree(QTreeWidget):
continue continue
trItem = QTreeWidgetItem() trItem = QTreeWidgetItem()
nwItem = self.theProject.tree[tHandle] nwItem = self.mainGui.project.tree[tHandle]
hDec = self.mainTheme.getHeaderDecoration(iLevel) hDec = CONFIG.theme.getHeaderDecoration(iLevel)
trItem.setData(self._colIdx[nwOutline.TITLE], Qt.DecorationRole, hDec) trItem.setData(self._colIdx[nwOutline.TITLE], Qt.DecorationRole, hDec)
trItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title) trItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title)
@@ -694,7 +689,7 @@ class GuiOutlineTree(QTreeWidget):
trItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight) trItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight)
trItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight) trItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight)
refs = self.theProject.index.getReferences(tHandle, sTitle) refs = self.mainGui.project.index.getReferences(tHandle, sTitle)
trItem.setText(self._colIdx[nwOutline.POV], ", ".join(refs[nwKeyWords.POV_KEY])) trItem.setText(self._colIdx[nwOutline.POV], ", ".join(refs[nwKeyWords.POV_KEY]))
trItem.setText(self._colIdx[nwOutline.FOCUS], ", ".join(refs[nwKeyWords.FOCUS_KEY])) trItem.setText(self._colIdx[nwOutline.FOCUS], ", ".join(refs[nwKeyWords.FOCUS_KEY]))
trItem.setText(self._colIdx[nwOutline.CHAR], ", ".join(refs[nwKeyWords.CHAR_KEY])) trItem.setText(self._colIdx[nwOutline.CHAR], ", ".join(refs[nwKeyWords.CHAR_KEY]))
@@ -776,13 +771,11 @@ class GuiOutlineDetails(QScrollArea):
self.theOutline = theOutline self.theOutline = theOutline
self.mainGui = theOutline.mainGui self.mainGui = theOutline.mainGui
self.theProject = theOutline.mainGui.theProject
self.mainTheme = theOutline.mainGui.mainTheme
# Sizes # Sizes
minTitle = 30*self.mainTheme.textNWidth minTitle = 30*CONFIG.theme.textNWidth
maxTitle = 40*self.mainTheme.textNWidth maxTitle = 40*CONFIG.theme.textNWidth
wCount = self.mainTheme.getTextWidth("999,999") wCount = CONFIG.theme.getTextWidth("999,999")
hSpace = int(CONFIG.pxInt(10)) hSpace = int(CONFIG.pxInt(10))
vSpace = int(CONFIG.pxInt(4)) vSpace = int(CONFIG.pxInt(4))
@@ -1012,8 +1005,8 @@ class GuiOutlineDetails(QScrollArea):
"""Update the content of the tree with the given handle and line """Update the content of the tree with the given handle and line
number pointing to a header. number pointing to a header.
""" """
pIndex = self.theProject.index pIndex = self.mainGui.project.index
nwItem = self.theProject.tree[tHandle] nwItem = self.mainGui.project.tree[tHandle]
novIdx = pIndex.getItemHeader(tHandle, sTitle) novIdx = pIndex.getItemHeader(tHandle, sTitle)
theRefs = pIndex.getReferences(tHandle, sTitle) theRefs = pIndex.getReferences(tHandle, sTitle)
if nwItem is None or novIdx is None: if nwItem is None or novIdx is None:
@@ -1056,7 +1049,7 @@ class GuiOutlineDetails(QScrollArea):
def updateClasses(self): def updateClasses(self):
"""Update the visibility status of class details. """Update the visibility status of class details.
""" """
usedClasses = self.theProject.tree.rootClasses() usedClasses = self.mainGui.project.tree.rootClasses()
pltVisible = nwItemClass.PLOT in usedClasses pltVisible = nwItemClass.PLOT in usedClasses
timVisible = nwItemClass.TIMELINE in usedClasses timVisible = nwItemClass.TIMELINE in usedClasses
+76 -81
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Project Tree novelWriter GUI Project Tree
============================== ==============================
GUI classes for the main window project tree
File History: File History:
Created: 2018-09-29 [0.0.1] GuiProjectTree Created: 2018-09-29 [0.0.1] GuiProjectTree
@@ -233,13 +232,11 @@ class GuiProjectToolBar(QWidget):
logger.debug("Create: GuiProjectToolBar") logger.debug("Create: GuiProjectToolBar")
self.projView = projView self.projView = projView
self.projTree = projView.projTree self.projTree = projView.projTree
self.mainGui = projView.mainGui self.mainGui = projView.mainGui
self.theProject = projView.mainGui.theProject
self.mainTheme = projView.mainGui.mainTheme
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
mPx = CONFIG.pxInt(2) mPx = CONFIG.pxInt(2)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
@@ -370,16 +367,16 @@ class GuiProjectToolBar(QWidget):
self.tbAdd.setStyleSheet(buttonStyle) self.tbAdd.setStyleSheet(buttonStyle)
self.tbMore.setStyleSheet(buttonStyle) self.tbMore.setStyleSheet(buttonStyle)
self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark")) self.tbQuick.setIcon(CONFIG.theme.getIcon("bookmark"))
self.tbMoveU.setIcon(self.mainTheme.getIcon("up")) self.tbMoveU.setIcon(CONFIG.theme.getIcon("up"))
self.tbMoveD.setIcon(self.mainTheme.getIcon("down")) self.tbMoveD.setIcon(CONFIG.theme.getIcon("down"))
self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document")) self.aAddEmpty.setIcon(CONFIG.theme.getIcon("proj_document"))
self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter")) self.aAddChap.setIcon(CONFIG.theme.getIcon("proj_chapter"))
self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene")) self.aAddScene.setIcon(CONFIG.theme.getIcon("proj_scene"))
self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note")) self.aAddNote.setIcon(CONFIG.theme.getIcon("proj_note"))
self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder")) self.aAddFolder.setIcon(CONFIG.theme.getIcon("proj_folder"))
self.tbAdd.setIcon(self.mainTheme.getIcon("add")) self.tbAdd.setIcon(CONFIG.theme.getIcon("add"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu")) self.tbMore.setIcon(CONFIG.theme.getIcon("menu"))
self.buildQuickLinkMenu() self.buildQuickLinkMenu()
self._buildRootMenu() self._buildRootMenu()
@@ -395,10 +392,10 @@ class GuiProjectToolBar(QWidget):
"""Build the quick link menu.""" """Build the quick link menu."""
logger.debug("Rebuilding quick links menu") logger.debug("Rebuilding quick links menu")
self.mQuick.clear() self.mQuick.clear()
for n, (tHandle, nwItem) in enumerate(self.theProject.tree.iterRoots(None)): for n, (tHandle, nwItem) in enumerate(self.mainGui.project.tree.iterRoots(None)):
aRoot = self.mQuick.addAction(nwItem.itemName) aRoot = self.mQuick.addAction(nwItem.itemName)
aRoot.setData(tHandle) aRoot.setData(tHandle)
aRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass])) aRoot.setIcon(CONFIG.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]))
aRoot.triggered.connect( aRoot.triggered.connect(
lambda n, tHandle=tHandle: self.projView.setSelectedHandle(tHandle, doScroll=True) lambda n, tHandle=tHandle: self.projView.setSelectedHandle(tHandle, doScroll=True)
) )
@@ -412,7 +409,7 @@ class GuiProjectToolBar(QWidget):
"""Build the rood folder menu.""" """Build the rood folder menu."""
def addClass(itemClass): def addClass(itemClass):
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass])) aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
aNew.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[itemClass])) aNew.setIcon(CONFIG.theme.getIcon(nwLabels.CLASS_ICON[itemClass]))
aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass)) aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass))
self.mAddRoot.addAction(aNew) self.mAddRoot.addAction(aNew)
return return
@@ -441,7 +438,7 @@ class GuiProjectToolBar(QWidget):
documents. They should only be visible if novel documents can documents. They should only be visible if novel documents can
actually be added. actually be added.
""" """
nwItem = self.theProject.tree[tHandle] nwItem = self.mainGui.project.tree[tHandle]
allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed() allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed()
self.aAddEmpty.setVisible(allowDoc) self.aAddEmpty.setVisible(allowDoc)
self.aAddChap.setVisible(allowDoc) self.aAddChap.setVisible(allowDoc)
@@ -467,10 +464,8 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Create: GuiProjectTree") logger.debug("Create: GuiProjectTree")
self.projView = projView self.projView = projView
self.mainGui = projView.mainGui self.mainGui = projView.mainGui
self.mainTheme = projView.mainGui.mainTheme
self.theProject = projView.mainGui.theProject
# Internal Variables # Internal Variables
self._treeMap = {} self._treeMap = {}
@@ -485,7 +480,7 @@ class GuiProjectTree(QTreeWidget):
self.customContextMenuRequested.connect(self._openContextMenu) self.customContextMenuRequested.connect(self._openContextMenu)
# Tree Settings # Tree Settings
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
cMg = CONFIG.pxInt(6) cMg = CONFIG.pxInt(6)
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
@@ -577,15 +572,15 @@ class GuiProjectTree(QTreeWidget):
if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass): if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
tHandle = self.theProject.newRoot(itemClass) tHandle = self.mainGui.project.newRoot(itemClass)
sHandle = self.getSelectedHandle() sHandle = self.getSelectedHandle()
pItem = self.theProject.tree[sHandle] if sHandle else None pItem = self.mainGui.project.tree[sHandle] if sHandle else None
nHandle = pItem.itemRoot if pItem else None nHandle = pItem.itemRoot if pItem else None
elif itemType in (nwItemType.FILE, nwItemType.FOLDER): elif itemType in (nwItemType.FILE, nwItemType.FOLDER):
sHandle = self.getSelectedHandle() sHandle = self.getSelectedHandle()
pItem = self.theProject.tree[sHandle] if sHandle else None pItem = self.mainGui.project.tree[sHandle] if sHandle else None
if sHandle is None or pItem is None: if sHandle is None or pItem is None:
self.mainGui.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Did not find anywhere to add the file or folder!" "Did not find anywhere to add the file or folder!"
@@ -597,7 +592,7 @@ class GuiProjectTree(QTreeWidget):
sLevel = nwHeaders.H_LEVEL.get(pItem.mainHeading, 0) sLevel = nwHeaders.H_LEVEL.get(pItem.mainHeading, 0)
sIsParent = False if qItem is None else qItem.childCount() > 0 sIsParent = False if qItem is None else qItem.childCount() > 0
if self.theProject.tree.isTrash(sHandle): if self.mainGui.project.tree.isTrash(sHandle):
self.mainGui.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Cannot add new files or folders to the Trash folder." "Cannot add new files or folders to the Trash folder."
), level=nwAlert.ERROR) ), level=nwAlert.ERROR)
@@ -640,9 +635,9 @@ class GuiProjectTree(QTreeWidget):
# Add the file or folder # Add the file or folder
if itemType == nwItemType.FILE: if itemType == nwItemType.FILE:
tHandle = self.theProject.newFile(newLabel, sHandle) tHandle = self.mainGui.project.newFile(newLabel, sHandle)
else: else:
tHandle = self.theProject.newFolder(newLabel, sHandle) tHandle = self.mainGui.project.newFolder(newLabel, sHandle)
else: else:
logger.error("Failed to add new item") logger.error("Failed to add new item")
@@ -655,7 +650,7 @@ class GuiProjectTree(QTreeWidget):
# Handle new file creation # Handle new file creation
if itemType == nwItemType.FILE and hLevel > 0: if itemType == nwItemType.FILE and hLevel > 0:
self.theProject.writeNewFile(tHandle, hLevel, not isNote) self.mainGui.project.writeNewFile(tHandle, hLevel, not isNote)
# Add the new item to the project tree # Add the new item to the project tree
self.revealNewTreeItem(tHandle, nHandle=nHandle, wordCount=True) self.revealNewTreeItem(tHandle, nHandle=nHandle, wordCount=True)
@@ -666,7 +661,7 @@ class GuiProjectTree(QTreeWidget):
def revealNewTreeItem(self, tHandle: str | None, nHandle: str | None = None, def revealNewTreeItem(self, tHandle: str | None, nHandle: str | None = None,
wordCount: bool = False) -> bool: wordCount: bool = False) -> bool:
"""Reveal a newly added project item in the project tree.""" """Reveal a newly added project item in the project tree."""
nwItem = self.theProject.tree[tHandle] if tHandle else None nwItem = self.mainGui.project.tree[tHandle] if tHandle else None
if tHandle is None or nwItem is None: if tHandle is None or nwItem is None:
return False return False
@@ -675,7 +670,7 @@ class GuiProjectTree(QTreeWidget):
return False return False
if nwItem.isFileType() and wordCount: if nwItem.isFileType() and wordCount:
wC = self.theProject.index.getCounts(tHandle)[1] wC = self.mainGui.project.index.getCounts(tHandle)[1]
self.propagateCount(tHandle, wC) self.propagateCount(tHandle, wC)
self.projView.wordCountsChanged.emit() self.projView.wordCountsChanged.emit()
@@ -750,7 +745,7 @@ class GuiProjectTree(QTreeWidget):
def renameTreeItem(self, tHandle: str) -> bool: def renameTreeItem(self, tHandle: str) -> bool:
"""Open a dialog to edit the label of an item.""" """Open a dialog to edit the label of an item."""
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is None: if tItem is None:
return False return False
@@ -774,7 +769,7 @@ class GuiProjectTree(QTreeWidget):
if isinstance(item, QTreeWidgetItem): if isinstance(item, QTreeWidgetItem):
theList = self._scanChildren(theList, item, i) theList = self._scanChildren(theList, item, i)
logger.debug("Saving project tree item order") logger.debug("Saving project tree item order")
self.theProject.setTreeOrder(theList) self.mainGui.project.setTreeOrder(theList)
return return
def getTreeFromHandle(self, tHandle: str) -> list[str]: def getTreeFromHandle(self, tHandle: str) -> list[str]:
@@ -807,16 +802,16 @@ class GuiProjectTree(QTreeWidget):
logger.error("There is no item to delete") logger.error("There is no item to delete")
return False return False
trashHandle = self.theProject.tree.trashRoot() trashHandle = self.mainGui.project.tree.trashRoot()
if tHandle == trashHandle: if tHandle == trashHandle:
logger.error("Cannot delete the Trash folder") logger.error("Cannot delete the Trash folder")
return False return False
nwItem = self.theProject.tree[tHandle] nwItem = self.mainGui.project.tree[tHandle]
if nwItem is None: if nwItem is None:
return False return False
if self.theProject.tree.isTrash(tHandle) or nwItem.isRootType(): if self.mainGui.project.tree.isTrash(tHandle) or nwItem.isRootType():
status = self.permDeleteItem(tHandle) status = self.permDeleteItem(tHandle)
else: else:
status = self.moveItemToTrash(tHandle) status = self.moveItemToTrash(tHandle)
@@ -832,7 +827,7 @@ class GuiProjectTree(QTreeWidget):
logger.error("No project open") logger.error("No project open")
return False return False
trashHandle = self.theProject.tree.trashRoot() trashHandle = self.mainGui.project.tree.trashRoot()
logger.debug("Emptying Trash folder") logger.debug("Emptying Trash folder")
if trashHandle is None: if trashHandle is None:
@@ -875,13 +870,13 @@ class GuiProjectTree(QTreeWidget):
so such a request is cancelled. so such a request is cancelled.
""" """
trItemS = self._getTreeItem(tHandle) trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.tree[tHandle] nwItemS = self.mainGui.project.tree[tHandle]
if trItemS is None or nwItemS is None: if trItemS is None or nwItemS is None:
logger.error("Could not find tree item for deletion") logger.error("Could not find tree item for deletion")
return False return False
if self.theProject.tree.isTrash(tHandle): if self.mainGui.project.tree.isTrash(tHandle):
logger.error("Item is already in the Trash folder") logger.error("Item is already in the Trash folder")
return False return False
@@ -925,7 +920,7 @@ class GuiProjectTree(QTreeWidget):
Root items are handled a little different than other items. Root items are handled a little different than other items.
""" """
trItemS = self._getTreeItem(tHandle) trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.tree[tHandle] nwItemS = self.mainGui.project.tree[tHandle]
if trItemS is None or nwItemS is None: if trItemS is None or nwItemS is None:
logger.error("Could not find tree item for deletion") logger.error("Could not find tree item for deletion")
return False return False
@@ -942,7 +937,7 @@ class GuiProjectTree(QTreeWidget):
tIndex = self.indexOfTopLevelItem(trItemS) tIndex = self.indexOfTopLevelItem(trItemS)
self.takeTopLevelItem(tIndex) self.takeTopLevelItem(tIndex)
self.theProject.removeItem(tHandle) self.mainGui.project.removeItem(tHandle)
self._treeMap.pop(tHandle, None) self._treeMap.pop(tHandle, None)
self._alertTreeChange(tHandle, flush=True) self._alertTreeChange(tHandle, flush=True)
@@ -971,7 +966,7 @@ class GuiProjectTree(QTreeWidget):
for dHandle in reversed(self.getTreeFromHandle(tHandle)): for dHandle in reversed(self.getTreeFromHandle(tHandle)):
if self.mainGui.docEditor.docHandle() == dHandle: if self.mainGui.docEditor.docHandle() == dHandle:
self.mainGui.closeDocument() self.mainGui.closeDocument()
self.theProject.removeItem(dHandle) self.mainGui.project.removeItem(dHandle)
self._treeMap.pop(dHandle, None) self._treeMap.pop(dHandle, None)
self._alertTreeChange(tHandle, flush=flush) self._alertTreeChange(tHandle, flush=flush)
@@ -989,13 +984,13 @@ class GuiProjectTree(QTreeWidget):
already coming from the project tree. already coming from the project tree.
""" """
trItem = self._getTreeItem(tHandle) trItem = self._getTreeItem(tHandle)
nwItem = self.theProject.tree[tHandle] nwItem = self.mainGui.project.tree[tHandle]
if trItem is None or nwItem is None: if trItem is None or nwItem is None:
return return
itemStatus, statusIcon = nwItem.getImportStatus(incIcon=True) itemStatus, statusIcon = nwItem.getImportStatus(incIcon=True)
hLevel = nwItem.mainHeading hLevel = nwItem.mainHeading
itemIcon = self.mainTheme.getItemIcon( itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
) )
@@ -1011,7 +1006,7 @@ class GuiProjectTree(QTreeWidget):
else: else:
iconName = "noncheckable" iconName = "noncheckable"
trItem.setIcon(self.C_ACTIVE, self.mainTheme.getIcon(iconName)) trItem.setIcon(self.C_ACTIVE, CONFIG.theme.getIcon(iconName))
if CONFIG.emphLabels and nwItem.isDocumentLayout(): if CONFIG.emphLabels and nwItem.isDocumentLayout():
trFont = trItem.font(self.C_NAME) trFont = trItem.font(self.C_NAME)
@@ -1051,10 +1046,10 @@ class GuiProjectTree(QTreeWidget):
pHandle = pItem.data(self.C_DATA, self.D_HANDLE) pHandle = pItem.data(self.C_DATA, self.D_HANDLE)
if pHandle: if pHandle:
if self.theProject.tree.checkType(pHandle, nwItemType.FILE): if self.mainGui.project.tree.checkType(pHandle, nwItemType.FILE):
# A file has an internal word count we need to account # A file has an internal word count we need to account
# for, but a folder always has 0 words on its own. # for, but a folder always has 0 words on its own.
pCount += self.theProject.index.getCounts(pHandle)[1] pCount += self.mainGui.project.index.getCounts(pHandle)[1]
self.propagateCount(pHandle, pCount, countChildren=False) self.propagateCount(pHandle, pCount, countChildren=False)
@@ -1069,7 +1064,7 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Building the project tree ...") logger.debug("Building the project tree ...")
self.clearTree() self.clearTree()
count = 0 count = 0
for nwItem in self.theProject.getProjectItems(): for nwItem in self.mainGui.project.getProjectItems():
count += 1 count += 1
self._addTreeItem(nwItem) self._addTreeItem(nwItem)
if count > 0: if count > 0:
@@ -1182,7 +1177,7 @@ class GuiProjectTree(QTreeWidget):
if tHandle is None: if tHandle is None:
return return
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is None: if tItem is None:
return return
@@ -1204,7 +1199,7 @@ class GuiProjectTree(QTreeWidget):
selItem = self.itemAt(clickPos) selItem = self.itemAt(clickPos)
if isinstance(selItem, QTreeWidgetItem): if isinstance(selItem, QTreeWidgetItem):
tHandle = selItem.data(self.C_DATA, self.D_HANDLE) tHandle = selItem.data(self.C_DATA, self.D_HANDLE)
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
hasChild = selItem.childCount() > 0 hasChild = selItem.childCount() > 0
if tItem is None or tHandle is None: if tItem is None or tHandle is None:
@@ -1216,7 +1211,7 @@ class GuiProjectTree(QTreeWidget):
# Trash Folder # Trash Folder
# ============ # ============
trashHandle = self.theProject.tree.trashRoot() trashHandle = self.mainGui.project.tree.trashRoot()
if tItem.itemHandle == trashHandle and trashHandle is not None: if tItem.itemHandle == trashHandle and trashHandle is not None:
# The trash folder only has one option # The trash folder only has one option
aEmptyTrash = ctxMenu.addAction(self.tr("Empty Trash")) aEmptyTrash = ctxMenu.addAction(self.tr("Empty Trash"))
@@ -1255,7 +1250,7 @@ class GuiProjectTree(QTreeWidget):
checkMark = f" ({nwUnicode.U_CHECK})" checkMark = f" ({nwUnicode.U_CHECK})"
if tItem.isNovelLike(): if tItem.isNovelLike():
mStatus = ctxMenu.addMenu(self.tr("Set Status to ...")) mStatus = ctxMenu.addMenu(self.tr("Set Status to ..."))
for n, (key, entry) in enumerate(self.theProject.data.itemStatus.items()): for n, (key, entry) in enumerate(self.mainGui.project.data.itemStatus.items()):
entryName = entry["name"] + (checkMark if tItem.itemStatus == key else "") entryName = entry["name"] + (checkMark if tItem.itemStatus == key else "")
aStatus = mStatus.addAction(entry["icon"], entryName) aStatus = mStatus.addAction(entry["icon"], entryName)
aStatus.triggered.connect( aStatus.triggered.connect(
@@ -1268,7 +1263,7 @@ class GuiProjectTree(QTreeWidget):
) )
else: else:
mImport = ctxMenu.addMenu(self.tr("Set Importance to ...")) mImport = ctxMenu.addMenu(self.tr("Set Importance to ..."))
for n, (key, entry) in enumerate(self.theProject.data.itemImport.items()): for n, (key, entry) in enumerate(self.mainGui.project.data.itemImport.items()):
entryName = entry["name"] + (checkMark if tItem.itemImport == key else "") entryName = entry["name"] + (checkMark if tItem.itemImport == key else "")
aImport = mImport.addAction(entry["icon"], entryName) aImport = mImport.addAction(entry["icon"], entryName)
aImport.triggered.connect( aImport.triggered.connect(
@@ -1380,7 +1375,7 @@ class GuiProjectTree(QTreeWidget):
return return
tHandle = selItem.data(self.C_DATA, self.D_HANDLE) tHandle = selItem.data(self.C_DATA, self.D_HANDLE)
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is None: if tItem is None:
return return
@@ -1424,7 +1419,7 @@ class GuiProjectTree(QTreeWidget):
def _postItemMove(self, tHandle: str, wCount: int) -> bool: def _postItemMove(self, tHandle: str, wCount: int) -> bool:
"""Run various maintenance tasks for a moved item.""" """Run various maintenance tasks for a moved item."""
trItemS = self._getTreeItem(tHandle) trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.tree[tHandle] nwItemS = self.mainGui.project.tree[tHandle]
trItemP = trItemS.parent() if trItemS else None trItemP = trItemS.parent() if trItemS else None
if trItemP is None or nwItemS is None: if trItemP is None or nwItemS is None:
logger.error("Failed to find new parent item of '%s'", tHandle) logger.error("Failed to find new parent item of '%s'", tHandle)
@@ -1441,13 +1436,13 @@ class GuiProjectTree(QTreeWidget):
logger.debug("A total of %d item(s) were moved", len(mHandles)) logger.debug("A total of %d item(s) were moved", len(mHandles))
for mHandle in mHandles: for mHandle in mHandles:
logger.debug("Updating item '%s'", mHandle) logger.debug("Updating item '%s'", mHandle)
self.theProject.tree.updateItemData(mHandle) self.mainGui.project.tree.updateItemData(mHandle)
# Update the index # Update the index
if nwItemS.isInactiveClass(): if nwItemS.isInactiveClass():
self.theProject.index.deleteHandle(mHandle) self.mainGui.project.index.deleteHandle(mHandle)
else: else:
self.theProject.index.reIndexHandle(mHandle) self.mainGui.project.index.reIndexHandle(mHandle)
self.setTreeItemValues(mHandle) self.setTreeItemValues(mHandle)
@@ -1467,7 +1462,7 @@ class GuiProjectTree(QTreeWidget):
def _toggleItemActive(self, tHandle: str) -> None: def _toggleItemActive(self, tHandle: str) -> None:
"""Toggle the active status of an item.""" """Toggle the active status of an item."""
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is not None: if tItem is not None:
tItem.setActive(not tItem.isActive) tItem.setActive(not tItem.isActive)
self.setTreeItemValues(tItem.itemHandle) self.setTreeItemValues(tItem.itemHandle)
@@ -1488,7 +1483,7 @@ class GuiProjectTree(QTreeWidget):
def _changeItemStatus(self, tHandle: str, tStatus: str) -> None: def _changeItemStatus(self, tHandle: str, tStatus: str) -> None:
"""Set a new status value of an item.""" """Set a new status value of an item."""
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is not None: if tItem is not None:
tItem.setStatus(tStatus) tItem.setStatus(tStatus)
self.setTreeItemValues(tItem.itemHandle) self.setTreeItemValues(tItem.itemHandle)
@@ -1497,7 +1492,7 @@ class GuiProjectTree(QTreeWidget):
def _changeItemImport(self, tHandle: str, tImport: str) -> None: def _changeItemImport(self, tHandle: str, tImport: str) -> None:
"""Set a new importance value of an item.""" """Set a new importance value of an item."""
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is not None: if tItem is not None:
tItem.setImport(tImport) tItem.setImport(tImport)
self.setTreeItemValues(tItem.itemHandle) self.setTreeItemValues(tItem.itemHandle)
@@ -1506,7 +1501,7 @@ class GuiProjectTree(QTreeWidget):
def _changeItemLayout(self, tHandle: str, itemLayout: nwItemLayout) -> None: def _changeItemLayout(self, tHandle: str, itemLayout: nwItemLayout) -> None:
"""Set a new item layout value of an item.""" """Set a new item layout value of an item."""
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is not None: if tItem is not None:
if itemLayout == nwItemLayout.DOCUMENT and tItem.documentAllowed(): if itemLayout == nwItemLayout.DOCUMENT and tItem.documentAllowed():
tItem.setLayout(nwItemLayout.DOCUMENT) tItem.setLayout(nwItemLayout.DOCUMENT)
@@ -1520,7 +1515,7 @@ class GuiProjectTree(QTreeWidget):
def _covertFolderToFile(self, tHandle: str, itemLayout: nwItemLayout) -> None: def _covertFolderToFile(self, tHandle: str, itemLayout: nwItemLayout) -> None:
"""Convert a folder to a note or document.""" """Convert a folder to a note or document."""
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is not None and tItem.isFolderType(): if tItem is not None and tItem.isFolderType():
msgYes = self.mainGui.askQuestion(self.tr( msgYes = self.mainGui.askQuestion(self.tr(
"Do you want to convert the folder to a {0}? " "Do you want to convert the folder to a {0}? "
@@ -1545,7 +1540,7 @@ class GuiProjectTree(QTreeWidget):
logger.info("Request to merge items under handle '%s'", tHandle) logger.info("Request to merge items under handle '%s'", tHandle)
itemList = self.getTreeFromHandle(tHandle) itemList = self.getTreeFromHandle(tHandle)
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is None: if tItem is None:
return False return False
@@ -1571,7 +1566,7 @@ class GuiProjectTree(QTreeWidget):
self.mainGui.saveDocument() self.mainGui.saveDocument()
# Create merge object, and append docs # Create merge object, and append docs
docMerger = DocMerger(self.theProject) docMerger = DocMerger(self.mainGui.project)
mLabel = self.tr("Merged") mLabel = self.tr("Merged")
if newFile: if newFile:
@@ -1593,7 +1588,7 @@ class GuiProjectTree(QTreeWidget):
) )
return False return False
self.theProject.index.reIndexHandle(mHandle) self.mainGui.project.index.reIndexHandle(mHandle)
if newFile: if newFile:
self.revealNewTreeItem(mHandle, nHandle=tHandle, wordCount=True) self.revealNewTreeItem(mHandle, nHandle=tHandle, wordCount=True)
@@ -1618,7 +1613,7 @@ class GuiProjectTree(QTreeWidget):
"""Split a document into multiple documents.""" """Split a document into multiple documents."""
logger.info("Request to split items with handle '%s'", tHandle) logger.info("Request to split items with handle '%s'", tHandle)
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem is None: if tItem is None:
return False return False
@@ -1637,7 +1632,7 @@ class GuiProjectTree(QTreeWidget):
intoFolder = splitData.get("intoFolder", False) intoFolder = splitData.get("intoFolder", False)
docHierarchy = splitData.get("docHierarchy", False) docHierarchy = splitData.get("docHierarchy", False)
docSplit = DocSplitter(self.theProject, tHandle) docSplit = DocSplitter(self.mainGui.project, tHandle)
if intoFolder: if intoFolder:
fHandle = docSplit.newParentFolder(tItem.itemParent, tItem.itemName) fHandle = docSplit.newParentFolder(tItem.itemParent, tItem.itemName)
self.revealNewTreeItem(fHandle, nHandle=tHandle) self.revealNewTreeItem(fHandle, nHandle=tHandle)
@@ -1647,7 +1642,7 @@ class GuiProjectTree(QTreeWidget):
docSplit.splitDocument(headerList, splitText) docSplit.splitDocument(headerList, splitText)
for writeOk, dHandle, nHandle in docSplit.writeDocuments(docHierarchy): for writeOk, dHandle, nHandle in docSplit.writeDocuments(docHierarchy):
self.theProject.index.reIndexHandle(dHandle) self.mainGui.project.index.reIndexHandle(dHandle)
self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True) self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True)
self._alertTreeChange(dHandle, flush=False) self._alertTreeChange(dHandle, flush=False)
if not writeOk: if not writeOk:
@@ -1681,10 +1676,10 @@ class GuiProjectTree(QTreeWidget):
if not self.mainGui.askQuestion(question): if not self.mainGui.askQuestion(question):
return False return False
docDup = DocDuplicator(self.theProject) docDup = DocDuplicator(self.mainGui.project)
dupCount = 0 dupCount = 0
for dHandle, nHandle in docDup.duplicate(itemTree): for dHandle, nHandle in docDup.duplicate(itemTree):
self.theProject.index.reIndexHandle(dHandle) self.mainGui.project.index.reIndexHandle(dHandle)
self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True) self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True)
self._alertTreeChange(dHandle, flush=False) self._alertTreeChange(dHandle, flush=False)
dupCount += 1 dupCount += 1
@@ -1704,7 +1699,7 @@ class GuiProjectTree(QTreeWidget):
cCount = tItem.childCount() cCount = tItem.childCount()
# Update tree-related meta data # Update tree-related meta data
nwItem = self.theProject.tree[tHandle] nwItem = self.mainGui.project.tree[tHandle]
if nwItem is not None: if nwItem is not None:
nwItem.setExpanded(tItem.isExpanded() and cCount > 0) nwItem.setExpanded(tItem.isExpanded() and cCount > 0)
nwItem.setOrder(tIndex) nwItem.setOrder(tIndex)
@@ -1771,13 +1766,13 @@ class GuiProjectTree(QTreeWidget):
"""Adds the trash root folder if it doesn't already exist in the """Adds the trash root folder if it doesn't already exist in the
project tree. project tree.
""" """
trashHandle = self.theProject.trashFolder() trashHandle = self.mainGui.project.trashFolder()
if trashHandle is None: if trashHandle is None:
return None return None
trItem = self._getTreeItem(trashHandle) trItem = self._getTreeItem(trashHandle)
if trItem is None: if trItem is None:
trItem = self._addTreeItem(self.theProject.tree[trashHandle]) trItem = self._addTreeItem(self.mainGui.project.tree[trashHandle])
if trItem is not None: if trItem is not None:
trItem.setExpanded(True) trItem.setExpanded(True)
self._alertTreeChange(trashHandle, flush=True) self._alertTreeChange(trashHandle, flush=True)
@@ -1790,14 +1785,14 @@ class GuiProjectTree(QTreeWidget):
deleted. deleted.
""" """
self._timeChanged = time() self._timeChanged = time()
self.theProject.setProjectChanged(True) self.mainGui.project.setProjectChanged(True)
if flush: if flush:
self.saveTreeOrder() self.saveTreeOrder()
if tHandle is None or tHandle not in self.theProject.tree: if tHandle is None or tHandle not in self.mainGui.project.tree:
return return
tItem = self.theProject.tree[tHandle] tItem = self.mainGui.project.tree[tHandle]
if tItem and tItem.isRootType(): if tItem and tItem.isRootType():
self.projView.rootFolderChanged.emit(tHandle) self.projView.rootFolderChanged.emit(tHandle)
+11 -12
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Main Window SideBar novelWriter GUI Main Window SideBar
===================================== =====================================
GUI class for the main window side bar
File History: File History:
Created: 2022-05-10 [2.0rc1] Created: 2022-05-10 [2.0rc1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -45,15 +45,14 @@ class GuiSideBar(QToolBar):
logger.debug("Create: GuiSideBar") logger.debug("Create: GuiSideBar")
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
# Style # Style
iPx = CONFIG.pxInt(22) iPx = CONFIG.pxInt(22)
mPx = CONFIG.pxInt(60) mPx = CONFIG.pxInt(60)
lblFont = self.mainTheme.guiFont lblFont = CONFIG.theme.guiFont
lblFont.setPointSizeF(0.65*self.mainTheme.fontPointSize) lblFont.setPointSizeF(0.65*CONFIG.theme.fontPointSize)
self.setMovable(False) self.setMovable(False)
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
@@ -131,13 +130,13 @@ class GuiSideBar(QToolBar):
""" """
self.setStyleSheet("QToolBar {border: 0px;}") self.setStyleSheet("QToolBar {border: 0px;}")
self.aProject.setIcon(self.mainTheme.getIcon("view_editor")) self.aProject.setIcon(CONFIG.theme.getIcon("view_editor"))
self.aNovel.setIcon(self.mainTheme.getIcon("view_novel")) self.aNovel.setIcon(CONFIG.theme.getIcon("view_novel"))
self.aOutline.setIcon(self.mainTheme.getIcon("view_outline")) self.aOutline.setIcon(CONFIG.theme.getIcon("view_outline"))
self.aBuild.setIcon(self.mainTheme.getIcon("view_build")) self.aBuild.setIcon(CONFIG.theme.getIcon("view_build"))
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details")) self.aDetails.setIcon(CONFIG.theme.getIcon("proj_details"))
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats")) self.aStats.setIcon(CONFIG.theme.getIcon("proj_stats"))
self.tbSettings.setIcon(self.mainTheme.getIcon("settings")) self.tbSettings.setIcon(CONFIG.theme.getIcon("settings"))
return return
+12 -13
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI Main Window Status Bar novelWriter GUI Main Window Status Bar
======================================== ========================================
GUI class for the main window status bar
File History: File History:
Created: 2019-04-20 [0.0.1] GuiMainStatus Created: 2019-04-20 [0.0.1] GuiMainStatus
@@ -23,6 +22,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import logging import logging
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import qApp, QStatusBar, QLabel
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.common import formatTime from novelwriter.common import formatTime
from novelwriter.gui.components import StatusLED from novelwriter.extensions.statusled import StatusLED
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -47,15 +47,14 @@ class GuiMainStatus(QStatusBar):
logger.debug("Create: GuiMainStatus") logger.debug("Create: GuiMainStatus")
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.refTime = None self.refTime = None
self.userIdle = False self.userIdle = False
colNone = QColor(*self.mainTheme.statNone) colNone = QColor(*CONFIG.theme.statNone)
colSaved = QColor(*self.mainTheme.statSaved) colSaved = QColor(*CONFIG.theme.statSaved)
colUnsaved = QColor(*self.mainTheme.statUnsaved) colUnsaved = QColor(*CONFIG.theme.statUnsaved)
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
# Permanent Widgets # Permanent Widgets
# ================= # =================
@@ -99,7 +98,7 @@ class GuiMainStatus(QStatusBar):
self.timeIcon = QLabel() self.timeIcon = QLabel()
self.timeText = QLabel("") self.timeText = QLabel("")
self.timeText.setToolTip(self.tr("Session Time")) self.timeText.setToolTip(self.tr("Session Time"))
self.timeText.setMinimumWidth(self.mainTheme.getTextWidth("00:00:00:")) self.timeText.setMinimumWidth(CONFIG.theme.getTextWidth("00:00:00:"))
self.timeIcon.setContentsMargins(0, 0, 0, 0) self.timeIcon.setContentsMargins(0, 0, 0, 0)
self.timeText.setContentsMargins(0, 0, 0, 0) self.timeText.setContentsMargins(0, 0, 0, 0)
self.addPermanentWidget(self.timeIcon) self.addPermanentWidget(self.timeIcon)
@@ -129,13 +128,13 @@ class GuiMainStatus(QStatusBar):
def updateTheme(self): def updateTheme(self):
"""Update theme elements. """Update theme elements.
""" """
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx))) self.langIcon.setPixmap(CONFIG.theme.getPixmap("status_lang", (iPx, iPx)))
self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx))) self.statsIcon.setPixmap(CONFIG.theme.getPixmap("status_stats", (iPx, iPx)))
self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx)) self.timePixmap = CONFIG.theme.getPixmap("status_time", (iPx, iPx))
self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx)) self.idlePixmap = CONFIG.theme.getPixmap("status_idle", (iPx, iPx))
self.timeIcon.setPixmap(self.timePixmap) self.timeIcon.setPixmap(self.timePixmap)
-1
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Theme and Icons Classes novelWriter Theme and Icons Classes
===================================== =====================================
Classes managing and caching themes and icons
File History: File History:
Created: 2019-05-18 [0.1.3] GuiTheme Created: 2019-05-18 [0.1.3] GuiTheme
+52 -43
View File
@@ -113,8 +113,8 @@ class GuiMain(QMainWindow):
# ============ # ============
# Core Classes # Core Classes
self.mainTheme = GuiTheme() CONFIG.setThemeInstance(GuiTheme())
self.theProject = NWProject(self) self._project = NWProject(self)
# Core Settings # Core Settings
self.hasProject = False self.hasProject = False
@@ -135,7 +135,7 @@ class GuiMain(QMainWindow):
# ============= # =============
# Sizes # Sizes
iPx = self.mainTheme.fontPixelSize iPx = CONFIG.theme.fontPixelSize
mPx = CONFIG.pxInt(4) mPx = CONFIG.pxInt(4)
hWd = CONFIG.pxInt(4) hWd = CONFIG.pxInt(4)
@@ -238,7 +238,7 @@ class GuiMain(QMainWindow):
# Connect Signals # Connect Signals
# =============== # ===============
self.theProject.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus) self._project.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus)
self.viewsBar.viewChangeRequested.connect(self._changeView) self.viewsBar.viewChangeRequested.connect(self._changeView)
@@ -307,10 +307,10 @@ class GuiMain(QMainWindow):
# Cache Alert Pixmaps # Cache Alert Pixmaps
pxSize = (2*iPx, 2*iPx) pxSize = (2*iPx, 2*iPx)
self.alertPix: dict[nwAlert, QPixmap] = { self.alertPix: dict[nwAlert, QPixmap] = {
nwAlert.INFO: self.mainTheme.getPixmap("alert_info", pxSize), nwAlert.INFO: CONFIG.theme.getPixmap("alert_info", pxSize),
nwAlert.WARN: self.mainTheme.getPixmap("alert_warn", pxSize), nwAlert.WARN: CONFIG.theme.getPixmap("alert_warn", pxSize),
nwAlert.ERROR: self.mainTheme.getPixmap("alert_error", pxSize), nwAlert.ERROR: CONFIG.theme.getPixmap("alert_error", pxSize),
nwAlert.ASK: self.mainTheme.getPixmap("alert_question", pxSize), nwAlert.ASK: CONFIG.theme.getPixmap("alert_question", pxSize),
} }
# Check that config loaded fine # Check that config loaded fine
@@ -382,6 +382,15 @@ class GuiMain(QMainWindow):
return return
##
# Properties
##
@property
def project(self) -> NWProject:
"""The project instance."""
return self._project
## ##
# Project Actions # Project Actions
## ##
@@ -444,7 +453,7 @@ class GuiMain(QMainWindow):
saveOK = self.saveProject() saveOK = self.saveProject()
doBackup = False doBackup = False
if self.theProject.data.doBackup and CONFIG.backupOnClose: if self._project.data.doBackup and CONFIG.backupOnClose:
doBackup = True doBackup = True
if CONFIG.askBeforeBackup: if CONFIG.askBeforeBackup:
msgYes = self.askQuestion(self.tr("Backup the current project?")) msgYes = self.askQuestion(self.tr("Backup the current project?"))
@@ -452,7 +461,7 @@ class GuiMain(QMainWindow):
doBackup = False doBackup = False
if doBackup: if doBackup:
self.theProject.backupProject(False) self._project.backupProject(False)
if saveOK: if saveOK:
self.closeDocument() self.closeDocument()
@@ -460,7 +469,7 @@ class GuiMain(QMainWindow):
self.outlineView.closeProjectTasks() self.outlineView.closeProjectTasks()
self.novelView.closeProjectTasks() self.novelView.closeProjectTasks()
self.theProject.closeProject(self.idleTime) self._project.closeProject(self.idleTime)
self.idleRefTime = time() self.idleRefTime = time()
self.idleTime = 0.0 self.idleTime = 0.0
@@ -484,9 +493,9 @@ class GuiMain(QMainWindow):
self._changeView(nwView.PROJECT) self._changeView(nwView.PROJECT)
# Try to open the project # Try to open the project
if not self.theProject.openProject(projFile): if not self._project.openProject(projFile):
# The project open failed. # The project open failed.
lockStatus = self.theProject.getLockStatus() lockStatus = self._project.getLockStatus()
if lockStatus is None: if lockStatus is None:
# The project is not locked, so failed for some other # The project is not locked, so failed for some other
# reason handled by the project class. # reason handled by the project class.
@@ -516,7 +525,7 @@ class GuiMain(QMainWindow):
lockDetails = "" lockDetails = ""
if self.askQuestion(lockText, info=lockInfo, details=lockDetails, level=nwAlert.WARN): if self.askQuestion(lockText, info=lockInfo, details=lockDetails, level=nwAlert.WARN):
if not self.theProject.openProject(projFile, overrideLock=True): if not self._project.openProject(projFile, overrideLock=True):
return False return False
else: else:
return False return False
@@ -527,11 +536,11 @@ class GuiMain(QMainWindow):
self.idleTime = 0.0 self.idleTime = 0.0
# Update GUI # Update GUI
self._updateWindowTitle(self.theProject.data.name) self._updateWindowTitle(self._project.data.name)
self.rebuildTrees() self.rebuildTrees()
self.docEditor.setDictionaries() self.docEditor.setDictionaries()
self.docEditor.toggleSpellCheck(self.theProject.data.spellCheck) self.docEditor.toggleSpellCheck(self._project.data.spellCheck)
self.mainStatus.setRefTime(self.theProject.projOpened) self.mainStatus.setRefTime(self._project.projOpened)
self.projView.openProjectTasks() self.projView.openProjectTasks()
self.novelView.openProjectTasks() self.novelView.openProjectTasks()
self.outlineView.openProjectTasks() self.outlineView.openProjectTasks()
@@ -539,9 +548,9 @@ class GuiMain(QMainWindow):
# Restore previously open documents, if any # Restore previously open documents, if any
# If none was recorded, open the first document found # If none was recorded, open the first document found
lastEdited = self.theProject.data.getLastHandle("editor") lastEdited = self._project.data.getLastHandle("editor")
if lastEdited is None: if lastEdited is None:
for nwItem in self.theProject.tree: for nwItem in self._project.tree:
if nwItem and nwItem.isFileType(): if nwItem and nwItem.isFileType():
lastEdited = nwItem.itemHandle lastEdited = nwItem.itemHandle
break break
@@ -549,19 +558,19 @@ class GuiMain(QMainWindow):
if lastEdited is not None: if lastEdited is not None:
self.openDocument(lastEdited, doScroll=True) self.openDocument(lastEdited, doScroll=True)
lastViewed = self.theProject.data.getLastHandle("viewer") lastViewed = self._project.data.getLastHandle("viewer")
if lastViewed is not None: if lastViewed is not None:
self.viewDocument(lastViewed) self.viewDocument(lastViewed)
# Check if we need to rebuild the index # Check if we need to rebuild the index
if self.theProject.index.indexBroken: if self._project.index.indexBroken:
self.makeAlert(self.tr("The project index is outdated or broken. Rebuilding index.")) self.makeAlert(self.tr("The project index is outdated or broken. Rebuilding index."))
self.rebuildIndex() self.rebuildIndex()
# Make sure the changed status is set to false on things opened # Make sure the changed status is set to false on things opened
qApp.processEvents() qApp.processEvents()
self.docEditor.setDocumentChanged(False) self.docEditor.setDocumentChanged(False)
self.theProject.setProjectChanged(False) self._project.setProjectChanged(False)
logger.debug("Project load complete") logger.debug("Project load complete")
@@ -573,7 +582,7 @@ class GuiMain(QMainWindow):
logger.error("No project open") logger.error("No project open")
return False return False
self.projView.saveProjectTasks() self.projView.saveProjectTasks()
self.theProject.saveProject(autoSave=autoSave) self._project.saveProject(autoSave=autoSave)
return True return True
## ##
@@ -606,7 +615,7 @@ class GuiMain(QMainWindow):
logger.error("No project open") logger.error("No project open")
return False return False
if not tHandle or not self.theProject.tree.checkType(tHandle, nwItemType.FILE): if not tHandle or not self._project.tree.checkType(tHandle, nwItemType.FILE):
logger.debug("Requested item '%s' is not a document", tHandle) logger.debug("Requested item '%s' is not a document", tHandle)
return False return False
@@ -620,7 +629,7 @@ class GuiMain(QMainWindow):
self.closeDocument(beforeOpen=True) self.closeDocument(beforeOpen=True)
if self.docEditor.loadText(tHandle, tLine): if self.docEditor.loadText(tHandle, tLine):
self.theProject.data.setLastHandle(tHandle, "editor") self._project.data.setLastHandle(tHandle, "editor")
self.projView.setSelectedHandle(tHandle, doScroll=doScroll) self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
self.novelView.setActiveHandle(tHandle) self.novelView.setActiveHandle(tHandle)
if changeFocus: if changeFocus:
@@ -641,7 +650,7 @@ class GuiMain(QMainWindow):
nHandle = None # The next handle after tHandle nHandle = None # The next handle after tHandle
fHandle = None # The first file handle we encounter fHandle = None # The first file handle we encounter
foundIt = False # We've found tHandle, pick the next we see foundIt = False # We've found tHandle, pick the next we see
for tItem in self.theProject.tree: for tItem in self._project.tree:
if not tItem.isFileType(): if not tItem.isFileType():
continue continue
if fHandle is None: if fHandle is None:
@@ -687,7 +696,7 @@ class GuiMain(QMainWindow):
tHandle = self.projView.getSelectedHandle() tHandle = self.projView.getSelectedHandle()
if tHandle is None: if tHandle is None:
tHandle = self.theProject.data.getLastHandle("viewer") tHandle = self._project.data.getLastHandle("viewer")
if tHandle is None: if tHandle is None:
logger.debug("No document to view, giving up") logger.debug("No document to view, giving up")
@@ -806,7 +815,7 @@ class GuiMain(QMainWindow):
return False return False
if tHandle is not None and sTitle is not None: if tHandle is not None and sTitle is not None:
hItem = self.theProject.index.getItemHeader(tHandle, sTitle) hItem = self._project.index.getItemHeader(tHandle, sTitle)
if hItem is not None: if hItem is not None:
tLine = hItem.line tLine = hItem.line
@@ -843,7 +852,7 @@ class GuiMain(QMainWindow):
tStart = time() tStart = time()
self.projView.saveProjectTasks() self.projView.saveProjectTasks()
self.theProject.index.rebuildIndex() self._project.index.rebuildIndex()
self.projView.populateTree() self.projView.populateTree()
self.novelView.refreshTree() self.novelView.refreshTree()
@@ -912,7 +921,7 @@ class GuiMain(QMainWindow):
if dlgConf.updateTheme: if dlgConf.updateTheme:
# We are doing this manually instead of connecting to # We are doing this manually instead of connecting to
# qApp.paletteChanged since the processing order matters # qApp.paletteChanged since the processing order matters
self.mainTheme.loadTheme() CONFIG.theme.loadTheme()
self.docEditor.updateTheme() self.docEditor.updateTheme()
self.docViewer.updateTheme() self.docViewer.updateTheme()
self.viewsBar.updateTheme() self.viewsBar.updateTheme()
@@ -923,7 +932,7 @@ class GuiMain(QMainWindow):
self.mainStatus.updateTheme() self.mainStatus.updateTheme()
if dlgConf.updateSyntax: if dlgConf.updateSyntax:
self.mainTheme.loadSyntax() CONFIG.theme.loadSyntax()
self.docEditor.updateSyntaxColours() self.docEditor.updateSyntaxColours()
self.docEditor.initEditor() self.docEditor.initEditor()
@@ -951,7 +960,7 @@ class GuiMain(QMainWindow):
if dlgProj.spellChanged: if dlgProj.spellChanged:
self.docEditor.setDictionaries() self.docEditor.setDictionaries()
self.itemDetails.refreshDetails() self.itemDetails.refreshDetails()
self._updateWindowTitle(self.theProject.data.name) self._updateWindowTitle(self._project.data.name)
return True return True
@@ -1197,7 +1206,7 @@ class GuiMain(QMainWindow):
def closeDocEditor(self) -> None: def closeDocEditor(self) -> None:
"""Close the document editor. This does not hide the editor.""" """Close the document editor. This does not hide the editor."""
self.closeDocument() self.closeDocument()
self.theProject.data.setLastHandle(None, "editor") self._project.data.setLastHandle(None, "editor")
return return
def closeDocViewer(self, byUser: bool = True) -> bool: def closeDocViewer(self, byUser: bool = True) -> bool:
@@ -1205,7 +1214,7 @@ class GuiMain(QMainWindow):
self.docViewer.clearViewer() self.docViewer.clearViewer()
if byUser: if byUser:
# Only reset the last handle if the user called this # Only reset the last handle if the user called this
self.theProject.data.setLastHandle(None, "viewer") self._project.data.setLastHandle(None, "viewer")
# Hide the panel # Hide the panel
bPos = self.splitMain.sizes() bPos = self.splitMain.sizes()
@@ -1391,7 +1400,7 @@ class GuiMain(QMainWindow):
"""Handle the index lookup of a tag and display an alert if the """Handle the index lookup of a tag and display an alert if the
tag cannot be found. tag cannot be found.
""" """
tHandle, sTitle = self.theProject.index.getTagSource(tag) tHandle, sTitle = self._project.index.getTagSource(tag)
if tHandle is None: if tHandle is None:
self.makeAlert(self.tr( self.makeAlert(self.tr(
"Could not find the reference for tag '{0}'. It either doesn't " "Could not find the reference for tag '{0}'. It either doesn't "
@@ -1438,7 +1447,7 @@ class GuiMain(QMainWindow):
if tHandle is not None: if tHandle is not None:
if mode == nwDocMode.EDIT: if mode == nwDocMode.EDIT:
tLine = None tLine = None
hItem = self.theProject.index.getItemHeader(tHandle, sTitle) hItem = self._project.index.getItemHeader(tHandle, sTitle)
if hItem is not None: if hItem is not None:
tLine = hItem.line tLine = hItem.line
self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus) self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus)
@@ -1491,8 +1500,8 @@ class GuiMain(QMainWindow):
def _autoSaveProject(self) -> None: def _autoSaveProject(self) -> None:
"""Autosave of the project. This is a timer-activated slot.""" """Autosave of the project. This is a timer-activated slot."""
doSave = self.hasProject doSave = self.hasProject
doSave &= self.theProject.projChanged doSave &= self._project.projChanged
doSave &= self.theProject.storage.isOpen() doSave &= self._project.storage.isOpen()
if doSave: if doSave:
logger.debug("Autosaving project") logger.debug("Autosaving project")
self.saveProject(autoSave=True) self.saveProject(autoSave=True)
@@ -1512,14 +1521,14 @@ class GuiMain(QMainWindow):
if not self.hasProject: if not self.hasProject:
self.mainStatus.setProjectStats(0, 0) self.mainStatus.setProjectStats(0, 0)
self.theProject.updateWordCounts() self._project.updateWordCounts()
if CONFIG.incNotesWCount: if CONFIG.incNotesWCount:
iTotal = sum(self.theProject.data.initCounts) iTotal = sum(self._project.data.initCounts)
cTotal = sum(self.theProject.data.currCounts) cTotal = sum(self._project.data.currCounts)
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal) self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
else: else:
iNovel, _ = self.theProject.data.initCounts iNovel, _ = self._project.data.initCounts
cNovel, _ = self.theProject.data.currCounts cNovel, _ = self._project.data.currCounts
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel) self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
return return
+3 -4
View File
@@ -1,7 +1,6 @@
""" """
novelWriter Lorem Ipsum Tool novelWriter Lorem Ipsum Tool
============================== ==============================
Simple tool for inserting placeholder text in a document
File History: File History:
Created: 2022-04-02 [2.0rc1] Created: 2022-04-02 [2.0rc1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import random import random
import logging import logging
@@ -49,8 +49,7 @@ class GuiLipsum(QDialog):
if CONFIG.osDarwin: if CONFIG.osDarwin:
self.setWindowFlag(Qt.WindowType.Tool) self.setWindowFlag(Qt.WindowType.Tool)
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.setWindowTitle(self.tr("Insert Placeholder Text")) self.setWindowTitle(self.tr("Insert Placeholder Text"))
@@ -61,7 +60,7 @@ class GuiLipsum(QDialog):
nPx = CONFIG.pxInt(64) nPx = CONFIG.pxInt(64)
vSp = CONFIG.pxInt(4) vSp = CONFIG.pxInt(4)
self.docIcon = QLabel() self.docIcon = QLabel()
self.docIcon.setPixmap(self.mainTheme.getPixmap("proj_document", (nPx, nPx))) self.docIcon.setPixmap(CONFIG.theme.getPixmap("proj_document", (nPx, nPx)))
self.leftBox = QVBoxLayout() self.leftBox = QVBoxLayout()
self.leftBox.setSpacing(vSp) self.leftBox.setSpacing(vSp)
+13 -15
View File
@@ -65,9 +65,7 @@ class GuiManuscriptBuild(QDialog):
logger.debug("Create: GuiManuscriptBuild") logger.debug("Create: GuiManuscriptBuild")
self.setObjectName("GuiManuscriptBuild") self.setObjectName("GuiManuscriptBuild")
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._parent = parent self._parent = parent
self._build = build self._build = build
@@ -76,14 +74,14 @@ class GuiManuscriptBuild(QDialog):
self.setMinimumWidth(CONFIG.pxInt(500)) self.setMinimumWidth(CONFIG.pxInt(500))
self.setMinimumHeight(CONFIG.pxInt(300)) self.setMinimumHeight(CONFIG.pxInt(300))
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
sp4 = CONFIG.pxInt(4) sp4 = CONFIG.pxInt(4)
sp8 = CONFIG.pxInt(8) sp8 = CONFIG.pxInt(8)
sp16 = CONFIG.pxInt(16) sp16 = CONFIG.pxInt(16)
wWin = CONFIG.pxInt(620) wWin = CONFIG.pxInt(620)
hWin = CONFIG.pxInt(360) hWin = CONFIG.pxInt(360)
pOptions = self.theProject.options pOptions = self.mainGui.project.options
self.resize( self.resize(
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winWidth", wWin)), CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winHeight", hWin)) CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winHeight", hWin))
@@ -148,7 +146,7 @@ class GuiManuscriptBuild(QDialog):
# Build Path # Build Path
self.lblPath = QLabel(self.tr("Path")) self.lblPath = QLabel(self.tr("Path"))
self.buildPath = QLineEdit(self) self.buildPath = QLineEdit(self)
self.btnBrowse = QPushButton(self.mainTheme.getIcon("browse"), "") self.btnBrowse = QPushButton(CONFIG.theme.getIcon("browse"), "")
self.pathBox = QHBoxLayout() self.pathBox = QHBoxLayout()
self.pathBox.addWidget(self.buildPath) self.pathBox.addWidget(self.buildPath)
@@ -158,7 +156,7 @@ class GuiManuscriptBuild(QDialog):
# Build Name # Build Name
self.lblName = QLabel(self.tr("File Name")) self.lblName = QLabel(self.tr("File Name"))
self.buildName = QLineEdit(self) self.buildName = QLineEdit(self)
self.btnReset = QPushButton(self.mainTheme.getIcon("revert"), "") self.btnReset = QPushButton(CONFIG.theme.getIcon("revert"), "")
self.btnReset.setToolTip(self.tr("Reset file name to default")) self.btnReset.setToolTip(self.tr("Reset file name to default"))
self.nameBox = QHBoxLayout() self.nameBox = QHBoxLayout()
@@ -183,7 +181,7 @@ class GuiManuscriptBuild(QDialog):
self.buildBox.setVerticalSpacing(sp4) self.buildBox.setVerticalSpacing(sp4)
# Dialog Buttons # Dialog Buttons
self.btnBuild = QPushButton(self.mainTheme.getIcon("export"), self.tr("&Build")) self.btnBuild = QPushButton(CONFIG.theme.getIcon("export"), self.tr("&Build"))
self.dlgButtons = QDialogButtonBox(QDialogButtonBox.Close) self.dlgButtons = QDialogButtonBox(QDialogButtonBox.Close)
self.dlgButtons.addButton(self.btnBuild, QDialogButtonBox.ActionRole) self.dlgButtons.addButton(self.btnBuild, QDialogButtonBox.ActionRole)
@@ -281,7 +279,7 @@ class GuiManuscriptBuild(QDialog):
@pyqtSlot() @pyqtSlot()
def _doResetBuildName(self): def _doResetBuildName(self):
"""Generate a default build name.""" """Generate a default build name."""
bName = f"{self.theProject.data.name} - {self._build.name}" bName = f"{self.mainGui.project.data.name} - {self._build.name}"
self.buildName.setText(bName) self.buildName.setText(bName)
self._build.setLastBuildName(bName) self._build.setLastBuildName(bName)
return return
@@ -322,7 +320,7 @@ class GuiManuscriptBuild(QDialog):
): ):
return False return False
docBuild = NWBuildDocument(self.theProject, self._build) docBuild = NWBuildDocument(self.mainGui.project, self._build)
docBuild.queueAll() docBuild.queueAll()
self.buildProgress.setMaximum(len(docBuild)) self.buildProgress.setMaximum(len(docBuild))
@@ -355,7 +353,7 @@ class GuiManuscriptBuild(QDialog):
fmtWidth = CONFIG.rpxInt(mainSplit[0]) fmtWidth = CONFIG.rpxInt(mainSplit[0])
sumWidth = CONFIG.rpxInt(mainSplit[1]) sumWidth = CONFIG.rpxInt(mainSplit[1])
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth) pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth)
pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight) pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight)
pOptions.setValue("GuiManuscriptBuild", "fmtWidth", fmtWidth) pOptions.setValue("GuiManuscriptBuild", "fmtWidth", fmtWidth)
@@ -367,9 +365,9 @@ class GuiManuscriptBuild(QDialog):
def _populateContentList(self): def _populateContentList(self):
"""Build the content list.""" """Build the content list."""
rootMap = {} rootMap = {}
filtered = self._build.buildItemFilter(self.theProject) filtered = self._build.buildItemFilter(self.mainGui.project)
self.listContent.clear() self.listContent.clear()
for nwItem in self.theProject.tree: for nwItem in self.mainGui.project.tree:
tHandle = nwItem.itemHandle tHandle = nwItem.itemHandle
rHandle = nwItem.itemRoot rHandle = nwItem.itemRoot
@@ -378,11 +376,11 @@ class GuiManuscriptBuild(QDialog):
if filtered.get(tHandle, (False, 0))[0]: if filtered.get(tHandle, (False, 0))[0]:
if rHandle not in rootMap: if rHandle not in rootMap:
rItem = self.theProject.tree[rHandle] rItem = self.mainGui.project.tree[rHandle]
if isinstance(rItem, NWItem): if isinstance(rItem, NWItem):
rootMap[rHandle] = rItem.itemName rootMap[rHandle] = rItem.itemName
itemIcon = self.mainTheme.getItemIcon( itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemType, nwItem.itemClass,
nwItem.itemLayout, nwItem.mainHeading nwItem.itemLayout, nwItem.mainHeading
) )
+20 -24
View File
@@ -72,22 +72,20 @@ class GuiManuscript(QDialog):
if CONFIG.osDarwin: if CONFIG.osDarwin:
self.setWindowFlag(Qt.WindowType.Tool) self.setWindowFlag(Qt.WindowType.Tool)
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._builds = BuildCollection(self.theProject) self._builds = BuildCollection(self.mainGui.project)
self._buildMap: dict[str, QListWidgetItem] = {} self._buildMap: dict[str, QListWidgetItem] = {}
self.setWindowTitle(self.tr("Build Manuscript")) self.setWindowTitle(self.tr("Build Manuscript"))
self.setMinimumWidth(CONFIG.pxInt(600)) self.setMinimumWidth(CONFIG.pxInt(600))
self.setMinimumHeight(CONFIG.pxInt(500)) self.setMinimumHeight(CONFIG.pxInt(500))
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
wWin = CONFIG.pxInt(900) wWin = CONFIG.pxInt(900)
hWin = CONFIG.pxInt(600) hWin = CONFIG.pxInt(600)
pOptions = self.theProject.options pOptions = self.mainGui.project.options
self.resize( self.resize(
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winWidth", wWin)), CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winHeight", hWin)) CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winHeight", hWin))
@@ -107,21 +105,21 @@ class GuiManuscript(QDialog):
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue()) ).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
self.tbAdd = QToolButton(self) self.tbAdd = QToolButton(self)
self.tbAdd.setIcon(self.mainTheme.getIcon("add")) self.tbAdd.setIcon(CONFIG.theme.getIcon("add"))
self.tbAdd.setIconSize(QSize(iPx, iPx)) self.tbAdd.setIconSize(QSize(iPx, iPx))
self.tbAdd.setToolTip(self.tr("Add New Build")) self.tbAdd.setToolTip(self.tr("Add New Build"))
self.tbAdd.setStyleSheet(buttonStyle) self.tbAdd.setStyleSheet(buttonStyle)
self.tbAdd.clicked.connect(self._createNewBuild) self.tbAdd.clicked.connect(self._createNewBuild)
self.tbDel = QToolButton(self) self.tbDel = QToolButton(self)
self.tbDel.setIcon(self.mainTheme.getIcon("remove")) self.tbDel.setIcon(CONFIG.theme.getIcon("remove"))
self.tbDel.setIconSize(QSize(iPx, iPx)) self.tbDel.setIconSize(QSize(iPx, iPx))
self.tbDel.setToolTip(self.tr("Delete Selected Build")) self.tbDel.setToolTip(self.tr("Delete Selected Build"))
self.tbDel.setStyleSheet(buttonStyle) self.tbDel.setStyleSheet(buttonStyle)
self.tbDel.clicked.connect(self._deleteSelectedBuild) self.tbDel.clicked.connect(self._deleteSelectedBuild)
self.tbEdit = QToolButton(self) self.tbEdit = QToolButton(self)
self.tbEdit.setIcon(self.mainTheme.getIcon("edit")) self.tbEdit.setIcon(CONFIG.theme.getIcon("edit"))
self.tbEdit.setIconSize(QSize(iPx, iPx)) self.tbEdit.setIconSize(QSize(iPx, iPx))
self.tbEdit.setToolTip(self.tr("Edit Selected Build")) self.tbEdit.setToolTip(self.tr("Edit Selected Build"))
self.tbEdit.setStyleSheet(buttonStyle) self.tbEdit.setStyleSheet(buttonStyle)
@@ -212,7 +210,7 @@ class GuiManuscript(QDialog):
self._updateBuildsList() self._updateBuildsList()
logger.debug("Loading build cache") logger.debug("Loading build cache")
cache = CONFIG.dataPath("cache") / f"build_{self.theProject.data.uuid}.json" cache = CONFIG.dataPath("cache") / f"build_{self.mainGui.project.data.uuid}.json"
if cache.is_file(): if cache.is_file():
try: try:
with open(cache, mode="r", encoding="utf-8") as fObj: with open(cache, mode="r", encoding="utf-8") as fObj:
@@ -291,7 +289,7 @@ class GuiManuscript(QDialog):
if build is None: if build is None:
return return
docBuild = NWBuildDocument(self.theProject, build) docBuild = NWBuildDocument(self.mainGui.project, build)
docBuild.queueAll() docBuild.queueAll()
self.docPreview.beginNewBuild(len(docBuild)) self.docPreview.beginNewBuild(len(docBuild))
@@ -311,7 +309,7 @@ class GuiManuscript(QDialog):
self._updatePreview(result, build) self._updatePreview(result, build)
logger.debug("Saving build cache") logger.debug("Saving build cache")
cache = CONFIG.dataPath("cache") / f"build_{self.theProject.data.uuid}.json" cache = CONFIG.dataPath("cache") / f"build_{self.mainGui.project.data.uuid}.json"
try: try:
with open(cache, mode="w+", encoding="utf-8") as outFile: with open(cache, mode="w+", encoding="utf-8") as outFile:
outFile.write(json.dumps(result, indent=2)) outFile.write(json.dumps(result, indent=2))
@@ -392,7 +390,7 @@ class GuiManuscript(QDialog):
optsWidth = CONFIG.rpxInt(mainSplit[0]) optsWidth = CONFIG.rpxInt(mainSplit[0])
viewWidth = CONFIG.rpxInt(mainSplit[1]) viewWidth = CONFIG.rpxInt(mainSplit[1])
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiManuscript", "winWidth", winWidth) pOptions.setValue("GuiManuscript", "winWidth", winWidth)
pOptions.setValue("GuiManuscript", "winHeight", winHeight) pOptions.setValue("GuiManuscript", "winHeight", winHeight)
pOptions.setValue("GuiManuscript", "optsWidth", optsWidth) pOptions.setValue("GuiManuscript", "optsWidth", optsWidth)
@@ -428,7 +426,7 @@ class GuiManuscript(QDialog):
for key, name in self._builds.builds(): for key, name in self._builds.builds():
bItem = QListWidgetItem() bItem = QListWidgetItem()
bItem.setText(name) bItem.setText(name)
bItem.setIcon(self.mainTheme.getIcon("export")) bItem.setIcon(CONFIG.theme.getIcon("export"))
bItem.setData(self.D_KEY, key) bItem.setData(self.D_KEY, key)
self.buildList.addItem(bItem) self.buildList.addItem(bItem)
self._buildMap[key] = bItem self._buildMap[key] = bItem
@@ -451,9 +449,7 @@ class _PreviewWidget(QTextBrowser):
def __init__(self, mainGui: GuiMain): def __init__(self, mainGui: GuiMain):
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._docTime = 0 self._docTime = 0
self._buildName = "" self._buildName = ""
@@ -464,7 +460,7 @@ class _PreviewWidget(QTextBrowser):
dPalette.setColor(QPalette.Text, QColor(0, 0, 0)) dPalette.setColor(QPalette.Text, QColor(0, 0, 0))
self.setPalette(dPalette) self.setPalette(dPalette)
self.setMinimumWidth(40*self.mainGui.mainTheme.textNWidth) self.setMinimumWidth(40*CONFIG.theme.textNWidth)
self.setTextFont(CONFIG.textFont, CONFIG.textSize) self.setTextFont(CONFIG.textFont, CONFIG.textSize)
self.setTabStopDistance(CONFIG.getTabWidth()) self.setTabStopDistance(CONFIG.getTabWidth())
self.setOpenExternalLinks(False) self.setOpenExternalLinks(False)
@@ -482,7 +478,7 @@ class _PreviewWidget(QTextBrowser):
aPalette.setColor(QPalette.Foreground, aPalette.toolTipText().color()) aPalette.setColor(QPalette.Foreground, aPalette.toolTipText().color())
aFont = self.font() aFont = self.font()
aFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) aFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.ageLabel = QLabel("", self) self.ageLabel = QLabel("", self)
self.ageLabel.setIndent(0) self.ageLabel.setIndent(0)
@@ -490,7 +486,7 @@ class _PreviewWidget(QTextBrowser):
self.ageLabel.setPalette(aPalette) self.ageLabel.setPalette(aPalette)
self.ageLabel.setAutoFillBackground(True) self.ageLabel.setAutoFillBackground(True)
self.ageLabel.setAlignment(Qt.AlignCenter) self.ageLabel.setAlignment(Qt.AlignCenter)
self.ageLabel.setFixedHeight(int(2.1*self.mainTheme.fontPixelSize)) self.ageLabel.setFixedHeight(int(2.1*CONFIG.theme.fontPixelSize))
# Progress # Progress
self.buildProgress = NProgressCircle(self, CONFIG.pxInt(160), CONFIG.pxInt(16)) self.buildProgress = NProgressCircle(self, CONFIG.pxInt(160), CONFIG.pxInt(16))
@@ -526,12 +522,12 @@ class _PreviewWidget(QTextBrowser):
def setJustify(self, state: bool): def setJustify(self, state: bool):
"""Enable/disable the justify text option.""" """Enable/disable the justify text option."""
options = self.document().defaultTextOption() pOptions = self.document().defaultTextOption()
if state: if state:
options.setAlignment(Qt.AlignJustify) pOptions.setAlignment(Qt.AlignJustify)
else: else:
options.setAlignment(Qt.AlignAbsolute) pOptions.setAlignment(Qt.AlignAbsolute)
self.document().setDefaultTextOption(options) self.document().setDefaultTextOption(pOptions)
return return
def setTextFont(self, family: str, size: int): def setTextFont(self, family: str, size: int):
+39 -53
View File
@@ -49,7 +49,6 @@ from novelwriter.extensions.pagedsidebar import NPagedSideBar
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
from novelwriter.gui.theme import GuiTheme
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -77,9 +76,7 @@ class GuiBuildSettings(QDialog):
if CONFIG.osDarwin: if CONFIG.osDarwin:
self.setWindowFlag(Qt.WindowType.Tool) self.setWindowFlag(Qt.WindowType.Tool)
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._build = build self._build = build
@@ -91,7 +88,7 @@ class GuiBuildSettings(QDialog):
wWin = CONFIG.pxInt(750) wWin = CONFIG.pxInt(750)
hWin = CONFIG.pxInt(550) hWin = CONFIG.pxInt(550)
pOptions = self.theProject.options pOptions = self.mainGui.project.options
self.resize( self.resize(
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winWidth", wWin)), CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winHeight", hWin)) CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winHeight", hWin))
@@ -103,7 +100,7 @@ class GuiBuildSettings(QDialog):
self.optSideBar = NPagedSideBar(self) self.optSideBar = NPagedSideBar(self)
self.optSideBar.setMinimumWidth(mPx) self.optSideBar.setMinimumWidth(mPx)
self.optSideBar.setMaximumWidth(mPx) self.optSideBar.setMaximumWidth(mPx)
self.optSideBar.setLabelColor(self.mainTheme.helpText) self.optSideBar.setLabelColor(CONFIG.theme.helpText)
self.optSideBar.addLabel(self.tr("Options")) self.optSideBar.addLabel(self.tr("Options"))
self.optSideBar.addButton(self.tr("Selection"), self.OPT_FILTERS) self.optSideBar.addButton(self.tr("Selection"), self.OPT_FILTERS)
@@ -265,7 +262,7 @@ class GuiBuildSettings(QDialog):
treeWidth, filterWidth = self.optTabSelect.mainSplitSizes() treeWidth, filterWidth = self.optTabSelect.mainSplitSizes()
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiBuildSettings", "winWidth", winWidth) pOptions.setValue("GuiBuildSettings", "winWidth", winWidth)
pOptions.setValue("GuiBuildSettings", "winHeight", winHeight) pOptions.setValue("GuiBuildSettings", "winHeight", winHeight)
pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth) pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth)
@@ -306,18 +303,16 @@ class _FilterTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
self.mainGui = buildMain.mainGui self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self.theProject = buildMain.mainGui.theProject
self._treeMap: dict[str, QTreeWidgetItem] = {} self._treeMap: dict[str, QTreeWidgetItem] = {}
self._build = build self._build = build
self._statusFlags: dict[int, QIcon] = { self._statusFlags: dict[int, QIcon] = {
self.F_NONE: QIcon(), self.F_NONE: QIcon(),
self.F_FILTERED: self.mainTheme.getIcon("build_filtered"), self.F_FILTERED: CONFIG.theme.getIcon("build_filtered"),
self.F_INCLUDED: self.mainTheme.getIcon("build_included"), self.F_INCLUDED: CONFIG.theme.getIcon("build_included"),
self.F_EXCLUDED: self.mainTheme.getIcon("build_excluded"), self.F_EXCLUDED: CONFIG.theme.getIcon("build_excluded"),
} }
self._trIncluded = self.tr("Included in manuscript") self._trIncluded = self.tr("Included in manuscript")
@@ -327,7 +322,7 @@ class _FilterTab(QWidget):
# ============ # ============
# Tree Settings # Tree Settings
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
cMg = CONFIG.pxInt(6) cMg = CONFIG.pxInt(6)
# Tree Widget # Tree Widget
@@ -365,7 +360,7 @@ class _FilterTab(QWidget):
self.resetButton = QToolButton(self) self.resetButton = QToolButton(self)
self.resetButton.setToolTip(self.tr("Reset to default")) self.resetButton.setToolTip(self.tr("Reset to default"))
self.resetButton.setIcon(self.mainTheme.getIcon("revert")) self.resetButton.setIcon(CONFIG.theme.getIcon("revert"))
self.resetButton.clicked.connect(lambda: self._setSelectedMode(self.F_FILTERED)) self.resetButton.clicked.connect(lambda: self._setSelectedMode(self.F_FILTERED))
self.modeBox = QHBoxLayout() self.modeBox = QHBoxLayout()
@@ -384,7 +379,7 @@ class _FilterTab(QWidget):
# Assemble GUI # Assemble GUI
# ============ # ============
pOptions = self.theProject.options pOptions = self.mainGui.project.options
self.selectionBox = QVBoxLayout() self.selectionBox = QVBoxLayout()
self.selectionBox.addWidget(self.optTree) self.selectionBox.addWidget(self.optTree)
@@ -450,7 +445,7 @@ class _FilterTab(QWidget):
logger.debug("Building project tree") logger.debug("Building project tree")
self._treeMap = {} self._treeMap = {}
self.optTree.clear() self.optTree.clear()
for nwItem in self.theProject.getProjectItems(): for nwItem in self.mainGui.project.getProjectItems():
tHandle = nwItem.itemHandle tHandle = nwItem.itemHandle
pHandle = nwItem.itemParent pHandle = nwItem.itemParent
@@ -466,7 +461,7 @@ class _FilterTab(QWidget):
continue continue
hLevel = nwItem.mainHeading hLevel = nwItem.mainHeading
itemIcon = self.mainTheme.getItemIcon( itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
) )
@@ -480,7 +475,7 @@ class _FilterTab(QWidget):
trItem.setText(self.C_NAME, nwItem.itemName) trItem.setText(self.C_NAME, nwItem.itemName)
trItem.setData(self.C_DATA, self.D_HANDLE, tHandle) trItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
trItem.setData(self.C_DATA, self.D_FILE, isFile) trItem.setData(self.C_DATA, self.D_FILE, isFile)
trItem.setIcon(self.C_ACTIVE, self.mainTheme.getIcon(iconName)) trItem.setIcon(self.C_ACTIVE, CONFIG.theme.getIcon(iconName))
trItem.setTextAlignment(self.C_NAME, Qt.AlignLeft) trItem.setTextAlignment(self.C_NAME, Qt.AlignLeft)
@@ -504,19 +499,19 @@ class _FilterTab(QWidget):
self.filterOpt.clear() self.filterOpt.clear()
self.filterOpt.addLabel(self._build.getLabel("filter")) self.filterOpt.addLabel(self._build.getLabel("filter"))
self.filterOpt.addItem( self.filterOpt.addItem(
self.mainTheme.getIcon("proj_scene"), CONFIG.theme.getIcon("proj_scene"),
self._build.getLabel("filter.includeNovel"), self._build.getLabel("filter.includeNovel"),
"doc:filter.includeNovel", "doc:filter.includeNovel",
default=self._build.getBool("filter.includeNovel") default=self._build.getBool("filter.includeNovel")
) )
self.filterOpt.addItem( self.filterOpt.addItem(
self.mainTheme.getIcon("proj_note"), CONFIG.theme.getIcon("proj_note"),
self._build.getLabel("filter.includeNotes"), self._build.getLabel("filter.includeNotes"),
"doc:filter.includeNotes", "doc:filter.includeNotes",
default=self._build.getBool("filter.includeNotes") default=self._build.getBool("filter.includeNotes")
) )
self.filterOpt.addItem( self.filterOpt.addItem(
self.mainTheme.getIcon("unchecked"), CONFIG.theme.getIcon("unchecked"),
self._build.getLabel("filter.includeInactive"), self._build.getLabel("filter.includeInactive"),
"doc:filter.includeInactive", "doc:filter.includeInactive",
default=self._build.getBool("filter.includeInactive") default=self._build.getBool("filter.includeInactive")
@@ -526,9 +521,9 @@ class _FilterTab(QWidget):
# Root Classes # Root Classes
self.filterOpt.addLabel(self.tr("Select Root Folders")) self.filterOpt.addLabel(self.tr("Select Root Folders"))
for tHandle, nwItem in self.theProject.tree.iterRoots(None): for tHandle, nwItem in self.mainGui.project.tree.iterRoots(None):
if not nwItem.isInactiveClass(): if not nwItem.isInactiveClass():
itemIcon = self.mainTheme.getItemIcon( itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout nwItem.itemType, nwItem.itemClass, nwItem.itemLayout
) )
self.filterOpt.addItem( self.filterOpt.addItem(
@@ -562,7 +557,7 @@ class _FilterTab(QWidget):
def _setTreeItemMode(self) -> None: def _setTreeItemMode(self) -> None:
"""Update the filtered mode icon on all items.""" """Update the filtered mode icon on all items."""
filtered = self._build.buildItemFilter(self.theProject) filtered = self._build.buildItemFilter(self.mainGui.project)
for tHandle, item in self._treeMap.items(): for tHandle, item in self._treeMap.items():
allow, mode = filtered.get(tHandle, (False, FilterMode.UNKNOWN)) allow, mode = filtered.get(tHandle, (False, FilterMode.UNKNOWN))
if mode == FilterMode.INCLUDED: if mode == FilterMode.INCLUDED:
@@ -602,14 +597,12 @@ class _HeadingsTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
self.mainGui = buildMain.mainGui self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self.theProject = buildMain.mainGui.theProject
self._build = build self._build = build
self._editing = 0 self._editing = 0
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
vSp = CONFIG.pxInt(12) vSp = CONFIG.pxInt(12)
bSp = CONFIG.pxInt(6) bSp = CONFIG.pxInt(6)
@@ -623,7 +616,7 @@ class _HeadingsTab(QWidget):
self.fmtTitle = QLineEdit("") self.fmtTitle = QLineEdit("")
self.fmtTitle.setReadOnly(True) self.fmtTitle.setReadOnly(True)
self.btnTitle = QToolButton() self.btnTitle = QToolButton()
self.btnTitle.setIcon(self.mainTheme.getIcon("edit")) self.btnTitle.setIcon(CONFIG.theme.getIcon("edit"))
self.btnTitle.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE)) self.btnTitle.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE))
wrapTitle = QHBoxLayout() wrapTitle = QHBoxLayout()
@@ -639,7 +632,7 @@ class _HeadingsTab(QWidget):
self.fmtChapter = QLineEdit("") self.fmtChapter = QLineEdit("")
self.fmtChapter.setReadOnly(True) self.fmtChapter.setReadOnly(True)
self.btnChapter = QToolButton() self.btnChapter = QToolButton()
self.btnChapter.setIcon(self.mainTheme.getIcon("edit")) self.btnChapter.setIcon(CONFIG.theme.getIcon("edit"))
self.btnChapter.clicked.connect(lambda: self._editHeading(self.EDIT_CHAPTER)) self.btnChapter.clicked.connect(lambda: self._editHeading(self.EDIT_CHAPTER))
wrapChapter = QHBoxLayout() wrapChapter = QHBoxLayout()
@@ -655,7 +648,7 @@ class _HeadingsTab(QWidget):
self.fmtUnnumbered = QLineEdit("") self.fmtUnnumbered = QLineEdit("")
self.fmtUnnumbered.setReadOnly(True) self.fmtUnnumbered.setReadOnly(True)
self.btnUnnumbered = QToolButton() self.btnUnnumbered = QToolButton()
self.btnUnnumbered.setIcon(self.mainTheme.getIcon("edit")) self.btnUnnumbered.setIcon(CONFIG.theme.getIcon("edit"))
self.btnUnnumbered.clicked.connect(lambda: self._editHeading(self.EDIT_UNNUM)) self.btnUnnumbered.clicked.connect(lambda: self._editHeading(self.EDIT_UNNUM))
wrapUnnumbered = QHBoxLayout() wrapUnnumbered = QHBoxLayout()
@@ -672,7 +665,7 @@ class _HeadingsTab(QWidget):
self.fmtScene = QLineEdit("") self.fmtScene = QLineEdit("")
self.fmtScene.setReadOnly(True) self.fmtScene.setReadOnly(True)
self.btnScene = QToolButton() self.btnScene = QToolButton()
self.btnScene.setIcon(self.mainTheme.getIcon("edit")) self.btnScene.setIcon(CONFIG.theme.getIcon("edit"))
self.btnScene.clicked.connect(lambda: self._editHeading(self.EDIT_SCENE)) self.btnScene.clicked.connect(lambda: self._editHeading(self.EDIT_SCENE))
self.hdeScene = QLabel(self.tr("Hide")) self.hdeScene = QLabel(self.tr("Hide"))
self.hdeScene.setToolTip(sceneHideTip) self.hdeScene.setToolTip(sceneHideTip)
@@ -699,7 +692,7 @@ class _HeadingsTab(QWidget):
self.fmtSection = QLineEdit("") self.fmtSection = QLineEdit("")
self.fmtSection.setReadOnly(True) self.fmtSection.setReadOnly(True)
self.btnSection = QToolButton() self.btnSection = QToolButton()
self.btnSection.setIcon(self.mainTheme.getIcon("edit")) self.btnSection.setIcon(CONFIG.theme.getIcon("edit"))
self.btnSection.clicked.connect(lambda: self._editHeading(self.EDIT_SECTION)) self.btnSection.clicked.connect(lambda: self._editHeading(self.EDIT_SECTION))
self.hdeSection = QLabel(self.tr("Hide")) self.hdeSection = QLabel(self.tr("Hide"))
self.hdeSection.setToolTip(sectionHideTip) self.hdeSection.setToolTip(sectionHideTip)
@@ -729,7 +722,7 @@ class _HeadingsTab(QWidget):
self.editTextBox.setFixedHeight(5*iPx) self.editTextBox.setFixedHeight(5*iPx)
self.editTextBox.setEnabled(False) self.editTextBox.setEnabled(False)
self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document(), self.mainTheme) self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document())
self.menuInsert = QMenu() self.menuInsert = QMenu()
self.aInsTitle = self.menuInsert.addAction(self.tr("Title")) self.aInsTitle = self.menuInsert.addAction(self.tr("Title"))
@@ -872,12 +865,12 @@ class _HeadingsTab(QWidget):
class _HeadingSyntaxHighlighter(QSyntaxHighlighter): class _HeadingSyntaxHighlighter(QSyntaxHighlighter):
def __init__(self, document: QTextDocument, mainTheme: GuiTheme) -> None: def __init__(self, document: QTextDocument) -> None:
super().__init__(document) super().__init__(document)
self._fmtSymbol = QTextCharFormat() self._fmtSymbol = QTextCharFormat()
self._fmtSymbol.setForeground(QColor(*mainTheme.colHead)) self._fmtSymbol.setForeground(QColor(*CONFIG.theme.colHead))
self._fmtFormat = QTextCharFormat() self._fmtFormat = QTextCharFormat()
self._fmtFormat.setForeground(QColor(*mainTheme.colEmph)) self._fmtFormat.setForeground(QColor(*CONFIG.theme.colEmph))
return return
def highlightBlock(self, text: str) -> None: def highlightBlock(self, text: str) -> None:
@@ -901,12 +894,9 @@ class _ContentTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self._build = build self._build = build
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
# Left Form # Left Form
# ========= # =========
@@ -973,16 +963,15 @@ class _FormatTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
self.buildMain = buildMain self.buildMain = buildMain
self.mainGui = buildMain.mainGui self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self._build = build self._build = build
self._unitScale = 1.0 self._unitScale = 1.0
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
spW = 6*self.mainTheme.textNWidth spW = 6*CONFIG.theme.textNWidth
dbW = 8*self.mainTheme.textNWidth dbW = 8*CONFIG.theme.textNWidth
# Text Format Form # Text Format Form
# ================ # ================
@@ -1003,7 +992,7 @@ class _FormatTab(QWidget):
self.textFont = QLineEdit() self.textFont = QLineEdit()
self.textFont.setReadOnly(True) self.textFont.setReadOnly(True)
self.btnTextFont = QPushButton("...") self.btnTextFont = QPushButton("...")
self.btnTextFont.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) self.btnTextFont.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("...")))
self.btnTextFont.clicked.connect(self._selectFont) self.btnTextFont.clicked.connect(self._selectFont)
self.formFormat.addRow( self.formFormat.addRow(
self._build.getLabel("format.textFont"), self.textFont, button=self.btnTextFont self._build.getLabel("format.textFont"), self.textFont, button=self.btnTextFont
@@ -1287,12 +1276,9 @@ class _OutputTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self._build = build self._build = build
iPx = self.mainTheme.baseIconSize iPx = CONFIG.theme.baseIconSize
# Left Form # Left Form
# ========= # =========
+5 -18
View File
@@ -1,7 +1,6 @@
""" """
novelWriter GUI New Project Wizard novelWriter GUI New Project Wizard
==================================== ====================================
GUI classes for the new project wizard dialog
File History: File History:
Created: 2020-07-11 [0.10.1] Created: 2020-07-11 [0.10.1]
@@ -22,6 +21,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import os import os
import logging import logging
@@ -53,10 +53,9 @@ class GuiProjectWizard(QWizard):
logger.debug("Create: GuiProjectWizard") logger.debug("Create: GuiProjectWizard")
self.setObjectName("GuiProjectWizard") self.setObjectName("GuiProjectWizard")
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.sideImage = self.mainTheme.loadDecoration( self.sideImage = CONFIG.theme.loadDecoration(
"wiz-back", None, CONFIG.pxInt(370) "wiz-back", None, CONFIG.pxInt(370)
) )
self.setWizardStyle(QWizard.ModernStyle) self.setWizardStyle(QWizard.ModernStyle)
@@ -92,9 +91,6 @@ class ProjWizardIntroPage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
super().__init__() super().__init__()
self.theWizard = theWizard
self.mainTheme = theWizard.mainTheme
self.setTitle(self.tr("Create New Project")) self.setTitle(self.tr("Create New Project"))
self.theText = QLabel(self.tr( self.theText = QLabel(self.tr(
"Provide at least a project name. The project name should not " "Provide at least a project name. The project name should not "
@@ -108,7 +104,7 @@ class ProjWizardIntroPage(QWizardPage):
"Peter Mitterhofer", "CC BY-SA 4.0" "Peter Mitterhofer", "CC BY-SA 4.0"
)) ))
lblFont = self.imgCredit.font() lblFont = self.imgCredit.font()
lblFont.setPointSizeF(0.6*self.mainTheme.fontPointSize) lblFont.setPointSizeF(0.6*CONFIG.theme.fontPointSize)
self.imgCredit.setFont(lblFont) self.imgCredit.setFont(lblFont)
xW = CONFIG.pxInt(300) xW = CONFIG.pxInt(300)
@@ -160,9 +156,6 @@ class ProjWizardFolderPage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
super().__init__() super().__init__()
self.theWizard = theWizard
self.mainTheme = theWizard.mainTheme
self.setTitle(self.tr("Select Project Folder")) self.setTitle(self.tr("Select Project Folder"))
self.theText = QLabel(self.tr( self.theText = QLabel(self.tr(
"Select a location to store the project. A new project folder " "Select a location to store the project. A new project folder "
@@ -179,7 +172,7 @@ class ProjWizardFolderPage(QWizardPage):
self.projPath.setPlaceholderText(self.tr("Required")) self.projPath.setPlaceholderText(self.tr("Required"))
self.browseButton = QPushButton("...") self.browseButton = QPushButton("...")
self.browseButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("..."))) self.browseButton.setMaximumWidth(int(2.5*CONFIG.theme.getTextWidth("...")))
self.browseButton.clicked.connect(self._doBrowse) self.browseButton.clicked.connect(self._doBrowse)
self.errLabel = QLabel("") self.errLabel = QLabel("")
@@ -257,8 +250,6 @@ class ProjWizardPopulatePage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
super().__init__() super().__init__()
self.theWizard = theWizard
self.setTitle(self.tr("Populate Project")) self.setTitle(self.tr("Populate Project"))
self.theText = QLabel(self.tr( self.theText = QLabel(self.tr(
"Choose how to pre-fill the project. Either with a minimal set of " "Choose how to pre-fill the project. Either with a minimal set of "
@@ -312,8 +303,6 @@ class ProjWizardCustomPage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
super().__init__() super().__init__()
self.theWizard = theWizard
self.setTitle(self.tr("Custom Project Options")) self.setTitle(self.tr("Custom Project Options"))
self.theText = QLabel(self.tr( self.theText = QLabel(self.tr(
"Select which additional elements to populate the project with. " "Select which additional elements to populate the project with. "
@@ -412,8 +401,6 @@ class ProjWizardFinalPage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
super().__init__() super().__init__()
self.theWizard = theWizard
self.setTitle(self.tr("Summary")) self.setTitle(self.tr("Summary"))
self.theText = QLabel("") self.theText = QLabel("")
self.theText.setWordWrap(True) self.theText.setWordWrap(True)
+17 -19
View File
@@ -72,16 +72,14 @@ class GuiWritingStats(QDialog):
if CONFIG.osDarwin: if CONFIG.osDarwin:
self.setWindowFlag(Qt.WindowType.Tool) self.setWindowFlag(Qt.WindowType.Tool)
self.mainGui = mainGui self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.logData = [] self.logData = []
self.filterData = [] self.filterData = []
self.timeFilter = 0.0 self.timeFilter = 0.0
self.wordOffset = 0 self.wordOffset = 0
pOptions = self.theProject.options pOptions = self.mainGui.project.options
self.setWindowTitle(self.tr("Writing Statistics")) self.setWindowTitle(self.tr("Writing Statistics"))
self.setMinimumWidth(CONFIG.pxInt(420)) self.setMinimumWidth(CONFIG.pxInt(420))
@@ -134,7 +132,7 @@ class GuiWritingStats(QDialog):
self.listBox.setSortingEnabled(True) self.listBox.setSortingEnabled(True)
# Word Bar # Word Bar
self.barHeight = int(round(0.5*self.mainTheme.fontPixelSize)) self.barHeight = int(round(0.5*CONFIG.theme.fontPixelSize))
self.barWidth = CONFIG.pxInt(200) self.barWidth = CONFIG.pxInt(200)
self.barImage = QPixmap(self.barHeight, self.barHeight) self.barImage = QPixmap(self.barHeight, self.barHeight)
self.barImage.fill(self.palette().highlight().color()) self.barImage.fill(self.palette().highlight().color())
@@ -145,27 +143,27 @@ class GuiWritingStats(QDialog):
self.infoBox.setLayout(self.infoForm) self.infoBox.setLayout(self.infoForm)
self.labelTotal = QLabel(formatTime(0)) self.labelTotal = QLabel(formatTime(0))
self.labelTotal.setFont(self.mainTheme.guiFontFixed) self.labelTotal.setFont(CONFIG.theme.guiFontFixed)
self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.labelIdleT = QLabel(formatTime(0)) self.labelIdleT = QLabel(formatTime(0))
self.labelIdleT.setFont(self.mainTheme.guiFontFixed) self.labelIdleT.setFont(CONFIG.theme.guiFontFixed)
self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.labelFilter = QLabel(formatTime(0)) self.labelFilter = QLabel(formatTime(0))
self.labelFilter.setFont(self.mainTheme.guiFontFixed) self.labelFilter.setFont(CONFIG.theme.guiFontFixed)
self.labelFilter.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.labelFilter.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.novelWords = QLabel("0") self.novelWords = QLabel("0")
self.novelWords.setFont(self.mainTheme.guiFontFixed) self.novelWords.setFont(CONFIG.theme.guiFontFixed)
self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.notesWords = QLabel("0") self.notesWords = QLabel("0")
self.notesWords.setFont(self.mainTheme.guiFontFixed) self.notesWords.setFont(CONFIG.theme.guiFontFixed)
self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.totalWords = QLabel("0") self.totalWords = QLabel("0")
self.totalWords.setFont(self.mainTheme.guiFontFixed) self.totalWords.setFont(CONFIG.theme.guiFontFixed)
self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
lblTTime = QLabel(self.tr("Total Time:")) lblTTime = QLabel(self.tr("Total Time:"))
@@ -192,7 +190,7 @@ class GuiWritingStats(QDialog):
self.infoForm.setRowStretch(6, 1) self.infoForm.setRowStretch(6, 1)
# Filter Options # Filter Options
sPx = self.mainTheme.baseIconSize sPx = CONFIG.theme.baseIconSize
self.filterBox = QGroupBox(self.tr("Filters"), self) self.filterBox = QGroupBox(self.tr("Filters"), self)
self.filterForm = QGridLayout(self) self.filterForm = QGridLayout(self)
@@ -335,7 +333,7 @@ class GuiWritingStats(QDialog):
showIdleTime = self.showIdleTime.isChecked() showIdleTime = self.showIdleTime.isChecked()
histMax = self.histMax.value() histMax = self.histMax.value()
pOptions = self.theProject.options pOptions = self.mainGui.project.options
pOptions.setValue("GuiWritingStats", "winWidth", winWidth) pOptions.setValue("GuiWritingStats", "winWidth", winWidth)
pOptions.setValue("GuiWritingStats", "winHeight", winHeight) pOptions.setValue("GuiWritingStats", "winHeight", winHeight)
pOptions.setValue("GuiWritingStats", "widthCol0", widthCol0) pOptions.setValue("GuiWritingStats", "widthCol0", widthCol0)
@@ -443,7 +441,7 @@ class GuiWritingStats(QDialog):
ttTime = 0 ttTime = 0
ttIdle = 0 ttIdle = 0
for record in self.theProject.session.iterRecords(): for record in self.mainGui.project.session.iterRecords():
rType = record.get("type") rType = record.get("type")
if rType == "initial": if rType == "initial":
self.wordOffset = checkInt(record.get("offset"), 0) self.wordOffset = checkInt(record.get("offset"), 0)
@@ -589,13 +587,13 @@ class GuiWritingStats(QDialog):
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight) newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
newItem.setTextAlignment(self.C_BAR, Qt.AlignLeft | Qt.AlignVCenter) newItem.setTextAlignment(self.C_BAR, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed) newItem.setFont(self.C_TIME, CONFIG.theme.guiFontFixed)
newItem.setFont(self.C_LENGTH, self.mainTheme.guiFontFixed) newItem.setFont(self.C_LENGTH, CONFIG.theme.guiFontFixed)
newItem.setFont(self.C_COUNT, self.mainTheme.guiFontFixed) newItem.setFont(self.C_COUNT, CONFIG.theme.guiFontFixed)
if showIdleTime: if showIdleTime:
newItem.setFont(self.C_IDLE, self.mainTheme.guiFontFixed) newItem.setFont(self.C_IDLE, CONFIG.theme.guiFontFixed)
else: else:
newItem.setFont(self.C_IDLE, self.mainTheme.guiFont) newItem.setFont(self.C_IDLE, CONFIG.theme.guiFont)
self.listBox.addTopLevelItem(newItem) self.listBox.addTopLevelItem(newItem)
self.timeFilter += sDiff self.timeFilter += sDiff
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.1-beta1" hexVersion="0x020100b1" fileVersion="1.5" fileRevision="1" timeStamp="2023-07-30 14:08:40"> <novelWriterXML appVersion="2.1-beta1" hexVersion="0x020100b1" fileVersion="1.5" fileRevision="1" timeStamp="2023-08-08 22:19:23">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1514" autoCount="237" editTime="75228"> <project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1517" autoCount="237" editTime="75241">
<name>Sample Project</name> <name>Sample Project</name>
<title>Sample Project</title> <title>Sample Project</title>
<author>Jane Smith</author> <author>Jane Smith</author>
+6 -1
View File
@@ -31,8 +31,9 @@ class MockGuiMain(QObject):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._project = None
self.hasProject = True self.hasProject = True
self.theProject = None
self.mainStatus = MockStatusBar() self.mainStatus = MockStatusBar()
self.projPath = "" self.projPath = ""
@@ -43,6 +44,10 @@ class MockGuiMain(QObject):
return return
@property
def project(self):
return self._project
def postLaunchTasks(self, cmdOpen): def postLaunchTasks(self, cmdOpen):
return return
+2 -2
View File
@@ -1,5 +1,5 @@
[Meta] [Meta]
timestamp = 2023-08-02 14:53:36 timestamp = 2023-08-08 19:01:25
[Main] [Main]
theme = default theme = default
@@ -10,7 +10,7 @@ localisation = en_GB
hidevscroll = False hidevscroll = False
hidehscroll = False hidehscroll = False
lastnotes = 0x0 lastnotes = 0x0
lastpath = /home/vkbo lastpath =
[Sizes] [Sizes]
mainwindow = 1200, 650 mainwindow = 1200, 650
+1 -9
View File
@@ -111,7 +111,7 @@ def testBaseConfig_InitLoadSave(monkeypatch, fncPath, tstPaths):
# Check that we have a default file # Check that we have a default file
copyfile(confFile, testFile) copyfile(confFile, testFile)
ignore = ("timestamp", "lastnotes", "localisation", "lastpath") ignore = ("timestamp", "lastnotes", "localisation", "lastpath", "backuppath")
assert cmpFiles(testFile, compFile, ignoreStart=ignore) assert cmpFiles(testFile, compFile, ignoreStart=ignore)
tstConf.errorText() # This clears the error cache tstConf.errorText() # This clears the error cache
@@ -366,14 +366,6 @@ def testBaseConfig_Internal(monkeypatch, fncPath):
# Function _packList # Function _packList
assert tstConf._packList(["A", 1, 2.0, None, False]) == "A, 1, 2.0, None, False" assert tstConf._packList(["A", 1, 2.0, None, False]) == "A, 1, 2.0, None, False"
# Function _checkNone
assert tstConf._checkNone(None) is None
assert tstConf._checkNone("None") is None
assert tstConf._checkNone("none") is None
assert tstConf._checkNone("NONE") is None
assert tstConf._checkNone("NoNe") is None
assert tstConf._checkNone(123456) == 123456
# Function _checkOptionalPackages # Function _checkOptionalPackages
# (Assumes enchant package exists and is importable) # (Assumes enchant package exists and is importable)
tstConf._checkOptionalPackages() tstConf._checkOptionalPackages()
-4
View File
@@ -590,10 +590,6 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tstPaths):
# Invalid Settings # Invalid Settings
# ================ # ================
# Invalid path
CONFIG._backupPath = None
assert theProject.backupProject(doNotify=False) is False
# Missing project name # Missing project name
CONFIG._backupPath = tstPaths.tmpDir CONFIG._backupPath = tstPaths.tmpDir
theProject.data.setName("") theProject.data.setName("")
+1 -1
View File
@@ -344,7 +344,7 @@ def testCoreStatus_PackUnpack(mockRnd):
# Unpack # Unpack
theStatus = NWStatus(NWStatus.STATUS) theStatus = NWStatus(NWStatus.STATUS)
assert theStatus.unpack({ theStatus.unpack({
statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]}, statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]}, statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]}, statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]},
-2
View File
@@ -34,8 +34,6 @@ from novelwriter.dialogs.about import GuiAbout
def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI): def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
"""Test the novelWriter about dialogs.""" """Test the novelWriter about dialogs."""
# NW About # NW About
nwGUI.mainTheme.themeName = "A Theme"
nwGUI.mainTheme.themeAuthor = "An Author"
assert nwGUI.showAboutNWDialog(showNotes=True) is True assert nwGUI.showAboutNWDialog(showNotes=True) is True
qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000)
+1 -1
View File
@@ -35,7 +35,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# Create a new project # Create a new project
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
theProject = nwGUI.theProject theProject = nwGUI.project
projTree = nwGUI.projView.projTree projTree = nwGUI.projView.projTree
docText = ( docText = (
+1 -1
View File
@@ -54,7 +54,7 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
assert projDet.tabMain.wordCountVal.text() == f"{3000:n}" assert projDet.tabMain.wordCountVal.text() == f"{3000:n}"
assert projDet.tabMain.chapCountVal.text() == f"{3:n}" assert projDet.tabMain.chapCountVal.text() == f"{3:n}"
assert projDet.tabMain.sceneCountVal.text() == f"{5:n}" assert projDet.tabMain.sceneCountVal.text() == f"{5:n}"
assert projDet.tabMain.revCountVal.text() == f"{nwGUI.theProject.data.saveCount:n}" assert projDet.tabMain.revCountVal.text() == f"{nwGUI.project.data.saveCount:n}"
assert projDet.tabMain.projPathVal.text() == str(prjLipsum) assert projDet.tabMain.projPathVal.text() == str(prjLipsum)
+4 -4
View File
@@ -51,7 +51,7 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
# Pretend we have a project # Pretend we have a project
nwGUI.hasProject = True nwGUI.hasProject = True
nwGUI.theProject.data.setSpellLang("en") nwGUI.project.data.setSpellLang("en")
# Get the dialog object # Get the dialog object
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
@@ -95,7 +95,7 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockR
CONFIG.setBackupPath(fncPath) CONFIG.setBackupPath(fncPath)
# Set some values # Set some values
theProject = nwGUI.theProject theProject = nwGUI.project
theProject.data.setSpellLang("en") theProject.data.setSpellLang("en")
theProject.data.setAuthor("Jane Smith") theProject.data.setAuthor("Jane Smith")
theProject.data.setAutoReplace({"A": "B", "C": "D"}) theProject.data.setAutoReplace({"A": "B", "C": "D"})
@@ -160,7 +160,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
CONFIG.setBackupPath(fncPath) CONFIG.setBackupPath(fncPath)
# Set some values # Set some values
theProject = nwGUI.theProject theProject = nwGUI.project
theProject.tree[C.hTitlePage].setStatus(C.sFinished) theProject.tree[C.hTitlePage].setStatus(C.sFinished)
theProject.tree[C.hChapterDoc].setStatus(C.sDraft) theProject.tree[C.hChapterDoc].setStatus(C.sDraft)
theProject.tree[C.hSceneDoc].setStatus(C.sDraft) theProject.tree[C.hSceneDoc].setStatus(C.sDraft)
@@ -361,7 +361,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo
CONFIG.setBackupPath(fncPath) CONFIG.setBackupPath(fncPath)
# Set some values # Set some values
theProject = nwGUI.theProject theProject = nwGUI.project
theProject.data.setAutoReplace({ theProject.data.setAutoReplace({
"A": "B", "C": "D" "A": "B", "C": "D"
}) })
+1 -1
View File
@@ -55,7 +55,7 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, projPath):
assert wList.listBox.count() == 0 assert wList.listBox.count() == 0
# Add words # Add words
userDict = UserDictionary(nwGUI.theProject) userDict = UserDictionary(nwGUI.project)
userDict.add("word_a") userDict.add("word_a")
userDict.add("word_c") userDict.add("word_c")
userDict.add("word_g") userDict.add("word_g")
+10 -10
View File
@@ -163,10 +163,10 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, projPath, ipsumTex
assert "Could not save document." in caplog.text assert "Could not save document." in caplog.text
# Change header level # Change header level
assert nwGUI.theProject.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT assert nwGUI.project.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT
nwGUI.docEditor.replaceText(longText[1:]) nwGUI.docEditor.replaceText(longText[1:])
assert nwGUI.docEditor.saveText() is True assert nwGUI.docEditor.saveText() is True
assert nwGUI.theProject.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT assert nwGUI.project.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT
# Regular save # Regular save
assert nwGUI.docEditor.saveText() is True assert nwGUI.docEditor.saveText() is True
@@ -203,9 +203,9 @@ def testGuiEditor_MetaData(qtbot, nwGUI, projPath, mockRnd):
assert nwGUI.docEditor.setCursorPosition(None) is False assert nwGUI.docEditor.setCursorPosition(None) is False
assert nwGUI.docEditor.setCursorPosition(10) is True assert nwGUI.docEditor.setCursorPosition(10) is True
assert nwGUI.docEditor.getCursorPosition() == 10 assert nwGUI.docEditor.getCursorPosition() == 10
assert nwGUI.theProject.tree[C.hSceneDoc].cursorPos != 10 assert nwGUI.project.tree[C.hSceneDoc].cursorPos != 10
nwGUI.docEditor.saveCursorPosition() nwGUI.docEditor.saveCursorPosition()
assert nwGUI.theProject.tree[C.hSceneDoc].cursorPos == 10 assert nwGUI.project.tree[C.hSceneDoc].cursorPos == 10
assert nwGUI.docEditor.setCursorLine(None) is False assert nwGUI.docEditor.setCursorLine(None) is False
assert nwGUI.docEditor.setCursorLine(3) is True assert nwGUI.docEditor.setCursorLine(3) is True
@@ -1067,7 +1067,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# Create Character # Create Character
theText = "### Jane Doe\n\n@tag: Jane\n\n" + ipsumText[1] + "\n\n" theText = "### Jane Doe\n\n@tag: Jane\n\n" + ipsumText[1] + "\n\n"
cHandle = nwGUI.theProject.newFile("Jane Doe", C.hCharRoot) cHandle = nwGUI.project.newFile("Jane Doe", C.hCharRoot)
assert nwGUI.openDocument(cHandle) is True assert nwGUI.openDocument(cHandle) is True
assert nwGUI.docEditor.replaceText(theText) is True assert nwGUI.docEditor.replaceText(theText) is True
assert nwGUI.saveDocument() is True assert nwGUI.saveDocument() is True
@@ -1145,8 +1145,8 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m
assert nwGUI.docEditor.docFooter.wordsText.text() == "Words: 0 (+0)" assert nwGUI.docEditor.docFooter.wordsText.text() == "Words: 0 (+0)"
# Open a document and populate it # Open a document and populate it
nwGUI.theProject.tree[C.hSceneDoc]._initCount = 0 # Clear item's count nwGUI.project.tree[C.hSceneDoc]._initCount = 0 # Clear item's count
nwGUI.theProject.tree[C.hSceneDoc]._wordCount = 0 # Clear item's count nwGUI.project.tree[C.hSceneDoc]._wordCount = 0 # Clear item's count
assert nwGUI.openDocument(C.hSceneDoc) is True assert nwGUI.openDocument(C.hSceneDoc) is True
theText = "\n\n".join(ipsumText) theText = "\n\n".join(ipsumText)
@@ -1170,9 +1170,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m
nwGUI.docEditor.wCounterDoc.run() nwGUI.docEditor.wCounterDoc.run()
# nwGUI.docEditor._updateDocCounts(cC, wC, pC) # nwGUI.docEditor._updateDocCounts(cC, wC, pC)
assert nwGUI.theProject.tree[C.hSceneDoc]._charCount == cC assert nwGUI.project.tree[C.hSceneDoc]._charCount == cC
assert nwGUI.theProject.tree[C.hSceneDoc]._wordCount == wC assert nwGUI.project.tree[C.hSceneDoc]._wordCount == wC
assert nwGUI.theProject.tree[C.hSceneDoc]._paraCount == pC assert nwGUI.project.tree[C.hSceneDoc]._paraCount == pC
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})" assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})"
# Select all text # Select all text
+3 -3
View File
@@ -40,8 +40,8 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
# Rebuild the index # Rebuild the index
nwGUI.mainMenu.aRebuildIndex.activate(QAction.Trigger) nwGUI.mainMenu.aRebuildIndex.activate(QAction.Trigger)
assert nwGUI.theProject.index._tagsIndex._tags != {} assert nwGUI.project.index._tagsIndex._tags != {}
assert nwGUI.theProject.index._itemIndex._items != {} assert nwGUI.project.index._itemIndex._items != {}
# Select a document in the project tree # Select a document in the project tree
nwGUI.projView.setSelectedHandle("88243afbe5ed8") nwGUI.projView.setSelectedHandle("88243afbe5ed8")
@@ -128,7 +128,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
nwGUI.docViewer.reloadText() nwGUI.docViewer.reloadText()
# Change document title # Change document title
nwItem = nwGUI.theProject.tree["4c4f28287af27"] nwItem = nwGUI.project.tree["4c4f28287af27"]
nwItem.setName("Test Title") nwItem.setName("Test Title")
assert nwItem.itemName == "Test Title" assert nwItem.itemName == "Test Title"
nwGUI.docViewer.updateDocInfo("4c4f28287af27") nwGUI.docViewer.updateDocInfo("4c4f28287af27")
+16 -16
View File
@@ -202,14 +202,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
assert nwGUI.saveProject() assert nwGUI.saveProject()
assert nwGUI.closeProject() assert nwGUI.closeProject()
assert len(nwGUI.theProject.tree) == 0 assert len(nwGUI.project.tree) == 0
assert len(nwGUI.theProject.tree._treeOrder) == 0 assert len(nwGUI.project.tree._treeOrder) == 0
assert len(nwGUI.theProject.tree._treeRoots) == 0 assert len(nwGUI.project.tree._treeRoots) == 0
assert nwGUI.theProject.tree.trashRoot() is None assert nwGUI.project.tree.trashRoot() is None
assert nwGUI.theProject.data.name == "" assert nwGUI.project.data.name == ""
assert nwGUI.theProject.data.title == "" assert nwGUI.project.data.title == ""
assert nwGUI.theProject.data.author == "" assert nwGUI.project.data.author == ""
assert nwGUI.theProject.data.spellCheck is False assert nwGUI.project.data.spellCheck is False
# Check the files # Check the files
projFile = projPath / "nwProject.nwx" projFile = projPath / "nwProject.nwx"
@@ -222,14 +222,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
assert nwGUI.openProject(projPath) assert nwGUI.openProject(projPath)
# Check that we loaded the data # Check that we loaded the data
assert len(nwGUI.theProject.tree) == 8 assert len(nwGUI.project.tree) == 8
assert len(nwGUI.theProject.tree._treeOrder) == 8 assert len(nwGUI.project.tree._treeOrder) == 8
assert len(nwGUI.theProject.tree._treeRoots) == 4 assert len(nwGUI.project.tree._treeRoots) == 4
assert nwGUI.theProject.tree.trashRoot() is None assert nwGUI.project.tree.trashRoot() is None
assert nwGUI.theProject.data.name == "New Project" assert nwGUI.project.data.name == "New Project"
assert nwGUI.theProject.data.title == "New Novel" assert nwGUI.project.data.title == "New Novel"
assert nwGUI.theProject.data.author == "Jane Doe" assert nwGUI.project.data.author == "Jane Doe"
assert nwGUI.theProject.data.spellCheck is False assert nwGUI.project.data.spellCheck is False
# Check that tree items have been created # Check that tree items have been created
assert nwGUI.projView.projTree._getTreeItem(C.hNovelRoot) is not None assert nwGUI.projView.projTree._getTreeItem(C.hNovelRoot) is not None
+1 -1
View File
@@ -48,7 +48,7 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
nwGUI.projView.projTree._getTreeItem(C.hCharRoot).setSelected(True) nwGUI.projView.projTree._getTreeItem(C.hCharRoot).setSelected(True)
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
contentPath = nwGUI.theProject.storage.contentPath contentPath = nwGUI.project.storage.contentPath
assert isinstance(contentPath, Path) assert isinstance(contentPath, Path)
(contentPath / "0000000000010.nwd").write_text( (contentPath / "0000000000010.nwd").write_text(
+3 -3
View File
@@ -71,7 +71,7 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, projPath):
# Option State # Option State
# ============ # ============
pOptions = nwGUI.theProject.options pOptions = nwGUI.project.options
colNames = [h.name for h in nwOutline] colNames = [h.name for h in nwOutline]
colItems = [h for h in nwOutline] colItems = [h for h in nwOutline]
colWidth = {h: outlineTree.DEF_WIDTH[h] for h in nwOutline} colWidth = {h: outlineTree.DEF_WIDTH[h] for h in nwOutline}
@@ -181,7 +181,7 @@ def testGuiOutline_Content(qtbot, nwGUI, prjLipsum):
assert outlineBar.novelValue.itemData(2) == "" # All novels assert outlineBar.novelValue.itemData(2) == "" # All novels
# Add a second novel folder # Add a second novel folder
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL) newHandle = nwGUI.project.newRoot(nwItemClass.NOVEL)
nwGUI.projView.projTree.revealNewTreeItem(newHandle) nwGUI.projView.projTree.revealNewTreeItem(newHandle)
# Check new values in dropdown list # Check new values in dropdown list
@@ -198,7 +198,7 @@ def testGuiOutline_Content(qtbot, nwGUI, prjLipsum):
("Section 4", 4), ("Section 4", 4),
] ]
for dTitle, hLevel in docList: for dTitle, hLevel in docList:
aHandle = nwGUI.theProject.newFile(dTitle, newHandle) aHandle = nwGUI.project.newFile(dTitle, newHandle)
hHash = "#"*hLevel hHash = "#"*hLevel
writeFile(prjLipsum / "content" / f"{aHandle}.nwd", f"{hHash} {dTitle}\n\n") writeFile(prjLipsum / "content" / f"{aHandle}.nwd", f"{hHash} {dTitle}\n\n")
nwGUI.projView.projTree.revealNewTreeItem(aHandle) nwGUI.projView.projTree.revealNewTreeItem(aHandle)
+33 -33
View File
@@ -46,7 +46,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
projView = nwGUI.projView projView = nwGUI.projView
projTree = nwGUI.projView.projTree projTree = nwGUI.projView.projTree
theProject = nwGUI.theProject theProject = nwGUI.project
# Try to add item with no project # Try to add item with no project
assert projView.projTree.newTreeItem(nwItemType.FILE) is False assert projView.projTree.newTreeItem(nwItemType.FILE) is False
@@ -260,19 +260,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# =========== # ===========
projView.setSelectedHandle(C.hNovelRoot) projView.setSelectedHandle(C.hNovelRoot)
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0 assert nwGUI.project.tree._treeOrder.index(C.hNovelRoot) == 0
# Move novel folder up # Move novel folder up
assert projTree.moveTreeItem(-1) is False assert projTree.moveTreeItem(-1) is False
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0 assert nwGUI.project.tree._treeOrder.index(C.hNovelRoot) == 0
# Move novel folder down # Move novel folder down
assert projTree.moveTreeItem(1) is True assert projTree.moveTreeItem(1) is True
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 1 assert nwGUI.project.tree._treeOrder.index(C.hNovelRoot) == 1
# Move novel folder up again # Move novel folder up again
assert projTree.moveTreeItem(-1) is True assert projTree.moveTreeItem(-1) is True
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0 assert nwGUI.project.tree._treeOrder.index(C.hNovelRoot) == 0
# Clean up # Clean up
# qtbot.stop() # qtbot.stop()
@@ -348,7 +348,7 @@ def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, projPat
C.hChapterDir, C.hChapterDoc, C.hSceneDoc, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010" "0000000000010"
] ]
trashHandle = nwGUI.theProject.tree.trashRoot() trashHandle = nwGUI.project.tree.trashRoot()
assert projTree.getTreeFromHandle(trashHandle) == [ assert projTree.getTreeFromHandle(trashHandle) == [
trashHandle, "0000000000012", "0000000000011" trashHandle, "0000000000012", "0000000000011"
] ]
@@ -368,7 +368,7 @@ def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, projPath,
"""Test moving items to Trash.""" """Test moving items to Trash."""
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject theProject = nwGUI.project
projTree = nwGUI.projView.projTree projTree = nwGUI.projView.projTree
# Create a project # Create a project
@@ -420,7 +420,7 @@ def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, pro
"""Test permanently deleting items.""" """Test permanently deleting items."""
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject theProject = nwGUI.project
projTree = nwGUI.projView.projTree projTree = nwGUI.projView.projTree
# Create a project # Create a project
@@ -471,7 +471,7 @@ def testGuiProjTree_EmptyTrash(qtbot, caplog, monkeypatch, nwGUI, projPath, mock
"""Test emptying Trash.""" """Test emptying Trash."""
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject theProject = nwGUI.project
projTree = nwGUI.projView.projTree projTree = nwGUI.projView.projTree
# No project open # No project open
@@ -541,16 +541,16 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
projTree.setExpandedFromHandle(None, True) projTree.setExpandedFromHandle(None, True)
projTree._addTrashRoot() projTree._addTrashRoot()
hTrashRoot = projTree.theProject.tree.trashRoot() hTrashRoot = nwGUI.project.tree.trashRoot()
projTree.setSelectedHandle(C.hCharRoot) projTree.setSelectedHandle(C.hCharRoot)
projTree.newTreeItem(nwItemType.FILE) projTree.newTreeItem(nwItemType.FILE)
projTree.setSelectedHandle(C.hNovelRoot) projTree.setSelectedHandle(C.hNovelRoot)
projTree.newTreeItem(nwItemType.FILE, isNote=True) projTree.newTreeItem(nwItemType.FILE, isNote=True)
nwGUI.theProject.newFile("SubNote", hNovelNote) nwGUI.project.newFile("SubNote", hNovelNote)
projTree.revealNewTreeItem(hSubNote) projTree.revealNewTreeItem(hSubNote)
assert nwGUI.theProject.tree[hSubNote].itemParent == hNovelNote assert nwGUI.project.tree[hSubNote].itemParent == hNovelNote
def itemPos(tHandle): def itemPos(tHandle):
return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center() return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center()
@@ -578,7 +578,7 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# Direct Edit Functions # Direct Edit Functions
# ===================== # =====================
# Trigger the dedicated functions the menu entries connect to # Trigger the dedicated functions the menu entries connect to
nwItem = projTree.theProject.tree[hNovelNote] nwItem = nwGUI.project.tree[hNovelNote]
# Toggle active flag # Toggle active flag
assert nwItem.isActive is True assert nwItem.isActive is True
@@ -619,17 +619,17 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No) mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No)
projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT) projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT)
assert nwGUI.theProject.tree[hNewFolderOne].isFolderType() assert nwGUI.project.tree[hNewFolderOne].isFolderType()
# Convert the first folder to a document # Convert the first folder to a document
projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT) projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT)
assert nwGUI.theProject.tree[hNewFolderOne].isFileType() assert nwGUI.project.tree[hNewFolderOne].isFileType()
assert nwGUI.theProject.tree[hNewFolderOne].isDocumentLayout() assert nwGUI.project.tree[hNewFolderOne].isDocumentLayout()
# Convert the second folder to a note # Convert the second folder to a note
projTree._covertFolderToFile(hNewFolderTwo, nwItemLayout.NOTE) projTree._covertFolderToFile(hNewFolderTwo, nwItemLayout.NOTE)
assert nwGUI.theProject.tree[hNewFolderTwo].isFileType() assert nwGUI.project.tree[hNewFolderTwo].isFileType()
assert nwGUI.theProject.tree[hNewFolderTwo].isNoteLayout() assert nwGUI.project.tree[hNewFolderTwo].isNoteLayout()
# qtbot.stop() # qtbot.stop()
@@ -649,7 +649,7 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
# Create a project # Create a project
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
theProject = nwGUI.theProject theProject = nwGUI.project
projTree = nwGUI.projView.projTree projTree = nwGUI.projView.projTree
mergedDoc1 = "0000000000014" mergedDoc1 = "0000000000014"
@@ -751,7 +751,7 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
# Create a project # Create a project
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
theProject = nwGUI.theProject theProject = nwGUI.project
projTree = nwGUI.projView.projTree projTree = nwGUI.projView.projTree
docText = ( docText = (
@@ -852,7 +852,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
"""Test the duplicate items function.""" """Test the duplicate items function."""
# Create a project # Create a project
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
assert len(nwGUI.theProject.tree) == 8 assert len(nwGUI.project.tree) == 8
projTree = nwGUI.projView.projTree projTree = nwGUI.projView.projTree
projTree._getTreeItem(C.hNovelRoot).setExpanded(True) # type: ignore projTree._getTreeItem(C.hNovelRoot).setExpanded(True) # type: ignore
@@ -860,28 +860,28 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
# Nothing to do # Nothing to do
assert projTree._duplicateFromHandle(C.hInvalid) is False assert projTree._duplicateFromHandle(C.hInvalid) is False
assert len(nwGUI.theProject.tree) == 8 assert len(nwGUI.project.tree) == 8
# Duplicate title page, but select no # Duplicate title page, but select no
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No) mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No)
assert projTree._duplicateFromHandle(C.hTitlePage) is False assert projTree._duplicateFromHandle(C.hTitlePage) is False
assert len(nwGUI.theProject.tree) == 8 assert len(nwGUI.project.tree) == 8
# Duplicate title page # Duplicate title page
assert projTree._duplicateFromHandle(C.hTitlePage) is True assert projTree._duplicateFromHandle(C.hTitlePage) is True
assert len(nwGUI.theProject.tree) == 9 assert len(nwGUI.project.tree) == 9
# Duplicate folder # Duplicate folder
assert projTree._duplicateFromHandle(C.hChapterDir) is True assert projTree._duplicateFromHandle(C.hChapterDir) is True
assert len(nwGUI.theProject.tree) == 12 assert len(nwGUI.project.tree) == 12
# Duplicate novel root # Duplicate novel root
assert projTree._duplicateFromHandle(C.hNovelRoot) is True assert projTree._duplicateFromHandle(C.hNovelRoot) is True
assert len(nwGUI.theProject.tree) == 21 assert len(nwGUI.project.tree) == 21
# Check tree order that all items are next to eachother # Check tree order that all items are next to eachother
assert nwGUI.theProject.tree._treeOrder == [ assert nwGUI.project.tree._treeOrder == [
C.hNovelRoot, C.hTitlePage, "0000000000010", C.hChapterDir, C.hChapterDoc, C.hSceneDoc, C.hNovelRoot, C.hTitlePage, "0000000000010", C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000011", "0000000000012", "0000000000013", "0000000000014", "0000000000015", "0000000000011", "0000000000012", "0000000000013", "0000000000014", "0000000000015",
"0000000000016", "0000000000017", "0000000000018", "0000000000019", "000000000001a", "0000000000016", "0000000000017", "0000000000018", "0000000000019", "000000000001a",
@@ -889,7 +889,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
] ]
# Make the duplicator stop early # Make the duplicator stop early
content = nwGUI.theProject.storage.contentPath content = nwGUI.project.storage.contentPath
assert isinstance(content, Path) assert isinstance(content, Path)
(content / "000000000001e.nwd").touch() (content / "000000000001e.nwd").touch()
assert (content / "000000000001e.nwd").exists() assert (content / "000000000001e.nwd").exists()
@@ -897,7 +897,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
# Should only create the folder, and skip the two files because the # Should only create the folder, and skip the two files because the
# next handle is already a file # next handle is already a file
assert projTree._duplicateFromHandle(C.hChapterDir) is True assert projTree._duplicateFromHandle(C.hChapterDir) is True
assert len(nwGUI.theProject.tree) == 22 assert len(nwGUI.project.tree) == 22
# qtbot.stop() # qtbot.stop()
@@ -938,13 +938,13 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd)
assert projTree.revealNewTreeItem(C.hInvalid) is False assert projTree.revealNewTreeItem(C.hInvalid) is False
# Try to add an orphaned file to the tree # Try to add an orphaned file to the tree
nHandle = nwGUI.theProject.newFile("Test", C.hNovelRoot) nHandle = nwGUI.project.newFile("Test", C.hNovelRoot)
nwGUI.theProject.tree[nHandle].setParent(None) # type: ignore nwGUI.project.tree[nHandle].setParent(None) # type: ignore
assert projTree.revealNewTreeItem(nHandle) is False assert projTree.revealNewTreeItem(nHandle) is False
# Try to add an item with unknown parent to the tree # Try to add an item with unknown parent to the tree
nHandle = nwGUI.theProject.newFile("Test", C.hNovelRoot) nHandle = nwGUI.project.newFile("Test", C.hNovelRoot)
nwGUI.theProject.tree[nHandle].setParent(C.hInvalid) # type: ignore nwGUI.project.tree[nHandle].setParent(C.hInvalid) # type: ignore
assert projTree.revealNewTreeItem(nHandle) is False assert projTree.revealNewTreeItem(nHandle) is False
# Method: undoLastMove # Method: undoLastMove
+3 -3
View File
@@ -25,7 +25,7 @@ import pytest
from tools import C, buildTestProject from tools import C, buildTestProject
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.gui.statusbar import StatusLED from novelwriter.extensions.statusled import StatusLED
@pytest.mark.gui @pytest.mark.gui
@@ -33,8 +33,8 @@ def testGuiStatusBar_Main(qtbot, nwGUI, projPath, mockRnd):
"""Test the the various features of the status bar. """Test the the various features of the status bar.
""" """
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot) cHandle = nwGUI.project.newFile("A Note", C.hCharRoot)
newDoc = nwGUI.theProject.storage.getDocument(cHandle) newDoc = nwGUI.project.storage.getDocument(cHandle)
newDoc.writeDocument("# A Note\n\n") newDoc.writeDocument("# A Note\n\n")
nwGUI.projView.projTree.revealNewTreeItem(cHandle) nwGUI.projView.projTree.revealNewTreeItem(cHandle)
nwGUI.rebuildIndex(beQuiet=True) nwGUI.rebuildIndex(beQuiet=True)
+7 -11
View File
@@ -33,14 +33,12 @@ from PyQt5.QtWidgets import QApplication
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.constants import nwLabels from novelwriter.constants import nwLabels
from novelwriter.gui.theme import GuiIcons, GuiTheme
@pytest.mark.gui @pytest.mark.gui
def testGuiTheme_Main(qtbot, nwGUI, tstPaths): def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
"""Test the theme class init. """Test the theme class init."""
""" mainTheme = CONFIG.theme
mainTheme: GuiTheme = nwGUI.mainTheme
# Methods # Methods
# ======= # =======
@@ -123,7 +121,7 @@ def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
@pytest.mark.gui @pytest.mark.gui
def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI): def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI):
"""Test the theme part of the class.""" """Test the theme part of the class."""
mainTheme: GuiTheme = nwGUI.mainTheme mainTheme = CONFIG.theme
# List Themes # List Themes
# =========== # ===========
@@ -200,9 +198,8 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI):
@pytest.mark.gui @pytest.mark.gui
def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI): def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI):
"""Test the syntax part of the class. """Test the syntax part of the class."""
""" mainTheme = CONFIG.theme
mainTheme: GuiTheme = nwGUI.mainTheme
# List Themes # List Themes
# =========== # ===========
@@ -266,9 +263,8 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI):
@pytest.mark.gui @pytest.mark.gui
def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, tstPaths): def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, tstPaths):
"""Test the icon cache class. """Test the icon cache class."""
""" iconCache = CONFIG.theme.iconCache
iconCache: GuiIcons = nwGUI.mainTheme.iconCache
# Load Theme # Load Theme
# ========== # ==========
+2 -2
View File
@@ -45,7 +45,7 @@ def testManuscript_Init(monkeypatch, qtbot: QtBot, nwGUI: GuiMain, projPath: Pat
"""Test the init/main functionality of the GuiManuscript dialog.""" """Test the init/main functionality of the GuiManuscript dialog."""
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
nwGUI.openProject(projPath) nwGUI.openProject(projPath)
nwGUI.theProject.storage.getDocument(C.hChapterDoc).writeDocument("## A Chapter\n\n\t\tHi") nwGUI.project.storage.getDocument(C.hChapterDoc).writeDocument("## A Chapter\n\n\t\tHi")
allText = "New Novel\nBy Jane Doe\nA Chapter\n\t\tHi\n* * *" allText = "New Novel\nBy Jane Doe\nA Chapter\n\t\tHi\n* * *"
manus = GuiManuscript(nwGUI) manus = GuiManuscript(nwGUI)
@@ -159,7 +159,7 @@ def testManuscript_Features(monkeypatch, qtbot: QtBot, nwGUI: GuiMain, projPath:
manus.show() manus.show()
manus.loadContent() manus.loadContent()
cacheFile = CONFIG.dataPath("cache") / f"build_{nwGUI.theProject.data.uuid}.json" cacheFile = CONFIG.dataPath("cache") / f"build_{nwGUI.project.data.uuid}.json"
manus.buildList.setCurrentRow(0) manus.buildList.setCurrentRow(0)
build = manus._getSelectedBuild() build = manus._getSelectedBuild()
assert isinstance(build, BuildSettings) assert isinstance(build, BuildSettings)
+13 -13
View File
@@ -128,11 +128,11 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
"worldRoot": 9, "worldRoot": 9,
} }
hPlotDoc = nwGUI.theProject.newFile("Main Plot", C.hPlotRoot) hPlotDoc = nwGUI.project.newFile("Main Plot", C.hPlotRoot)
hCharDoc = nwGUI.theProject.newFile("Jane Doe", C.hCharRoot) hCharDoc = nwGUI.project.newFile("Jane Doe", C.hCharRoot)
nwGUI.projView.projTree.revealNewTreeItem(hPlotDoc) nwGUI.projView.projTree.revealNewTreeItem(hPlotDoc)
nwGUI.projView.projTree.revealNewTreeItem(hCharDoc) nwGUI.projView.projTree.revealNewTreeItem(hCharDoc)
nwGUI.theProject.tree[hPlotDoc].setActive(False) # type: ignore nwGUI.project.tree[hPlotDoc].setActive(False) # type: ignore
# Create the dialog and populate it # Create the dialog and populate it
bSettings = GuiBuildSettings(nwGUI, build) bSettings = GuiBuildSettings(nwGUI, build)
@@ -167,7 +167,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
# Switch off novel docs # Switch off novel docs
filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(False) filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(False)
assert build.buildItemFilter(nwGUI.theProject) == { assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -182,7 +182,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
# Switch on note docs # Switch on note docs
filterTab.filterOpt._widgets[switchMap["incNotes"]].setChecked(True) filterTab.filterOpt._widgets[switchMap["incNotes"]].setChecked(True)
assert build.buildItemFilter(nwGUI.theProject) == { assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -197,7 +197,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
# Switch on inactive docs # Switch on inactive docs
filterTab.filterOpt._widgets[switchMap["incInactive"]].setChecked(True) filterTab.filterOpt._widgets[switchMap["incInactive"]].setChecked(True)
assert build.buildItemFilter(nwGUI.theProject) == { assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -214,7 +214,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
filterTab._treeMap[C.hChapterDoc].setSelected(True) filterTab._treeMap[C.hChapterDoc].setSelected(True)
filterTab._treeMap[C.hSceneDoc].setSelected(True) filterTab._treeMap[C.hSceneDoc].setSelected(True)
filterTab.includedButton.click() filterTab.includedButton.click()
assert build.buildItemFilter(nwGUI.theProject) == { assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -232,7 +232,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore
filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore
filterTab.excludedButton.click() filterTab.excludedButton.click()
assert build.buildItemFilter(nwGUI.theProject) == { assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -247,7 +247,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
# Switch on novel docs # Switch on novel docs
filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(True) filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(True)
assert build.buildItemFilter(nwGUI.theProject) == { assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED), # Now enabled C.hTitlePage: (True, FilterMode.FILTERED), # Now enabled
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -264,7 +264,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
filterTab.optTree.clearSelection() filterTab.optTree.clearSelection()
filterTab._treeMap[C.hNovelRoot].setSelected(True) filterTab._treeMap[C.hNovelRoot].setSelected(True)
filterTab.resetButton.click() filterTab.resetButton.click()
assert build.buildItemFilter(nwGUI.theProject) == { assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED), C.hTitlePage: (True, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -284,7 +284,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore
filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore
filterTab.resetButton.click() filterTab.resetButton.click()
assert build.buildItemFilter(nwGUI.theProject) == { assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED), C.hTitlePage: (True, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -302,8 +302,8 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
C.hNovelRoot, C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc, C.hNovelRoot, C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
C.hPlotRoot, hPlotDoc, C.hCharRoot, hCharDoc, C.hPlotRoot, hPlotDoc, C.hCharRoot, hCharDoc,
] ]
nwGUI.theProject.tree[hCharDoc].setRoot(None) # type: ignore nwGUI.project.tree[hCharDoc].setRoot(None) # type: ignore
nwGUI.theProject.tree[hPlotDoc].setParent(None) # type: ignore nwGUI.project.tree[hPlotDoc].setParent(None) # type: ignore
filterTab._populateTree() filterTab._populateTree()
assert list(filterTab._treeMap.keys()) == [ assert list(filterTab._treeMap.keys()) == [
C.hNovelRoot, C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc, C.hNovelRoot, C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
+1 -1
View File
@@ -39,7 +39,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
""" """
# Create a project to work on # Create a project to work on
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
project = nwGUI.theProject project = nwGUI.project
qtbot.wait(100) qtbot.wait(100)
assert nwGUI.saveProject() assert nwGUI.saveProject()
+36 -36
View File
@@ -154,59 +154,59 @@ def cleanProject(path: str | Path):
return return
def buildTestProject(theObject, projPath): def buildTestProject(obj, projPath):
"""Build a standard test project in projPath using theProject """Build a standard test project in projPath using the project
object as the parent. object as the parent.
""" """
from novelwriter.enum import nwItemClass from novelwriter.enum import nwItemClass
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
if isinstance(theObject, NWProject): if isinstance(obj, NWProject):
theGUI = None nwGUI = None
theProject = theObject project = obj
else: else:
theGUI = theObject nwGUI = obj
theProject = theObject.theProject project = obj.project
theProject.clearProject() project.clearProject()
theProject.storage.openProjectInPlace(projPath) project.storage.openProjectInPlace(projPath)
theProject.setDefaultStatusImport() project.setDefaultStatusImport()
theProject.data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed") project.data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
theProject.data.setName("New Project") project.data.setName("New Project")
theProject.data.setTitle("New Novel") project.data.setTitle("New Novel")
theProject.data.setAuthor("Jane Doe") project.data.setAuthor("Jane Doe")
# Creating a minimal project with a few root folders and a # Creating a minimal project with a few root folders and a
# single chapter folder with a single file. # single chapter folder with a single file.
xHandle = {} xHandle = {}
xHandle[1] = theProject.newRoot(nwItemClass.NOVEL, "Novel") xHandle[1] = project.newRoot(nwItemClass.NOVEL, "Novel")
xHandle[2] = theProject.newRoot(nwItemClass.PLOT, "Plot") xHandle[2] = project.newRoot(nwItemClass.PLOT, "Plot")
xHandle[3] = theProject.newRoot(nwItemClass.CHARACTER, "Characters") xHandle[3] = project.newRoot(nwItemClass.CHARACTER, "Characters")
xHandle[4] = theProject.newRoot(nwItemClass.WORLD, "World") xHandle[4] = project.newRoot(nwItemClass.WORLD, "World")
xHandle[5] = theProject.newFile("Title Page", xHandle[1]) xHandle[5] = project.newFile("Title Page", xHandle[1])
xHandle[6] = theProject.newFolder("New Chapter", xHandle[1]) xHandle[6] = project.newFolder("New Chapter", xHandle[1])
xHandle[7] = theProject.newFile("New Chapter", xHandle[6]) xHandle[7] = project.newFile("New Chapter", xHandle[6])
xHandle[8] = theProject.newFile("New Scene", xHandle[6]) xHandle[8] = project.newFile("New Scene", xHandle[6])
aDoc = theProject.storage.getDocument(xHandle[5]) aDoc = project.storage.getDocument(xHandle[5])
aDoc.writeDocument("#! New Novel\n\n>> By Jane Doe <<\n") aDoc.writeDocument("#! New Novel\n\n>> By Jane Doe <<\n")
theProject.index.reIndexHandle(xHandle[5]) project.index.reIndexHandle(xHandle[5])
aDoc = theProject.storage.getDocument(xHandle[7]) aDoc = project.storage.getDocument(xHandle[7])
aDoc.writeDocument("## %s\n\n" % theProject.tr("New Chapter")) aDoc.writeDocument("## %s\n\n" % project.tr("New Chapter"))
theProject.index.reIndexHandle(xHandle[7]) project.index.reIndexHandle(xHandle[7])
aDoc = theProject.storage.getDocument(xHandle[8]) aDoc = project.storage.getDocument(xHandle[8])
aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene")) aDoc.writeDocument("### %s\n\n" % project.tr("New Scene"))
theProject.index.reIndexHandle(xHandle[8]) project.index.reIndexHandle(xHandle[8])
theProject.session.startSession() project.session.startSession()
theProject.setProjectChanged(True) project.setProjectChanged(True)
theProject.saveProject(autoSave=True) project.saveProject(autoSave=True)
if theGUI is not None: if nwGUI is not None:
theGUI.hasProject = True nwGUI.hasProject = True
theGUI.rebuildTrees() nwGUI.rebuildTrees()
return return