@@ -133,6 +133,11 @@ The different notes can be assigned tags, which the novel files can refer back t
|
|||||||
Currently, this information can be used to display a Timeline View of the story, showing where each scene connects to the plot, and which characters, etc. occur in them.
|
Currently, this information can be used to display a Timeline View of the story, showing where each scene connects to the plot, and which characters, etc. occur in them.
|
||||||
Further features using this meta data will be added in the future.
|
Further features using this meta data will be added in the future.
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
|
||||||
|
If you ant to contribute to novelWriter, please follow the coding convention laid out in the [Style Guide](docs/markdown/style.md).
|
||||||
|
They broadly follow Python PEP8, but there are a few modifications.
|
||||||
|
|
||||||
## Screenshot
|
## Screenshot
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Code Style Guide
|
||||||
|
|
||||||
|
The source code of novelWriter broadly follows the style guide [PEP8](https://www.python.org/dev/peps/pep-0008/), but with a few modifications and exceptions.
|
||||||
|
|
||||||
|
### Source Code Exceptions
|
||||||
|
|
||||||
|
* Methods are camelCase, not underscore based.
|
||||||
|
The reason is partially because of the maintainers personal preference, and partially because that is what Qt5 and PyQt5 uses.
|
||||||
|
The maintainer generally, across multiple programming languages, uses underscores for defining namespaces.
|
||||||
|
* The maximum length of a code line is 99 characters, not 79.
|
||||||
|
The reason for this is that novelWriter is almost entirely made up of classes, meaning nearly all lines of code already have 8 leading spaces.
|
||||||
|
A 79 character limitation is too strict, and causes too many wrapped lines.
|
||||||
|
99 characters is suitable for GitHub diff readability, and therefore the preferred limit.
|
||||||
|
It is also permitted under PEP8 as the maximum.
|
||||||
|
Comments and docstrings should comply with the 72 character limit.
|
||||||
|
* Aligning code with additional spaces is acceptable in those cases where it improves readability.
|
||||||
|
Otherwise, the PEP8 standard should be applied.
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
The documentation does not adhere to the 80 character limit either.
|
||||||
|
The standard used in documentation is one line break after each sentence.
|
||||||
|
This is an alternative style that greatly improves readability of diffs as re-wrapping text is not needed when inserting new text in paragraphs.
|
||||||
|
Instead, the diff will show changes to each sentence.
|
||||||
+1
-10
@@ -80,7 +80,6 @@ def main(sysArgs=None):
|
|||||||
"help",
|
"help",
|
||||||
"debug",
|
"debug",
|
||||||
"verbose",
|
"verbose",
|
||||||
"debuggui",
|
|
||||||
"quiet",
|
"quiet",
|
||||||
"time",
|
"time",
|
||||||
"logfile=",
|
"logfile=",
|
||||||
@@ -100,9 +99,8 @@ def main(sysArgs=None):
|
|||||||
" -v, --version Print program version and exit.\n"
|
" -v, --version Print program version and exit.\n"
|
||||||
" -d, --debug Print debug output.\n"
|
" -d, --debug Print debug output.\n"
|
||||||
" --verbose Increase verbosity of debug output.\n"
|
" --verbose Increase verbosity of debug output.\n"
|
||||||
" -D, --debuggui Shows additional debug GUI elements. Includes -d.\n"
|
|
||||||
" -q, --quiet Disable output to command line. Does not affect log file.\n"
|
" -q, --quiet Disable output to command line. Does not affect log file.\n"
|
||||||
" -t, --time Shows time stamp in logging output. Adds milliseconds when --verbose.\n"
|
" -t, --time Shows time stamp in logging output.\n"
|
||||||
" -l, --logfile= Specify log file.\n"
|
" -l, --logfile= Specify log file.\n"
|
||||||
" --style= Set Qt5 style flag. Defaults to Fusion.\n"
|
" --style= Set Qt5 style flag. Defaults to Fusion.\n"
|
||||||
" --config= Alternative config file.\n"
|
" --config= Alternative config file.\n"
|
||||||
@@ -124,7 +122,6 @@ def main(sysArgs=None):
|
|||||||
showTime = False
|
showTime = False
|
||||||
confPath = None
|
confPath = None
|
||||||
testMode = False
|
testMode = False
|
||||||
debugGUI = False
|
|
||||||
spellTool = None
|
spellTool = None
|
||||||
qtStyle = "Fusion"
|
qtStyle = "Fusion"
|
||||||
|
|
||||||
@@ -152,7 +149,6 @@ def main(sysArgs=None):
|
|||||||
toStd = False
|
toStd = False
|
||||||
elif inOpt in ("--verbose"):
|
elif inOpt in ("--verbose"):
|
||||||
debugLevel = VERBOSE
|
debugLevel = VERBOSE
|
||||||
timeStr = "[{asctime:}.{msecs:03.0f}] "
|
|
||||||
elif inOpt in ("-t","--time"):
|
elif inOpt in ("-t","--time"):
|
||||||
showTime = True
|
showTime = True
|
||||||
elif inOpt in ("--style"):
|
elif inOpt in ("--style"):
|
||||||
@@ -163,14 +159,9 @@ def main(sysArgs=None):
|
|||||||
testMode = True
|
testMode = True
|
||||||
elif inOpt in ("--spell"):
|
elif inOpt in ("--spell"):
|
||||||
spellTool = inArg
|
spellTool = inArg
|
||||||
elif inOpt in ("-D","--debuggui"):
|
|
||||||
debugLevel = logging.DEBUG
|
|
||||||
debugStr = "{name:>20}:{lineno:<4d} {levelname:8} {message:}"
|
|
||||||
debugGUI = True
|
|
||||||
|
|
||||||
# Set Config Options
|
# Set Config Options
|
||||||
CONFIG.showGUI = not testMode
|
CONFIG.showGUI = not testMode
|
||||||
CONFIG.debugGUI = debugGUI
|
|
||||||
CONFIG.debugInfo = debugLevel < logging.INFO
|
CONFIG.debugInfo = debugLevel < logging.INFO
|
||||||
CONFIG.spellTool = spellTool
|
CONFIG.spellTool = spellTool
|
||||||
|
|
||||||
|
|||||||
+16
-10
@@ -17,16 +17,20 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def checkString(checkValue, defaultValue, allowNone=False):
|
def checkString(checkValue, defaultValue, allowNone=False):
|
||||||
if allowNone:
|
if allowNone:
|
||||||
if checkValue == None: return None
|
if checkValue == None:
|
||||||
if checkValue == "None": return None
|
return None
|
||||||
|
if checkValue == "None":
|
||||||
|
return None
|
||||||
if isinstance(checkValue,str):
|
if isinstance(checkValue,str):
|
||||||
return str(checkValue)
|
return str(checkValue)
|
||||||
return defaultValue
|
return defaultValue
|
||||||
|
|
||||||
def checkInt(checkValue, defaultValue, allowNone=False):
|
def checkInt(checkValue, defaultValue, allowNone=False):
|
||||||
if allowNone:
|
if allowNone:
|
||||||
if checkValue == None: return None
|
if checkValue == None:
|
||||||
if checkValue == "None": return None
|
return None
|
||||||
|
if checkValue == "None":
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
return int(checkValue)
|
return int(checkValue)
|
||||||
except:
|
except:
|
||||||
@@ -34,8 +38,10 @@ def checkInt(checkValue, defaultValue, allowNone=False):
|
|||||||
|
|
||||||
def checkBool(checkValue, defaultValue, allowNone=False):
|
def checkBool(checkValue, defaultValue, allowNone=False):
|
||||||
if allowNone:
|
if allowNone:
|
||||||
if checkValue == None: return None
|
if checkValue == None:
|
||||||
if checkValue == "None": return None
|
return None
|
||||||
|
if checkValue == "None":
|
||||||
|
return None
|
||||||
if isinstance(checkValue, str):
|
if isinstance(checkValue, str):
|
||||||
if checkValue == "True":
|
if checkValue == "True":
|
||||||
return True
|
return True
|
||||||
@@ -83,8 +89,8 @@ def colRange(rgbStart, rgbEnd, nStep):
|
|||||||
return retCol
|
return retCol
|
||||||
|
|
||||||
def splitVersionNumber(vString):
|
def splitVersionNumber(vString):
|
||||||
""" Splits a version string on the form aa.bb.cc into major, minor and patch, and computes an
|
""" Splits a version string on the form aa.bb.cc into major, minor
|
||||||
integer value aabbcc.
|
and patch, and computes an integer value aabbcc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
vMajor = 0
|
vMajor = 0
|
||||||
@@ -92,8 +98,8 @@ def splitVersionNumber(vString):
|
|||||||
vPatch = 0
|
vPatch = 0
|
||||||
vInt = 0
|
vInt = 0
|
||||||
|
|
||||||
vBits = vString.split(".")
|
vBits = vString.split(".")
|
||||||
nBits = len(vBits)
|
nBits = len(vBits)
|
||||||
|
|
||||||
if nBits > 0:
|
if nBits > 0:
|
||||||
vMajor = checkInt(vBits[0],0)
|
vMajor = checkInt(vBits[0],0)
|
||||||
|
|||||||
+111
-44
@@ -15,15 +15,15 @@ import configparser
|
|||||||
import sys
|
import sys
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path, mkdir, makedirs, getcwd
|
from os import path, mkdir, makedirs, getcwd
|
||||||
from appdirs import user_config_dir
|
from appdirs import user_config_dir
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from PyQt5.Qt import PYQT_VERSION_STR
|
||||||
|
from PyQt5.QtCore import QT_VERSION_STR
|
||||||
|
|
||||||
from nw.constants import nwFiles, nwUnicode
|
from nw.constants import nwFiles, nwUnicode
|
||||||
from nw.common import splitVersionNumber
|
from nw.common import splitVersionNumber
|
||||||
|
|
||||||
from PyQt5.Qt import PYQT_VERSION_STR
|
|
||||||
from PyQt5.QtCore import QT_VERSION_STR
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -40,7 +40,6 @@ class Config:
|
|||||||
self.appName = nw.__package__
|
self.appName = nw.__package__
|
||||||
self.appHandle = nw.__package__.lower()
|
self.appHandle = nw.__package__.lower()
|
||||||
self.showGUI = True
|
self.showGUI = True
|
||||||
self.debugGUI = False
|
|
||||||
self.debugInfo = False
|
self.debugInfo = False
|
||||||
self.spellTool = None
|
self.spellTool = None
|
||||||
|
|
||||||
@@ -60,8 +59,8 @@ class Config:
|
|||||||
self.confChanged = False
|
self.confChanged = False
|
||||||
|
|
||||||
## General
|
## General
|
||||||
self.guiTheme = "default"
|
self.guiTheme = "default"
|
||||||
self.guiSyntax = "default_light"
|
self.guiSyntax = "default_light"
|
||||||
|
|
||||||
## Sizes
|
## Sizes
|
||||||
self.winGeometry = [1100, 650]
|
self.winGeometry = [1100, 650]
|
||||||
@@ -201,65 +200,133 @@ class Config:
|
|||||||
logger.debug("Loading config file")
|
logger.debug("Loading config file")
|
||||||
cnfParse = configparser.ConfigParser()
|
cnfParse = configparser.ConfigParser()
|
||||||
try:
|
try:
|
||||||
cnfParse.read_file(open(path.join(self.confPath,self.confFile),mode="r",encoding="utf8"))
|
cnfParse.read_file(
|
||||||
|
open(path.join(self.confPath,self.confFile),mode="r",encoding="utf8")
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Could not load config file")
|
logger.error("Could not load config file")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
## Main
|
## Main
|
||||||
cnfSec = "Main"
|
cnfSec = "Main"
|
||||||
self.guiTheme = self._parseLine(cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme)
|
self.guiTheme = self._parseLine(
|
||||||
self.guiSyntax = self._parseLine(cnfParse, cnfSec, "syntax", self.CNF_STR, self.guiSyntax)
|
cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme
|
||||||
|
)
|
||||||
|
self.guiSyntax = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "syntax", self.CNF_STR, self.guiSyntax
|
||||||
|
)
|
||||||
|
|
||||||
## Sizes
|
## Sizes
|
||||||
cnfSec = "Sizes"
|
cnfSec = "Sizes"
|
||||||
self.winGeometry = self._parseLine(cnfParse, cnfSec, "geometry", self.CNF_LIST, self.winGeometry)
|
self.winGeometry = self._parseLine(
|
||||||
self.treeColWidth = self._parseLine(cnfParse, cnfSec, "treecols", self.CNF_LIST, self.treeColWidth)
|
cnfParse, cnfSec, "geometry", self.CNF_LIST, self.winGeometry
|
||||||
self.mainPanePos = self._parseLine(cnfParse, cnfSec, "mainpane", self.CNF_LIST, self.mainPanePos)
|
)
|
||||||
self.docPanePos = self._parseLine(cnfParse, cnfSec, "docpane", self.CNF_LIST, self.docPanePos)
|
self.treeColWidth = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "treecols", self.CNF_LIST, self.treeColWidth
|
||||||
|
)
|
||||||
|
self.mainPanePos = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "mainpane", self.CNF_LIST, self.mainPanePos
|
||||||
|
)
|
||||||
|
self.docPanePos = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "docpane", self.CNF_LIST, self.docPanePos
|
||||||
|
)
|
||||||
|
|
||||||
## Project
|
## Project
|
||||||
cnfSec = "Project"
|
cnfSec = "Project"
|
||||||
self.autoSaveProj = self._parseLine(cnfParse, cnfSec, "autosaveproject", self.CNF_INT, self.autoSaveProj)
|
self.autoSaveProj = self._parseLine(
|
||||||
self.autoSaveDoc = self._parseLine(cnfParse, cnfSec, "autosavedoc", self.CNF_INT, self.autoSaveDoc)
|
cnfParse, cnfSec, "autosaveproject", self.CNF_INT, self.autoSaveProj
|
||||||
|
)
|
||||||
|
self.autoSaveDoc = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "autosavedoc", self.CNF_INT, self.autoSaveDoc
|
||||||
|
)
|
||||||
|
|
||||||
## Editor
|
## Editor
|
||||||
cnfSec = "Editor"
|
cnfSec = "Editor"
|
||||||
self.textFont = self._parseLine(cnfParse, cnfSec, "textfont", self.CNF_STR, self.textFont)
|
self.textFont = self._parseLine(
|
||||||
self.textSize = self._parseLine(cnfParse, cnfSec, "textsize", self.CNF_INT, self.textSize)
|
cnfParse, cnfSec, "textfont", self.CNF_STR, self.textFont
|
||||||
self.textFixedW = self._parseLine(cnfParse, cnfSec, "fixedwidth", self.CNF_BOOL, self.textFixedW)
|
)
|
||||||
self.textWidth = self._parseLine(cnfParse, cnfSec, "width", self.CNF_INT, self.textWidth)
|
self.textSize = self._parseLine(
|
||||||
self.textMargin = self._parseLine(cnfParse, cnfSec, "margin", self.CNF_INT, self.textMargin)
|
cnfParse, cnfSec, "textsize", self.CNF_INT, self.textSize
|
||||||
self.tabWidth = self._parseLine(cnfParse, cnfSec, "tabwidth", self.CNF_INT, self.tabWidth)
|
)
|
||||||
self.doJustify = self._parseLine(cnfParse, cnfSec, "justify", self.CNF_BOOL, self.doJustify)
|
self.textFixedW = self._parseLine(
|
||||||
self.autoSelect = self._parseLine(cnfParse, cnfSec, "autoselect", self.CNF_BOOL, self.autoSelect)
|
cnfParse, cnfSec, "fixedwidth", self.CNF_BOOL, self.textFixedW
|
||||||
self.doReplace = self._parseLine(cnfParse, cnfSec, "autoreplace", self.CNF_BOOL, self.doReplace)
|
)
|
||||||
self.doReplaceSQuote = self._parseLine(cnfParse, cnfSec, "repsquotes", self.CNF_BOOL, self.doReplaceSQuote)
|
self.textWidth = self._parseLine(
|
||||||
self.doReplaceDQuote = self._parseLine(cnfParse, cnfSec, "repdquotes", self.CNF_BOOL, self.doReplaceDQuote)
|
cnfParse, cnfSec, "width", self.CNF_INT, self.textWidth
|
||||||
self.doReplaceDash = self._parseLine(cnfParse, cnfSec, "repdash", self.CNF_BOOL, self.doReplaceDash)
|
)
|
||||||
self.doReplaceDots = self._parseLine(cnfParse, cnfSec, "repdots", self.CNF_BOOL, self.doReplaceDots)
|
self.textMargin = self._parseLine(
|
||||||
self.fmtSingleQuotes = self._parseLine(cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes)
|
cnfParse, cnfSec, "margin", self.CNF_INT, self.textMargin
|
||||||
self.fmtDoubleQuotes = self._parseLine(cnfParse, cnfSec, "fmtdoublequote", self.CNF_LIST, self.fmtDoubleQuotes)
|
)
|
||||||
self.spellLanguage = self._parseLine(cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage)
|
self.tabWidth = self._parseLine(
|
||||||
self.showTabsNSpaces = self._parseLine(cnfParse, cnfSec, "showtabsnspaces", self.CNF_BOOL, self.showTabsNSpaces)
|
cnfParse, cnfSec, "tabwidth", self.CNF_INT, self.tabWidth
|
||||||
self.showLineEndings = self._parseLine(cnfParse, cnfSec, "showlineendings", self.CNF_BOOL, self.showLineEndings)
|
)
|
||||||
|
self.doJustify = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "justify", self.CNF_BOOL, self.doJustify
|
||||||
|
)
|
||||||
|
self.autoSelect = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "autoselect", self.CNF_BOOL, self.autoSelect
|
||||||
|
)
|
||||||
|
self.doReplace = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "autoreplace", self.CNF_BOOL, self.doReplace
|
||||||
|
)
|
||||||
|
self.doReplaceSQuote = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "repsquotes", self.CNF_BOOL, self.doReplaceSQuote
|
||||||
|
)
|
||||||
|
self.doReplaceDQuote = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "repdquotes", self.CNF_BOOL, self.doReplaceDQuote
|
||||||
|
)
|
||||||
|
self.doReplaceDash = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "repdash", self.CNF_BOOL, self.doReplaceDash
|
||||||
|
)
|
||||||
|
self.doReplaceDots = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "repdots", self.CNF_BOOL, self.doReplaceDots
|
||||||
|
)
|
||||||
|
self.fmtSingleQuotes = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes
|
||||||
|
)
|
||||||
|
self.fmtDoubleQuotes = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "fmtdoublequote", self.CNF_LIST, self.fmtDoubleQuotes
|
||||||
|
)
|
||||||
|
self.spellLanguage = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "spellcheck", self.CNF_STR, self.spellLanguage
|
||||||
|
)
|
||||||
|
self.showTabsNSpaces = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "showtabsnspaces", self.CNF_BOOL, self.showTabsNSpaces
|
||||||
|
)
|
||||||
|
self.showLineEndings = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "showlineendings", self.CNF_BOOL, self.showLineEndings
|
||||||
|
)
|
||||||
|
|
||||||
## Backup
|
## Backup
|
||||||
cnfSec = "Backup"
|
cnfSec = "Backup"
|
||||||
self.backupPath = self._parseLine(cnfParse, cnfSec, "backuppath", self.CNF_STR, self.backupPath)
|
self.backupPath = self._parseLine(
|
||||||
self.backupOnClose = self._parseLine(cnfParse, cnfSec, "backuponclose", self.CNF_BOOL, self.backupOnClose)
|
cnfParse, cnfSec, "backuppath", self.CNF_STR, self.backupPath
|
||||||
self.askBeforeBackup = self._parseLine(cnfParse, cnfSec, "askbeforebackup", self.CNF_BOOL, self.askBeforeBackup)
|
)
|
||||||
|
self.backupOnClose = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "backuponclose", self.CNF_BOOL, self.backupOnClose
|
||||||
|
)
|
||||||
|
self.askBeforeBackup = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "askbeforebackup", self.CNF_BOOL, self.askBeforeBackup
|
||||||
|
)
|
||||||
|
|
||||||
## State
|
## State
|
||||||
cnfSec = "State"
|
cnfSec = "State"
|
||||||
self.showRefPanel = self._parseLine(cnfParse, cnfSec, "showrefpanel", self.CNF_BOOL, self.showRefPanel)
|
self.showRefPanel = self._parseLine(
|
||||||
self.viewComments = self._parseLine(cnfParse, cnfSec, "viewcomments", self.CNF_BOOL, self.viewComments)
|
cnfParse, cnfSec, "showrefpanel", self.CNF_BOOL, self.showRefPanel
|
||||||
|
)
|
||||||
|
self.viewComments = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "viewcomments", self.CNF_BOOL, self.viewComments
|
||||||
|
)
|
||||||
|
|
||||||
## Path
|
## Path
|
||||||
cnfSec = "Path"
|
cnfSec = "Path"
|
||||||
self.lastPath = self._parseLine(cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath)
|
self.lastPath = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath
|
||||||
|
)
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
self.recentList[i] = self._parseLine(cnfParse, cnfSec, "recent%d" % i,self.CNF_STR, self.recentList[i])
|
self.recentList[i] = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "recent%d" % i,self.CNF_STR, self.recentList[i]
|
||||||
|
)
|
||||||
|
|
||||||
# Check Certain Values for None
|
# Check Certain Values for None
|
||||||
self.spellLanguage = self._checkNone(self.spellLanguage)
|
self.spellLanguage = self._checkNone(self.spellLanguage)
|
||||||
|
|||||||
@@ -13,12 +13,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
from nw.convert.file.text import TextFile
|
from nw.convert.file.text import TextFile
|
||||||
from nw.convert.tokenizer import Tokenizer
|
from nw.convert.tokenizer import Tokenizer
|
||||||
from nw.enum import nwAlert, nwItemLayout
|
from nw.enum import nwAlert, nwItemLayout
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -26,9 +27,7 @@ class ConcatFile(TextFile):
|
|||||||
|
|
||||||
def __init__(self, theProject, theParent):
|
def __init__(self, theProject, theParent):
|
||||||
TextFile.__init__(self, theProject, theParent)
|
TextFile.__init__(self, theProject, theParent)
|
||||||
|
|
||||||
self.theConv = Tokenizer(self.theProject, self.theParent)
|
self.theConv = Tokenizer(self.theProject, self.theParent)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def addText(self, tHandle):
|
def addText(self, tHandle):
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from nw.convert.file.text import TextFile
|
from nw.convert.file.text import TextFile
|
||||||
from nw.convert.text.tohtml import ToHtml
|
from nw.convert.text.tohtml import ToHtml
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -23,9 +23,7 @@ class HtmlFile(TextFile):
|
|||||||
|
|
||||||
def __init__(self, theProject, theParent):
|
def __init__(self, theProject, theParent):
|
||||||
TextFile.__init__(self, theProject, theParent)
|
TextFile.__init__(self, theProject, theParent)
|
||||||
|
|
||||||
self.theConv = ToHtml(self.theProject, self.theParent)
|
self.theConv = ToHtml(self.theProject, self.theParent)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from nw.convert.file.text import TextFile
|
from nw.convert.file.text import TextFile
|
||||||
from nw.convert.text.tolatex import ToLaTeX
|
from nw.convert.text.tolatex import ToLaTeX
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -32,7 +32,6 @@ class LaTeXFile(TextFile):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def _doOpenFile(self, filePath):
|
def _doOpenFile(self, filePath):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.outFile = open(filePath,mode="wt+",encoding="utf8")
|
self.outFile = open(filePath,mode="wt+",encoding="utf8")
|
||||||
self.outFile.write("\\documentclass[12pt]{report}\n")
|
self.outFile.write("\\documentclass[12pt]{report}\n")
|
||||||
@@ -43,17 +42,13 @@ class LaTeXFile(TextFile):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
|
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _doCloseFile(self):
|
def _doCloseFile(self):
|
||||||
|
|
||||||
if self.outFile is not None:
|
if self.outFile is not None:
|
||||||
self.outFile.write("\\end{document}\n")
|
self.outFile.write("\\end{document}\n")
|
||||||
self.outFile.close()
|
self.outFile.close()
|
||||||
|
|
||||||
self.texCodecFail = self.theConv.texCodecFail
|
self.texCodecFail = self.theConv.texCodecFail
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# END Class LaTeXFile
|
# END Class LaTeXFile
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from nw.convert.file.text import TextFile
|
from nw.convert.file.text import TextFile
|
||||||
from nw.convert.text.tomarkdown import ToMarkdown
|
from nw.convert.text.tomarkdown import ToMarkdown
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -23,9 +23,7 @@ class MarkdownFile(TextFile):
|
|||||||
|
|
||||||
def __init__(self, theProject, theParent):
|
def __init__(self, theProject, theParent):
|
||||||
TextFile.__init__(self, theProject, theParent)
|
TextFile.__init__(self, theProject, theParent)
|
||||||
|
|
||||||
self.theConv = ToMarkdown(self.theProject, self.theParent)
|
self.theConv = ToMarkdown(self.theProject, self.theParent)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
+25
-20
@@ -13,11 +13,12 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
from nw.convert.text.totext import ToText
|
from nw.convert.text.totext import ToText
|
||||||
from nw.enum import nwAlert, nwItemType, nwItemLayout, nwItemClass
|
from nw.enum import nwAlert, nwItemType, nwItemLayout, nwItemClass
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -29,14 +30,14 @@ class TextFile():
|
|||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
|
|
||||||
self.outFile = None
|
self.outFile = None
|
||||||
self.fileName = ""
|
self.fileName = ""
|
||||||
self.theText = ""
|
self.theText = ""
|
||||||
self.expNovel = True
|
self.expNovel = True
|
||||||
self.expNotes = False
|
self.expNotes = False
|
||||||
|
|
||||||
self.theConv = ToText(self.theProject, self.theParent)
|
self.theConv = ToText(self.theProject, self.theParent)
|
||||||
self.makeAlert = self.theParent.makeAlert
|
self.makeAlert = self.theParent.makeAlert
|
||||||
|
|
||||||
self.setComments(False)
|
self.setComments(False)
|
||||||
self.setKeywords(False)
|
self.setKeywords(False)
|
||||||
@@ -100,9 +101,9 @@ class TextFile():
|
|||||||
self.fileName = path.basename(filePath)
|
self.fileName = path.basename(filePath)
|
||||||
if path.isfile(filePath) and self.mainConf.showGUI:
|
if path.isfile(filePath) and self.mainConf.showGUI:
|
||||||
msgBox = QMessageBox()
|
msgBox = QMessageBox()
|
||||||
msgRes = msgBox.question(
|
msgRes = msgBox.question(self.theParent, "Overwrite", (
|
||||||
self.theParent, "Overwrite", ("File '%s' already exists.<br>Do you want to overwrite it?" % self.fileName)
|
"File '%s' already exists.<br>Do you want to overwrite it?" % self.fileName
|
||||||
)
|
))
|
||||||
if msgRes != QMessageBox.Yes:
|
if msgRes != QMessageBox.Yes:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -137,11 +138,14 @@ class TextFile():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def checkInclude(self, tHandle):
|
def checkInclude(self, tHandle):
|
||||||
"""This function checks whether a file should be included in the export or not. For standard
|
"""This function checks whether a file should be included in the
|
||||||
note and novel files, this is controlled by the options selected by the user. For other
|
export or not. For standard note and novel files, this is
|
||||||
files classified as non-exportable, a few checks must be made, and the following are not:
|
controlled by the options selected by the user. For other files
|
||||||
|
classified as non-exportable, a few checks must be made, and the
|
||||||
|
following are not:
|
||||||
* Items that are not actual files.
|
* Items that are not actual files.
|
||||||
* Items that have been orphaned which are tagged as NO_LAYOUT and NO_CLASS.
|
* Items that have been orphaned which are tagged as NO_LAYOUT
|
||||||
|
and NO_CLASS.
|
||||||
* Items that appear in the TRASH folder
|
* Items that appear in the TRASH folder
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -168,8 +172,9 @@ class TextFile():
|
|||||||
##
|
##
|
||||||
|
|
||||||
def _doOpenFile(self, filePath):
|
def _doOpenFile(self, filePath):
|
||||||
"""This function does the actual opening of the file, and can be overloaded by a subclass
|
"""This function does the actual opening of the file, and can be
|
||||||
that uses a different file format that requires a different approach.
|
overloaded by a subclass that uses a different file format that
|
||||||
|
requires a different approach.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.outFile = open(filePath,mode="wt+",encoding="utf8")
|
self.outFile = open(filePath,mode="wt+",encoding="utf8")
|
||||||
@@ -180,8 +185,8 @@ class TextFile():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _doCloseFile(self):
|
def _doCloseFile(self):
|
||||||
"""This function closes the file, and is meant to be overloaded by the subclass for other
|
"""This function closes the file, and is meant to be overloaded
|
||||||
file formats.
|
by the subclass for other file formats.
|
||||||
"""
|
"""
|
||||||
if self.outFile is not None:
|
if self.outFile is not None:
|
||||||
self.outFile.close()
|
self.outFile.close()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import re
|
|||||||
import nw
|
import nw
|
||||||
|
|
||||||
from nw.convert.tokenizer import Tokenizer
|
from nw.convert.tokenizer import Tokenizer
|
||||||
from nw.constants import nwUnicode, nwLabels
|
from nw.constants import nwUnicode, nwLabels
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -27,15 +27,14 @@ class ToHtml(Tokenizer):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def setPreview(self, forPreview, doComments):
|
def setPreview(self, forPreview, doComments):
|
||||||
"""If we're using this class to generate markdown preview, we need to make a few changes to
|
"""If we're using this class to generate markdown preview, we
|
||||||
formatting, which is selected by this flag.
|
need to make a few changes to formatting, which is selected by
|
||||||
|
this flag.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.forPreview = forPreview
|
self.forPreview = forPreview
|
||||||
if forPreview:
|
if forPreview:
|
||||||
self.doKeywords = True
|
self.doKeywords = True
|
||||||
self.doComments = doComments
|
self.doComments = doComments
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def doAutoReplace(self):
|
def doAutoReplace(self):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import re
|
|||||||
import nw
|
import nw
|
||||||
|
|
||||||
from nw.convert.tokenizer import Tokenizer
|
from nw.convert.tokenizer import Tokenizer
|
||||||
from nw.constants import nwUnicode
|
from nw.constants import nwUnicode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -28,7 +28,8 @@ class ToLaTeX(Tokenizer):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def doPostProcessing(self):
|
def doPostProcessing(self):
|
||||||
"""The latexcodec misses dashes and non-breaking spaces, so we do those here.
|
"""The latexcodec misses dashes and non-breaking spaces, so we
|
||||||
|
do those here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
repDict = {
|
repDict = {
|
||||||
@@ -62,8 +63,9 @@ class ToLaTeX(Tokenizer):
|
|||||||
begText = "\\begin{center}\n"
|
begText = "\\begin{center}\n"
|
||||||
endText = "\\end{center}\n\n"
|
endText = "\\end{center}\n\n"
|
||||||
|
|
||||||
# First check if we have a comment or plain text, as they need some
|
# First check if we have a comment or plain text, as they
|
||||||
# extra replacing before we proceed to wrapping and final formatting.
|
# need some extra replacing before we proceed to wrapping
|
||||||
|
# and final formatting.
|
||||||
if tType == self.T_COMMENT:
|
if tType == self.T_COMMENT:
|
||||||
tText = "%% %s" % tText
|
tText = "%% %s" % tText
|
||||||
|
|
||||||
@@ -75,8 +77,9 @@ class ToLaTeX(Tokenizer):
|
|||||||
|
|
||||||
tLen = len(tText)
|
tLen = len(tText)
|
||||||
|
|
||||||
# Then the text can receive final formatting before we append it to the results.
|
# Then the text can receive final formatting before we
|
||||||
# We also store text lines in a buffer and merge them only when we find an empty line
|
# append it to the results. We also store text lines in a
|
||||||
|
# buffer and merge them only when we find an empty line
|
||||||
# indicating a new paragraph.
|
# indicating a new paragraph.
|
||||||
if tType == self.T_EMPTY:
|
if tType == self.T_EMPTY:
|
||||||
if len(thisPar) > 0:
|
if len(thisPar) > 0:
|
||||||
|
|||||||
@@ -55,8 +55,9 @@ class ToMarkdown(Tokenizer):
|
|||||||
thisPar = []
|
thisPar = []
|
||||||
for tType, tText, tFormat, tAlign in self.theTokens:
|
for tType, tText, tFormat, tAlign in self.theTokens:
|
||||||
|
|
||||||
# First check if we have a comment or plain text, as they need some
|
# First check if we have a comment or plain text, as they
|
||||||
# extra replacing before we proceed to wrapping and final formatting.
|
# need some extra replacing before we proceed to wrapping
|
||||||
|
# and final formatting.
|
||||||
if tType == self.T_COMMENT:
|
if tType == self.T_COMMENT:
|
||||||
tText = " %s" % tText
|
tText = " %s" % tText
|
||||||
|
|
||||||
@@ -68,15 +69,19 @@ class ToMarkdown(Tokenizer):
|
|||||||
|
|
||||||
tLen = len(tText)
|
tLen = len(tText)
|
||||||
|
|
||||||
# The text can now be word wrapped, if we have requested this and it's needed.
|
# The text can now be word wrapped, if we have requested
|
||||||
|
# this and it's needed.
|
||||||
if self.wordWrap > 0 and tLen > self.wordWrap:
|
if self.wordWrap > 0 and tLen > self.wordWrap:
|
||||||
if tType == self.T_COMMENT:
|
if tType == self.T_COMMENT:
|
||||||
tText = textwrap.fill(tText.strip(),initial_indent=" ",subsequent_indent=" ")
|
tText = textwrap.fill(
|
||||||
|
tText.strip(),initial_indent=" ",subsequent_indent=" "
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
tText = tWrap.fill(tText)
|
tText = tWrap.fill(tText)
|
||||||
|
|
||||||
# Then the text can receive final formatting before we append it to the results.
|
# Then the text can receive final formatting before we
|
||||||
# We also store text lines in a buffer and merge them only when we find an empty line,
|
# append it to the results. We also store text lines in a
|
||||||
|
# buffer and merge them only when we find an empty line,
|
||||||
# indicating a new paragraph.
|
# indicating a new paragraph.
|
||||||
if tType == self.T_EMPTY:
|
if tType == self.T_EMPTY:
|
||||||
if len(thisPar) > 0:
|
if len(thisPar) > 0:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import re
|
|||||||
import nw
|
import nw
|
||||||
|
|
||||||
from nw.convert.tokenizer import Tokenizer
|
from nw.convert.tokenizer import Tokenizer
|
||||||
from nw.constants import nwUnicode
|
from nw.constants import nwUnicode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -61,8 +61,9 @@ class ToText(Tokenizer):
|
|||||||
thisPar = []
|
thisPar = []
|
||||||
for tType, tText, tFormat, tAlign in self.theTokens:
|
for tType, tText, tFormat, tAlign in self.theTokens:
|
||||||
|
|
||||||
# First check if we have a comment or plain text, as they need some
|
# First check if we have a comment or plain text, as they
|
||||||
# extra replacing before we proceed to wrapping and final formatting.
|
# need some extra replacing before we proceed to wrapping
|
||||||
|
# and final formatting.
|
||||||
if tType == self.T_COMMENT:
|
if tType == self.T_COMMENT:
|
||||||
tText = "[%s]" % tText
|
tText = "[%s]" % tText
|
||||||
|
|
||||||
@@ -74,7 +75,8 @@ class ToText(Tokenizer):
|
|||||||
|
|
||||||
tLen = len(tText)
|
tLen = len(tText)
|
||||||
|
|
||||||
# The text can now be word wrapped, if we have requested this and it's needed.
|
# The text can now be word wrapped, if we have requested
|
||||||
|
# this and it's needed.
|
||||||
if tAlign == self.A_CENTRE:
|
if tAlign == self.A_CENTRE:
|
||||||
if self.wordWrap > 0:
|
if self.wordWrap > 0:
|
||||||
if tLen > self.wordWrap:
|
if tLen > self.wordWrap:
|
||||||
@@ -88,8 +90,9 @@ class ToText(Tokenizer):
|
|||||||
if self.wordWrap > 0 and tLen > self.wordWrap:
|
if self.wordWrap > 0 and tLen > self.wordWrap:
|
||||||
tText = tWrap.fill(tText)
|
tText = tWrap.fill(tText)
|
||||||
|
|
||||||
# Then the text can receive final formatting before we append it to the results.
|
# Then the text can receive final formatting before we
|
||||||
# We also store text lines in a buffer and merge them only when we find an empty line,
|
# append it to the results. We also store text lines in a
|
||||||
|
# buffer and merge them only when we find an empty line,
|
||||||
# indicating a new paragraph.
|
# indicating a new paragraph.
|
||||||
if tType == self.T_EMPTY:
|
if tType == self.T_EMPTY:
|
||||||
if len(thisPar) > 0:
|
if len(thisPar) > 0:
|
||||||
|
|||||||
+13
-10
@@ -15,12 +15,12 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from PyQt5.QtCore import QRegularExpression
|
from PyQt5.QtCore import QRegularExpression
|
||||||
|
|
||||||
from nw.project.document import NWDoc
|
from nw.project.document import NWDoc
|
||||||
from nw.tools.translate import numberToWord
|
from nw.tools.translate import numberToWord
|
||||||
from nw.enum import nwItemLayout
|
from nw.enum import nwItemLayout
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -153,10 +153,11 @@ class Tokenizer():
|
|||||||
return
|
return
|
||||||
|
|
||||||
def tokenizeText(self):
|
def tokenizeText(self):
|
||||||
"""Scan the text for either lines starting with specific characters that indicate headers,
|
"""Scan the text for either lines starting with specific
|
||||||
comments, commands etc, or just contains plain text. in the case of plain text, apply the
|
characters that indicate headers, comments, commands etc, or
|
||||||
same RegExes that the syntax highlighter uses and save the locations of these formatting
|
just contains plain text. in the case of plain text, apply the
|
||||||
tags into the token array.
|
same RegExes that the syntax highlighter uses and save the
|
||||||
|
locations of these formatting tags into the token array.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# RegExes for adding formatting tags within text lines
|
# RegExes for adding formatting tags within text lines
|
||||||
@@ -203,7 +204,8 @@ class Tokenizer():
|
|||||||
xLen = rxMatch.capturedLength(n)
|
xLen = rxMatch.capturedLength(n)
|
||||||
fmtPos.append([xPos,xLen,theKeys[n]])
|
fmtPos.append([xPos,xLen,theKeys[n]])
|
||||||
|
|
||||||
# Save the line as is, but append the array of formatting locations sorted by position
|
# Save the line as is, but append the array of formatting locations
|
||||||
|
# sorted by position
|
||||||
fmtPos = sorted(fmtPos,key=itemgetter(0))
|
fmtPos = sorted(fmtPos,key=itemgetter(0))
|
||||||
self.theTokens.append((self.T_TEXT,aLine,fmtPos,self.A_LEFT))
|
self.theTokens.append((self.T_TEXT,aLine,fmtPos,self.A_LEFT))
|
||||||
|
|
||||||
@@ -228,7 +230,8 @@ class Tokenizer():
|
|||||||
if isNone: return
|
if isNone: return
|
||||||
if isNote: return
|
if isNote: return
|
||||||
|
|
||||||
# For novel files, we need to handle chapter numbering and scene breaks
|
# For novel files, we need to handle chapter numbering and scene
|
||||||
|
# breaks
|
||||||
if isBook or isUnNum or isChap or isScene:
|
if isBook or isUnNum or isChap or isScene:
|
||||||
for n in range(len(self.theTokens)):
|
for n in range(len(self.theTokens)):
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ import nw
|
|||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel, QFont
|
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel, QFont
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt5.QtSvg import QSvgWidget
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel,
|
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel,
|
||||||
QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox,
|
QWidget, QTabWidget, QDialogButtonBox, QSpinBox, QGroupBox, QComboBox, QMessageBox,
|
||||||
QCheckBox, QGridLayout, QFontComboBox, QPushButton, QFileDialog
|
QCheckBox, QGridLayout, QFontComboBox, QPushButton, QFileDialog
|
||||||
)
|
)
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
from nw.constants import nwQuotes
|
from nw.constants import nwQuotes
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -80,8 +80,8 @@ class GuiConfigEditor(QDialog):
|
|||||||
|
|
||||||
logger.verbose("ConfigEditor save button clicked")
|
logger.verbose("ConfigEditor save button clicked")
|
||||||
|
|
||||||
validEntries = True
|
validEntries = True
|
||||||
needsRestart = False
|
needsRestart = False
|
||||||
|
|
||||||
retA, retB = self.tabMain.saveValues()
|
retA, retB = self.tabMain.saveValues()
|
||||||
validEntries &= retA
|
validEntries &= retA
|
||||||
@@ -407,7 +407,9 @@ class GuiConfigEditEditor(QWidget):
|
|||||||
self.autoReplaceDQ.setCheckState(Qt.Unchecked)
|
self.autoReplaceDQ.setCheckState(Qt.Unchecked)
|
||||||
|
|
||||||
self.autoReplaceDash = QCheckBox(self)
|
self.autoReplaceDash = QCheckBox(self)
|
||||||
self.autoReplaceDash.setToolTip("Auto-replace double and triple hyphens with short and long dash.")
|
self.autoReplaceDash.setToolTip(
|
||||||
|
"Auto-replace double and triple hyphens with short and long dash."
|
||||||
|
)
|
||||||
if self.mainConf.doReplaceDash:
|
if self.mainConf.doReplaceDash:
|
||||||
self.autoReplaceDash.setCheckState(Qt.Checked)
|
self.autoReplaceDash.setCheckState(Qt.Checked)
|
||||||
else:
|
else:
|
||||||
@@ -555,25 +557,33 @@ class GuiConfigEditEditor(QWidget):
|
|||||||
if self._checkQuoteSymbol(fmtSingleQuotesO):
|
if self._checkQuoteSymbol(fmtSingleQuotesO):
|
||||||
self.mainConf.fmtSingleQuotes[0] = fmtSingleQuotesO
|
self.mainConf.fmtSingleQuotes[0] = fmtSingleQuotesO
|
||||||
else:
|
else:
|
||||||
self.theParent.makeAlert("Invalid quote symbol: %s" % fmtSingleQuotesO, nwAlert.ERROR)
|
self.theParent.makeAlert(
|
||||||
|
"Invalid quote symbol: %s" % fmtSingleQuotesO, nwAlert.ERROR
|
||||||
|
)
|
||||||
validEntries = False
|
validEntries = False
|
||||||
|
|
||||||
if self._checkQuoteSymbol(fmtSingleQuotesC):
|
if self._checkQuoteSymbol(fmtSingleQuotesC):
|
||||||
self.mainConf.fmtSingleQuotes[1] = fmtSingleQuotesC
|
self.mainConf.fmtSingleQuotes[1] = fmtSingleQuotesC
|
||||||
else:
|
else:
|
||||||
self.theParent.makeAlert("Invalid quote symbol: %s" % fmtSingleQuotesC, nwAlert.ERROR)
|
self.theParent.makeAlert(
|
||||||
|
"Invalid quote symbol: %s" % fmtSingleQuotesC, nwAlert.ERROR
|
||||||
|
)
|
||||||
validEntries = False
|
validEntries = False
|
||||||
|
|
||||||
if self._checkQuoteSymbol(fmtDoubleQuotesO):
|
if self._checkQuoteSymbol(fmtDoubleQuotesO):
|
||||||
self.mainConf.fmtDoubleQuotes[0] = fmtDoubleQuotesO
|
self.mainConf.fmtDoubleQuotes[0] = fmtDoubleQuotesO
|
||||||
else:
|
else:
|
||||||
self.theParent.makeAlert("Invalid quote symbol: %s" % fmtDoubleQuotesO, nwAlert.ERROR)
|
self.theParent.makeAlert(
|
||||||
|
"Invalid quote symbol: %s" % fmtDoubleQuotesO, nwAlert.ERROR
|
||||||
|
)
|
||||||
validEntries = False
|
validEntries = False
|
||||||
|
|
||||||
if self._checkQuoteSymbol(fmtDoubleQuotesC):
|
if self._checkQuoteSymbol(fmtDoubleQuotesC):
|
||||||
self.mainConf.fmtDoubleQuotes[1] = fmtDoubleQuotesC
|
self.mainConf.fmtDoubleQuotes[1] = fmtDoubleQuotesC
|
||||||
else:
|
else:
|
||||||
self.theParent.makeAlert("Invalid quote symbol: %s" % fmtDoubleQuotesC, nwAlert.ERROR)
|
self.theParent.makeAlert(
|
||||||
|
"Invalid quote symbol: %s" % fmtDoubleQuotesC, nwAlert.ERROR
|
||||||
|
)
|
||||||
validEntries = False
|
validEntries = False
|
||||||
|
|
||||||
showTabsNSpaces = self.showTabsNSpaces.isChecked()
|
showTabsNSpaces = self.showTabsNSpaces.isChecked()
|
||||||
|
|||||||
+41
-35
@@ -16,24 +16,25 @@ import nw
|
|||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt5.QtSvg import QSvgWidget
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QHBoxLayout, QVBoxLayout, QWidget, QTabWidget, QGridLayout, QGroupBox, QCheckBox,
|
QDialog, QHBoxLayout, QVBoxLayout, QWidget, QTabWidget, QGridLayout,
|
||||||
QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog, QProgressBar, QSpinBox, QMessageBox
|
QGroupBox, QCheckBox, QLabel, QComboBox, QLineEdit, QPushButton,
|
||||||
|
QFileDialog, QProgressBar, QSpinBox, QMessageBox
|
||||||
)
|
)
|
||||||
|
|
||||||
from nw.project.document import NWDoc
|
from nw.project.document import NWDoc
|
||||||
from nw.tools.translate import numberToWord
|
from nw.tools.translate import numberToWord
|
||||||
from nw.tools.optlaststate import OptLastState
|
from nw.tools.optlaststate import OptLastState
|
||||||
from nw.convert.file.text import TextFile
|
from nw.convert.file.text import TextFile
|
||||||
from nw.convert.file.html import HtmlFile
|
from nw.convert.file.html import HtmlFile
|
||||||
from nw.convert.file.markdown import MarkdownFile
|
from nw.convert.file.markdown import MarkdownFile
|
||||||
from nw.convert.file.latex import LaTeXFile
|
from nw.convert.file.latex import LaTeXFile
|
||||||
from nw.convert.file.concat import ConcatFile
|
from nw.convert.file.concat import ConcatFile
|
||||||
from nw.common import packageRefURL
|
from nw.common import packageRefURL
|
||||||
from nw.constants import nwFiles
|
from nw.constants import nwFiles
|
||||||
from nw.enum import nwItemType, nwAlert
|
from nw.enum import nwItemType, nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -201,9 +202,10 @@ class GuiExport(QDialog):
|
|||||||
# Check that encoding was successful
|
# Check that encoding was successful
|
||||||
if outFile.texCodecFail:
|
if outFile.texCodecFail:
|
||||||
self.theParent.makeAlert((
|
self.theParent.makeAlert((
|
||||||
"Failed to escape unicode characters while writing LaTeX file. The generated "
|
"Failed to escape unicode characters while writing LaTeX "
|
||||||
".tex file may not build properly. Make sure the python package '{package:s}' "
|
"file. The generated .tex file may not build properly. "
|
||||||
"is installed and working."
|
"Make sure the python package '{package:s}' is installed "
|
||||||
|
"and working."
|
||||||
).format(
|
).format(
|
||||||
package = packageRefURL("latexcodec")
|
package = packageRefURL("latexcodec")
|
||||||
), nwAlert.WARN)
|
), nwAlert.WARN)
|
||||||
@@ -343,27 +345,31 @@ class GuiExportMain(QWidget):
|
|||||||
}
|
}
|
||||||
FMT_HELP = {
|
FMT_HELP = {
|
||||||
FMT_NWD : (
|
FMT_NWD : (
|
||||||
"Exports a document using the novelWriter markdown format. The files selected by the "
|
"Exports a document using the novelWriter markdown format. "
|
||||||
"filters are appended as-is, including comments and other settings."
|
"The files selected by the filters are appended as-is, "
|
||||||
|
"including comments and other settings."
|
||||||
),
|
),
|
||||||
FMT_TXT : (
|
FMT_TXT : (
|
||||||
"Exports a plain text file. All formatting is stripped and comments are in square "
|
"Exports a plain text file. All formatting is stripped and "
|
||||||
"brackets."
|
"comments are in square brackets."
|
||||||
),
|
),
|
||||||
FMT_MD : (
|
FMT_MD : (
|
||||||
"Exports a standard markdown file. Comments are converted to preformatted text blocks."
|
"Exports a standard markdown file. Comments are converted "
|
||||||
|
"to preformatted text blocks."
|
||||||
),
|
),
|
||||||
FMT_HTML : (
|
FMT_HTML : (
|
||||||
"Exports a plain html5 file. Comments are wrapped in blocks with a yellow background "
|
"Exports a plain html5 file. Comments are wrapped in "
|
||||||
"colour."
|
"blocks with a yellow background colour."
|
||||||
),
|
),
|
||||||
FMT_TEX : (
|
FMT_TEX : (
|
||||||
"Exports a LaTeX file that can be compiled to PDF using for instance PDFLaTeX. "
|
"Exports a LaTeX file that can be compiled to PDF using "
|
||||||
"Comments are exported as LaTeX comments."
|
"for instance PDFLaTeX. Comments are exported as LaTeX "
|
||||||
|
"comments."
|
||||||
),
|
),
|
||||||
FMT_PDOC : (
|
FMT_PDOC : (
|
||||||
"Exports first to markdown or html5. The file is then passed on to Pandoc for a second "
|
"Exports first to markdown or html5. The file is then "
|
||||||
"stage. Use the Pandoc tab for settings up the conversion."
|
"passed on to Pandoc for a second stage. Use the Pandoc "
|
||||||
|
"tab for settings up the conversion."
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +516,9 @@ class GuiExportMain(QWidget):
|
|||||||
self.fixedWidth.setMaximum(999)
|
self.fixedWidth.setMaximum(999)
|
||||||
self.fixedWidth.setSingleStep(1)
|
self.fixedWidth.setSingleStep(1)
|
||||||
self.fixedWidth.setValue(self.optState.getSetting("fixWidth"))
|
self.fixedWidth.setValue(self.optState.getSetting("fixWidth"))
|
||||||
self.fixedWidth.setToolTip("Applies to .txt and .md files. A value of '0' disables the feature.")
|
self.fixedWidth.setToolTip(
|
||||||
|
"Applies to .txt and .md files. A value of '0' disables the feature."
|
||||||
|
)
|
||||||
|
|
||||||
self.addSettingsForm.addWidget(QLabel("Fixed width"), 0, 0)
|
self.addSettingsForm.addWidget(QLabel("Fixed width"), 0, 0)
|
||||||
self.addSettingsForm.addWidget(self.fixedWidth, 0, 1)
|
self.addSettingsForm.addWidget(self.fixedWidth, 0, 1)
|
||||||
@@ -535,7 +543,8 @@ class GuiExportMain(QWidget):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def _updateFormat(self, currIdx):
|
def _updateFormat(self, currIdx):
|
||||||
"""Update help text under output format selection and file extension in file box
|
"""Update help text under output format selection and file
|
||||||
|
extension in file box
|
||||||
"""
|
"""
|
||||||
if currIdx == -1:
|
if currIdx == -1:
|
||||||
self.outputHelp.setText("")
|
self.outputHelp.setText("")
|
||||||
@@ -563,7 +572,8 @@ class GuiExportMain(QWidget):
|
|||||||
dlgOpt = QFileDialog.Options()
|
dlgOpt = QFileDialog.Options()
|
||||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||||
saveTo = QFileDialog.getSaveFileName(
|
saveTo = QFileDialog.getSaveFileName(
|
||||||
self,"Export File",self.exportPath.text(),options=dlgOpt,filter=";;".join(extFilter)
|
self, "Export File", self.exportPath.text(),
|
||||||
|
options=dlgOpt, filter=";;".join(extFilter)
|
||||||
)
|
)
|
||||||
if saveTo:
|
if saveTo:
|
||||||
self.exportPath.setText(saveTo[0])
|
self.exportPath.setText(saveTo[0])
|
||||||
@@ -655,7 +665,6 @@ class GuiExportPandoc(QWidget):
|
|||||||
self.outputFormat.addItem("ePUB eBook v2 (.epub2)", self.FMT_EPUB2)
|
self.outputFormat.addItem("ePUB eBook v2 (.epub2)", self.FMT_EPUB2)
|
||||||
self.outputFormat.addItem("ePUB eBook v3 (.epub3)", self.FMT_EPUB3)
|
self.outputFormat.addItem("ePUB eBook v3 (.epub3)", self.FMT_EPUB3)
|
||||||
self.outputFormat.addItem("Zim Wiki (.txt)", self.FMT_ZIM)
|
self.outputFormat.addItem("Zim Wiki (.txt)", self.FMT_ZIM)
|
||||||
# self.outputFormat.currentIndexChanged.connect(self._updateFormat)
|
|
||||||
|
|
||||||
optIdx = self.outputFormat.findData(self.optState.getSetting("pFormat"))
|
optIdx = self.outputFormat.findData(self.optState.getSetting("pFormat"))
|
||||||
if optIdx == -1:
|
if optIdx == -1:
|
||||||
@@ -671,9 +680,6 @@ class GuiExportPandoc(QWidget):
|
|||||||
self.outerBox.addWidget(self.guiInfo, 0, 0)
|
self.outerBox.addWidget(self.guiInfo, 0, 0)
|
||||||
self.outerBox.addWidget(self.guiOutput, 1, 0)
|
self.outerBox.addWidget(self.guiOutput, 1, 0)
|
||||||
self.outerBox.setRowStretch(2, 1)
|
self.outerBox.setRowStretch(2, 1)
|
||||||
# self.outerBox.setColumnStretch(0, 1)
|
|
||||||
# self.outerBox.setColumnStretch(1, 1)
|
|
||||||
# self.outerBox.setColumnStretch(2, 1)
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -13,14 +13,17 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtWidgets import QDialog, QHBoxLayout, QVBoxLayout, QGroupBox, QFormLayout, QLineEdit, QPushButton, QComboBox
|
from PyQt5.QtSvg import QSvgWidget
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialog, QHBoxLayout, QVBoxLayout, QGroupBox, QFormLayout,
|
||||||
|
QLineEdit, QPushButton, QComboBox
|
||||||
|
)
|
||||||
|
|
||||||
from nw.enum import nwItemLayout, nwItemClass, nwItemType
|
from nw.enum import nwItemLayout, nwItemClass, nwItemType
|
||||||
from nw.constants import nwLabels
|
from nw.constants import nwLabels
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -36,8 +39,8 @@ class GuiItemEditor(QDialog):
|
|||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.theItem = self.theProject.getItem(tHandle)
|
self.theItem = self.theProject.getItem(tHandle)
|
||||||
|
|
||||||
self.outerBox = QHBoxLayout()
|
self.outerBox = QHBoxLayout()
|
||||||
self.innerBox = QVBoxLayout()
|
self.innerBox = QVBoxLayout()
|
||||||
|
|
||||||
self.setWindowTitle("Item Settings")
|
self.setWindowTitle("Item Settings")
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ import nw
|
|||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel
|
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt5.QtSvg import QSvgWidget
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel,
|
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit,
|
||||||
QWidget, QTabWidget, QDialogButtonBox, QListWidget, QListWidgetItem, QPushButton,
|
QLabel, QWidget, QTabWidget, QDialogButtonBox, QListWidget,
|
||||||
QColorDialog, QAbstractItemView, QTreeWidget, QTreeWidgetItem, QCheckBox
|
QListWidgetItem, QPushButton, QColorDialog, QAbstractItemView, QTreeWidget,
|
||||||
|
QTreeWidgetItem, QCheckBox
|
||||||
)
|
)
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
|
|
||||||
@@ -173,7 +174,7 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
for iName, iCol, nUse in self.theStatus:
|
for iName, iCol, nUse in self.theStatus:
|
||||||
self._addItem(iName, iCol, iName, nUse)
|
self._addItem(iName, iCol, iName, nUse)
|
||||||
|
|
||||||
self.editName = QLineEdit()
|
self.editName = QLineEdit()
|
||||||
self.editName.setEnabled(False)
|
self.editName.setEnabled(False)
|
||||||
self.newButton = QPushButton("New")
|
self.newButton = QPushButton("New")
|
||||||
self.delButton = QPushButton("Delete")
|
self.delButton = QPushButton("Delete")
|
||||||
@@ -221,7 +222,9 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
def _selectColour(self):
|
def _selectColour(self):
|
||||||
logger.verbose("Item colour button clicked")
|
logger.verbose("Item colour button clicked")
|
||||||
if self.selColour is not None:
|
if self.selColour is not None:
|
||||||
newCol = QColorDialog.getColor(self.selColour, self, "Select Colour", QColorDialog.DontUseNativeDialog)
|
newCol = QColorDialog.getColor(
|
||||||
|
self.selColour, self, "Select Colour", QColorDialog.DontUseNativeDialog
|
||||||
|
)
|
||||||
if newCol:
|
if newCol:
|
||||||
self.selColour = newCol
|
self.selColour = newCol
|
||||||
colPixmap = QPixmap(16,16)
|
colPixmap = QPixmap(16,16)
|
||||||
|
|||||||
@@ -13,18 +13,20 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from PyQt5.QtCore import Qt
|
|
||||||
from PyQt5.QtGui import QIcon, QColor, QPixmap, QFont
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtGui import QIcon, QColor, QPixmap, QFont
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QVBoxLayout, QHBoxLayout, QTreeWidget, QTreeWidgetItem, QDialogButtonBox, QHeaderView,
|
QDialog, QVBoxLayout, QHBoxLayout, QTreeWidget, QTreeWidgetItem,
|
||||||
QGridLayout, QLabel, QGroupBox, QCheckBox
|
QDialogButtonBox, QHeaderView, QGridLayout, QLabel, QGroupBox,
|
||||||
|
QCheckBox
|
||||||
)
|
)
|
||||||
|
|
||||||
from nw.tools.optlaststate import OptLastState
|
from nw.tools.optlaststate import OptLastState
|
||||||
from nw.constants import nwConst, nwFiles
|
from nw.constants import nwConst, nwFiles
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -44,16 +46,22 @@ class GuiSessionLogView(QDialog):
|
|||||||
self.timeFilter = 0.0
|
self.timeFilter = 0.0
|
||||||
self.timeTotal = 0.0
|
self.timeTotal = 0.0
|
||||||
|
|
||||||
self.outerBox = QGridLayout()
|
self.outerBox = QGridLayout()
|
||||||
self.bottomBox = QHBoxLayout()
|
self.bottomBox = QHBoxLayout()
|
||||||
|
|
||||||
self.setWindowTitle("Session Log")
|
self.setWindowTitle("Session Log")
|
||||||
self.setMinimumWidth(420)
|
self.setMinimumWidth(420)
|
||||||
self.setMinimumHeight(400)
|
self.setMinimumHeight(400)
|
||||||
|
|
||||||
widthCol0 = self.optState.validIntRange(self.optState.getSetting("widthCol0"), 30, 999, 180)
|
widthCol0 = self.optState.validIntRange(
|
||||||
widthCol1 = self.optState.validIntRange(self.optState.getSetting("widthCol1"), 30, 999, 80)
|
self.optState.getSetting("widthCol0"), 30, 999, 180
|
||||||
widthCol2 = self.optState.validIntRange(self.optState.getSetting("widthCol2"), 30, 999, 80)
|
)
|
||||||
|
widthCol1 = self.optState.validIntRange(
|
||||||
|
self.optState.getSetting("widthCol1"), 30, 999, 80
|
||||||
|
)
|
||||||
|
widthCol2 = self.optState.validIntRange(
|
||||||
|
self.optState.getSetting("widthCol2"), 30, 999, 80
|
||||||
|
)
|
||||||
|
|
||||||
self.listBox = QTreeWidget()
|
self.listBox = QTreeWidget()
|
||||||
self.listBox.setHeaderLabels(["Session Start","Length","Words",""])
|
self.listBox.setHeaderLabels(["Session Start","Length","Words",""])
|
||||||
@@ -70,8 +78,12 @@ class GuiSessionLogView(QDialog):
|
|||||||
self.monoFont = QFont("Monospace",10)
|
self.monoFont = QFont("Monospace",10)
|
||||||
|
|
||||||
sortValid = (Qt.AscendingOrder, Qt.DescendingOrder)
|
sortValid = (Qt.AscendingOrder, Qt.DescendingOrder)
|
||||||
sortCol = self.optState.validIntRange(self.optState.getSetting("sortCol"), 0, 2, 0)
|
sortCol = self.optState.validIntRange(
|
||||||
sortOrder = self.optState.validIntTuple(self.optState.getSetting("sortOrder"), sortValid, Qt.DescendingOrder)
|
self.optState.getSetting("sortCol"), 0, 2, 0
|
||||||
|
)
|
||||||
|
sortOrder = self.optState.validIntTuple(
|
||||||
|
self.optState.getSetting("sortOrder"), sortValid, Qt.DescendingOrder
|
||||||
|
)
|
||||||
|
|
||||||
self.listBox.sortByColumn(sortCol, sortOrder)
|
self.listBox.sortByColumn(sortCol, sortOrder)
|
||||||
self.listBox.setSortingEnabled(True)
|
self.listBox.setSortingEnabled(True)
|
||||||
@@ -81,7 +93,7 @@ class GuiSessionLogView(QDialog):
|
|||||||
self.infoBoxForm = QGridLayout(self)
|
self.infoBoxForm = QGridLayout(self)
|
||||||
self.infoBox.setLayout(self.infoBoxForm)
|
self.infoBox.setLayout(self.infoBoxForm)
|
||||||
|
|
||||||
self.labelTotal = QLabel(self._formatTime(0))
|
self.labelTotal = QLabel(self._formatTime(0))
|
||||||
self.labelTotal.setFont(self.monoFont)
|
self.labelTotal.setFont(self.monoFont)
|
||||||
self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
|
self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
|
||||||
|
|
||||||
@@ -152,11 +164,11 @@ class GuiSessionLogView(QDialog):
|
|||||||
inData = inLine.split()
|
inData = inLine.split()
|
||||||
if len(inData) != 8:
|
if len(inData) != 8:
|
||||||
continue
|
continue
|
||||||
dStart = datetime.strptime("%s %s" % (inData[1],inData[2]),nwConst.tStampFmt)
|
dStart = datetime.strptime("%s %s" % (inData[1],inData[2]),nwConst.tStampFmt)
|
||||||
dEnd = datetime.strptime("%s %s" % (inData[4],inData[5]),nwConst.tStampFmt)
|
dEnd = datetime.strptime("%s %s" % (inData[4],inData[5]),nwConst.tStampFmt)
|
||||||
nWords = int(inData[7])
|
nWords = int(inData[7])
|
||||||
tDiff = dEnd - dStart
|
tDiff = dEnd - dStart
|
||||||
sDiff = tDiff.total_seconds()
|
sDiff = tDiff.total_seconds()
|
||||||
|
|
||||||
self.timeTotal += sDiff
|
self.timeTotal += sDiff
|
||||||
if abs(nWords) > 0:
|
if abs(nWords) > 0:
|
||||||
|
|||||||
@@ -13,17 +13,19 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
from PyQt5.QtCore import Qt
|
|
||||||
from PyQt5.QtGui import QIcon, QColor, QPixmap
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtGui import QIcon, QColor, QPixmap
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QDialogButtonBox, QLabel,
|
QDialog, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem,
|
||||||
QPushButton, QHeaderView, QGridLayout, QGroupBox, QCheckBox
|
QDialogButtonBox, QLabel, QPushButton, QHeaderView, QGridLayout,
|
||||||
|
QGroupBox, QCheckBox
|
||||||
)
|
)
|
||||||
|
|
||||||
from nw.tools.optlaststate import OptLastState
|
from nw.tools.optlaststate import OptLastState
|
||||||
from nw.constants import nwFiles
|
from nw.constants import nwFiles
|
||||||
from nw.enum import nwItemClass
|
from nw.enum import nwItemClass
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -41,21 +43,25 @@ class GuiTimeLineView(QDialog):
|
|||||||
self.optState = TimeLineLastState(self.theProject,nwFiles.TLINE_OPT)
|
self.optState = TimeLineLastState(self.theProject,nwFiles.TLINE_OPT)
|
||||||
self.optState.loadSettings()
|
self.optState.loadSettings()
|
||||||
|
|
||||||
self.theMatrix = {}
|
self.theMatrix = {}
|
||||||
self.numRows = 0
|
self.numRows = 0
|
||||||
self.numCols = 0
|
self.numCols = 0
|
||||||
|
|
||||||
self.outerBox = QVBoxLayout()
|
self.outerBox = QVBoxLayout()
|
||||||
self.filterBox = QVBoxLayout()
|
self.filterBox = QVBoxLayout()
|
||||||
self.centreBox = QHBoxLayout()
|
self.centreBox = QHBoxLayout()
|
||||||
self.bottomBox = QHBoxLayout()
|
self.bottomBox = QHBoxLayout()
|
||||||
|
|
||||||
self.setWindowTitle("Timeline View")
|
self.setWindowTitle("Timeline View")
|
||||||
self.setMinimumWidth(700)
|
self.setMinimumWidth(700)
|
||||||
self.setMinimumHeight(400)
|
self.setMinimumHeight(400)
|
||||||
|
|
||||||
winWidth = self.optState.validIntRange(self.optState.getSetting("winWidth"), 700, 10000, 700)
|
winWidth = self.optState.validIntRange(
|
||||||
winHeight = self.optState.validIntRange(self.optState.getSetting("winHeight"), 400, 10000, 400)
|
self.optState.getSetting("winWidth"), 700, 10000, 700
|
||||||
|
)
|
||||||
|
winHeight = self.optState.validIntRange(
|
||||||
|
self.optState.getSetting("winHeight"), 400, 10000, 400
|
||||||
|
)
|
||||||
self.resize(winWidth,winHeight)
|
self.resize(winWidth,winHeight)
|
||||||
|
|
||||||
# TimeLine Table
|
# TimeLine Table
|
||||||
|
|||||||
@@ -13,10 +13,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from PyQt5.QtGui import QFont
|
from PyQt5.QtGui import QFont
|
||||||
from PyQt5.QtWidgets import QFrame, QGridLayout, QLabel
|
from PyQt5.QtWidgets import QFrame, QGridLayout, QLabel
|
||||||
|
|
||||||
from nw.constants import nwLabels
|
from nw.constants import nwLabels
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -32,7 +32,6 @@ class GuiDocDetails(QFrame):
|
|||||||
|
|
||||||
logger.debug("Initialising DocDetails ...")
|
logger.debug("Initialising DocDetails ...")
|
||||||
self.mainConf = nw.CONFIG
|
self.mainConf = nw.CONFIG
|
||||||
self.debugGUI = self.mainConf.debugGUI
|
|
||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
|
|
||||||
|
|||||||
+120
-69
@@ -15,18 +15,21 @@ import nw
|
|||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QTimer, QSizeF
|
from PyQt5.QtCore import Qt, QTimer, QSizeF
|
||||||
from PyQt5.QtWidgets import qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox
|
from PyQt5.QtWidgets import (
|
||||||
from PyQt5.QtGui import (
|
qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox
|
||||||
QTextCursor, QTextOption, QIcon, QKeySequence, QFont, QColor, QPalette, QTextDocument,
|
)
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QTextCursor, QTextOption, QIcon, QKeySequence, QFont, QColor,
|
||||||
|
QPalette, QTextDocument,
|
||||||
)
|
)
|
||||||
|
|
||||||
from nw.project.document import NWDoc
|
from nw.project.document import NWDoc
|
||||||
from nw.gui.tools.dochighlight import GuiDocHighlighter
|
from nw.gui.tools.dochighlight import GuiDocHighlighter
|
||||||
from nw.gui.tools.wordcounter import WordCounter
|
from nw.gui.tools.wordcounter import WordCounter
|
||||||
from nw.tools.spellcheck import NWSpellCheck
|
from nw.tools.spellcheck import NWSpellCheck
|
||||||
from nw.constants import nwFiles, nwUnicode
|
from nw.constants import nwFiles, nwUnicode
|
||||||
from nw.enum import nwDocAction, nwAlert
|
from nw.enum import nwDocAction, nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -82,9 +85,24 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.setAcceptRichText(False)
|
self.setAcceptRichText(False)
|
||||||
|
|
||||||
# Custom Shortcuts
|
# Custom Shortcuts
|
||||||
QShortcut(QKeySequence("Ctrl+."), self, context=Qt.WidgetShortcut, activated=self._openSpellContext)
|
QShortcut(
|
||||||
QShortcut(Qt.Key_Return | Qt.ControlModifier, self, context=Qt.WidgetShortcut, activated=self._followTag)
|
QKeySequence("Ctrl+."),
|
||||||
QShortcut(Qt.Key_Enter | Qt.ControlModifier, self, context=Qt.WidgetShortcut, activated=self._followTag)
|
self,
|
||||||
|
context=Qt.WidgetShortcut,
|
||||||
|
activated=self._openSpellContext
|
||||||
|
)
|
||||||
|
QShortcut(
|
||||||
|
Qt.Key_Return | Qt.ControlModifier,
|
||||||
|
self,
|
||||||
|
context=Qt.WidgetShortcut,
|
||||||
|
activated=self._followTag
|
||||||
|
)
|
||||||
|
QShortcut(
|
||||||
|
Qt.Key_Enter | Qt.ControlModifier,
|
||||||
|
self,
|
||||||
|
context=Qt.WidgetShortcut,
|
||||||
|
activated=self._followTag
|
||||||
|
)
|
||||||
|
|
||||||
# Set Up Word Count Thread and Timer
|
# Set Up Word Count Thread and Timer
|
||||||
self.wcInterval = self.mainConf.wordCountTimer
|
self.wcInterval = self.mainConf.wordCountTimer
|
||||||
@@ -121,9 +139,9 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def initEditor(self):
|
def initEditor(self):
|
||||||
"""Initialise or re-initialise the editor with the user's settings.
|
"""Initialise or re-initialise the editor with the user's
|
||||||
This function is both called when the editor is created, and when the user changes the
|
settings. This function is both called when the editor is
|
||||||
main editor preferences.
|
created, and when the user changes the main editor preferences.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Reload dictionaries
|
# Reload dictionaries
|
||||||
@@ -164,10 +182,12 @@ class GuiDocEditor(QTextEdit):
|
|||||||
# Initialise the syntax highlighter
|
# Initialise the syntax highlighter
|
||||||
self.hLight.initHighlighter()
|
self.hLight.initHighlighter()
|
||||||
|
|
||||||
# If we have a document open, we should reload it in case the font changed, otherwise
|
# If we have a document open, we should reload it in case the
|
||||||
# we just clear the editor entirely, which makes it read only.
|
# font changed, otherwise we just clear the editor entirely,
|
||||||
|
# which makes it read only.
|
||||||
if self.theHandle is not None:
|
if self.theHandle is not None:
|
||||||
# We must save the current handle as clearEditor() sets it to None
|
# We must save the current handle as clearEditor() sets it
|
||||||
|
# to None
|
||||||
tHandle = self.theHandle
|
tHandle = self.theHandle
|
||||||
self.clearEditor()
|
self.clearEditor()
|
||||||
self.loadText(tHandle)
|
self.loadText(tHandle)
|
||||||
@@ -178,11 +198,13 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def loadText(self, tHandle):
|
def loadText(self, tHandle):
|
||||||
"""Load text from a document into the editor. If we have an io error, we must handle this
|
"""Load text from a document into the editor. If we have an io
|
||||||
and clear the editor so that we don't risk overwriting the file if it exists. This can for
|
error, we must handle this and clear the editor so that we don't
|
||||||
instance happen of the file contains binary elements or an encoding that novelWriter does
|
risk overwriting the file if it exists. This can for instance
|
||||||
not support. If load is successful, ot the document is new (empty string) we set up the
|
happen of the file contains binary elements or an encoding that
|
||||||
editor for editing the file.
|
novelWriter does not support. If load is successful, or the
|
||||||
|
document is new (empty string) we set up the editor for editing
|
||||||
|
the file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
theDoc = self.nwDocument.openDocument(tHandle)
|
theDoc = self.nwDocument.openDocument(tHandle)
|
||||||
@@ -236,8 +258,9 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return self.docChanged
|
return self.docChanged
|
||||||
|
|
||||||
def getText(self):
|
def getText(self):
|
||||||
"""Get the text content of the current document. This method uses QTextEdit->toPlainText for
|
"""Get the text content of the current document. This method
|
||||||
Qt versions lower than 5.9, and the QDocument->toRawText for higher version. The latter
|
uses QTextEdit->toPlainText for Qt versions lower than 5.9, and
|
||||||
|
the QDocument->toRawText for higher version. The latter
|
||||||
preserves non-breaking spaces, which the former does not.
|
preserves non-breaking spaces, which the former does not.
|
||||||
"""
|
"""
|
||||||
if self.mainConf.verQtValue >= 50900:
|
if self.mainConf.verQtValue >= 50900:
|
||||||
@@ -281,8 +304,8 @@ class GuiDocEditor(QTextEdit):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def changeWidth(self):
|
def changeWidth(self):
|
||||||
"""Automatically adjust the margins so the text is centred, but only if Config.textFixedW is
|
"""Automatically adjust the margins so the text is centred, but
|
||||||
set to True.
|
only if Config.textFixedW is set to True.
|
||||||
"""
|
"""
|
||||||
if self.mainConf.textFixedW:
|
if self.mainConf.textFixedW:
|
||||||
vBar = self.verticalScrollBar()
|
vBar = self.verticalScrollBar()
|
||||||
@@ -307,23 +330,40 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if not self.theParent.hasProject:
|
if not self.theParent.hasProject:
|
||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
if theAction == nwDocAction.UNDO: self.undo()
|
if theAction == nwDocAction.UNDO:
|
||||||
elif theAction == nwDocAction.REDO: self.redo()
|
self.undo()
|
||||||
elif theAction == nwDocAction.CUT: self.cut()
|
elif theAction == nwDocAction.REDO:
|
||||||
elif theAction == nwDocAction.COPY: self.copy()
|
self.redo()
|
||||||
elif theAction == nwDocAction.PASTE: self.paste()
|
elif theAction == nwDocAction.CUT:
|
||||||
elif theAction == nwDocAction.BOLD: self._wrapSelection("**","**")
|
self.cut()
|
||||||
elif theAction == nwDocAction.ITALIC: self._wrapSelection("_","_")
|
elif theAction == nwDocAction.COPY:
|
||||||
elif theAction == nwDocAction.U_LINE: self._wrapSelection("__","__")
|
self.copy()
|
||||||
elif theAction == nwDocAction.S_QUOTE: self._wrapSelection(self.typSQOpen,self.typSQClose)
|
elif theAction == nwDocAction.PASTE:
|
||||||
elif theAction == nwDocAction.D_QUOTE: self._wrapSelection(self.typDQOpen,self.typDQClose)
|
self.paste()
|
||||||
elif theAction == nwDocAction.SEL_ALL: self._makeSelection(QTextCursor.Document)
|
elif theAction == nwDocAction.BOLD:
|
||||||
elif theAction == nwDocAction.SEL_PARA: self._makeSelection(QTextCursor.BlockUnderCursor)
|
self._wrapSelection("**","**")
|
||||||
elif theAction == nwDocAction.FIND: self._beginSearch()
|
elif theAction == nwDocAction.ITALIC:
|
||||||
elif theAction == nwDocAction.REPLACE: self._beginReplace()
|
self._wrapSelection("_","_")
|
||||||
elif theAction == nwDocAction.GO_NEXT: self._findNext()
|
elif theAction == nwDocAction.U_LINE:
|
||||||
elif theAction == nwDocAction.GO_PREV: self._findPrev()
|
self._wrapSelection("__","__")
|
||||||
elif theAction == nwDocAction.REPL_NEXT: self._replaceNext()
|
elif theAction == nwDocAction.S_QUOTE:
|
||||||
|
self._wrapSelection(self.typSQOpen,self.typSQClose)
|
||||||
|
elif theAction == nwDocAction.D_QUOTE:
|
||||||
|
self._wrapSelection(self.typDQOpen,self.typDQClose)
|
||||||
|
elif theAction == nwDocAction.SEL_ALL:
|
||||||
|
self._makeSelection(QTextCursor.Document)
|
||||||
|
elif theAction == nwDocAction.SEL_PARA:
|
||||||
|
self._makeSelection(QTextCursor.BlockUnderCursor)
|
||||||
|
elif theAction == nwDocAction.FIND:
|
||||||
|
self._beginSearch()
|
||||||
|
elif theAction == nwDocAction.REPLACE:
|
||||||
|
self._beginReplace()
|
||||||
|
elif theAction == nwDocAction.GO_NEXT:
|
||||||
|
self._findNext()
|
||||||
|
elif theAction == nwDocAction.GO_PREV:
|
||||||
|
self._findPrev()
|
||||||
|
elif theAction == nwDocAction.REPL_NEXT:
|
||||||
|
self._replaceNext()
|
||||||
else:
|
else:
|
||||||
logger.error("Unknown or unsupported document action %s" % str(theAction))
|
logger.error("Unknown or unsupported document action %s" % str(theAction))
|
||||||
return False
|
return False
|
||||||
@@ -351,13 +391,15 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
def keyPressEvent(self, keyEvent):
|
def keyPressEvent(self, keyEvent):
|
||||||
"""Intercept key press events.
|
"""Intercept key press events.
|
||||||
We need to intercept key presses briefly to record the state of selection. This is in order
|
We need to intercept key presses briefly to record the state of
|
||||||
to know whether we had a selection prior to triggering the _docChange slot, as we do not
|
selection. This is in order to know whether we had a selection
|
||||||
want to trigger autoreplace on selections. Autoreplace on selections messes with undo/redo
|
prior to triggering the _docChange slot, as we do not want to
|
||||||
history.
|
trigger autoreplace on selections. Autoreplace on selections
|
||||||
We also need to intercept the Shift key modifier for certain key combinations that modifies
|
messes with undo/redo history.
|
||||||
standard keys like enter and space. However, we don't want to spend a lot of time in this
|
We also need to intercept the Shift key modifier for certain key
|
||||||
function as it is triggered on every keypress when typing.
|
combinations that modifies standard keys like enter and space.
|
||||||
|
However, we don't want to spend a lot of time in this function
|
||||||
|
as it is triggered on every keypress when typing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.hasSelection = self.textCursor().hasSelection()
|
self.hasSelection = self.textCursor().hasSelection()
|
||||||
@@ -378,8 +420,9 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def mouseReleaseEvent(self, mEvent):
|
def mouseReleaseEvent(self, mEvent):
|
||||||
"""If the mouse button is released and the control key is pressed, check if we're clicking
|
"""If the mouse button is released and the control key is
|
||||||
on a tag, and trigger the follow tag function.
|
pressed, check if we're clicking on a tag, and trigger the
|
||||||
|
follow tag function.
|
||||||
"""
|
"""
|
||||||
if qApp.keyboardModifiers() == Qt.ControlModifier:
|
if qApp.keyboardModifiers() == Qt.ControlModifier:
|
||||||
theCursor = self.cursorForPosition(mEvent.pos())
|
theCursor = self.cursorForPosition(mEvent.pos())
|
||||||
@@ -392,9 +435,11 @@ class GuiDocEditor(QTextEdit):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def _followTag(self, theCursor=None):
|
def _followTag(self, theCursor=None):
|
||||||
"""Activated by Ctrl+Enter. Checks that we're in a block starting with '@'. We then find the
|
"""Activated by Ctrl+Enter. Checks that we're in a block
|
||||||
word under the cursor and check that it is after the ':'. If all this is fine, we have a tag
|
starting with '@'. We then find the word under the cursor and
|
||||||
and can tell the document viewer to try and find and load the file where the tag is defined.
|
check that it is after the ':'. If all this is fine, we have a
|
||||||
|
tag and can tell the document viewer to try and find and load
|
||||||
|
the file where the tag is defined.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if theCursor is None:
|
if theCursor is None:
|
||||||
@@ -459,7 +504,9 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if len(theSuggest) > 0:
|
if len(theSuggest) > 0:
|
||||||
for aWord in theSuggest:
|
for aWord in theSuggest:
|
||||||
mnuWord = QAction(aWord, mnuSuggest)
|
mnuWord = QAction(aWord, mnuSuggest)
|
||||||
mnuWord.triggered.connect(lambda thePos, aWord=aWord : self._correctWord(theCursor, aWord))
|
mnuWord.triggered.connect(
|
||||||
|
lambda thePos, aWord=aWord : self._correctWord(theCursor, aWord)
|
||||||
|
)
|
||||||
mnuSuggest.addAction(mnuWord)
|
mnuSuggest.addAction(mnuWord)
|
||||||
mnuSuggest.addSeparator()
|
mnuSuggest.addSeparator()
|
||||||
mnuAdd = QAction("Add Word to Dictionary", mnuSuggest)
|
mnuAdd = QAction("Add Word to Dictionary", mnuSuggest)
|
||||||
@@ -557,7 +604,8 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _runCounter(self):
|
def _runCounter(self):
|
||||||
"""Decide whether to run the word counter, or stop the timer due to inactivity.
|
"""Decide whether to run the word counter, or stop the timer due
|
||||||
|
to inactivity.
|
||||||
"""
|
"""
|
||||||
sinceActive = time()-self.lastEdit
|
sinceActive = time()-self.lastEdit
|
||||||
if sinceActive > 5*self.wcInterval:
|
if sinceActive > 5*self.wcInterval:
|
||||||
@@ -586,9 +634,10 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _wrapSelection(self, tBefore, tAfter):
|
def _wrapSelection(self, tBefore, tAfter):
|
||||||
"""Wraps the selected text in whatever is in tBefore and tAfter. If there is no selection,
|
"""Wraps the selected text in whatever is in tBefore and tAfter.
|
||||||
the autoSelect setting decides the action. AutoSelect will select the word under the cursor
|
If there is no selection, the autoSelect setting decides the
|
||||||
before wrapping it. If this feature is disabled, nothing is done.
|
action. AutoSelect will select the word under the cursor before
|
||||||
|
wrapping it. If this feature is disabled, nothing is done.
|
||||||
"""
|
"""
|
||||||
theCursor = self.textCursor()
|
theCursor = self.textCursor()
|
||||||
if self.mainConf.autoSelect and not theCursor.hasSelection():
|
if self.mainConf.autoSelect and not theCursor.hasSelection():
|
||||||
@@ -626,18 +675,19 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _beginReplace(self):
|
def _beginReplace(self):
|
||||||
"""Opens the replace line of the search bar and sets the replace text.
|
"""Opens the replace line of the search bar and sets the replace
|
||||||
|
text.
|
||||||
"""
|
"""
|
||||||
self._beginSearch()
|
self._beginSearch()
|
||||||
self.theParent.searchBar.setReplaceText("")
|
self.theParent.searchBar.setReplaceText("")
|
||||||
return
|
return
|
||||||
|
|
||||||
def _findNext(self):
|
def _findNext(self):
|
||||||
"""Searches for the next occurrence of the search bar text in the document.
|
"""Searches for the next occurrence of the search bar text in
|
||||||
Wraps back to the top if not found.
|
the document. Wraps back to the top if not found.
|
||||||
"""
|
"""
|
||||||
searchFor = self.theParent.searchBar.getSearchText()
|
searchFor = self.theParent.searchBar.getSearchText()
|
||||||
wasFound = self.find(searchFor)
|
wasFound = self.find(searchFor)
|
||||||
if not wasFound:
|
if not wasFound:
|
||||||
theCursor = self.textCursor()
|
theCursor = self.textCursor()
|
||||||
theCursor.movePosition(QTextCursor.Start)
|
theCursor.movePosition(QTextCursor.Start)
|
||||||
@@ -645,8 +695,8 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _findPrev(self):
|
def _findPrev(self):
|
||||||
"""Searches for the previous occurrence of the search bar text in the document.
|
"""Searches for the previous occurrence of the search bar text
|
||||||
Wraps back to the end if not found.
|
in the document. Wraps back to the end if not found.
|
||||||
"""
|
"""
|
||||||
searchFor = self.theParent.searchBar.getSearchText()
|
searchFor = self.theParent.searchBar.getSearchText()
|
||||||
wasFound = self.find(searchFor, QTextDocument.FindBackward)
|
wasFound = self.find(searchFor, QTextDocument.FindBackward)
|
||||||
@@ -657,8 +707,9 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _replaceNext(self):
|
def _replaceNext(self):
|
||||||
"""Searches for the next occurrence of the search bar text in the document and replaces it
|
"""Searches for the next occurrence of the search bar text in
|
||||||
with the replace text. Wraps back to the top if not found.
|
the document and replaces it with the replace text. Wraps back
|
||||||
|
to the top if not found.
|
||||||
"""
|
"""
|
||||||
theCursor = self.textCursor()
|
theCursor = self.textCursor()
|
||||||
searchFor = self.theParent.searchBar.getSearchText()
|
searchFor = self.theParent.searchBar.getSearchText()
|
||||||
|
|||||||
+34
-26
@@ -13,13 +13,15 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtGui import QIcon, QFont, QColor
|
from PyQt5.QtGui import QIcon, QFont, QColor
|
||||||
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication
|
from PyQt5.QtWidgets import (
|
||||||
|
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication
|
||||||
|
)
|
||||||
|
|
||||||
from nw.project.item import NWItem
|
from nw.project.item import NWItem
|
||||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
from nw.constants import nwLabels
|
||||||
from nw.constants import nwLabels
|
from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -35,7 +37,6 @@ class GuiDocTree(QTreeWidget):
|
|||||||
|
|
||||||
logger.debug("Initialising DocTree ...")
|
logger.debug("Initialising DocTree ...")
|
||||||
self.mainConf = nw.CONFIG
|
self.mainConf = nw.CONFIG
|
||||||
self.debugGUI = self.mainConf.debugGUI
|
|
||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.theTheme = theParent.theTheme
|
self.theTheme = theParent.theTheme
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
@@ -52,8 +53,7 @@ class GuiDocTree(QTreeWidget):
|
|||||||
self.setIndentation(13)
|
self.setIndentation(13)
|
||||||
self.setColumnCount(4)
|
self.setColumnCount(4)
|
||||||
self.setHeaderLabels(["Label","Words","Flags","Handle"])
|
self.setHeaderLabels(["Label","Words","Flags","Handle"])
|
||||||
if not self.debugGUI:
|
self.hideColumn(self.C_HANDLE)
|
||||||
self.hideColumn(self.C_HANDLE)
|
|
||||||
|
|
||||||
treeHead = self.headerItem()
|
treeHead = self.headerItem()
|
||||||
treeHead.setTextAlignment(self.C_COUNT,Qt.AlignRight)
|
treeHead.setTextAlignment(self.C_COUNT,Qt.AlignRight)
|
||||||
@@ -131,7 +131,8 @@ class GuiDocTree(QTreeWidget):
|
|||||||
tHandle = self.theProject.newRoot(nwLabels.CLASS_NAME[itemClass], itemClass)
|
tHandle = self.theProject.newRoot(nwLabels.CLASS_NAME[itemClass], itemClass)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# If no parent has been selected, make the new file under the root NOVEL item.
|
# If no parent has been selected, make the new file under
|
||||||
|
# the root NOVEL item.
|
||||||
if pHandle is None:
|
if pHandle is None:
|
||||||
pHandle = self.theProject.findRootItem(nwItemClass.NOVEL)
|
pHandle = self.theProject.findRootItem(nwItemClass.NOVEL)
|
||||||
|
|
||||||
@@ -140,18 +141,23 @@ class GuiDocTree(QTreeWidget):
|
|||||||
logger.error("Did not find anywhere to add the item!")
|
logger.error("Did not find anywhere to add the item!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Now check if the selected item is a file, in which case the new file will be a sibling
|
# Now check if the selected item is a file, in which case
|
||||||
|
# the new file will be a sibling
|
||||||
pItem = self.theProject.getItem(pHandle)
|
pItem = self.theProject.getItem(pHandle)
|
||||||
if pItem.itemType == nwItemType.FILE:
|
if pItem.itemType == nwItemType.FILE:
|
||||||
pHandle = pItem.parHandle
|
pHandle = pItem.parHandle
|
||||||
|
|
||||||
# If we again has no home, give up
|
# If we again has no home, give up
|
||||||
if pHandle is None:
|
if pHandle is None:
|
||||||
self.makeAlert("Did not find anywhere to add the file or folder!", nwAlert.ERROR)
|
self.makeAlert(
|
||||||
|
"Did not find anywhere to add the file or folder!", nwAlert.ERROR
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if pHandle == self.theProject.trashRoot:
|
if pHandle == self.theProject.trashRoot:
|
||||||
self.makeAlert("Cannot add new files or folders to the trash folder.", nwAlert.ERROR)
|
self.makeAlert(
|
||||||
|
"Cannot add new files or folders to the trash folder.", nwAlert.ERROR
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# If we're still here, add the file or folder
|
# If we're still here, add the file or folder
|
||||||
@@ -175,8 +181,8 @@ class GuiDocTree(QTreeWidget):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def moveTreeItem(self, nStep):
|
def moveTreeItem(self, nStep):
|
||||||
"""Move an item up or down in the tree, but only if the treeView has focus. This also
|
"""Move an item up or down in the tree, but only if the treeView
|
||||||
applies when the menu is used.
|
has focus. This also applies when the menu is used.
|
||||||
"""
|
"""
|
||||||
if QApplication.focusWidget() == self and self.theParent.hasProject:
|
if QApplication.focusWidget() == self and self.theParent.hasProject:
|
||||||
tHandle = self.getSelectedHandle()
|
tHandle = self.getSelectedHandle()
|
||||||
@@ -223,10 +229,11 @@ class GuiDocTree(QTreeWidget):
|
|||||||
return retVals
|
return retVals
|
||||||
|
|
||||||
def deleteItem(self, tHandle=None):
|
def deleteItem(self, tHandle=None):
|
||||||
"""Delete items from the tree. Note that this does not delete the item from the item tree in
|
"""Delete items from the tree. Note that this does not delete
|
||||||
the project object. However, since this is only meta data, there isn't really a need to do
|
the item from the item tree in the project object. However,
|
||||||
that to save memory. Items not in the tree are not saved to the project file, so a loaded
|
since this is only meta data, there isn't really a need to do
|
||||||
project will be clean anyway.
|
that to save memory. Items not in the tree are not saved to the
|
||||||
|
project file, so a loaded project will be clean anyway.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
@@ -297,10 +304,10 @@ class GuiDocTree(QTreeWidget):
|
|||||||
tStatus += "."+nwLabels.LAYOUT_FLAG[nwItem.itemLayout]
|
tStatus += "."+nwLabels.LAYOUT_FLAG[nwItem.itemLayout]
|
||||||
iStatus = nwItem.itemStatus
|
iStatus = nwItem.itemStatus
|
||||||
if tClass == nwItemClass.NOVEL:
|
if tClass == nwItemClass.NOVEL:
|
||||||
iStatus = self.theProject.statusItems.checkEntry(iStatus) # Make sure it's a valid index
|
iStatus = self.theProject.statusItems.checkEntry(iStatus) # Make sure it's valid
|
||||||
flagIcon = self.theParent.statusIcons[iStatus]
|
flagIcon = self.theParent.statusIcons[iStatus]
|
||||||
else:
|
else:
|
||||||
iStatus = self.theProject.importItems.checkEntry(iStatus) # Make sure it's a valid index
|
iStatus = self.theProject.importItems.checkEntry(iStatus) # Make sure it's valid
|
||||||
flagIcon = self.theParent.importIcons[iStatus]
|
flagIcon = self.theParent.importIcons[iStatus]
|
||||||
|
|
||||||
trItem.setText(self.C_NAME, tName)
|
trItem.setText(self.C_NAME, tName)
|
||||||
@@ -451,8 +458,9 @@ class GuiDocTree(QTreeWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _updateItemParent(self, tHandle):
|
def _updateItemParent(self, tHandle):
|
||||||
"""Update the parent handle of an item so that the information in the project is consistent
|
"""Update the parent handle of an item so that the information
|
||||||
with the treeView. Also move the word count over to the new parent tree.
|
in the project is consistent with the treeView. Also move the
|
||||||
|
word count over to the new parent tree.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
trItemS = self._getTreeItem(tHandle)
|
trItemS = self._getTreeItem(tHandle)
|
||||||
@@ -494,8 +502,8 @@ class GuiDocTree(QTreeWidget):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def mousePressEvent(self, theEvent):
|
def mousePressEvent(self, theEvent):
|
||||||
"""Overload mousePressEvent to clear selection if clicking the mouse in a blank
|
"""Overload mousePressEvent to clear selection if clicking the
|
||||||
area of the tree view.
|
mouse in a blank area of the tree view.
|
||||||
"""
|
"""
|
||||||
QTreeWidget.mousePressEvent(self, theEvent)
|
QTreeWidget.mousePressEvent(self, theEvent)
|
||||||
selItem = self.indexAt(theEvent.pos())
|
selItem = self.indexAt(theEvent.pos())
|
||||||
@@ -504,8 +512,8 @@ class GuiDocTree(QTreeWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def dropEvent(self, theEvent):
|
def dropEvent(self, theEvent):
|
||||||
"""Overload the drop of dragged item event to check whether the drop is allowed
|
"""Overload the drop of dragged item event to check whether the
|
||||||
or not. Disallowed drops are cancelled.
|
drop is allowed or not. Disallowed drops are cancelled.
|
||||||
"""
|
"""
|
||||||
sHandle = self.getSelectedHandle()
|
sHandle = self.getSelectedHandle()
|
||||||
if sHandle is None:
|
if sHandle is None:
|
||||||
|
|||||||
@@ -13,13 +13,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QTextBrowser
|
from PyQt5.QtWidgets import QTextBrowser
|
||||||
from PyQt5.QtGui import QTextOption, QFont, QPalette, QColor
|
from PyQt5.QtGui import QTextOption, QFont, QPalette, QColor
|
||||||
|
|
||||||
from nw.convert.tokenizer import Tokenizer
|
from nw.convert.tokenizer import Tokenizer
|
||||||
from nw.convert.text.tohtml import ToHtml
|
from nw.convert.text.tohtml import ToHtml
|
||||||
from nw.enum import nwAlert, nwItemType
|
from nw.enum import nwAlert, nwItemType
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -205,24 +205,24 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
).format(
|
).format(
|
||||||
textSize = self.mainConf.textSize,
|
textSize = self.mainConf.textSize,
|
||||||
preSize = self.mainConf.textSize*0.9,
|
preSize = self.mainConf.textSize*0.9,
|
||||||
tColR = self.theTheme.colText[0],
|
tColR = self.theTheme.colText[0],
|
||||||
tColG = self.theTheme.colText[1],
|
tColG = self.theTheme.colText[1],
|
||||||
tColB = self.theTheme.colText[2],
|
tColB = self.theTheme.colText[2],
|
||||||
hColR = self.theTheme.colHead[0],
|
hColR = self.theTheme.colHead[0],
|
||||||
hColG = self.theTheme.colHead[1],
|
hColG = self.theTheme.colHead[1],
|
||||||
hColB = self.theTheme.colHead[2],
|
hColB = self.theTheme.colHead[2],
|
||||||
cColR = self.theTheme.colComm[0],
|
cColR = self.theTheme.colComm[0],
|
||||||
cColG = self.theTheme.colComm[1],
|
cColG = self.theTheme.colComm[1],
|
||||||
cColB = self.theTheme.colComm[2],
|
cColB = self.theTheme.colComm[2],
|
||||||
eColR = self.theTheme.colEmph[0],
|
eColR = self.theTheme.colEmph[0],
|
||||||
eColG = self.theTheme.colEmph[1],
|
eColG = self.theTheme.colEmph[1],
|
||||||
eColB = self.theTheme.colEmph[2],
|
eColB = self.theTheme.colEmph[2],
|
||||||
aColR = self.theTheme.colVal[0],
|
aColR = self.theTheme.colVal[0],
|
||||||
aColG = self.theTheme.colVal[1],
|
aColG = self.theTheme.colVal[1],
|
||||||
aColB = self.theTheme.colVal[2],
|
aColB = self.theTheme.colVal[2],
|
||||||
kColR = self.theTheme.colKey[0],
|
kColR = self.theTheme.colKey[0],
|
||||||
kColG = self.theTheme.colKey[1],
|
kColG = self.theTheme.colKey[1],
|
||||||
kColB = self.theTheme.colKey[2],
|
kColB = self.theTheme.colKey[2],
|
||||||
)
|
)
|
||||||
self.qDocument.setDefaultStyleSheet(styleSheet)
|
self.qDocument.setDefaultStyleSheet(styleSheet)
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QPalette, QColor
|
from PyQt5.QtGui import QPalette, QColor
|
||||||
from PyQt5.QtWidgets import QFrame, QHBoxLayout, QLabel, QPushButton
|
from PyQt5.QtWidgets import QFrame, QHBoxLayout, QLabel, QPushButton
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -36,7 +36,8 @@ class GuiNoticeBar(QFrame):
|
|||||||
self.mainBox = QHBoxLayout(self)
|
self.mainBox = QHBoxLayout(self)
|
||||||
self.mainBox.setContentsMargins(8,2,2,2)
|
self.mainBox.setContentsMargins(8,2,2,2)
|
||||||
|
|
||||||
self.noteLabel = QLabel("Hi there!")
|
self.noteLabel = QLabel("Hi there!")
|
||||||
|
|
||||||
self.closeButton = QPushButton(self.theTheme.getIcon("close"),"")
|
self.closeButton = QPushButton(self.theTheme.getIcon("close"),"")
|
||||||
self.closeButton.clicked.connect(self.hideNote)
|
self.closeButton.clicked.connect(self.hideNote)
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
from PyQt5.QtWidgets import QFrame, QGridLayout, QLabel, QLineEdit, QPushButton, QApplication
|
from PyQt5.QtWidgets import (
|
||||||
|
QFrame, QGridLayout, QLabel, QLineEdit, QPushButton, QApplication
|
||||||
|
)
|
||||||
|
|
||||||
from nw.enum import nwDocAction
|
from nw.enum import nwDocAction
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtGui import QFont
|
from PyQt5.QtGui import QFont
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget, QHBoxLayout, QVBoxLayout, QLabel, QGroupBox, QScrollArea, QFrame, QToolButton,
|
QWidget, QHBoxLayout, QVBoxLayout, QLabel, QGroupBox, QScrollArea, QFrame,
|
||||||
QSizePolicy, QCheckBox, QGridLayout
|
QToolButton, QSizePolicy, QCheckBox, QGridLayout
|
||||||
)
|
)
|
||||||
|
|
||||||
from nw.constants import nwLabels
|
from nw.constants import nwLabels
|
||||||
@@ -31,7 +31,6 @@ class GuiDocViewDetails(QWidget):
|
|||||||
|
|
||||||
logger.debug("Initialising DocViewDetails ...")
|
logger.debug("Initialising DocViewDetails ...")
|
||||||
self.mainConf = nw.CONFIG
|
self.mainConf = nw.CONFIG
|
||||||
self.debugGUI = self.mainConf.debugGUI
|
|
||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
self.currHandle = None
|
self.currHandle = None
|
||||||
|
|||||||
+12
-7
@@ -13,11 +13,11 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
from PyQt5.QtGui import QIcon, QDesktopServices
|
from PyQt5.QtGui import QIcon, QDesktopServices
|
||||||
from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox
|
from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox
|
||||||
|
|
||||||
from nw.enum import nwItemType, nwItemClass, nwDocAction
|
from nw.enum import nwItemType, nwItemClass, nwDocAction
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -78,7 +78,9 @@ class GuiMainMenu(QMenuBar):
|
|||||||
recentProject = self.mainConf.recentList[n]
|
recentProject = self.mainConf.recentList[n]
|
||||||
if recentProject == "": continue
|
if recentProject == "": continue
|
||||||
menuItem = QAction("%s" % recentProject, self.projMenu)
|
menuItem = QAction("%s" % recentProject, self.projMenu)
|
||||||
menuItem.triggered.connect(lambda menuItem, n=n : self.openRecentProject(menuItem, n))
|
menuItem.triggered.connect(
|
||||||
|
lambda menuItem, n=n : self.openRecentProject(menuItem, n)
|
||||||
|
)
|
||||||
self.recentMenu.addAction(menuItem)
|
self.recentMenu.addAction(menuItem)
|
||||||
|
|
||||||
self.recentMenu.addSeparator()
|
self.recentMenu.addSeparator()
|
||||||
@@ -122,8 +124,9 @@ class GuiMainMenu(QMenuBar):
|
|||||||
aboutMsg = (
|
aboutMsg = (
|
||||||
"<h3>About {name:s}</h3>"
|
"<h3>About {name:s}</h3>"
|
||||||
"<p>Version: {version:s}<br>Release Date: {date:s}</p>"
|
"<p>Version: {version:s}<br>Release Date: {date:s}</p>"
|
||||||
"<p>{name:s} is a markdown-like text editor designed for organising and writing novels. "
|
"<p>{name:s} is a markdown-like text editor designed for organising "
|
||||||
"It is written in Python 3 with a Qt5 GUI, using PyQt5</p>"
|
"and writing novels. It is written in Python 3 with a Qt5 GUI, "
|
||||||
|
"using PyQt5</p>"
|
||||||
"<p>{name:s} is licensed under GPL v3.0</p>"
|
"<p>{name:s} is licensed under GPL v3.0</p>"
|
||||||
"<p>{copyright:s}</p>"
|
"<p>{copyright:s}</p>"
|
||||||
"<p>Website: <a href='{website:s}'>{website:s}</a></p>"
|
"<p>Website: <a href='{website:s}'>{website:s}</a></p>"
|
||||||
@@ -353,7 +356,9 @@ class GuiMainMenu(QMenuBar):
|
|||||||
|
|
||||||
# Document > Show File Details
|
# Document > Show File Details
|
||||||
menuItem = QAction("Show File Details", self)
|
menuItem = QAction("Show File Details", self)
|
||||||
menuItem.setStatusTip("Shows a message box with the document location in the project folder")
|
menuItem.setStatusTip(
|
||||||
|
"Shows a message box with the document location in the project folder"
|
||||||
|
)
|
||||||
menuItem.triggered.connect(self._showDocumentLocation)
|
menuItem.triggered.connect(self._showDocumentLocation)
|
||||||
self.docuMenu.addAction(menuItem)
|
self.docuMenu.addAction(menuItem)
|
||||||
|
|
||||||
|
|||||||
+4
-16
@@ -13,9 +13,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
from PyQt5.QtCore import Qt, QTimer
|
|
||||||
from PyQt5.QtGui import QIcon, QColor, QPixmap
|
from PyQt5.QtCore import Qt, QTimer
|
||||||
|
from PyQt5.QtGui import QIcon, QColor, QPixmap
|
||||||
from PyQt5.QtWidgets import QStatusBar, QLabel, QFrame
|
from PyQt5.QtWidgets import QStatusBar, QLabel, QFrame
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -60,9 +61,6 @@ class GuiMainStatus(QStatusBar):
|
|||||||
self.docChanged.setFixedWidth(16)
|
self.docChanged.setFixedWidth(16)
|
||||||
self.docChanged.setToolTip("Document Changes Saved")
|
self.docChanged.setToolTip("Document Changes Saved")
|
||||||
|
|
||||||
self.boxDocHandle = QLabel()
|
|
||||||
self.boxDocHandle.setFrameStyle(QFrame.Panel | QFrame.Sunken);
|
|
||||||
|
|
||||||
# Add Them
|
# Add Them
|
||||||
self.addPermanentWidget(self.docChanged)
|
self.addPermanentWidget(self.docChanged)
|
||||||
self.addPermanentWidget(self.boxCounts)
|
self.addPermanentWidget(self.boxCounts)
|
||||||
@@ -70,8 +68,6 @@ class GuiMainStatus(QStatusBar):
|
|||||||
self.addPermanentWidget(self.projChanged)
|
self.addPermanentWidget(self.projChanged)
|
||||||
self.addPermanentWidget(self.boxStats)
|
self.addPermanentWidget(self.boxStats)
|
||||||
self.addPermanentWidget(self.boxTime)
|
self.addPermanentWidget(self.boxTime)
|
||||||
if self.mainConf.debugGUI:
|
|
||||||
self.addPermanentWidget(self.boxDocHandle)
|
|
||||||
|
|
||||||
self.setSizeGripEnabled(True)
|
self.setSizeGripEnabled(True)
|
||||||
|
|
||||||
@@ -90,7 +86,6 @@ class GuiMainStatus(QStatusBar):
|
|||||||
self.setRefTime(None)
|
self.setRefTime(None)
|
||||||
self.setStats(0,0)
|
self.setStats(0,0)
|
||||||
self.setCounts(0,0,0)
|
self.setCounts(0,0,0)
|
||||||
self.setDocHandle(None)
|
|
||||||
self.setProjectStatus(None)
|
self.setProjectStatus(None)
|
||||||
self.setDocumentStatus(None)
|
self.setDocumentStatus(None)
|
||||||
self._updateTime()
|
self._updateTime()
|
||||||
@@ -134,13 +129,6 @@ class GuiMainStatus(QStatusBar):
|
|||||||
self.boxCounts.setText("<b>Document:</b> {:d} : {:d} : {:d}".format(cC,wC,pC))
|
self.boxCounts.setText("<b>Document:</b> {:d} : {:d} : {:d}".format(cC,wC,pC))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setDocHandle(self, theHandle):
|
|
||||||
if theHandle is None:
|
|
||||||
self.boxDocHandle.setText("0000000000000")
|
|
||||||
else:
|
|
||||||
self.boxDocHandle.setText("%13s" % theHandle)
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import logging
|
|||||||
import nw
|
import nw
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QRegularExpression
|
from PyQt5.QtCore import Qt, QRegularExpression
|
||||||
from PyQt5.QtGui import QColor, QTextCharFormat, QFont, QSyntaxHighlighter, QBrush
|
from PyQt5.QtGui import (
|
||||||
|
QColor, QTextCharFormat, QFont, QSyntaxHighlighter, QBrush
|
||||||
|
)
|
||||||
|
|
||||||
from nw.constants import nwUnicode
|
from nw.constants import nwUnicode
|
||||||
|
|
||||||
@@ -38,18 +40,18 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|||||||
self.hRules = []
|
self.hRules = []
|
||||||
self.hStyles = {}
|
self.hStyles = {}
|
||||||
|
|
||||||
self.colHead = QColor(0,0,0)
|
self.colHead = QColor(0,0,0)
|
||||||
self.colHeadH = QColor(0,0,0)
|
self.colHeadH = QColor(0,0,0)
|
||||||
self.colEmph = QColor(0,0,0)
|
self.colEmph = QColor(0,0,0)
|
||||||
self.colDialN = QColor(0,0,0)
|
self.colDialN = QColor(0,0,0)
|
||||||
self.colDialD = QColor(0,0,0)
|
self.colDialD = QColor(0,0,0)
|
||||||
self.colDialS = QColor(0,0,0)
|
self.colDialS = QColor(0,0,0)
|
||||||
self.colComm = QColor(0,0,0)
|
self.colComm = QColor(0,0,0)
|
||||||
self.colKey = QColor(0,0,0)
|
self.colKey = QColor(0,0,0)
|
||||||
self.colVal = QColor(0,0,0)
|
self.colVal = QColor(0,0,0)
|
||||||
self.colSpell = QColor(0,0,0)
|
self.colSpell = QColor(0,0,0)
|
||||||
self.colTagErr = QColor(0,0,0)
|
self.colTagErr = QColor(0,0,0)
|
||||||
self.colRepTag = QColor(0,0,0)
|
self.colRepTag = QColor(0,0,0)
|
||||||
|
|
||||||
self.initHighlighter()
|
self.initHighlighter()
|
||||||
|
|
||||||
@@ -196,7 +198,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|||||||
))
|
))
|
||||||
|
|
||||||
self.hRules.append((
|
self.hRules.append((
|
||||||
"<(\S+?)>", {
|
r"<(\S+?)>", {
|
||||||
0 : self.hStyles["replace"],
|
0 : self.hStyles["replace"],
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|||||||
+83
-51
@@ -16,37 +16,37 @@ import nw
|
|||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QTimer
|
from PyQt5.QtCore import Qt, QTimer
|
||||||
from PyQt5.QtGui import QIcon, QPixmap, QColor
|
from PyQt5.QtGui import QIcon, QPixmap, QColor
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
qApp, QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog,
|
qApp, QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog,
|
||||||
QShortcut, QMessageBox, QProgressDialog, QDialog
|
QShortcut, QMessageBox, QProgressDialog, QDialog
|
||||||
)
|
)
|
||||||
|
|
||||||
from nw.gui.mainmenu import GuiMainMenu
|
from nw.gui.mainmenu import GuiMainMenu
|
||||||
from nw.gui.statusbar import GuiMainStatus
|
from nw.gui.statusbar import GuiMainStatus
|
||||||
from nw.gui.elements.doctree import GuiDocTree
|
from nw.gui.elements.doctree import GuiDocTree
|
||||||
from nw.gui.elements.doceditor import GuiDocEditor
|
from nw.gui.elements.doceditor import GuiDocEditor
|
||||||
from nw.gui.elements.docviewer import GuiDocViewer
|
from nw.gui.elements.docviewer import GuiDocViewer
|
||||||
from nw.gui.elements.docdetails import GuiDocDetails
|
from nw.gui.elements.docdetails import GuiDocDetails
|
||||||
from nw.gui.elements.searchbar import GuiSearchBar
|
from nw.gui.elements.searchbar import GuiSearchBar
|
||||||
from nw.gui.elements.noticebar import GuiNoticeBar
|
from nw.gui.elements.noticebar import GuiNoticeBar
|
||||||
from nw.gui.elements.viewdetails import GuiDocViewDetails
|
from nw.gui.elements.viewdetails import GuiDocViewDetails
|
||||||
from nw.gui.dialogs.configeditor import GuiConfigEditor
|
from nw.gui.dialogs.configeditor import GuiConfigEditor
|
||||||
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
from nw.gui.dialogs.projecteditor import GuiProjectEditor
|
||||||
from nw.gui.dialogs.export import GuiExport
|
from nw.gui.dialogs.export import GuiExport
|
||||||
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
from nw.gui.dialogs.itemeditor import GuiItemEditor
|
||||||
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
from nw.gui.dialogs.timelineview import GuiTimeLineView
|
||||||
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
from nw.gui.dialogs.sessionlog import GuiSessionLogView
|
||||||
from nw.project.project import NWProject
|
from nw.project.project import NWProject
|
||||||
from nw.project.document import NWDoc
|
from nw.project.document import NWDoc
|
||||||
from nw.project.item import NWItem
|
from nw.project.item import NWItem
|
||||||
from nw.project.index import NWIndex
|
from nw.project.index import NWIndex
|
||||||
from nw.project.backup import NWBackup
|
from nw.project.backup import NWBackup
|
||||||
from nw.tools.wordcount import countWords
|
from nw.tools.wordcount import countWords
|
||||||
from nw.theme import Theme
|
from nw.theme import Theme
|
||||||
from nw.enum import nwItemType, nwAlert
|
from nw.enum import nwItemType, nwAlert
|
||||||
from nw.constants import nwFiles
|
from nw.constants import nwFiles
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -63,10 +63,18 @@ class GuiMain(QMainWindow):
|
|||||||
self.theIndex = NWIndex(self.theProject, self)
|
self.theIndex = NWIndex(self.theProject, self)
|
||||||
self.hasProject = False
|
self.hasProject = False
|
||||||
|
|
||||||
logger.info("OS: %s" % (self.mainConf.osType))
|
logger.info("OS: %s" % (
|
||||||
logger.info("Qt5 Version: %s (%d)" % (self.mainConf.verQtString, self.mainConf.verQtValue))
|
self.mainConf.osType)
|
||||||
logger.info("PyQt5 Version: %s (%d)" % (self.mainConf.verPyQtString, self.mainConf.verPyQtValue))
|
)
|
||||||
logger.info("Python Version: %s (0x%x)" % (self.mainConf.verPyString, self.mainConf.verPyHexVal))
|
logger.info("Qt5 Version: %s (%d)" % (
|
||||||
|
self.mainConf.verQtString, self.mainConf.verQtValue)
|
||||||
|
)
|
||||||
|
logger.info("PyQt5 Version: %s (%d)" % (
|
||||||
|
self.mainConf.verPyQtString, self.mainConf.verPyQtValue)
|
||||||
|
)
|
||||||
|
logger.info("Python Version: %s (0x%x)" % (
|
||||||
|
self.mainConf.verPyString, self.mainConf.verPyHexVal)
|
||||||
|
)
|
||||||
|
|
||||||
self.resize(*self.mainConf.winGeometry)
|
self.resize(*self.mainConf.winGeometry)
|
||||||
self._setWindowTitle()
|
self._setWindowTitle()
|
||||||
@@ -157,11 +165,20 @@ class GuiMain(QMainWindow):
|
|||||||
self.asDocTimer.timeout.connect(self._autoSaveDocument)
|
self.asDocTimer.timeout.connect(self._autoSaveDocument)
|
||||||
|
|
||||||
# Keyboard Shortcuts
|
# Keyboard Shortcuts
|
||||||
QShortcut(Qt.Key_Return, self.treeView, context=Qt.WidgetShortcut, activated=self._treeKeyPressReturn)
|
QShortcut(
|
||||||
QShortcut(Qt.Key_Escape, self, activated=self._keyPressEscape)
|
Qt.Key_Return,
|
||||||
|
self.treeView,
|
||||||
|
context=Qt.WidgetShortcut,
|
||||||
|
activated=self._treeKeyPressReturn
|
||||||
|
)
|
||||||
|
QShortcut(
|
||||||
|
Qt.Key_Escape,
|
||||||
|
self,
|
||||||
|
activated=self._keyPressEscape
|
||||||
|
)
|
||||||
|
|
||||||
# Forward Functions
|
# Forward Functions
|
||||||
self.setStatus = self.statusBar.setStatus
|
self.setStatus = self.statusBar.setStatus
|
||||||
self.setProjectStatus = self.statusBar.setProjectStatus
|
self.setProjectStatus = self.statusBar.setProjectStatus
|
||||||
|
|
||||||
if self.mainConf.showGUI:
|
if self.mainConf.showGUI:
|
||||||
@@ -227,7 +244,8 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
def closeProject(self, isYes=False):
|
def closeProject(self, isYes=False):
|
||||||
"""Closes the project if one is open.
|
"""Closes the project if one is open.
|
||||||
isYes is passed on from the close application event so the user doesn't get prompted twice.
|
isYes is passed on from the close application event so the user
|
||||||
|
doesn't get prompted twice.
|
||||||
"""
|
"""
|
||||||
if not self.hasProject:
|
if not self.hasProject:
|
||||||
# There is no project loaded, everything OK
|
# There is no project loaded, everything OK
|
||||||
@@ -271,15 +289,17 @@ class GuiMain(QMainWindow):
|
|||||||
return saveOK
|
return saveOK
|
||||||
|
|
||||||
def openProject(self, projFile=None):
|
def openProject(self, projFile=None):
|
||||||
"""Open a project.
|
"""Open a project. The parameter projFile is passed from the
|
||||||
projFile is passed from the open recent projects menu, so can be set. If not, we pop the dialog.
|
open recent projects menu, so can be set. If not, we pop the
|
||||||
|
dialog.
|
||||||
"""
|
"""
|
||||||
if projFile is None:
|
if projFile is None:
|
||||||
projFile = self.openProjectDialog()
|
projFile = self.openProjectDialog()
|
||||||
if projFile is None:
|
if projFile is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Make sure any open project is cleared out first before we load another one
|
# Make sure any open project is cleared out first before we load
|
||||||
|
# another one
|
||||||
if not self.closeProject():
|
if not self.closeProject():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -415,20 +435,26 @@ class GuiMain(QMainWindow):
|
|||||||
with open(loadFile,mode="rt",encoding="utf8") as inFile:
|
with open(loadFile,mode="rt",encoding="utf8") as inFile:
|
||||||
theText = inFile.read()
|
theText = inFile.read()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.makeAlert(["Could not read file. The file cannot be a binary file.",str(e)], nwAlert.ERROR)
|
self.makeAlert(
|
||||||
|
["Could not read file. The file cannot be a binary file.",str(e)],
|
||||||
|
nwAlert.ERROR
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.docEditor.theHandle is None:
|
if self.docEditor.theHandle is None:
|
||||||
self.makeAlert(["Please open a document to import the text file into."], nwAlert.ERROR)
|
self.makeAlert(
|
||||||
|
["Please open a document to import the text file into."],
|
||||||
|
nwAlert.ERROR
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not self.docEditor.isEmpty():
|
if not self.docEditor.isEmpty():
|
||||||
if self.mainConf.showGUI:
|
if self.mainConf.showGUI:
|
||||||
msgBox = QMessageBox()
|
msgBox = QMessageBox()
|
||||||
msgRes = msgBox.question(
|
msgRes = msgBox.question(self, "Import Document",(
|
||||||
self, "Import Document",
|
"Importing the file will overwrite the current content of the document. "
|
||||||
"Importing the file will overwrite the current content of the document. Do you want to proceed?"
|
"Do you want to proceed?"
|
||||||
)
|
))
|
||||||
if msgRes != QMessageBox.Yes:
|
if msgRes != QMessageBox.Yes:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@@ -552,7 +578,7 @@ class GuiMain(QMainWindow):
|
|||||||
dlgOpt |= QFileDialog.ShowDirsOnly
|
dlgOpt |= QFileDialog.ShowDirsOnly
|
||||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||||
projPath = QFileDialog.getExistingDirectory(
|
projPath = QFileDialog.getExistingDirectory(
|
||||||
self,"Save novelWriter Project","",options=dlgOpt
|
self, "Save novelWriter Project", "", options=dlgOpt
|
||||||
)
|
)
|
||||||
if projPath:
|
if projPath:
|
||||||
return projPath
|
return projPath
|
||||||
@@ -563,7 +589,7 @@ class GuiMain(QMainWindow):
|
|||||||
dlgOpt |= QFileDialog.ShowDirsOnly
|
dlgOpt |= QFileDialog.ShowDirsOnly
|
||||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||||
projPath = QFileDialog.getExistingDirectory(
|
projPath = QFileDialog.getExistingDirectory(
|
||||||
self,"Select Location for New novelWriter Project","",options=dlgOpt
|
self, "Select Location for New novelWriter Project", "", options=dlgOpt
|
||||||
)
|
)
|
||||||
if projPath:
|
if projPath:
|
||||||
return projPath
|
return projPath
|
||||||
@@ -606,8 +632,9 @@ class GuiMain(QMainWindow):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def makeAlert(self, theMessage, theLevel=nwAlert.INFO):
|
def makeAlert(self, theMessage, theLevel=nwAlert.INFO):
|
||||||
"""Alert both the user and the logger at the same time. Message can be either a string or an
|
"""Alert both the user and the logger at the same time. Message
|
||||||
array of strings. Severity level is 0 = info, 1 = warning, and 2 = error.
|
can be either a string or an array of strings. Severity level is
|
||||||
|
0 = info, 1 = warning, and 2 = error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(theMessage, list):
|
if isinstance(theMessage, list):
|
||||||
@@ -701,7 +728,8 @@ class GuiMain(QMainWindow):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _autoSaveProject(self):
|
def _autoSaveProject(self):
|
||||||
if self.hasProject and self.theProject.projChanged and self.theProject.projPath is not None:
|
if (self.hasProject and self.theProject.projChanged and
|
||||||
|
self.theProject.projPath is not None):
|
||||||
logger.debug("Autosaving project")
|
logger.debug("Autosaving project")
|
||||||
self.saveProject(isAuto=True)
|
self.saveProject(isAuto=True)
|
||||||
return
|
return
|
||||||
@@ -733,7 +761,8 @@ class GuiMain(QMainWindow):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def resizeEvent(self, theEvent):
|
def resizeEvent(self, theEvent):
|
||||||
"""Extend QMainWindow.resizeEvent to signal dependent GUI elements that its pane may have changed size.
|
"""Extend QMainWindow.resizeEvent to signal dependent GUI
|
||||||
|
elements that its pane may have changed size.
|
||||||
"""
|
"""
|
||||||
QMainWindow.resizeEvent(self,theEvent)
|
QMainWindow.resizeEvent(self,theEvent)
|
||||||
self.docEditor.changeWidth()
|
self.docEditor.changeWidth()
|
||||||
@@ -779,7 +808,8 @@ class GuiMain(QMainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _keyPressEscape(self):
|
def _keyPressEscape(self):
|
||||||
"""When the escape key is pressed somewhere in the main window, do the following, in order
|
"""When the escape key is pressed somewhere in the main window,
|
||||||
|
do the following, in order.
|
||||||
"""
|
"""
|
||||||
if self.searchBar.isVisible():
|
if self.searchBar.isVisible():
|
||||||
self.searchBar.setVisible(False)
|
self.searchBar.setVisible(False)
|
||||||
@@ -787,13 +817,15 @@ class GuiMain(QMainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _splitMainMove(self, pWidth, pHeight):
|
def _splitMainMove(self, pWidth, pHeight):
|
||||||
"""Alert dependent GUI elements that the main pane splitter has been moved.
|
"""Alert dependent GUI elements that the main pane splitter has
|
||||||
|
been moved.
|
||||||
"""
|
"""
|
||||||
self.docEditor.changeWidth()
|
self.docEditor.changeWidth()
|
||||||
return
|
return
|
||||||
|
|
||||||
def _splitViewMove(self, pWidth, pHeight):
|
def _splitViewMove(self, pWidth, pHeight):
|
||||||
"""Alert dependent GUI elements that the main pane splitter has been moved.
|
"""Alert dependent GUI elements that the main pane splitter has
|
||||||
|
been moved.
|
||||||
"""
|
"""
|
||||||
self.docEditor.changeWidth()
|
self.docEditor.changeWidth()
|
||||||
return
|
return
|
||||||
|
|||||||
+12
-5
@@ -13,8 +13,8 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path, mkdir, listdir
|
from os import path, mkdir, listdir
|
||||||
from shutil import make_archive
|
from shutil import make_archive
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
@@ -32,11 +32,15 @@ class NWBackup():
|
|||||||
def zipIt(self):
|
def zipIt(self):
|
||||||
|
|
||||||
if self.mainConf.backupPath is None:
|
if self.mainConf.backupPath is None:
|
||||||
self.theParent.makeAlert("Cannot backup project because no backup path is set.",nwAlert.WARN)
|
self.theParent.makeAlert(
|
||||||
|
"Cannot backup project because no backup path is set.",nwAlert.WARN
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.theProject.projName is None:
|
if self.theProject.projName is None:
|
||||||
self.theParent.makeAlert("Cannot backup project because no project name is set.",nwAlert.WARN)
|
self.theParent.makeAlert(
|
||||||
|
"Cannot backup project because no project name is set.",nwAlert.WARN
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info("Backing up project")
|
logger.info("Backing up project")
|
||||||
@@ -55,7 +59,10 @@ class NWBackup():
|
|||||||
try:
|
try:
|
||||||
make_archive(baseName, "zip", self.theProject.projPath, ".")
|
make_archive(baseName, "zip", self.theProject.projPath, ".")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.theParent.makeAlert(["Could not write backup archive.",str(e)],nwAlert.ERROR)
|
self.theParent.makeAlert(
|
||||||
|
["Could not write backup archive.",str(e)],
|
||||||
|
nwAlert.ERROR
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.theParent.statusBar.setStatus("Project backup complete")
|
self.theParent.statusBar.setStatus("Project backup complete")
|
||||||
|
|||||||
+13
-9
@@ -53,7 +53,8 @@ class NWDoc():
|
|||||||
self.clearDocument()
|
self.clearDocument()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# By default, the document is editable. Except for files in the trash folder.
|
# By default, the document is editable.
|
||||||
|
# Except for files in the trash folder.
|
||||||
self.docEditable = True
|
self.docEditable = True
|
||||||
if self.theItem.parHandle == self.theProject.trashRoot:
|
if self.theItem.parHandle == self.theProject.trashRoot:
|
||||||
self.docEditable = False
|
self.docEditable = False
|
||||||
@@ -70,19 +71,19 @@ class NWDoc():
|
|||||||
theDoc = inFile.read()
|
theDoc = inFile.read()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.makeAlert(["Failed to open document file.",str(e)], nwAlert.ERROR)
|
self.makeAlert(["Failed to open document file.",str(e)], nwAlert.ERROR)
|
||||||
# Note: Document must be cleared in case of an io error, or else the auto-save or
|
# Note: Document must be cleared in case of an io error,
|
||||||
# save will try to overwrite it with an empty file. Return None to alert the caller.
|
# or else the auto-save or save will try to overwrite it
|
||||||
|
# with an empty file. Return None to alert the caller.
|
||||||
self.clearDocument()
|
self.clearDocument()
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# The document file does not exist, so we assume it's a new document and initialise an
|
# The document file does not exist, so we assume it's a new
|
||||||
# empty text string.
|
# document and initialise an empty text string.
|
||||||
logger.debug("The requested document does not exist.")
|
logger.debug("The requested document does not exist.")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
if showStatus:
|
if showStatus:
|
||||||
self.theParent.statusBar.setStatus("Opened Document: %s" % self.theItem.itemName)
|
self.theParent.statusBar.setStatus("Opened Document: %s" % self.theItem.itemName)
|
||||||
self.theParent.statusBar.setDocHandle(tHandle)
|
|
||||||
|
|
||||||
return theDoc
|
return theDoc
|
||||||
|
|
||||||
@@ -102,9 +103,12 @@ class NWDoc():
|
|||||||
docTemp = path.join(dataPath,docFile[:-3]+"tmp")
|
docTemp = path.join(dataPath,docFile[:-3]+"tmp")
|
||||||
docBack = path.join(dataPath,docFile[:-3]+"bak")
|
docBack = path.join(dataPath,docFile[:-3]+"bak")
|
||||||
|
|
||||||
if path.isfile(docTemp): unlink(docTemp)
|
if path.isfile(docTemp):
|
||||||
if path.isfile(docBack): rename(docBack,docTemp)
|
unlink(docTemp)
|
||||||
if path.isfile(docPath): rename(docPath,docBack)
|
if path.isfile(docBack):
|
||||||
|
rename(docBack,docTemp)
|
||||||
|
if path.isfile(docPath):
|
||||||
|
rename(docPath,docBack)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(docPath,mode="w",encoding="utf8") as outFile:
|
with open(docPath,mode="w",encoding="utf8") as outFile:
|
||||||
|
|||||||
+30
-23
@@ -17,9 +17,9 @@ import nw
|
|||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from nw.project.document import NWDoc
|
from nw.project.document import NWDoc
|
||||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
||||||
from nw.constants import nwFiles, nwKeyWords
|
from nw.constants import nwFiles, nwKeyWords
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ class NWIndex():
|
|||||||
self.noteIndex = {}
|
self.noteIndex = {}
|
||||||
|
|
||||||
# Lists
|
# Lists
|
||||||
self.novelList = []
|
self.novelList = []
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ class NWIndex():
|
|||||||
"""Load index from last session from the project meta folder.
|
"""Load index from last session from the project meta folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
theData = {}
|
theData = {}
|
||||||
indexFile = path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
indexFile = path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||||
if path.isfile(indexFile):
|
if path.isfile(indexFile):
|
||||||
logger.debug("Loading index file")
|
logger.debug("Loading index file")
|
||||||
@@ -130,7 +130,8 @@ class NWIndex():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def saveIndex(self):
|
def saveIndex(self):
|
||||||
"""Save the current index as a json file in the project meta folder.
|
"""Save the current index as a json file in the project meta
|
||||||
|
folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
indexFile = path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
indexFile = path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||||
@@ -155,7 +156,8 @@ class NWIndex():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def checkIndex(self):
|
def checkIndex(self):
|
||||||
"""Check that the entries in the index are valid and contain the elements it should.
|
"""Check that the entries in the index are valid and contain the
|
||||||
|
elements it should.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.indexBroken = False
|
self.indexBroken = False
|
||||||
@@ -193,8 +195,9 @@ class NWIndex():
|
|||||||
##
|
##
|
||||||
|
|
||||||
def scanText(self, tHandle, theText):
|
def scanText(self, tHandle, theText):
|
||||||
"""Scan a piece of text associated with a handle. This will update the indices accordingly.
|
"""Scan a piece of text associated with a handle. This will
|
||||||
This function takes the handle and text as separate inputs as we want to primarily scan the
|
update the indices accordingly. This function takes the handle
|
||||||
|
and text as separate inputs as we want to primarily scan the
|
||||||
files before we save them, unless we're rebuilding the index.
|
files before we save them, unless we're rebuilding the index.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -210,11 +213,11 @@ class NWIndex():
|
|||||||
# Check file type, and reset its old index
|
# Check file type, and reset its old index
|
||||||
if itemClass == nwItemClass.NOVEL:
|
if itemClass == nwItemClass.NOVEL:
|
||||||
self.novelIndex[tHandle] = []
|
self.novelIndex[tHandle] = []
|
||||||
self.refIndex[tHandle] = []
|
self.refIndex[tHandle] = []
|
||||||
isNovel = True
|
isNovel = True
|
||||||
else:
|
else:
|
||||||
self.noteIndex[tHandle] = []
|
self.noteIndex[tHandle] = []
|
||||||
self.refIndex[tHandle] = []
|
self.refIndex[tHandle] = []
|
||||||
isNovel = False
|
isNovel = False
|
||||||
|
|
||||||
# Also clear references to file in tag index
|
# Also clear references to file in tag index
|
||||||
@@ -243,7 +246,8 @@ class NWIndex():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def indexTitle(self, tHandle, isNovel, aLine, nLine, itemLayout):
|
def indexTitle(self, tHandle, isNovel, aLine, nLine, itemLayout):
|
||||||
"""Save information about the title and its location in the file.
|
"""Save information about the title and its location in the
|
||||||
|
file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if aLine.startswith("# "):
|
if aLine.startswith("# "):
|
||||||
@@ -272,7 +276,8 @@ class NWIndex():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def indexNoteRef(self, tHandle, aLine, nLine, nTitle):
|
def indexNoteRef(self, tHandle, aLine, nLine, nTitle):
|
||||||
"""Validate and save the information about a reference to a tag in another file.
|
"""Validate and save the information about a reference to a tag
|
||||||
|
in another file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
isValid, theBits, thePos = self.scanThis(aLine)
|
isValid, theBits, thePos = self.scanThis(aLine)
|
||||||
@@ -303,8 +308,9 @@ class NWIndex():
|
|||||||
##
|
##
|
||||||
|
|
||||||
def scanThis(self, aLine):
|
def scanThis(self, aLine):
|
||||||
"""Scan a line starting with @ to check that it's valid and to split up its elements into
|
"""Scan a line starting with @ to check that it's valid and to
|
||||||
an array and an array of positions. The latter is needed for the syntax highlighter.
|
split up its elements into an array and an array of positions.
|
||||||
|
The latter is needed for the syntax highlighter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
theBits = []
|
theBits = []
|
||||||
@@ -343,8 +349,8 @@ class NWIndex():
|
|||||||
return True, theBits, thePos
|
return True, theBits, thePos
|
||||||
|
|
||||||
def checkThese(self, theBits, tItem):
|
def checkThese(self, theBits, tItem):
|
||||||
"""Check the tags against the index to see if they are valid tags. This is needed for syntax
|
"""Check the tags against the index to see if they are valid
|
||||||
highlighting.
|
tags. This is needed for syntax highlighting.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
nBits = len(theBits)
|
nBits = len(theBits)
|
||||||
@@ -357,7 +363,8 @@ class NWIndex():
|
|||||||
if not isGood[0] or nBits == 1:
|
if not isGood[0] or nBits == 1:
|
||||||
return isGood
|
return isGood
|
||||||
|
|
||||||
# If we have a tag, only the first value is accepted, the rest is ignored
|
# If we have a tag, only the first value is accepted, the rest
|
||||||
|
# is ignored
|
||||||
if theBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
|
if theBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
|
||||||
isGood[0] = True
|
isGood[0] = True
|
||||||
if theBits[1] in self.tagIndex.keys():
|
if theBits[1] in self.tagIndex.keys():
|
||||||
@@ -383,7 +390,6 @@ class NWIndex():
|
|||||||
def buildNovelList(self):
|
def buildNovelList(self):
|
||||||
"""Build a list of the content of the novel.
|
"""Build a list of the content of the novel.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.novelList = []
|
self.novelList = []
|
||||||
self.novelOrder = []
|
self.novelOrder = []
|
||||||
for tHandle in self.theProject.treeOrder:
|
for tHandle in self.theProject.treeOrder:
|
||||||
@@ -392,11 +398,11 @@ class NWIndex():
|
|||||||
for tEntry in self.novelIndex[tHandle]:
|
for tEntry in self.novelIndex[tHandle]:
|
||||||
self.novelList.append(tEntry)
|
self.novelList.append(tEntry)
|
||||||
self.novelOrder.append("%s:%d" % (tHandle,tEntry[0]))
|
self.novelOrder.append("%s:%d" % (tHandle,tEntry[0]))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def buildReferenceList(self, tHandle):
|
def buildReferenceList(self, tHandle):
|
||||||
"""Build a list of files referring back to our file, specified by tHandle.
|
"""Build a list of files referring back to our file, specified
|
||||||
|
by tHandle.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
theRefs = {}
|
theRefs = {}
|
||||||
@@ -429,8 +435,9 @@ class NWIndex():
|
|||||||
return None, 0
|
return None, 0
|
||||||
|
|
||||||
def buildTagNovelMap(self, theTags, theFilters=None):
|
def buildTagNovelMap(self, theTags, theFilters=None):
|
||||||
"""Build a two-dimensional map of all titles of the novel and which tags they link to from
|
"""Build a two-dimensional map of all titles of the novel and
|
||||||
the various meta tags. This map is used to display the timeline view.
|
which tags they link to from the various meta tags. This map is
|
||||||
|
used to display the timeline view.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tagMap = {}
|
tagMap = {}
|
||||||
|
|||||||
+40
-29
@@ -13,11 +13,11 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path, mkdir
|
from os import path, mkdir
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
||||||
from nw.common import checkInt
|
from nw.common import checkInt
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -28,23 +28,23 @@ class NWItem():
|
|||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, theProject):
|
||||||
|
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
|
|
||||||
self.itemName = ""
|
self.itemName = ""
|
||||||
self.itemHandle = None
|
self.itemHandle = None
|
||||||
self.parHandle = None
|
self.parHandle = None
|
||||||
self.itemOrder = None
|
self.itemOrder = None
|
||||||
self.itemType = nwItemType.NO_TYPE
|
self.itemType = nwItemType.NO_TYPE
|
||||||
self.itemClass = nwItemClass.NO_CLASS
|
self.itemClass = nwItemClass.NO_CLASS
|
||||||
self.itemLayout = nwItemLayout.NO_LAYOUT
|
self.itemLayout = nwItemLayout.NO_LAYOUT
|
||||||
self.itemStatus = None
|
self.itemStatus = None
|
||||||
self.isExpanded = False
|
self.isExpanded = False
|
||||||
|
|
||||||
# Document Meta Data
|
# Document Meta Data
|
||||||
self.charCount = 0
|
self.charCount = 0
|
||||||
self.wordCount = 0
|
self.wordCount = 0
|
||||||
self.paraCount = 0
|
self.paraCount = 0
|
||||||
self.cursorPos = 0
|
self.cursorPos = 0
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -85,17 +85,28 @@ class NWItem():
|
|||||||
|
|
||||||
def setFromTag(self, tagName, tagValue):
|
def setFromTag(self, tagName, tagValue):
|
||||||
logger.verbose("Setting tag '%s' to value '%s'" % (tagName, str(tagValue)))
|
logger.verbose("Setting tag '%s' to value '%s'" % (tagName, str(tagValue)))
|
||||||
if tagName == "name": self.setName(tagValue)
|
if tagName == "name":
|
||||||
elif tagName == "order": self.setOrder(tagValue)
|
self.setName(tagValue)
|
||||||
elif tagName == "type": self.setType(tagValue)
|
elif tagName == "order":
|
||||||
elif tagName == "class": self.setClass(tagValue)
|
self.setOrder(tagValue)
|
||||||
elif tagName == "layout": self.setLayout(tagValue)
|
elif tagName == "type":
|
||||||
elif tagName == "status": self.setStatus(tagValue)
|
self.setType(tagValue)
|
||||||
elif tagName == "expanded": self.setExpanded(tagValue)
|
elif tagName == "class":
|
||||||
elif tagName == "charCount": self.setCharCount(tagValue)
|
self.setClass(tagValue)
|
||||||
elif tagName == "wordCount": self.setWordCount(tagValue)
|
elif tagName == "layout":
|
||||||
elif tagName == "paraCount": self.setParaCount(tagValue)
|
self.setLayout(tagValue)
|
||||||
elif tagName == "cursorPos": self.setCursorPos(tagValue)
|
elif tagName == "status":
|
||||||
|
self.setStatus(tagValue)
|
||||||
|
elif tagName == "expanded":
|
||||||
|
self.setExpanded(tagValue)
|
||||||
|
elif tagName == "charCount":
|
||||||
|
self.setCharCount(tagValue)
|
||||||
|
elif tagName == "wordCount":
|
||||||
|
self.setWordCount(tagValue)
|
||||||
|
elif tagName == "paraCount":
|
||||||
|
self.setParaCount(tagValue)
|
||||||
|
elif tagName == "cursorPos":
|
||||||
|
self.setCursorPos(tagValue)
|
||||||
else:
|
else:
|
||||||
logger.error("Unknown tag '%s'" % tagName)
|
logger.error("Unknown tag '%s'" % tagName)
|
||||||
return
|
return
|
||||||
|
|||||||
+82
-63
@@ -13,18 +13,18 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path, mkdir, listdir
|
from os import path, mkdir, listdir
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from nw.project.item import NWItem
|
from nw.project.item import NWItem
|
||||||
from nw.project.status import NWStatus
|
from nw.project.status import NWStatus
|
||||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
from nw.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||||
from nw.common import checkString, checkBool, checkInt
|
from nw.common import checkString, checkBool, checkInt
|
||||||
from nw.constants import nwFiles, nwConst
|
from nw.constants import nwFiles, nwConst
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -37,21 +37,21 @@ class NWProject():
|
|||||||
self.mainConf = self.theParent.mainConf
|
self.mainConf = self.theParent.mainConf
|
||||||
self.projOpened = None # The time stamp of when the project file was opened
|
self.projOpened = None # The time stamp of when the project file was opened
|
||||||
self.projChanged = None # The project has unsaved changes
|
self.projChanged = None # The project has unsaved changes
|
||||||
self.projAltered = None # The project has been altered this session (used to trigger backup)
|
self.projAltered = None # The project has been altered this session
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
self.handleSeed = None
|
self.handleSeed = None
|
||||||
|
|
||||||
# Class Settings
|
# Class Settings
|
||||||
self.projTree = None # Holds all the items of the project
|
self.projTree = None # Holds all the items of the project
|
||||||
self.treeOrder = None # The order of the tree items on the tree view
|
self.treeOrder = None # The order of the tree items on the tree view
|
||||||
self.treeRoots = None # The root items of the tree
|
self.treeRoots = None # The root items of the tree
|
||||||
self.trashRoot = None # The handle of the trash root folder
|
self.trashRoot = None # The handle of the trash root folder
|
||||||
self.projPath = None # The full path to where the currently open project is saved
|
self.projPath = None # The full path to where the currently open project is saved
|
||||||
self.projMeta = None # The full path to the project's meta data folder
|
self.projMeta = None # The full path to the project's meta data folder
|
||||||
self.projCache = None # The full path to the project's cache folder
|
self.projCache = None # The full path to the project's cache folder
|
||||||
self.projDict = None # The spell check dictionary
|
self.projDict = None # The spell check dictionary
|
||||||
self.projFile = None # The file name of the project main xml file
|
self.projFile = None # The file name of the project main xml file
|
||||||
|
|
||||||
# Project Meta
|
# Project Meta
|
||||||
self.projName = None
|
self.projName = None
|
||||||
@@ -194,8 +194,10 @@ class NWProject():
|
|||||||
self.projCache = path.join(self.projPath,"cache")
|
self.projCache = path.join(self.projPath,"cache")
|
||||||
self.projDict = path.join(self.projMeta, nwFiles.PROJ_DICT)
|
self.projDict = path.join(self.projMeta, nwFiles.PROJ_DICT)
|
||||||
|
|
||||||
if not self._checkFolder(self.projMeta): return
|
if not self._checkFolder(self.projMeta):
|
||||||
if not self._checkFolder(self.projCache): return
|
return
|
||||||
|
if not self._checkFolder(self.projCache):
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nwXML = etree.parse(fileName)
|
nwXML = etree.parse(fileName)
|
||||||
@@ -214,14 +216,18 @@ class NWProject():
|
|||||||
logger.verbose("File version is %s" % fileVersion)
|
logger.verbose("File version is %s" % fileVersion)
|
||||||
|
|
||||||
if not nwxRoot == "novelWriterXML" or not fileVersion == "1.0":
|
if not nwxRoot == "novelWriterXML" or not fileVersion == "1.0":
|
||||||
self.makeAlert("Project file does not appear to be a novelWriterXML file version 1.0", nwAlert.ERROR)
|
self.makeAlert(
|
||||||
|
"Project file does not appear to be a novelWriterXML file version 1.0",
|
||||||
|
nwAlert.ERROR
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for xChild in xRoot:
|
for xChild in xRoot:
|
||||||
if xChild.tag == "project":
|
if xChild.tag == "project":
|
||||||
logger.debug("Found project meta")
|
logger.debug("Found project meta")
|
||||||
for xItem in xChild:
|
for xItem in xChild:
|
||||||
if xItem.text is None: continue
|
if xItem.text is None:
|
||||||
|
continue
|
||||||
if xItem.tag == "name":
|
if xItem.tag == "name":
|
||||||
logger.verbose("Working Title: '%s'" % xItem.text)
|
logger.verbose("Working Title: '%s'" % xItem.text)
|
||||||
self.projName = xItem.text
|
self.projName = xItem.text
|
||||||
@@ -236,7 +242,8 @@ class NWProject():
|
|||||||
elif xChild.tag == "settings":
|
elif xChild.tag == "settings":
|
||||||
logger.debug("Found project settings")
|
logger.debug("Found project settings")
|
||||||
for xItem in xChild:
|
for xItem in xChild:
|
||||||
if xItem.text is None: continue
|
if xItem.text is None:
|
||||||
|
continue
|
||||||
if xItem.tag == "spellCheck":
|
if xItem.tag == "spellCheck":
|
||||||
self.spellCheck = checkBool(xItem.text,False)
|
self.spellCheck = checkBool(xItem.text,False)
|
||||||
elif xItem.tag == "lastEdited":
|
elif xItem.tag == "lastEdited":
|
||||||
@@ -308,19 +315,19 @@ class NWProject():
|
|||||||
})
|
})
|
||||||
|
|
||||||
# Save Project Meta
|
# Save Project Meta
|
||||||
xProject = etree.SubElement(nwXML,"project")
|
xProject = etree.SubElement(nwXML, "project")
|
||||||
self._saveProjectValue(xProject,"name", self.projName, True)
|
self._saveProjectValue(xProject, "name", self.projName, True)
|
||||||
self._saveProjectValue(xProject,"title", self.bookTitle, True)
|
self._saveProjectValue(xProject, "title", self.bookTitle, True)
|
||||||
self._saveProjectValue(xProject,"author",self.bookAuthors)
|
self._saveProjectValue(xProject, "author", self.bookAuthors)
|
||||||
self._saveProjectValue(xProject,"backup",self.doBackup)
|
self._saveProjectValue(xProject, "backup", self.doBackup)
|
||||||
|
|
||||||
# Save Project Settings
|
# Save Project Settings
|
||||||
xSettings = etree.SubElement(nwXML,"settings")
|
xSettings = etree.SubElement(nwXML, "settings")
|
||||||
self._saveProjectValue(xSettings,"spellCheck", self.spellCheck)
|
self._saveProjectValue(xSettings, "spellCheck", self.spellCheck)
|
||||||
self._saveProjectValue(xSettings,"lastEdited", self.lastEdited)
|
self._saveProjectValue(xSettings, "lastEdited", self.lastEdited)
|
||||||
self._saveProjectValue(xSettings,"lastViewed", self.lastViewed)
|
self._saveProjectValue(xSettings, "lastViewed", self.lastViewed)
|
||||||
self._saveProjectValue(xSettings,"lastWordCount",self.currWCount)
|
self._saveProjectValue(xSettings, "lastWordCount", self.currWCount)
|
||||||
xAutoRep = etree.SubElement(xSettings,"autoReplace")
|
xAutoRep = etree.SubElement(xSettings, "autoReplace")
|
||||||
for aKey, aValue in self.autoReplace.items():
|
for aKey, aValue in self.autoReplace.items():
|
||||||
if len(aKey) > 0:
|
if len(aKey) > 0:
|
||||||
self._saveProjectValue(xAutoRep,aKey,aValue)
|
self._saveProjectValue(xAutoRep,aKey,aValue)
|
||||||
@@ -332,7 +339,7 @@ class NWProject():
|
|||||||
|
|
||||||
# Save Tree Content
|
# Save Tree Content
|
||||||
logger.debug("Writing project content")
|
logger.debug("Writing project content")
|
||||||
xContent = etree.SubElement(nwXML,"content",attrib={"count":str(len(self.treeOrder))})
|
xContent = etree.SubElement(nwXML, "content", attrib={"count":str(len(self.treeOrder))})
|
||||||
for tHandle in self.treeOrder:
|
for tHandle in self.treeOrder:
|
||||||
self.projTree[tHandle].packXML(xContent)
|
self.projTree[tHandle].packXML(xContent)
|
||||||
|
|
||||||
@@ -399,16 +406,16 @@ class NWProject():
|
|||||||
self.doBackup = False
|
self.doBackup = False
|
||||||
if doBackup:
|
if doBackup:
|
||||||
if not path.isdir(self.mainConf.backupPath):
|
if not path.isdir(self.mainConf.backupPath):
|
||||||
self.theParent.makeAlert(
|
self.theParent.makeAlert((
|
||||||
"You must set a valid backup path in preferences<br>to use the automatic project backup feature.",
|
"You must set a valid backup path in preferences to use "
|
||||||
nwAlert.ERROR
|
"the automatic project backup feature."
|
||||||
)
|
), nwAlert.ERROR)
|
||||||
return False
|
return False
|
||||||
if self.projName == "":
|
if self.projName == "":
|
||||||
self.theParent.makeAlert(
|
self.theParent.makeAlert((
|
||||||
"You must set a valid project name in project settings<br>to use the automatic project backup feature.",
|
"You must set a valid project name in project settings to "
|
||||||
nwAlert.ERROR
|
"use the automatic project backup feature."
|
||||||
)
|
), nwAlert.ERROR)
|
||||||
return False
|
return False
|
||||||
self.doBackup = True
|
self.doBackup = True
|
||||||
return True
|
return True
|
||||||
@@ -490,8 +497,9 @@ class NWProject():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def getRootItem(self, tHandle):
|
def getRootItem(self, tHandle):
|
||||||
"""Iterate upwards in the tree until we find the item with parent None, the root item.
|
"""Iterate upwards in the tree until we find the item with
|
||||||
We do this with a for loop with a maximum depth of 200 to make infinite loops impossible.
|
parent None, the root item. We do this with a for loop with a
|
||||||
|
maximum depth of 200 to make infinite loops impossible.
|
||||||
"""
|
"""
|
||||||
tItem = self.getItem(tHandle)
|
tItem = self.getItem(tHandle)
|
||||||
if tItem is not None:
|
if tItem is not None:
|
||||||
@@ -503,9 +511,10 @@ class NWProject():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def getProjectItems(self):
|
def getProjectItems(self):
|
||||||
"""This function is called from the tree view when building the tree. Each item in the
|
"""This function is called from the tree view when building the
|
||||||
project is returned in the order saved in the project file, but first it checks that it has
|
tree. Each item in the project is returned in the order saved in
|
||||||
a parent item already sent to the tree.
|
the project file, but first it checks that it has a parent item
|
||||||
|
already sent to the tree.
|
||||||
"""
|
"""
|
||||||
sentItems = []
|
sentItems = []
|
||||||
iterItems = self.treeOrder.copy()
|
iterItems = self.treeOrder.copy()
|
||||||
@@ -518,10 +527,12 @@ class NWProject():
|
|||||||
if n > 10000:
|
if n > 10000:
|
||||||
return # Just in case
|
return # Just in case
|
||||||
if tItem is None:
|
if tItem is None:
|
||||||
# Technically a bug since treeOrder is built from the same data as projTree
|
# Technically a bug since treeOrder is built from the
|
||||||
|
# same data as projTree
|
||||||
continue
|
continue
|
||||||
elif tItem.parHandle is None:
|
elif tItem.parHandle is None:
|
||||||
# Item is a root, or already been identified as an orphaned item
|
# Item is a root, or already been identified as an
|
||||||
|
# orphaned item
|
||||||
sentItems.append(tHandle)
|
sentItems.append(tHandle)
|
||||||
yield tItem
|
yield tItem
|
||||||
elif tItem.parHandle in sentItems:
|
elif tItem.parHandle in sentItems:
|
||||||
@@ -529,7 +540,8 @@ class NWProject():
|
|||||||
sentItems.append(tHandle)
|
sentItems.append(tHandle)
|
||||||
yield tItem
|
yield tItem
|
||||||
elif tItem.parHandle in iterItems:
|
elif tItem.parHandle in iterItems:
|
||||||
# Item's parent exists, but hasn't been sent yet, so add it again to the end
|
# Item's parent exists, but hasn't been sent yet, so add
|
||||||
|
# it again to the end
|
||||||
logger.warning("Item %s found before its parent" % tHandle)
|
logger.warning("Item %s found before its parent" % tHandle)
|
||||||
iterItems.append(tHandle)
|
iterItems.append(tHandle)
|
||||||
nMax = len(iterItems)
|
nMax = len(iterItems)
|
||||||
@@ -544,7 +556,8 @@ class NWProject():
|
|||||||
##
|
##
|
||||||
|
|
||||||
def deleteItem(self, tHandle):
|
def deleteItem(self, tHandle):
|
||||||
"""This only removes the item from the order list, but not from the project tree.
|
"""This only removes the item from the order list, but not from
|
||||||
|
the project tree.
|
||||||
"""
|
"""
|
||||||
self.treeOrder.remove(tHandle)
|
self.treeOrder.remove(tHandle)
|
||||||
self.setProjectChanged(True)
|
self.setProjectChanged(True)
|
||||||
@@ -557,8 +570,8 @@ class NWProject():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def checkRootUnique(self, theClass):
|
def checkRootUnique(self, theClass):
|
||||||
"""Checks if there already is a root entry of class 'theClass' in the
|
"""Checks if there already is a root entry of class 'theClass'
|
||||||
root of the project tree.
|
in the root of the project tree.
|
||||||
"""
|
"""
|
||||||
if theClass == nwItemClass.CUSTOM:
|
if theClass == nwItemClass.CUSTOM:
|
||||||
return True
|
return True
|
||||||
@@ -634,7 +647,10 @@ class NWProject():
|
|||||||
|
|
||||||
# Report status
|
# Report status
|
||||||
if len(orphanFiles) > 0:
|
if len(orphanFiles) > 0:
|
||||||
self.makeAlert("Found %d orphaned file(s) in project folder!" % len(orphanFiles), nwAlert.WARN)
|
self.makeAlert(
|
||||||
|
"Found %d orphaned file(s) in project folder!" % len(orphanFiles),
|
||||||
|
nwAlert.WARN
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug("File check OK")
|
logger.debug("File check OK")
|
||||||
return
|
return
|
||||||
@@ -683,7 +699,9 @@ class NWProject():
|
|||||||
if self.projMeta is None:
|
if self.projMeta is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
with open(path.join(self.projMeta, nwFiles.SESS_INFO),mode="a+",encoding="utf8") as outFile:
|
sessionFile = path.join(self.projMeta, nwFiles.SESS_INFO)
|
||||||
|
|
||||||
|
with open(sessionFile, mode="a+", encoding="utf8") as outFile:
|
||||||
print((
|
print((
|
||||||
"Start: {opened:s} "
|
"Start: {opened:s} "
|
||||||
"End: {closed:s} "
|
"End: {closed:s} "
|
||||||
@@ -711,9 +729,10 @@ class NWProject():
|
|||||||
return itemHandle
|
return itemHandle
|
||||||
|
|
||||||
def _maintainPrevious(self):
|
def _maintainPrevious(self):
|
||||||
"""This function will take the current project file and copy it into the project cache
|
"""This function will take the current project file and copy it
|
||||||
folder with an incremental file extension added. These serve as a backup in case the xml
|
into the project cache folder with an incremental file extension
|
||||||
file gets corrupted.
|
added. These serve as a backup in case the xml file gets
|
||||||
|
corrupted.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
countFile = path.join(self.projCache, nwFiles.PROJ_COUNT)
|
countFile = path.join(self.projCache, nwFiles.PROJ_COUNT)
|
||||||
@@ -733,8 +752,8 @@ class NWProject():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
copyfile(
|
copyfile(
|
||||||
path.join(self.projPath,self.projFile),
|
path.join(self.projPath, self.projFile),
|
||||||
path.join(self.projCache,projBackup)
|
path.join(self.projCache, projBackup)
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
logger.error("Failed to write to file %s" % projBackup)
|
logger.error("Failed to write to file %s" % projBackup)
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from nw.enum import nwItemClass
|
from nw.enum import nwItemClass
|
||||||
from nw.common import checkInt
|
from nw.common import checkInt
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
+86
-86
@@ -17,7 +17,7 @@ import nw
|
|||||||
from os import path, listdir
|
from os import path, listdir
|
||||||
|
|
||||||
from PyQt5.QtWidgets import qApp
|
from PyQt5.QtWidgets import qApp
|
||||||
from PyQt5.QtGui import QPalette, QColor, QIcon
|
from PyQt5.QtGui import QPalette, QColor, QIcon
|
||||||
|
|
||||||
from nw.enum import nwAlert
|
from nw.enum import nwAlert
|
||||||
|
|
||||||
@@ -41,30 +41,30 @@ class Theme:
|
|||||||
|
|
||||||
def __init__(self, theParent):
|
def __init__(self, theParent):
|
||||||
|
|
||||||
self.mainConf = nw.CONFIG
|
self.mainConf = nw.CONFIG
|
||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.guiPalette = QPalette()
|
self.guiPalette = QPalette()
|
||||||
self.guiPath = "gui"
|
self.guiPath = "gui"
|
||||||
self.iconPath = "icons"
|
self.iconPath = "icons"
|
||||||
self.syntaxPath = "syntax"
|
self.syntaxPath = "syntax"
|
||||||
self.cssName = "style.qss"
|
self.cssName = "style.qss"
|
||||||
self.confName = "theme.conf"
|
self.confName = "theme.conf"
|
||||||
self.themeList = []
|
self.themeList = []
|
||||||
self.syntaxList = []
|
self.syntaxList = []
|
||||||
|
|
||||||
# Loaded Theme Settings
|
# Loaded Theme Settings
|
||||||
|
|
||||||
## Theme
|
## Theme
|
||||||
self.themeName = ""
|
self.themeName = ""
|
||||||
self.themeAuthor = ""
|
self.themeAuthor = ""
|
||||||
self.themeCredit = ""
|
self.themeCredit = ""
|
||||||
self.themeUrl = ""
|
self.themeUrl = ""
|
||||||
|
|
||||||
## GUI
|
## GUI
|
||||||
self.treeWCount = [ 0, 0, 0]
|
self.treeWCount = [ 0, 0, 0]
|
||||||
self.statNone = [120,120,120]
|
self.statNone = [120,120,120]
|
||||||
self.statUnsaved = [120,120, 40]
|
self.statUnsaved = [120,120, 40]
|
||||||
self.statSaved = [ 40,120, 0]
|
self.statSaved = [ 40,120, 0]
|
||||||
|
|
||||||
# Loaded Syntax Settings
|
# Loaded Syntax Settings
|
||||||
|
|
||||||
@@ -75,33 +75,33 @@ class Theme:
|
|||||||
self.syntaxUrl = ""
|
self.syntaxUrl = ""
|
||||||
|
|
||||||
## Colours
|
## Colours
|
||||||
self.colBack = [255,255,255]
|
self.colBack = [255,255,255]
|
||||||
self.colText = [ 0, 0, 0]
|
self.colText = [ 0, 0, 0]
|
||||||
self.colLink = [ 0, 0, 0]
|
self.colLink = [ 0, 0, 0]
|
||||||
self.colHead = [ 0, 0, 0]
|
self.colHead = [ 0, 0, 0]
|
||||||
self.colHeadH = [ 0, 0, 0]
|
self.colHeadH = [ 0, 0, 0]
|
||||||
self.colEmph = [ 0, 0, 0]
|
self.colEmph = [ 0, 0, 0]
|
||||||
self.colDialN = [ 0, 0, 0]
|
self.colDialN = [ 0, 0, 0]
|
||||||
self.colDialD = [ 0, 0, 0]
|
self.colDialD = [ 0, 0, 0]
|
||||||
self.colDialS = [ 0, 0, 0]
|
self.colDialS = [ 0, 0, 0]
|
||||||
self.colComm = [ 0, 0, 0]
|
self.colComm = [ 0, 0, 0]
|
||||||
self.colKey = [ 0, 0, 0]
|
self.colKey = [ 0, 0, 0]
|
||||||
self.colVal = [ 0, 0, 0]
|
self.colVal = [ 0, 0, 0]
|
||||||
self.colSpell = [ 0, 0, 0]
|
self.colSpell = [ 0, 0, 0]
|
||||||
self.colTagErr = [ 0, 0, 0]
|
self.colTagErr = [ 0, 0, 0]
|
||||||
self.colRepTag = [ 0, 0, 0]
|
self.colRepTag = [ 0, 0, 0]
|
||||||
|
|
||||||
## Icons
|
## Icons
|
||||||
self.themeIcons = {}
|
self.themeIcons = {}
|
||||||
|
|
||||||
# Changeable Settings
|
# Changeable Settings
|
||||||
self.guiTheme = None
|
self.guiTheme = None
|
||||||
self.guiSyntax = None
|
self.guiSyntax = None
|
||||||
self.themeRoot = None
|
self.themeRoot = None
|
||||||
self.themePath = None
|
self.themePath = None
|
||||||
self.syntaxFile = None
|
self.syntaxFile = None
|
||||||
self.confFile = None
|
self.confFile = None
|
||||||
self.cssFile = None
|
self.cssFile = None
|
||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
@@ -162,36 +162,36 @@ class Theme:
|
|||||||
## Main
|
## Main
|
||||||
cnfSec = "Main"
|
cnfSec = "Main"
|
||||||
if confParser.has_section(cnfSec):
|
if confParser.has_section(cnfSec):
|
||||||
self.themeName = self._parseLine(confParser,cnfSec,"name", "")
|
self.themeName = self._parseLine( confParser, cnfSec, "name", "")
|
||||||
self.themeAuthor = self._parseLine(confParser,cnfSec,"author","")
|
self.themeAuthor = self._parseLine( confParser, cnfSec, "author", "")
|
||||||
self.themeCredit = self._parseLine(confParser,cnfSec,"credit","")
|
self.themeCredit = self._parseLine( confParser, cnfSec, "credit", "")
|
||||||
self.themeUrl = self._parseLine(confParser,cnfSec,"url", "")
|
self.themeUrl = self._parseLine( confParser, cnfSec, "url", "")
|
||||||
|
|
||||||
## Palette
|
## Palette
|
||||||
cnfSec = "Palette"
|
cnfSec = "Palette"
|
||||||
if confParser.has_section(cnfSec):
|
if confParser.has_section(cnfSec):
|
||||||
self._setPalette(confParser,cnfSec,"window", QPalette.Window)
|
self._setPalette(confParser, cnfSec, "window", QPalette.Window)
|
||||||
self._setPalette(confParser,cnfSec,"windowtext", QPalette.WindowText)
|
self._setPalette(confParser, cnfSec, "windowtext", QPalette.WindowText)
|
||||||
self._setPalette(confParser,cnfSec,"base", QPalette.Base)
|
self._setPalette(confParser, cnfSec, "base", QPalette.Base)
|
||||||
self._setPalette(confParser,cnfSec,"alternatebase", QPalette.AlternateBase)
|
self._setPalette(confParser, cnfSec, "alternatebase", QPalette.AlternateBase)
|
||||||
self._setPalette(confParser,cnfSec,"text", QPalette.Text)
|
self._setPalette(confParser, cnfSec, "text", QPalette.Text)
|
||||||
self._setPalette(confParser,cnfSec,"tooltipbase", QPalette.ToolTipBase)
|
self._setPalette(confParser, cnfSec, "tooltipbase", QPalette.ToolTipBase)
|
||||||
self._setPalette(confParser,cnfSec,"tooltiptext", QPalette.ToolTipText)
|
self._setPalette(confParser, cnfSec, "tooltiptext", QPalette.ToolTipText)
|
||||||
self._setPalette(confParser,cnfSec,"button", QPalette.Button)
|
self._setPalette(confParser, cnfSec, "button", QPalette.Button)
|
||||||
self._setPalette(confParser,cnfSec,"buttontext", QPalette.ButtonText)
|
self._setPalette(confParser, cnfSec, "buttontext", QPalette.ButtonText)
|
||||||
self._setPalette(confParser,cnfSec,"brighttext", QPalette.BrightText)
|
self._setPalette(confParser, cnfSec, "brighttext", QPalette.BrightText)
|
||||||
self._setPalette(confParser,cnfSec,"highlight", QPalette.Highlight)
|
self._setPalette(confParser, cnfSec, "highlight", QPalette.Highlight)
|
||||||
self._setPalette(confParser,cnfSec,"highlightedtext",QPalette.HighlightedText)
|
self._setPalette(confParser, cnfSec, "highlightedtext", QPalette.HighlightedText)
|
||||||
self._setPalette(confParser,cnfSec,"link", QPalette.Link)
|
self._setPalette(confParser, cnfSec, "link", QPalette.Link)
|
||||||
self._setPalette(confParser,cnfSec,"linkvisited", QPalette.LinkVisited)
|
self._setPalette(confParser, cnfSec, "linkvisited", QPalette.LinkVisited)
|
||||||
|
|
||||||
## GUI
|
## GUI
|
||||||
cnfSec = "GUI"
|
cnfSec = "GUI"
|
||||||
if confParser.has_section(cnfSec):
|
if confParser.has_section(cnfSec):
|
||||||
self.treeWCount = self._loadColour(confParser,cnfSec,"treewordcount")
|
self.treeWCount = self._loadColour(confParser, cnfSec, "treewordcount")
|
||||||
self.statNone = self._loadColour(confParser,cnfSec,"statusnone")
|
self.statNone = self._loadColour(confParser, cnfSec, "statusnone")
|
||||||
self.statUnsaved = self._loadColour(confParser,cnfSec,"statusunsaved")
|
self.statUnsaved = self._loadColour(confParser, cnfSec, "statusunsaved")
|
||||||
self.statSaved = self._loadColour(confParser,cnfSec,"statussaved")
|
self.statSaved = self._loadColour(confParser, cnfSec, "statussaved")
|
||||||
|
|
||||||
# Apply Styles
|
# Apply Styles
|
||||||
qApp.setStyleSheet(cssData)
|
qApp.setStyleSheet(cssData)
|
||||||
@@ -205,7 +205,7 @@ class Theme:
|
|||||||
|
|
||||||
confParser = configparser.ConfigParser()
|
confParser = configparser.ConfigParser()
|
||||||
try:
|
try:
|
||||||
confParser.read_file(open(self.syntaxFile,mode="r",encoding="utf8"))
|
confParser.read_file(open(self.syntaxFile, mode="r", encoding="utf8"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Could not load syntax colours from: %s" % self.syntaxFile)
|
logger.error("Could not load syntax colours from: %s" % self.syntaxFile)
|
||||||
return False
|
return False
|
||||||
@@ -213,29 +213,29 @@ class Theme:
|
|||||||
## Main
|
## Main
|
||||||
cnfSec = "Main"
|
cnfSec = "Main"
|
||||||
if confParser.has_section(cnfSec):
|
if confParser.has_section(cnfSec):
|
||||||
self.syntaxName = self._parseLine(confParser,cnfSec,"name","")
|
self.syntaxName = self._parseLine(confParser, cnfSec, "name", "")
|
||||||
self.syntaxAuthor = self._parseLine(confParser,cnfSec,"author","")
|
self.syntaxAuthor = self._parseLine(confParser, cnfSec, "author", "")
|
||||||
self.syntaxCredit = self._parseLine(confParser,cnfSec,"credit","")
|
self.syntaxCredit = self._parseLine(confParser, cnfSec, "credit", "")
|
||||||
self.syntaxUrl = self._parseLine(confParser,cnfSec,"url", "")
|
self.syntaxUrl = self._parseLine(confParser, cnfSec, "url", "")
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
cnfSec = "Syntax"
|
cnfSec = "Syntax"
|
||||||
if confParser.has_section(cnfSec):
|
if confParser.has_section(cnfSec):
|
||||||
self.colBack = self._loadColour(confParser,cnfSec,"background")
|
self.colBack = self._loadColour(confParser, cnfSec, "background")
|
||||||
self.colText = self._loadColour(confParser,cnfSec,"text")
|
self.colText = self._loadColour(confParser, cnfSec, "text")
|
||||||
self.colLink = self._loadColour(confParser,cnfSec,"link")
|
self.colLink = self._loadColour(confParser, cnfSec, "link")
|
||||||
self.colHead = self._loadColour(confParser,cnfSec,"headertext")
|
self.colHead = self._loadColour(confParser, cnfSec, "headertext")
|
||||||
self.colHeadH = self._loadColour(confParser,cnfSec,"headertag")
|
self.colHeadH = self._loadColour(confParser, cnfSec, "headertag")
|
||||||
self.colEmph = self._loadColour(confParser,cnfSec,"emphasis")
|
self.colEmph = self._loadColour(confParser, cnfSec, "emphasis")
|
||||||
self.colDialN = self._loadColour(confParser,cnfSec,"straightquotes")
|
self.colDialN = self._loadColour(confParser, cnfSec, "straightquotes")
|
||||||
self.colDialD = self._loadColour(confParser,cnfSec,"doublequotes")
|
self.colDialD = self._loadColour(confParser, cnfSec, "doublequotes")
|
||||||
self.colDialS = self._loadColour(confParser,cnfSec,"singlequotes")
|
self.colDialS = self._loadColour(confParser, cnfSec, "singlequotes")
|
||||||
self.colComm = self._loadColour(confParser,cnfSec,"hidden")
|
self.colComm = self._loadColour(confParser, cnfSec, "hidden")
|
||||||
self.colKey = self._loadColour(confParser,cnfSec,"keyword")
|
self.colKey = self._loadColour(confParser, cnfSec, "keyword")
|
||||||
self.colVal = self._loadColour(confParser,cnfSec,"value")
|
self.colVal = self._loadColour(confParser, cnfSec, "value")
|
||||||
self.colSpell = self._loadColour(confParser,cnfSec,"spellcheckline")
|
self.colSpell = self._loadColour(confParser, cnfSec, "spellcheckline")
|
||||||
self.colTagErr = self._loadColour(confParser,cnfSec,"tagerror")
|
self.colTagErr = self._loadColour(confParser, cnfSec, "tagerror")
|
||||||
self.colRepTag = self._loadColour(confParser,cnfSec,"replacetag")
|
self.colRepTag = self._loadColour(confParser, cnfSec, "replacetag")
|
||||||
|
|
||||||
logger.info("Loaded syntax theme '%s'" % self.guiSyntax)
|
logger.info("Loaded syntax theme '%s'" % self.guiSyntax)
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ class Theme:
|
|||||||
themeConf = path.join(self.mainConf.themeRoot, self.guiPath, themeDir, self.confName)
|
themeConf = path.join(self.mainConf.themeRoot, self.guiPath, themeDir, self.confName)
|
||||||
logger.verbose("Checking theme config for '%s'" % themeDir)
|
logger.verbose("Checking theme config for '%s'" % themeDir)
|
||||||
try:
|
try:
|
||||||
confParser.read_file(open(themeConf,mode="r",encoding="utf8"))
|
confParser.read_file(open(themeConf, mode="r", encoding="utf8"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.theParent.makeAlert(["Could not load theme config file",str(e)],nwAlert.ERROR)
|
self.theParent.makeAlert(["Could not load theme config file",str(e)],nwAlert.ERROR)
|
||||||
continue
|
continue
|
||||||
@@ -279,7 +279,7 @@ class Theme:
|
|||||||
continue
|
continue
|
||||||
logger.verbose("Checking theme syntax for '%s'" % syntaxFile)
|
logger.verbose("Checking theme syntax for '%s'" % syntaxFile)
|
||||||
try:
|
try:
|
||||||
confParser.read_file(open(syntaxPath,mode="r",encoding="utf8"))
|
confParser.read_file(open(syntaxPath, mode="r", encoding="utf8"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.theParent.makeAlert(["Could not load syntax file",str(e)],nwAlert.ERROR)
|
self.theParent.makeAlert(["Could not load syntax file",str(e)],nwAlert.ERROR)
|
||||||
return []
|
return []
|
||||||
|
|||||||
+14
-11
@@ -58,7 +58,7 @@ class TextAnalysis():
|
|||||||
return rScore, gLevel
|
return rScore, gLevel
|
||||||
|
|
||||||
def getReadabilityText(self, rScore):
|
def getReadabilityText(self, rScore):
|
||||||
if rScore >= 90.0:
|
if rScore >= 90.0:
|
||||||
return "Very Easy"
|
return "Very Easy"
|
||||||
elif rScore >= 80.0:
|
elif rScore >= 80.0:
|
||||||
return "Easy"
|
return "Easy"
|
||||||
@@ -78,13 +78,15 @@ class TextAnalysis():
|
|||||||
#
|
#
|
||||||
|
|
||||||
def _countWords(self):
|
def _countWords(self):
|
||||||
"""Counts the number of words in a text by simply splitting on all white spaces.
|
"""Counts the number of words in a text by simply splitting on
|
||||||
|
all white spaces.
|
||||||
"""
|
"""
|
||||||
return len(self.theText.strip().split())
|
return len(self.theText.strip().split())
|
||||||
|
|
||||||
def _countSentences(self):
|
def _countSentences(self):
|
||||||
"""Counts the number of non-repeated sentence endings seen in the text.
|
"""Counts the number of non-repeated sentence endings seen in
|
||||||
Note: This will count filenames and urls as multiple sentences.
|
the text. Note: This will count filenames and urls as multiple
|
||||||
|
sentences.
|
||||||
"""
|
"""
|
||||||
nSent = 0
|
nSent = 0
|
||||||
sawEnd = False
|
sawEnd = False
|
||||||
@@ -98,7 +100,8 @@ class TextAnalysis():
|
|||||||
return nSent
|
return nSent
|
||||||
|
|
||||||
def _countParagraphs(self, pThreshold=2):
|
def _countParagraphs(self, pThreshold=2):
|
||||||
"""Counts the number of paragraphs by counting repeated line breaks.
|
"""Counts the number of paragraphs by counting repeated line
|
||||||
|
breaks.
|
||||||
"""
|
"""
|
||||||
nPara = 1
|
nPara = 1
|
||||||
sawEnd = 0
|
sawEnd = 0
|
||||||
@@ -114,9 +117,10 @@ class TextAnalysis():
|
|||||||
return nPara
|
return nPara
|
||||||
|
|
||||||
def _countSyllablesEN(self):
|
def _countSyllablesEN(self):
|
||||||
"""Attempt to count the syllables in a piece of English language text.
|
"""Attempt to count the syllables in a piece of English language
|
||||||
This function tends to slightly over-estimate the number of syllables as it doesn't handle
|
text. This function tends to slightly over-estimate the number
|
||||||
the complexity of silent vowels in endings very well. It will count them all.
|
of syllables as it doesn't handle the complexity of silent
|
||||||
|
vowels in endings very well. It will count them all.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cleanText = ""
|
cleanText = ""
|
||||||
@@ -126,8 +130,8 @@ class TextAnalysis():
|
|||||||
else:
|
else:
|
||||||
cleanText += " "
|
cleanText += " "
|
||||||
|
|
||||||
asVow = "aeiouy'’"
|
asVow = "aeiouy'’"
|
||||||
dExept = ("ei","ie","ua","ia","eo")
|
dExept = ("ei","ie","ua","ia","eo")
|
||||||
theWords = cleanText.lower().split()
|
theWords = cleanText.lower().split()
|
||||||
allSylls = 0
|
allSylls = 0
|
||||||
for inWord in theWords:
|
for inWord in theWords:
|
||||||
@@ -160,7 +164,6 @@ class TextAnalysis():
|
|||||||
nSyll += 1
|
nSyll += 1
|
||||||
if nSyll < 1:
|
if nSyll < 1:
|
||||||
nSyll = 1
|
nSyll = 1
|
||||||
# print("%-15s: %d" % (inWord,nSyll))
|
|
||||||
allSylls += nSyll
|
allSylls += nSyll
|
||||||
|
|
||||||
return allSylls/len(theWords)
|
return allSylls/len(theWords)
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def setLanguage(self, theLang, projectDict=None):
|
def setLanguage(self, theLang, projectDict=None):
|
||||||
"""Load a dictionary for the language specified in the config. If that fails, we load a
|
"""Load a dictionary for the language specified in the config.
|
||||||
dummy dictionary so that lookups don't crash.
|
If that fails, we load a dummy dictionary so that lookups don't
|
||||||
|
crash.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if projectDict is None:
|
if projectDict is None:
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ def countWords(theText):
|
|||||||
if aLine[0] == "@" or aLine[0] == "%":
|
if aLine[0] == "@" or aLine[0] == "%":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if aLine[0:5] == "#### ":
|
if aLine[0:5] == "#### ":
|
||||||
wordCount -= 1
|
wordCount -= 1
|
||||||
charCount -= 5
|
charCount -= 5
|
||||||
countPara = False
|
countPara = False
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ setuptools.setup(
|
|||||||
license = "GNU General Public License v3",
|
license = "GNU General Public License v3",
|
||||||
url = "https://github.com/vkbo/novelWriter",
|
url = "https://github.com/vkbo/novelWriter",
|
||||||
entry_points = {
|
entry_points = {
|
||||||
"console_scripts" : ["novelWriter=nw:main"],
|
"console_scripts" : ["novelWriter-cli=nw:main"],
|
||||||
"gui_scripts" : ["novelWriter-gui=nw:main"],
|
"gui_scripts" : ["novelWriter=nw:main"],
|
||||||
},
|
},
|
||||||
packages = setuptools.find_packages(exclude=["docs","tests","sample"]),
|
packages = setuptools.find_packages(exclude=["docs","tests","sample"]),
|
||||||
include_package_data = True,
|
include_package_data = True,
|
||||||
|
|||||||
Reference in New Issue
Block a user