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:
"""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):
return value
elif isinstance(value, str):
@@ -289,8 +288,7 @@ def transferCase(source: str, target: str) -> 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:
return QCoreApplication.translate(
"Common", "in the future"
@@ -350,8 +348,7 @@ def fuzzyTime(seconds: int) -> 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):
return "NAN"
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)
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
library. It behaves more closely to how the one from lxml does.
"""
if isinstance(tree, ET.ElementTree):
tree = tree.getroot()
if not isinstance(tree, ET.Element):
return
indentations = ["\n"]
+198 -201
View File
@@ -28,18 +28,23 @@ import json
import logging
from time import time
from typing import TYPE_CHECKING
from pathlib import Path
from PyQt5.QtGui import QFontDatabase
from PyQt5.QtCore import (
QT_VERSION, QT_VERSION_STR, PYQT_VERSION, PYQT_VERSION_STR, QStandardPaths,
QSysInfo, QLocale, QLibraryInfo, QTranslator
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
QLocale, QStandardPaths, QSysInfo, QTranslator
)
from PyQt5.QtWidgets import QApplication
from novelwriter.error import logException, formatException
from novelwriter.common import checkPath, formatTimeStamp, NWConfigParser
from novelwriter.error import formatException, logException
from novelwriter.common import NWConfigParser, checkInt, checkPath, formatTimeStamp
from novelwriter.constants import nwFiles, nwUnicode
if TYPE_CHECKING: # pragma: no cover
from novelwriter.gui.theme import GuiTheme
logger = logging.getLogger(__name__)
@@ -48,7 +53,7 @@ class Config:
LANG_NW = 1
LANG_PROJ = 2
def __init__(self):
def __init__(self) -> None:
# Initialisation
# ==============
@@ -64,6 +69,7 @@ class Config:
self._confPath = confRoot.absolute() / self.appHandle # The user config location
self._dataPath = dataRoot.absolute() / self.appHandle # The user data location
self._homePath = Path.home().absolute() # The user's home directory
self._backPath = self._homePath / "Backups"
self._appPath = Path(__file__).parent.absolute()
self._appRoot = self._appPath.parent
@@ -90,7 +96,8 @@ class Config:
# User Settings
# =============
self._recentProj = RecentProjects(self)
self._themeObj = None
self._recentObj = RecentProjects(self)
# General GUI Settings
self.guiLocale = self._qLocale.name()
@@ -102,7 +109,6 @@ class Config:
self.hideVScroll = False # Hide vertical 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._lastPath = self._homePath # The user's last used path
# Size Settings
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.autoSaveDoc = 30 # Interval for auto-saving document, in seconds
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.askBeforeBackup = True # Flag for asking before running automatic backup
@@ -169,6 +174,10 @@ class Config:
self.fmtPadAfter = ""
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
self.spellLanguage = "en"
@@ -228,53 +237,59 @@ class Config:
##
@property
def hasError(self):
def hasError(self) -> bool:
return self._hasError
@property
def recentProjects(self):
return self._recentProj
def recentProjects(self) -> RecentProjects:
return self._recentObj
@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]
@property
def preferencesWinSize(self):
def preferencesWinSize(self) -> list[int]:
return [int(x*self.guiScale) for x in self._prefsWinSize]
@property
def projLoadColWidths(self):
def projLoadColWidths(self) -> list[int]:
return [int(x*self.guiScale) for x in self._projLoadCols]
@property
def mainPanePos(self):
def mainPanePos(self) -> list[int]:
return [int(x*self.guiScale) for x in self._mainPanePos]
@property
def viewPanePos(self):
def viewPanePos(self) -> list[int]:
return [int(x*self.guiScale) for x in self._viewPanePos]
@property
def outlinePanePos(self):
def outlinePanePos(self) -> list[int]:
return [int(x*self.guiScale) for x in self._outlnPanePos]
##
# Getters
##
def getTextWidth(self, focusMode=False):
def getTextWidth(self, focusMode: bool = False) -> int:
"""Get the text with for the correct editor mode."""
if focusMode:
return self.pxInt(max(self.focusWidth, 200))
else:
return self.pxInt(max(self.textWidth, 200))
def getTextMargin(self):
def getTextMargin(self) -> int:
"""Get the scaled text margin."""
return self.pxInt(max(self.textMargin, 0))
def getTabWidth(self):
def getTabWidth(self) -> int:
"""Get the scaled tab width."""
return self.pxInt(max(self.tabWidth, 0))
@@ -282,65 +297,70 @@ class Config:
# 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
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
grow each time the app is opened.
"""
newWidth = int(newWidth/self.guiScale)
newHeight = int(newHeight/self.guiScale)
if abs(self._mainWinSize[0] - newWidth) > 5:
self._mainWinSize[0] = newWidth
if abs(self._mainWinSize[1] - newHeight) > 5:
self._mainWinSize[1] = newHeight
width = int(width/self.guiScale)
height = int(height/self.guiScale)
if abs(self._mainWinSize[0] - width) > 5:
self._mainWinSize[0] = width
if abs(self._mainWinSize[1] - height) > 5:
self._mainWinSize[1] = height
return
def setPreferencesWinSize(self, newWidth, newHeight):
def setPreferencesWinSize(self, width: int, height: int) -> None:
"""Set the size of the Preferences dialog window."""
self._prefsWinSize[0] = int(newWidth/self.guiScale)
self._prefsWinSize[1] = int(newHeight/self.guiScale)
self._prefsWinSize[0] = int(width/self.guiScale)
self._prefsWinSize[1] = int(height/self.guiScale)
return
def setProjLoadColWidths(self, colWidths):
def setProjLoadColWidths(self, widths: list[int]) -> None:
"""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
def setMainPanePos(self, panePos):
def setMainPanePos(self, pos: list[int]) -> None:
"""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
def setViewPanePos(self, panePos):
def setViewPanePos(self, pos: list[int]) -> None:
"""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
def setOutlinePanePos(self, panePos):
def setOutlinePanePos(self, pos: list[int]) -> None:
"""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
def setLastPath(self, lastPath):
def setLastPath(self, path: str | Path) -> None:
"""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.
"""
if isinstance(lastPath, (str, Path)):
lastPath = checkPath(lastPath, self._homePath)
if not lastPath.is_dir():
lastPath = lastPath.parent
if lastPath.is_dir():
self._lastPath = lastPath
if isinstance(path, (str, Path)):
path = checkPath(path, self._homePath)
if not path.is_dir():
path = path.parent
if path.is_dir():
self._lastPath = path
logger.debug("Last path updated: %s" % self._lastPath)
return
def setBackupPath(self, backupPath: Path | None):
def setBackupPath(self, path: Path | str) -> None:
"""Set the current backup path."""
self._backupPath = checkPath(backupPath, None)
self._backupPath = checkPath(path, self._backPath)
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 to default font.
"""
@@ -364,15 +384,11 @@ class Config:
##
def pxInt(self, value: int) -> int:
"""Used to scale fixed gui sizes by the screen scale factor.
This function returns an int, which is always rounded down.
"""
"""Scale fixed gui sizes by the screen scale factor."""
return int(value*self.guiScale)
def rpxInt(self, value: int) -> int:
"""Used to un-scale fixed gui sizes by the screen scale factor.
This function returns an int, which is always rounded down.
"""
"""Un-scale fixed gui sizes by the screen scale factor."""
return int(value/self.guiScale)
def dataPath(self, target: str | None = None) -> Path:
@@ -395,12 +411,12 @@ class Config:
return self._lastPath
return self._homePath
def backupPath(self) -> Path | None:
def backupPath(self) -> Path:
"""Return the backup path."""
if isinstance(self._backupPath, Path):
if self._backupPath.is_dir():
return self._backupPath
return None
return self._backPath
def errorText(self) -> str:
"""Compile and return error messages from the initialisation of
@@ -442,7 +458,8 @@ class Config:
# 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
and dataPath is mainly intended for the test suite.
"""
@@ -479,16 +496,15 @@ class Config:
else:
self.saveConfig()
self._recentProj.loadCache()
self._recentObj.loadCache()
self._checkOptionalPackages()
logger.debug("Config initialisation complete")
return
def initLocalisation(self, nwApp):
"""Initialise the localisation of the GUI.
"""
def initLocalisation(self, nwApp: QApplication) -> None:
"""Initialise the localisation of the GUI."""
self._qLocale = QLocale(self.guiLocale)
QLocale.setDefault(self._qLocale)
self._qtTrans = {}
@@ -509,16 +525,15 @@ class Config:
return
def loadConfig(self):
"""Load preferences from file and replace default settings.
"""
def loadConfig(self) -> bool:
"""Load preferences from file and replace default settings."""
logger.debug("Loading config file")
theConf = NWConfigParser()
conf = NWConfigParser()
cnfPath = self._confPath / nwFiles.CONF_FILE
try:
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
theConf.read_file(inFile)
conf.read_file(inFile)
except Exception as exc:
logger.error("Could not load config file")
logException()
@@ -528,98 +543,98 @@ class Config:
return False
# Main
cnfSec = "Main"
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
self.guiFont = theConf.rdStr(cnfSec, "font", self.guiFont)
self.guiFontSize = theConf.rdInt(cnfSec, "fontsize", self.guiFontSize)
self.guiLocale = theConf.rdStr(cnfSec, "localisation", self.guiLocale)
self.hideVScroll = theConf.rdBool(cnfSec, "hidevscroll", self.hideVScroll)
self.hideHScroll = theConf.rdBool(cnfSec, "hidehscroll", self.hideHScroll)
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
self._lastPath = theConf.rdPath(cnfSec, "lastpath", self._lastPath)
sec = "Main"
self.guiTheme = conf.rdStr(sec, "theme", self.guiTheme)
self.guiSyntax = conf.rdStr(sec, "syntax", self.guiSyntax)
self.guiFont = conf.rdStr(sec, "font", self.guiFont)
self.guiFontSize = conf.rdInt(sec, "fontsize", self.guiFontSize)
self.guiLocale = conf.rdStr(sec, "localisation", self.guiLocale)
self.hideVScroll = conf.rdBool(sec, "hidevscroll", self.hideVScroll)
self.hideHScroll = conf.rdBool(sec, "hidehscroll", self.hideHScroll)
self.lastNotes = conf.rdStr(sec, "lastnotes", self.lastNotes)
self._lastPath = conf.rdPath(sec, "lastpath", self._lastPath)
# Sizes
cnfSec = "Sizes"
self._mainWinSize = theConf.rdIntList(cnfSec, "mainwindow", self._mainWinSize)
self._prefsWinSize = theConf.rdIntList(cnfSec, "preferences", self._prefsWinSize)
self._projLoadCols = theConf.rdIntList(cnfSec, "projloadcols", self._projLoadCols)
self._mainPanePos = theConf.rdIntList(cnfSec, "mainpane", self._mainPanePos)
self._viewPanePos = theConf.rdIntList(cnfSec, "viewpane", self._viewPanePos)
self._outlnPanePos = theConf.rdIntList(cnfSec, "outlinepane", self._outlnPanePos)
sec = "Sizes"
self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize)
self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize)
self._projLoadCols = conf.rdIntList(sec, "projloadcols", self._projLoadCols)
self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos)
self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos)
self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos)
# Project
cnfSec = "Project"
self.autoSaveProj = theConf.rdInt(cnfSec, "autosaveproject", self.autoSaveProj)
self.autoSaveDoc = theConf.rdInt(cnfSec, "autosavedoc", self.autoSaveDoc)
self.emphLabels = theConf.rdBool(cnfSec, "emphlabels", self.emphLabels)
self._backupPath = theConf.rdPath(cnfSec, "backuppath", self._backupPath)
self.backupOnClose = theConf.rdBool(cnfSec, "backuponclose", self.backupOnClose)
self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup", self.askBeforeBackup)
sec = "Project"
self.autoSaveProj = conf.rdInt(sec, "autosaveproject", self.autoSaveProj)
self.autoSaveDoc = conf.rdInt(sec, "autosavedoc", self.autoSaveDoc)
self.emphLabels = conf.rdBool(sec, "emphlabels", self.emphLabels)
self._backupPath = conf.rdPath(sec, "backuppath", self._backupPath)
self.backupOnClose = conf.rdBool(sec, "backuponclose", self.backupOnClose)
self.askBeforeBackup = conf.rdBool(sec, "askbeforebackup", self.askBeforeBackup)
# Editor
cnfSec = "Editor"
self.textFont = theConf.rdStr(cnfSec, "textfont", self.textFont)
self.textSize = theConf.rdInt(cnfSec, "textsize", self.textSize)
self.textWidth = theConf.rdInt(cnfSec, "width", self.textWidth)
self.textMargin = theConf.rdInt(cnfSec, "margin", self.textMargin)
self.tabWidth = theConf.rdInt(cnfSec, "tabwidth", self.tabWidth)
self.focusWidth = theConf.rdInt(cnfSec, "focuswidth", self.focusWidth)
self.hideFocusFooter = theConf.rdBool(cnfSec, "hidefocusfooter", self.hideFocusFooter)
self.doJustify = theConf.rdBool(cnfSec, "justify", self.doJustify)
self.autoSelect = theConf.rdBool(cnfSec, "autoselect", self.autoSelect)
self.doReplace = theConf.rdBool(cnfSec, "autoreplace", self.doReplace)
self.doReplaceSQuote = theConf.rdBool(cnfSec, "repsquotes", self.doReplaceSQuote)
self.doReplaceDQuote = theConf.rdBool(cnfSec, "repdquotes", self.doReplaceDQuote)
self.doReplaceDash = theConf.rdBool(cnfSec, "repdash", self.doReplaceDash)
self.doReplaceDots = theConf.rdBool(cnfSec, "repdots", self.doReplaceDots)
self.scrollPastEnd = theConf.rdInt(cnfSec, "scrollpastend", self.scrollPastEnd)
self.autoScroll = theConf.rdBool(cnfSec, "autoscroll", self.autoScroll)
self.autoScrollPos = theConf.rdInt(cnfSec, "autoscrollpos", self.autoScrollPos)
self.fmtSQuoteOpen = theConf.rdStr(cnfSec, "fmtsquoteopen", self.fmtSQuoteOpen)
self.fmtSQuoteClose = theConf.rdStr(cnfSec, "fmtsquoteclose", self.fmtSQuoteClose)
self.fmtDQuoteOpen = theConf.rdStr(cnfSec, "fmtdquoteopen", self.fmtDQuoteOpen)
self.fmtDQuoteClose = theConf.rdStr(cnfSec, "fmtdquoteclose", self.fmtDQuoteClose)
self.fmtPadBefore = theConf.rdStr(cnfSec, "fmtpadbefore", self.fmtPadBefore)
self.fmtPadAfter = theConf.rdStr(cnfSec, "fmtpadafter", self.fmtPadAfter)
self.fmtPadThin = theConf.rdBool(cnfSec, "fmtpadthin", self.fmtPadThin)
self.spellLanguage = theConf.rdStr(cnfSec, "spellcheck", self.spellLanguage)
self.showTabsNSpaces = theConf.rdBool(cnfSec, "showtabsnspaces", self.showTabsNSpaces)
self.showLineEndings = theConf.rdBool(cnfSec, "showlineendings", self.showLineEndings)
self.showMultiSpaces = theConf.rdBool(cnfSec, "showmultispaces", self.showMultiSpaces)
self.wordCountTimer = theConf.rdFlt(cnfSec, "wordcounttimer", self.wordCountTimer)
self.bigDocLimit = theConf.rdInt(cnfSec, "bigdoclimit", self.bigDocLimit)
self.incNotesWCount = theConf.rdBool(cnfSec, "incnoteswcount", self.incNotesWCount)
self.showFullPath = theConf.rdBool(cnfSec, "showfullpath", self.showFullPath)
self.highlightQuotes = theConf.rdBool(cnfSec, "highlightquotes", self.highlightQuotes)
self.allowOpenSQuote = theConf.rdBool(cnfSec, "allowopensquote", self.allowOpenSQuote)
self.allowOpenDQuote = theConf.rdBool(cnfSec, "allowopendquote", self.allowOpenDQuote)
self.highlightEmph = theConf.rdBool(cnfSec, "highlightemph", self.highlightEmph)
self.stopWhenIdle = theConf.rdBool(cnfSec, "stopwhenidle", self.stopWhenIdle)
self.userIdleTime = theConf.rdInt(cnfSec, "useridletime", self.userIdleTime)
sec = "Editor"
self.textFont = conf.rdStr(sec, "textfont", self.textFont)
self.textSize = conf.rdInt(sec, "textsize", self.textSize)
self.textWidth = conf.rdInt(sec, "width", self.textWidth)
self.textMargin = conf.rdInt(sec, "margin", self.textMargin)
self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth)
self.focusWidth = conf.rdInt(sec, "focuswidth", self.focusWidth)
self.hideFocusFooter = conf.rdBool(sec, "hidefocusfooter", self.hideFocusFooter)
self.doJustify = conf.rdBool(sec, "justify", self.doJustify)
self.autoSelect = conf.rdBool(sec, "autoselect", self.autoSelect)
self.doReplace = conf.rdBool(sec, "autoreplace", self.doReplace)
self.doReplaceSQuote = conf.rdBool(sec, "repsquotes", self.doReplaceSQuote)
self.doReplaceDQuote = conf.rdBool(sec, "repdquotes", self.doReplaceDQuote)
self.doReplaceDash = conf.rdBool(sec, "repdash", self.doReplaceDash)
self.doReplaceDots = conf.rdBool(sec, "repdots", self.doReplaceDots)
self.scrollPastEnd = conf.rdInt(sec, "scrollpastend", self.scrollPastEnd)
self.autoScroll = conf.rdBool(sec, "autoscroll", self.autoScroll)
self.autoScrollPos = conf.rdInt(sec, "autoscrollpos", self.autoScrollPos)
self.fmtSQuoteOpen = conf.rdStr(sec, "fmtsquoteopen", self.fmtSQuoteOpen)
self.fmtSQuoteClose = conf.rdStr(sec, "fmtsquoteclose", self.fmtSQuoteClose)
self.fmtDQuoteOpen = conf.rdStr(sec, "fmtdquoteopen", self.fmtDQuoteOpen)
self.fmtDQuoteClose = conf.rdStr(sec, "fmtdquoteclose", self.fmtDQuoteClose)
self.fmtPadBefore = conf.rdStr(sec, "fmtpadbefore", self.fmtPadBefore)
self.fmtPadAfter = conf.rdStr(sec, "fmtpadafter", self.fmtPadAfter)
self.fmtPadThin = conf.rdBool(sec, "fmtpadthin", self.fmtPadThin)
self.spellLanguage = conf.rdStr(sec, "spellcheck", self.spellLanguage)
self.showTabsNSpaces = conf.rdBool(sec, "showtabsnspaces", self.showTabsNSpaces)
self.showLineEndings = conf.rdBool(sec, "showlineendings", self.showLineEndings)
self.showMultiSpaces = conf.rdBool(sec, "showmultispaces", self.showMultiSpaces)
self.wordCountTimer = conf.rdFlt(sec, "wordcounttimer", self.wordCountTimer)
self.bigDocLimit = conf.rdInt(sec, "bigdoclimit", self.bigDocLimit)
self.incNotesWCount = conf.rdBool(sec, "incnoteswcount", self.incNotesWCount)
self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
self.highlightQuotes = conf.rdBool(sec, "highlightquotes", self.highlightQuotes)
self.allowOpenSQuote = conf.rdBool(sec, "allowopensquote", self.allowOpenSQuote)
self.allowOpenDQuote = conf.rdBool(sec, "allowopendquote", self.allowOpenDQuote)
self.highlightEmph = conf.rdBool(sec, "highlightemph", self.highlightEmph)
self.stopWhenIdle = conf.rdBool(sec, "stopwhenidle", self.stopWhenIdle)
self.userIdleTime = conf.rdInt(sec, "useridletime", self.userIdleTime)
# State
cnfSec = "State"
self.showRefPanel = theConf.rdBool(cnfSec, "showrefpanel", self.showRefPanel)
self.viewComments = theConf.rdBool(cnfSec, "viewcomments", self.viewComments)
self.viewSynopsis = theConf.rdBool(cnfSec, "viewsynopsis", self.viewSynopsis)
self.searchCase = theConf.rdBool(cnfSec, "searchcase", self.searchCase)
self.searchWord = theConf.rdBool(cnfSec, "searchword", self.searchWord)
self.searchRegEx = theConf.rdBool(cnfSec, "searchregex", self.searchRegEx)
self.searchLoop = theConf.rdBool(cnfSec, "searchloop", self.searchLoop)
self.searchNextFile = theConf.rdBool(cnfSec, "searchnextfile", self.searchNextFile)
self.searchMatchCap = theConf.rdBool(cnfSec, "searchmatchcap", self.searchMatchCap)
sec = "State"
self.showRefPanel = conf.rdBool(sec, "showrefpanel", self.showRefPanel)
self.viewComments = conf.rdBool(sec, "viewcomments", self.viewComments)
self.viewSynopsis = conf.rdBool(sec, "viewsynopsis", self.viewSynopsis)
self.searchCase = conf.rdBool(sec, "searchcase", self.searchCase)
self.searchWord = conf.rdBool(sec, "searchword", self.searchWord)
self.searchRegEx = conf.rdBool(sec, "searchregex", self.searchRegEx)
self.searchLoop = conf.rdBool(sec, "searchloop", self.searchLoop)
self.searchNextFile = conf.rdBool(sec, "searchnextfile", self.searchNextFile)
self.searchMatchCap = conf.rdBool(sec, "searchmatchcap", self.searchMatchCap)
# Deprecated Settings or Locations as of 2.0
# 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.guiFontSize = theConf.rdInt("Main", "guifontsize", self.guiFontSize)
self.guiLocale = theConf.rdStr("Main", "guilang", self.guiLocale)
self._backupPath = theConf.rdPath("Backup", "backuppath", self._backupPath)
self.backupOnClose = theConf.rdBool("Backup", "backuponclose", self.backupOnClose)
self.askBeforeBackup = theConf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup)
fmtSingleQuotes = theConf.rdStrList(cnfSec, "fmtsinglequote", [])
fmtDoubleQuotes = theConf.rdStrList(cnfSec, "fmtdoublequote", [])
self.guiFont = conf.rdStr("Main", "guifont", self.guiFont)
self.guiFontSize = conf.rdInt("Main", "guifontsize", self.guiFontSize)
self.guiLocale = conf.rdStr("Main", "guilang", self.guiLocale)
self._backupPath = conf.rdPath("Backup", "backuppath", self._backupPath)
self.backupOnClose = conf.rdBool("Backup", "backuponclose", self.backupOnClose)
self.askBeforeBackup = conf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup)
fmtSingleQuotes = conf.rdStrList(sec, "fmtsinglequote", [])
fmtDoubleQuotes = conf.rdStrList(sec, "fmtdoublequote", [])
if isinstance(fmtSingleQuotes, list) and len(fmtSingleQuotes) == 2:
self.fmtSQuoteOpen = fmtSingleQuotes[0]
@@ -631,9 +646,6 @@ class Config:
# Check Values
# ============
# Check Certain Values for None
self.spellLanguage = self._checkNone(self.spellLanguage)
# If we're using straight quotes, disable auto-replace
if self.fmtSQuoteOpen == self.fmtSQuoteClose == "'" and self.doReplaceSQuote:
logger.info("Using straight single quotes, so disabling auto-replace")
@@ -645,18 +657,17 @@ class Config:
return True
def saveConfig(self):
"""Save the current preferences to file.
"""
def saveConfig(self) -> bool:
"""Save the current preferences to file."""
logger.debug("Saving config file")
theConf = NWConfigParser()
conf = NWConfigParser()
theConf["Meta"] = {
conf["Meta"] = {
"timestamp": formatTimeStamp(time()),
}
theConf["Main"] = {
conf["Main"] = {
"theme": str(self.guiTheme),
"syntax": str(self.guiSyntax),
"font": str(self.guiFont),
@@ -668,7 +679,7 @@ class Config:
"lastpath": str(self._lastPath),
}
theConf["Sizes"] = {
conf["Sizes"] = {
"mainwindow": self._packList(self._mainWinSize),
"preferences": self._packList(self._prefsWinSize),
"projloadcols": self._packList(self._projLoadCols),
@@ -677,16 +688,16 @@ class Config:
"outlinepane": self._packList(self._outlnPanePos),
}
theConf["Project"] = {
conf["Project"] = {
"autosaveproject": str(self.autoSaveProj),
"autosavedoc": str(self.autoSaveDoc),
"emphlabels": str(self.emphLabels),
"backuppath": str(self._backupPath or ""),
"backuppath": str(self._backupPath),
"backuponclose": str(self.backupOnClose),
"askbeforebackup": str(self.askBeforeBackup),
}
theConf["Editor"] = {
conf["Editor"] = {
"textfont": str(self.textFont),
"textsize": str(self.textSize),
"width": str(self.textWidth),
@@ -727,7 +738,7 @@ class Config:
"useridletime": str(self.userIdleTime),
}
theConf["State"] = {
conf["State"] = {
"showrefpanel": str(self.showRefPanel),
"viewcomments": str(self.viewComments),
"viewsynopsis": str(self.viewSynopsis),
@@ -743,7 +754,7 @@ class Config:
cnfPath = self._confPath / nwFiles.CONF_FILE
try:
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
theConf.write(outFile)
conf.write(outFile)
except Exception as exc:
logger.error("Could not save config file")
logException()
@@ -758,26 +769,14 @@ class Config:
# Internal Functions
##
def _packList(self, inData):
def _packList(self, data: list) -> str:
"""Pack a list of items into a comma-separated string for saving
to the config file.
"""
return ", ".join([str(inVal) for inVal in inData])
return ", ".join([str(inVal) for inVal in data])
def _checkNone(self, checkVal):
"""Return a NoneType if the value corresponds to None, otherwise
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.
"""
def _checkOptionalPackages(self) -> None:
"""Check optional packages used by some features."""
try:
import enchant # noqa: F401
except ImportError:
@@ -793,14 +792,13 @@ class Config:
class RecentProjects:
def __init__(self, config):
def __init__(self, config: Config) -> None:
self._conf = config
self._data = {}
return
def loadCache(self):
"""Load the cache file for recent projects.
"""
def loadCache(self) -> bool:
"""Load the cache file for recent projects."""
self._data = {}
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
@@ -823,9 +821,8 @@ class RecentProjects:
return True
def saveCache(self):
"""Save the cache dictionary of recent projects.
"""
def saveCache(self) -> bool:
"""Save the cache dictionary of recent projects."""
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
cacheTemp = cacheFile.with_suffix(".tmp")
try:
@@ -839,27 +836,27 @@ class RecentProjects:
return True
def listEntries(self):
"""List all items in the cache.
"""
return [(k, e["title"], e["words"], e["time"]) for k, e in self._data.items()]
def listEntries(self) -> list[tuple[str, str, int, int]]:
"""List all items in the cache."""
return [
(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):
"""Add or update recent cache information on a given project.
"""
self._data[str(projPath)] = {
"title": projTitle,
"words": int(wordCount),
"time": int(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."""
self._data[str(path)] = {
"title": title,
"words": int(words),
"time": int(saved),
}
self.saveCache()
return
def remove(self, projPath):
"""Try to remove a path from the recent projects cache.
"""
if self._data.pop(str(projPath), None) is not None:
logger.debug("Removed recent: %s", projPath)
def remove(self, path: str | Path) -> None:
"""Try to remove a path from the recent projects cache."""
if self._data.pop(str(path), None) is not None:
logger.debug("Removed recent: %s", path)
self.saveCache()
return
+16 -16
View File
@@ -153,7 +153,7 @@ class BuildSettings:
The settings can be packed/unpacked to/from a dictionary for JSON.
"""
def __init__(self):
def __init__(self) -> None:
self._name = ""
self._uuid = str(uuid.uuid4())
self._path = Path.home()
@@ -239,12 +239,12 @@ class BuildSettings:
# Setters
##
def setName(self, name: str):
def setName(self, name: str) -> None:
"""Set the build setting display name."""
self._name = str(name)
return
def setBuildID(self, value: str | uuid.UUID):
def setBuildID(self, value: str | uuid.UUID) -> None:
"""Set a UUID build ID."""
value = checkUuid(value, "")
if not value:
@@ -253,7 +253,7 @@ class BuildSettings:
self._uuid = value
return
def setLastPath(self, path: Path | str | None):
def setLastPath(self, path: Path | str | None) -> None:
"""Set the last used build path."""
if isinstance(path, str):
path = Path(path)
@@ -264,41 +264,41 @@ class BuildSettings:
self._changed = True
return
def setLastBuildName(self, name: str):
def setLastBuildName(self, name: str) -> None:
"""Set the last used build name."""
self._build = str(name).strip()
self._changed = True
return
def setLastFormat(self, value: nwBuildFmt):
def setLastFormat(self, value: nwBuildFmt) -> None:
"""Set the last used build format."""
if isinstance(value, nwBuildFmt):
self._format = value
self._changed = True
return
def setFiltered(self, tHandle: str):
def setFiltered(self, tHandle: str) -> None:
"""Set an item as filtered."""
self._excluded.discard(tHandle)
self._included.discard(tHandle)
self._changed = True
return
def setIncluded(self, tHandle: str):
def setIncluded(self, tHandle: str) -> None:
"""Set an item as explicitly included."""
self._excluded.discard(tHandle)
self._included.add(tHandle)
self._changed = True
return
def setExcluded(self, tHandle: str):
def setExcluded(self, tHandle: str) -> None:
"""Set an item as explicitly excluded."""
self._excluded.add(tHandle)
self._included.discard(tHandle)
self._changed = True
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."""
if state is True:
self._skipRoot.discard(tHandle)
@@ -386,7 +386,7 @@ class BuildSettings:
return result
def resetChangedState(self):
def resetChangedState(self) -> None:
"""Reset the changed status of the settings object. This must be
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."""
settings = data.get("settings", {})
content = data.get("content", {})
@@ -454,13 +454,13 @@ class BuildCollection:
project folder.
"""
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._builds = {}
self._loadCollection()
return
def __len__(self):
def __len__(self) -> int:
"""Return the number of builds."""
return len(self._builds)
@@ -476,7 +476,7 @@ class BuildCollection:
build.unpack(self._builds[buildID])
return build
def setBuild(self, build: BuildSettings):
def setBuild(self, build: BuildSettings) -> None:
"""Set build settings data in the collection."""
if isinstance(build, BuildSettings):
buildID = build.buildID
@@ -484,7 +484,7 @@ class BuildCollection:
self._saveCollection()
return
def removeBuild(self, buildID: str):
def removeBuild(self, buildID: str) -> None:
"""Remove the a build from the collection."""
self._builds.pop(buildID, None)
self._saveCollection()
+7 -6
View File
@@ -80,7 +80,7 @@ class DocMerger:
and a new doc label. Calling this function resets the class.
"""
srcItem = self._project.tree[srcHandle]
if srcItem is None:
if srcItem is None or srcItem.itemParent is None:
return None
newHandle = self._project.newFile(docLabel, srcItem.itemParent)
@@ -210,7 +210,7 @@ class DocSplitter:
"""An iterator that will write each document in the buffer, and
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
pHandle = self._parHandle
@@ -385,9 +385,10 @@ class ProjectBuilder:
aDoc = project.storage.getDocument(hChapter)
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
hScene = project.newFile(lblNewScene, hChapter)
aDoc = project.storage.getDocument(hScene)
aDoc.writeDocument(f"### {lblNewScene}\n\n")
if hChapter:
hScene = project.newFile(lblNewScene, hChapter)
aDoc = project.storage.getDocument(hScene)
aDoc.writeDocument(f"### {lblNewScene}\n\n")
project.newRoot(nwItemClass.PLOT)
project.newRoot(nwItemClass.CHARACTER)
@@ -418,7 +419,7 @@ class ProjectBuilder:
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
# Create chapter scenes
if numScenes > 0:
if numScenes > 0 and cHandle:
for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = project.newFile(scTitle, cHandle)
+3 -3
View File
@@ -54,7 +54,7 @@ class NWBuildDocument:
__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._build = build
self._queue = []
@@ -91,12 +91,12 @@ class NWBuildDocument:
# Methods
##
def addDocument(self, tHandle: str):
def addDocument(self, tHandle: str) -> None:
"""Add a document to the build queue manually."""
self._queue.append(tHandle)
return
def queueAll(self):
def queueAll(self) -> None:
"""Queue all document as defined by the build settings."""
self._queue = []
filtered = self._build.buildItemFilter(self._project)
+7 -14
View File
@@ -1,7 +1,6 @@
"""
novelWriter Project Wrapper
=============================
The parent class for a novelWriter project
File History:
Created: 2018-09-29 [0.0.1]
@@ -416,14 +415,6 @@ class NWProject(QObject):
logger.info("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:
self.mainGui.makeAlert(self.tr(
"Cannot backup project because no project name is set. "
@@ -432,9 +423,10 @@ class NWProject(QObject):
return False
cleanName = makeFileNameSafe(self._data.name)
backupPath = CONFIG.backupPath()
baseDir = backupPath / cleanName
try:
baseDir.mkdir(exist_ok=True)
baseDir.mkdir(exist_ok=True, parents=True)
except Exception as exc:
self.mainGui.makeAlert(self.tr(
"Could not create backup folder."
@@ -444,11 +436,12 @@ class NWProject(QObject):
timeStamp = formatTimeStamp(time(), fileSafe=True)
archName = baseDir / f"{cleanName} {timeStamp}.zip"
if self._storage.zipIt(archName, compression=2):
size = archName.stat().st_size
size = formatInt(archName.stat().st_size)
if doNotify:
self.mainGui.makeAlert(self.tr(
"Backup archive file written to: {0} [{1}B]"
).format(str(archName), formatInt(size)))
self.mainGui.makeAlert(
self.tr("Created a backup of your project of size {0}B.").format(size),
info=self.tr("Path: {0}").format(str(backupPath))
)
else:
self.mainGui.makeAlert(self.tr(
"Could not write backup archive."
+40 -37
View File
@@ -26,13 +26,16 @@ from __future__ import annotations
import uuid
import logging
from typing import Any
from typing import TYPE_CHECKING, Any
from novelwriter.common import (
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
)
from novelwriter.core.status import NWStatus
if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
@@ -43,9 +46,9 @@ class NWProjectData:
the list of project items.
"""
def __init__(self, theProject):
def __init__(self, project: NWProject) -> None:
self.theProject = theProject
self._project = project
# Project Meta
self._uuid = ""
@@ -184,16 +187,16 @@ class NWProjectData:
# Methods
##
def incSaveCount(self):
def incSaveCount(self) -> None:
"""Increment the save count by one."""
self._saveCount += 1
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def incAutoCount(self):
def incAutoCount(self) -> None:
"""Increment the auto save count by one."""
self._autoCount += 1
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
##
@@ -208,93 +211,93 @@ class NWProjectData:
# Setters
##
def setUuid(self, value: Any):
def setUuid(self, value: Any) -> None:
"""Set the project id."""
value = checkUuid(value, "")
if not value:
self._uuid = str(uuid.uuid4())
elif value != self._uuid:
self._uuid = value
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setName(self, value: str | None):
def setName(self, value: str | None) -> None:
"""Set a new project name."""
if value != self._name:
self._name = simplified(str(value or ""))
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setTitle(self, value: str | None):
def setTitle(self, value: str | None) -> None:
"""Set a new novel title."""
if value != self._title:
self._title = simplified(str(value or ""))
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setAuthor(self, value: str | None):
def setAuthor(self, value: str | None) -> None:
"""Set the author value."""
if value != self._title:
self._author = simplified(str(value or ""))
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setSaveCount(self, value: Any):
def setSaveCount(self, value: Any) -> None:
"""Set the save count from last session."""
self._saveCount = checkInt(value, 0)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setAutoCount(self, value: Any):
def setAutoCount(self, value: Any) -> None:
"""Set the auto save count from last session."""
self._autoCount = checkInt(value, 0)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setEditTime(self, value: Any):
def setEditTime(self, value: Any) -> None:
"""Set the edit time from last session."""
self._editTime = checkInt(value, 0)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setDoBackup(self, value: Any):
def setDoBackup(self, value: Any) -> None:
"""Set the do write backup flag."""
if value != self._doBackup:
self._doBackup = checkBool(value, False)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setLanguage(self, value: str | None):
def setLanguage(self, value: str | None) -> None:
"""Set the project language."""
if value != self._language:
self._language = checkStringNone(value, None)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setSpellCheck(self, value: Any):
def setSpellCheck(self, value: Any) -> None:
"""Set the spell check flag."""
if value != self._spellCheck:
self._spellCheck = checkBool(value, False)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setSpellLang(self, value: str | None):
def setSpellLang(self, value: str | None) -> None:
"""Set the spell check language."""
if value != self._spellLang:
self._spellLang = checkStringNone(value, None)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
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
component.
"""
if isinstance(component, str):
self._lastHandle[component] = checkStringNone(value, None)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
def setLastHandles(self, value: dict):
def setLastHandles(self, value: dict) -> None:
"""Set the full last handles dictionary to a new set of values.
This is intended to be used at project load.
"""
@@ -302,10 +305,10 @@ class NWProjectData:
for key, entry in value.items():
if key in self._lastHandle:
self._lastHandle[key] = str(entry) if isHandle(entry) else None
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
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."""
if novel is not None:
self._initCounts[0] = checkInt(novel, 0)
@@ -315,7 +318,7 @@ class NWProjectData:
self._currCounts[1] = checkInt(notes, 0)
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."""
if novel is not None:
self._currCounts[0] = checkInt(novel, 0)
@@ -323,14 +326,14 @@ class NWProjectData:
self._currCounts[1] = checkInt(notes, 0)
return
def setAutoReplace(self, value: dict):
def setAutoReplace(self, value: dict) -> None:
"""Set the auto-replace dictionary."""
if isinstance(value, dict):
self._autoReplace = {}
for key, entry in value.items():
if isinstance(entry, str):
self._autoReplace[key] = simplified(entry)
self.theProject.setProjectChanged(True)
self._project.setProjectChanged(True)
return
# END Class NWProjectData
+15 -11
View File
@@ -110,7 +110,7 @@ class ProjectXMLReader:
Rev 1: Drops the titleFormat section of settings.
"""
def __init__(self, path):
def __init__(self, path: str | Path) -> None:
self._path = Path(path)
self._state = XMLReadState.NO_ACTION
self._root = ""
@@ -236,7 +236,7 @@ class ProjectXMLReader:
# 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."""
logger.debug("Parsing <project> section")
@@ -267,7 +267,7 @@ class ProjectXMLReader:
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."""
logger.debug("Parsing <settings> section")
@@ -307,7 +307,9 @@ class ProjectXMLReader:
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."""
logger.debug("Parsing <content> section")
@@ -362,7 +364,9 @@ class ProjectXMLReader:
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."""
logger.debug("Parsing <content> section (legacy format)")
@@ -438,7 +442,7 @@ class ProjectXMLReader:
return
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus):
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus) -> None:
"""Parse a status or importance entry."""
for xEntry in xItem:
if xEntry.tag == "entry":
@@ -447,7 +451,7 @@ class ProjectXMLReader:
green = checkInt(xEntry.attrib.get("green", 0), 0)
blue = checkInt(xEntry.attrib.get("blue", 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
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
@@ -460,7 +464,7 @@ class ProjectXMLReader:
result[xEntry.attrib["key"]] = checkString(xEntry.text, "")
return result
def _parseDictTagText(self, xItem):
def _parseDictTagText(self, xItem) -> dict:
"""Parse a dictionary stored with key as the tag and the value
as the text property.
"""
@@ -476,7 +480,7 @@ class ProjectXMLWriter:
very latest spec.
"""
def __init__(self, path):
def __init__(self, path: str | Path) -> None:
self._path = Path(path)
self._error = None
return
@@ -585,13 +589,13 @@ class ProjectXMLWriter:
def _packSingleValue(
self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None
):
) -> None:
"""Pack a single value into an XML element."""
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
xItem.text = str(value) or ""
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."""
xItem = ET.SubElement(xParent, name)
for key, value in data.items():
+2 -2
View File
@@ -47,7 +47,7 @@ class NWSessionLog:
format. That is, one JSON object per line.
"""
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._start = 0.0
return
@@ -65,7 +65,7 @@ class NWSessionLog:
# Methods
##
def startSession(self):
def startSession(self) -> None:
"""Start the writing session."""
self._start = time()
return
+5 -5
View File
@@ -45,7 +45,7 @@ class NWSpellEnchant:
between spell check tools.
"""
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._dictObj = FakeEnchant()
self._userDict = UserDictionary(project)
@@ -165,7 +165,7 @@ class NWSpellEnchant:
class FakeEnchant:
"""Fallback for when Enchant is selected, but not installed."""
def __init__(self):
def __init__(self) -> None:
class FakeProvider:
name = ""
@@ -189,7 +189,7 @@ class FakeEnchant:
class UserDictionary:
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._words = set()
self._path = None
@@ -210,7 +210,7 @@ class UserDictionary:
self._words.add(word)
return True
def load(self):
def load(self) -> None:
"""Load the user's dictionary."""
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
if not isinstance(self._path, Path):
@@ -224,7 +224,7 @@ class UserDictionary:
logException()
return
def save(self):
def save(self) -> None:
"""Save the user's dictionary."""
if self._path is None:
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
+48 -64
View File
@@ -1,7 +1,6 @@
"""
novelWriter Project Item Status Class
=======================================
Data class for the status/importance settings of a project item
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import random
import logging
from typing import TYPE_CHECKING, ItemsView, Iterator, KeysView, Literal, ValuesView
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
from PyQt5.QtCore import QRectF, Qt
from novelwriter import CONFIG
from novelwriter.common import minmax, simplified
if TYPE_CHECKING: # pragma: no cover
from typing import TypeGuard # Requires Python 3.10
logger = logging.getLogger(__name__)
@@ -41,9 +46,9 @@ class NWStatus:
STATUS = 1
IMPORT = 2
def __init__(self, type):
def __init__(self, kind: Literal[1, 2]) -> None:
self._type = type
self._type = kind
self._store = {}
self._default = None
@@ -66,7 +71,7 @@ class NWStatus:
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
key is generated.
"""
@@ -96,10 +101,8 @@ class NWStatus:
return key
def remove(self, key):
"""Remove an entry in the list, but not if the count is larger
than 0.
"""
def remove(self, key: str) -> bool:
"""Remove an entry in the list, except if the count > 0."""
if key not in self._store:
return False
if self._store[key]["count"] > 0:
@@ -116,59 +119,48 @@ class NWStatus:
return True
def check(self, value):
"""Check the key against the stored status names.
"""
def check(self, value: str) -> str:
"""Check the key against the stored status names."""
if self._isKey(value) and value in self._store:
return value
elif self._default is not None:
return self._default
else:
return ""
return ""
def name(self, key):
"""Return the name associated with a given key.
"""
def name(self, key: str | None) -> str:
"""Return the name associated with a given key."""
if key in self._store:
return self._store[key]["name"]
elif self._default is not None:
return self._store[self._default]["name"]
else:
return ""
return ""
def cols(self, key):
"""Return the colours associated with a given key.
"""
def cols(self, key: str | None) -> tuple[int, int, int]:
"""Return the colours associated with a given key."""
if key in self._store:
return self._store[key]["cols"]
elif self._default is not None:
return self._store[self._default]["cols"]
else:
return (100, 100, 100)
return 100, 100, 100
def count(self, key):
"""Return the count associated with a given key.
"""
def count(self, key: str | None) -> int:
"""Return the count associated with a given key."""
if key in self._store:
return self._store[key]["count"]
elif self._default is not None:
return self._store[self._default]["count"]
else:
return 0
return 0
def icon(self, key):
"""Return the icon associated with a given key.
"""
def icon(self, key: str | None) -> QIcon:
"""Return the icon associated with a given key."""
if key in self._store:
return self._store[key]["icon"]
elif self._default is not None:
return self._store[self._default]["icon"]
else:
return self._defaultIcon
return self._defaultIcon
def reorder(self, order):
"""Reorder the items according to list.
"""
def reorder(self, order: list[str]) -> bool:
"""Reorder the items according to list."""
if len(order) != len(self._store):
logger.error("Length mismatch between new and old order")
return False
@@ -188,23 +180,20 @@ class NWStatus:
return True
def resetCounts(self):
"""Clear the counts of references to the status entries.
"""
def resetCounts(self) -> None:
"""Clear the counts of references to the status entries."""
for key in self._store:
self._store[key]["count"] = 0
return
def increment(self, key):
"""Increment the counter for a given entry.
"""
def increment(self, key: str) -> None:
"""Increment the counter for a given entry."""
if key in self._store:
self._store[key]["count"] += 1
return
def pack(self):
"""Pack the status entries into a dictionary.
"""
def pack(self) -> Iterator[tuple[str, dict]]:
"""Pack the status entries into a dictionary."""
for key, data in self._store.items():
yield (data["name"], {
"key": key,
@@ -215,25 +204,22 @@ class NWStatus:
})
return
def unpack(self, data):
"""Unpack a data dictionary and set the class values.
"""
def unpack(self, data: dict) -> None:
"""Unpack a data dictionary and set the class values."""
self._store = {}
self._default = None
for key, entry in data.items():
label = entry.get("label", "")
colour = entry.get("colour", (100, 100, 100))
count = entry.get("count", 0)
self.write(key, label, colour, count)
return True
return
##
# Internal Functions
##
def _newKey(self):
def _newKey(self) -> str:
"""Generate a new key for a status flag. This method is
recursive, but should only fail if there is an issue with the
random number generator or the user has added a lot of status
@@ -245,9 +231,8 @@ class NWStatus:
key = self._newKey()
return key
def _isKey(self, value):
"""Check if a value is a key or not.
"""
def _isKey(self, value: str | None) -> TypeGuard[str]:
"""Check if a value is a key or not."""
if not isinstance(value, str):
return False
if len(value) != 7:
@@ -259,9 +244,8 @@ class NWStatus:
return False
return True
def _createIcon(self, red, green, blue):
"""Generate an icon for a status label.
"""
def _createIcon(self, red: int, green: int, blue: int) -> QIcon:
"""Generate an icon for a status label."""
pixmap = QPixmap(self._iPX, self._iPX)
pixmap.fill(Qt.transparent)
@@ -276,22 +260,22 @@ class NWStatus:
# Iterator Bits
##
def __len__(self):
def __len__(self) -> int:
return len(self._store)
def __getitem__(self, key):
def __getitem__(self, key: str) -> dict:
return self._store[key]
def __iter__(self):
def __iter__(self) -> Iterator[dict]:
return iter(self._store)
def keys(self):
def keys(self) -> KeysView[str]:
return self._store.keys()
def items(self):
def items(self) -> ItemsView[str, dict]:
return self._store.items()
def values(self):
def values(self) -> ValuesView[dict]:
return self._store.values()
# END Class NWStatus
+9 -9
View File
@@ -55,7 +55,7 @@ class NWStorage:
MODE_INPLACE = 1
MODE_ARCHIVE = 2
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._storagePath = None
self._runtimePath = None
@@ -63,7 +63,7 @@ class NWStorage:
self._openMode = self.MODE_INACTIVE
return
def clear(self):
def clear(self) -> None:
"""Reset internal variables."""
self._storagePath = None
self._runtimePath = None
@@ -145,7 +145,7 @@ class NWStorage:
return True
return True
def closeSession(self):
def closeSession(self) -> None:
"""Run tasks related to closing the session."""
self.clearLockFile()
self.clear()
@@ -353,11 +353,11 @@ class _LegacyStorage:
file/folder layout to the current project format.
"""
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
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
project.
"""
@@ -396,7 +396,7 @@ class _LegacyStorage:
return
def deprecatedFiles(self, path: Path):
def deprecatedFiles(self, path: Path) -> None:
"""Handle files that are no longer used by novelWriter."""
self._convertOldWordList( # Changed in 2.1 Beta 1
path / "meta" / "wordlist.txt",
@@ -440,7 +440,7 @@ class _LegacyStorage:
# 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."""
if wordJson.exists() or not wordList.exists():
# If the new file already exists, we won't overwrite it
@@ -466,7 +466,7 @@ class _LegacyStorage:
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
format.
"""
@@ -507,7 +507,7 @@ class _LegacyStorage:
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."""
if optsNew.exists() or not optsOld.exists():
# 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_EBOOK = 2 # Tweak output for converting to epub
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
super().__init__(project)
self._genMode = self.M_EXPORT
self._cssStyles = True
self._fullHTML = []
self._fullHTML: list[str] = []
# Internals
self._trMap = {}
@@ -67,14 +67,14 @@ class ToHtml(Tokenizer):
##
@property
def fullHTML(self):
def fullHTML(self) -> list[str]:
return self._fullHTML
##
# 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
need to make a few changes to formatting, which is managed by
these flags.
@@ -85,14 +85,14 @@ class ToHtml(Tokenizer):
self._doSynopsis = doSynopsis
return
def setStyles(self, cssStyles: bool):
def setStyles(self, cssStyles: bool) -> None:
"""Enable or disable CSS styling. Some elements may still have
class tags.
"""
self._cssStyles = cssStyles
return
def setReplaceUnicode(self, doReplace: bool):
def setReplaceUnicode(self, doReplace: bool) -> None:
"""Set the translation map to either minimal or full unicode for
html entities replacement.
"""
@@ -113,7 +113,7 @@ class ToHtml(Tokenizer):
"""Return the size of the full HTML result."""
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
characters into their respective HTML entities.
"""
@@ -121,7 +121,7 @@ class ToHtml(Tokenizer):
self._text = self._text.translate(self._trMap)
return
def doConvert(self):
def doConvert(self) -> None:
"""Convert the list of text tokens into a HTML document saved
to _result.
"""
@@ -299,7 +299,7 @@ class ToHtml(Tokenizer):
return
def saveHtml5(self, path: str | Path):
def saveHtml5(self, path: str | Path) -> None:
"""Save the data to an HTML file."""
with open(path, mode="w", encoding="utf-8") as fObj:
fObj.write((
@@ -326,7 +326,7 @@ class ToHtml(Tokenizer):
logger.info("Wrote file: %s", path)
return
def saveHtmlJson(self, path: str | Path):
def saveHtmlJson(self, path: str | Path) -> None:
"""Save the data to a JSON file."""
timeStamp = time()
data = {
@@ -347,7 +347,7 @@ class ToHtml(Tokenizer):
logger.info("Wrote file: %s", path)
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."""
htmlText = []
tabSpace = spaceChar*nSpaces
@@ -357,7 +357,7 @@ class ToHtml(Tokenizer):
self._fullHTML = htmlText
return
def getStyleSheet(self) -> list:
def getStyleSheet(self) -> list[str]:
"""Generate a stylesheet for the current settings."""
styles = []
if not self._cssStyles:
+34 -34
View File
@@ -44,7 +44,7 @@ from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
def stripEscape(text):
def stripEscape(text) -> str:
"""Helper function to strip escaped Markdown characters from
paragraph text.
"""
@@ -100,7 +100,7 @@ class Tokenizer(ABC):
A_IND_L = 0x0100 # Left indentation
A_IND_R = 0x0200 # Right indentation
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
@@ -191,116 +191,116 @@ class Tokenizer(ABC):
# Setters
##
def setTitleFormat(self, hFormat: str):
def setTitleFormat(self, hFormat: str) -> None:
"""Set the title format pattern."""
self._fmtTitle = hFormat.strip()
return
def setChapterFormat(self, hFormat: str):
def setChapterFormat(self, hFormat: str) -> None:
"""Set the chapert format pattern."""
self._fmtChapter = hFormat.strip()
return
def setUnNumberedFormat(self, hFormat: str):
def setUnNumberedFormat(self, hFormat: str) -> None:
"""Set the unnumbered format pattern."""
self._fmtUnNum = hFormat.strip()
return
def setSceneFormat(self, hFormat: str, hide: bool):
def setSceneFormat(self, hFormat: str, hide: bool) -> None:
"""Set the scene format pattern and hidden status."""
self._fmtScene = hFormat.strip()
self._hideScene = hide
return
def setSectionFormat(self, hFormat: str, hide: bool):
def setSectionFormat(self, hFormat: str, hide: bool) -> None:
"""Set the section format pattern and hidden status."""
self._fmtSection = hFormat.strip()
self._hideSection = hide
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."""
self._textFont = family
self._textSize = round(int(size))
self._textFixed = isFixed
return
def setLineHeight(self, height: float):
def setLineHeight(self, height: float) -> None:
"""Set the line height between 0.5 and 5.0."""
self._lineHeight = min(max(float(height), 0.5), 5.0)
return
def setBlockIndent(self, indent: float):
def setBlockIndent(self, indent: float) -> None:
"""Set the block indent between 0.0 and 10.0."""
self._blockIndent = min(max(float(indent), 0.0), 10.0)
return
def setJustify(self, state: bool):
def setJustify(self, state: bool) -> None:
"""Enable or disable text justification."""
self._doJustify = state
return
def setTitleMargins(self, upper: float, lower: float):
def setTitleMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower title margin."""
self._marginTitle = (float(upper), float(lower))
return
def setHead1Margins(self, upper: float, lower: float):
def setHead1Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 1 margin."""
self._marginHead1 = (float(upper), float(lower))
return
def setHead2Margins(self, upper: float, lower: float):
def setHead2Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 2 margin."""
self._marginHead2 = (float(upper), float(lower))
return
def setHead3Margins(self, upper: float, lower: float):
def setHead3Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 3 margin."""
self._marginHead3 = (float(upper), float(lower))
return
def setHead4Margins(self, upper: float, lower: float):
def setHead4Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 4 margin."""
self._marginHead4 = (float(upper), float(lower))
return
def setTextMargins(self, upper: float, lower: float):
def setTextMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower text margin."""
self._marginText = (float(upper), float(lower))
return
def setMetaMargins(self, upper: float, lower: float):
def setMetaMargins(self, upper: float, lower: float) -> None:
"""Set the upper and lower meta text margin."""
self._marginMeta = (float(upper), float(lower))
return
def setLinkHeaders(self, state: bool):
def setLinkHeaders(self, state: bool) -> None:
"""Enable or disable adding an anchor before headers."""
self._linkHeaders = state
return
def setBodyText(self, state: bool):
def setBodyText(self, state: bool) -> None:
"""Include body text in build."""
self._doBodyText = state
return
def setSynopsis(self, state: bool):
def setSynopsis(self, state: bool) -> None:
"""Include synopsis comments in build."""
self._doSynopsis = state
return
def setComments(self, state: bool):
def setComments(self, state: bool) -> None:
"""Include comments in build."""
self._doComments = state
return
def setKeywords(self, state: bool):
def setKeywords(self, state: bool) -> None:
"""Include keywords in build."""
self._doKeywords = state
return
def setKeepMarkdown(self, state: bool):
def setKeepMarkdown(self, state: bool) -> None:
"""Keep original markdown during build."""
self._keepMarkdown = state
return
@@ -310,7 +310,7 @@ class Tokenizer(ABC):
##
@abstractmethod
def doConvert(self):
def doConvert(self) -> None:
raise NotImplementedError
def addRootHeading(self, tHandle: str) -> bool:
@@ -365,7 +365,7 @@ class Tokenizer(ABC):
return True
def doPreProcessing(self):
def doPreProcessing(self) -> None:
"""Run trough the various replace dictionaries."""
# Process the user's auto-replace dictionary
autoReplace = self._project.data.autoReplace
@@ -382,7 +382,7 @@ class Tokenizer(ABC):
return
def tokenizeText(self):
def tokenizeText(self) -> None:
"""Scan the text for either lines starting with specific
characters that indicate headers, comments, commands etc, or
just contain plain text. In the case of plain text, apply the
@@ -742,14 +742,14 @@ class Tokenizer(ABC):
return True
def saveRawMarkdown(self, path: str | Path):
def saveRawMarkdown(self, path: str | Path) -> None:
"""Save the raw text to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile:
for nwdPage in self._allMarkdown:
outFile.write(nwdPage)
return
def saveRawMarkdownJSON(self, path: str | Path):
def saveRawMarkdownJSON(self, path: str | Path) -> None:
"""Save the raw text to a JSON file."""
timeStamp = time()
data = {
@@ -773,30 +773,30 @@ class Tokenizer(ABC):
class HeadingFormatter:
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
self._project = project
self._chCount = 0
self._scChCount = 0
self._scAbsCount = 0
return
def incChapter(self):
def incChapter(self) -> None:
"""Increment the chapter counter."""
self._chCount += 1
return
def incScene(self):
def incScene(self) -> None:
"""Increment the scene counters."""
self._scChCount += 1
self._scAbsCount += 1
return
def resetScene(self):
def resetScene(self) -> None:
"""Reset the chapter scene counter."""
self._scChCount = 0
return
def apply(self, hFormat: str, text: str):
def apply(self, hFormat: str, text: str) -> str:
"""Apply formatting to a specific heading."""
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
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_GH = 1 # GitHub Markdown
def __init__(self, project: NWProject):
def __init__(self, project: NWProject) -> None:
super().__init__(project)
self._genMode = self.M_STD
self._fullMD = []
self._fullMD: list[str] = []
return
##
@@ -58,7 +56,7 @@ class ToMarkdown(Tokenizer):
##
@property
def fullMD(self) -> list:
def fullMD(self) -> list[str]:
"""Return the markdown as a list."""
return self._fullMD
@@ -66,11 +64,11 @@ class ToMarkdown(Tokenizer):
# Setters
##
def setStandardMarkdown(self):
def setStandardMarkdown(self) -> None:
self._genMode = self.M_STD
return
def setGitHubMarkdown(self):
def setGitHubMarkdown(self) -> None:
self._genMode = self.M_GH
return
@@ -82,7 +80,7 @@ class ToMarkdown(Tokenizer):
"""Return the size of the full Markdown result."""
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
to theResult.
"""
@@ -175,14 +173,14 @@ class ToMarkdown(Tokenizer):
return
def saveMarkdown(self, path: str | Path):
def saveMarkdown(self, path: str | Path) -> None:
"""Save the data to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile:
outFile.write("".join(self._fullMD))
logger.info("Wrote file: %s", path)
return
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "):
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
"""Replace tabs with spaces."""
spaces = spaceChar*nSpaces
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/
"""
def __init__(self, project: NWProject, isFlat: bool):
def __init__(self, project: NWProject, isFlat: bool) -> None:
super().__init__(project)
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
@@ -188,7 +188,7 @@ class ToOdt(Tokenizer):
# Setters
##
def setLanguage(self, language: str):
def setLanguage(self, language: str) -> None:
"""Set language for the document."""
if language:
langBits = language.split("_")
@@ -197,7 +197,7 @@ class ToOdt(Tokenizer):
self._dCountry = langBits[1]
return
def setColourHeaders(self, state: bool):
def setColourHeaders(self, state: bool) -> None:
"""Enable/disable coloured headings and comments."""
self._colourHead = state
return
@@ -205,7 +205,7 @@ class ToOdt(Tokenizer):
def setPageLayout(
self, width: int | float, height: int | float,
top: int | float, bottom: int | float, left: int | float, right: int | float
):
) -> None:
"""Set the document page size and margins in millimetres."""
self._mDocWidth = f"{width/10.0:.3f}cm"
self._mDocHeight = f"{height/10.0:.3f}cm"
@@ -219,7 +219,7 @@ class ToOdt(Tokenizer):
# Class Methods
##
def initDocument(self):
def initDocument(self) -> None:
"""Initialises a new open document XML tree."""
# Initialise Variables
# ====================
@@ -381,7 +381,7 @@ class ToOdt(Tokenizer):
return
def doConvert(self):
def doConvert(self) -> None:
"""Convert the list of text tokens into XML elements."""
self._result = "" # Not used, but cleared just in case
@@ -599,7 +599,7 @@ class ToOdt(Tokenizer):
def _addTextPar(
self, styleName: str, oStyle: ODTParagraphStyle, tText: str, tFmt: str = "",
isHead: bool = False, oLevel: str | None = None
):
) -> None:
"""Add a text paragraph to the text XML element."""
tAttr = {}
tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
@@ -726,7 +726,7 @@ class ToOdt(Tokenizer):
# Style Elements
##
def _pageStyles(self):
def _pageStyles(self) -> None:
"""Set the default page style."""
tAttr = {}
tAttr[_mkTag("style", "name")] = "PM1"
@@ -756,7 +756,7 @@ class ToOdt(Tokenizer):
return
def _defaultStyles(self):
def _defaultStyles(self) -> None:
"""Set the default styles."""
# Add Paragraph Family Style
# ==========================
@@ -829,7 +829,7 @@ class ToOdt(Tokenizer):
return
def _useableStyles(self):
def _useableStyles(self) -> None:
"""Set the usable styles."""
# Add Text Body Style
# ===================
@@ -1002,7 +1002,7 @@ class ToOdt(Tokenizer):
return
def _writeHeader(self):
def _writeHeader(self) -> None:
"""Write the header elements."""
tAttr = {}
tAttr[_mkTag("style", "name")] = "Standard"
@@ -1048,7 +1048,7 @@ class ODTParagraphStyle:
VALID_CLASS = ["text", "chapter"]
VALID_WEIGHT = ["normal", "inherit", "bold"]
def __init__(self):
def __init__(self) -> None:
# Attributes
self._mAttr = {
@@ -1087,26 +1087,26 @@ class ODTParagraphStyle:
# Attribute Setters
##
def setDisplayName(self, value: str | None):
def setDisplayName(self, value: str | None) -> None:
self._mAttr["display-name"][1] = value
return
def setParentStyleName(self, value: str | None):
def setParentStyleName(self, value: str | None) -> None:
self._mAttr["parent-style-name"][1] = value
return
def setNextStyleName(self, value: str | None):
def setNextStyleName(self, value: str | None) -> None:
self._mAttr["next-style-name"][1] = value
return
def setOutlineLevel(self, value: str | None):
def setOutlineLevel(self, value: str | None) -> None:
if value in self.VALID_LEVEL:
self._mAttr["default-outline-level"][1] = value
else:
self._mAttr["default-outline-level"][1] = None
return
def setClass(self, value: str | None):
def setClass(self, value: str | None) -> None:
if value in self.VALID_CLASS:
self._mAttr["class"][1] = value
else:
@@ -1117,41 +1117,41 @@ class ODTParagraphStyle:
# Paragraph Setters
##
def setMarginTop(self, value: str | None):
def setMarginTop(self, value: str | None) -> None:
self._pAttr["margin-top"][1] = value
return
def setMarginBottom(self, value: str | None):
def setMarginBottom(self, value: str | None) -> None:
self._pAttr["margin-bottom"][1] = value
return
def setMarginLeft(self, value: str | None):
def setMarginLeft(self, value: str | None) -> None:
self._pAttr["margin-left"][1] = value
return
def setMarginRight(self, value: str | None):
def setMarginRight(self, value: str | None) -> None:
self._pAttr["margin-right"][1] = value
return
def setLineHeight(self, value: str | None):
def setLineHeight(self, value: str | None) -> None:
self._pAttr["line-height"][1] = value
return
def setTextAlign(self, value: str | None):
def setTextAlign(self, value: str | None) -> None:
if value in self.VALID_ALIGN:
self._pAttr["text-align"][1] = value
else:
self._pAttr["text-align"][1] = None
return
def setBreakBefore(self, value: str | None):
def setBreakBefore(self, value: str | None) -> None:
if value in self.VALID_BREAK:
self._pAttr["break-before"][1] = value
else:
self._pAttr["break-before"][1] = None
return
def setBreakAfter(self, value: str | None):
def setBreakAfter(self, value: str | None) -> None:
if value in self.VALID_BREAK:
self._pAttr["break-after"][1] = value
else:
@@ -1162,30 +1162,30 @@ class ODTParagraphStyle:
# Text Setters
##
def setFontName(self, value: str | None):
def setFontName(self, value: str | None) -> None:
self._tAttr["font-name"][1] = value
return
def setFontFamily(self, value: str | None):
def setFontFamily(self, value: str | None) -> None:
self._tAttr["font-family"][1] = value
return
def setFontSize(self, value: str | None):
def setFontSize(self, value: str | None) -> None:
self._tAttr["font-size"][1] = value
return
def setFontWeight(self, value: str | None):
def setFontWeight(self, value: str | None) -> None:
if value in self.VALID_WEIGHT:
self._tAttr["font-weight"][1] = value
else:
self._tAttr["font-weight"][1] = None
return
def setColor(self, value: str | None):
def setColor(self, value: str | None) -> None:
self._tAttr["color"][1] = value
return
def setOpacity(self, value: str | None):
def setOpacity(self, value: str | None) -> None:
self._tAttr["opacity"][1] = value
return
@@ -1193,7 +1193,7 @@ class ODTParagraphStyle:
# Methods
##
def checkNew(self, refStyle: ODTParagraphStyle):
def checkNew(self, refStyle: ODTParagraphStyle) -> bool:
"""Check if there are new settings in refStyle that differ from
those in the current object.
"""
@@ -1217,7 +1217,7 @@ class ODTParagraphStyle:
)
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."""
theAttr = {}
theAttr[_mkTag("style", "name")] = name
@@ -1259,8 +1259,7 @@ class ODTTextStyle:
VALID_LSTYLE = ["none", "solid"]
VALID_LTYPE = ["none", "single", "double"]
def __init__(self):
def __init__(self) -> None:
# Text Attributes
self._tAttr = {
"font-weight": ["fo", None],
@@ -1268,35 +1267,34 @@ class ODTTextStyle:
"text-line-through-style": ["style", None],
"text-line-through-type": ["style", None],
}
return
##
# Setters
##
def setFontWeight(self, value: str | None):
def setFontWeight(self, value: str | None) -> None:
if value in self.VALID_WEIGHT:
self._tAttr["font-weight"][1] = value
else:
self._tAttr["font-weight"][1] = None
return
def setFontStyle(self, value: str | None):
def setFontStyle(self, value: str | None) -> None:
if value in self.VALID_STYLE:
self._tAttr["font-style"][1] = value
else:
self._tAttr["font-style"][1] = None
return
def setStrikeStyle(self, value: str | None):
def setStrikeStyle(self, value: str | None) -> None:
if value in self.VALID_LSTYLE:
self._tAttr["text-line-through-style"][1] = value
else:
self._tAttr["text-line-through-style"][1] = None
return
def setStrikeType(self, value: str | None):
def setStrikeType(self, value: str | None) -> None:
if value in self.VALID_LTYPE:
self._tAttr["text-line-through-type"][1] = value
else:
@@ -1307,7 +1305,7 @@ class ODTTextStyle:
# 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."""
theAttr = {}
theAttr[_mkTag("style", "name")] = name
@@ -1357,7 +1355,7 @@ class XMLParagraph:
object and attribute is written to,
"""
def __init__(self, xRoot: ET.Element):
def __init__(self, xRoot: ET.Element) -> None:
self._xRoot = xRoot
self._xTail = ET.Element("")
@@ -1370,7 +1368,7 @@ class XMLParagraph:
return
def appendText(self, tText: str):
def appendText(self, tText: str) -> None:
"""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
spaces separately. Multiple spaces are concatenated into a
@@ -1435,7 +1433,7 @@ class XMLParagraph:
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
closed since we do not allow nested spans (like Libre Office).
Therefore we return to the root element level when we're done
@@ -1449,7 +1447,7 @@ class XMLParagraph:
self._nState = X_ROOT_TAIL
return
def checkError(self):
def checkError(self) -> tuple[int, str]:
"""Check that the number of characters written matches the
number of characters received.
"""
@@ -1463,7 +1461,7 @@ class XMLParagraph:
# Internal Functions
##
def _processSpaces(self, nSpaces: int):
def _processSpaces(self, nSpaces: int) -> None:
"""Add spaces to paragraph. The first space is always written
as-is (unless it's the first character of the paragraph). The
second space uses the dedicated tag for spaces, and from the
+12 -15
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI About Box
===========================
The about novelWriter dialog box
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
import novelwriter
@@ -31,8 +31,8 @@ from datetime import datetime
from PyQt5.QtGui import QCursor
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget,
QTextBrowser, QLabel
qApp, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QTabWidget,
QTextBrowser, QVBoxLayout, QWidget
)
from novelwriter import CONFIG
@@ -44,15 +44,12 @@ logger = logging.getLogger(__name__)
class GuiAbout(QDialog):
def __init__(self, mainGui):
super().__init__(parent=mainGui)
def __init__(self, parent: QWidget):
super().__init__(parent=parent)
logger.debug("Create: GuiAbout")
self.setObjectName("GuiAbout")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout()
self.innerBox.setSpacing(CONFIG.pxInt(16))
@@ -63,7 +60,7 @@ class GuiAbout(QDialog):
nPx = CONFIG.pxInt(96)
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.lblVers = QLabel(f"v{novelwriter.__version__}")
self.lblDate = QLabel(datetime.strptime(novelwriter.__date__, "%Y-%m-%d").strftime("%x"))
@@ -231,12 +228,12 @@ class GuiAbout(QDialog):
" color: rgb({kColR},{kColG},{kColB});"
"}}\n"
).format(
hColR=self.mainGui.mainTheme.colHead[0],
hColG=self.mainGui.mainTheme.colHead[1],
hColB=self.mainGui.mainTheme.colHead[2],
kColR=self.mainTheme.colKey[0],
kColG=self.mainTheme.colKey[1],
kColB=self.mainTheme.colKey[2],
hColR=CONFIG.theme.colHead[0],
hColG=CONFIG.theme.colHead[1],
hColB=CONFIG.theme.colHead[2],
kColR=CONFIG.theme.colKey[0],
kColG=CONFIG.theme.colKey[1],
kColB=CONFIG.theme.colKey[2],
)
self.pageAbout.document().setDefaultStyleSheet(styleSheet)
self.pageNotes.document().setDefaultStyleSheet(styleSheet)
+6 -8
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Doc Merge Dialog
==================================
Custom dialog class for merging documents.
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -49,9 +49,7 @@ class GuiDocMerge(QDialog):
logger.debug("Create: GuiDocMerge")
self.setObjectName("GuiDocMerge")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
self._data = {}
@@ -60,9 +58,9 @@ class GuiDocMerge(QDialog):
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
self.helpLabel = NHelpLabel(self.tr(
"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)
vSp = CONFIG.pxInt(8)
bSp = CONFIG.pxInt(12)
@@ -157,11 +155,11 @@ class GuiDocMerge(QDialog):
self.listBox.clear()
for tHandle in itemList:
nwItem = self.theProject.tree[tHandle]
nwItem = self.mainGui.project.tree[tHandle]
if nwItem is None or not nwItem.isFileType():
continue
itemIcon = self.mainTheme.getItemIcon(
itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
)
+8 -10
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Doc Split Dialog
==================================
Custom dialog class for splitting documents.
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -51,9 +51,7 @@ class GuiDocSplit(QDialog):
logger.debug("Create: GuiDocSplit")
self.setObjectName("GuiDocSplit")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
self._data = {}
self._text = []
@@ -63,16 +61,16 @@ class GuiDocSplit(QDialog):
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
self.helpLabel = NHelpLabel(
self.tr("Select the maximum level to split into files."),
self.mainGui.mainTheme.helpText
CONFIG.theme.helpText
)
# Values
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
hSp = CONFIG.pxInt(12)
vSp = CONFIG.pxInt(8)
bSp = CONFIG.pxInt(12)
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
spLevel = pOptions.getInt("GuiDocSplit", "spLevel", 3)
intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True)
docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True)
@@ -171,7 +169,7 @@ class GuiDocSplit(QDialog):
self._data["docHierarchy"] = docHierarchy
self._data["moveToTrash"] = moveToTrash
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
pOptions.setValue("GuiDocSplit", "spLevel", spLevel)
pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder)
pOptions.setValue("GuiDocSplit", "docHierarchy", docHierarchy)
@@ -201,13 +199,13 @@ class GuiDocSplit(QDialog):
self.listBox.clear()
nwItem = self.theProject.tree[sHandle]
nwItem = self.mainGui.project.tree[sHandle]
if nwItem is None or not nwItem.isFileType():
return
spLevel = self.splitLevel.currentData()
if not self._text:
inDoc = self.theProject.storage.getDocument(sHandle)
inDoc = self.mainGui.project.storage.getDocument(sHandle)
self._text = (inDoc.readDocument() or "").splitlines()
for lineNo, aLine in enumerate(self._text):
+1 -1
View File
@@ -1,7 +1,6 @@
"""
novelWriter Edit Label Dialog
===============================
A simple dialog for editing a label
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
+17 -35
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Preferences
=============================
GUI classes for the user preferences dialog
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -49,8 +49,7 @@ class GuiPreferences(NPagedDialog):
logger.debug("Create: GuiPreferences")
self.setObjectName("GuiPreferences")
self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainGui = mainGui
self.setWindowTitle(self.tr("Preferences"))
@@ -160,13 +159,11 @@ class GuiPreferencesGeneral(QWidget):
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.prefsGui = prefsGui
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
self.prefsGui = prefsGui
# The Form
self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm)
# Look and Feel
@@ -193,7 +190,7 @@ class GuiPreferencesGeneral(QWidget):
# Select Theme
self.guiTheme = QComboBox()
self.guiTheme.setMinimumWidth(minWidth)
self.theThemes = self.mainTheme.listThemes()
self.theThemes = CONFIG.theme.listThemes()
for themeDir, themeName in self.theThemes:
self.guiTheme.addItem(themeName, themeDir)
themeIdx = self.guiTheme.findData(CONFIG.guiTheme)
@@ -209,7 +206,7 @@ class GuiPreferencesGeneral(QWidget):
# Editor Theme
self.guiSyntax = QComboBox()
self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200))
self.theSyntaxes = self.mainTheme.listSyntax()
self.theSyntaxes = CONFIG.theme.listSyntax()
for syntaxFile, syntaxName in self.theSyntaxes:
self.guiSyntax.addItem(syntaxName, syntaxFile)
syntaxIdx = self.guiSyntax.findData(CONFIG.guiSyntax)
@@ -228,7 +225,7 @@ class GuiPreferencesGeneral(QWidget):
self.guiFont.setFixedWidth(CONFIG.pxInt(162))
self.guiFont.setText(CONFIG.guiFont)
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.mainForm.addRow(
self.tr("Font family"),
@@ -342,12 +339,9 @@ class GuiPreferencesProjects(QWidget):
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm)
# Automatic Save
@@ -497,12 +491,9 @@ class GuiPreferencesDocuments(QWidget):
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm)
# Text Style
@@ -515,7 +506,7 @@ class GuiPreferencesDocuments(QWidget):
self.textFont.setFixedWidth(CONFIG.pxInt(162))
self.textFont.setText(CONFIG.textFont)
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.mainForm.addRow(
self.tr("Font family"),
@@ -654,12 +645,11 @@ class GuiPreferencesEditor(QWidget):
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
self.mainGui = prefsGui.mainGui
# The Form
self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm)
mW = CONFIG.pxInt(250)
@@ -825,13 +815,11 @@ class GuiPreferencesSyntax(QWidget):
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.prefsGui = prefsGui
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
self.prefsGui = prefsGui
# The Form
self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm)
# Quotes & Dialogue
@@ -931,12 +919,9 @@ class GuiPreferencesAutomation(QWidget):
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm)
# Automatic Features
@@ -1085,12 +1070,9 @@ class GuiPreferencesQuotes(QWidget):
def __init__(self, prefsGui):
super().__init__(parent=prefsGui)
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
# The Form
self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm)
# Quotation Style
@@ -1098,7 +1080,7 @@ class GuiPreferencesQuotes(QWidget):
self.mainForm.addGroupLabel(self.tr("Quotation Style"))
qWidth = CONFIG.pxInt(40)
bWidth = int(2.5*self.mainTheme.getTextWidth("..."))
bWidth = int(2.5*CONFIG.theme.getTextWidth("..."))
self.quoteSym = {}
# Single Quote Style
+26 -30
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Project Details
=================================
Class holding the project details dialog
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import math
import logging
@@ -36,9 +36,9 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG
from novelwriter.common import formatTime, numberToRoman
from novelwriter.constants import nwUnicode
from novelwriter.gui.components import NovelSelector
from novelwriter.extensions.switch import NSwitch
from novelwriter.extensions.pageddialog import NPagedDialog
from novelwriter.extensions.novelselector import NovelSelector
logger = logging.getLogger(__name__)
@@ -51,14 +51,13 @@ class GuiProjectDetails(NPagedDialog):
logger.debug("Create: GuiProjectDetails")
self.setObjectName("GuiProjectDetails")
self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainGui = mainGui
self.setWindowTitle(self.tr("Project Details"))
wW = CONFIG.pxInt(600)
wH = CONFIG.pxInt(400)
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
self.setMinimumWidth(wW)
self.setMinimumHeight(wH)
@@ -67,8 +66,8 @@ class GuiProjectDetails(NPagedDialog):
CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "winHeight", wH))
)
self.tabMain = GuiProjectDetailsMain(self.mainGui, self.theProject)
self.tabContents = GuiProjectDetailsContents(self.mainGui, self.theProject)
self.tabMain = GuiProjectDetailsMain(self.mainGui)
self.tabContents = GuiProjectDetailsContents(self.mainGui)
self.addTab(self.tabMain, self.tr("Overview"))
self.addTab(self.tabContents, self.tr("Contents"))
@@ -125,7 +124,7 @@ class GuiProjectDetails(NPagedDialog):
countFrom = self.tabContents.poValue.value()
clearDouble = self.tabContents.dblValue.isChecked()
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
pOptions.setValue("GuiProjectDetails", "winWidth", winWidth)
pOptions.setValue("GuiProjectDetails", "winHeight", winHeight)
pOptions.setValue("GuiProjectDetails", "widthCol0", widthCol0)
@@ -144,15 +143,13 @@ class GuiProjectDetails(NPagedDialog):
class GuiProjectDetailsMain(QWidget):
def __init__(self, mainGui, theProject):
def __init__(self, mainGui):
super().__init__(parent=mainGui)
self.theProject = theProject
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = mainGui
fPx = self.mainTheme.fontPixelSize
fPt = self.mainTheme.fontPointSize
fPx = CONFIG.theme.fontPixelSize
fPt = CONFIG.theme.fontPointSize
vPx = CONFIG.pxInt(4)
hPx = CONFIG.pxInt(12)
@@ -247,22 +244,23 @@ class GuiProjectDetailsMain(QWidget):
def updateValues(self):
"""Set all the values.
"""
pIndex = self.theProject.index
project = self.mainGui.project
pIndex = project.index
hCounts = pIndex.getNovelTitleCounts()
nwCount = pIndex.getNovelWordCount()
edTime = self.theProject.getCurrentEditTime()
edTime = project.getCurrentEditTime()
self.bookTitle.setText(self.theProject.data.title or self.theProject.data.name)
self.projName.setText(self.tr("Project: {0}").format(self.theProject.data.name))
self.bookAuthors.setText(self.tr("By {0}").format(self.theProject.data.author))
self.bookTitle.setText(project.data.title or project.data.name)
self.projName.setText(self.tr("Project: {0}").format(project.data.name))
self.bookAuthors.setText(self.tr("By {0}").format(project.data.author))
self.wordCountVal.setText(f"{nwCount:n}")
self.chapCountVal.setText(f"{hCounts[2]: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.projPathVal.setText(str(self.theProject.storage.storagePath))
self.projPathVal.setText(str(project.storage.storagePath))
return
@@ -277,28 +275,26 @@ class GuiProjectDetailsContents(QWidget):
C_PAGE = 3
C_PROG = 4
def __init__(self, mainGui, theProject):
def __init__(self, mainGui):
super().__init__(parent=mainGui)
self.theProject = theProject
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = mainGui
# Internal
self._theToC = []
self._currentRoot = None
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
hPx = CONFIG.pxInt(12)
vPx = CONFIG.pxInt(4)
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
# Header
# ======
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.novelSelectionChanged.connect(self._novelValueChanged)
@@ -447,7 +443,7 @@ class GuiProjectDetailsContents(QWidget):
"""Extract the information from the project index.
"""
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))
return
@@ -500,7 +496,7 @@ class GuiProjectDetailsContents(QWidget):
progPage = f"{cPage:n}"
progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"
hDec = self.mainTheme.getHeaderDecoration(tLevel)
hDec = CONFIG.theme.getHeaderDecoration(tLevel)
if tTitle.strip() == "":
tTitle = self.tr("Untitled")
+10 -11
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Open Project
==============================
GUI class for the load/browse/new project dialog
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -62,13 +62,12 @@ class GuiProjectLoad(QDialog):
self.setObjectName("GuiProjectLoad")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.openState = self.NONE_STATE
self.openPath = None
sPx = CONFIG.pxInt(16)
nPx = CONFIG.pxInt(96)
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout()
@@ -80,7 +79,7 @@ class GuiProjectLoad(QDialog):
self.setMinimumHeight(CONFIG.pxInt(400))
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.projectForm = QGridLayout()
@@ -101,8 +100,9 @@ class GuiProjectLoad(QDialog):
self.listBox.setIconSize(QSize(iPx, iPx))
treeHead = self.listBox.headerItem()
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
if treeHead:
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.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
@@ -110,7 +110,7 @@ class GuiProjectLoad(QDialog):
self.selPath.setReadOnly(True)
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.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3)
@@ -268,7 +268,7 @@ class GuiProjectLoad(QDialog):
self.listBox.clear()
dataList = CONFIG.recentProjects.listEntries()
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:
newItem = QTreeWidgetItem([""]*4)
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_COUNT, 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)
if self.listBox.topLevelItemCount() > 0:
self.listBox.topLevelItem(0).setSelected(True)
self.listBox.setCurrentItem(self.listBox.topLevelItem(0))
projColWidth = CONFIG.projLoadColWidths
if len(projColWidth) == 3:
+37 -42
View File
@@ -60,15 +60,13 @@ class GuiProjectSettings(NPagedDialog):
logger.debug("Create: GuiProjectSettings")
self.setObjectName("GuiProjectSettings")
self.mainGui = mainGui
self.theProject = mainGui.theProject
self.theProject.countStatus()
self.mainGui = mainGui
self.mainGui.project.countStatus()
self.setWindowTitle(self.tr("Project Settings"))
wW = CONFIG.pxInt(570)
wH = CONFIG.pxInt(375)
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
self.setMinimumWidth(wW)
self.setMinimumHeight(wH)
@@ -117,34 +115,35 @@ class GuiProjectSettings(NPagedDialog):
def _doSave(self):
"""Save settings and close dialog.
"""
project = self.mainGui.project
projName = self.tabMain.editName.text()
bookTitle = self.tabMain.editTitle.text()
bookAuthor = self.tabMain.editAuthor.text()
spellLang = self.tabMain.spellLang.currentData()
doBackup = not self.tabMain.doBackup.isChecked()
self.theProject.data.setName(projName)
self.theProject.data.setTitle(bookTitle)
self.theProject.data.setAuthor(bookAuthor)
self.theProject.data.setDoBackup(doBackup)
project.data.setName(projName)
project.data.setTitle(bookTitle)
project.data.setAuthor(bookAuthor)
project.data.setDoBackup(doBackup)
# 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:
newList, delList = self.tabStatus.getNewList()
self.theProject.setStatusColours(newList, delList)
project.setStatusColours(newList, delList)
if self.tabImport.colChanged:
newList, delList = self.tabImport.getNewList()
self.theProject.setImportColours(newList, delList)
project.setImportColours(newList, delList)
if self.tabStatus.colChanged or self.tabImport.colChanged:
self.mainGui.rebuildTrees()
if self.tabReplace.arChanged:
newList = self.tabReplace.getNewList()
self.theProject.data.setAutoReplace(newList)
project.data.setAutoReplace(newList)
self._saveGuiSettings()
self.accept()
@@ -184,7 +183,7 @@ class GuiProjectSettings(NPagedDialog):
statusColW = CONFIG.rpxInt(self.tabStatus.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", "winHeight", winHeight)
pOptions.setValue("GuiProjectSettings", "replaceColW", replaceColW)
@@ -201,22 +200,22 @@ class GuiProjectEditMain(QWidget):
def __init__(self, projGui):
super().__init__(parent=projGui)
self.mainGui = projGui.mainGui
self.theProject = projGui.theProject
self.mainGui = projGui.mainGui
# The Form
self.mainForm = NConfigLayout()
self.mainForm.setHelpTextStyle(self.mainGui.mainTheme.helpText)
self.mainForm.setHelpTextStyle(CONFIG.theme.helpText)
self.setLayout(self.mainForm)
self.mainForm.addGroupLabel(self.tr("Project Settings"))
xW = CONFIG.pxInt(250)
pData = self.mainGui.project.data
self.editName = QLineEdit()
self.editName.setMaxLength(200)
self.editName.setMaximumWidth(xW)
self.editName.setText(self.theProject.data.name)
self.editName.setText(pData.name)
self.mainForm.addRow(
self.tr("Project name"),
self.editName,
@@ -226,7 +225,7 @@ class GuiProjectEditMain(QWidget):
self.editTitle = QLineEdit()
self.editTitle.setMaxLength(200)
self.editTitle.setMaximumWidth(xW)
self.editTitle.setText(self.theProject.data.title)
self.editTitle.setText(pData.title)
self.mainForm.addRow(
self.tr("Novel title"),
self.editTitle,
@@ -236,7 +235,7 @@ class GuiProjectEditMain(QWidget):
self.editAuthor = QLineEdit()
self.editAuthor.setMaxLength(200)
self.editAuthor.setMaximumWidth(xW)
self.editAuthor.setText(self.theProject.data.author)
self.editAuthor.setText(pData.author)
self.mainForm.addRow(
self.tr("Author(s)"),
self.editAuthor,
@@ -259,13 +258,13 @@ class GuiProjectEditMain(QWidget):
)
spellIdx = 0
if self.theProject.data.spellLang is not None:
spellIdx = self.spellLang.findData(self.theProject.data.spellLang)
if pData.spellLang is not None:
spellIdx = self.spellLang.findData(pData.spellLang)
if spellIdx != -1:
self.spellLang.setCurrentIndex(spellIdx)
self.doBackup = NSwitch(self)
self.doBackup.setChecked(not self.theProject.data.doBackup)
self.doBackup.setChecked(not pData.doBackup)
self.mainForm.addRow(
self.tr("No backup on close"),
self.doBackup,
@@ -289,28 +288,26 @@ class GuiProjectEditStatus(QWidget):
def __init__(self, projGui, isStatus):
super().__init__(parent=projGui)
self.mainGui = projGui.mainGui
self.theProject = projGui.theProject
self.mainTheme = projGui.mainGui.mainTheme
self.mainGui = projGui.mainGui
if isStatus:
self.theStatus = self.theProject.data.itemStatus
self.theStatus = self.mainGui.project.data.itemStatus
pageLabel = self.tr("Novel File Status Levels")
colSetting = "statusColW"
else:
self.theStatus = self.theProject.data.itemImport
self.theStatus = self.mainGui.project.data.itemImport
pageLabel = self.tr("Note File Importance Levels")
colSetting = "importColW"
wCol0 = CONFIG.pxInt(
self.theProject.options.getInt("GuiProjectSettings", colSetting, 130)
self.mainGui.project.options.getInt("GuiProjectSettings", colSetting, 130)
)
self.colDeleted = []
self.colChanged = False
self.selColour = QColor(100, 100, 100)
self.iPx = self.mainTheme.baseIconSize
self.iPx = CONFIG.theme.baseIconSize
# The List
# ========
@@ -329,16 +326,16 @@ class GuiProjectEditStatus(QWidget):
# List Controls
# =============
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "")
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.upButton = QPushButton(self.mainTheme.getIcon("up"), "")
self.upButton = QPushButton(CONFIG.theme.getIcon("up"), "")
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))
# Edit Form
@@ -577,13 +574,11 @@ class GuiProjectEditReplace(QWidget):
def __init__(self, projGui):
super().__init__(parent=projGui)
self.mainGui = projGui.mainGui
self.mainTheme = projGui.mainGui.mainTheme
self.theProject = projGui.theProject
self.arChanged = False
self.mainGui = projGui.mainGui
self.arChanged = False
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")
@@ -599,7 +594,7 @@ class GuiProjectEditReplace(QWidget):
self.listBox.setColumnWidth(self.COL_KEY, wCol0)
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])
self.listBox.addTopLevelItem(newItem)
@@ -609,10 +604,10 @@ class GuiProjectEditReplace(QWidget):
# List Controls
# =============
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "")
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)
# Edit Form
+1 -1
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Quotes Dialog
===============================
GUI class for quotes dialog
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
+2 -5
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Updates
=========================
A dialog box for checking for latest updates
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import json
import logging
@@ -49,9 +49,6 @@ class GuiUpdates(QDialog):
logger.debug("Create: GuiUpdates")
self.setObjectName("GuiUpdates")
self.mainGui = mainGui
self.setWindowTitle(self.tr("Check for Updates"))
nPx = CONFIG.pxInt(96)
@@ -61,7 +58,7 @@ class GuiUpdates(QDialog):
# Left Box
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.addWidget(self.nwIcon)
+8 -11
View File
@@ -50,17 +50,14 @@ class GuiWordList(QDialog):
logger.debug("Create: GuiWordList")
self.setObjectName("GuiWordList")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.setWindowTitle(self.tr("Project Word List"))
self.mainGui = mainGui
mS = CONFIG.pxInt(250)
wW = CONFIG.pxInt(320)
wH = CONFIG.pxInt(340)
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
self.setMinimumWidth(mS)
self.setMinimumHeight(mS)
@@ -80,10 +77,10 @@ class GuiWordList(QDialog):
self.newEntry = QLineEdit()
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
self.addButton = QPushButton(CONFIG.theme.getIcon("add"), "")
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.editBox = QHBoxLayout()
@@ -152,7 +149,7 @@ class GuiWordList(QDialog):
def _doSave(self):
"""Save the new word list and close."""
self._saveGuiSettings()
userDict = UserDictionary(self.theProject)
userDict = UserDictionary(self.mainGui.project)
for i in range(self.listBox.count()):
item = self.listBox.item(i)
if isinstance(item, QListWidgetItem):
@@ -175,7 +172,7 @@ class GuiWordList(QDialog):
def _loadWordList(self):
"""Load the project's word list, if it exists."""
userDict = UserDictionary(self.theProject)
userDict = UserDictionary(self.mainGui.project)
userDict.load()
self.listBox.clear()
for word in userDict:
@@ -188,7 +185,7 @@ class GuiWordList(QDialog):
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
pOptions.setValue("GuiWordList", "winWidth", winWidth)
pOptions.setValue("GuiWordList", "winHeight", winHeight)
-1
View File
@@ -1,7 +1,6 @@
"""
novelWriter Enums
===================
Global enum values
File History:
Created: 2018-11-02 [0.0.1]
+5 -7
View File
@@ -42,7 +42,7 @@ class NProgressCircle(QProgressBar):
"_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)
self._text = None
self._point = point
@@ -60,10 +60,8 @@ class NProgressCircle(QProgressBar):
self.setFixedHeight(size)
return
def setColours(
self, back: QColor | None = None, track: QColor | None = None,
bar: QColor | None = None, text: QColor | None = None
):
def setColours(self, back: QColor | None = None, track: QColor | None = None,
bar: QColor | None = None, text: QColor | None = None) -> None:
"""Set the colours of the widget."""
if isinstance(back, QColor):
self._dPen = QPen(back)
@@ -76,13 +74,13 @@ class NProgressCircle(QProgressBar):
self._tColor = text
return
def setCentreText(self, text: str | None):
def setCentreText(self, text: str | None) -> None:
"""Replace the progress text with a custom string."""
self._text = text
self.setValue(self.value()) # Triggers a redraw
return
def paintEvent(self, event: QPaintEvent):
def paintEvent(self, event: QPaintEvent) -> None:
"""Custom painter for the progress bar."""
progress = 100.0*self.value()/self.maximum()
angle = ceil(16*3.6*progress)
+13 -14
View File
@@ -1,7 +1,6 @@
"""
novelWriter Custom Widget: Config Layout
==========================================
A custom grid layout for config pages
File History:
Created: 2020-05-03 [0.4.5]
@@ -38,7 +37,7 @@ FONT_SCALE = 0.9
class NConfigLayout(QGridLayout):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._nextRow = 0
@@ -57,7 +56,8 @@ class NConfigLayout(QGridLayout):
# 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."""
if isinstance(color, QColor):
self._helpCol = color
@@ -66,7 +66,7 @@ class NConfigLayout(QGridLayout):
self._fontScale = fontScale
return
def setHelpText(self, row: int, text: str):
def setHelpText(self, row: int, text: str) -> None:
"""Set the text for the help label."""
if row in self._itemMap:
qHelp = self._itemMap[row][1]
@@ -74,7 +74,7 @@ class NConfigLayout(QGridLayout):
qHelp.setText(text)
return
def setLabelText(self, row: int, text: str):
def setLabelText(self, row: int, text: str) -> None:
"""Set the text for the main label."""
if row in self._itemMap:
self._itemMap[row](0).setText(text)
@@ -84,7 +84,7 @@ class NConfigLayout(QGridLayout):
# Class Methods
##
def addGroupLabel(self, label: str):
def addGroupLabel(self, label: str) -> None:
"""Add a text label to separate groups of settings."""
hM = CONFIG.pxInt(4)
qLabel = QLabel("<b>%s</b>" % label)
@@ -95,10 +95,8 @@ class NConfigLayout(QGridLayout):
self._nextRow += 1
return
def addRow(
self, label: str, widget: QWidget, helpText: str | None = None,
unit: str | None = None, button: QWidget | None = None
) -> int:
def addRow(self, label: str, widget: QWidget, helpText: str | None = None,
unit: str | None = None, button: QWidget | None = None) -> int:
"""Add a label and a widget as a new row of the grid."""
wSp = CONFIG.pxInt(8)
qLabel = QLabel(label)
@@ -156,7 +154,7 @@ class NSimpleLayout(QGridLayout):
column layout.
"""
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._nextRow = 0
@@ -171,7 +169,7 @@ class NSimpleLayout(QGridLayout):
# Methods
##
def addGroupLabel(self, label: str):
def addGroupLabel(self, label: str) -> None:
"""Add a text label to separate groups of settings."""
hM = CONFIG.pxInt(4)
qLabel = QLabel("<b>%s</b>" % label)
@@ -182,7 +180,7 @@ class NSimpleLayout(QGridLayout):
self._nextRow += 1
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."""
wSp = CONFIG.pxInt(8)
qLabel = QLabel(label)
@@ -209,7 +207,8 @@ class NSimpleLayout(QGridLayout):
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)
if isinstance(color, QColor):
@@ -1,11 +1,9 @@
"""
novelWriter GUI Components Module
===================================
A module of various small GUI components
novelWriter Custom Widget: Novel Selector
===========================================
File History:
Created: 2020-05-17 [0.5.1] StatusLED
Created: 2022-11-17 [2.0] NovelSelector
Created: 2022-11-17 [2.0]
This file is a part of novelWriter
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
from PyQt5.QtGui import QPainter
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QAbstractButton, QComboBox
from typing import TYPE_CHECKING
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QComboBox, QWidget
from novelwriter import CONFIG
from novelwriter.enum import nwItemClass
from novelwriter.constants import nwLabels
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
@@ -40,17 +44,12 @@ class NovelSelector(QComboBox):
novelSelectionChanged = pyqtSignal(str)
def __init__(self, parent, project, mainGui):
def __init__(self, parent: QWidget, mainGui: GuiMain) -> None:
super().__init__(parent=parent)
self._mainGui = mainGui
self._project = project
self._theme = mainGui.mainTheme
self._blockSignal = False
self._firstHandle = None
self.currentIndexChanged.connect(self._indexChanged)
return
##
@@ -58,20 +57,19 @@ class NovelSelector(QComboBox):
##
@property
def handle(self):
def handle(self) -> str:
return self.currentData()
@property
def firstHandle(self):
def firstHandle(self) -> str | None:
return self._firstHandle
##
# Methods
##
def setHandle(self, tHandle, blockSignal=True):
"""Set the currently selected handle.
"""
def setHandle(self, tHandle: str, blockSignal: bool = True) -> None:
"""Set the currently selected handle."""
self._blockSignal = blockSignal
if tHandle is None:
index = self.count() - 1
@@ -82,16 +80,15 @@ class NovelSelector(QComboBox):
self._blockSignal = False
return
def updateList(self, includeAll=False, prefix=None):
"""Rebuild the list of novel items.
"""
def updateList(self, includeAll: bool = False, prefix: str | None = None) -> None:
"""Rebuild the list of novel items."""
self._blockSignal = True
self._firstHandle = None
self.clear()
icon = self._theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
icon = CONFIG.theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
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:
name = prefix.format(nwItem.itemName)
self.addItem(name, tHandle)
@@ -116,67 +113,10 @@ class NovelSelector(QComboBox):
##
@pyqtSlot(int)
def _indexChanged(self, index):
"""Re-emit the change of selected novel signal, unless blocked.
"""
def _indexChanged(self, index: int) -> None:
"""Re-emit the change of selection signal, unless blocked."""
if not self._blockSignal:
self.novelSelectionChanged.emit(self.currentData())
return
# 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
=========================================
A custom dialog with tabs and a vertical tab bar
File History:
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
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 (
QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar,
QTabWidget, QVBoxLayout
QTabWidget, QVBoxLayout, QWidget
)
from novelwriter import CONFIG
@@ -34,7 +35,7 @@ from novelwriter import CONFIG
class NPagedDialog(QDialog):
def __init__(self, parent=None):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
self._tabBar = NVerticalTabBar(self)
@@ -67,21 +68,18 @@ class NPagedDialog(QDialog):
return
def addTab(self, widget, label):
"""Forward the adding of tabs to the QTabWidget.
"""
def addTab(self, widget: QWidget, label: str) -> None:
"""Forward the adding of tabs to the QTabWidget."""
self._tabBox.addTab(widget, label)
return
def addControls(self, buttonBar):
"""Add a button bar to the dialog.
"""
def addControls(self, buttonBar: QWidget) -> None:
"""Add a button bar to the dialog."""
self._buttonBox.addWidget(buttonBar)
return
def setCurrentWidget(self, widget):
"""Forward the changing of tab to the QTabWidget.
"""
def setCurrentWidget(self, widget: QWidget) -> None:
"""Forward the changing of tab to the QTabWidget."""
self._tabBox.setCurrentWidget(widget)
return
@@ -90,20 +88,19 @@ class NPagedDialog(QDialog):
class NVerticalTabBar(QTabBar):
def __init__(self, parent=None):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
self._mW = CONFIG.pxInt(150)
return
def tabSizeHint(self, index):
"""Return a transposed size hint for the rotated bar.
"""
def tabSizeHint(self, index: int) -> QSize:
"""Return a transposed size hint for the rotated bar."""
tSize = super().tabSizeHint(index)
tSize.transpose()
tSize.setWidth(min(tSize.width(), self._mW))
return tSize
def paintEvent(self, event):
def paintEvent(self, event: QPaintEvent) -> None:
"""Custom implementation of the label painter that rotates the
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.
"""
def __init__(self, parent: QWidget):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
return
def paintEvent(self, event: QPaintEvent):
def paintEvent(self, event: QPaintEvent) -> None:
"""Custom painter for the progress bar."""
if self.value() == 0:
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
===================================
A custom switch widget
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from PyQt5.QtGui import QPainter
from PyQt5.QtCore import Qt, QRectF, QPropertyAnimation, pyqtProperty
from PyQt5.QtWidgets import QSizePolicy, QAbstractButton
from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent
from PyQt5.QtCore import QEvent, QPropertyAnimation, QRectF, Qt, pyqtProperty
from PyQt5.QtWidgets import QAbstractButton, QSizePolicy, QWidget
from novelwriter import CONFIG
from novelwriter.constants import nwUnicode
@@ -33,7 +33,8 @@ from novelwriter.constants import nwUnicode
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)
if width is None:
@@ -64,12 +65,12 @@ class NSwitch(QAbstractButton):
# Properties
##
@pyqtProperty(int)
def offset(self):
@pyqtProperty(int) # type: ignore
def offset(self) -> int: # type: ignore
return self._offset
@offset.setter
def offset(self, offset):
@offset.setter # type: ignore
def offset(self, offset: int):
self._offset = offset
self.update()
return
@@ -78,33 +79,30 @@ class NSwitch(QAbstractButton):
# Getters and Setters
##
def setChecked(self, checked):
"""Overload setChecked to also alter the offset.
"""
def setChecked(self, checked: bool) -> None:
"""Overload setChecked to also alter the offset."""
super().setChecked(checked)
if checked:
self.offset = self._xW - self._xR
self._offset = self._xW - self._xR
else:
self.offset = self._xR
self._offset = self._xR
return
##
# Events
##
def resizeEvent(self, event):
"""Overload resize to ensure correct offset.
"""
def resizeEvent(self, event: QResizeEvent) -> None:
"""Overload resize to ensure correct offset."""
super().resizeEvent(event)
if self.isChecked():
self.offset = self._xW - self._xR
self._offset = self._xW - self._xR
else:
self.offset = self._xR
self._offset = self._xR
return
def paintEvent(self, event):
"""Drawing the switch itself.
"""
def paintEvent(self, event: QPaintEvent) -> None:
"""Drawing the switch itself."""
qPaint = QPainter(self)
qPaint.setRenderHint(QPainter.Antialiasing, True)
qPaint.setPen(Qt.NoPen)
@@ -134,27 +132,26 @@ class NSwitch(QAbstractButton):
qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
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.setPixelSize(self._xT)
qPaint.setPen(textColor)
qPaint.setFont(theFont)
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
)
return
def mouseReleaseEvent(self, event):
"""Animate the switch on mouse release.
"""
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
"""Animate the switch on mouse release."""
super().mouseReleaseEvent(event)
if event.button() == Qt.LeftButton:
doAnim = QPropertyAnimation(self, b"offset", self)
doAnim.setDuration(120)
doAnim.setStartValue(self.offset)
doAnim.setStartValue(self._offset)
if self.isChecked():
doAnim.setEndValue(self._xW - self._xR)
else:
@@ -162,9 +159,8 @@ class NSwitch(QAbstractButton):
doAnim.start()
return
def enterEvent(self, event):
"""Change the cursor when hovering the button.
"""
def enterEvent(self, event: QEvent) -> None:
"""Change the cursor when hovering the button."""
self.setCursor(Qt.PointingHandCursor)
super().enterEvent(event)
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 PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget
from novelwriter.extensions.switch import NSwitch
@@ -39,7 +39,7 @@ class NSwitchBox(QScrollArea):
switchToggled = pyqtSignal(str, bool)
def __init__(self, parent: QWidget, baseSize: int):
def __init__(self, parent: QWidget, baseSize: int) -> None:
super().__init__(parent=parent)
self._index = 0
self._hSwitch = baseSize
@@ -49,7 +49,7 @@ class NSwitchBox(QScrollArea):
self.clear()
return
def clear(self):
def clear(self) -> None:
"""Rebuild the content of the core widget."""
self._index = 0
self._widgets = []
@@ -66,7 +66,7 @@ class NSwitchBox(QScrollArea):
return
def addLabel(self, text: str):
def addLabel(self, text: str) -> None:
"""Add a header label to the content box."""
label = QLabel(text)
font = label.font()
@@ -77,7 +77,7 @@ class NSwitchBox(QScrollArea):
self._bumpIndex()
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."""
icon = QLabel("")
icon.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
@@ -97,7 +97,7 @@ class NSwitchBox(QScrollArea):
return
def addSeparator(self):
def addSeparator(self) -> None:
"""Add a blank entry in the content box."""
spacer = QWidget()
spacer.setFixedHeight(int(0.5*self._sIcon))
@@ -106,7 +106,7 @@ class NSwitchBox(QScrollArea):
self._bumpIndex()
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."""
self._content.setContentsMargins(left, top, right, bottom)
return
@@ -115,12 +115,12 @@ class NSwitchBox(QScrollArea):
# Internal Functions
##
def _emitSwitchSignal(self, identifier: str, state: bool):
def _emitSwitchSignal(self, identifier: str, state: bool) -> None:
"""Emit a signal for a switch toggle."""
self.switchToggled.emit(identifier, state)
return
def _bumpIndex(self):
def _bumpIndex(self) -> None:
"""Increase the index counter and make sure only the last
columns is stretching.
"""
+60 -67
View File
@@ -84,9 +84,7 @@ class GuiDocEditor(QTextEdit):
logger.debug("Create: GuiDocEditor")
# Class Variables
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
self._nwDocument = None
self._nwItem = None
@@ -134,7 +132,7 @@ class GuiDocEditor(QTextEdit):
self.docSearch = GuiDocEditSearch(self)
# Syntax
self.spEnchant = NWSpellEnchant(self.theProject)
self.spEnchant = NWSpellEnchant(self.mainGui.project)
self.highLight = GuiDocHighlighter(qDoc, self.mainGui, self.spEnchant)
# Context Menu
@@ -229,14 +227,14 @@ class GuiDocEditor(QTextEdit):
"""Update the syntax highlighting theme.
"""
mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
mainPalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(mainPalette)
docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
docPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack))
docPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.viewport().setPalette(docPalette)
self.docHeader.matchColours()
@@ -342,7 +340,7 @@ class GuiDocEditor(QTextEdit):
document is new (empty string), we set up the editor for editing
the file.
"""
self._nwDocument = self.theProject.storage.getDocument(tHandle)
self._nwDocument = self.mainGui.project.storage.getDocument(tHandle)
self._nwItem = self._nwDocument.getCurrentItem()
theDoc = self._nwDocument.readDocument()
@@ -518,10 +516,10 @@ class GuiDocEditor(QTextEdit):
self.setDocumentChanged(False)
oldHeader = self._nwItem.mainHeading
oldCount = self.theProject.index.getHandleHeaderCount(tHandle)
self.theProject.index.scanText(tHandle, docText)
oldCount = self.mainGui.project.index.getHandleHeaderCount(tHandle)
self.mainGui.project.index.scanText(tHandle, docText)
newHeader = self._nwItem.mainHeading
newCount = self.theProject.index.getHandleHeaderCount(tHandle)
newCount = self.mainGui.project.index.getHandleHeaderCount(tHandle)
if self._nwItem.itemClass == nwItemClass.NOVEL:
if oldCount == newCount:
@@ -700,10 +698,10 @@ class GuiDocEditor(QTextEdit):
"""Set the spell checker dictionary language, and emit the
dictionary changed signal.
"""
if self.theProject.data.spellLang is None:
if self.mainGui.project.data.spellLang is None:
theLang = CONFIG.spellLanguage
else:
theLang = self.theProject.data.spellLang
theLang = self.mainGui.project.data.spellLang
self.spEnchant.setLanguage(theLang)
_, theProvider = self.spEnchant.describeDict()
@@ -736,7 +734,7 @@ class GuiDocEditor(QTextEdit):
self._spellCheck = theMode
self.mainGui.mainMenu.setSpellCheck(theMode)
self.theProject.data.setSpellCheck(theMode)
self.mainGui.project.data.setSpellCheck(theMode)
self.highLight.setSpellCheck(theMode)
if not self._bigDoc or theMode is False:
# We don't run the spell checker automatically on big docs
@@ -1918,7 +1916,7 @@ class GuiDocEditor(QTextEdit):
if theText.startswith("@"):
isGood, tBits, tPos = self.theProject.index.scanThis(theText)
isGood, tBits, tPos = self.mainGui.project.index.scanThis(theText)
if not isGood:
return False
@@ -2223,10 +2221,8 @@ class GuiDocEditSearch(QFrame):
logger.debug("Create: GuiDocEditSearch")
self.docEditor = docEditor
self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject
self.mainTheme = docEditor.mainTheme
self.docEditor = docEditor
self.mainGui = docEditor.mainGui
self.repVisible = False
self.isCaseSense = CONFIG.searchCase
@@ -2237,9 +2233,9 @@ class GuiDocEditSearch(QFrame):
self.doMatchCap = CONFIG.searchMatchCap
mPx = CONFIG.pxInt(6)
tPx = int(0.8*self.mainTheme.fontPixelSize)
self.boxFont = self.mainTheme.guiFont
self.boxFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
tPx = int(0.8*CONFIG.theme.fontPixelSize)
self.boxFont = CONFIG.theme.guiFont
self.boxFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
@@ -2272,7 +2268,7 @@ class GuiDocEditSearch(QFrame):
self.resultLabel = QLabel("?/?")
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.setCheckable(True)
@@ -2378,15 +2374,15 @@ class GuiDocEditSearch(QFrame):
self.replaceBox.setPalette(qPalette)
# Set icons
self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
self.toggleWord.setIcon(self.mainTheme.getIcon("search_word"))
self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex"))
self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop"))
self.toggleProject.setIcon(self.mainTheme.getIcon("search_project"))
self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve"))
self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel"))
self.searchButton.setIcon(self.mainTheme.getIcon("search"))
self.replaceButton.setIcon(self.mainTheme.getIcon("search_replace"))
self.toggleCase.setIcon(CONFIG.theme.getIcon("search_case"))
self.toggleWord.setIcon(CONFIG.theme.getIcon("search_word"))
self.toggleRegEx.setIcon(CONFIG.theme.getIcon("search_regex"))
self.toggleLoop.setIcon(CONFIG.theme.getIcon("search_loop"))
self.toggleProject.setIcon(CONFIG.theme.getIcon("search_project"))
self.toggleMatchCap.setIcon(CONFIG.theme.getIcon("search_preserve"))
self.cancelSearch.setIcon(CONFIG.theme.getIcon("search_cancel"))
self.searchButton.setIcon(CONFIG.theme.getIcon("search"))
self.replaceButton.setIcon(CONFIG.theme.getIcon("search_replace"))
# Set stylesheets
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
@@ -2478,7 +2474,7 @@ class GuiDocEditSearch(QFrame):
"""
currRes = "?" if currRes is None else currRes
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.setMinimumWidth(minWidth)
self.adjustSize()
@@ -2638,14 +2634,12 @@ class GuiDocEditHeader(QWidget):
logger.debug("Create: GuiDocEditHeader")
self.docEditor = docEditor
self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject
self.mainTheme = docEditor.mainTheme
self.docEditor = docEditor
self.mainGui = docEditor.mainGui
self._docHandle = None
fPx = int(0.9*self.mainTheme.fontPixelSize)
fPx = int(0.9*CONFIG.theme.fontPixelSize)
hSp = CONFIG.pxInt(6)
# Main Widget Settings
@@ -2662,7 +2656,7 @@ class GuiDocEditHeader(QWidget):
self.theTitle.setFixedHeight(fPx)
lblFont = self.theTitle.font()
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.theTitle.setFont(lblFont)
# Buttons
@@ -2732,15 +2726,15 @@ class GuiDocEditHeader(QWidget):
def updateTheme(self):
"""Update theme elements.
"""
self.editButton.setIcon(self.mainTheme.getIcon("edit"))
self.searchButton.setIcon(self.mainTheme.getIcon("search"))
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
self.editButton.setIcon(CONFIG.theme.getIcon("edit"))
self.searchButton.setIcon(CONFIG.theme.getIcon("search"))
self.minmaxButton.setIcon(CONFIG.theme.getIcon("maximise"))
self.closeButton.setIcon(CONFIG.theme.getIcon("close"))
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
).format(*CONFIG.theme.colText)
self.editButton.setStyleSheet(buttonStyle)
self.searchButton.setStyleSheet(buttonStyle)
@@ -2756,9 +2750,9 @@ class GuiDocEditHeader(QWidget):
theme rather than the main GUI.
"""
thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText))
thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(thePalette)
self.theTitle.setPalette(thePalette)
@@ -2778,17 +2772,18 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.setVisible(False)
return True
pTree = self.mainGui.project.tree
if CONFIG.showFullPath:
tTitle = []
tTree = self.theProject.tree.getItemPath(tHandle)
tTree = pTree.getItemPath(tHandle)
for aHandle in reversed(tTree):
nwItem = self.theProject.tree[aHandle]
nwItem = pTree[aHandle]
if nwItem is not None:
tTitle.append(nwItem.itemName)
sSep = " %s " % nwUnicode.U_RSAQUO
self.theTitle.setText(sSep.join(tTitle))
else:
nwItem = self.theProject.tree[tHandle]
nwItem = pTree[tHandle]
if nwItem is None:
return False
self.theTitle.setText(nwItem.itemName)
@@ -2806,9 +2801,9 @@ class GuiDocEditHeader(QWidget):
toggleFocusMode function and should not be activated directly.
"""
if self.mainGui.isFocusMode:
self.minmaxButton.setIcon(self.mainTheme.getIcon("minimise"))
self.minmaxButton.setIcon(CONFIG.theme.getIcon("minimise"))
else:
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
self.minmaxButton.setIcon(CONFIG.theme.getIcon("maximise"))
return
##
@@ -2873,23 +2868,21 @@ class GuiDocEditFooter(QWidget):
logger.debug("Create: GuiDocEditFooter")
self.docEditor = docEditor
self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject
self.mainTheme = docEditor.mainTheme
self.docEditor = docEditor
self.mainGui = docEditor.mainGui
self._theItem = None
self._docHandle = None
self._docSelection = False
self.sPx = int(round(0.9*self.mainTheme.baseIconSize))
fPx = int(0.9*self.mainTheme.fontPixelSize)
self.sPx = int(round(0.9*CONFIG.theme.baseIconSize))
fPx = int(0.9*CONFIG.theme.fontPixelSize)
bSp = CONFIG.pxInt(4)
hSp = CONFIG.pxInt(6)
lblFont = self.font()
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
# Main Widget Settings
self.setContentsMargins(0, 0, 0, 0)
@@ -2976,8 +2969,8 @@ class GuiDocEditFooter(QWidget):
def updateTheme(self):
"""Update theme elements.
"""
self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx)))
self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx)))
self.linesIcon.setPixmap(CONFIG.theme.getPixmap("status_lines", (self.sPx, self.sPx)))
self.wordsIcon.setPixmap(CONFIG.theme.getPixmap("status_stats", (self.sPx, self.sPx)))
self.matchColours()
@@ -2988,9 +2981,9 @@ class GuiDocEditFooter(QWidget):
theme rather than the main GUI.
"""
thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText))
thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(thePalette)
self.statusText.setPalette(thePalette)
@@ -3007,7 +3000,7 @@ class GuiDocEditFooter(QWidget):
logger.debug("No handle set, so clearing the editor footer")
self._theItem = None
else:
self._theItem = self.theProject.tree[self._docHandle]
self._theItem = self.mainGui.project.tree[self._docHandle]
self.setHasSelection(False)
self.updateInfo()
+17 -19
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Syntax Highlighter
====================================
Class for the main document editor syntax highlighter
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -54,8 +54,6 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.theDoc = theDoc
self.spEnchant = spEnchant
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.theHandle = None
self.spellCheck = False
self.spellRx = None
@@ -87,24 +85,24 @@ class GuiDocHighlighter(QSyntaxHighlighter):
"""
logger.debug("Setting up highlighting rules")
self.colHead = QColor(*self.mainTheme.colHead)
self.colHeadH = QColor(*self.mainTheme.colHeadH)
self.colDialN = QColor(*self.mainTheme.colDialN)
self.colDialD = QColor(*self.mainTheme.colDialD)
self.colDialS = QColor(*self.mainTheme.colDialS)
self.colHidden = QColor(*self.mainTheme.colHidden)
self.colKey = QColor(*self.mainTheme.colKey)
self.colVal = QColor(*self.mainTheme.colVal)
self.colSpell = QColor(*self.mainTheme.colSpell)
self.colError = QColor(*self.mainTheme.colError)
self.colRepTag = QColor(*self.mainTheme.colRepTag)
self.colMod = QColor(*self.mainTheme.colMod)
self.colBreak = QColor(*self.mainTheme.colEmph)
self.colHead = QColor(*CONFIG.theme.colHead)
self.colHeadH = QColor(*CONFIG.theme.colHeadH)
self.colDialN = QColor(*CONFIG.theme.colDialN)
self.colDialD = QColor(*CONFIG.theme.colDialD)
self.colDialS = QColor(*CONFIG.theme.colDialS)
self.colHidden = QColor(*CONFIG.theme.colHidden)
self.colKey = QColor(*CONFIG.theme.colKey)
self.colVal = QColor(*CONFIG.theme.colVal)
self.colSpell = QColor(*CONFIG.theme.colSpell)
self.colError = QColor(*CONFIG.theme.colError)
self.colRepTag = QColor(*CONFIG.theme.colRepTag)
self.colMod = QColor(*CONFIG.theme.colMod)
self.colBreak = QColor(*CONFIG.theme.colEmph)
self.colBreak.setAlpha(64)
self.colEmph = None
if CONFIG.highlightEmph:
self.colEmph = QColor(*self.mainTheme.colEmph)
self.colEmph = QColor(*CONFIG.theme.colEmph)
self.hStyles = {
"header1": self._makeFormat(self.colHead, "bold", 1.8),
@@ -287,8 +285,8 @@ class GuiDocHighlighter(QSyntaxHighlighter):
if theText.startswith("@"): # Keywords and commands
self.setCurrentBlockState(self.BLOCK_META)
pIndex = self.theProject.index
tItem = self.mainGui.theProject.tree[self.theHandle]
pIndex = self.mainGui.project.index
tItem = self.mainGui.project.tree[self.theHandle]
isValid, theBits, thePos = pIndex.scanThis(theText)
isGood = pIndex.checkThese(theBits, tItem)
if isValid:
+63 -71
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Document Viewer
=================================
GUI classes for the main document viewer
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -59,9 +59,7 @@ class GuiDocViewer(QTextBrowser):
logger.debug("Create: GuiDocViewer")
# Class Variables
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
# Internal Variables
self._docHandle = None
@@ -121,14 +119,14 @@ class GuiDocViewer(QTextBrowser):
# Set the widget colours to match syntax theme
mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
mainPalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(mainPalette)
docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
docPalette.setColor(QPalette.Base, QColor(*CONFIG.theme.colBack))
docPalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.viewport().setPalette(docPalette)
self.docHeader.matchColours()
@@ -164,7 +162,7 @@ class GuiDocViewer(QTextBrowser):
def loadText(self, tHandle, updateHistory=True):
"""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")
return False
@@ -172,7 +170,7 @@ class GuiDocViewer(QTextBrowser):
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
sPos = self.verticalScrollBar().value()
aDoc = ToHtml(self.theProject)
aDoc = ToHtml(self.mainGui.project)
aDoc.setPreview(CONFIG.viewComments, CONFIG.viewSynopsis)
aDoc.setLinkHeaders(True)
@@ -212,7 +210,7 @@ class GuiDocViewer(QTextBrowser):
self.verticalScrollBar().setValue(sPos)
self._docHandle = tHandle
self.theProject._data.setLastHandle(tHandle, "viewer")
self.mainGui.project.data.setLastHandle(tHandle, "viewer")
self.docHeader.setTitleFromHandle(self._docHandle)
self.updateDocMargins()
@@ -508,27 +506,27 @@ class GuiDocViewer(QTextBrowser):
" text-align: center;"
"}}\n"
).format(
tColR=self.mainTheme.colText[0],
tColG=self.mainTheme.colText[1],
tColB=self.mainTheme.colText[2],
hColR=self.mainTheme.colHead[0],
hColG=self.mainTheme.colHead[1],
hColB=self.mainTheme.colHead[2],
aColR=self.mainTheme.colVal[0],
aColG=self.mainTheme.colVal[1],
aColB=self.mainTheme.colVal[2],
eColR=self.mainTheme.colEmph[0],
eColG=self.mainTheme.colEmph[1],
eColB=self.mainTheme.colEmph[2],
kColR=self.mainTheme.colKey[0],
kColG=self.mainTheme.colKey[1],
kColB=self.mainTheme.colKey[2],
cColR=self.mainTheme.colHidden[0],
cColG=self.mainTheme.colHidden[1],
cColB=self.mainTheme.colHidden[2],
mColR=self.mainTheme.colMod[0],
mColG=self.mainTheme.colMod[1],
mColB=self.mainTheme.colMod[2],
tColR=CONFIG.theme.colText[0],
tColG=CONFIG.theme.colText[1],
tColB=CONFIG.theme.colText[2],
hColR=CONFIG.theme.colHead[0],
hColG=CONFIG.theme.colHead[1],
hColB=CONFIG.theme.colHead[2],
aColR=CONFIG.theme.colVal[0],
aColG=CONFIG.theme.colVal[1],
aColB=CONFIG.theme.colVal[2],
eColR=CONFIG.theme.colEmph[0],
eColG=CONFIG.theme.colEmph[1],
eColB=CONFIG.theme.colEmph[2],
kColR=CONFIG.theme.colKey[0],
kColG=CONFIG.theme.colKey[1],
kColB=CONFIG.theme.colKey[2],
cColR=CONFIG.theme.colHidden[0],
cColG=CONFIG.theme.colHidden[1],
cColB=CONFIG.theme.colHidden[2],
mColR=CONFIG.theme.colMod[0],
mColG=CONFIG.theme.colMod[1],
mColB=CONFIG.theme.colMod[2],
)
self.document().setDefaultStyleSheet(styleSheet)
@@ -681,15 +679,13 @@ class GuiDocViewHeader(QWidget):
logger.debug("Create: GuiDocViewHeader")
self.docViewer = docViewer
self.mainGui = docViewer.mainGui
self.theProject = docViewer.theProject
self.mainTheme = docViewer.mainTheme
self.docViewer = docViewer
self.mainGui = docViewer.mainGui
# Internal Variables
self._docHandle = None
fPx = int(0.9*self.mainTheme.fontPixelSize)
fPx = int(0.9*CONFIG.theme.fontPixelSize)
hSp = CONFIG.pxInt(6)
# Main Widget Settings
@@ -706,7 +702,7 @@ class GuiDocViewHeader(QWidget):
self.theTitle.setFixedHeight(fPx)
lblFont = self.theTitle.font()
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.theTitle.setFont(lblFont)
# Buttons
@@ -777,15 +773,15 @@ class GuiDocViewHeader(QWidget):
def updateTheme(self):
"""Update theme elements.
"""
self.backButton.setIcon(self.mainTheme.getIcon("backward"))
self.forwardButton.setIcon(self.mainTheme.getIcon("forward"))
self.refreshButton.setIcon(self.mainTheme.getIcon("refresh"))
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
self.backButton.setIcon(CONFIG.theme.getIcon("backward"))
self.forwardButton.setIcon(CONFIG.theme.getIcon("forward"))
self.refreshButton.setIcon(CONFIG.theme.getIcon("refresh"))
self.closeButton.setIcon(CONFIG.theme.getIcon("close"))
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
).format(*CONFIG.theme.colText)
self.backButton.setStyleSheet(buttonStyle)
self.forwardButton.setStyleSheet(buttonStyle)
@@ -801,9 +797,9 @@ class GuiDocViewHeader(QWidget):
theme rather than the main GUI.
"""
thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText))
thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(thePalette)
self.theTitle.setPalette(thePalette)
@@ -823,17 +819,18 @@ class GuiDocViewHeader(QWidget):
self.refreshButton.setVisible(False)
return True
pTree = self.mainGui.project.tree
if CONFIG.showFullPath:
tTitle = []
tTree = self.theProject.tree.getItemPath(tHandle)
tTree = pTree.getItemPath(tHandle)
for aHandle in reversed(tTree):
nwItem = self.theProject.tree[aHandle]
nwItem = pTree[aHandle]
if nwItem is not None:
tTitle.append(nwItem.itemName)
sSep = " %s " % nwUnicode.U_RSAQUO
self.theTitle.setText(sSep.join(tTitle))
else:
nwItem = self.theProject.tree[tHandle]
nwItem = pTree[tHandle]
if nwItem is None:
return False
self.theTitle.setText(nwItem.itemName)
@@ -900,13 +897,12 @@ class GuiDocViewFooter(QWidget):
self.docViewer = docViewer
self.mainGui = docViewer.mainGui
self.mainTheme = docViewer.mainTheme
self.viewMeta = docViewer.mainGui.viewMeta
# Internal Variables
self._docHandle = None
fPx = int(0.9*self.mainTheme.fontPixelSize)
fPx = int(0.9*CONFIG.theme.fontPixelSize)
bSp = CONFIG.pxInt(2)
hSp = CONFIG.pxInt(8)
@@ -991,7 +987,7 @@ class GuiDocViewFooter(QWidget):
self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop)
lblFont = self.font()
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
lblFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.lblRefs.setFont(lblFont)
self.lblSticky.setFont(lblFont)
self.lblComments.setFont(lblFont)
@@ -1036,21 +1032,21 @@ class GuiDocViewFooter(QWidget):
"""
# Icons
fPx = int(0.9*self.mainTheme.fontPixelSize)
fPx = int(0.9*CONFIG.theme.fontPixelSize)
stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
stickyOn = CONFIG.theme.getPixmap("sticky-on", (fPx, fPx))
stickyOff = CONFIG.theme.getPixmap("sticky-off", (fPx, fPx))
stickyIcon = QIcon()
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
bulletOn = CONFIG.theme.getPixmap("bullet-on", (fPx, fPx))
bulletOff = CONFIG.theme.getPixmap("bullet-off", (fPx, fPx))
bulletIcon = QIcon()
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
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.showComments.setIcon(bulletIcon)
self.showSynopsis.setIcon(bulletIcon)
@@ -1060,7 +1056,7 @@ class GuiDocViewFooter(QWidget):
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
).format(*CONFIG.theme.colText)
self.showHide.setStyleSheet(buttonStyle)
self.stickyRefs.setStyleSheet(buttonStyle)
@@ -1076,9 +1072,9 @@ class GuiDocViewFooter(QWidget):
theme rather than the main GUI.
"""
thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Window, QColor(*CONFIG.theme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*CONFIG.theme.colText))
thePalette.setColor(QPalette.Text, QColor(*CONFIG.theme.colText))
self.setPalette(thePalette)
self.lblRefs.setPalette(thePalette)
@@ -1141,9 +1137,7 @@ class GuiDocViewDetails(QScrollArea):
logger.debug("Create: GuiDocViewDetails")
self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainTheme = mainGui.mainTheme
self.mainGui = mainGui
self.refList = QLabel("")
self.refList.setWordWrap(True)
@@ -1151,9 +1145,7 @@ class GuiDocViewDetails(QScrollArea):
self.refList.setScaledContents(True)
self.refList.linkActivated.connect(self._linkClicked)
self.linkStyle = "style='color: rgb({0},{1},{2})'".format(
*self.mainTheme.colLink
)
self.linkStyle = "style='color: rgb({0},{1},{2})'".format(*CONFIG.theme.colLink)
# Assemble
self.outerWidget = QWidget()
@@ -1180,10 +1172,10 @@ class GuiDocViewDetails(QScrollArea):
if self.mainGui.docViewer.stickyRef:
return
theRefs = self.theProject.index.getBackReferenceList(tHandle)
theRefs = self.mainGui.project.index.getBackReferenceList(tHandle)
theList = []
for tHandle in theRefs:
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
if tItem is not None:
theList.append("<a href='%s#%s' %s>%s</a>" % (
tHandle, theRefs[tHandle], self.linkStyle, tItem.itemName
+12 -14
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Item Details Panel
====================================
GUI class for the project tree item details panel
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -42,9 +42,7 @@ class GuiItemDetails(QWidget):
logger.debug("Create: GuiItemDetails")
self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainTheme = mainGui.mainTheme
self.mainGui = mainGui
# Internal Variables
self._itemHandle = None
@@ -53,7 +51,7 @@ class GuiItemDetails(QWidget):
hSp = CONFIG.pxInt(6)
vSp = CONFIG.pxInt(1)
mPx = CONFIG.pxInt(6)
fPt = self.mainTheme.fontPointSize
fPt = CONFIG.theme.fontPointSize
fntLabel = QFont()
fntLabel.setBold(True)
@@ -178,8 +176,8 @@ class GuiItemDetails(QWidget):
self.updateTheme()
# Make sure the columns for flags and counts don't resize too often
flagWidth = self.mainTheme.getTextWidth("Mm", fntValue)
countWidth = self.mainTheme.getTextWidth("99,999", fntValue)
flagWidth = CONFIG.theme.getTextWidth("Mm", fntValue)
countWidth = CONFIG.theme.getTextWidth("99,999", fntValue)
self.mainBox.setColumnMinimumWidth(1, flagWidth)
self.mainBox.setColumnMinimumWidth(4, countWidth)
@@ -235,13 +233,13 @@ class GuiItemDetails(QWidget):
self.clearDetails()
return
nwItem = self.theProject.tree[tHandle]
nwItem = self.mainGui.project.tree[tHandle]
if nwItem is None:
self.clearDetails()
return
self._itemHandle = tHandle
iPx = int(round(0.8*self.mainTheme.baseIconSize))
iPx = int(round(0.8*CONFIG.theme.baseIconSize))
# Label
# =====
@@ -252,11 +250,11 @@ class GuiItemDetails(QWidget):
if nwItem.isFileType():
if nwItem.isActive:
self.labelIcon.setPixmap(self.mainTheme.getPixmap("checked", (iPx, iPx)))
self.labelIcon.setPixmap(CONFIG.theme.getPixmap("checked", (iPx, iPx)))
else:
self.labelIcon.setPixmap(self.mainTheme.getPixmap("unchecked", (iPx, iPx)))
self.labelIcon.setPixmap(CONFIG.theme.getPixmap("unchecked", (iPx, iPx)))
else:
self.labelIcon.setPixmap(self.mainTheme.getPixmap("noncheckable", (iPx, iPx)))
self.labelIcon.setPixmap(CONFIG.theme.getPixmap("noncheckable", (iPx, iPx)))
self.labelData.setText(theLabel)
@@ -270,14 +268,14 @@ class GuiItemDetails(QWidget):
# 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.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass]))
# Layout
# ======
usageIcon = self.mainTheme.getItemIcon(
usageIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
)
self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx))
+14 -15
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Main Menu
===========================
GUI class for the main window menu
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -51,8 +51,7 @@ class GuiMainMenu(QMenuBar):
logger.debug("Create: GuiMainMenu")
self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainGui = mainGui
# Build Menu
self._buildProjectMenu()
@@ -380,10 +379,10 @@ class GuiMainMenu(QMenuBar):
"""Assemble the Insert menu.
"""
# Insert
self.insertMenu = self.addMenu(self.tr("&Insert"))
self.insMenu = self.addMenu(self.tr("&Insert"))
# Insert > Dashes and Dots
self.mInsDashes = self.insertMenu.addMenu(self.tr("Dashes"))
self.mInsDashes = self.insMenu.addMenu(self.tr("Dashes"))
# Insert > Short Dash
self.aInsENDash = QAction(self.tr("Short Dash"), self)
@@ -410,7 +409,7 @@ class GuiMainMenu(QMenuBar):
self.mInsDashes.addAction(self.aInsFigDash)
# 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
self.aInsQuoteLS = QAction(self.tr("Left Single Quote"), self)
@@ -443,7 +442,7 @@ class GuiMainMenu(QMenuBar):
self.mInsQuotes.addAction(self.aInsMSApos)
# Insert > Symbols
self.mInsPunct = self.insertMenu.addMenu(self.tr("General Punctuation"))
self.mInsPunct = self.insMenu.addMenu(self.tr("General Punctuation"))
# Insert > Ellipsis
self.aInsEllipsis = QAction(self.tr("Ellipsis"), self)
@@ -464,7 +463,7 @@ class GuiMainMenu(QMenuBar):
self.mInsPunct.addAction(self.aInsDPrime)
# 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
self.aInsNBSpace = QAction(self.tr("Non-Breaking Space"), self)
@@ -485,7 +484,7 @@ class GuiMainMenu(QMenuBar):
self.mInsSpace.addAction(self.aInsThinNBSpace)
# Insert > Symbols
self.mInsSymbol = self.insertMenu.addMenu(self.tr("Other Symbols"))
self.mInsSymbol = self.insMenu.addMenu(self.tr("Other Symbols"))
# Insert > List Bullet
self.aInsBullet = QAction(self.tr("List Bullet"), self)
@@ -536,7 +535,7 @@ class GuiMainMenu(QMenuBar):
self.mInsSymbol.addAction(self.aInsDivide)
# 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[nwKeyWords.TAG_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, G")
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])
# Insert > Special Comments
self.mInsComments = self.insertMenu.addMenu(self.tr("Special Comments"))
self.mInsComments = self.insMenu.addMenu(self.tr("Special Comments"))
# Insert > Synopsis Comment
self.aInsSynopsis = QAction(self.tr("Synopsis Comment"), self)
@@ -566,7 +565,7 @@ class GuiMainMenu(QMenuBar):
self.mInsComments.addAction(self.aInsSynopsis)
# 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
self.aInsNewPage = QAction(self.tr("Page Break"), self)
@@ -586,7 +585,7 @@ class GuiMainMenu(QMenuBar):
# Insert > Placeholder Text
self.aLipsumText = QAction(self.tr("Placeholder Text"), self)
self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog())
self.insertMenu.addAction(self.aLipsumText)
self.insMenu.addAction(self.aLipsumText)
return
@@ -796,7 +795,7 @@ class GuiMainMenu(QMenuBar):
# Tools > Check Spelling
self.aSpellCheck = QAction(self.tr("Check Spelling"), self)
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.setShortcut("Ctrl+F7")
self.toolsMenu.addAction(self.aSpellCheck)
@@ -826,7 +825,7 @@ class GuiMainMenu(QMenuBar):
# Tools > Backup Project
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)
# Tools > Build Manuscript
+34 -38
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Novel Tree
============================
GUI class for the main window novel tree
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -42,7 +42,7 @@ from novelwriter import CONFIG
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
from novelwriter.common import minmax
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
from novelwriter.gui.components import NovelSelector
from novelwriter.extensions.novelselector import NovelSelector
logger = logging.getLogger(__name__)
@@ -66,8 +66,7 @@ class GuiNovelView(QWidget):
def __init__(self, mainGui):
super().__init__(parent=mainGui)
self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainGui = mainGui
# Build GUI
self.novelTree = GuiNovelTree(self)
@@ -118,16 +117,16 @@ class GuiNovelView(QWidget):
def openProjectTasks(self):
"""Run open project tasks.
"""
lastNovel = self.theProject.data.getLastHandle("novelTree")
if lastNovel not in self.theProject.tree:
lastNovel = self.theProject.tree.findRoot(nwItemClass.NOVEL)
lastNovel = self.mainGui.project.data.getLastHandle("novelTree")
if lastNovel not in self.mainGui.project.tree:
lastNovel = self.mainGui.project.tree.findRoot(nwItemClass.NOVEL)
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
)
lastColSize = self.theProject.options.getInt(
lastColSize = self.mainGui.project.options.getInt(
"GuiNovelView", "lastColSize", 25
)
@@ -147,8 +146,9 @@ class GuiNovelView(QWidget):
"""
lastColType = self.novelTree.lastColType
lastColSize = self.novelTree.lastColSize
self.theProject.options.setValue("GuiNovelView", "lastCol", lastColType)
self.theProject.options.setValue("GuiNovelView", "lastColSize", lastColSize)
pOptions = self.mainGui.project.options
pOptions.setValue("GuiNovelView", "lastCol", lastColType)
pOptions.setValue("GuiNovelView", "lastColSize", lastColSize)
return
def setTreeFocus(self):
@@ -170,7 +170,7 @@ class GuiNovelView(QWidget):
def refreshTree(self):
"""Refresh the current tree.
"""
self.novelTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("novelTree"))
self.novelTree.refreshTree(rootHandle=self.mainGui.project.data.getLastHandle("novelTree"))
return
@pyqtSlot(str)
@@ -198,12 +198,10 @@ class GuiNovelToolBar(QWidget):
logger.debug("Create: GuiNovelToolBar")
self.novelView = novelView
self.mainGui = novelView.mainGui
self.theProject = novelView.mainGui.theProject
self.mainTheme = novelView.mainGui.mainTheme
self.novelView = novelView
self.mainGui = novelView.mainGui
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
mPx = CONFIG.pxInt(2)
self.setContentsMargins(0, 0, 0, 0)
@@ -213,7 +211,7 @@ class GuiNovelToolBar(QWidget):
selFont = self.font()
selFont.setWeight(QFont.Bold)
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.setMinimumWidth(CONFIG.pxInt(150))
self.novelValue.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -276,9 +274,9 @@ class GuiNovelToolBar(QWidget):
"""Update theme elements.
"""
# Icons
self.tbNovel.setIcon(self.mainTheme.getIcon("cls_novel"))
self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
self.tbNovel.setIcon(CONFIG.theme.getIcon("cls_novel"))
self.tbRefresh.setIcon(CONFIG.theme.getIcon("refresh"))
self.tbMore.setIcon(CONFIG.theme.getIcon("menu"))
qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base())
@@ -347,7 +345,7 @@ class GuiNovelToolBar(QWidget):
def _refreshNovelTree(self):
"""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)
return
@@ -399,10 +397,8 @@ class GuiNovelTree(QTreeWidget):
logger.debug("Create: GuiNovelTree")
self.novelView = novelView
self.mainGui = novelView.mainGui
self.mainTheme = novelView.mainGui.mainTheme
self.theProject = novelView.mainGui.theProject
self.novelView = novelView
self.mainGui = novelView.mainGui
# Internal Variables
self._treeMap = {}
@@ -419,7 +415,7 @@ class GuiNovelTree(QTreeWidget):
# Build GUI
# =========
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
cMg = CONFIG.pxInt(6)
self.setIconSize(QSize(iPx, iPx))
@@ -485,8 +481,8 @@ class GuiNovelTree(QTreeWidget):
def updateTheme(self):
"""Update theme elements.
"""
iPx = self.mainTheme.baseIconSize
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
iPx = CONFIG.theme.baseIconSize
self._pMore = CONFIG.theme.loadDecoration("deco_doc_more", pxH=iPx)
return
##
@@ -518,10 +514,10 @@ class GuiNovelTree(QTreeWidget):
"""
logger.debug("Requesting refresh of the novel tree")
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)
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild)
indexChanged = self.mainGui.project.index.rootChangedSince(rootHandle, self._lastBuild)
if not (treeChanged or indexChanged or overRide):
logger.debug("No changes have been made to the novel index")
return
@@ -532,7 +528,7 @@ class GuiNovelTree(QTreeWidget):
titleKey = selItem[0].data(self.C_DATA, self.D_KEY)
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:
self._treeMap[titleKey].setSelected(True)
@@ -542,7 +538,7 @@ class GuiNovelTree(QTreeWidget):
def refreshHandle(self, tHandle):
"""Refresh the data for a given handle.
"""
idxData = self.theProject.index.getItemData(tHandle)
idxData = self.mainGui.project.index.getItemData(tHandle)
if idxData is None:
return
@@ -579,7 +575,7 @@ class GuiNovelTree(QTreeWidget):
self._lastCol = colType
self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN)
if doRefresh:
lastNovel = self.theProject.data.getLastHandle("novelTree")
lastNovel = self.mainGui.project.data.getLastHandle("novelTree")
self.refreshTree(rootHandle=lastNovel, overRide=True)
return
@@ -711,7 +707,7 @@ class GuiNovelTree(QTreeWidget):
tStart = time()
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:
if novIdx.level == "H0":
continue
@@ -737,7 +733,7 @@ class GuiNovelTree(QTreeWidget):
"""Set the tree item values from the index entry.
"""
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.setText(self.C_TITLE, idxItem.title)
@@ -763,7 +759,7 @@ class GuiNovelTree(QTreeWidget):
refData = []
refName = ""
theRefs = self.theProject.index.getReferences(tHandle, sTitle)
theRefs = self.mainGui.project.index.getReferences(tHandle, sTitle)
if self._lastCol == NovelTreeColumn.POV:
refData = theRefs[nwKeyWords.POV_KEY]
refName = self._povLabel
@@ -787,7 +783,7 @@ class GuiNovelTree(QTreeWidget):
"""
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)
refTags = pIndex.getReferences(tHandle, sTitle)
+31 -38
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Project Outline
=================================
GUI class for the project outline view
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -48,7 +48,7 @@ from novelwriter.enum import (
from novelwriter.error import logException
from novelwriter.common import checkInt
from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels
from novelwriter.gui.components import NovelSelector
from novelwriter.extensions.novelselector import NovelSelector
logger = logging.getLogger(__name__)
@@ -62,8 +62,7 @@ class GuiOutlineView(QWidget):
def __init__(self, mainGui):
super().__init__(parent=mainGui)
self.mainGui = mainGui
self.theProject = mainGui.theProject
self.mainGui = mainGui
# Build GUI
self.outlineTree = GuiOutlineTree(self)
@@ -118,7 +117,7 @@ class GuiOutlineView(QWidget):
def refreshTree(self):
"""Refresh the current tree.
"""
self.outlineTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("outline"))
self.outlineTree.refreshTree(rootHandle=self.mainGui.project.data.getLastHandle("outline"))
return
def clearProject(self):
@@ -131,9 +130,9 @@ class GuiOutlineView(QWidget):
def openProjectTasks(self):
"""Run open project tasks.
"""
lastOutline = self.theProject.data.getLastHandle("outline")
if not (lastOutline in self.theProject.tree or lastOutline is None):
lastOutline = self.theProject.tree.findRoot(nwItemClass.NOVEL)
lastOutline = self.mainGui.project.data.getLastHandle("outline")
if not (lastOutline in self.mainGui.project.tree or lastOutline is None):
lastOutline = self.mainGui.project.tree.findRoot(nwItemClass.NOVEL)
logger.debug("Setting outline tree to root item '%s'", lastOutline)
@@ -215,9 +214,7 @@ class GuiOutlineToolBar(QToolBar):
logger.debug("Create: GuiOutlineToolBar")
self.mainGui = theOutline.mainGui
self.theProject = theOutline.mainGui.theProject
self.mainTheme = theOutline.mainGui.mainTheme
self.mainGui = theOutline.mainGui
iPx = CONFIG.pxInt(22)
mPx = CONFIG.pxInt(12)
@@ -233,7 +230,7 @@ class GuiOutlineToolBar(QToolBar):
self.novelLabel = QLabel(self.tr("Outline of"))
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.novelSelectionChanged.connect(self._novelValueChanged)
@@ -275,8 +272,8 @@ class GuiOutlineToolBar(QToolBar):
self.setStyleSheet("QToolBar {border: 0px;}")
self.novelValue.updateList(includeAll=True)
self.aRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.tbColumns.setIcon(self.mainTheme.getIcon("menu"))
self.aRefresh.setIcon(CONFIG.theme.getIcon("refresh"))
self.tbColumns.setIcon(CONFIG.theme.getIcon("menu"))
return
@@ -374,8 +371,6 @@ class GuiOutlineTree(QTreeWidget):
self.outlineView = outlineView
self.mainGui = outlineView.mainGui
self.theProject = outlineView.mainGui.theProject
self.mainTheme = outlineView.mainGui.mainTheme
self.setUniformRowHeights(True)
self.setFrameStyle(QFrame.NoFrame)
@@ -386,7 +381,7 @@ class GuiOutlineTree(QTreeWidget):
self.itemDoubleClicked.connect(self._treeDoubleClick)
self.itemSelectionChanged.connect(self._itemSelected)
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
self.setIconSize(QSize(iPx, iPx))
self.setIndentation(0)
@@ -403,11 +398,11 @@ class GuiOutlineTree(QTreeWidget):
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
self._dIcon = {
"H0": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H0"),
"H1": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H1"),
"H2": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H2"),
"H3": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H3"),
"H4": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H4"),
"H0": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H0"),
"H1": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H1"),
"H2": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H2"),
"H3": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H3"),
"H4": CONFIG.theme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H4"),
}
# Internals
@@ -493,13 +488,13 @@ class GuiOutlineTree(QTreeWidget):
# If the novel index or novel tree has changed since the tree
# 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):
logger.debug("No changes have been made to the novel index")
return
self._populateTree(rootHandle)
self.theProject.data.setLastHandle(rootHandle or None, "outline")
self.mainGui.project.data.setLastHandle(rootHandle or None, "outline")
return
@@ -579,7 +574,7 @@ class GuiOutlineTree(QTreeWidget):
"""
# Load whatever we saved last time, regardless of wether it
# contains the correct names or number of columns.
colState = self.theProject.options.getValue("GuiOutline", "columnState", {})
colState = self.mainGui.project.options.getValue("GuiOutline", "columnState", {})
tmpOrder = []
tmpHidden = {}
@@ -630,7 +625,7 @@ class GuiOutlineTree(QTreeWidget):
logHidden, orgWidth if logHidden and logWidth == 0 else logWidth
]
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
pOptions.setValue("GuiOutline", "columnState", colState)
pOptions.saveSettings()
@@ -666,7 +661,7 @@ class GuiOutlineTree(QTreeWidget):
headItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], 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:
iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0)
@@ -674,8 +669,8 @@ class GuiOutlineTree(QTreeWidget):
continue
trItem = QTreeWidgetItem()
nwItem = self.theProject.tree[tHandle]
hDec = self.mainTheme.getHeaderDecoration(iLevel)
nwItem = self.mainGui.project.tree[tHandle]
hDec = CONFIG.theme.getHeaderDecoration(iLevel)
trItem.setData(self._colIdx[nwOutline.TITLE], Qt.DecorationRole, hDec)
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.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.FOCUS], ", ".join(refs[nwKeyWords.FOCUS_KEY]))
trItem.setText(self._colIdx[nwOutline.CHAR], ", ".join(refs[nwKeyWords.CHAR_KEY]))
@@ -776,13 +771,11 @@ class GuiOutlineDetails(QScrollArea):
self.theOutline = theOutline
self.mainGui = theOutline.mainGui
self.theProject = theOutline.mainGui.theProject
self.mainTheme = theOutline.mainGui.mainTheme
# Sizes
minTitle = 30*self.mainTheme.textNWidth
maxTitle = 40*self.mainTheme.textNWidth
wCount = self.mainTheme.getTextWidth("999,999")
minTitle = 30*CONFIG.theme.textNWidth
maxTitle = 40*CONFIG.theme.textNWidth
wCount = CONFIG.theme.getTextWidth("999,999")
hSpace = int(CONFIG.pxInt(10))
vSpace = int(CONFIG.pxInt(4))
@@ -1012,8 +1005,8 @@ class GuiOutlineDetails(QScrollArea):
"""Update the content of the tree with the given handle and line
number pointing to a header.
"""
pIndex = self.theProject.index
nwItem = self.theProject.tree[tHandle]
pIndex = self.mainGui.project.index
nwItem = self.mainGui.project.tree[tHandle]
novIdx = pIndex.getItemHeader(tHandle, sTitle)
theRefs = pIndex.getReferences(tHandle, sTitle)
if nwItem is None or novIdx is None:
@@ -1056,7 +1049,7 @@ class GuiOutlineDetails(QScrollArea):
def updateClasses(self):
"""Update the visibility status of class details.
"""
usedClasses = self.theProject.tree.rootClasses()
usedClasses = self.mainGui.project.tree.rootClasses()
pltVisible = nwItemClass.PLOT in usedClasses
timVisible = nwItemClass.TIMELINE in usedClasses
+76 -81
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Project Tree
==============================
GUI classes for the main window project tree
File History:
Created: 2018-09-29 [0.0.1] GuiProjectTree
@@ -233,13 +232,11 @@ class GuiProjectToolBar(QWidget):
logger.debug("Create: GuiProjectToolBar")
self.projView = projView
self.projTree = projView.projTree
self.mainGui = projView.mainGui
self.theProject = projView.mainGui.theProject
self.mainTheme = projView.mainGui.mainTheme
self.projView = projView
self.projTree = projView.projTree
self.mainGui = projView.mainGui
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
mPx = CONFIG.pxInt(2)
self.setContentsMargins(0, 0, 0, 0)
@@ -370,16 +367,16 @@ class GuiProjectToolBar(QWidget):
self.tbAdd.setStyleSheet(buttonStyle)
self.tbMore.setStyleSheet(buttonStyle)
self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark"))
self.tbMoveU.setIcon(self.mainTheme.getIcon("up"))
self.tbMoveD.setIcon(self.mainTheme.getIcon("down"))
self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document"))
self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter"))
self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene"))
self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note"))
self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder"))
self.tbAdd.setIcon(self.mainTheme.getIcon("add"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
self.tbQuick.setIcon(CONFIG.theme.getIcon("bookmark"))
self.tbMoveU.setIcon(CONFIG.theme.getIcon("up"))
self.tbMoveD.setIcon(CONFIG.theme.getIcon("down"))
self.aAddEmpty.setIcon(CONFIG.theme.getIcon("proj_document"))
self.aAddChap.setIcon(CONFIG.theme.getIcon("proj_chapter"))
self.aAddScene.setIcon(CONFIG.theme.getIcon("proj_scene"))
self.aAddNote.setIcon(CONFIG.theme.getIcon("proj_note"))
self.aAddFolder.setIcon(CONFIG.theme.getIcon("proj_folder"))
self.tbAdd.setIcon(CONFIG.theme.getIcon("add"))
self.tbMore.setIcon(CONFIG.theme.getIcon("menu"))
self.buildQuickLinkMenu()
self._buildRootMenu()
@@ -395,10 +392,10 @@ class GuiProjectToolBar(QWidget):
"""Build the quick link menu."""
logger.debug("Rebuilding quick links menu")
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.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(
lambda n, tHandle=tHandle: self.projView.setSelectedHandle(tHandle, doScroll=True)
)
@@ -412,7 +409,7 @@ class GuiProjectToolBar(QWidget):
"""Build the rood folder menu."""
def addClass(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))
self.mAddRoot.addAction(aNew)
return
@@ -441,7 +438,7 @@ class GuiProjectToolBar(QWidget):
documents. They should only be visible if novel documents can
actually be added.
"""
nwItem = self.theProject.tree[tHandle]
nwItem = self.mainGui.project.tree[tHandle]
allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed()
self.aAddEmpty.setVisible(allowDoc)
self.aAddChap.setVisible(allowDoc)
@@ -467,10 +464,8 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Create: GuiProjectTree")
self.projView = projView
self.mainGui = projView.mainGui
self.mainTheme = projView.mainGui.mainTheme
self.theProject = projView.mainGui.theProject
self.projView = projView
self.mainGui = projView.mainGui
# Internal Variables
self._treeMap = {}
@@ -485,7 +480,7 @@ class GuiProjectTree(QTreeWidget):
self.customContextMenuRequested.connect(self._openContextMenu)
# Tree Settings
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
cMg = CONFIG.pxInt(6)
self.setIconSize(QSize(iPx, iPx))
@@ -577,15 +572,15 @@ class GuiProjectTree(QTreeWidget):
if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
tHandle = self.theProject.newRoot(itemClass)
tHandle = self.mainGui.project.newRoot(itemClass)
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
elif itemType in (nwItemType.FILE, nwItemType.FOLDER):
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:
self.mainGui.makeAlert(self.tr(
"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)
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(
"Cannot add new files or folders to the Trash folder."
), level=nwAlert.ERROR)
@@ -640,9 +635,9 @@ class GuiProjectTree(QTreeWidget):
# Add the file or folder
if itemType == nwItemType.FILE:
tHandle = self.theProject.newFile(newLabel, sHandle)
tHandle = self.mainGui.project.newFile(newLabel, sHandle)
else:
tHandle = self.theProject.newFolder(newLabel, sHandle)
tHandle = self.mainGui.project.newFolder(newLabel, sHandle)
else:
logger.error("Failed to add new item")
@@ -655,7 +650,7 @@ class GuiProjectTree(QTreeWidget):
# Handle new file creation
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
self.revealNewTreeItem(tHandle, nHandle=nHandle, wordCount=True)
@@ -666,7 +661,7 @@ class GuiProjectTree(QTreeWidget):
def revealNewTreeItem(self, tHandle: str | None, nHandle: str | None = None,
wordCount: bool = False) -> bool:
"""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:
return False
@@ -675,7 +670,7 @@ class GuiProjectTree(QTreeWidget):
return False
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.projView.wordCountsChanged.emit()
@@ -750,7 +745,7 @@ class GuiProjectTree(QTreeWidget):
def renameTreeItem(self, tHandle: str) -> bool:
"""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:
return False
@@ -774,7 +769,7 @@ class GuiProjectTree(QTreeWidget):
if isinstance(item, QTreeWidgetItem):
theList = self._scanChildren(theList, item, i)
logger.debug("Saving project tree item order")
self.theProject.setTreeOrder(theList)
self.mainGui.project.setTreeOrder(theList)
return
def getTreeFromHandle(self, tHandle: str) -> list[str]:
@@ -807,16 +802,16 @@ class GuiProjectTree(QTreeWidget):
logger.error("There is no item to delete")
return False
trashHandle = self.theProject.tree.trashRoot()
trashHandle = self.mainGui.project.tree.trashRoot()
if tHandle == trashHandle:
logger.error("Cannot delete the Trash folder")
return False
nwItem = self.theProject.tree[tHandle]
nwItem = self.mainGui.project.tree[tHandle]
if nwItem is None:
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)
else:
status = self.moveItemToTrash(tHandle)
@@ -832,7 +827,7 @@ class GuiProjectTree(QTreeWidget):
logger.error("No project open")
return False
trashHandle = self.theProject.tree.trashRoot()
trashHandle = self.mainGui.project.tree.trashRoot()
logger.debug("Emptying Trash folder")
if trashHandle is None:
@@ -875,13 +870,13 @@ class GuiProjectTree(QTreeWidget):
so such a request is cancelled.
"""
trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.tree[tHandle]
nwItemS = self.mainGui.project.tree[tHandle]
if trItemS is None or nwItemS is None:
logger.error("Could not find tree item for deletion")
return False
if self.theProject.tree.isTrash(tHandle):
if self.mainGui.project.tree.isTrash(tHandle):
logger.error("Item is already in the Trash folder")
return False
@@ -925,7 +920,7 @@ class GuiProjectTree(QTreeWidget):
Root items are handled a little different than other items.
"""
trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.tree[tHandle]
nwItemS = self.mainGui.project.tree[tHandle]
if trItemS is None or nwItemS is None:
logger.error("Could not find tree item for deletion")
return False
@@ -942,7 +937,7 @@ class GuiProjectTree(QTreeWidget):
tIndex = self.indexOfTopLevelItem(trItemS)
self.takeTopLevelItem(tIndex)
self.theProject.removeItem(tHandle)
self.mainGui.project.removeItem(tHandle)
self._treeMap.pop(tHandle, None)
self._alertTreeChange(tHandle, flush=True)
@@ -971,7 +966,7 @@ class GuiProjectTree(QTreeWidget):
for dHandle in reversed(self.getTreeFromHandle(tHandle)):
if self.mainGui.docEditor.docHandle() == dHandle:
self.mainGui.closeDocument()
self.theProject.removeItem(dHandle)
self.mainGui.project.removeItem(dHandle)
self._treeMap.pop(dHandle, None)
self._alertTreeChange(tHandle, flush=flush)
@@ -989,13 +984,13 @@ class GuiProjectTree(QTreeWidget):
already coming from the project tree.
"""
trItem = self._getTreeItem(tHandle)
nwItem = self.theProject.tree[tHandle]
nwItem = self.mainGui.project.tree[tHandle]
if trItem is None or nwItem is None:
return
itemStatus, statusIcon = nwItem.getImportStatus(incIcon=True)
hLevel = nwItem.mainHeading
itemIcon = self.mainTheme.getItemIcon(
itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
)
@@ -1011,7 +1006,7 @@ class GuiProjectTree(QTreeWidget):
else:
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():
trFont = trItem.font(self.C_NAME)
@@ -1051,10 +1046,10 @@ class GuiProjectTree(QTreeWidget):
pHandle = pItem.data(self.C_DATA, self.D_HANDLE)
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
# 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)
@@ -1069,7 +1064,7 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Building the project tree ...")
self.clearTree()
count = 0
for nwItem in self.theProject.getProjectItems():
for nwItem in self.mainGui.project.getProjectItems():
count += 1
self._addTreeItem(nwItem)
if count > 0:
@@ -1182,7 +1177,7 @@ class GuiProjectTree(QTreeWidget):
if tHandle is None:
return
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
if tItem is None:
return
@@ -1204,7 +1199,7 @@ class GuiProjectTree(QTreeWidget):
selItem = self.itemAt(clickPos)
if isinstance(selItem, QTreeWidgetItem):
tHandle = selItem.data(self.C_DATA, self.D_HANDLE)
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
hasChild = selItem.childCount() > 0
if tItem is None or tHandle is None:
@@ -1216,7 +1211,7 @@ class GuiProjectTree(QTreeWidget):
# Trash Folder
# ============
trashHandle = self.theProject.tree.trashRoot()
trashHandle = self.mainGui.project.tree.trashRoot()
if tItem.itemHandle == trashHandle and trashHandle is not None:
# The trash folder only has one option
aEmptyTrash = ctxMenu.addAction(self.tr("Empty Trash"))
@@ -1255,7 +1250,7 @@ class GuiProjectTree(QTreeWidget):
checkMark = f" ({nwUnicode.U_CHECK})"
if tItem.isNovelLike():
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 "")
aStatus = mStatus.addAction(entry["icon"], entryName)
aStatus.triggered.connect(
@@ -1268,7 +1263,7 @@ class GuiProjectTree(QTreeWidget):
)
else:
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 "")
aImport = mImport.addAction(entry["icon"], entryName)
aImport.triggered.connect(
@@ -1380,7 +1375,7 @@ class GuiProjectTree(QTreeWidget):
return
tHandle = selItem.data(self.C_DATA, self.D_HANDLE)
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
if tItem is None:
return
@@ -1424,7 +1419,7 @@ class GuiProjectTree(QTreeWidget):
def _postItemMove(self, tHandle: str, wCount: int) -> bool:
"""Run various maintenance tasks for a moved item."""
trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.tree[tHandle]
nwItemS = self.mainGui.project.tree[tHandle]
trItemP = trItemS.parent() if trItemS else None
if trItemP is None or nwItemS is None:
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))
for mHandle in mHandles:
logger.debug("Updating item '%s'", mHandle)
self.theProject.tree.updateItemData(mHandle)
self.mainGui.project.tree.updateItemData(mHandle)
# Update the index
if nwItemS.isInactiveClass():
self.theProject.index.deleteHandle(mHandle)
self.mainGui.project.index.deleteHandle(mHandle)
else:
self.theProject.index.reIndexHandle(mHandle)
self.mainGui.project.index.reIndexHandle(mHandle)
self.setTreeItemValues(mHandle)
@@ -1467,7 +1462,7 @@ class GuiProjectTree(QTreeWidget):
def _toggleItemActive(self, tHandle: str) -> None:
"""Toggle the active status of an item."""
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
if tItem is not None:
tItem.setActive(not tItem.isActive)
self.setTreeItemValues(tItem.itemHandle)
@@ -1488,7 +1483,7 @@ class GuiProjectTree(QTreeWidget):
def _changeItemStatus(self, tHandle: str, tStatus: str) -> None:
"""Set a new status value of an item."""
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
if tItem is not None:
tItem.setStatus(tStatus)
self.setTreeItemValues(tItem.itemHandle)
@@ -1497,7 +1492,7 @@ class GuiProjectTree(QTreeWidget):
def _changeItemImport(self, tHandle: str, tImport: str) -> None:
"""Set a new importance value of an item."""
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
if tItem is not None:
tItem.setImport(tImport)
self.setTreeItemValues(tItem.itemHandle)
@@ -1506,7 +1501,7 @@ class GuiProjectTree(QTreeWidget):
def _changeItemLayout(self, tHandle: str, itemLayout: nwItemLayout) -> None:
"""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 itemLayout == nwItemLayout.DOCUMENT and tItem.documentAllowed():
tItem.setLayout(nwItemLayout.DOCUMENT)
@@ -1520,7 +1515,7 @@ class GuiProjectTree(QTreeWidget):
def _covertFolderToFile(self, tHandle: str, itemLayout: nwItemLayout) -> None:
"""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():
msgYes = self.mainGui.askQuestion(self.tr(
"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)
itemList = self.getTreeFromHandle(tHandle)
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
if tItem is None:
return False
@@ -1571,7 +1566,7 @@ class GuiProjectTree(QTreeWidget):
self.mainGui.saveDocument()
# Create merge object, and append docs
docMerger = DocMerger(self.theProject)
docMerger = DocMerger(self.mainGui.project)
mLabel = self.tr("Merged")
if newFile:
@@ -1593,7 +1588,7 @@ class GuiProjectTree(QTreeWidget):
)
return False
self.theProject.index.reIndexHandle(mHandle)
self.mainGui.project.index.reIndexHandle(mHandle)
if newFile:
self.revealNewTreeItem(mHandle, nHandle=tHandle, wordCount=True)
@@ -1618,7 +1613,7 @@ class GuiProjectTree(QTreeWidget):
"""Split a document into multiple documents."""
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:
return False
@@ -1637,7 +1632,7 @@ class GuiProjectTree(QTreeWidget):
intoFolder = splitData.get("intoFolder", False)
docHierarchy = splitData.get("docHierarchy", False)
docSplit = DocSplitter(self.theProject, tHandle)
docSplit = DocSplitter(self.mainGui.project, tHandle)
if intoFolder:
fHandle = docSplit.newParentFolder(tItem.itemParent, tItem.itemName)
self.revealNewTreeItem(fHandle, nHandle=tHandle)
@@ -1647,7 +1642,7 @@ class GuiProjectTree(QTreeWidget):
docSplit.splitDocument(headerList, splitText)
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._alertTreeChange(dHandle, flush=False)
if not writeOk:
@@ -1681,10 +1676,10 @@ class GuiProjectTree(QTreeWidget):
if not self.mainGui.askQuestion(question):
return False
docDup = DocDuplicator(self.theProject)
docDup = DocDuplicator(self.mainGui.project)
dupCount = 0
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._alertTreeChange(dHandle, flush=False)
dupCount += 1
@@ -1704,7 +1699,7 @@ class GuiProjectTree(QTreeWidget):
cCount = tItem.childCount()
# Update tree-related meta data
nwItem = self.theProject.tree[tHandle]
nwItem = self.mainGui.project.tree[tHandle]
if nwItem is not None:
nwItem.setExpanded(tItem.isExpanded() and cCount > 0)
nwItem.setOrder(tIndex)
@@ -1771,13 +1766,13 @@ class GuiProjectTree(QTreeWidget):
"""Adds the trash root folder if it doesn't already exist in the
project tree.
"""
trashHandle = self.theProject.trashFolder()
trashHandle = self.mainGui.project.trashFolder()
if trashHandle is None:
return None
trItem = self._getTreeItem(trashHandle)
if trItem is None:
trItem = self._addTreeItem(self.theProject.tree[trashHandle])
trItem = self._addTreeItem(self.mainGui.project.tree[trashHandle])
if trItem is not None:
trItem.setExpanded(True)
self._alertTreeChange(trashHandle, flush=True)
@@ -1790,14 +1785,14 @@ class GuiProjectTree(QTreeWidget):
deleted.
"""
self._timeChanged = time()
self.theProject.setProjectChanged(True)
self.mainGui.project.setProjectChanged(True)
if flush:
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
tItem = self.theProject.tree[tHandle]
tItem = self.mainGui.project.tree[tHandle]
if tItem and tItem.isRootType():
self.projView.rootFolderChanged.emit(tHandle)
+11 -12
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Main Window SideBar
=====================================
GUI class for the main window side bar
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -45,15 +45,14 @@ class GuiSideBar(QToolBar):
logger.debug("Create: GuiSideBar")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = mainGui
# Style
iPx = CONFIG.pxInt(22)
mPx = CONFIG.pxInt(60)
lblFont = self.mainTheme.guiFont
lblFont.setPointSizeF(0.65*self.mainTheme.fontPointSize)
lblFont = CONFIG.theme.guiFont
lblFont.setPointSizeF(0.65*CONFIG.theme.fontPointSize)
self.setMovable(False)
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
@@ -131,13 +130,13 @@ class GuiSideBar(QToolBar):
"""
self.setStyleSheet("QToolBar {border: 0px;}")
self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
self.tbSettings.setIcon(self.mainTheme.getIcon("settings"))
self.aProject.setIcon(CONFIG.theme.getIcon("view_editor"))
self.aNovel.setIcon(CONFIG.theme.getIcon("view_novel"))
self.aOutline.setIcon(CONFIG.theme.getIcon("view_outline"))
self.aBuild.setIcon(CONFIG.theme.getIcon("view_build"))
self.aDetails.setIcon(CONFIG.theme.getIcon("proj_details"))
self.aStats.setIcon(CONFIG.theme.getIcon("proj_stats"))
self.tbSettings.setIcon(CONFIG.theme.getIcon("settings"))
return
+12 -13
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI Main Window Status Bar
========================================
GUI class for the main window status bar
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import qApp, QStatusBar, QLabel
from novelwriter import CONFIG
from novelwriter.common import formatTime
from novelwriter.gui.components import StatusLED
from novelwriter.extensions.statusled import StatusLED
logger = logging.getLogger(__name__)
@@ -47,15 +47,14 @@ class GuiMainStatus(QStatusBar):
logger.debug("Create: GuiMainStatus")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.refTime = None
self.userIdle = False
colNone = QColor(*self.mainTheme.statNone)
colSaved = QColor(*self.mainTheme.statSaved)
colUnsaved = QColor(*self.mainTheme.statUnsaved)
colNone = QColor(*CONFIG.theme.statNone)
colSaved = QColor(*CONFIG.theme.statSaved)
colUnsaved = QColor(*CONFIG.theme.statUnsaved)
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
# Permanent Widgets
# =================
@@ -99,7 +98,7 @@ class GuiMainStatus(QStatusBar):
self.timeIcon = QLabel()
self.timeText = QLabel("")
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.timeText.setContentsMargins(0, 0, 0, 0)
self.addPermanentWidget(self.timeIcon)
@@ -129,13 +128,13 @@ class GuiMainStatus(QStatusBar):
def updateTheme(self):
"""Update theme elements.
"""
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx)))
self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx)))
self.langIcon.setPixmap(CONFIG.theme.getPixmap("status_lang", (iPx, iPx)))
self.statsIcon.setPixmap(CONFIG.theme.getPixmap("status_stats", (iPx, iPx)))
self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
self.timePixmap = CONFIG.theme.getPixmap("status_time", (iPx, iPx))
self.idlePixmap = CONFIG.theme.getPixmap("status_idle", (iPx, iPx))
self.timeIcon.setPixmap(self.timePixmap)
-1
View File
@@ -1,7 +1,6 @@
"""
novelWriter Theme and Icons Classes
=====================================
Classes managing and caching themes and icons
File History:
Created: 2019-05-18 [0.1.3] GuiTheme
+52 -43
View File
@@ -113,8 +113,8 @@ class GuiMain(QMainWindow):
# ============
# Core Classes
self.mainTheme = GuiTheme()
self.theProject = NWProject(self)
CONFIG.setThemeInstance(GuiTheme())
self._project = NWProject(self)
# Core Settings
self.hasProject = False
@@ -135,7 +135,7 @@ class GuiMain(QMainWindow):
# =============
# Sizes
iPx = self.mainTheme.fontPixelSize
iPx = CONFIG.theme.fontPixelSize
mPx = CONFIG.pxInt(4)
hWd = CONFIG.pxInt(4)
@@ -238,7 +238,7 @@ class GuiMain(QMainWindow):
# Connect Signals
# ===============
self.theProject.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus)
self._project.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus)
self.viewsBar.viewChangeRequested.connect(self._changeView)
@@ -307,10 +307,10 @@ class GuiMain(QMainWindow):
# Cache Alert Pixmaps
pxSize = (2*iPx, 2*iPx)
self.alertPix: dict[nwAlert, QPixmap] = {
nwAlert.INFO: self.mainTheme.getPixmap("alert_info", pxSize),
nwAlert.WARN: self.mainTheme.getPixmap("alert_warn", pxSize),
nwAlert.ERROR: self.mainTheme.getPixmap("alert_error", pxSize),
nwAlert.ASK: self.mainTheme.getPixmap("alert_question", pxSize),
nwAlert.INFO: CONFIG.theme.getPixmap("alert_info", pxSize),
nwAlert.WARN: CONFIG.theme.getPixmap("alert_warn", pxSize),
nwAlert.ERROR: CONFIG.theme.getPixmap("alert_error", pxSize),
nwAlert.ASK: CONFIG.theme.getPixmap("alert_question", pxSize),
}
# Check that config loaded fine
@@ -382,6 +382,15 @@ class GuiMain(QMainWindow):
return
##
# Properties
##
@property
def project(self) -> NWProject:
"""The project instance."""
return self._project
##
# Project Actions
##
@@ -444,7 +453,7 @@ class GuiMain(QMainWindow):
saveOK = self.saveProject()
doBackup = False
if self.theProject.data.doBackup and CONFIG.backupOnClose:
if self._project.data.doBackup and CONFIG.backupOnClose:
doBackup = True
if CONFIG.askBeforeBackup:
msgYes = self.askQuestion(self.tr("Backup the current project?"))
@@ -452,7 +461,7 @@ class GuiMain(QMainWindow):
doBackup = False
if doBackup:
self.theProject.backupProject(False)
self._project.backupProject(False)
if saveOK:
self.closeDocument()
@@ -460,7 +469,7 @@ class GuiMain(QMainWindow):
self.outlineView.closeProjectTasks()
self.novelView.closeProjectTasks()
self.theProject.closeProject(self.idleTime)
self._project.closeProject(self.idleTime)
self.idleRefTime = time()
self.idleTime = 0.0
@@ -484,9 +493,9 @@ class GuiMain(QMainWindow):
self._changeView(nwView.PROJECT)
# Try to open the project
if not self.theProject.openProject(projFile):
if not self._project.openProject(projFile):
# The project open failed.
lockStatus = self.theProject.getLockStatus()
lockStatus = self._project.getLockStatus()
if lockStatus is None:
# The project is not locked, so failed for some other
# reason handled by the project class.
@@ -516,7 +525,7 @@ class GuiMain(QMainWindow):
lockDetails = ""
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
else:
return False
@@ -527,11 +536,11 @@ class GuiMain(QMainWindow):
self.idleTime = 0.0
# Update GUI
self._updateWindowTitle(self.theProject.data.name)
self._updateWindowTitle(self._project.data.name)
self.rebuildTrees()
self.docEditor.setDictionaries()
self.docEditor.toggleSpellCheck(self.theProject.data.spellCheck)
self.mainStatus.setRefTime(self.theProject.projOpened)
self.docEditor.toggleSpellCheck(self._project.data.spellCheck)
self.mainStatus.setRefTime(self._project.projOpened)
self.projView.openProjectTasks()
self.novelView.openProjectTasks()
self.outlineView.openProjectTasks()
@@ -539,9 +548,9 @@ class GuiMain(QMainWindow):
# Restore previously open documents, if any
# 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:
for nwItem in self.theProject.tree:
for nwItem in self._project.tree:
if nwItem and nwItem.isFileType():
lastEdited = nwItem.itemHandle
break
@@ -549,19 +558,19 @@ class GuiMain(QMainWindow):
if lastEdited is not None:
self.openDocument(lastEdited, doScroll=True)
lastViewed = self.theProject.data.getLastHandle("viewer")
lastViewed = self._project.data.getLastHandle("viewer")
if lastViewed is not None:
self.viewDocument(lastViewed)
# 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.rebuildIndex()
# Make sure the changed status is set to false on things opened
qApp.processEvents()
self.docEditor.setDocumentChanged(False)
self.theProject.setProjectChanged(False)
self._project.setProjectChanged(False)
logger.debug("Project load complete")
@@ -573,7 +582,7 @@ class GuiMain(QMainWindow):
logger.error("No project open")
return False
self.projView.saveProjectTasks()
self.theProject.saveProject(autoSave=autoSave)
self._project.saveProject(autoSave=autoSave)
return True
##
@@ -606,7 +615,7 @@ class GuiMain(QMainWindow):
logger.error("No project open")
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)
return False
@@ -620,7 +629,7 @@ class GuiMain(QMainWindow):
self.closeDocument(beforeOpen=True)
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.novelView.setActiveHandle(tHandle)
if changeFocus:
@@ -641,7 +650,7 @@ class GuiMain(QMainWindow):
nHandle = None # The next handle after tHandle
fHandle = None # The first file handle we encounter
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():
continue
if fHandle is None:
@@ -687,7 +696,7 @@ class GuiMain(QMainWindow):
tHandle = self.projView.getSelectedHandle()
if tHandle is None:
tHandle = self.theProject.data.getLastHandle("viewer")
tHandle = self._project.data.getLastHandle("viewer")
if tHandle is None:
logger.debug("No document to view, giving up")
@@ -806,7 +815,7 @@ class GuiMain(QMainWindow):
return False
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:
tLine = hItem.line
@@ -843,7 +852,7 @@ class GuiMain(QMainWindow):
tStart = time()
self.projView.saveProjectTasks()
self.theProject.index.rebuildIndex()
self._project.index.rebuildIndex()
self.projView.populateTree()
self.novelView.refreshTree()
@@ -912,7 +921,7 @@ class GuiMain(QMainWindow):
if dlgConf.updateTheme:
# We are doing this manually instead of connecting to
# qApp.paletteChanged since the processing order matters
self.mainTheme.loadTheme()
CONFIG.theme.loadTheme()
self.docEditor.updateTheme()
self.docViewer.updateTheme()
self.viewsBar.updateTheme()
@@ -923,7 +932,7 @@ class GuiMain(QMainWindow):
self.mainStatus.updateTheme()
if dlgConf.updateSyntax:
self.mainTheme.loadSyntax()
CONFIG.theme.loadSyntax()
self.docEditor.updateSyntaxColours()
self.docEditor.initEditor()
@@ -951,7 +960,7 @@ class GuiMain(QMainWindow):
if dlgProj.spellChanged:
self.docEditor.setDictionaries()
self.itemDetails.refreshDetails()
self._updateWindowTitle(self.theProject.data.name)
self._updateWindowTitle(self._project.data.name)
return True
@@ -1197,7 +1206,7 @@ class GuiMain(QMainWindow):
def closeDocEditor(self) -> None:
"""Close the document editor. This does not hide the editor."""
self.closeDocument()
self.theProject.data.setLastHandle(None, "editor")
self._project.data.setLastHandle(None, "editor")
return
def closeDocViewer(self, byUser: bool = True) -> bool:
@@ -1205,7 +1214,7 @@ class GuiMain(QMainWindow):
self.docViewer.clearViewer()
if byUser:
# 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
bPos = self.splitMain.sizes()
@@ -1391,7 +1400,7 @@ class GuiMain(QMainWindow):
"""Handle the index lookup of a tag and display an alert if the
tag cannot be found.
"""
tHandle, sTitle = self.theProject.index.getTagSource(tag)
tHandle, sTitle = self._project.index.getTagSource(tag)
if tHandle is None:
self.makeAlert(self.tr(
"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 mode == nwDocMode.EDIT:
tLine = None
hItem = self.theProject.index.getItemHeader(tHandle, sTitle)
hItem = self._project.index.getItemHeader(tHandle, sTitle)
if hItem is not None:
tLine = hItem.line
self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus)
@@ -1491,8 +1500,8 @@ class GuiMain(QMainWindow):
def _autoSaveProject(self) -> None:
"""Autosave of the project. This is a timer-activated slot."""
doSave = self.hasProject
doSave &= self.theProject.projChanged
doSave &= self.theProject.storage.isOpen()
doSave &= self._project.projChanged
doSave &= self._project.storage.isOpen()
if doSave:
logger.debug("Autosaving project")
self.saveProject(autoSave=True)
@@ -1512,14 +1521,14 @@ class GuiMain(QMainWindow):
if not self.hasProject:
self.mainStatus.setProjectStats(0, 0)
self.theProject.updateWordCounts()
self._project.updateWordCounts()
if CONFIG.incNotesWCount:
iTotal = sum(self.theProject.data.initCounts)
cTotal = sum(self.theProject.data.currCounts)
iTotal = sum(self._project.data.initCounts)
cTotal = sum(self._project.data.currCounts)
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
else:
iNovel, _ = self.theProject.data.initCounts
cNovel, _ = self.theProject.data.currCounts
iNovel, _ = self._project.data.initCounts
cNovel, _ = self._project.data.currCounts
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
return
+3 -4
View File
@@ -1,7 +1,6 @@
"""
novelWriter Lorem Ipsum Tool
==============================
Simple tool for inserting placeholder text in a document
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import random
import logging
@@ -49,8 +49,7 @@ class GuiLipsum(QDialog):
if CONFIG.osDarwin:
self.setWindowFlag(Qt.WindowType.Tool)
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = mainGui
self.setWindowTitle(self.tr("Insert Placeholder Text"))
@@ -61,7 +60,7 @@ class GuiLipsum(QDialog):
nPx = CONFIG.pxInt(64)
vSp = CONFIG.pxInt(4)
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.setSpacing(vSp)
+13 -15
View File
@@ -65,9 +65,7 @@ class GuiManuscriptBuild(QDialog):
logger.debug("Create: GuiManuscriptBuild")
self.setObjectName("GuiManuscriptBuild")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
self._parent = parent
self._build = build
@@ -76,14 +74,14 @@ class GuiManuscriptBuild(QDialog):
self.setMinimumWidth(CONFIG.pxInt(500))
self.setMinimumHeight(CONFIG.pxInt(300))
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
sp4 = CONFIG.pxInt(4)
sp8 = CONFIG.pxInt(8)
sp16 = CONFIG.pxInt(16)
wWin = CONFIG.pxInt(620)
hWin = CONFIG.pxInt(360)
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winHeight", hWin))
@@ -148,7 +146,7 @@ class GuiManuscriptBuild(QDialog):
# Build Path
self.lblPath = QLabel(self.tr("Path"))
self.buildPath = QLineEdit(self)
self.btnBrowse = QPushButton(self.mainTheme.getIcon("browse"), "")
self.btnBrowse = QPushButton(CONFIG.theme.getIcon("browse"), "")
self.pathBox = QHBoxLayout()
self.pathBox.addWidget(self.buildPath)
@@ -158,7 +156,7 @@ class GuiManuscriptBuild(QDialog):
# Build Name
self.lblName = QLabel(self.tr("File Name"))
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.nameBox = QHBoxLayout()
@@ -183,7 +181,7 @@ class GuiManuscriptBuild(QDialog):
self.buildBox.setVerticalSpacing(sp4)
# 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.addButton(self.btnBuild, QDialogButtonBox.ActionRole)
@@ -281,7 +279,7 @@ class GuiManuscriptBuild(QDialog):
@pyqtSlot()
def _doResetBuildName(self):
"""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._build.setLastBuildName(bName)
return
@@ -322,7 +320,7 @@ class GuiManuscriptBuild(QDialog):
):
return False
docBuild = NWBuildDocument(self.theProject, self._build)
docBuild = NWBuildDocument(self.mainGui.project, self._build)
docBuild.queueAll()
self.buildProgress.setMaximum(len(docBuild))
@@ -355,7 +353,7 @@ class GuiManuscriptBuild(QDialog):
fmtWidth = CONFIG.rpxInt(mainSplit[0])
sumWidth = CONFIG.rpxInt(mainSplit[1])
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth)
pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight)
pOptions.setValue("GuiManuscriptBuild", "fmtWidth", fmtWidth)
@@ -367,9 +365,9 @@ class GuiManuscriptBuild(QDialog):
def _populateContentList(self):
"""Build the content list."""
rootMap = {}
filtered = self._build.buildItemFilter(self.theProject)
filtered = self._build.buildItemFilter(self.mainGui.project)
self.listContent.clear()
for nwItem in self.theProject.tree:
for nwItem in self.mainGui.project.tree:
tHandle = nwItem.itemHandle
rHandle = nwItem.itemRoot
@@ -378,11 +376,11 @@ class GuiManuscriptBuild(QDialog):
if filtered.get(tHandle, (False, 0))[0]:
if rHandle not in rootMap:
rItem = self.theProject.tree[rHandle]
rItem = self.mainGui.project.tree[rHandle]
if isinstance(rItem, NWItem):
rootMap[rHandle] = rItem.itemName
itemIcon = self.mainTheme.getItemIcon(
itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass,
nwItem.itemLayout, nwItem.mainHeading
)
+20 -24
View File
@@ -72,22 +72,20 @@ class GuiManuscript(QDialog):
if CONFIG.osDarwin:
self.setWindowFlag(Qt.WindowType.Tool)
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
self._builds = BuildCollection(self.theProject)
self._builds = BuildCollection(self.mainGui.project)
self._buildMap: dict[str, QListWidgetItem] = {}
self.setWindowTitle(self.tr("Build Manuscript"))
self.setMinimumWidth(CONFIG.pxInt(600))
self.setMinimumHeight(CONFIG.pxInt(500))
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
wWin = CONFIG.pxInt(900)
hWin = CONFIG.pxInt(600)
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winHeight", hWin))
@@ -107,21 +105,21 @@ class GuiManuscript(QDialog):
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
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.setToolTip(self.tr("Add New Build"))
self.tbAdd.setStyleSheet(buttonStyle)
self.tbAdd.clicked.connect(self._createNewBuild)
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.setToolTip(self.tr("Delete Selected Build"))
self.tbDel.setStyleSheet(buttonStyle)
self.tbDel.clicked.connect(self._deleteSelectedBuild)
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.setToolTip(self.tr("Edit Selected Build"))
self.tbEdit.setStyleSheet(buttonStyle)
@@ -212,7 +210,7 @@ class GuiManuscript(QDialog):
self._updateBuildsList()
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():
try:
with open(cache, mode="r", encoding="utf-8") as fObj:
@@ -291,7 +289,7 @@ class GuiManuscript(QDialog):
if build is None:
return
docBuild = NWBuildDocument(self.theProject, build)
docBuild = NWBuildDocument(self.mainGui.project, build)
docBuild.queueAll()
self.docPreview.beginNewBuild(len(docBuild))
@@ -311,7 +309,7 @@ class GuiManuscript(QDialog):
self._updatePreview(result, build)
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:
with open(cache, mode="w+", encoding="utf-8") as outFile:
outFile.write(json.dumps(result, indent=2))
@@ -392,7 +390,7 @@ class GuiManuscript(QDialog):
optsWidth = CONFIG.rpxInt(mainSplit[0])
viewWidth = CONFIG.rpxInt(mainSplit[1])
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
pOptions.setValue("GuiManuscript", "winWidth", winWidth)
pOptions.setValue("GuiManuscript", "winHeight", winHeight)
pOptions.setValue("GuiManuscript", "optsWidth", optsWidth)
@@ -428,7 +426,7 @@ class GuiManuscript(QDialog):
for key, name in self._builds.builds():
bItem = QListWidgetItem()
bItem.setText(name)
bItem.setIcon(self.mainTheme.getIcon("export"))
bItem.setIcon(CONFIG.theme.getIcon("export"))
bItem.setData(self.D_KEY, key)
self.buildList.addItem(bItem)
self._buildMap[key] = bItem
@@ -451,9 +449,7 @@ class _PreviewWidget(QTextBrowser):
def __init__(self, mainGui: GuiMain):
super().__init__(parent=mainGui)
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
self._docTime = 0
self._buildName = ""
@@ -464,7 +460,7 @@ class _PreviewWidget(QTextBrowser):
dPalette.setColor(QPalette.Text, QColor(0, 0, 0))
self.setPalette(dPalette)
self.setMinimumWidth(40*self.mainGui.mainTheme.textNWidth)
self.setMinimumWidth(40*CONFIG.theme.textNWidth)
self.setTextFont(CONFIG.textFont, CONFIG.textSize)
self.setTabStopDistance(CONFIG.getTabWidth())
self.setOpenExternalLinks(False)
@@ -482,7 +478,7 @@ class _PreviewWidget(QTextBrowser):
aPalette.setColor(QPalette.Foreground, aPalette.toolTipText().color())
aFont = self.font()
aFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
aFont.setPointSizeF(0.9*CONFIG.theme.fontPointSize)
self.ageLabel = QLabel("", self)
self.ageLabel.setIndent(0)
@@ -490,7 +486,7 @@ class _PreviewWidget(QTextBrowser):
self.ageLabel.setPalette(aPalette)
self.ageLabel.setAutoFillBackground(True)
self.ageLabel.setAlignment(Qt.AlignCenter)
self.ageLabel.setFixedHeight(int(2.1*self.mainTheme.fontPixelSize))
self.ageLabel.setFixedHeight(int(2.1*CONFIG.theme.fontPixelSize))
# Progress
self.buildProgress = NProgressCircle(self, CONFIG.pxInt(160), CONFIG.pxInt(16))
@@ -526,12 +522,12 @@ class _PreviewWidget(QTextBrowser):
def setJustify(self, state: bool):
"""Enable/disable the justify text option."""
options = self.document().defaultTextOption()
pOptions = self.document().defaultTextOption()
if state:
options.setAlignment(Qt.AlignJustify)
pOptions.setAlignment(Qt.AlignJustify)
else:
options.setAlignment(Qt.AlignAbsolute)
self.document().setDefaultTextOption(options)
pOptions.setAlignment(Qt.AlignAbsolute)
self.document().setDefaultTextOption(pOptions)
return
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
from novelwriter.guimain import GuiMain
from novelwriter.gui.theme import GuiTheme
logger = logging.getLogger(__name__)
@@ -77,9 +76,7 @@ class GuiBuildSettings(QDialog):
if CONFIG.osDarwin:
self.setWindowFlag(Qt.WindowType.Tool)
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
self._build = build
@@ -91,7 +88,7 @@ class GuiBuildSettings(QDialog):
wWin = CONFIG.pxInt(750)
hWin = CONFIG.pxInt(550)
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winHeight", hWin))
@@ -103,7 +100,7 @@ class GuiBuildSettings(QDialog):
self.optSideBar = NPagedSideBar(self)
self.optSideBar.setMinimumWidth(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.addButton(self.tr("Selection"), self.OPT_FILTERS)
@@ -265,7 +262,7 @@ class GuiBuildSettings(QDialog):
treeWidth, filterWidth = self.optTabSelect.mainSplitSizes()
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
pOptions.setValue("GuiBuildSettings", "winWidth", winWidth)
pOptions.setValue("GuiBuildSettings", "winHeight", winHeight)
pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth)
@@ -306,18 +303,16 @@ class _FilterTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain)
self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self.theProject = buildMain.mainGui.theProject
self.mainGui = buildMain.mainGui
self._treeMap: dict[str, QTreeWidgetItem] = {}
self._build = build
self._statusFlags: dict[int, QIcon] = {
self.F_NONE: QIcon(),
self.F_FILTERED: self.mainTheme.getIcon("build_filtered"),
self.F_INCLUDED: self.mainTheme.getIcon("build_included"),
self.F_EXCLUDED: self.mainTheme.getIcon("build_excluded"),
self.F_FILTERED: CONFIG.theme.getIcon("build_filtered"),
self.F_INCLUDED: CONFIG.theme.getIcon("build_included"),
self.F_EXCLUDED: CONFIG.theme.getIcon("build_excluded"),
}
self._trIncluded = self.tr("Included in manuscript")
@@ -327,7 +322,7 @@ class _FilterTab(QWidget):
# ============
# Tree Settings
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
cMg = CONFIG.pxInt(6)
# Tree Widget
@@ -365,7 +360,7 @@ class _FilterTab(QWidget):
self.resetButton = QToolButton(self)
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.modeBox = QHBoxLayout()
@@ -384,7 +379,7 @@ class _FilterTab(QWidget):
# Assemble GUI
# ============
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
self.selectionBox = QVBoxLayout()
self.selectionBox.addWidget(self.optTree)
@@ -450,7 +445,7 @@ class _FilterTab(QWidget):
logger.debug("Building project tree")
self._treeMap = {}
self.optTree.clear()
for nwItem in self.theProject.getProjectItems():
for nwItem in self.mainGui.project.getProjectItems():
tHandle = nwItem.itemHandle
pHandle = nwItem.itemParent
@@ -466,7 +461,7 @@ class _FilterTab(QWidget):
continue
hLevel = nwItem.mainHeading
itemIcon = self.mainTheme.getItemIcon(
itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
)
@@ -480,7 +475,7 @@ class _FilterTab(QWidget):
trItem.setText(self.C_NAME, nwItem.itemName)
trItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
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)
@@ -504,19 +499,19 @@ class _FilterTab(QWidget):
self.filterOpt.clear()
self.filterOpt.addLabel(self._build.getLabel("filter"))
self.filterOpt.addItem(
self.mainTheme.getIcon("proj_scene"),
CONFIG.theme.getIcon("proj_scene"),
self._build.getLabel("filter.includeNovel"),
"doc:filter.includeNovel",
default=self._build.getBool("filter.includeNovel")
)
self.filterOpt.addItem(
self.mainTheme.getIcon("proj_note"),
CONFIG.theme.getIcon("proj_note"),
self._build.getLabel("filter.includeNotes"),
"doc:filter.includeNotes",
default=self._build.getBool("filter.includeNotes")
)
self.filterOpt.addItem(
self.mainTheme.getIcon("unchecked"),
CONFIG.theme.getIcon("unchecked"),
self._build.getLabel("filter.includeInactive"),
"doc:filter.includeInactive",
default=self._build.getBool("filter.includeInactive")
@@ -526,9 +521,9 @@ class _FilterTab(QWidget):
# Root Classes
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():
itemIcon = self.mainTheme.getItemIcon(
itemIcon = CONFIG.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout
)
self.filterOpt.addItem(
@@ -562,7 +557,7 @@ class _FilterTab(QWidget):
def _setTreeItemMode(self) -> None:
"""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():
allow, mode = filtered.get(tHandle, (False, FilterMode.UNKNOWN))
if mode == FilterMode.INCLUDED:
@@ -602,14 +597,12 @@ class _HeadingsTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain)
self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self.theProject = buildMain.mainGui.theProject
self.mainGui = buildMain.mainGui
self._build = build
self._editing = 0
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
vSp = CONFIG.pxInt(12)
bSp = CONFIG.pxInt(6)
@@ -623,7 +616,7 @@ class _HeadingsTab(QWidget):
self.fmtTitle = QLineEdit("")
self.fmtTitle.setReadOnly(True)
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))
wrapTitle = QHBoxLayout()
@@ -639,7 +632,7 @@ class _HeadingsTab(QWidget):
self.fmtChapter = QLineEdit("")
self.fmtChapter.setReadOnly(True)
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))
wrapChapter = QHBoxLayout()
@@ -655,7 +648,7 @@ class _HeadingsTab(QWidget):
self.fmtUnnumbered = QLineEdit("")
self.fmtUnnumbered.setReadOnly(True)
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))
wrapUnnumbered = QHBoxLayout()
@@ -672,7 +665,7 @@ class _HeadingsTab(QWidget):
self.fmtScene = QLineEdit("")
self.fmtScene.setReadOnly(True)
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.hdeScene = QLabel(self.tr("Hide"))
self.hdeScene.setToolTip(sceneHideTip)
@@ -699,7 +692,7 @@ class _HeadingsTab(QWidget):
self.fmtSection = QLineEdit("")
self.fmtSection.setReadOnly(True)
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.hdeSection = QLabel(self.tr("Hide"))
self.hdeSection.setToolTip(sectionHideTip)
@@ -729,7 +722,7 @@ class _HeadingsTab(QWidget):
self.editTextBox.setFixedHeight(5*iPx)
self.editTextBox.setEnabled(False)
self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document(), self.mainTheme)
self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document())
self.menuInsert = QMenu()
self.aInsTitle = self.menuInsert.addAction(self.tr("Title"))
@@ -872,12 +865,12 @@ class _HeadingsTab(QWidget):
class _HeadingSyntaxHighlighter(QSyntaxHighlighter):
def __init__(self, document: QTextDocument, mainTheme: GuiTheme) -> None:
def __init__(self, document: QTextDocument) -> None:
super().__init__(document)
self._fmtSymbol = QTextCharFormat()
self._fmtSymbol.setForeground(QColor(*mainTheme.colHead))
self._fmtSymbol.setForeground(QColor(*CONFIG.theme.colHead))
self._fmtFormat = QTextCharFormat()
self._fmtFormat.setForeground(QColor(*mainTheme.colEmph))
self._fmtFormat.setForeground(QColor(*CONFIG.theme.colEmph))
return
def highlightBlock(self, text: str) -> None:
@@ -901,12 +894,9 @@ class _ContentTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain)
self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self._build = build
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
# Left Form
# =========
@@ -973,16 +963,15 @@ class _FormatTab(QWidget):
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain)
self.buildMain = buildMain
self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self.buildMain = buildMain
self.mainGui = buildMain.mainGui
self._build = build
self._unitScale = 1.0
iPx = self.mainTheme.baseIconSize
spW = 6*self.mainTheme.textNWidth
dbW = 8*self.mainTheme.textNWidth
iPx = CONFIG.theme.baseIconSize
spW = 6*CONFIG.theme.textNWidth
dbW = 8*CONFIG.theme.textNWidth
# Text Format Form
# ================
@@ -1003,7 +992,7 @@ class _FormatTab(QWidget):
self.textFont = QLineEdit()
self.textFont.setReadOnly(True)
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.formFormat.addRow(
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:
super().__init__(parent=buildMain)
self.mainGui = buildMain.mainGui
self.mainTheme = buildMain.mainGui.mainTheme
self._build = build
iPx = self.mainTheme.baseIconSize
iPx = CONFIG.theme.baseIconSize
# Left Form
# =========
+5 -18
View File
@@ -1,7 +1,6 @@
"""
novelWriter GUI New Project Wizard
====================================
GUI classes for the new project wizard dialog
File History:
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
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import os
import logging
@@ -53,10 +53,9 @@ class GuiProjectWizard(QWizard):
logger.debug("Create: GuiProjectWizard")
self.setObjectName("GuiProjectWizard")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.mainGui = mainGui
self.sideImage = self.mainTheme.loadDecoration(
self.sideImage = CONFIG.theme.loadDecoration(
"wiz-back", None, CONFIG.pxInt(370)
)
self.setWizardStyle(QWizard.ModernStyle)
@@ -92,9 +91,6 @@ class ProjWizardIntroPage(QWizardPage):
def __init__(self, theWizard):
super().__init__()
self.theWizard = theWizard
self.mainTheme = theWizard.mainTheme
self.setTitle(self.tr("Create New Project"))
self.theText = QLabel(self.tr(
"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"
))
lblFont = self.imgCredit.font()
lblFont.setPointSizeF(0.6*self.mainTheme.fontPointSize)
lblFont.setPointSizeF(0.6*CONFIG.theme.fontPointSize)
self.imgCredit.setFont(lblFont)
xW = CONFIG.pxInt(300)
@@ -160,9 +156,6 @@ class ProjWizardFolderPage(QWizardPage):
def __init__(self, theWizard):
super().__init__()
self.theWizard = theWizard
self.mainTheme = theWizard.mainTheme
self.setTitle(self.tr("Select Project Folder"))
self.theText = QLabel(self.tr(
"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.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.errLabel = QLabel("")
@@ -257,8 +250,6 @@ class ProjWizardPopulatePage(QWizardPage):
def __init__(self, theWizard):
super().__init__()
self.theWizard = theWizard
self.setTitle(self.tr("Populate Project"))
self.theText = QLabel(self.tr(
"Choose how to pre-fill the project. Either with a minimal set of "
@@ -312,8 +303,6 @@ class ProjWizardCustomPage(QWizardPage):
def __init__(self, theWizard):
super().__init__()
self.theWizard = theWizard
self.setTitle(self.tr("Custom Project Options"))
self.theText = QLabel(self.tr(
"Select which additional elements to populate the project with. "
@@ -412,8 +401,6 @@ class ProjWizardFinalPage(QWizardPage):
def __init__(self, theWizard):
super().__init__()
self.theWizard = theWizard
self.setTitle(self.tr("Summary"))
self.theText = QLabel("")
self.theText.setWordWrap(True)
+17 -19
View File
@@ -72,16 +72,14 @@ class GuiWritingStats(QDialog):
if CONFIG.osDarwin:
self.setWindowFlag(Qt.WindowType.Tool)
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self.mainGui = mainGui
self.logData = []
self.filterData = []
self.timeFilter = 0.0
self.wordOffset = 0
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
self.setWindowTitle(self.tr("Writing Statistics"))
self.setMinimumWidth(CONFIG.pxInt(420))
@@ -134,7 +132,7 @@ class GuiWritingStats(QDialog):
self.listBox.setSortingEnabled(True)
# 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.barImage = QPixmap(self.barHeight, self.barHeight)
self.barImage.fill(self.palette().highlight().color())
@@ -145,27 +143,27 @@ class GuiWritingStats(QDialog):
self.infoBox.setLayout(self.infoForm)
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.labelIdleT = QLabel(formatTime(0))
self.labelIdleT.setFont(self.mainTheme.guiFontFixed)
self.labelIdleT.setFont(CONFIG.theme.guiFontFixed)
self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
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.novelWords = QLabel("0")
self.novelWords.setFont(self.mainTheme.guiFontFixed)
self.novelWords.setFont(CONFIG.theme.guiFontFixed)
self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.notesWords = QLabel("0")
self.notesWords.setFont(self.mainTheme.guiFontFixed)
self.notesWords.setFont(CONFIG.theme.guiFontFixed)
self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.totalWords = QLabel("0")
self.totalWords.setFont(self.mainTheme.guiFontFixed)
self.totalWords.setFont(CONFIG.theme.guiFontFixed)
self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
lblTTime = QLabel(self.tr("Total Time:"))
@@ -192,7 +190,7 @@ class GuiWritingStats(QDialog):
self.infoForm.setRowStretch(6, 1)
# Filter Options
sPx = self.mainTheme.baseIconSize
sPx = CONFIG.theme.baseIconSize
self.filterBox = QGroupBox(self.tr("Filters"), self)
self.filterForm = QGridLayout(self)
@@ -335,7 +333,7 @@ class GuiWritingStats(QDialog):
showIdleTime = self.showIdleTime.isChecked()
histMax = self.histMax.value()
pOptions = self.theProject.options
pOptions = self.mainGui.project.options
pOptions.setValue("GuiWritingStats", "winWidth", winWidth)
pOptions.setValue("GuiWritingStats", "winHeight", winHeight)
pOptions.setValue("GuiWritingStats", "widthCol0", widthCol0)
@@ -443,7 +441,7 @@ class GuiWritingStats(QDialog):
ttTime = 0
ttIdle = 0
for record in self.theProject.session.iterRecords():
for record in self.mainGui.project.session.iterRecords():
rType = record.get("type")
if rType == "initial":
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_BAR, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed)
newItem.setFont(self.C_LENGTH, self.mainTheme.guiFontFixed)
newItem.setFont(self.C_COUNT, self.mainTheme.guiFontFixed)
newItem.setFont(self.C_TIME, CONFIG.theme.guiFontFixed)
newItem.setFont(self.C_LENGTH, CONFIG.theme.guiFontFixed)
newItem.setFont(self.C_COUNT, CONFIG.theme.guiFontFixed)
if showIdleTime:
newItem.setFont(self.C_IDLE, self.mainTheme.guiFontFixed)
newItem.setFont(self.C_IDLE, CONFIG.theme.guiFontFixed)
else:
newItem.setFont(self.C_IDLE, self.mainTheme.guiFont)
newItem.setFont(self.C_IDLE, CONFIG.theme.guiFont)
self.listBox.addTopLevelItem(newItem)
self.timeFilter += sDiff
+2 -2
View File
@@ -1,6 +1,6 @@
<?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">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1514" autoCount="237" editTime="75228">
<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="1517" autoCount="237" editTime="75241">
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
+6 -1
View File
@@ -31,8 +31,9 @@ class MockGuiMain(QObject):
def __init__(self):
super().__init__()
self._project = None
self.hasProject = True
self.theProject = None
self.mainStatus = MockStatusBar()
self.projPath = ""
@@ -43,6 +44,10 @@ class MockGuiMain(QObject):
return
@property
def project(self):
return self._project
def postLaunchTasks(self, cmdOpen):
return
+2 -2
View File
@@ -1,5 +1,5 @@
[Meta]
timestamp = 2023-08-02 14:53:36
timestamp = 2023-08-08 19:01:25
[Main]
theme = default
@@ -10,7 +10,7 @@ localisation = en_GB
hidevscroll = False
hidehscroll = False
lastnotes = 0x0
lastpath = /home/vkbo
lastpath =
[Sizes]
mainwindow = 1200, 650
+1 -9
View File
@@ -111,7 +111,7 @@ def testBaseConfig_InitLoadSave(monkeypatch, fncPath, tstPaths):
# Check that we have a default file
copyfile(confFile, testFile)
ignore = ("timestamp", "lastnotes", "localisation", "lastpath")
ignore = ("timestamp", "lastnotes", "localisation", "lastpath", "backuppath")
assert cmpFiles(testFile, compFile, ignoreStart=ignore)
tstConf.errorText() # This clears the error cache
@@ -366,14 +366,6 @@ def testBaseConfig_Internal(monkeypatch, fncPath):
# Function _packList
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
# (Assumes enchant package exists and is importable)
tstConf._checkOptionalPackages()
-4
View File
@@ -590,10 +590,6 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tstPaths):
# Invalid Settings
# ================
# Invalid path
CONFIG._backupPath = None
assert theProject.backupProject(doNotify=False) is False
# Missing project name
CONFIG._backupPath = tstPaths.tmpDir
theProject.data.setName("")
+1 -1
View File
@@ -344,7 +344,7 @@ def testCoreStatus_PackUnpack(mockRnd):
# Unpack
theStatus = NWStatus(NWStatus.STATUS)
assert theStatus.unpack({
theStatus.unpack({
statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
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):
"""Test the novelWriter about dialogs."""
# NW About
nwGUI.mainTheme.themeName = "A Theme"
nwGUI.mainTheme.themeAuthor = "An Author"
assert nwGUI.showAboutNWDialog(showNotes=True) is True
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
buildTestProject(nwGUI, projPath)
theProject = nwGUI.theProject
theProject = nwGUI.project
projTree = nwGUI.projView.projTree
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.chapCountVal.text() == f"{3: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)
+4 -4
View File
@@ -51,7 +51,7 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
# Pretend we have a project
nwGUI.hasProject = True
nwGUI.theProject.data.setSpellLang("en")
nwGUI.project.data.setSpellLang("en")
# Get the dialog object
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
@@ -95,7 +95,7 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockR
CONFIG.setBackupPath(fncPath)
# Set some values
theProject = nwGUI.theProject
theProject = nwGUI.project
theProject.data.setSpellLang("en")
theProject.data.setAuthor("Jane Smith")
theProject.data.setAutoReplace({"A": "B", "C": "D"})
@@ -160,7 +160,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
CONFIG.setBackupPath(fncPath)
# Set some values
theProject = nwGUI.theProject
theProject = nwGUI.project
theProject.tree[C.hTitlePage].setStatus(C.sFinished)
theProject.tree[C.hChapterDoc].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)
# Set some values
theProject = nwGUI.theProject
theProject = nwGUI.project
theProject.data.setAutoReplace({
"A": "B", "C": "D"
})
+1 -1
View File
@@ -55,7 +55,7 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, projPath):
assert wList.listBox.count() == 0
# Add words
userDict = UserDictionary(nwGUI.theProject)
userDict = UserDictionary(nwGUI.project)
userDict.add("word_a")
userDict.add("word_c")
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
# 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:])
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
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(10) is True
assert nwGUI.docEditor.getCursorPosition() == 10
assert nwGUI.theProject.tree[C.hSceneDoc].cursorPos != 10
assert nwGUI.project.tree[C.hSceneDoc].cursorPos != 10
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(3) is True
@@ -1067,7 +1067,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# Create Character
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.docEditor.replaceText(theText) 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)"
# Open a document and populate it
nwGUI.theProject.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]._initCount = 0 # Clear item's count
nwGUI.project.tree[C.hSceneDoc]._wordCount = 0 # Clear item's count
assert nwGUI.openDocument(C.hSceneDoc) is True
theText = "\n\n".join(ipsumText)
@@ -1170,9 +1170,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m
nwGUI.docEditor.wCounterDoc.run()
# nwGUI.docEditor._updateDocCounts(cC, wC, pC)
assert nwGUI.theProject.tree[C.hSceneDoc]._charCount == cC
assert nwGUI.theProject.tree[C.hSceneDoc]._wordCount == wC
assert nwGUI.theProject.tree[C.hSceneDoc]._paraCount == pC
assert nwGUI.project.tree[C.hSceneDoc]._charCount == cC
assert nwGUI.project.tree[C.hSceneDoc]._wordCount == wC
assert nwGUI.project.tree[C.hSceneDoc]._paraCount == pC
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})"
# Select all text
+3 -3
View File
@@ -40,8 +40,8 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
# Rebuild the index
nwGUI.mainMenu.aRebuildIndex.activate(QAction.Trigger)
assert nwGUI.theProject.index._tagsIndex._tags != {}
assert nwGUI.theProject.index._itemIndex._items != {}
assert nwGUI.project.index._tagsIndex._tags != {}
assert nwGUI.project.index._itemIndex._items != {}
# Select a document in the project tree
nwGUI.projView.setSelectedHandle("88243afbe5ed8")
@@ -128,7 +128,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
nwGUI.docViewer.reloadText()
# Change document title
nwItem = nwGUI.theProject.tree["4c4f28287af27"]
nwItem = nwGUI.project.tree["4c4f28287af27"]
nwItem.setName("Test Title")
assert nwItem.itemName == "Test Title"
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.closeProject()
assert len(nwGUI.theProject.tree) == 0
assert len(nwGUI.theProject.tree._treeOrder) == 0
assert len(nwGUI.theProject.tree._treeRoots) == 0
assert nwGUI.theProject.tree.trashRoot() is None
assert nwGUI.theProject.data.name == ""
assert nwGUI.theProject.data.title == ""
assert nwGUI.theProject.data.author == ""
assert nwGUI.theProject.data.spellCheck is False
assert len(nwGUI.project.tree) == 0
assert len(nwGUI.project.tree._treeOrder) == 0
assert len(nwGUI.project.tree._treeRoots) == 0
assert nwGUI.project.tree.trashRoot() is None
assert nwGUI.project.data.name == ""
assert nwGUI.project.data.title == ""
assert nwGUI.project.data.author == ""
assert nwGUI.project.data.spellCheck is False
# Check the files
projFile = projPath / "nwProject.nwx"
@@ -222,14 +222,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
assert nwGUI.openProject(projPath)
# Check that we loaded the data
assert len(nwGUI.theProject.tree) == 8
assert len(nwGUI.theProject.tree._treeOrder) == 8
assert len(nwGUI.theProject.tree._treeRoots) == 4
assert nwGUI.theProject.tree.trashRoot() is None
assert nwGUI.theProject.data.name == "New Project"
assert nwGUI.theProject.data.title == "New Novel"
assert nwGUI.theProject.data.author == "Jane Doe"
assert nwGUI.theProject.data.spellCheck is False
assert len(nwGUI.project.tree) == 8
assert len(nwGUI.project.tree._treeOrder) == 8
assert len(nwGUI.project.tree._treeRoots) == 4
assert nwGUI.project.tree.trashRoot() is None
assert nwGUI.project.data.name == "New Project"
assert nwGUI.project.data.title == "New Novel"
assert nwGUI.project.data.author == "Jane Doe"
assert nwGUI.project.data.spellCheck is False
# Check that tree items have been created
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.newTreeItem(nwItemType.FILE)
contentPath = nwGUI.theProject.storage.contentPath
contentPath = nwGUI.project.storage.contentPath
assert isinstance(contentPath, Path)
(contentPath / "0000000000010.nwd").write_text(
+3 -3
View File
@@ -71,7 +71,7 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, projPath):
# Option State
# ============
pOptions = nwGUI.theProject.options
pOptions = nwGUI.project.options
colNames = [h.name for h in nwOutline]
colItems = [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
# Add a second novel folder
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
newHandle = nwGUI.project.newRoot(nwItemClass.NOVEL)
nwGUI.projView.projTree.revealNewTreeItem(newHandle)
# Check new values in dropdown list
@@ -198,7 +198,7 @@ def testGuiOutline_Content(qtbot, nwGUI, prjLipsum):
("Section 4", 4),
]
for dTitle, hLevel in docList:
aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
aHandle = nwGUI.project.newFile(dTitle, newHandle)
hHash = "#"*hLevel
writeFile(prjLipsum / "content" / f"{aHandle}.nwd", f"{hHash} {dTitle}\n\n")
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
projTree = nwGUI.projView.projTree
theProject = nwGUI.theProject
theProject = nwGUI.project
# Try to add item with no project
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
@@ -260,19 +260,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# ===========
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
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
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
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
# qtbot.stop()
@@ -348,7 +348,7 @@ def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, projPat
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010"
]
trashHandle = nwGUI.theProject.tree.trashRoot()
trashHandle = nwGUI.project.tree.trashRoot()
assert projTree.getTreeFromHandle(trashHandle) == [
trashHandle, "0000000000012", "0000000000011"
]
@@ -368,7 +368,7 @@ def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, projPath,
"""Test moving items to Trash."""
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject
theProject = nwGUI.project
projTree = nwGUI.projView.projTree
# Create a project
@@ -420,7 +420,7 @@ def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, pro
"""Test permanently deleting items."""
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject
theProject = nwGUI.project
projTree = nwGUI.projView.projTree
# Create a project
@@ -471,7 +471,7 @@ def testGuiProjTree_EmptyTrash(qtbot, caplog, monkeypatch, nwGUI, projPath, mock
"""Test emptying Trash."""
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject
theProject = nwGUI.project
projTree = nwGUI.projView.projTree
# No project open
@@ -541,16 +541,16 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
projTree.setExpandedFromHandle(None, True)
projTree._addTrashRoot()
hTrashRoot = projTree.theProject.tree.trashRoot()
hTrashRoot = nwGUI.project.tree.trashRoot()
projTree.setSelectedHandle(C.hCharRoot)
projTree.newTreeItem(nwItemType.FILE)
projTree.setSelectedHandle(C.hNovelRoot)
projTree.newTreeItem(nwItemType.FILE, isNote=True)
nwGUI.theProject.newFile("SubNote", hNovelNote)
nwGUI.project.newFile("SubNote", hNovelNote)
projTree.revealNewTreeItem(hSubNote)
assert nwGUI.theProject.tree[hSubNote].itemParent == hNovelNote
assert nwGUI.project.tree[hSubNote].itemParent == hNovelNote
def itemPos(tHandle):
return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center()
@@ -578,7 +578,7 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# Direct Edit Functions
# =====================
# Trigger the dedicated functions the menu entries connect to
nwItem = projTree.theProject.tree[hNovelNote]
nwItem = nwGUI.project.tree[hNovelNote]
# Toggle active flag
assert nwItem.isActive is True
@@ -619,17 +619,17 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
with monkeypatch.context() as mp:
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No)
projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT)
assert nwGUI.theProject.tree[hNewFolderOne].isFolderType()
assert nwGUI.project.tree[hNewFolderOne].isFolderType()
# Convert the first folder to a document
projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT)
assert nwGUI.theProject.tree[hNewFolderOne].isFileType()
assert nwGUI.theProject.tree[hNewFolderOne].isDocumentLayout()
assert nwGUI.project.tree[hNewFolderOne].isFileType()
assert nwGUI.project.tree[hNewFolderOne].isDocumentLayout()
# Convert the second folder to a note
projTree._covertFolderToFile(hNewFolderTwo, nwItemLayout.NOTE)
assert nwGUI.theProject.tree[hNewFolderTwo].isFileType()
assert nwGUI.theProject.tree[hNewFolderTwo].isNoteLayout()
assert nwGUI.project.tree[hNewFolderTwo].isFileType()
assert nwGUI.project.tree[hNewFolderTwo].isNoteLayout()
# qtbot.stop()
@@ -649,7 +649,7 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
# Create a project
buildTestProject(nwGUI, projPath)
theProject = nwGUI.theProject
theProject = nwGUI.project
projTree = nwGUI.projView.projTree
mergedDoc1 = "0000000000014"
@@ -751,7 +751,7 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
# Create a project
buildTestProject(nwGUI, projPath)
theProject = nwGUI.theProject
theProject = nwGUI.project
projTree = nwGUI.projView.projTree
docText = (
@@ -852,7 +852,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
"""Test the duplicate items function."""
# Create a project
buildTestProject(nwGUI, projPath)
assert len(nwGUI.theProject.tree) == 8
assert len(nwGUI.project.tree) == 8
projTree = nwGUI.projView.projTree
projTree._getTreeItem(C.hNovelRoot).setExpanded(True) # type: ignore
@@ -860,28 +860,28 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
# Nothing to do
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
with monkeypatch.context() as mp:
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No)
assert projTree._duplicateFromHandle(C.hTitlePage) is False
assert len(nwGUI.theProject.tree) == 8
assert len(nwGUI.project.tree) == 8
# Duplicate title page
assert projTree._duplicateFromHandle(C.hTitlePage) is True
assert len(nwGUI.theProject.tree) == 9
assert len(nwGUI.project.tree) == 9
# Duplicate folder
assert projTree._duplicateFromHandle(C.hChapterDir) is True
assert len(nwGUI.theProject.tree) == 12
assert len(nwGUI.project.tree) == 12
# Duplicate novel root
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
assert nwGUI.theProject.tree._treeOrder == [
assert nwGUI.project.tree._treeOrder == [
C.hNovelRoot, C.hTitlePage, "0000000000010", C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000011", "0000000000012", "0000000000013", "0000000000014", "0000000000015",
"0000000000016", "0000000000017", "0000000000018", "0000000000019", "000000000001a",
@@ -889,7 +889,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
]
# Make the duplicator stop early
content = nwGUI.theProject.storage.contentPath
content = nwGUI.project.storage.contentPath
assert isinstance(content, Path)
(content / "000000000001e.nwd").touch()
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
# next handle is already a file
assert projTree._duplicateFromHandle(C.hChapterDir) is True
assert len(nwGUI.theProject.tree) == 22
assert len(nwGUI.project.tree) == 22
# qtbot.stop()
@@ -938,13 +938,13 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd)
assert projTree.revealNewTreeItem(C.hInvalid) is False
# Try to add an orphaned file to the tree
nHandle = nwGUI.theProject.newFile("Test", C.hNovelRoot)
nwGUI.theProject.tree[nHandle].setParent(None) # type: ignore
nHandle = nwGUI.project.newFile("Test", C.hNovelRoot)
nwGUI.project.tree[nHandle].setParent(None) # type: ignore
assert projTree.revealNewTreeItem(nHandle) is False
# Try to add an item with unknown parent to the tree
nHandle = nwGUI.theProject.newFile("Test", C.hNovelRoot)
nwGUI.theProject.tree[nHandle].setParent(C.hInvalid) # type: ignore
nHandle = nwGUI.project.newFile("Test", C.hNovelRoot)
nwGUI.project.tree[nHandle].setParent(C.hInvalid) # type: ignore
assert projTree.revealNewTreeItem(nHandle) is False
# Method: undoLastMove
+3 -3
View File
@@ -25,7 +25,7 @@ import pytest
from tools import C, buildTestProject
from novelwriter import CONFIG
from novelwriter.gui.statusbar import StatusLED
from novelwriter.extensions.statusled import StatusLED
@pytest.mark.gui
@@ -33,8 +33,8 @@ def testGuiStatusBar_Main(qtbot, nwGUI, projPath, mockRnd):
"""Test the the various features of the status bar.
"""
buildTestProject(nwGUI, projPath)
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
newDoc = nwGUI.theProject.storage.getDocument(cHandle)
cHandle = nwGUI.project.newFile("A Note", C.hCharRoot)
newDoc = nwGUI.project.storage.getDocument(cHandle)
newDoc.writeDocument("# A Note\n\n")
nwGUI.projView.projTree.revealNewTreeItem(cHandle)
nwGUI.rebuildIndex(beQuiet=True)
+7 -11
View File
@@ -33,14 +33,12 @@ from PyQt5.QtWidgets import QApplication
from novelwriter import CONFIG
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.constants import nwLabels
from novelwriter.gui.theme import GuiIcons, GuiTheme
@pytest.mark.gui
def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
"""Test the theme class init.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
"""Test the theme class init."""
mainTheme = CONFIG.theme
# Methods
# =======
@@ -123,7 +121,7 @@ def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
@pytest.mark.gui
def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI):
"""Test the theme part of the class."""
mainTheme: GuiTheme = nwGUI.mainTheme
mainTheme = CONFIG.theme
# List Themes
# ===========
@@ -200,9 +198,8 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI):
@pytest.mark.gui
def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI):
"""Test the syntax part of the class.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
"""Test the syntax part of the class."""
mainTheme = CONFIG.theme
# List Themes
# ===========
@@ -266,9 +263,8 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI):
@pytest.mark.gui
def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, tstPaths):
"""Test the icon cache class.
"""
iconCache: GuiIcons = nwGUI.mainTheme.iconCache
"""Test the icon cache class."""
iconCache = CONFIG.theme.iconCache
# 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."""
buildTestProject(nwGUI, 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* * *"
manus = GuiManuscript(nwGUI)
@@ -159,7 +159,7 @@ def testManuscript_Features(monkeypatch, qtbot: QtBot, nwGUI: GuiMain, projPath:
manus.show()
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)
build = manus._getSelectedBuild()
assert isinstance(build, BuildSettings)
+13 -13
View File
@@ -128,11 +128,11 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
"worldRoot": 9,
}
hPlotDoc = nwGUI.theProject.newFile("Main Plot", C.hPlotRoot)
hCharDoc = nwGUI.theProject.newFile("Jane Doe", C.hCharRoot)
hPlotDoc = nwGUI.project.newFile("Main Plot", C.hPlotRoot)
hCharDoc = nwGUI.project.newFile("Jane Doe", C.hCharRoot)
nwGUI.projView.projTree.revealNewTreeItem(hPlotDoc)
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
bSettings = GuiBuildSettings(nwGUI, build)
@@ -167,7 +167,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
# Switch off novel docs
filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(False)
assert build.buildItemFilter(nwGUI.theProject) == {
assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -182,7 +182,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
# Switch on note docs
filterTab.filterOpt._widgets[switchMap["incNotes"]].setChecked(True)
assert build.buildItemFilter(nwGUI.theProject) == {
assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -197,7 +197,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
# Switch on inactive docs
filterTab.filterOpt._widgets[switchMap["incInactive"]].setChecked(True)
assert build.buildItemFilter(nwGUI.theProject) == {
assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED),
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.hSceneDoc].setSelected(True)
filterTab.includedButton.click()
assert build.buildItemFilter(nwGUI.theProject) == {
assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED),
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[hCharDoc].setSelected(True) # type: ignore
filterTab.excludedButton.click()
assert build.buildItemFilter(nwGUI.theProject) == {
assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -247,7 +247,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
# Switch on novel docs
filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(True)
assert build.buildItemFilter(nwGUI.theProject) == {
assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED), # Now enabled
C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -264,7 +264,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
filterTab.optTree.clearSelection()
filterTab._treeMap[C.hNovelRoot].setSelected(True)
filterTab.resetButton.click()
assert build.buildItemFilter(nwGUI.theProject) == {
assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED),
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[hCharDoc].setSelected(True) # type: ignore
filterTab.resetButton.click()
assert build.buildItemFilter(nwGUI.theProject) == {
assert build.buildItemFilter(nwGUI.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED),
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.hPlotRoot, hPlotDoc, C.hCharRoot, hCharDoc,
]
nwGUI.theProject.tree[hCharDoc].setRoot(None) # type: ignore
nwGUI.theProject.tree[hPlotDoc].setParent(None) # type: ignore
nwGUI.project.tree[hCharDoc].setRoot(None) # type: ignore
nwGUI.project.tree[hPlotDoc].setParent(None) # type: ignore
filterTab._populateTree()
assert list(filterTab._treeMap.keys()) == [
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
buildTestProject(nwGUI, projPath)
project = nwGUI.theProject
project = nwGUI.project
qtbot.wait(100)
assert nwGUI.saveProject()
+36 -36
View File
@@ -154,59 +154,59 @@ def cleanProject(path: str | Path):
return
def buildTestProject(theObject, projPath):
"""Build a standard test project in projPath using theProject
def buildTestProject(obj, projPath):
"""Build a standard test project in projPath using the project
object as the parent.
"""
from novelwriter.enum import nwItemClass
from novelwriter.core.project import NWProject
if isinstance(theObject, NWProject):
theGUI = None
theProject = theObject
if isinstance(obj, NWProject):
nwGUI = None
project = obj
else:
theGUI = theObject
theProject = theObject.theProject
nwGUI = obj
project = obj.project
theProject.clearProject()
theProject.storage.openProjectInPlace(projPath)
theProject.setDefaultStatusImport()
project.clearProject()
project.storage.openProjectInPlace(projPath)
project.setDefaultStatusImport()
theProject.data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
theProject.data.setName("New Project")
theProject.data.setTitle("New Novel")
theProject.data.setAuthor("Jane Doe")
project.data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
project.data.setName("New Project")
project.data.setTitle("New Novel")
project.data.setAuthor("Jane Doe")
# Creating a minimal project with a few root folders and a
# single chapter folder with a single file.
xHandle = {}
xHandle[1] = theProject.newRoot(nwItemClass.NOVEL, "Novel")
xHandle[2] = theProject.newRoot(nwItemClass.PLOT, "Plot")
xHandle[3] = theProject.newRoot(nwItemClass.CHARACTER, "Characters")
xHandle[4] = theProject.newRoot(nwItemClass.WORLD, "World")
xHandle[5] = theProject.newFile("Title Page", xHandle[1])
xHandle[6] = theProject.newFolder("New Chapter", xHandle[1])
xHandle[7] = theProject.newFile("New Chapter", xHandle[6])
xHandle[8] = theProject.newFile("New Scene", xHandle[6])
xHandle[1] = project.newRoot(nwItemClass.NOVEL, "Novel")
xHandle[2] = project.newRoot(nwItemClass.PLOT, "Plot")
xHandle[3] = project.newRoot(nwItemClass.CHARACTER, "Characters")
xHandle[4] = project.newRoot(nwItemClass.WORLD, "World")
xHandle[5] = project.newFile("Title Page", xHandle[1])
xHandle[6] = project.newFolder("New Chapter", xHandle[1])
xHandle[7] = project.newFile("New Chapter", 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")
theProject.index.reIndexHandle(xHandle[5])
project.index.reIndexHandle(xHandle[5])
aDoc = theProject.storage.getDocument(xHandle[7])
aDoc.writeDocument("## %s\n\n" % theProject.tr("New Chapter"))
theProject.index.reIndexHandle(xHandle[7])
aDoc = project.storage.getDocument(xHandle[7])
aDoc.writeDocument("## %s\n\n" % project.tr("New Chapter"))
project.index.reIndexHandle(xHandle[7])
aDoc = theProject.storage.getDocument(xHandle[8])
aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene"))
theProject.index.reIndexHandle(xHandle[8])
aDoc = project.storage.getDocument(xHandle[8])
aDoc.writeDocument("### %s\n\n" % project.tr("New Scene"))
project.index.reIndexHandle(xHandle[8])
theProject.session.startSession()
theProject.setProjectChanged(True)
theProject.saveProject(autoSave=True)
project.session.startSession()
project.setProjectChanged(True)
project.saveProject(autoSave=True)
if theGUI is not None:
theGUI.hasProject = True
theGUI.rebuildTrees()
if nwGUI is not None:
nwGUI.hasProject = True
nwGUI.rebuildTrees()
return