Merge pull request #816 from vkbo/style

Style
This commit is contained in:
Veronica Berglyd Olsen
2021-06-25 21:56:03 +02:00
committed by GitHub
93 changed files with 986 additions and 725 deletions
+2 -2
View File
@@ -25,5 +25,5 @@ jobs:
flake8 tests --count --select=E9,F63,F7,F82 --show-source --statistics flake8 tests --count --select=E9,F63,F7,F82 --show-source --statistics
- name: Coding Style Violations - name: Coding Style Violations
run: | run: |
flake8 nw --count --max-line-length=99 --ignore E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 --show-source --statistics flake8 nw --count --max-line-length=99 --ignore E203,E221,E226,E228,E241,E251 --show-source --statistics
flake8 tests --count --max-line-length=99 --ignore E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 --show-source --statistics flake8 tests --count --max-line-length=99 --ignore E203,E221,E226,E228,E241,E251 --show-source --statistics
+14 -27
View File
@@ -77,28 +77,28 @@ The `setup.cfg` file in the root of this project has the following settings for
matches the coding standard: matches the coding standard:
```conf ```conf
[flake8] [flake8]
ignore = E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 ignore = E203,E221,E226,E228,E241,E251
max-line-length = 99 max-line-length = 99
exclude = docs/* exclude = docs/*
``` ```
The command line equivalent, with reporting, is: The command line equivalent, with reporting, is:
```bash ```bash
flake8 . --count --ignore E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 --max-line-length=99 --show-source --statistics flake8 . --count --ignore E203,E221,E226,E228,E241,E251 --max-line-length=99 --show-source --statistics
``` ```
Passing this check is required before contributions are merged into the `main` branch. This is Passing this check is required before contributions are merged into the `main`, `testing` or `dev`
checked automatically when you make a pull request. You can run the `flake8` command locally to branches. This is checked automatically when you make a pull request. You can run the `flake8`
check beforehand. The full command will give you a detailed description of the code lines that do command locally to check beforehand. The full command will give you a detailed description of the
not conform to the standard. code lines that do not conform to the standard.
## Ignored Errors ## Ignored Errors
Some `flake8` error codes are ignored for this project for various reasons. The source also uses Some `flake8` error codes are ignored for this project for various reasons. The source also uses
camelCase function and variable names. This is the standard for the Qt libraries novelWriter camelCase function and variable names. This is the standard for the Qt libraries novelWriter
integrates with. It also happens to be the author's personal preferences. integrates with. It also happens to be the author's personal preferences. (Yay!)
The reason behind the other ignored error codes are listed below. Many of them are due to PEP8 not The reason behind the other ignored error codes are listed below. Most of them are due to PEP8 not
permitting column alignment as opposed to many other coding styles, like for instance for Go. I permitting column alignment as opposed to many other coding styles, like for instance for Go. I
find them useful in regions of bulk value assignments. There's a reason why tables are more find them useful in regions of bulk value assignments. There's a reason why tables are more
readable than lists. They should be used sparingly though. readable than lists. They should be used sparingly though.
@@ -113,10 +113,12 @@ The ignored errors are all `pycodestyle` errors, and they are documented
**Reason:** Column alignment. **Reason:** Column alignment.
**E226:** missing whitespace around arithmetic operator **E226:** missing whitespace around arithmetic operator
**Reason:** This doesn't actually follow the PEP8 recommendation of grouping longer equations by **Reason:** This doesn't actually follow the
operator precedence like `2*a + 3*b` instead of `a * a + 3 * b`. Generally, don't use spaces around [PEP8 recommendation](https://www.python.org/dev/peps/pep-0008/#other-recommendations)
`*`, `/` and `**`, but _do_ use spaces around `+` and `-`. For appending strings, the spaces can be of grouping longer equations by operator precedence like `2*a + 3*b` instead of `a * a + 3 * b`.
dropped. Don't use the `+` operator for appending multiple strings. Use formatting instead. Generally, don't use spaces around `*`, `/` and `**`, but _do_ use spaces around `+` and `-`.
For appending strings, the spaces can be dropped. Don't use the `+` operator for appending multiple
strings. Use formatting instead.
**E228** missing whitespace around modulo operator **E228** missing whitespace around modulo operator
**Reason:** See reason for E226. Formatting `%` like `/` and `*` should be possible. **Reason:** See reason for E226. Formatting `%` like `/` and `*` should be possible.
@@ -126,18 +128,3 @@ dropped. Don't use the `+` operator for appending multiple strings. Use formatti
**E251:** unexpected spaces around keyword / parameter equals **E251:** unexpected spaces around keyword / parameter equals
**Reason:** Column alignment. **Reason:** Column alignment.
**E261:** at least two spaces before inline comment
**Reason:** With syntax highlighting, this one doesn't make much sense.
**E266:** too many leading # for block comment
**Reason:** In the source multiple `#`s is sometimes used to indicate importance or heading level,
like markdown headers.
**E302:** expected 2 blank lines, found 0
**Reason:** Applies to classes. Instead, end classes with a comment like `# END Class ClassName` to
make it easier to see which class just ended. The double line break is then redundant.
**E305:** expected 2 blank lines after end of function or class
**Reason:** Instead, _always_ end a function with a `return`, preferably indented at function
level. The end of the function is then clear.
-1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# Configuration file for the Sphinx documentation builder. # Configuration file for the Sphinx documentation builder.
# #
-1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
Qt Base Translation File Qt Base Translation File
======================== ========================
+3 -4
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
novelWriter Start Script novelWriter Start Script
========================== ==========================
@@ -8,9 +7,9 @@ import os
import sys import sys
try: try:
import PyQt5.QtWidgets # noqa: F401 import PyQt5.QtWidgets # noqa: F401
import PyQt5.QtGui # noqa: F401 import PyQt5.QtGui # noqa: F401
import PyQt5.QtCore # noqa: F401 import PyQt5.QtCore # noqa: F401
except Exception: except Exception:
print("ERROR: Failed to load dependency PyQt5") print("ERROR: Failed to load dependency PyQt5")
sys.exit(1) sys.exit(1)
+6 -2
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Init File novelWriter Init File
======================= =======================
@@ -90,15 +89,19 @@ __docurl__ = "https://novelwriter.readthedocs.io"
# Add verbose logging level # Add verbose logging level
VERBOSE = 5 VERBOSE = 5
logging.addLevelName(VERBOSE, "VERBOSE") logging.addLevelName(VERBOSE, "VERBOSE")
def logVerbose(self, message, *args, **kws): def logVerbose(self, message, *args, **kws):
if self.isEnabledFor(VERBOSE): if self.isEnabledFor(VERBOSE):
self._log(VERBOSE, message, args, **kws) self._log(VERBOSE, message, args, **kws)
logging.Logger.verbose = logVerbose logging.Logger.verbose = logVerbose
# Initiating logging # Initiating logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
## ##
# Main Program # Main Program
## ##
@@ -106,6 +109,7 @@ logger = logging.getLogger(__name__)
# Load the main config as a global object # Load the main config as a global object
CONFIG = Config() CONFIG = Config()
def main(sysArgs=None): def main(sysArgs=None):
"""Parses command line, sets up logging, and launches main GUI. """Parses command line, sets up logging, and launches main GUI.
""" """
@@ -229,7 +233,7 @@ def main(sysArgs=None):
errorCode |= 16 errorCode |= 16
try: try:
import lxml # noqa: F401 import lxml # noqa: F401
except ImportError: except ImportError:
errorData.append("Python module 'lxml' is missing.") errorData.append("Python module 'lxml' is missing.")
errorCode |= 32 errorCode |= 32
+29 -11
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Common Functions novelWriter Common Functions
============================== ==============================
@@ -36,6 +35,7 @@ from nw.constants import nwConst, nwUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def checkString(value, default, allowNone=False): def checkString(value, default, allowNone=False):
"""Check if a variable is a string or a none. """Check if a variable is a string or a none.
""" """
@@ -48,6 +48,7 @@ def checkString(value, default, allowNone=False):
return str(value) return str(value)
return default return default
def checkInt(value, default, allowNone=False): def checkInt(value, default, allowNone=False):
"""Check if a variable is an integer or a none. """Check if a variable is an integer or a none.
""" """
@@ -61,6 +62,7 @@ def checkInt(value, default, allowNone=False):
except Exception: except Exception:
return default return default
def checkBool(value, default, allowNone=False): def checkBool(value, default, allowNone=False):
"""Check if a variable is a boolean or a none. """Check if a variable is a boolean or a none.
""" """
@@ -85,6 +87,7 @@ def checkBool(value, default, allowNone=False):
return default return default
return default return default
def checkHandle(value, default, allowNone=False): def checkHandle(value, default, allowNone=False):
"""Check if a value is a handle. """Check if a value is a handle.
""" """
@@ -97,6 +100,7 @@ def checkHandle(value, default, allowNone=False):
return str(value) return str(value)
return default return default
def isHandle(theString): def isHandle(theString):
"""Check if a string is a valid novelWriter handle. """Check if a string is a valid novelWriter handle.
Note: This is case sensitive. Must be lower case! Note: This is case sensitive. Must be lower case!
@@ -110,6 +114,7 @@ def isHandle(theString):
return False return False
return True return True
def isTitleTag(theString): def isTitleTag(theString):
"""Check if a string is a valid title string. """Check if a string is a valid title string.
""" """
@@ -124,21 +129,25 @@ def isTitleTag(theString):
return False return False
return True return True
def isItemClass(theString): def isItemClass(theString):
"""Check if an item is a calid nwItemClass identifier. """Check if an item is a calid nwItemClass identifier.
""" """
return theString in nwItemClass.__members__ return theString in nwItemClass.__members__
def isItemType(theString): def isItemType(theString):
"""Check if an item is a calid nwItemType identifier. """Check if an item is a calid nwItemType identifier.
""" """
return theString in nwItemType.__members__ return theString in nwItemType.__members__
def isItemLayout(theString): def isItemLayout(theString):
"""Check if an item is a calid nwItemLayout identifier. """Check if an item is a calid nwItemLayout identifier.
""" """
return theString in nwItemLayout.__members__ return theString in nwItemLayout.__members__
def hexToInt(value, default=0): def hexToInt(value, default=0):
"""Convert a hex string to an integer. """Convert a hex string to an integer.
""" """
@@ -149,6 +158,7 @@ def hexToInt(value, default=0):
return default return default
return default return default
def formatInt(theInt): def formatInt(theInt):
"""Formats an integer with k, M, G etc. """Formats an integer with k, M, G etc.
""" """
@@ -168,6 +178,7 @@ def formatInt(theInt):
return str(theInt) return str(theInt)
def formatTimeStamp(theTime, fileSafe=False): def formatTimeStamp(theTime, fileSafe=False):
"""Take a number (on the format returned by time.time()) and convert """Take a number (on the format returned by time.time()) and convert
it to a timestamp string. it to a timestamp string.
@@ -177,6 +188,7 @@ def formatTimeStamp(theTime, fileSafe=False):
else: else:
return datetime.fromtimestamp(theTime).strftime(nwConst.FMT_TSTAMP) return datetime.fromtimestamp(theTime).strftime(nwConst.FMT_TSTAMP)
def formatTime(tS): def formatTime(tS):
"""Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format """Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format
if a full day or longer. if a full day or longer.
@@ -188,6 +200,7 @@ def formatTime(tS):
return f"{tS//3600:02d}:{tS%3600//60:02d}:{tS%60:02d}" return f"{tS//3600:02d}:{tS%3600//60:02d}:{tS%60:02d}"
return "ERROR" return "ERROR"
def splitVersionNumber(vString): def splitVersionNumber(vString):
""" Splits a version string on the form aa.bb.cc into major, minor """ Splits a version string on the form aa.bb.cc into major, minor
and patch, and computes an integer value aabbcc. and patch, and computes an integer value aabbcc.
@@ -211,6 +224,7 @@ def splitVersionNumber(vString):
return [vMajor, vMinor, vPatch, vInt] return [vMajor, vMinor, vPatch, vInt]
def transferCase(theSource, theTarget): def transferCase(theSource, theTarget):
"""Transfers the case of the source word to the target word. This """Transfers the case of the source word to the target word. This
will consider all upper or lower, and first char capitalisation. will consider all upper or lower, and first char capitalisation.
@@ -232,6 +246,7 @@ def transferCase(theSource, theTarget):
return theResult return theResult
def fuzzyTime(secDiff): def fuzzyTime(secDiff):
"""Converts a time difference in seconds into a fuzzy time string. """Converts a time difference in seconds into a fuzzy time string.
""" """
@@ -247,43 +262,43 @@ def fuzzyTime(secDiff):
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "a minute ago" "Common", "a minute ago"
) )
elif secDiff < 3300: # 55 minutes elif secDiff < 3300: # 55 minutes
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "{0} minutes ago" "Common", "{0} minutes ago"
).format(int(round(secDiff/60))) ).format(int(round(secDiff/60)))
elif secDiff < 5400: # 90 minutes elif secDiff < 5400: # 90 minutes
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "an hour ago" "Common", "an hour ago"
) )
elif secDiff < 84600: # 23.5 hours elif secDiff < 84600: # 23.5 hours
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "{0} hours ago" "Common", "{0} hours ago"
).format(int(round(secDiff/3600))) ).format(int(round(secDiff/3600)))
elif secDiff < 129600: # 1.5 days elif secDiff < 129600: # 1.5 days
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "a day ago" "Common", "a day ago"
) )
elif secDiff < 561600: # 6.5 days elif secDiff < 561600: # 6.5 days
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "{0} days ago" "Common", "{0} days ago"
).format(int(round(secDiff/86400))) ).format(int(round(secDiff/86400)))
elif secDiff < 907200: # 10.5 days elif secDiff < 907200: # 10.5 days
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "a week ago" "Common", "a week ago"
) )
elif secDiff < 2419200: # 28 days elif secDiff < 2419200: # 28 days
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "{0} weeks ago" "Common", "{0} weeks ago"
).format(int(round(secDiff/604800))) ).format(int(round(secDiff/604800)))
elif secDiff < 3888000: # 45 days elif secDiff < 3888000: # 45 days
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "a month ago" "Common", "a month ago"
) )
elif secDiff < 29808000: # 345 days elif secDiff < 29808000: # 345 days
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "{0} months ago" "Common", "{0} months ago"
).format(int(round(secDiff/2592000))) ).format(int(round(secDiff/2592000)))
elif secDiff < 47336400: # 1.5 years elif secDiff < 47336400: # 1.5 years
return QCoreApplication.translate( return QCoreApplication.translate(
"Common", "a year ago" "Common", "a year ago"
) )
@@ -292,6 +307,7 @@ def fuzzyTime(secDiff):
"Common", "{0} years ago" "Common", "{0} years ago"
).format(int(round(secDiff/31557600))) ).format(int(round(secDiff/31557600)))
def makeFileNameSafe(theText): def makeFileNameSafe(theText):
"""Returns a filename safe version of the text. """Returns a filename safe version of the text.
""" """
@@ -301,6 +317,7 @@ def makeFileNameSafe(theText):
cleanName += c cleanName += c
return cleanName return cleanName
def getGuiItem(theName): def getGuiItem(theName):
"""Returns a QtWidget based on its objectName. """Returns a QtWidget based on its objectName.
""" """
@@ -309,6 +326,7 @@ def getGuiItem(theName):
return qWidget return qWidget
return None return None
def numberToRoman(numVal, isLower=False): def numberToRoman(numVal, isLower=False):
"""Convert an integer to a roman number. """Convert an integer to a roman number.
""" """
+73 -73
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Config Class novelWriter Config Class
========================== ==========================
@@ -45,6 +44,7 @@ from nw.constants import nwConst, nwFiles, nwUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Config: class Config:
CNF_STR = 0 CNF_STR = 0
@@ -82,27 +82,27 @@ class Config:
self.helpPath = None # The full path to the novelwriter .qhc help file self.helpPath = None # The full path to the novelwriter .qhc help file
# Runtime Settings and Variables # Runtime Settings and Variables
self.confChanged = False # True whenever the config has chenged, false after save self.confChanged = False # True whenever the config has chenged, false after save
self.hasHelp = False # True if the Qt help files are present in the assets folder self.hasHelp = False # True if the Qt help files are present in the assets folder
## General # General
self.guiTheme = "default" self.guiTheme = "default"
self.guiSyntax = "default_light" self.guiSyntax = "default_light"
self.guiIcons = "typicons_colour_light" self.guiIcons = "typicons_colour_light"
self.guiDark = False # Load icons for dark backgrounds, if available self.guiDark = False # Load icons for dark backgrounds, if available
self.guiFont = "" # Defaults to system default font self.guiFont = "" # Defaults to system default font
self.guiFontSize = 11 # Is overridden if system default is loaded self.guiFontSize = 11 # Is overridden if system default is loaded
self.guiScale = 1.0 # Set automatically by Theme class self.guiScale = 1.0 # Set automatically by Theme class
self.lastNotes = "0x0" # The latest release notes that have been shown self.lastNotes = "0x0" # The latest release notes that have been shown
## Localisation # Localisation
self.qLocal = QLocale.system() self.qLocal = QLocale.system()
self.guiLang = self.qLocal.name() self.guiLang = self.qLocal.name()
self.qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath) self.qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
self.nwLangPath = None self.nwLangPath = None
self.qtTrans = {} self.qtTrans = {}
## Sizes # Sizes
self.winGeometry = [1200, 650] self.winGeometry = [1200, 650]
self.prefGeometry = [700, 615] self.prefGeometry = [700, 615]
self.treeColWidth = [200, 50, 30] self.treeColWidth = [200, 50, 30]
@@ -114,54 +114,54 @@ class Config:
self.outlnPanePos = [500, 150] self.outlnPanePos = [500, 150]
self.isFullScreen = False self.isFullScreen = False
## Features # Features
self.hideVScroll = False # Hide vertical scroll bars on main widgets self.hideVScroll = False # Hide vertical scroll bars on main widgets
self.hideHScroll = False # Hide horizontal scroll bars on main widgets self.hideHScroll = False # Hide horizontal scroll bars on main widgets
## Project # Project
self.autoSaveProj = 60 # Interval for auto-saving project in seconds self.autoSaveProj = 60 # Interval for auto-saving project in seconds
self.autoSaveDoc = 30 # Interval for auto-saving document in seconds self.autoSaveDoc = 30 # Interval for auto-saving document in seconds
## Text Editor # Text Editor
self.textFont = None # Editor font self.textFont = None # Editor font
self.textSize = 12 # Editor font size self.textSize = 12 # Editor font size
self.textFixedW = True # Keep editor text fixed width self.textFixedW = True # Keep editor text fixed width
self.textWidth = 600 # Editor text width self.textWidth = 600 # Editor text width
self.textMargin = 40 # Editor/viewer text margin self.textMargin = 40 # Editor/viewer text margin
self.tabWidth = 40 # Editor tabulator width self.tabWidth = 40 # Editor tabulator width
self.focusWidth = 800 # Focus Mode text width self.focusWidth = 800 # Focus Mode text width
self.hideFocusFooter = False # Hide document footer in Focus Mode self.hideFocusFooter = False # Hide document footer in Focus Mode
self.showFullPath = True # Show full document path in editor header self.showFullPath = True # Show full document path in editor header
self.autoSelect = True # Auto-select word when applying format with no selection self.autoSelect = True # Auto-select word when applying format with no selection
self.doJustify = False # Justify text self.doJustify = False # Justify text
self.showTabsNSpaces = False # Show tabs and spaces in edior self.showTabsNSpaces = False # Show tabs and spaces in edior
self.showLineEndings = False # Show line endings in editor self.showLineEndings = False # Show line endings in editor
self.showMultiSpaces = True # Highlight multiple spaces in the text self.showMultiSpaces = True # Highlight multiple spaces in the text
self.doReplace = True # Enable auto-replace as you type self.doReplace = True # Enable auto-replace as you type
self.doReplaceSQuote = True # Smart single quotes self.doReplaceSQuote = True # Smart single quotes
self.doReplaceDQuote = True # Smart double quotes self.doReplaceDQuote = True # Smart double quotes
self.doReplaceDash = True # Replace multiple hyphens with dashes self.doReplaceDash = True # Replace multiple hyphens with dashes
self.doReplaceDots = True # Replace three dots with ellipsis self.doReplaceDots = True # Replace three dots with ellipsis
self.scrollPastEnd = True # Allow scrolling past end of document self.scrollPastEnd = True # Allow scrolling past end of document
self.autoScroll = False # Typewriter-like scrolling self.autoScroll = False # Typewriter-like scrolling
self.autoScrollPos = 30 # Start point for typewriter-like scrolling self.autoScrollPos = 30 # Start point for typewriter-like scrolling
self.wordCountTimer = 5.0 # Interval for word count update in seconds self.wordCountTimer = 5.0 # Interval for word count update in seconds
self.bigDocLimit = 800 # Size threshold for heavy editor features in kilobytes self.bigDocLimit = 800 # Size threshold for heavy editor features in kilobytes
self.highlightQuotes = True # Highlight text in quotes self.highlightQuotes = True # Highlight text in quotes
self.allowOpenSQuote = False # Allow open-ended single quotes self.allowOpenSQuote = False # Allow open-ended single quotes
self.allowOpenDQuote = True # Allow open-ended double quotes self.allowOpenDQuote = True # Allow open-ended double quotes
self.highlightEmph = True # Add colour to text emphasis self.highlightEmph = True # Add colour to text emphasis
self.stopWhenIdle = True # Stop the status bar clock when the user is idle self.stopWhenIdle = True # Stop the status bar clock when the user is idle
self.userIdleTime = 300 # Time of inactivity to consider user idle self.userIdleTime = 300 # Time of inactivity to consider user idle
## User-Selected Symbols # User-Selected Symbols
self.fmtApostrophe = nwUnicode.U_RSQUO self.fmtApostrophe = nwUnicode.U_RSQUO
self.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO] self.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO]
self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO] self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO]
@@ -169,11 +169,11 @@ class Config:
self.fmtPadAfter = "" self.fmtPadAfter = ""
self.fmtPadThin = False self.fmtPadThin = False
## Spell Checking # Spell Checking
self.spellTool = None self.spellTool = None
self.spellLanguage = None self.spellLanguage = None
## Search Bar Switches # Search Bar Switches
self.searchCase = False self.searchCase = False
self.searchWord = False self.searchWord = False
self.searchRegEx = False self.searchRegEx = False
@@ -181,12 +181,12 @@ class Config:
self.searchNextFile = False self.searchNextFile = False
self.searchMatchCap = False self.searchMatchCap = False
## Backup # Backup
self.backupPath = "" self.backupPath = ""
self.backupOnClose = False self.backupOnClose = False
self.askBeforeBackup = True self.askBeforeBackup = True
## State # State
self.showRefPanel = True self.showRefPanel = True
self.viewComments = True self.viewComments = True
self.viewSynopsis = True self.viewSynopsis = True
@@ -235,8 +235,8 @@ class Config:
self.kernelVer = "Unknown" self.kernelVer = "Unknown"
# Packages # Packages
self.hasEnchant = False # The pyenchant package self.hasEnchant = False # The pyenchant package
self.hasAssistant = False # The Qt Assistant executable self.hasAssistant = False # The Qt Assistant executable
# Recent Cache # Recent Cache
self.recentProj = {} self.recentProj = {}
@@ -382,9 +382,9 @@ class Config:
self.qtTrans = {} self.qtTrans = {}
langList = [ langList = [
(self.qtLangPath, "qtbase"), # Qt 5.x (self.qtLangPath, "qtbase"), # Qt 5.x
(self.nwLangPath, "qtbase"), # Alternative Qt 5.x (self.nwLangPath, "qtbase"), # Alternative Qt 5.x
(self.nwLangPath, "nw"), # novelWriter (self.nwLangPath, "nw"), # novelWriter
] ]
for lngPath, lngBase in langList: for lngPath, lngBase in langList:
for lngCode in self.qLocal.uiLanguages(): for lngCode in self.qLocal.uiLanguages():
@@ -445,7 +445,7 @@ class Config:
self.errData.append(str(e)) self.errData.append(str(e))
return False return False
## Main # Main
cnfSec = "Main" cnfSec = "Main"
self.guiTheme = self._parseLine( self.guiTheme = self._parseLine(
cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme
@@ -472,7 +472,7 @@ class Config:
cnfParse, cnfSec, "guilang", self.CNF_STR, self.guiLang cnfParse, cnfSec, "guilang", self.CNF_STR, self.guiLang
) )
## Sizes # Sizes
cnfSec = "Sizes" cnfSec = "Sizes"
self.winGeometry = self._parseLine( self.winGeometry = self._parseLine(
cnfParse, cnfSec, "geometry", self.CNF_I_LST, self.winGeometry cnfParse, cnfSec, "geometry", self.CNF_I_LST, self.winGeometry
@@ -511,7 +511,7 @@ class Config:
cnfParse, cnfSec, "hidehscroll", self.CNF_BOOL, self.hideHScroll cnfParse, cnfSec, "hidehscroll", self.CNF_BOOL, self.hideHScroll
) )
## Project # Project
cnfSec = "Project" cnfSec = "Project"
self.autoSaveProj = self._parseLine( self.autoSaveProj = self._parseLine(
cnfParse, cnfSec, "autosaveproject", self.CNF_INT, self.autoSaveProj cnfParse, cnfSec, "autosaveproject", self.CNF_INT, self.autoSaveProj
@@ -520,7 +520,7 @@ class Config:
cnfParse, cnfSec, "autosavedoc", self.CNF_INT, self.autoSaveDoc cnfParse, cnfSec, "autosavedoc", self.CNF_INT, self.autoSaveDoc
) )
## Editor # Editor
cnfSec = "Editor" cnfSec = "Editor"
self.textFont = self._parseLine( self.textFont = self._parseLine(
cnfParse, cnfSec, "textfont", self.CNF_STR, self.textFont cnfParse, cnfSec, "textfont", self.CNF_STR, self.textFont
@@ -631,7 +631,7 @@ class Config:
cnfParse, cnfSec, "useridletime", self.CNF_INT, self.userIdleTime cnfParse, cnfSec, "useridletime", self.CNF_INT, self.userIdleTime
) )
## Backup # Backup
cnfSec = "Backup" cnfSec = "Backup"
self.backupPath = self._parseLine( self.backupPath = self._parseLine(
cnfParse, cnfSec, "backuppath", self.CNF_STR, self.backupPath cnfParse, cnfSec, "backuppath", self.CNF_STR, self.backupPath
@@ -643,7 +643,7 @@ class Config:
cnfParse, cnfSec, "askbeforebackup", self.CNF_BOOL, self.askBeforeBackup cnfParse, cnfSec, "askbeforebackup", self.CNF_BOOL, self.askBeforeBackup
) )
## State # State
cnfSec = "State" cnfSec = "State"
self.showRefPanel = self._parseLine( self.showRefPanel = self._parseLine(
cnfParse, cnfSec, "showrefpanel", self.CNF_BOOL, self.showRefPanel cnfParse, cnfSec, "showrefpanel", self.CNF_BOOL, self.showRefPanel
@@ -673,7 +673,7 @@ class Config:
cnfParse, cnfSec, "searchmatchcap", self.CNF_BOOL, self.searchMatchCap cnfParse, cnfSec, "searchmatchcap", self.CNF_BOOL, self.searchMatchCap
) )
## Path # Path
cnfSec = "Path" cnfSec = "Path"
self.lastPath = self._parseLine( self.lastPath = self._parseLine(
cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath
@@ -704,7 +704,7 @@ class Config:
# Set options # Set options
## Main # Main
cnfSec = "Main" cnfSec = "Main"
cnfParse.add_section(cnfSec) cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec, "timestamp", formatTimeStamp(time())) cnfParse.set(cnfSec, "timestamp", formatTimeStamp(time()))
@@ -717,7 +717,7 @@ class Config:
cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes)) cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes))
cnfParse.set(cnfSec, "guilang", str(self.guiLang)) cnfParse.set(cnfSec, "guilang", str(self.guiLang))
## Sizes # Sizes
cnfSec = "Sizes" cnfSec = "Sizes"
cnfParse.add_section(cnfSec) cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec, "geometry", self._packList(self.winGeometry)) cnfParse.set(cnfSec, "geometry", self._packList(self.winGeometry))
@@ -733,13 +733,13 @@ class Config:
cnfParse.set(cnfSec, "hidevscroll", str(self.hideVScroll)) cnfParse.set(cnfSec, "hidevscroll", str(self.hideVScroll))
cnfParse.set(cnfSec, "hidehscroll", str(self.hideHScroll)) cnfParse.set(cnfSec, "hidehscroll", str(self.hideHScroll))
## Project # Project
cnfSec = "Project" cnfSec = "Project"
cnfParse.add_section(cnfSec) cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec, "autosaveproject", str(self.autoSaveProj)) cnfParse.set(cnfSec, "autosaveproject", str(self.autoSaveProj))
cnfParse.set(cnfSec, "autosavedoc", str(self.autoSaveDoc)) cnfParse.set(cnfSec, "autosavedoc", str(self.autoSaveDoc))
## Editor # Editor
cnfSec = "Editor" cnfSec = "Editor"
cnfParse.add_section(cnfSec) cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec, "textfont", str(self.textFont)) cnfParse.set(cnfSec, "textfont", str(self.textFont))
@@ -779,14 +779,14 @@ class Config:
cnfParse.set(cnfSec, "stopwhenidle", str(self.stopWhenIdle)) cnfParse.set(cnfSec, "stopwhenidle", str(self.stopWhenIdle))
cnfParse.set(cnfSec, "useridletime", str(self.userIdleTime)) cnfParse.set(cnfSec, "useridletime", str(self.userIdleTime))
## Backup # Backup
cnfSec = "Backup" cnfSec = "Backup"
cnfParse.add_section(cnfSec) cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec, "backuppath", str(self.backupPath)) cnfParse.set(cnfSec, "backuppath", str(self.backupPath))
cnfParse.set(cnfSec, "backuponclose", str(self.backupOnClose)) cnfParse.set(cnfSec, "backuponclose", str(self.backupOnClose))
cnfParse.set(cnfSec, "askbeforebackup", str(self.askBeforeBackup)) cnfParse.set(cnfSec, "askbeforebackup", str(self.askBeforeBackup))
## State # State
cnfSec = "State" cnfSec = "State"
cnfParse.add_section(cnfSec) cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec, "showrefpanel", str(self.showRefPanel)) cnfParse.set(cnfSec, "showrefpanel", str(self.showRefPanel))
@@ -799,7 +799,7 @@ class Config:
cnfParse.set(cnfSec, "searchnextfile", str(self.searchNextFile)) cnfParse.set(cnfSec, "searchnextfile", str(self.searchNextFile))
cnfParse.set(cnfSec, "searchmatchcap", str(self.searchMatchCap)) cnfParse.set(cnfSec, "searchmatchcap", str(self.searchMatchCap))
## Path # Path
cnfSec = "Path" cnfSec = "Path"
cnfParse.add_section(cnfSec) cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec, "lastpath", str(self.lastPath)) cnfParse.set(cnfSec, "lastpath", str(self.lastPath))
@@ -1119,7 +1119,7 @@ class Config:
"""Cheks if we have the optional packages used by some features. """Cheks if we have the optional packages used by some features.
""" """
try: try:
import enchant # noqa: F401 import enchant # noqa: F401
self.hasEnchant = True self.hasEnchant = True
logger.debug("Checking package 'pyenchant': OK") logger.debug("Checking package 'pyenchant': OK")
except Exception: except Exception:
+83 -74
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Constants novelWriter Constants
======================= =======================
@@ -28,22 +27,24 @@ from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
from nw.enum import nwItemClass, nwItemLayout, nwItemType, nwOutline from nw.enum import nwItemClass, nwItemLayout, nwItemType, nwOutline
def trConst(tString): def trConst(tString):
"""Wrapper function for locally translating constants. """Wrapper function for locally translating constants.
""" """
return QCoreApplication.translate("Constant", tString) return QCoreApplication.translate("Constant", tString)
class nwConst(): class nwConst():
# Date and Time Formats # Date and Time Formats
FMT_TSTAMP = "%Y-%m-%d %H:%M:%S" # Default format FMT_TSTAMP = "%Y-%m-%d %H:%M:%S" # Default format
FMT_FSTAMP = "%Y-%m-%d %H.%M.%S" # FileName safe format FMT_FSTAMP = "%Y-%m-%d %H.%M.%S" # FileName safe format
FMT_DSTAMP = "%Y-%m-%d" # Date only format FMT_DSTAMP = "%Y-%m-%d" # Date only format
# Various Hard Limits # Various Hard Limits
MAX_DEPTH = 30 # Maximum folder depth of a project MAX_DEPTH = 30 # Maximum folder depth of a project
MAX_DOCSIZE = 5000000 # Maxium size of a single document MAX_DOCSIZE = 5000000 # Maxium size of a single document
MAX_BUILDSIZE = 10000000 # Maxium size of a project build MAX_BUILDSIZE = 10000000 # Maxium size of a project build
# Spell Check Providers # Spell Check Providers
SP_INTERNAL = "internal" SP_INTERNAL = "internal"
@@ -51,6 +52,7 @@ class nwConst():
# END Class nwConst # END Class nwConst
class nwLists(): class nwLists():
"""Lists used for grouping various other constants. """Lists used for grouping various other constants.
""" """
@@ -65,6 +67,7 @@ class nwLists():
# END Class nwLists # END Class nwLists
class nwRegEx(): class nwRegEx():
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)" FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
@@ -73,6 +76,7 @@ class nwRegEx():
# END Class nwRegEx # END Class nwRegEx
class nwFiles(): class nwFiles():
PROJ_FILE = "nwProject.nwx" PROJ_FILE = "nwProject.nwx"
@@ -87,6 +91,7 @@ class nwFiles():
# END Class nwFiles # END Class nwFiles
class nwKeyWords: class nwKeyWords:
TAG_KEY = "@tag" TAG_KEY = "@tag"
@@ -121,6 +126,7 @@ class nwKeyWords:
# END Class nwKeyWords # END Class nwKeyWords
class nwLabels(): class nwLabels():
CLASS_NAME = { CLASS_NAME = {
@@ -218,6 +224,7 @@ class nwLabels():
# END Class nwLabels # END Class nwLabels
class nwQuotes(): class nwQuotes():
"""Allowed quotation marks. """Allowed quotation marks.
Source: https://en.wikipedia.org/wiki/Quotation_mark Source: https://en.wikipedia.org/wiki/Quotation_mark
@@ -249,79 +256,80 @@ class nwQuotes():
# END Class nwQuotes # END Class nwQuotes
class nwUnicode: class nwUnicode:
"""Supported unicode character constants and their HTML equivalents. """Supported unicode character constants and their HTML equivalents.
""" """
# Unicode Constants # Unicode Constants
# ================= # =================
## Quotation Marks # Quotation Marks
U_QUOT = "\u0022" # Quotation mark U_QUOT = "\u0022" # Quotation mark
U_APOS = "\u0027" # Apostrophe U_APOS = "\u0027" # Apostrophe
U_LAQUO = "\u00ab" # Left-pointing double angle quotation mark U_LAQUO = "\u00ab" # Left-pointing double angle quotation mark
U_RAQUO = "\u00bb" # Right-pointing double angle quotation mark U_RAQUO = "\u00bb" # Right-pointing double angle quotation mark
U_LSQUO = "\u2018" # Left single quotation mark U_LSQUO = "\u2018" # Left single quotation mark
U_RSQUO = "\u2019" # Right single quotation mark U_RSQUO = "\u2019" # Right single quotation mark
U_SBQUO = "\u201a" # Single low-9 quotation mark U_SBQUO = "\u201a" # Single low-9 quotation mark
U_SUQUO = "\u201b" # Single high-reversed-9 quotation mark U_SUQUO = "\u201b" # Single high-reversed-9 quotation mark
U_LDQUO = "\u201c" # Left double quotation mark U_LDQUO = "\u201c" # Left double quotation mark
U_RDQUO = "\u201d" # Right double quotation mark U_RDQUO = "\u201d" # Right double quotation mark
U_BDQUO = "\u201e" # Double low-9 quotation mark U_BDQUO = "\u201e" # Double low-9 quotation mark
U_UDQUO = "\u201f" # Double high-reversed-9 quotation mark U_UDQUO = "\u201f" # Double high-reversed-9 quotation mark
U_LSAQUO = "\u2039" # Single left-pointing angle quotation mark U_LSAQUO = "\u2039" # Single left-pointing angle quotation mark
U_RSAQUO = "\u203a" # Single right-pointing angle quotation mark U_RSAQUO = "\u203a" # Single right-pointing angle quotation mark
U_BDRQUO = "\u2e42" # Double low-reversed-9 quotation mark U_BDRQUO = "\u2e42" # Double low-reversed-9 quotation mark
U_LCQUO = "\u300c" # Left corner bracket U_LCQUO = "\u300c" # Left corner bracket
U_RCQUO = "\u300d" # Right corner bracket U_RCQUO = "\u300d" # Right corner bracket
U_LWCQUO = "\u300e" # Left white corner bracket U_LWCQUO = "\u300e" # Left white corner bracket
U_RWCQUO = "\u300f" # Right white corner bracket U_RWCQUO = "\u300f" # Right white corner bracket
## Punctuation # Punctuation
U_FGDASH = "\u2012" # Figure dash U_FGDASH = "\u2012" # Figure dash
U_ENDASH = "\u2013" # Short dash U_ENDASH = "\u2013" # Short dash
U_EMDASH = "\u2014" # Long dash U_EMDASH = "\u2014" # Long dash
U_HBAR = "\u2015" # Horizontal bar U_HBAR = "\u2015" # Horizontal bar
U_HELLIP = "\u2026" # Ellipsis U_HELLIP = "\u2026" # Ellipsis
U_MAPOSS = "\u02bc" # Modifier letter single apostrophe U_MAPOSS = "\u02bc" # Modifier letter single apostrophe
U_PRIME = "\u2032" # Prime U_PRIME = "\u2032" # Prime
U_DPRIME = "\u2033" # Double prime U_DPRIME = "\u2033" # Double prime
## Spaces and Lines # Spaces and Lines
U_NBSP = "\u00a0" # Non-breaking space U_NBSP = "\u00a0" # Non-breaking space
U_THSP = "\u2009" # Thin space U_THSP = "\u2009" # Thin space
U_THNBSP = "\u202f" # Thin non-breaking space U_THNBSP = "\u202f" # Thin non-breaking space
U_ENSP = "\u2002" # Short (en) space U_ENSP = "\u2002" # Short (en) space
U_EMSP = "\u2003" # Long (em) space U_EMSP = "\u2003" # Long (em) space
U_LSEP = "\u2028" # Line separator U_LSEP = "\u2028" # Line separator
U_PSEP = "\u2029" # Paragraph separator U_PSEP = "\u2029" # Paragraph separator
## Symbols # Symbols
U_CHECK = "\u2714" # Heavy check mark U_CHECK = "\u2714" # Heavy check mark
U_CROSS = "\u2715" # Heavy cross mark U_CROSS = "\u2715" # Heavy cross mark
U_BULL = "\u2022" # List bullet U_BULL = "\u2022" # List bullet
U_TRBULL = "\u2023" # Triangle bullet U_TRBULL = "\u2023" # Triangle bullet
U_HYBULL = "\u2043" # Hyphen bullet U_HYBULL = "\u2043" # Hyphen bullet
U_FLOWER = "\u2055" # Flower punctuation mark U_FLOWER = "\u2055" # Flower punctuation mark
U_PERMIL = "\u2030" # Per mille sign U_PERMIL = "\u2030" # Per mille sign
U_DEGREE = "\u00b0" # Degree symbol U_DEGREE = "\u00b0" # Degree symbol
U_MINUS = "\u2212" # Minus sign U_MINUS = "\u2212" # Minus sign
U_TIMES = "\u00d7" # Multiplaction sign U_TIMES = "\u00d7" # Multiplaction sign
U_DIVIDE = "\u00f7" # Division sign U_DIVIDE = "\u00f7" # Division sign
## Arrows # Arrows
U_UTRI = "\u25b2" # Up-pointing triangle U_UTRI = "\u25b2" # Up-pointing triangle
U_UTRIS = "\u25b4" # Up-pointing triangle, small U_UTRIS = "\u25b4" # Up-pointing triangle, small
U_RTRI = "\u25b6" # Right-pointing triangle U_RTRI = "\u25b6" # Right-pointing triangle
U_RTRIS = "\u25b8" # Right-pointing triangle, small U_RTRIS = "\u25b8" # Right-pointing triangle, small
U_DTRI = "\u25bc" # Down-pointing triangle U_DTRI = "\u25bc" # Down-pointing triangle
U_DTRIS = "\u25be" # Down-pointing triangle, small U_DTRIS = "\u25be" # Down-pointing triangle, small
U_LTRI = "\u25c0" # Left-pointing triangle U_LTRI = "\u25c0" # Left-pointing triangle
U_LTRIS = "\u25c2" # Left-pointing triangle, small U_LTRIS = "\u25c2" # Left-pointing triangle, small
# HTML Equivalents # HTML Equivalents
# ================ # ================
## Quotes # Quotes
H_QUOT = "&quot;" H_QUOT = "&quot;"
H_APOS = "&#39;" H_APOS = "&#39;"
H_LAQUO = "&laquo;" H_LAQUO = "&laquo;"
@@ -342,7 +350,7 @@ class nwUnicode:
H_LWCQUO = "&#12302;" H_LWCQUO = "&#12302;"
H_RWCQUO = "&#12303;" H_RWCQUO = "&#12303;"
## Punctuation # Punctuation
H_FGDASH = "&#8210;" H_FGDASH = "&#8210;"
H_ENDASH = "&ndash;" H_ENDASH = "&ndash;"
H_EMDASH = "&mdash;" H_EMDASH = "&mdash;"
@@ -352,14 +360,14 @@ class nwUnicode:
H_PRIME = "&prime;" H_PRIME = "&prime;"
H_DPRIME = "&#8243;" H_DPRIME = "&#8243;"
## Spaces # Spaces
H_NBSP = "&nbsp;" H_NBSP = "&nbsp;"
H_THSP = "&thinsp;" H_THSP = "&thinsp;"
H_THNBSP = "&#8239;" H_THNBSP = "&#8239;"
H_ENSP = "&ensp;" H_ENSP = "&ensp;"
H_EMSP = "&emsp;" H_EMSP = "&emsp;"
## Symbols # Symbols
H_CHECK = "&#10004;" H_CHECK = "&#10004;"
H_CROSS = "&#10005;" H_CROSS = "&#10005;"
H_BULL = "&bull;" H_BULL = "&bull;"
@@ -372,7 +380,7 @@ class nwUnicode:
H_TIMES = "&times;" H_TIMES = "&times;"
H_DIVIDE = "&divide;" H_DIVIDE = "&divide;"
## Arrows # Arrows
H_UTRI = "&#9650;" H_UTRI = "&#9650;"
H_UTRIS = "&#9652;" H_UTRIS = "&#9652;"
H_RTRI = "&#9654;" H_RTRI = "&#9654;"
@@ -384,10 +392,11 @@ class nwUnicode:
# END Class nwUnicode # END Class nwUnicode
class nwHtmlUnicode(): class nwHtmlUnicode():
U_TO_H = { U_TO_H = {
## Quotes # Quotes
nwUnicode.U_QUOT : nwUnicode.H_QUOT, nwUnicode.U_QUOT : nwUnicode.H_QUOT,
nwUnicode.U_APOS : nwUnicode.H_APOS, nwUnicode.U_APOS : nwUnicode.H_APOS,
nwUnicode.U_LAQUO : nwUnicode.H_LAQUO, nwUnicode.U_LAQUO : nwUnicode.H_LAQUO,
@@ -408,7 +417,7 @@ class nwHtmlUnicode():
nwUnicode.U_LWCQUO : nwUnicode.H_LWCQUO, nwUnicode.U_LWCQUO : nwUnicode.H_LWCQUO,
nwUnicode.U_RWCQUO : nwUnicode.H_RWCQUO, nwUnicode.U_RWCQUO : nwUnicode.H_RWCQUO,
## Punctuation # Punctuation
nwUnicode.U_FGDASH : nwUnicode.H_FGDASH, nwUnicode.U_FGDASH : nwUnicode.H_FGDASH,
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH, nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH, nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
@@ -418,14 +427,14 @@ class nwHtmlUnicode():
nwUnicode.U_PRIME : nwUnicode.H_PRIME, nwUnicode.U_PRIME : nwUnicode.H_PRIME,
nwUnicode.U_DPRIME : nwUnicode.H_DPRIME, nwUnicode.U_DPRIME : nwUnicode.H_DPRIME,
## Spaces # Spaces
nwUnicode.U_NBSP : nwUnicode.H_NBSP, nwUnicode.U_NBSP : nwUnicode.H_NBSP,
nwUnicode.U_THSP : nwUnicode.H_THSP, nwUnicode.U_THSP : nwUnicode.H_THSP,
nwUnicode.U_THNBSP : nwUnicode.H_THNBSP, nwUnicode.U_THNBSP : nwUnicode.H_THNBSP,
nwUnicode.U_ENSP : nwUnicode.H_ENSP, nwUnicode.U_ENSP : nwUnicode.H_ENSP,
nwUnicode.U_EMSP : nwUnicode.H_EMSP, nwUnicode.U_EMSP : nwUnicode.H_EMSP,
## Symbols # Symbols
nwUnicode.U_CHECK : nwUnicode.H_CHECK, nwUnicode.U_CHECK : nwUnicode.H_CHECK,
nwUnicode.U_CROSS : nwUnicode.H_CROSS, nwUnicode.U_CROSS : nwUnicode.H_CROSS,
nwUnicode.U_BULL : nwUnicode.H_BULL, nwUnicode.U_BULL : nwUnicode.H_BULL,
+20 -1
View File
@@ -1,4 +1,23 @@
# -*- coding: utf-8 -*- """
novelWriter Core Init
=======================
This file is a part of novelWriter
Copyright 20182021, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from nw.core.document import NWDoc from nw.core.document import NWDoc
from nw.core.index import NWIndex, countWords from nw.core.index import NWIndex, countWords
+6 -6
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Document novelWriter Project Document
============================== ==============================
@@ -32,6 +31,7 @@ from nw.common import isHandle
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class NWDoc(): class NWDoc():
def __init__(self, theProject, theHandle): def __init__(self, theProject, theHandle):
@@ -39,11 +39,11 @@ class NWDoc():
self.theProject = theProject self.theProject = theProject
# Internal Variables # Internal Variables
self._theItem = None # The currently open item self._theItem = None # The currently open item
self._docHandle = None # The handle of the currently open item self._docHandle = None # The handle of the currently open item
self._fileLoc = None # The file location of the currently open item self._fileLoc = None # The file location of the currently open item
self._docMeta = {} # The meta data of the currently open item self._docMeta = {} # The meta data of the currently open item
self._docError = "" # The latest encountered IO error self._docError = "" # The latest encountered IO error
if isHandle(theHandle): if isHandle(theHandle):
self._docHandle = theHandle self._docHandle = theHandle
+5 -4
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Index novelWriter Project Index
=========================== ===========================
@@ -39,6 +38,7 @@ from nw.core.document import NWDoc
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class NWIndex(): class NWIndex():
H_VALID = ("H0", "H1", "H2", "H3", "H4") H_VALID = ("H0", "H1", "H2", "H3", "H4")
@@ -482,10 +482,10 @@ class NWIndex():
"""Scan a line starting with @ to check that it's valid. Then """Scan a line starting with @ to check that it's valid. Then
split it up into its elements and positions as two arrays. split it up into its elements and positions as two arrays.
""" """
theBits = [] # The elements of the string theBits = [] # The elements of the string
thePos = [] # The absolute position of each element thePos = [] # The absolute position of each element
aLine = aLine.rstrip() # Remove all trailing white spaces aLine = aLine.rstrip() # Remove all trailing white spaces
nChar = len(aLine) nChar = len(aLine)
if nChar < 2: if nChar < 2:
return False, theBits, thePos return False, theBits, thePos
@@ -895,6 +895,7 @@ class NWIndex():
# END Class NWIndex # END Class NWIndex
# =============================================================================================== # # =============================================================================================== #
# Simple Word Counter # Simple Word Counter
# =============================================================================================== # # =============================================================================================== #
+8 -8
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Item Class novelWriter Project Item Class
================================ ================================
@@ -33,6 +32,7 @@ from nw.common import checkInt, isHandle, isItemClass, isItemLayout, isItemType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class NWItem(): class NWItem():
def __init__(self, theProject): def __init__(self, theProject):
@@ -51,11 +51,11 @@ class NWItem():
self.isExported = True self.isExported = True
# Document Meta Data # Document Meta Data
self.charCount = 0 # Current character count self.charCount = 0 # Current character count
self.wordCount = 0 # Current word count self.wordCount = 0 # Current word count
self.paraCount = 0 # Current paragraph count self.paraCount = 0 # Current paragraph count
self.initCount = 0 # Initial word count self.initCount = 0 # Initial word count
self.cursorPos = 0 # Last cursor position self.cursorPos = 0 # Last cursor position
return return
@@ -251,7 +251,7 @@ class NWItem():
if isinstance(expState, str): if isinstance(expState, str):
self.isExpanded = (expState == str(True)) self.isExpanded = (expState == str(True))
else: else:
self.isExpanded = (expState == True) # noqa: E712 self.isExpanded = (expState == True) # noqa: E712
return return
def setExported(self, expState): def setExported(self, expState):
@@ -260,7 +260,7 @@ class NWItem():
if isinstance(expState, str): if isinstance(expState, str):
self.isExported = (expState == str(True)) self.isExported = (expState == str(True))
else: else:
self.isExported = (expState == True) # noqa: E712 self.isExported = (expState == True) # noqa: E712
return return
## ##
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Options Cache novelWriter Project Options Cache
=================================== ===================================
@@ -34,6 +33,7 @@ from nw.constants import nwFiles
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class OptionState(): class OptionState():
def __init__(self, theProject): def __init__(self, theProject):
+36 -36
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Wrapper novelWriter Project Wrapper
============================= =============================
@@ -50,6 +49,7 @@ from nw.constants import trConst, nwFiles, nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class NWProject(): class NWProject():
def __init__(self, theParent): def __init__(self, theParent):
@@ -59,48 +59,48 @@ class NWProject():
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
# Core Elements # Core Elements
self.optState = OptionState(self) # Project-specific GUI options self.optState = OptionState(self) # Project-specific GUI options
self.projTree = NWTree(self) # The project tree self.projTree = NWTree(self) # The project tree
self.langData = {} # Localisation data self.langData = {} # Localisation data
# Project Status # Project Status
self.projOpened = 0 # The time stamp of when the project file was opened self.projOpened = 0 # The time stamp of when the project file was opened
self.projChanged = False # The project has unsaved changes self.projChanged = False # The project has unsaved changes
self.projAltered = False # The project has been altered this session self.projAltered = False # The project has been altered this session
self.lockedBy = None # Data on which computer has the project open self.lockedBy = None # Data on which computer has the project open
self.saveCount = 0 # Meta data: number of saves self.saveCount = 0 # Meta data: number of saves
self.autoCount = 0 # Meta data: number of automatic saves self.autoCount = 0 # Meta data: number of automatic saves
self.editTime = 0 # The accumulated edit time read from the project file self.editTime = 0 # The accumulated edit time read from the project file
# Class Settings # Class Settings
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.projContent = None # The full path to the project's content folder self.projContent = None # The full path to the project's content folder
self.projDict = None # The spell check dictionary self.projDict = None # The spell check dictionary
self.projSpell = None # The spell check language, if different than default self.projSpell = None # The spell check language, if different than default
self.projLang = None # The project language, used for builds self.projLang = None # The project language, used for builds
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 = "" # Project name (working title) self.projName = "" # Project name (working title)
self.bookTitle = "" # The final title; should only be used for exports self.bookTitle = "" # The final title; should only be used for exports
self.bookAuthors = [] # A list of book authors self.bookAuthors = [] # A list of book authors
# Project Settings # Project Settings
self.autoReplace = {} # Text to auto-replace on exports self.autoReplace = {} # Text to auto-replace on exports
self.titleFormat = {} # The formatting of titles for exports self.titleFormat = {} # The formatting of titles for exports
self.spellCheck = False # Controls the spellcheck-as-you-type feature self.spellCheck = False # Controls the spellcheck-as-you-type feature
self.autoOutline = True # If true, the Project Outline is updated automatically self.autoOutline = True # If true, the Project Outline is updated automatically
self.statusItems = None # Novel file progress status values self.statusItems = None # Novel file progress status values
self.importItems = None # Note file importance values self.importItems = None # Note file importance values
self.lastEdited = None # The handle of the last file to be edited self.lastEdited = None # The handle of the last file to be edited
self.lastViewed = None # The handle of the last file to be viewed self.lastViewed = None # The handle of the last file to be viewed
self.lastWCount = 0 # The project word count from last session self.lastWCount = 0 # The project word count from last session
self.currWCount = 0 # The project word count in current session self.currWCount = 0 # The project word count in current session
self.novelWCount = 0 # Total number of words in novel files self.novelWCount = 0 # Total number of words in novel files
self.notesWCount = 0 # Total number of words in note files self.notesWCount = 0 # Total number of words in note files
self.doBackup = True # Run project backup on exit self.doBackup = True # Run project backup on exit
# Internal Mapping # Internal Mapping
self.makeAlert = self.theParent.makeAlert self.makeAlert = self.theParent.makeAlert
@@ -383,7 +383,7 @@ class NWProject():
# Check for Old Legacy Data # Check for Old Legacy Data
# ========================= # =========================
legacyList = [] # Cleanup is done later legacyList = [] # Cleanup is done later
for projItem in os.listdir(self.projPath): for projItem in os.listdir(self.projPath):
logger.verbose("Project contains: %s" % projItem) logger.verbose("Project contains: %s" % projItem)
if projItem.startswith("data_"): if projItem.startswith("data_"):
+4 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Spell Check Classes novelWriter Spell Check Classes
================================= =================================
@@ -31,6 +30,7 @@ import difflib
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# =============================================================================================== # # =============================================================================================== #
# SpellChecking SuperClass # SpellChecking SuperClass
# =============================================================================================== # # =============================================================================================== #
@@ -122,6 +122,7 @@ class NWSpellCheck():
# END Class NWSpellCheck # END Class NWSpellCheck
# =============================================================================================== # # =============================================================================================== #
# Enchant Based SpellChecking # Enchant Based SpellChecking
# =============================================================================================== # # =============================================================================================== #
@@ -208,6 +209,7 @@ class NWSpellEnchant(NWSpellCheck):
# END Class NWSpellEnchant # END Class NWSpellEnchant
class FakeEnchant: class FakeEnchant:
"""Fallback for when Enchant is selected, but not installed. """Fallback for when Enchant is selected, but not installed.
""" """
@@ -225,6 +227,7 @@ class FakeEnchant:
# END Class FakeEnchant # END Class FakeEnchant
# =============================================================================================== # # =============================================================================================== #
# Fallback SpellChecking Using difflib # Fallback SpellChecking Using difflib
# =============================================================================================== # # =============================================================================================== #
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Item Status Class novelWriter Project Item Status Class
======================================= =======================================
@@ -32,6 +31,7 @@ from nw.common import checkInt
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class NWStatus(): class NWStatus():
def __init__(self): def __init__(self):
+4 -4
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter HTML Text Converter novelWriter HTML Text Converter
================================= =================================
@@ -31,11 +30,12 @@ from nw.constants import nwKeyWords, nwLabels, nwHtmlUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ToHtml(Tokenizer): class ToHtml(Tokenizer):
M_PREVIEW = 0 # Tweak output for the DocViewer M_PREVIEW = 0 # Tweak output for the DocViewer
M_EXPORT = 1 # Tweak output for saving to HTML or printing M_EXPORT = 1 # Tweak output for saving to HTML or printing
M_EBOOK = 2 # Tweak output for converting to epub M_EBOOK = 2 # Tweak output for converting to epub
def __init__(self, theProject): def __init__(self, theProject):
Tokenizer.__init__(self, theProject) Tokenizer.__init__(self, theProject)
+57 -57
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Text Tokenizer novelWriter Text Tokenizer
============================ ============================
@@ -40,6 +39,7 @@ from nw.core.document import NWDoc
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Tokenizer(): class Tokenizer():
# In-Text Format # In-Text Format
@@ -51,33 +51,33 @@ class Tokenizer():
FMT_D_E = 6 # End strikeout FMT_D_E = 6 # End strikeout
# Block Type # Block Type
T_EMPTY = 1 # Empty line (new paragraph) T_EMPTY = 1 # Empty line (new paragraph)
T_SYNOPSIS = 2 # Synopsis comment T_SYNOPSIS = 2 # Synopsis comment
T_COMMENT = 3 # Comment line T_COMMENT = 3 # Comment line
T_KEYWORD = 4 # Command line T_KEYWORD = 4 # Command line
T_TITLE = 5 # Title T_TITLE = 5 # Title
T_HEAD1 = 6 # Header 1 T_HEAD1 = 6 # Header 1
T_HEAD2 = 7 # Header 2 T_HEAD2 = 7 # Header 2
T_HEAD3 = 8 # Header 3 T_HEAD3 = 8 # Header 3
T_HEAD4 = 9 # Header 4 T_HEAD4 = 9 # Header 4
T_TEXT = 10 # Text line T_TEXT = 10 # Text line
T_SEP = 11 # Scene separator T_SEP = 11 # Scene separator
T_SKIP = 12 # Paragraph break T_SKIP = 12 # Paragraph break
# Block Style # Block Style
A_NONE = 0x0000 # No special style A_NONE = 0x0000 # No special style
A_LEFT = 0x0001 # Left aligned A_LEFT = 0x0001 # Left aligned
A_RIGHT = 0x0002 # Right aligned A_RIGHT = 0x0002 # Right aligned
A_CENTRE = 0x0004 # Centred A_CENTRE = 0x0004 # Centred
A_JUSTIFY = 0x0008 # Justified A_JUSTIFY = 0x0008 # Justified
A_PBB = 0x0010 # Page break before always A_PBB = 0x0010 # Page break before always
A_PBB_AUT = 0x0020 # Page break before auto A_PBB_AUT = 0x0020 # Page break before auto
A_PBA = 0x0040 # Page break after always A_PBA = 0x0040 # Page break after always
A_PBA_AUT = 0x0080 # Page break after auto A_PBA_AUT = 0x0080 # Page break after auto
A_Z_TOPMRG = 0x0100 # Zero top margin A_Z_TOPMRG = 0x0100 # Zero top margin
A_Z_BTMMRG = 0x0200 # Zero bottom margin A_Z_BTMMRG = 0x0200 # Zero bottom margin
A_IND_L = 0x0400 # Left indentation A_IND_L = 0x0400 # Left indentation
A_IND_R = 0x0800 # Right indentation A_IND_R = 0x0800 # Right indentation
def __init__(self, theProject): def __init__(self, theProject):
@@ -86,28 +86,28 @@ class Tokenizer():
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
# Data Variables # Data Variables
self.theText = "" # The raw text to be tokenized self.theText = "" # The raw text to be tokenized
self.theHandle = None # The handle associated with the text self.theHandle = None # The handle associated with the text
self.theItem = None # The NWItem associated with the handle self.theItem = None # The NWItem associated with the handle
self.theTokens = [] # The list of the processed tokens self.theTokens = [] # The list of the processed tokens
self.theResult = "" # The result of the last document self.theResult = "" # The result of the last document
self.keepMarkdown = False # Whether to keep the markdown text self.keepMarkdown = False # Whether to keep the markdown text
self.theMarkdown = [] # The result novelWriter markdown of all documents self.theMarkdown = [] # The result novelWriter markdown of all documents
# User Settings # User Settings
self.textFont = "Serif" # Output text font self.textFont = "Serif" # Output text font
self.textSize = 11 # Output text size self.textSize = 11 # Output text size
self.textFixed = False # Fixed width text self.textFixed = False # Fixed width text
self.lineHeight = 1.15 # Line height in units of em self.lineHeight = 1.15 # Line height in units of em
self.blockIndent = 4.00 # Block indent in units of em self.blockIndent = 4.00 # Block indent in units of em
self.doJustify = False # Justify text self.doJustify = False # Justify text
self.doBodyText = True # Include body text self.doBodyText = True # Include body text
self.doSynopsis = False # Also process synopsis comments self.doSynopsis = False # Also process synopsis comments
self.doComments = False # Also process comments self.doComments = False # Also process comments
self.doKeywords = False # Also process keywords like tags and references self.doKeywords = False # Also process keywords like tags and references
## Margins # Margins
self.marginTitle = (1.000, 0.500) self.marginTitle = (1.000, 0.500)
self.marginHead1 = (1.000, 0.500) self.marginHead1 = (1.000, 0.500)
self.marginHead2 = (0.834, 0.500) self.marginHead2 = (0.834, 0.500)
@@ -116,23 +116,23 @@ class Tokenizer():
self.marginText = (0.000, 0.584) self.marginText = (0.000, 0.584)
self.marginMeta = (0.000, 0.584) self.marginMeta = (0.000, 0.584)
## Title Formats # Title Formats
self.fmtTitle = "%title%" # Formatting for titles self.fmtTitle = "%title%" # Formatting for titles
self.fmtChapter = "%title%" # Formatting for numbered chapters self.fmtChapter = "%title%" # Formatting for numbered chapters
self.fmtUnNum = "%title%" # Formatting for unnumbered chapters self.fmtUnNum = "%title%" # Formatting for unnumbered chapters
self.fmtScene = "%title%" # Formatting for scenes self.fmtScene = "%title%" # Formatting for scenes
self.fmtSection = "%title%" # Formatting for sections self.fmtSection = "%title%" # Formatting for sections
self.hideScene = False # Do not include scene headers self.hideScene = False # Do not include scene headers
self.hideSection = False # Do not include section headers self.hideSection = False # Do not include section headers
self.linkHeaders = False # Add an anchor before headers self.linkHeaders = False # Add an anchor before headers
# Instance Variables # Instance Variables
self.numChapter = 0 # Counter for chapter numbers self.numChapter = 0 # Counter for chapter numbers
self.numChScene = 0 # Counter for scene number within chapter self.numChScene = 0 # Counter for scene number within chapter
self.numAbsScene = 0 # Counter for scene number within novel self.numAbsScene = 0 # Counter for scene number within novel
self.firstScene = False # Flag to indicate that the first scene of the chapter self.firstScene = False # Flag to indicate that the first scene of the chapter
# This File # This File
self.isNone = False self.isNone = False
+3 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Markdown Text Converter novelWriter Markdown Text Converter
===================================== =====================================
@@ -31,10 +30,11 @@ from nw.core.tokenizer import Tokenizer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ToMarkdown(Tokenizer): class ToMarkdown(Tokenizer):
M_STD = 0 # Standard Markdown M_STD = 0 # Standard Markdown
M_GH = 1 # GitHub Markdown M_GH = 1 # GitHub Markdown
def __init__(self, theProject): def __init__(self, theProject):
Tokenizer.__init__(self, theProject) Tokenizer.__init__(self, theProject)
+34 -31
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter ODT Text Converter novelWriter ODT Text Converter
================================ ================================
@@ -57,38 +56,39 @@ TAG_TAB = "{%s}tab" % XML_NS["text"]
TAG_SPAN = "{%s}span" % XML_NS["text"] TAG_SPAN = "{%s}span" % XML_NS["text"]
TAG_STNM = "{%s}style-name" % XML_NS["text"] TAG_STNM = "{%s}style-name" % XML_NS["text"]
class ToOdt(Tokenizer): class ToOdt(Tokenizer):
X_BLD = 0x01 # Bold format X_BLD = 0x01 # Bold format
X_ITA = 0x02 # Italic format X_ITA = 0x02 # Italic format
X_DEL = 0x04 # Strikethrough format X_DEL = 0x04 # Strikethrough format
X_BRK = 0x08 # Line break X_BRK = 0x08 # Line break
X_TAB = 0x10 # Tab X_TAB = 0x10 # Tab
def __init__(self, theProject, isFlat): def __init__(self, theProject, isFlat):
Tokenizer.__init__(self, theProject) Tokenizer.__init__(self, theProject)
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self._isFlat = isFlat # Flat: .fodt, otherwise .odt self._isFlat = isFlat # Flat: .fodt, otherwise .odt
self._dFlat = None # FODT file XML root self._dFlat = None # FODT file XML root
self._dCont = None # ODT content.xml root self._dCont = None # ODT content.xml root
self._dMeta = None # ODT meta.xml root self._dMeta = None # ODT meta.xml root
self._dStyl = None # ODT styles.xml root self._dStyl = None # ODT styles.xml root
self._xMeta = None # Office meta root self._xMeta = None # Office meta root
self._xStyl = None # Office styles root self._xStyl = None # Office styles root
self._xAuto = None # Office auto-styles root self._xAuto = None # Office auto-styles root
self._xMast = None # Office master-styles root self._xMast = None # Office master-styles root
self._xBody = None # Office body root self._xBody = None # Office body root
self._xText = None # Office text root self._xText = None # Office text root
self._xAut2 = None # Page layout auto-styles for ODT file self._xAut2 = None # Page layout auto-styles for ODT file
self._mainPara = {} # User-accessible paragraph styles self._mainPara = {} # User-accessible paragraph styles
self._autoPara = {} # Auto-generated paragraph styles self._autoPara = {} # Auto-generated paragraph styles
self._autoText = {} # Auto-generated text styles self._autoText = {} # Auto-generated text styles
# Properties # Properties
self.textFont = "Liberation Serif" self.textFont = "Liberation Serif"
@@ -114,7 +114,7 @@ class ToOdt(Tokenizer):
self._dLanguage = "en" self._dLanguage = "en"
self._dCountry = "GB" self._dCountry = "GB"
## Text Margings in Units of em # Text Margings in Units of em
self._mTopTitle = "0.423cm" self._mTopTitle = "0.423cm"
self._mTopHead1 = "0.423cm" self._mTopHead1 = "0.423cm"
self._mTopHead2 = "0.353cm" self._mTopHead2 = "0.353cm"
@@ -133,13 +133,13 @@ class ToOdt(Tokenizer):
self._mBotText = "0.247cm" self._mBotText = "0.247cm"
self._mBotMeta = "0.106cm" self._mBotMeta = "0.106cm"
## Document Margins # Document Margins
self._mDocTop = "2.000cm" self._mDocTop = "2.000cm"
self._mDocBtm = "2.000cm" self._mDocBtm = "2.000cm"
self._mDocLeft = "2.000cm" self._mDocLeft = "2.000cm"
self._mDocRight = "2.000cm" self._mDocRight = "2.000cm"
## Colour # Colour
self._colHead12 = None self._colHead12 = None
self._opaHead12 = None self._opaHead12 = None
self._colHead34 = None self._colHead34 = None
@@ -315,15 +315,15 @@ class ToOdt(Tokenizer):
def doConvert(self): def doConvert(self):
"""Convert the list of text tokens into XML elements. """Convert the list of text tokens into XML elements.
""" """
self.theResult = "" # Not used, but cleared just in case self.theResult = "" # Not used, but cleared just in case
odtTags = { odtTags = {
self.FMT_B_B : "_B", # Bold open format self.FMT_B_B : "_B", # Bold open format
self.FMT_B_E : "b_", # Bold close format self.FMT_B_E : "b_", # Bold close format
self.FMT_I_B : "I", # Italic open format self.FMT_I_B : "I", # Italic open format
self.FMT_I_E : "i", # Italic close format self.FMT_I_E : "i", # Italic close format
self.FMT_D_B : "_S", # Strikethrough open format self.FMT_D_B : "_S", # Strikethrough open format
self.FMT_D_E : "s_", # Strikethrough close format self.FMT_D_E : "s_", # Strikethrough close format
} }
thisPar = [] thisPar = []
@@ -985,6 +985,7 @@ class ToOdt(Tokenizer):
# END Class ToOdt # END Class ToOdt
# =============================================================================================== # # =============================================================================================== #
# Auto-Style Classes # Auto-Style Classes
# =============================================================================================== # # =============================================================================================== #
@@ -1212,6 +1213,7 @@ class ODTParagraphStyle():
# END Class ODTParagraphStyle # END Class ODTParagraphStyle
class ODTTextStyle(): class ODTTextStyle():
"""Wrapper class for the text style setting used by the exporter. """Wrapper class for the text style setting used by the exporter.
Only the used settings are exposed here to keep the class minimal Only the used settings are exposed here to keep the class minimal
@@ -1282,6 +1284,7 @@ class ODTTextStyle():
# END Class ODTTextStyle # END Class ODTTextStyle
# =============================================================================================== # # =============================================================================================== #
# Local Functions # Local Functions
# =============================================================================================== # # =============================================================================================== #
+10 -10
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Tree Class novelWriter Project Tree Class
================================ ================================
@@ -62,22 +61,23 @@ LAYOUT_MAP = {
}, },
} }
class NWTree(): class NWTree():
def __init__(self, theProject): def __init__(self, theProject):
self.theProject = theProject self.theProject = theProject
self._projTree = {} # Holds all the items of the project self._projTree = {} # Holds all the items of the project
self._treeOrder = [] # The order of the tree items on the tree view self._treeOrder = [] # The order of the tree items on the tree view
self._treeRoots = [] # The root items of the tree self._treeRoots = [] # 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._archRoot = None # The handle of the archive root folder self._archRoot = None # The handle of the archive root folder
self._theIndex = 0 # The current iterator index self._theIndex = 0 # The current iterator index
self._treeChanged = False # True if tree structure has changed self._treeChanged = False # True if tree structure has changed
self._handleSeed = None # Used for generating handles for testing self._handleSeed = None # Used for generating handles for testing
self._handleCount = 0 # A counter that is added to the handle generator self._handleCount = 0 # A counter that is added to the handle generator
return return
+20 -1
View File
@@ -1,4 +1,23 @@
# -*- coding: utf-8 -*- """
novelWriter Dialogs Init
==========================
This file is a part of novelWriter
Copyright 20182021, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from nw.dialogs.about import GuiAbout from nw.dialogs.about import GuiAbout
from nw.dialogs.docmerge import GuiDocMerge from nw.dialogs.docmerge import GuiDocMerge
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI About Box novelWriter GUI About Box
=========================== ===========================
@@ -39,6 +38,7 @@ from PyQt5.QtWidgets import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiAbout(QDialog): class GuiAbout(QDialog):
def __init__(self, theParent): def __init__(self, theParent):
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Doc Merge Tool novelWriter GUI Doc Merge Tool
================================ ================================
@@ -39,6 +38,7 @@ from nw.gui.custom import QHelpLabel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiDocMerge(QDialog): class GuiDocMerge(QDialog):
def __init__(self, theParent): def __init__(self, theParent):
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Doc Split Tool novelWriter GUI Doc Split Tool
================================ ================================
@@ -40,6 +39,7 @@ from nw.gui.custom import QHelpLabel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiDocSplit(QDialog): class GuiDocSplit(QDialog):
def __init__(self, theParent): def __init__(self, theParent):
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Item Editor novelWriter GUI Item Editor
============================= =============================
@@ -39,6 +38,7 @@ from nw.gui.custom import QSwitch
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiItemEditor(QDialog): class GuiItemEditor(QDialog):
def __init__(self, theParent, tHandle): def __init__(self, theParent, tHandle):
+50 -43
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Preferences novelWriter GUI Preferences
============================= =============================
@@ -43,6 +42,7 @@ from nw.dialogs.quotes import GuiQuoteSelect
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiPreferences(PagedDialog): class GuiPreferences(PagedDialog):
def __init__(self, theParent): def __init__(self, theParent):
@@ -134,6 +134,7 @@ class GuiPreferences(PagedDialog):
# END Class GuiPreferences # END Class GuiPreferences
class GuiPreferencesGeneral(QWidget): class GuiPreferencesGeneral(QWidget):
def __init__(self, theParent): def __init__(self, theParent):
@@ -153,7 +154,7 @@ class GuiPreferencesGeneral(QWidget):
self.mainForm.addGroupLabel(self.tr("Look and Feel")) self.mainForm.addGroupLabel(self.tr("Look and Feel"))
minWidth = self.mainConf.pxInt(200) minWidth = self.mainConf.pxInt(200)
## Select Locale # Select Locale
self.guiLang = QComboBox() self.guiLang = QComboBox()
self.guiLang.setMinimumWidth(minWidth) self.guiLang.setMinimumWidth(minWidth)
theLangs = self.mainConf.listLanguages(self.mainConf.LANG_NW) theLangs = self.mainConf.listLanguages(self.mainConf.LANG_NW)
@@ -169,7 +170,7 @@ class GuiPreferencesGeneral(QWidget):
self.tr("Changing this requires restarting novelWriter.") self.tr("Changing this requires restarting novelWriter.")
) )
## Select Theme # Select Theme
self.guiTheme = QComboBox() self.guiTheme = QComboBox()
self.guiTheme.setMinimumWidth(minWidth) self.guiTheme.setMinimumWidth(minWidth)
self.theThemes = self.theTheme.listThemes() self.theThemes = self.theTheme.listThemes()
@@ -185,7 +186,7 @@ class GuiPreferencesGeneral(QWidget):
self.tr("Changing this requires restarting novelWriter.") self.tr("Changing this requires restarting novelWriter.")
) )
## Select Icon Theme # Select Icon Theme
self.guiIcons = QComboBox() self.guiIcons = QComboBox()
self.guiIcons.setMinimumWidth(minWidth) self.guiIcons.setMinimumWidth(minWidth)
self.theIcons = self.theTheme.theIcons.listThemes() self.theIcons = self.theTheme.theIcons.listThemes()
@@ -201,7 +202,7 @@ class GuiPreferencesGeneral(QWidget):
self.tr("Changing this requires restarting novelWriter.") self.tr("Changing this requires restarting novelWriter.")
) )
## Dark Icons # Dark Icons
self.guiDark = QSwitch() self.guiDark = QSwitch()
self.guiDark.setChecked(self.mainConf.guiDark) self.guiDark.setChecked(self.mainConf.guiDark)
self.mainForm.addRow( self.mainForm.addRow(
@@ -210,7 +211,7 @@ class GuiPreferencesGeneral(QWidget):
self.tr("May improve the look of icons on dark themes.") self.tr("May improve the look of icons on dark themes.")
) )
## Font Family # Font Family
self.guiFont = QLineEdit() self.guiFont = QLineEdit()
self.guiFont.setReadOnly(True) self.guiFont.setReadOnly(True)
self.guiFont.setFixedWidth(self.mainConf.pxInt(162)) self.guiFont.setFixedWidth(self.mainConf.pxInt(162))
@@ -225,7 +226,7 @@ class GuiPreferencesGeneral(QWidget):
theButton = self.fontButton theButton = self.fontButton
) )
## Font Size # Font Size
self.guiFontSize = QSpinBox(self) self.guiFontSize = QSpinBox(self)
self.guiFontSize.setMinimum(8) self.guiFontSize.setMinimum(8)
self.guiFontSize.setMaximum(60) self.guiFontSize.setMaximum(60)
@@ -319,6 +320,7 @@ class GuiPreferencesGeneral(QWidget):
# END Class GuiPreferencesGeneral # END Class GuiPreferencesGeneral
class GuiPreferencesProjects(QWidget): class GuiPreferencesProjects(QWidget):
def __init__(self, theParent): def __init__(self, theParent):
@@ -337,7 +339,7 @@ class GuiPreferencesProjects(QWidget):
# ============== # ==============
self.mainForm.addGroupLabel(self.tr("Automatic Save")) self.mainForm.addGroupLabel(self.tr("Automatic Save"))
## Document Save Timer # Document Save Timer
self.autoSaveDoc = QSpinBox(self) self.autoSaveDoc = QSpinBox(self)
self.autoSaveDoc.setMinimum(5) self.autoSaveDoc.setMinimum(5)
self.autoSaveDoc.setMaximum(600) self.autoSaveDoc.setMaximum(600)
@@ -350,7 +352,7 @@ class GuiPreferencesProjects(QWidget):
theUnit=self.tr("seconds") theUnit=self.tr("seconds")
) )
## Project Save Timer # Project Save Timer
self.autoSaveProj = QSpinBox(self) self.autoSaveProj = QSpinBox(self)
self.autoSaveProj.setMinimum(5) self.autoSaveProj.setMinimum(5)
self.autoSaveProj.setMaximum(600) self.autoSaveProj.setMaximum(600)
@@ -367,7 +369,7 @@ class GuiPreferencesProjects(QWidget):
# ============== # ==============
self.mainForm.addGroupLabel(self.tr("Project Backup")) self.mainForm.addGroupLabel(self.tr("Project Backup"))
## Backup Path # Backup Path
self.backupPath = self.mainConf.backupPath self.backupPath = self.mainConf.backupPath
self.backupGetPath = QPushButton(self.tr("Browse")) self.backupGetPath = QPushButton(self.tr("Browse"))
self.backupGetPath.clicked.connect(self._backupFolder) self.backupGetPath.clicked.connect(self._backupFolder)
@@ -377,7 +379,7 @@ class GuiPreferencesProjects(QWidget):
self.tr("Path: {0}").format(self.backupPath) self.tr("Path: {0}").format(self.backupPath)
) )
## Run when closing # Run when closing
self.backupOnClose = QSwitch() self.backupOnClose = QSwitch()
self.backupOnClose.setChecked(self.mainConf.backupOnClose) self.backupOnClose.setChecked(self.mainConf.backupOnClose)
self.backupOnClose.toggled.connect(self._toggledBackupOnClose) self.backupOnClose.toggled.connect(self._toggledBackupOnClose)
@@ -387,8 +389,8 @@ class GuiPreferencesProjects(QWidget):
self.tr("Can be overridden for individual projects in Project Settings.") self.tr("Can be overridden for individual projects in Project Settings.")
) )
## Ask before backup # Ask before backup
## Only enabled when "Run when closing" is checked # Only enabled when "Run when closing" is checked
self.askBeforeBackup = QSwitch() self.askBeforeBackup = QSwitch()
self.askBeforeBackup.setChecked(self.mainConf.askBeforeBackup) self.askBeforeBackup.setChecked(self.mainConf.askBeforeBackup)
self.askBeforeBackup.setEnabled(self.mainConf.backupOnClose) self.askBeforeBackup.setEnabled(self.mainConf.backupOnClose)
@@ -402,7 +404,7 @@ class GuiPreferencesProjects(QWidget):
# ============= # =============
self.mainForm.addGroupLabel(self.tr("Session Timer")) self.mainForm.addGroupLabel(self.tr("Session Timer"))
## Pause when idle # Pause when idle
self.stopWhenIdle = QSwitch() self.stopWhenIdle = QSwitch()
self.stopWhenIdle.setChecked(self.mainConf.stopWhenIdle) self.stopWhenIdle.setChecked(self.mainConf.stopWhenIdle)
self.mainForm.addRow( self.mainForm.addRow(
@@ -411,7 +413,7 @@ class GuiPreferencesProjects(QWidget):
self.tr("Also pauses when the application window does not have focus.") self.tr("Also pauses when the application window does not have focus.")
) )
## Inactive time for idle # Inactive time for idle
self.userIdleTime = QDoubleSpinBox() self.userIdleTime = QDoubleSpinBox()
self.userIdleTime.setMinimum(0.5) self.userIdleTime.setMinimum(0.5)
self.userIdleTime.setMaximum(600.0) self.userIdleTime.setMaximum(600.0)
@@ -479,6 +481,7 @@ class GuiPreferencesProjects(QWidget):
# END Class GuiPreferencesProjects # END Class GuiPreferencesProjects
class GuiPreferencesDocuments(QWidget): class GuiPreferencesDocuments(QWidget):
def __init__(self, theParent): def __init__(self, theParent):
@@ -497,7 +500,7 @@ class GuiPreferencesDocuments(QWidget):
# ========== # ==========
self.mainForm.addGroupLabel(self.tr("Text Style")) self.mainForm.addGroupLabel(self.tr("Text Style"))
## Font Family # Font Family
self.textFont = QLineEdit() self.textFont = QLineEdit()
self.textFont.setReadOnly(True) self.textFont.setReadOnly(True)
self.textFont.setFixedWidth(self.mainConf.pxInt(162)) self.textFont.setFixedWidth(self.mainConf.pxInt(162))
@@ -512,7 +515,7 @@ class GuiPreferencesDocuments(QWidget):
theButton = self.fontButton theButton = self.fontButton
) )
## Font Size # Font Size
self.textSize = QSpinBox(self) self.textSize = QSpinBox(self)
self.textSize.setMinimum(8) self.textSize.setMinimum(8)
self.textSize.setMaximum(60) self.textSize.setMaximum(60)
@@ -529,7 +532,7 @@ class GuiPreferencesDocuments(QWidget):
# ========= # =========
self.mainForm.addGroupLabel(self.tr("Text Flow")) self.mainForm.addGroupLabel(self.tr("Text Flow"))
## Max Text Width in Normal Mode # Max Text Width in Normal Mode
self.textWidth = QSpinBox(self) self.textWidth = QSpinBox(self)
self.textWidth.setMinimum(300) self.textWidth.setMinimum(300)
self.textWidth.setMaximum(10000) self.textWidth.setMaximum(10000)
@@ -542,7 +545,7 @@ class GuiPreferencesDocuments(QWidget):
theUnit=self.tr("px") theUnit=self.tr("px")
) )
## Max Text Width in Focus Mode # Max Text Width in Focus Mode
self.focusWidth = QSpinBox(self) self.focusWidth = QSpinBox(self)
self.focusWidth.setMinimum(300) self.focusWidth.setMinimum(300)
self.focusWidth.setMaximum(10000) self.focusWidth.setMaximum(10000)
@@ -555,7 +558,7 @@ class GuiPreferencesDocuments(QWidget):
theUnit=self.tr("px") theUnit=self.tr("px")
) )
## Document Fixed Width # Document Fixed Width
self.textFixedW = QSwitch() self.textFixedW = QSwitch()
self.textFixedW.setChecked(not self.mainConf.textFixedW) self.textFixedW.setChecked(not self.mainConf.textFixedW)
self.mainForm.addRow( self.mainForm.addRow(
@@ -564,7 +567,7 @@ class GuiPreferencesDocuments(QWidget):
self.tr("Text width is defined by the margins only.") self.tr("Text width is defined by the margins only.")
) )
## Focus Mode Footer # Focus Mode Footer
self.hideFocusFooter = QSwitch() self.hideFocusFooter = QSwitch()
self.hideFocusFooter.setChecked(self.mainConf.hideFocusFooter) self.hideFocusFooter.setChecked(self.mainConf.hideFocusFooter)
self.mainForm.addRow( self.mainForm.addRow(
@@ -573,7 +576,7 @@ class GuiPreferencesDocuments(QWidget):
self.tr("Hide the information bar at the bottom of the document.") self.tr("Hide the information bar at the bottom of the document.")
) )
## Justify Text # Justify Text
self.doJustify = QSwitch() self.doJustify = QSwitch()
self.doJustify.setChecked(self.mainConf.doJustify) self.doJustify.setChecked(self.mainConf.doJustify)
self.mainForm.addRow( self.mainForm.addRow(
@@ -582,7 +585,7 @@ class GuiPreferencesDocuments(QWidget):
self.tr("Lay out text with straight edges in the editor and viewer.") self.tr("Lay out text with straight edges in the editor and viewer.")
) )
## Document Margins # Document Margins
self.textMargin = QSpinBox(self) self.textMargin = QSpinBox(self)
self.textMargin.setMinimum(0) self.textMargin.setMinimum(0)
self.textMargin.setMaximum(900) self.textMargin.setMaximum(900)
@@ -595,7 +598,7 @@ class GuiPreferencesDocuments(QWidget):
theUnit=self.tr("px") theUnit=self.tr("px")
) )
## Tab Width # Tab Width
self.tabWidth = QSpinBox(self) self.tabWidth = QSpinBox(self)
self.tabWidth.setMinimum(0) self.tabWidth.setMinimum(0)
self.tabWidth.setMaximum(200) self.tabWidth.setMaximum(200)
@@ -649,6 +652,7 @@ class GuiPreferencesDocuments(QWidget):
# END Class GuiPreferencesDocuments # END Class GuiPreferencesDocuments
class GuiPreferencesEditor(QWidget): class GuiPreferencesEditor(QWidget):
def __init__(self, theParent): def __init__(self, theParent):
@@ -669,7 +673,7 @@ class GuiPreferencesEditor(QWidget):
# ============== # ==============
self.mainForm.addGroupLabel(self.tr("Spell Checking")) self.mainForm.addGroupLabel(self.tr("Spell Checking"))
## Spell Check Provider and Language # Spell Check Provider and Language
self.spellLangList = QComboBox(self) self.spellLangList = QComboBox(self)
self.spellLangList.setMaximumWidth(mW) self.spellLangList.setMaximumWidth(mW)
@@ -699,7 +703,7 @@ class GuiPreferencesEditor(QWidget):
self.tr("Available languages are determined by your system.") self.tr("Available languages are determined by your system.")
) )
## Big Document Size Limit # Big Document Size Limit
self.bigDocLimit = QSpinBox(self) self.bigDocLimit = QSpinBox(self)
self.bigDocLimit.setMinimum(10) self.bigDocLimit.setMinimum(10)
self.bigDocLimit.setMaximum(10000) self.bigDocLimit.setMaximum(10000)
@@ -716,7 +720,7 @@ class GuiPreferencesEditor(QWidget):
# ========== # ==========
self.mainForm.addGroupLabel(self.tr("Word Count")) self.mainForm.addGroupLabel(self.tr("Word Count"))
## Word Count Timer # Word Count Timer
self.wordCountTimer = QDoubleSpinBox(self) self.wordCountTimer = QDoubleSpinBox(self)
self.wordCountTimer.setDecimals(1) self.wordCountTimer.setDecimals(1)
self.wordCountTimer.setMinimum(2.0) self.wordCountTimer.setMinimum(2.0)
@@ -734,7 +738,7 @@ class GuiPreferencesEditor(QWidget):
# ============== # ==============
self.mainForm.addGroupLabel(self.tr("Writing Guides")) self.mainForm.addGroupLabel(self.tr("Writing Guides"))
## Show Tabs and Spaces # Show Tabs and Spaces
self.showTabsNSpaces = QSwitch() self.showTabsNSpaces = QSwitch()
self.showTabsNSpaces.setChecked(self.mainConf.showTabsNSpaces) self.showTabsNSpaces.setChecked(self.mainConf.showTabsNSpaces)
self.mainForm.addRow( self.mainForm.addRow(
@@ -743,7 +747,7 @@ class GuiPreferencesEditor(QWidget):
self.tr("Add symbols to indicate tabs and spaces in the editor.") self.tr("Add symbols to indicate tabs and spaces in the editor.")
) )
## Show Line Endings # Show Line Endings
self.showLineEndings = QSwitch() self.showLineEndings = QSwitch()
self.showLineEndings.setChecked(self.mainConf.showLineEndings) self.showLineEndings.setChecked(self.mainConf.showLineEndings)
self.mainForm.addRow( self.mainForm.addRow(
@@ -756,7 +760,7 @@ class GuiPreferencesEditor(QWidget):
# ================ # ================
self.mainForm.addGroupLabel(self.tr("Scroll Behaviour")) self.mainForm.addGroupLabel(self.tr("Scroll Behaviour"))
## Scroll Past End # Scroll Past End
self.scrollPastEnd = QSwitch() self.scrollPastEnd = QSwitch()
self.scrollPastEnd.setChecked(self.mainConf.scrollPastEnd) self.scrollPastEnd.setChecked(self.mainConf.scrollPastEnd)
self.mainForm.addRow( self.mainForm.addRow(
@@ -765,7 +769,7 @@ class GuiPreferencesEditor(QWidget):
self.tr("Also improves trypewriter scrolling for short documents.") self.tr("Also improves trypewriter scrolling for short documents.")
) )
## Typewriter Scrolling # Typewriter Scrolling
self.autoScroll = QSwitch() self.autoScroll = QSwitch()
self.autoScroll.setChecked(self.mainConf.autoScroll) self.autoScroll.setChecked(self.mainConf.autoScroll)
self.mainForm.addRow( self.mainForm.addRow(
@@ -774,7 +778,7 @@ class GuiPreferencesEditor(QWidget):
self.tr("Try to keep the cursor at a fixed vertical position.") self.tr("Try to keep the cursor at a fixed vertical position.")
) )
## Typewriter Position # Typewriter Position
self.autoScrollPos = QSpinBox(self) self.autoScrollPos = QSpinBox(self)
self.autoScrollPos.setMinimum(10) self.autoScrollPos.setMinimum(10)
self.autoScrollPos.setMaximum(90) self.autoScrollPos.setMaximum(90)
@@ -849,6 +853,7 @@ class GuiPreferencesEditor(QWidget):
# END Class GuiPreferencesEditor # END Class GuiPreferencesEditor
class GuiPreferencesSyntax(QWidget): class GuiPreferencesSyntax(QWidget):
def __init__(self, theParent): def __init__(self, theParent):
@@ -973,6 +978,7 @@ class GuiPreferencesSyntax(QWidget):
# END Class GuiPreferencesSyntax # END Class GuiPreferencesSyntax
class GuiPreferencesAutomation(QWidget): class GuiPreferencesAutomation(QWidget):
def __init__(self, theParent): def __init__(self, theParent):
@@ -991,7 +997,7 @@ class GuiPreferencesAutomation(QWidget):
# ================== # ==================
self.mainForm.addGroupLabel(self.tr("Automatic Features")) self.mainForm.addGroupLabel(self.tr("Automatic Features"))
## Auto-Select Word Under Cursor # Auto-Select Word Under Cursor
self.autoSelect = QSwitch() self.autoSelect = QSwitch()
self.autoSelect.setChecked(self.mainConf.autoSelect) self.autoSelect.setChecked(self.mainConf.autoSelect)
self.mainForm.addRow( self.mainForm.addRow(
@@ -1000,7 +1006,7 @@ class GuiPreferencesAutomation(QWidget):
self.tr("Apply formatting to word under cursor if no selection is made.") self.tr("Apply formatting to word under cursor if no selection is made.")
) )
## Auto-Replace as You Type Main Switch # Auto-Replace as You Type Main Switch
self.doReplace = QSwitch() self.doReplace = QSwitch()
self.doReplace.setChecked(self.mainConf.doReplace) self.doReplace.setChecked(self.mainConf.doReplace)
self.doReplace.toggled.connect(self._toggleAutoReplaceMain) self.doReplace.toggled.connect(self._toggleAutoReplaceMain)
@@ -1014,7 +1020,7 @@ class GuiPreferencesAutomation(QWidget):
# =================== # ===================
self.mainForm.addGroupLabel(self.tr("Replace as You Type")) self.mainForm.addGroupLabel(self.tr("Replace as You Type"))
## Auto-Replace Single Quotes # Auto-Replace Single Quotes
self.doReplaceSQuote = QSwitch() self.doReplaceSQuote = QSwitch()
self.doReplaceSQuote.setChecked(self.mainConf.doReplaceSQuote) self.doReplaceSQuote.setChecked(self.mainConf.doReplaceSQuote)
self.doReplaceSQuote.setEnabled(self.mainConf.doReplace) self.doReplaceSQuote.setEnabled(self.mainConf.doReplace)
@@ -1024,7 +1030,7 @@ class GuiPreferencesAutomation(QWidget):
self.tr("Try to guess which is an opening or a closing single quote.") self.tr("Try to guess which is an opening or a closing single quote.")
) )
## Auto-Replace Double Quotes # Auto-Replace Double Quotes
self.doReplaceDQuote = QSwitch() self.doReplaceDQuote = QSwitch()
self.doReplaceDQuote.setChecked(self.mainConf.doReplaceDQuote) self.doReplaceDQuote.setChecked(self.mainConf.doReplaceDQuote)
self.doReplaceDQuote.setEnabled(self.mainConf.doReplace) self.doReplaceDQuote.setEnabled(self.mainConf.doReplace)
@@ -1034,7 +1040,7 @@ class GuiPreferencesAutomation(QWidget):
self.tr("Try to guess which is an opening or a closing double quote.") self.tr("Try to guess which is an opening or a closing double quote.")
) )
## Auto-Replace Hyphens # Auto-Replace Hyphens
self.doReplaceDash = QSwitch() self.doReplaceDash = QSwitch()
self.doReplaceDash.setChecked(self.mainConf.doReplaceDash) self.doReplaceDash.setChecked(self.mainConf.doReplaceDash)
self.doReplaceDash.setEnabled(self.mainConf.doReplace) self.doReplaceDash.setEnabled(self.mainConf.doReplace)
@@ -1044,7 +1050,7 @@ class GuiPreferencesAutomation(QWidget):
self.tr("Double and triple hyphens become short and long dashes.") self.tr("Double and triple hyphens become short and long dashes.")
) )
## Auto-Replace Dots # Auto-Replace Dots
self.doReplaceDots = QSwitch() self.doReplaceDots = QSwitch()
self.doReplaceDots.setChecked(self.mainConf.doReplaceDots) self.doReplaceDots.setChecked(self.mainConf.doReplaceDots)
self.doReplaceDots.setEnabled(self.mainConf.doReplace) self.doReplaceDots.setEnabled(self.mainConf.doReplace)
@@ -1058,7 +1064,7 @@ class GuiPreferencesAutomation(QWidget):
# ================= # =================
self.mainForm.addGroupLabel(self.tr("Automatic Padding")) self.mainForm.addGroupLabel(self.tr("Automatic Padding"))
## Pad Before # Pad Before
self.fmtPadBefore = QLineEdit() self.fmtPadBefore = QLineEdit()
self.fmtPadBefore.setMaxLength(32) self.fmtPadBefore.setMaxLength(32)
self.fmtPadBefore.setText(self.mainConf.fmtPadBefore) self.fmtPadBefore.setText(self.mainConf.fmtPadBefore)
@@ -1068,7 +1074,7 @@ class GuiPreferencesAutomation(QWidget):
self.tr("Automatically add space before any of these symbols."), self.tr("Automatically add space before any of these symbols."),
) )
## Pad After # Pad After
self.fmtPadAfter = QLineEdit() self.fmtPadAfter = QLineEdit()
self.fmtPadAfter.setMaxLength(32) self.fmtPadAfter.setMaxLength(32)
self.fmtPadAfter.setText(self.mainConf.fmtPadAfter) self.fmtPadAfter.setText(self.mainConf.fmtPadAfter)
@@ -1078,7 +1084,7 @@ class GuiPreferencesAutomation(QWidget):
self.tr("Automatically add space after any of these symbols."), self.tr("Automatically add space after any of these symbols."),
) )
## Use Thin Space # Use Thin Space
self.fmtPadThin = QSwitch() self.fmtPadThin = QSwitch()
self.fmtPadThin.setChecked(self.mainConf.fmtPadThin) self.fmtPadThin.setChecked(self.mainConf.fmtPadThin)
self.fmtPadThin.setEnabled(self.mainConf.doReplace) self.fmtPadThin.setEnabled(self.mainConf.doReplace)
@@ -1129,6 +1135,7 @@ class GuiPreferencesAutomation(QWidget):
# END Class GuiPreferencesAutomation # END Class GuiPreferencesAutomation
class GuiPreferencesQuotes(QWidget): class GuiPreferencesQuotes(QWidget):
def __init__(self, theParent): def __init__(self, theParent):
@@ -1151,7 +1158,7 @@ class GuiPreferencesQuotes(QWidget):
bWidth = int(2.5*self.theTheme.getTextWidth("...")) bWidth = int(2.5*self.theTheme.getTextWidth("..."))
self.quoteSym = {} self.quoteSym = {}
## Single Quote Style # Single Quote Style
self.quoteSym["SO"] = QLineEdit() self.quoteSym["SO"] = QLineEdit()
self.quoteSym["SO"].setMaxLength(1) self.quoteSym["SO"].setMaxLength(1)
self.quoteSym["SO"].setReadOnly(True) self.quoteSym["SO"].setReadOnly(True)
@@ -1184,7 +1191,7 @@ class GuiPreferencesQuotes(QWidget):
theButton=self.btnSingleStyleC theButton=self.btnSingleStyleC
) )
## Double Quote Style # Double Quote Style
self.quoteSym["DO"] = QLineEdit() self.quoteSym["DO"] = QLineEdit()
self.quoteSym["DO"].setMaxLength(1) self.quoteSym["DO"].setMaxLength(1)
self.quoteSym["DO"].setReadOnly(True) self.quoteSym["DO"].setReadOnly(True)
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Open Project novelWriter GUI Open Project
============================== ==============================
@@ -43,6 +42,7 @@ from nw.constants import nwFiles
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiProjectLoad(QDialog): class GuiProjectLoad(QDialog):
NONE_STATE = 0 NONE_STATE = 0
+4 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Project Settings novelWriter GUI Project Settings
================================== ==================================
@@ -40,6 +39,7 @@ from nw.gui.custom import QSwitch, PagedDialog, QConfigLayout
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiProjectSettings(PagedDialog): class GuiProjectSettings(PagedDialog):
def __init__(self, theParent): def __init__(self, theParent):
@@ -156,6 +156,7 @@ class GuiProjectSettings(PagedDialog):
# END Class GuiProjectSettings # END Class GuiProjectSettings
class GuiProjectEditMain(QWidget): class GuiProjectEditMain(QWidget):
def __init__(self, theParent, theProject): def __init__(self, theParent, theProject):
@@ -239,6 +240,7 @@ class GuiProjectEditMain(QWidget):
# END Class GuiProjectEditMain # END Class GuiProjectEditMain
class GuiProjectEditStatus(QWidget): class GuiProjectEditStatus(QWidget):
COL_LABEL = 0 COL_LABEL = 0
@@ -487,6 +489,7 @@ class GuiProjectEditStatus(QWidget):
# END Class GuiProjectEditStatus # END Class GuiProjectEditStatus
class GuiProjectEditReplace(QWidget): class GuiProjectEditReplace(QWidget):
COL_KEY = 0 COL_KEY = 0
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Quotes Dialog novelWriter GUI Quotes Dialog
=============================== ===============================
@@ -38,6 +37,7 @@ from nw.constants import trConst, nwQuotes
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiQuoteSelect(QDialog): class GuiQuoteSelect(QDialog):
selectedQuote = "" selectedQuote = ""
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI User Wordlist novelWriter GUI User Wordlist
=============================== ===============================
@@ -39,6 +38,7 @@ from nw.constants import nwFiles
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiWordList(QDialog): class GuiWordList(QDialog):
def __init__(self, theParent): def __init__(self, theParent):
+9 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Enums novelWriter Enums
=================== ===================
@@ -26,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from enum import Enum from enum import Enum
class nwItemType(Enum): class nwItemType(Enum):
NO_TYPE = 0 NO_TYPE = 0
@@ -36,6 +36,7 @@ class nwItemType(Enum):
# END Enum nwItemType # END Enum nwItemType
class nwItemClass(Enum): class nwItemClass(Enum):
NO_CLASS = 0 NO_CLASS = 0
@@ -52,6 +53,7 @@ class nwItemClass(Enum):
# END Enum nwItemClass # END Enum nwItemClass
class nwItemLayout(Enum): class nwItemLayout(Enum):
NO_LAYOUT = 0 NO_LAYOUT = 0
@@ -66,6 +68,7 @@ class nwItemLayout(Enum):
# END Enum nwItemLayout # END Enum nwItemLayout
class nwDocAction(Enum): class nwDocAction(Enum):
NO_ACTION = 0 NO_ACTION = 0
@@ -98,6 +101,7 @@ class nwDocAction(Enum):
# END Enum nwDocAction # END Enum nwDocAction
class nwDocInsert(Enum): class nwDocInsert(Enum):
NO_INSERT = 0 NO_INSERT = 0
@@ -108,6 +112,7 @@ class nwDocInsert(Enum):
# END Enum nwDocInsert # END Enum nwDocInsert
class nwAlert(Enum): class nwAlert(Enum):
INFO = 0 INFO = 0
@@ -117,6 +122,7 @@ class nwAlert(Enum):
# END Enum nwAlert # END Enum nwAlert
class nwState(Enum): class nwState(Enum):
NONE = 0 NONE = 0
@@ -125,6 +131,7 @@ class nwState(Enum):
# END Enum nwState # END Enum nwState
class nwWidget(Enum): class nwWidget(Enum):
TREE = 1 TREE = 1
@@ -134,6 +141,7 @@ class nwWidget(Enum):
# END Enum nwWidget # END Enum nwWidget
class nwOutline(Enum): class nwOutline(Enum):
TITLE = 0 TITLE = 0
+3 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Exception Handling novelWriter Exception Handling
================================ ================================
@@ -35,6 +34,7 @@ from PyQt5.QtWidgets import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# =============================================================================================== # # =============================================================================================== #
# Utility Functions # Utility Functions
# =============================================================================================== # # =============================================================================================== #
@@ -45,6 +45,7 @@ def logException():
exType, exValue, _ = sys.exc_info() exType, exValue, _ = sys.exc_info()
logger.error("%s: %s" % (exType.__name__, str(exValue).strip("'"))) logger.error("%s: %s" % (exType.__name__, str(exValue).strip("'")))
# =============================================================================================== # # =============================================================================================== #
# Error Handler # Error Handler
# =============================================================================================== # # =============================================================================================== #
@@ -151,6 +152,7 @@ class NWErrorMessage(QDialog):
# END Class NWErrorMessage # END Class NWErrorMessage
def exceptionHandler(exType, exValue, exTrace): def exceptionHandler(exType, exValue, exTrace):
"""Function to catch unhandled global exceptions. """Function to catch unhandled global exceptions.
""" """
+20 -1
View File
@@ -1,4 +1,23 @@
# -*- coding: utf-8 -*- """
novelWriter GUI Init
======================
This file is a part of novelWriter
Copyright 20182021, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from nw.gui.doceditor import GuiDocEditor from nw.gui.doceditor import GuiDocEditor
from nw.gui.docviewer import GuiDocViewer, GuiDocViewDetails from nw.gui.docviewer import GuiDocViewer, GuiDocViewDetails
+5 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Custom Widgets and Layouts novelWriter Custom Widgets and Layouts
======================================== ========================================
@@ -43,6 +42,7 @@ from nw.constants import nwUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# =============================================================================================== # # =============================================================================================== #
# Config Form Layout # Config Form Layout
# =============================================================================================== # # =============================================================================================== #
@@ -198,6 +198,7 @@ class QConfigLayout(QGridLayout):
# END Class QConfigLayout # END Class QConfigLayout
class QHelpLabel(QLabel): class QHelpLabel(QLabel):
def __init__(self, theText, textCol, fontSize=0.9): def __init__(self, theText, textCol, fontSize=0.9):
@@ -223,6 +224,7 @@ class QHelpLabel(QLabel):
# END Class QHelpLabel # END Class QHelpLabel
# =============================================================================================== # # =============================================================================================== #
# Switch Widget # Switch Widget
# =============================================================================================== # # =============================================================================================== #
@@ -367,6 +369,7 @@ class QSwitch(QAbstractButton):
# END Class QSwitch # END Class QSwitch
# =============================================================================================== # # =============================================================================================== #
# Paged Dialog w/Custom TabWidget # Paged Dialog w/Custom TabWidget
# =============================================================================================== # # =============================================================================================== #
@@ -417,6 +420,7 @@ class PagedDialog(QDialog):
# END Class PagedDialog # END Class PagedDialog
class VerticalTabBar(QTabBar): class VerticalTabBar(QTabBar):
def __init__(self, theParent=None): def __init__(self, theParent=None):
+26 -21
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Document Editor novelWriter GUI Document Editor
================================= =================================
@@ -57,6 +56,7 @@ from nw.gui.dochighlight import GuiDocHighlighter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiDocEditor(QTextEdit): class GuiDocEditor(QTextEdit):
MOVE_KEYS = ( MOVE_KEYS = (
@@ -84,24 +84,24 @@ class GuiDocEditor(QTextEdit):
self._nwDocument = None self._nwDocument = None
self._nwItem = None self._nwItem = None
self._docChanged = False # Flag for changed status of document self._docChanged = False # Flag for changed status of document
self._docHandle = None # The handle of the open file self._docHandle = None # The handle of the open file
self._docHeaders = [] # Record of headers in the file self._docHeaders = [] # Record of headers in the file
self._spellCheck = False # Flag for spell checking enabled self._spellCheck = False # Flag for spell checking enabled
self._theDict = None # The current spell check dictionary self._theDict = None # The current spell check dictionary
self._nonWord = "\"'" # Characters to not include in spell checking self._nonWord = "\"'" # Characters to not include in spell checking
# Document Variables # Document Variables
self._charCount = 0 # Character count self._charCount = 0 # Character count
self._wordCount = 0 # Word count self._wordCount = 0 # Word count
self._paraCount = 0 # Paragraph count self._paraCount = 0 # Paragraph count
self._lastEdit = 0 # Time stamp of last edit self._lastEdit = 0 # Time stamp of last edit
self._lastActive = 0 # Time stamp of last activity self._lastActive = 0 # Time stamp of last activity
self._lastFind = None # Position of the last found search word self._lastFind = None # Position of the last found search word
self._bigDoc = False # Flag for very large document size self._bigDoc = False # Flag for very large document size
self._doReplace = False # Switch to temporarily disable auto-replace self._doReplace = False # Switch to temporarily disable auto-replace
self._queuePos = None # Used for delayed change of cursor position self._queuePos = None # Used for delayed change of cursor position
# Typography # Typography
self._typDQOpen = '"' self._typDQOpen = '"'
@@ -597,8 +597,8 @@ class GuiDocEditor(QTextEdit):
""" """
if self.mainConf.verQtValue >= 50900: if self.mainConf.verQtValue >= 50900:
theText = self._qDocument.toRawText() theText = self._qDocument.toRawText()
theText = theText.replace(nwUnicode.U_LSEP, "\n") # Line separators theText = theText.replace(nwUnicode.U_LSEP, "\n") # Line separators
theText = theText.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators theText = theText.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators
else: else:
theText = self.toPlainText() theText = self.toPlainText()
return theText return theText
@@ -1424,7 +1424,7 @@ class GuiDocEditor(QTextEdit):
theTwo = theText[thePos-2:thePos] theTwo = theText[thePos-2:thePos]
theThree = theText[thePos-3:thePos] theThree = theText[thePos-3:thePos]
if not theOne: # Makes Neo sad if not theOne: # Makes Neo sad
return return
nDelete = 0 nDelete = 0
@@ -1931,6 +1931,7 @@ class GuiDocEditor(QTextEdit):
# END Class GuiDocEditor # END Class GuiDocEditor
# =============================================================================================== # # =============================================================================================== #
# The Off-GUI Thread Word Counter # The Off-GUI Thread Word Counter
# A runnable for the word counter to be run in the thread pool off the main GUI thread. # A runnable for the word counter to be run in the thread pool off the main GUI thread.
@@ -1960,7 +1961,8 @@ class BackgroundWordCounter(QRunnable):
self._isRunning = False self._isRunning = False
return return
## END Class BackgroundWordCounter # END Class BackgroundWordCounter
class BackgroundWordCounterSignals(QObject): class BackgroundWordCounterSignals(QObject):
"""The QRunnable cannot emit a signal, so we need a simple QObject """The QRunnable cannot emit a signal, so we need a simple QObject
@@ -1970,6 +1972,7 @@ class BackgroundWordCounterSignals(QObject):
# END Class BackgroundWordCounterSignals # END Class BackgroundWordCounterSignals
# =============================================================================================== # # =============================================================================================== #
# The Embedded Document Search/Replace Feature # The Embedded Document Search/Replace Feature
# Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport # Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport
@@ -2233,7 +2236,7 @@ class GuiDocEditSearch(QFrame):
self._alertSearchValid(theRegEx.isValid()) self._alertSearchValid(theRegEx.isValid())
return theRegEx return theRegEx
else: # >= 50300 to < 51300 else: # >= 50300 to < 51300
if self.isCaseSense: if self.isCaseSense:
rxOpt = Qt.CaseSensitive rxOpt = Qt.CaseSensitive
else: else:
@@ -2345,6 +2348,7 @@ class GuiDocEditSearch(QFrame):
# END Class GuiDocEditSearch # END Class GuiDocEditSearch
# =============================================================================================== # # =============================================================================================== #
# The Embedded Document Header # The Embedded Document Header
# Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport # Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport
@@ -2567,6 +2571,7 @@ class GuiDocEditHeader(QWidget):
# END Class GuiDocEditHeader # END Class GuiDocEditHeader
# =============================================================================================== # # =============================================================================================== #
# The Embedded Document Footer # The Embedded Document Footer
# Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport # Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport
+8 -8
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Syntax Highlighter novelWriter GUI Syntax Highlighter
==================================== ====================================
@@ -38,6 +37,7 @@ from nw.constants import nwRegEx, nwUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiDocHighlighter(QSyntaxHighlighter): class GuiDocHighlighter(QSyntaxHighlighter):
BLOCK_NONE = 0 BLOCK_NONE = 0
@@ -288,7 +288,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
if self.theHandle is None or not theText: if self.theHandle is None or not theText:
return return
if theText.startswith("@"): # Keywords and commands if theText.startswith("@"): # Keywords and commands
self.setCurrentBlockState(self.BLOCK_META) self.setCurrentBlockState(self.BLOCK_META)
tItem = self.theParent.theProject.projTree[self.theHandle] tItem = self.theParent.theProject.projTree[self.theHandle]
isValid, theBits, thePos = self.theIndex.scanThis(theText) isValid, theBits, thePos = self.theIndex.scanThis(theText)
@@ -312,27 +312,27 @@ class GuiDocHighlighter(QSyntaxHighlighter):
# so we force a return here # so we force a return here
return return
elif theText.startswith("# "): # Header 1 elif theText.startswith("# "): # Header 1
self.setCurrentBlockState(self.BLOCK_TITLE) self.setCurrentBlockState(self.BLOCK_TITLE)
self.setFormat(0, 1, self.hStyles["header1h"]) self.setFormat(0, 1, self.hStyles["header1h"])
self.setFormat(1, len(theText), self.hStyles["header1"]) self.setFormat(1, len(theText), self.hStyles["header1"])
elif theText.startswith("## "): # Header 2 elif theText.startswith("## "): # Header 2
self.setCurrentBlockState(self.BLOCK_TITLE) self.setCurrentBlockState(self.BLOCK_TITLE)
self.setFormat(0, 2, self.hStyles["header2h"]) self.setFormat(0, 2, self.hStyles["header2h"])
self.setFormat(2, len(theText), self.hStyles["header2"]) self.setFormat(2, len(theText), self.hStyles["header2"])
elif theText.startswith("### "): # Header 3 elif theText.startswith("### "): # Header 3
self.setCurrentBlockState(self.BLOCK_TITLE) self.setCurrentBlockState(self.BLOCK_TITLE)
self.setFormat(0, 3, self.hStyles["header3h"]) self.setFormat(0, 3, self.hStyles["header3h"])
self.setFormat(3, len(theText), self.hStyles["header3"]) self.setFormat(3, len(theText), self.hStyles["header3"])
elif theText.startswith("#### "): # Header 4 elif theText.startswith("#### "): # Header 4
self.setCurrentBlockState(self.BLOCK_TITLE) self.setCurrentBlockState(self.BLOCK_TITLE)
self.setFormat(0, 4, self.hStyles["header4h"]) self.setFormat(0, 4, self.hStyles["header4h"])
self.setFormat(4, len(theText), self.hStyles["header4"]) self.setFormat(4, len(theText), self.hStyles["header4"])
elif theText.startswith("%"): # Comments elif theText.startswith("%"): # Comments
self.setCurrentBlockState(self.BLOCK_TEXT) self.setCurrentBlockState(self.BLOCK_TEXT)
toCheck = theText[1:].lstrip() toCheck = theText[1:].lstrip()
synTag = toCheck[:9].lower() synTag = toCheck[:9].lower()
@@ -345,7 +345,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
else: else:
self.setFormat(0, tLen, self.hStyles["hidden"]) self.setFormat(0, tLen, self.hStyles["hidden"])
else: # Text Paragraph else: # Text Paragraph
self.setCurrentBlockState(self.BLOCK_TEXT) self.setCurrentBlockState(self.BLOCK_TEXT)
for rX, xFmt in self.rxRules: for rX, xFmt in self.rxRules:
rxItt = rX.globalMatch(theText, 0) rxItt = rX.globalMatch(theText, 0)
+5 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Document Viewer novelWriter GUI Document Viewer
================================= =================================
@@ -46,6 +45,7 @@ from nw.constants import nwUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiDocViewer(QTextBrowser): class GuiDocViewer(QTextBrowser):
def __init__(self, theParent): def __init__(self, theParent):
@@ -571,6 +571,7 @@ class GuiDocViewer(QTextBrowser):
# END Class GuiDocViewer # END Class GuiDocViewer
class GuiDocViewHistory(): class GuiDocViewHistory():
def __init__(self, docViewer): def __init__(self, docViewer):
@@ -702,6 +703,7 @@ class GuiDocViewHistory():
# END Class GuiDocViewHistory # END Class GuiDocViewHistory
# =============================================================================================== # # =============================================================================================== #
# The Embedded Document Header # The Embedded Document Header
# Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport # Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport
@@ -908,6 +910,7 @@ class GuiDocViewHeader(QWidget):
# END Class GuiDocViewHeader # END Class GuiDocViewHeader
# =============================================================================================== # # =============================================================================================== #
# The Embedded Document Footer # The Embedded Document Footer
# Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport # Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport
@@ -1133,6 +1136,7 @@ class GuiDocViewFooter(QWidget):
# END Class GuiDocViewFooter # END Class GuiDocViewFooter
# =============================================================================================== # # =============================================================================================== #
# The Document Back-Reference Panel # The Document Back-Reference Panel
# Placed in a separate QSplitter position in the main GUI window # Placed in a separate QSplitter position in the main GUI window
+3 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Item Details Panel novelWriter GUI Item Details Panel
==================================== ====================================
@@ -36,6 +35,7 @@ from nw.constants import trConst, nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiItemDetails(QWidget): class GuiItemDetails(QWidget):
def __init__(self, theParent): def __init__(self, theParent):
@@ -235,10 +235,10 @@ class GuiItemDetails(QWidget):
itStatus = nwItem.itemStatus itStatus = nwItem.itemStatus
if nwItem.itemClass == nwItemClass.NOVEL: if nwItem.itemClass == nwItemClass.NOVEL:
itStatus = self.theProject.statusItems.checkEntry(itStatus) # Make sure it's valid itStatus = self.theProject.statusItems.checkEntry(itStatus) # Make sure it's valid
flagIcon = self.theParent.statusIcons[itStatus] flagIcon = self.theParent.statusIcons[itStatus]
else: else:
itStatus = self.theProject.importItems.checkEntry(itStatus) # Make sure it's valid itStatus = self.theProject.importItems.checkEntry(itStatus) # Make sure it's valid
flagIcon = self.theParent.importIcons[itStatus] flagIcon = self.theParent.importIcons[itStatus]
if nwItem.itemType == nwItemType.FILE: if nwItem.itemType == nwItemType.FILE:
+3 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Main Menu novelWriter GUI Main Menu
=========================== ===========================
@@ -36,6 +35,7 @@ from nw.constants import trConst, nwKeyWords, nwLabels, nwUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiMainMenu(QMenuBar): class GuiMainMenu(QMenuBar):
def __init__(self, theParent): def __init__(self, theParent):
@@ -241,7 +241,7 @@ class GuiMainMenu(QMenuBar):
self.rootItems[nwItemClass.ARCHIVE] = QAction(self.tr("Outtakes Root"), self.rootMenu) self.rootItems[nwItemClass.ARCHIVE] = QAction(self.tr("Outtakes Root"), self.rootMenu)
nCount = 0 nCount = 0
for itemClass in self.rootItems.keys(): for itemClass in self.rootItems.keys():
nCount += 1 # This forces the lambdas to be unique nCount += 1 # This forces the lambdas to be unique
self.rootItems[itemClass].triggered.connect( self.rootItems[itemClass].triggered.connect(
lambda nCount, itemClass=itemClass: self._newTreeItem(nwItemType.ROOT, itemClass) lambda nCount, itemClass=itemClass: self._newTreeItem(nwItemType.ROOT, itemClass)
) )
@@ -982,7 +982,7 @@ class GuiMainMenu(QMenuBar):
self.aSpellCheck.setStatusTip(self.tr("Toggle check spelling")) self.aSpellCheck.setStatusTip(self.tr("Toggle check spelling"))
self.aSpellCheck.setCheckable(True) self.aSpellCheck.setCheckable(True)
self.aSpellCheck.setChecked(self.theProject.spellCheck) self.aSpellCheck.setChecked(self.theProject.spellCheck)
self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled! self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled!
self.aSpellCheck.setShortcut("Ctrl+F7") self.aSpellCheck.setShortcut("Ctrl+F7")
self.toolsMenu.addAction(self.aSpellCheck) self.toolsMenu.addAction(self.aSpellCheck)
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Novel Tree novelWriter GUI Novel Tree
============================ ============================
@@ -37,6 +36,7 @@ from nw.constants import nwKeyWords
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiNovelTree(QTreeWidget): class GuiNovelTree(QTreeWidget):
C_TITLE = 0 C_TITLE = 0
+2 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Project Outline novelWriter GUI Project Outline
================================= =================================
@@ -39,6 +38,7 @@ from nw.constants import trConst, nwKeyWords, nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiOutline(QTreeWidget): class GuiOutline(QTreeWidget):
DEF_WIDTH = { DEF_WIDTH = {
@@ -467,6 +467,7 @@ class GuiOutline(QTreeWidget):
# END Class GuiOutline # END Class GuiOutline
class GuiOutlineHeaderMenu(QMenu): class GuiOutlineHeaderMenu(QMenu):
def __init__(self, theParent): def __init__(self, theParent):
+1 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Project Outline Details novelWriter GUI Project Outline Details
========================================= =========================================
@@ -37,6 +36,7 @@ from nw.constants import trConst, nwKeyWords, nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiOutlineDetails(QScrollArea): class GuiOutlineDetails(QScrollArea):
LVL_MAP = { LVL_MAP = {
+3 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Project Details novelWriter GUI Project Details
================================= =================================
@@ -41,6 +40,7 @@ from nw.gui.custom import PagedDialog, QSwitch
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiProjectDetails(PagedDialog): class GuiProjectDetails(PagedDialog):
def __init__(self, theParent): def __init__(self, theParent):
@@ -128,6 +128,7 @@ class GuiProjectDetails(PagedDialog):
# END Class GuiProjectDetails # END Class GuiProjectDetails
class GuiProjectDetailsMain(QWidget): class GuiProjectDetailsMain(QWidget):
def __init__(self, theParent, theProject): def __init__(self, theParent, theProject):
@@ -241,6 +242,7 @@ class GuiProjectDetailsMain(QWidget):
# END Class GuiProjectDetailsMain # END Class GuiProjectDetailsMain
class GuiProjectDetailsContents(QWidget): class GuiProjectDetailsContents(QWidget):
C_TITLE = 0 C_TITLE = 0
+5 -4
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Project Tree novelWriter GUI Project Tree
============================== ==============================
@@ -42,6 +41,7 @@ from nw.constants import nwConst, trConst, nwLists, nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiProjectTree(QTreeWidget): class GuiProjectTree(QTreeWidget):
C_NAME = 0 C_NAME = 0
@@ -620,10 +620,10 @@ class GuiProjectTree(QTreeWidget):
iStatus = nwItem.itemStatus iStatus = nwItem.itemStatus
if nwItem.itemClass == nwItemClass.NOVEL: if nwItem.itemClass == nwItemClass.NOVEL:
iStatus = self.theProject.statusItems.checkEntry(iStatus) # Make sure it's valid 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 valid 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, nwItem.itemName) trItem.setText(self.C_NAME, nwItem.itemName)
@@ -790,7 +790,7 @@ class GuiProjectTree(QTreeWidget):
selItem = self.itemAt(clickPos) selItem = self.itemAt(clickPos)
if isinstance(selItem, QTreeWidgetItem): if isinstance(selItem, QTreeWidgetItem):
tHandle = selItem.data(self.C_NAME, Qt.UserRole) tHandle = selItem.data(self.C_NAME, Qt.UserRole)
self.setSelectedHandle(tHandle) # Just to be safe self.setSelectedHandle(tHandle) # Just to be safe
tItem = self.theProject.projTree[tHandle] tItem = self.theProject.projTree[tHandle]
if tItem is not None: if tItem is not None:
if self.ctxMenu.filterActions(tItem): if self.ctxMenu.filterActions(tItem):
@@ -1102,6 +1102,7 @@ class GuiProjectTree(QTreeWidget):
# END Class GuiProjectTree # END Class GuiProjectTree
class GuiProjectTreeMenu(QMenu): class GuiProjectTreeMenu(QMenu):
def __init__(self, theTree): def __init__(self, theTree):
+8 -7
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Main Window Status Bar novelWriter GUI Main Window Status Bar
======================================== ========================================
@@ -38,6 +37,7 @@ from nw.enum import nwState
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiMainStatus(QStatusBar): class GuiMainStatus(QStatusBar):
def __init__(self, theParent): def __init__(self, theParent):
@@ -62,7 +62,7 @@ class GuiMainStatus(QStatusBar):
xM = self.mainConf.pxInt(8) xM = self.mainConf.pxInt(8)
## The Spell Checker Language # The Spell Checker Language
self.langIcon = QLabel("") self.langIcon = QLabel("")
self.langText = QLabel(self.tr("None")) self.langText = QLabel(self.tr("None"))
self.langIcon.setPixmap(self.theTheme.getPixmap("status_lang", (iPx, iPx))) self.langIcon.setPixmap(self.theTheme.getPixmap("status_lang", (iPx, iPx)))
@@ -71,7 +71,7 @@ class GuiMainStatus(QStatusBar):
self.addPermanentWidget(self.langIcon) self.addPermanentWidget(self.langIcon)
self.addPermanentWidget(self.langText) self.addPermanentWidget(self.langText)
## The Editor Status # The Editor Status
self.docIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self) self.docIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self)
self.docText = QLabel(self.tr("Editor")) self.docText = QLabel(self.tr("Editor"))
self.docIcon.setContentsMargins(0, 0, 0, 0) self.docIcon.setContentsMargins(0, 0, 0, 0)
@@ -79,7 +79,7 @@ class GuiMainStatus(QStatusBar):
self.addPermanentWidget(self.docIcon) self.addPermanentWidget(self.docIcon)
self.addPermanentWidget(self.docText) self.addPermanentWidget(self.docText)
## The Project Status # The Project Status
self.projIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self) self.projIcon = StatusLED(colNone, colTrue, colFalse, iPx, iPx, self)
self.projText = QLabel(self.tr("Project")) self.projText = QLabel(self.tr("Project"))
self.projIcon.setContentsMargins(0, 0, 0, 0) self.projIcon.setContentsMargins(0, 0, 0, 0)
@@ -87,7 +87,7 @@ class GuiMainStatus(QStatusBar):
self.addPermanentWidget(self.projIcon) self.addPermanentWidget(self.projIcon)
self.addPermanentWidget(self.projText) self.addPermanentWidget(self.projText)
## The Project and Session Stats # The Project and Session Stats
self.statsIcon = QLabel() self.statsIcon = QLabel()
self.statsText = QLabel("") self.statsText = QLabel("")
self.statsIcon.setPixmap(self.theTheme.getPixmap("status_stats", (iPx, iPx))) self.statsIcon.setPixmap(self.theTheme.getPixmap("status_stats", (iPx, iPx)))
@@ -96,8 +96,8 @@ class GuiMainStatus(QStatusBar):
self.addPermanentWidget(self.statsIcon) self.addPermanentWidget(self.statsIcon)
self.addPermanentWidget(self.statsText) self.addPermanentWidget(self.statsText)
## The Session Clock # The Session Clock
### Set the mimimum width so the label doesn't rescale every second # Set the mimimum width so the label doesn't rescale every second
self.timePixmap = self.theTheme.getPixmap("status_time", (iPx, iPx)) self.timePixmap = self.theTheme.getPixmap("status_time", (iPx, iPx))
self.idlePixmap = self.theTheme.getPixmap("status_idle", (iPx, iPx)) self.idlePixmap = self.theTheme.getPixmap("status_idle", (iPx, iPx))
@@ -235,6 +235,7 @@ class GuiMainStatus(QStatusBar):
# END Class GuiMainStatus # END Class GuiMainStatus
class StatusLED(QAbstractButton): class StatusLED(QAbstractButton):
def __init__(self, colNone, colGood, colBad, sW, sH, parent=None): def __init__(self, colNone, colGood, colBad, sW, sH, parent=None):
+16 -14
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Theme and Icons Classes novelWriter Theme and Icons Classes
===================================== =====================================
@@ -43,6 +42,7 @@ from nw.enum import nwAlert
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# =============================================================================================== # # =============================================================================================== #
# Gui Theme Class # Gui Theme Class
# Handles the look and feel of novelWriter # Handles the look and feel of novelWriter
@@ -65,8 +65,9 @@ class GuiTheme:
self.syntaxList = [] self.syntaxList = []
# Loaded Theme Settings # Loaded Theme Settings
# =====================
## Theme # Theme
self.themeName = "" self.themeName = ""
self.themeDescription = "" self.themeDescription = ""
self.themeAuthor = "" self.themeAuthor = ""
@@ -75,7 +76,7 @@ class GuiTheme:
self.themeLicense = "" self.themeLicense = ""
self.themeLicenseUrl = "" self.themeLicenseUrl = ""
## GUI # GUI
self.statNone = [120, 120, 120] self.statNone = [120, 120, 120]
self.statUnsaved = [200, 15, 39] self.statUnsaved = [200, 15, 39]
self.statSaved = [2, 133, 37] self.statSaved = [2, 133, 37]
@@ -83,7 +84,7 @@ class GuiTheme:
# Loaded Syntax Settings # Loaded Syntax Settings
## Main # Main
self.syntaxName = "" self.syntaxName = ""
self.syntaxDescription = "" self.syntaxDescription = ""
self.syntaxAuthor = "" self.syntaxAuthor = ""
@@ -92,7 +93,7 @@ class GuiTheme:
self.syntaxLicense = "" self.syntaxLicense = ""
self.syntaxLicenseUrl = "" self.syntaxLicenseUrl = ""
## 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]
@@ -288,7 +289,7 @@ class GuiTheme:
nw.logException() nw.logException()
return False return False
## 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", "")
@@ -299,7 +300,7 @@ class GuiTheme:
self.themeLicense = self._parseLine(confParser, cnfSec, "license", "N/A") self.themeLicense = self._parseLine(confParser, cnfSec, "license", "N/A")
self.themeLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "") self.themeLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
## 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)
@@ -317,7 +318,7 @@ class GuiTheme:
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.statNone = self._loadColour(confParser, cnfSec, "statusnone") self.statNone = self._loadColour(confParser, cnfSec, "statusnone")
@@ -346,7 +347,7 @@ class GuiTheme:
nw.logException() nw.logException()
return False return False
## 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", "")
@@ -357,7 +358,7 @@ class GuiTheme:
self.syntaxLicense = self._parseLine(confParser, cnfSec, "license", "") self.syntaxLicense = self._parseLine(confParser, cnfSec, "license", "")
self.syntaxLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "") self.syntaxLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
## 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")
@@ -496,6 +497,7 @@ class GuiTheme:
# End Class GuiTheme # End Class GuiTheme
# =============================================================================================== # # =============================================================================================== #
# Icons Class # Icons Class
# =============================================================================================== # # =============================================================================================== #
@@ -560,7 +562,7 @@ class GuiIcons:
"search_cancel" : (None, None), "search_cancel" : (None, None),
"search_preserve" : (None, None), "search_preserve" : (None, None),
## General Button Icons # General Button Icons
"folder-open" : (QStyle.SP_DirOpenIcon, "folder-open"), "folder-open" : (QStyle.SP_DirOpenIcon, "folder-open"),
"delete" : (QStyle.SP_DialogDiscardButton, "edit-delete"), "delete" : (QStyle.SP_DialogDiscardButton, "edit-delete"),
"close" : (QStyle.SP_DialogCloseButton, "window-close"), "close" : (QStyle.SP_DialogCloseButton, "window-close"),
@@ -583,7 +585,7 @@ class GuiIcons:
"forward" : (None, None), "forward" : (None, None),
"settings" : (None, None), "settings" : (None, None),
## Switches # Switches
"sticky-on" : (None, None), "sticky-on" : (None, None),
"sticky-off" : (None, None), "sticky-off" : (None, None),
"bullet-on" : (None, None), "bullet-on" : (None, None),
@@ -651,7 +653,7 @@ class GuiIcons:
nw.logException() nw.logException()
return False return False
## 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", "")
@@ -662,7 +664,7 @@ class GuiIcons:
self.themeLicense = self._parseLine(confParser, cnfSec, "license", "N/A") self.themeLicense = self._parseLine(confParser, cnfSec, "license", "N/A")
self.themeLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "") self.themeLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
## Palette # Palette
cnfSec = "Map" cnfSec = "Map"
if confParser.has_section(cnfSec): if confParser.has_section(cnfSec):
for iconName, iconFile in confParser.items(cnfSec): for iconName, iconFile in confParser.items(cnfSec):
+4 -4
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Main Window novelWriter GUI Main Window
============================= =============================
@@ -55,6 +54,7 @@ from nw.constants import nwLists
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiMain(QMainWindow): class GuiMain(QMainWindow):
def __init__(self): def __init__(self):
@@ -613,9 +613,9 @@ class GuiMain(QMainWindow):
return False return False
self.treeView.flushTreeOrder() self.treeView.flushTreeOrder()
nHandle = None # The next handle after tHandle nHandle = None # The next handle after tHandle
fHandle = None # The first file handle we encounter fHandle = None # The first file handle we encounter
foundIt = False # We've found tHandle, pick the next we see foundIt = False # We've found tHandle, pick the next we see
for tItem in self.theProject.projTree: for tItem in self.theProject.projTree:
if tItem is None: if tItem is None:
continue continue
+20 -1
View File
@@ -1,4 +1,23 @@
# -*- coding: utf-8 -*- """
novelWriter Tools Init
========================
This file is a part of novelWriter
Copyright 20182021, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from nw.tools.build import GuiBuildNovel from nw.tools.build import GuiBuildNovel
from nw.tools.projwizard import GuiProjectWizard from nw.tools.projwizard import GuiProjectWizard
+18 -17
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Build Novel Project novelWriter GUI Build Novel Project
===================================== =====================================
@@ -52,19 +51,20 @@ from nw.gui.custom import QSwitch
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiBuildNovel(QDialog): class GuiBuildNovel(QDialog):
FMT_PDF = 1 # Print to PDF FMT_PDF = 1 # Print to PDF
FMT_ODT = 2 # Open Document file FMT_ODT = 2 # Open Document file
FMT_FODT = 3 # Flat Open Document file FMT_FODT = 3 # Flat Open Document file
FMT_HTM = 4 # HTML5 FMT_HTM = 4 # HTML5
FMT_NWD = 5 # nW Markdown FMT_NWD = 5 # nW Markdown
FMT_MD = 6 # Standard Markdown FMT_MD = 6 # Standard Markdown
FMT_GH = 7 # GitHub Markdown FMT_GH = 7 # GitHub Markdown
FMT_JSON_H = 8 # HTML5 wrapped in JSON FMT_JSON_H = 8 # HTML5 wrapped in JSON
FMT_JSON_M = 9 # nW Markdown wrapped in JSON FMT_JSON_M = 9 # nW Markdown wrapped in JSON
def __init__(self, theParent): def __init__(self, theParent):
QDialog.__init__(self, theParent) QDialog.__init__(self, theParent)
@@ -78,10 +78,10 @@ class GuiBuildNovel(QDialog):
self.theProject = theParent.theProject self.theProject = theParent.theProject
self.optState = theParent.theProject.optState self.optState = theParent.theProject.optState
self.htmlText = [] # List of html documents self.htmlText = [] # List of html documents
self.htmlStyle = [] # List of html styles self.htmlStyle = [] # List of html styles
self.htmlSize = 0 # Size of the html document self.htmlSize = 0 # Size of the html document
self.buildTime = 0 # The timestamp of the last build self.buildTime = 0 # The timestamp of the last build
self.setWindowTitle(self.tr("Build Novel Project")) self.setWindowTitle(self.tr("Build Novel Project"))
self.setMinimumWidth(self.mainConf.pxInt(700)) self.setMinimumWidth(self.mainConf.pxInt(700))
@@ -215,7 +215,7 @@ class GuiBuildNovel(QDialog):
self.fontForm = QGridLayout(self) self.fontForm = QGridLayout(self)
self.fontGroup.setLayout(self.fontForm) self.fontGroup.setLayout(self.fontForm)
## Font Family # Font Family
self.textFont = QLineEdit() self.textFont = QLineEdit()
self.textFont.setReadOnly(True) self.textFont.setReadOnly(True)
self.textFont.setMinimumWidth(xFmt) self.textFont.setMinimumWidth(xFmt)
@@ -991,7 +991,7 @@ class GuiBuildNovel(QDialog):
else: else:
# If the if statements above and here match, it should not # If the if statements above and here match, it should not
# be possible to reach this else statement. # be possible to reach this else statement.
return False # pragma: no cover return False # pragma: no cover
# Report to User # Report to User
# ============== # ==============
@@ -1035,7 +1035,7 @@ class GuiBuildNovel(QDialog):
self.textFont.setText(theFont.family()) self.textFont.setText(theFont.family())
self.textSize.setValue(theFont.pointSize()) self.textSize.setValue(theFont.pointSize())
self.raise_() # Move the dialog to front (fixes a bug on macOS) self.raise_() # Move the dialog to front (fixes a bug on macOS)
return return
@@ -1182,6 +1182,7 @@ class GuiBuildNovel(QDialog):
# END Class GuiBuildNovel # END Class GuiBuildNovel
class GuiBuildNovelDocView(QTextBrowser): class GuiBuildNovelDocView(QTextBrowser):
def __init__(self, theParent, theProject): def __init__(self, theParent, theProject):
+6 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI New Project Wizard novelWriter GUI New Project Wizard
==================================== ====================================
@@ -48,6 +47,7 @@ PAGE_POP = 2
PAGE_CUSTOM = 3 PAGE_CUSTOM = 3
PAGE_FINAL = 4 PAGE_FINAL = 4
class GuiProjectWizard(QWizard): class GuiProjectWizard(QWizard):
def __init__(self, theParent): def __init__(self, theParent):
@@ -86,6 +86,7 @@ class GuiProjectWizard(QWizard):
# END Class GuiProjectWizard # END Class GuiProjectWizard
class ProjWizardIntroPage(QWizardPage): class ProjWizardIntroPage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
@@ -155,6 +156,7 @@ class ProjWizardIntroPage(QWizardPage):
# END Class ProjWizardIntroPage # END Class ProjWizardIntroPage
class ProjWizardFolderPage(QWizardPage): class ProjWizardFolderPage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
@@ -227,6 +229,7 @@ class ProjWizardFolderPage(QWizardPage):
# END Class ProjWizardFolderPage # END Class ProjWizardFolderPage
class ProjWizardPopulatePage(QWizardPage): class ProjWizardPopulatePage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
@@ -282,6 +285,7 @@ class ProjWizardPopulatePage(QWizardPage):
# END Class ProjWizardPopulatePage # END Class ProjWizardPopulatePage
class ProjWizardCustomPage(QWizardPage): class ProjWizardCustomPage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
@@ -400,6 +404,7 @@ class ProjWizardCustomPage(QWizardPage):
# END Class ProjWizardCustomPage # END Class ProjWizardCustomPage
class ProjWizardFinalPage(QWizardPage): class ProjWizardFinalPage(QWizardPage):
def __init__(self, theWizard): def __init__(self, theWizard):
+2 -2
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Writing Statistics novelWriter GUI Writing Statistics
==================================== ====================================
@@ -45,6 +44,7 @@ from nw.gui.custom import QSwitch
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiWritingStats(QDialog): class GuiWritingStats(QDialog):
C_TIME = 0 C_TIME = 0
@@ -570,7 +570,7 @@ class GuiWritingStats(QDialog):
if isFirst: if isFirst:
# Subtract the offset from the first list entry # Subtract the offset from the first list entry
dwTotal -= self.wordOffset dwTotal -= self.wordOffset
dwTotal = max(dwTotal, 1) # Don't go zero or negative dwTotal = max(dwTotal, 1) # Don't go zero or negative
isFirst = False isFirst = False
if groupByDay: if groupByDay:
+1 -1
View File
@@ -49,6 +49,6 @@ gui_scripts =
universal = 0 universal = 0
[flake8] [flake8]
ignore = E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 ignore = E203,E221,E226,E228,E241,E251
max-line-length = 99 max-line-length = 99
exclude = docs/* exclude = docs/*
+16 -2
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
novelWriter Main Setup Script novelWriter Main Setup Script
=============================== ===============================
@@ -35,6 +34,7 @@ OS_LINUX = 1
OS_WIN = 2 OS_WIN = 2
OS_DARWIN = 3 OS_DARWIN = 3
# =============================================================================================== # # =============================================================================================== #
# Utilities # Utilities
# =============================================================================================== # # =============================================================================================== #
@@ -66,6 +66,7 @@ def extractVersion():
return numVers, hexVers return numVers, hexVers
# =============================================================================================== # # =============================================================================================== #
# General # General
# =============================================================================================== # # =============================================================================================== #
@@ -102,6 +103,7 @@ def installPackages(hostOS):
return return
## ##
# Clean Build and Dist Folders (clean) # Clean Build and Dist Folders (clean)
## ##
@@ -140,6 +142,7 @@ def cleanInstall():
return return
# =============================================================================================== # # =============================================================================================== #
# Additional Buiilds # Additional Buiilds
# =============================================================================================== # # =============================================================================================== #
@@ -217,6 +220,7 @@ def buildQtDocs():
return return
## ##
# Qt Linguist QM Builder (qtlrelease) # Qt Linguist QM Builder (qtlrelease)
## ##
@@ -255,6 +259,7 @@ def buildQtI18n():
return return
## ##
# Qt Linguist TS Builder (qtlupdate) # Qt Linguist TS Builder (qtlupdate)
## ##
@@ -278,6 +283,7 @@ def buildQtI18nTS():
return return
## ##
# Sample Project ZIP File Builder (sample) # Sample Project ZIP File Builder (sample)
## ##
@@ -318,6 +324,7 @@ def buildSampleZip():
return return
# =============================================================================================== # # =============================================================================================== #
# Python Packaging # Python Packaging
# =============================================================================================== # # =============================================================================================== #
@@ -429,6 +436,7 @@ def makeMinimalPackage(targetOS):
return return
## ##
# Make Simple Package (pack-pyz) # Make Simple Package (pack-pyz)
## ##
@@ -607,6 +615,7 @@ def makeSimplePackage(embedPython):
return return
# =============================================================================================== # # =============================================================================================== #
# General Installers # General Installers
# =============================================================================================== # # =============================================================================================== #
@@ -754,6 +763,7 @@ def xdgInstall():
return return
## ##
# XDG Uninstallation (xdg-uninstall) # XDG Uninstallation (xdg-uninstall)
## ##
@@ -823,6 +833,7 @@ def xdgUninstall():
return return
## ##
# WIN Installation (win-install) # WIN Installation (win-install)
## ##
@@ -947,6 +958,7 @@ def winInstall():
return return
## ##
# WIN Uninstallation (win-uninstall) # WIN Uninstallation (win-uninstall)
## ##
@@ -1033,6 +1045,7 @@ def winUninstall():
return return
# =============================================================================================== # # =============================================================================================== #
# Windows Installers # Windows Installers
# =============================================================================================== # # =============================================================================================== #
@@ -1070,6 +1083,7 @@ def innoSetup():
return return
# =============================================================================================== # # =============================================================================================== #
# Process Command Line # Process Command Line
# =============================================================================================== # # =============================================================================================== #
@@ -1280,7 +1294,7 @@ if __name__ == "__main__":
sys.exit(0) sys.exit(0)
# Run the standard setup # Run the standard setup
import setuptools # noqa: F401 import setuptools # noqa: F401
setuptools.setup() setuptools.setup()
# END Main # END Main
+15 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Test Suite Configuration novelWriter Test Suite Configuration
====================================== ======================================
@@ -30,9 +29,10 @@ from tools import cleanProject
sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
import nw # noqa: E402 import nw # noqa: E402
from nw.config import Config # noqa: E402
from nw.config import Config # noqa: E402
## ##
# Core Test Folders # Core Test Folders
@@ -52,6 +52,7 @@ def tmpDir():
os.mkdir(theDir) os.mkdir(theDir)
return theDir return theDir
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def refDir(): def refDir():
"""The folder where all the reference files are stored for verifying """The folder where all the reference files are stored for verifying
@@ -61,6 +62,7 @@ def refDir():
theDir = os.path.join(testDir, "reference") theDir = os.path.join(testDir, "reference")
return theDir return theDir
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def filesDir(): def filesDir():
"""The folder where additional test files are stored. """The folder where additional test files are stored.
@@ -69,6 +71,7 @@ def filesDir():
theDir = os.path.join(testDir, "files") theDir = os.path.join(testDir, "files")
return theDir return theDir
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def outDir(tmpDir): def outDir(tmpDir):
"""An output folder for test results """An output folder for test results
@@ -78,6 +81,7 @@ def outDir(tmpDir):
os.mkdir(theDir) os.mkdir(theDir)
return theDir return theDir
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def fncDir(tmpDir): def fncDir(tmpDir):
"""A temporary folder for a single test function. """A temporary folder for a single test function.
@@ -92,6 +96,7 @@ def fncDir(tmpDir):
shutil.rmtree(fncDir) shutil.rmtree(fncDir)
return return
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def fncProj(fncDir): def fncProj(fncDir):
"""A temporary folder for a single test function, """A temporary folder for a single test function,
@@ -104,6 +109,7 @@ def fncProj(fncDir):
os.mkdir(prjDir) os.mkdir(prjDir)
return prjDir return prjDir
## ##
# novelWriter Objects # novelWriter Objects
## ##
@@ -121,6 +127,7 @@ def tmpConf(tmpDir):
theConf.guiLang = "en_GB" theConf.guiLang = "en_GB"
return theConf return theConf
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def fncConf(fncDir): def fncConf(fncDir):
"""Create a temporary novelWriter configuration object. """Create a temporary novelWriter configuration object.
@@ -134,6 +141,7 @@ def fncConf(fncDir):
theConf.guiLang = "en_GB" theConf.guiLang = "en_GB"
return theConf return theConf
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def dummyGUI(monkeypatch, tmpConf): def dummyGUI(monkeypatch, tmpConf):
"""Create a mock instance of novelWriter's main GUI class. """Create a mock instance of novelWriter's main GUI class.
@@ -143,6 +151,7 @@ def dummyGUI(monkeypatch, tmpConf):
theGui.mainConf = tmpConf theGui.mainConf = tmpConf
return theGui return theGui
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def nwGUI(qtbot, monkeypatch, fncDir, fncConf): def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
"""Create an instance of the novelWriter GUI. """Create an instance of the novelWriter GUI.
@@ -164,6 +173,7 @@ def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
return return
## ##
# Temp Project Folders # Temp Project Folders
## ##
@@ -188,6 +198,7 @@ def nwMinimal(tmpDir):
return return
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def nwLipsum(tmpDir): def nwLipsum(tmpDir):
"""A medium sized novelWriter example project with a lot of Lorem """A medium sized novelWriter example project with a lot of Lorem
@@ -209,6 +220,7 @@ def nwLipsum(tmpDir):
return return
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def nwOldProj(tmpDir): def nwOldProj(tmpDir):
"""A minimal movelWriter project using the old folder structure used """A minimal movelWriter project using the old folder structure used
+5 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Test Suite Mocked Classes novelWriter Test Suite Mocked Classes
======================================= =======================================
@@ -20,6 +19,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
# =========================================================================== # # =========================================================================== #
# Mock GUI # Mock GUI
# =========================================================================== # # =========================================================================== #
@@ -79,6 +79,7 @@ class MockGuiMain():
# END Class MockGuiMain # END Class MockGuiMain
class MockStatusBar(): class MockStatusBar():
def __init__(self): def __init__(self):
@@ -92,6 +93,7 @@ class MockStatusBar():
# END Class MockStatusBar # END Class MockStatusBar
class MockApp: class MockApp:
def __init__(self): def __init__(self):
@@ -102,6 +104,7 @@ class MockApp:
# END Class MockApp # END Class MockApp
# =========================================================================== # # =========================================================================== #
# Error Functions # Error Functions
# Mock functions that will raise errors instead. # Mock functions that will raise errors instead.
@@ -110,5 +113,6 @@ class MockApp:
def causeOSError(*args, **kwargs): def causeOSError(*args, **kwargs):
raise OSError("OSError") raise OSError("OSError")
def causeException(*args, **kwargs): def causeException(*args, **kwargs):
raise Exception("Exception") raise Exception("Exception")
+17 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Common Functions Tester novelWriter Common Functions Tester
===================================== =====================================
@@ -30,6 +29,7 @@ from nw.common import (
isItemLayout, numberToRoman isItemLayout, numberToRoman
) )
@pytest.mark.base @pytest.mark.base
def testBaseCommon_CheckString(): def testBaseCommon_CheckString():
"""Test the checkString function. """Test the checkString function.
@@ -44,6 +44,7 @@ def testBaseCommon_CheckString():
# END Test testBaseCommon_CheckString # END Test testBaseCommon_CheckString
@pytest.mark.base @pytest.mark.base
def testBaseCommon_CheckInt(): def testBaseCommon_CheckInt():
"""Test the checkInt function. """Test the checkInt function.
@@ -57,6 +58,7 @@ def testBaseCommon_CheckInt():
# END Test testBaseCommon_CheckInt # END Test testBaseCommon_CheckInt
@pytest.mark.base @pytest.mark.base
def testBaseCommon_CheckBool(): def testBaseCommon_CheckBool():
"""Test the checkBool function. """Test the checkBool function.
@@ -75,6 +77,7 @@ def testBaseCommon_CheckBool():
# END Test testBaseCommon_CheckBool # END Test testBaseCommon_CheckBool
@pytest.mark.base @pytest.mark.base
def testBaseCommon_CheckHandle(): def testBaseCommon_CheckHandle():
"""Test the checkHandle function. """Test the checkHandle function.
@@ -88,6 +91,7 @@ def testBaseCommon_CheckHandle():
# END Test testBaseCommon_CheckHandle # END Test testBaseCommon_CheckHandle
@pytest.mark.base @pytest.mark.base
def testBaseCommon_IsHandle(): def testBaseCommon_IsHandle():
"""Test the isHandle function. """Test the isHandle function.
@@ -102,6 +106,7 @@ def testBaseCommon_IsHandle():
# END Test testBaseCommon_IsHandle # END Test testBaseCommon_IsHandle
@pytest.mark.base @pytest.mark.base
def testBaseCommon_IsTitleTag(): def testBaseCommon_IsTitleTag():
"""Test the isItemClass function. """Test the isItemClass function.
@@ -119,6 +124,7 @@ def testBaseCommon_IsTitleTag():
# END Test testBaseCommon_IsTitleTag # END Test testBaseCommon_IsTitleTag
@pytest.mark.base @pytest.mark.base
def testBaseCommon_IsItemClass(): def testBaseCommon_IsItemClass():
"""Test the isItemClass function. """Test the isItemClass function.
@@ -141,6 +147,7 @@ def testBaseCommon_IsItemClass():
# END Test testBaseCommon_IsItemClass # END Test testBaseCommon_IsItemClass
@pytest.mark.base @pytest.mark.base
def testBaseCommon_IsItemType(): def testBaseCommon_IsItemType():
"""Test the isItemType function. """Test the isItemType function.
@@ -157,6 +164,7 @@ def testBaseCommon_IsItemType():
# END Test testBaseCommon_IsItemType # END Test testBaseCommon_IsItemType
@pytest.mark.base @pytest.mark.base
def testBaseCommon_IsItemLayout(): def testBaseCommon_IsItemLayout():
"""Test the isItemLayout function. """Test the isItemLayout function.
@@ -177,6 +185,7 @@ def testBaseCommon_IsItemLayout():
# END Test testBaseCommon_IsItemLayout # END Test testBaseCommon_IsItemLayout
@pytest.mark.base @pytest.mark.base
def testBaseCommon_HexToInt(): def testBaseCommon_HexToInt():
"""Test the hexToInt function. """Test the hexToInt function.
@@ -190,6 +199,7 @@ def testBaseCommon_HexToInt():
# END Test testBaseCommon_HexToInt # END Test testBaseCommon_HexToInt
@pytest.mark.base @pytest.mark.base
def testBaseCommon_FormatTimeStamp(): def testBaseCommon_FormatTimeStamp():
"""Test the formatTimeStamp function. """Test the formatTimeStamp function.
@@ -200,6 +210,7 @@ def testBaseCommon_FormatTimeStamp():
# END Test testBaseCommon_FormatTimeStamp # END Test testBaseCommon_FormatTimeStamp
@pytest.mark.base @pytest.mark.base
def testBaseCommon_FormatTime(): def testBaseCommon_FormatTime():
"""Test the formatTime function. """Test the formatTime function.
@@ -222,6 +233,7 @@ def testBaseCommon_FormatTime():
# END Test testBaseCommon_FormatTime # END Test testBaseCommon_FormatTime
@pytest.mark.base @pytest.mark.base
def testBaseCommon_FormatInt(): def testBaseCommon_FormatInt():
"""Test the formatInt function. """Test the formatInt function.
@@ -237,6 +249,7 @@ def testBaseCommon_FormatInt():
# END Test testBaseCommon_FormatInt # END Test testBaseCommon_FormatInt
@pytest.mark.base @pytest.mark.base
def testBaseCommon_TransferCase(): def testBaseCommon_TransferCase():
"""Test the transferCase function. """Test the transferCase function.
@@ -251,6 +264,7 @@ def testBaseCommon_TransferCase():
# END Test testBaseCommon_TransferCase # END Test testBaseCommon_TransferCase
@pytest.mark.base @pytest.mark.base
def testBaseCommon_FuzzyTime(): def testBaseCommon_FuzzyTime():
"""Test the fuzzyTime function. """Test the fuzzyTime function.
@@ -286,6 +300,7 @@ def testBaseCommon_FuzzyTime():
# END Test testBaseCommon_FuzzyTime # END Test testBaseCommon_FuzzyTime
@pytest.mark.base @pytest.mark.base
def testBaseCommon_MakeFileNameSafe(): def testBaseCommon_MakeFileNameSafe():
"""Test the fuzzyTime function. """Test the fuzzyTime function.
@@ -297,6 +312,7 @@ def testBaseCommon_MakeFileNameSafe():
# END Test testBaseCommon_MakeFileNameSafe # END Test testBaseCommon_MakeFileNameSafe
@pytest.mark.core @pytest.mark.core
def testBaseCommon_RomanNumbers(): def testBaseCommon_RomanNumbers():
"""Test conversion of integers to Roman numbers. """Test conversion of integers to Roman numbers.
+8 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Config Class Tester novelWriter Config Class Tester
================================= =================================
@@ -33,6 +32,7 @@ from tools import cmpFiles, writeFile
from nw.config import Config from nw.config import Config
from nw.constants import nwConst, nwFiles from nw.constants import nwConst, nwFiles
@pytest.mark.base @pytest.mark.base
def testBaseConfig_Constructor(monkeypatch): def testBaseConfig_Constructor(monkeypatch):
"""Test config contructor. """Test config contructor.
@@ -79,6 +79,7 @@ def testBaseConfig_Constructor(monkeypatch):
# END Test testBaseConfig_Constructor # END Test testBaseConfig_Constructor
@pytest.mark.base @pytest.mark.base
def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir): def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
"""Test config intialisation. """Test config intialisation.
@@ -95,7 +96,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
# Let the config class figure out the path # Let the config class figure out the path
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("PyQt5.QtCore.QStandardPaths.writableLocation", lambda *args: fncDir) mp.setattr("PyQt5.QtCore.QStandardPaths.writableLocation", lambda *a: fncDir)
tstConf.verQtValue = 50600 tstConf.verQtValue = 50600
tstConf.initConfig() tstConf.initConfig()
assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle) assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle)
@@ -132,7 +133,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
# Run again and set the paths directly and correctly # Run again and set the paths directly and correctly
# This should create a config file as well # This should create a config file as well
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("os.path.expanduser", lambda *args: "") mp.setattr("os.path.expanduser", lambda *a: "")
tstConf.spellTool = nwConst.SP_INTERNAL tstConf.spellTool = nwConst.SP_INTERNAL
tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir) tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir)
assert tstConf.confPath == tmpDir assert tstConf.confPath == tmpDir
@@ -205,6 +206,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
# END Test testBaseConfig_Init # END Test testBaseConfig_Init
@pytest.mark.base @pytest.mark.base
def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir): def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir):
"""Test recent cache file. """Test recent cache file.
@@ -266,6 +268,7 @@ def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir):
# END Test testBaseConfig_RecentCache # END Test testBaseConfig_RecentCache
@pytest.mark.base @pytest.mark.base
def testBaseConfig_SetPath(tmpConf, tmpDir): def testBaseConfig_SetPath(tmpConf, tmpDir):
"""Test path setters. """Test path setters.
@@ -297,6 +300,7 @@ def testBaseConfig_SetPath(tmpConf, tmpDir):
# END Test testBaseConfig_SetPath # END Test testBaseConfig_SetPath
@pytest.mark.base @pytest.mark.base
def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir): def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
"""Set various sizes and positions """Set various sizes and positions
@@ -470,6 +474,7 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
# END Test testBaseConfig_SettersGetters # END Test testBaseConfig_SettersGetters
@pytest.mark.base @pytest.mark.base
def testBaseConfig_Internal(monkeypatch, tmpConf): def testBaseConfig_Internal(monkeypatch, tmpConf):
"""Check internal functions. """Check internal functions.
+10 -9
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Error Handler Tester novelWriter Error Handler Tester
================================== ==================================
@@ -29,6 +28,7 @@ from mock import causeException
from nw.error import NWErrorMessage, exceptionHandler from nw.error import NWErrorMessage, exceptionHandler
@pytest.mark.base @pytest.mark.base
def testBaseError_Dialog(qtbot, monkeypatch, fncDir, tmpDir): def testBaseError_Dialog(qtbot, monkeypatch, fncDir, tmpDir):
"""Test the error dialog. """Test the error dialog.
@@ -71,6 +71,7 @@ def testBaseError_Dialog(qtbot, monkeypatch, fncDir, tmpDir):
# END Test testBaseError_Dialog # END Test testBaseError_Dialog
@pytest.mark.base @pytest.mark.base
def testBaseError_Handler(qtbot, monkeypatch, fncDir, tmpDir): def testBaseError_Handler(qtbot, monkeypatch, fncDir, tmpDir):
"""Test the error handler. This test doesn'thave any asserts, but it """Test the error handler. This test doesn'thave any asserts, but it
@@ -85,28 +86,28 @@ def testBaseError_Handler(qtbot, monkeypatch, fncDir, tmpDir):
# Normal shutdown # Normal shutdown
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(NWErrorMessage, "exec_", lambda *args: None) mp.setattr(NWErrorMessage, "exec_", lambda *a: None)
mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *a: None)
exceptionHandler(Exception, "Error Message", None) exceptionHandler(Exception, "Error Message", None)
# Should not crash when no GUI is found # Should not crash when no GUI is found
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(NWErrorMessage, "exec_", lambda *args: None) mp.setattr(NWErrorMessage, "exec_", lambda *a: None)
mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *a: None)
mp.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", lambda: []) mp.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", lambda: [])
exceptionHandler(Exception, "Error Message", None) exceptionHandler(Exception, "Error Message", None)
# Should handle qApp failing # Should handle qApp failing
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(NWErrorMessage, "exec_", lambda *args: None) mp.setattr(NWErrorMessage, "exec_", lambda *a: None)
mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *a: None)
mp.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", causeException) mp.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", causeException)
exceptionHandler(Exception, "Error Message", None) exceptionHandler(Exception, "Error Message", None)
# Should handle failing to close main GUI # Should handle failing to close main GUI
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(NWErrorMessage, "exec_", lambda *args: None) mp.setattr(NWErrorMessage, "exec_", lambda *a: None)
mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *a: None)
mp.setattr(nwGUI, "closeMain", causeException) mp.setattr(nwGUI, "closeMain", causeException)
exceptionHandler(Exception, "Error Message", None) exceptionHandler(Exception, "Error Message", None)
+7 -5
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Main Init Tester novelWriter Main Init Tester
============================== ==============================
@@ -27,6 +26,7 @@ import sys
from mock import MockGuiMain from mock import MockGuiMain
@pytest.mark.base @pytest.mark.base
def testBaseInit_Launch(caplog, monkeypatch, tmpDir): def testBaseInit_Launch(caplog, monkeypatch, tmpDir):
"""Check launching the main GUI. """Check launching the main GUI.
@@ -64,6 +64,7 @@ def testBaseInit_Launch(caplog, monkeypatch, tmpDir):
# END Test testBaseInit_Launch # END Test testBaseInit_Launch
@pytest.mark.base @pytest.mark.base
def testBaseInit_Options(monkeypatch, tmpDir): def testBaseInit_Options(monkeypatch, tmpDir):
"""Test command line options for logging level. """Test command line options for logging level.
@@ -136,6 +137,7 @@ def testBaseInit_Options(monkeypatch, tmpDir):
# END Test testBaseInit_Options # END Test testBaseInit_Options
@pytest.mark.base @pytest.mark.base
def testBaseInit_Imports(caplog, monkeypatch, tmpDir): def testBaseInit_Imports(caplog, monkeypatch, tmpDir):
"""Check import error handling. """Check import error handling.
@@ -156,10 +158,10 @@ def testBaseInit_Imports(caplog, monkeypatch, tmpDir):
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir] ["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
) )
assert ex.value.code & 4 == 4 # Python version not satisfied assert ex.value.code & 4 == 4 # Python version not satisfied
assert ex.value.code & 8 == 8 # Qt version not satisfied assert ex.value.code & 8 == 8 # Qt version not satisfied
assert ex.value.code & 16 == 16 # PyQt version not satisfied assert ex.value.code & 16 == 16 # PyQt version not satisfied
assert ex.value.code & 32 == 32 # lxml package missing assert ex.value.code & 32 == 32 # lxml package missing
assert "At least Python" in caplog.messages[0] assert "At least Python" in caplog.messages[0]
assert "At least Qt5" in caplog.messages[1] assert "At least Qt5" in caplog.messages[1]
+3 -2
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter NWDoc Class Tester novelWriter NWDoc Class Tester
================================ ================================
@@ -28,6 +27,7 @@ from mock import causeOSError
from nw.core import NWProject, NWDoc from nw.core import NWProject, NWDoc
from nw.enum import nwItemClass, nwItemLayout from nw.enum import nwItemClass, nwItemLayout
@pytest.mark.core @pytest.mark.core
def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal): def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
"""Test loading and saving a document with the NWDoc class. """Test loading and saving a document with the NWDoc class.
@@ -120,8 +120,9 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
# END Test testCoreDocument_Load # END Test testCoreDocument_Load
@pytest.mark.core @pytest.mark.core
def testCoreDocument_Methods(monkeypatch, dummyGUI, nwMinimal): def testCoreDocument_Methods(dummyGUI, nwMinimal):
"""Test other methods of the NWDoc class. """Test other methods of the NWDoc class.
""" """
theProject = NWProject(dummyGUI) theProject = NWProject(dummyGUI)
+29 -20
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter NWIndex Class Tester novelWriter NWIndex Class Tester
================================== ==================================
@@ -33,6 +32,7 @@ from nw.core.project import NWProject
from nw.core.index import NWIndex, countWords from nw.core.index import NWIndex, countWords
from nw.enum import nwItemClass, nwItemLayout from nw.enum import nwItemClass, nwItemLayout
@pytest.mark.core @pytest.mark.core
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir): def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
"""Test core functionality of scaning, saving, loading and checking """Test core functionality of scaning, saving, loading and checking
@@ -50,12 +50,12 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
theIndex = NWIndex(theProject) theIndex = NWIndex(theProject)
notIndexable = { notIndexable = {
"b3643d0f92e32": False, # Novel ROOT "b3643d0f92e32": False, # Novel ROOT
"45e6b01ca35c1": False, # Chapter One FOLDER "45e6b01ca35c1": False, # Chapter One FOLDER
"6bd935d2490cd": False, # Chapter Two FOLDER "6bd935d2490cd": False, # Chapter Two FOLDER
"67a8707f2f249": False, # Character ROOT "67a8707f2f249": False, # Character ROOT
"6c6afb1247750": False, # Plot ROOT "6c6afb1247750": False, # Plot ROOT
"60bdf227455cc": False, # World ROOT "60bdf227455cc": False, # World ROOT
} }
for tItem in theProject.projTree: for tItem in theProject.projTree:
assert theIndex.reIndexHandle(tItem.itemHandle) is notIndexable.get(tItem.itemHandle, True) assert theIndex.reIndexHandle(tItem.itemHandle) is notIndexable.get(tItem.itemHandle, True)
@@ -124,6 +124,7 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
# END Test testCoreIndex_LoadSave # END Test testCoreIndex_LoadSave
@pytest.mark.core @pytest.mark.core
def testCoreIndex_ScanThis(nwMinimal, dummyGUI): def testCoreIndex_ScanThis(nwMinimal, dummyGUI):
"""Test the tag scanner function scanThis. """Test the tag scanner function scanThis.
@@ -175,6 +176,7 @@ def testCoreIndex_ScanThis(nwMinimal, dummyGUI):
# END Test testCoreIndex_ScanThis # END Test testCoreIndex_ScanThis
@pytest.mark.core @pytest.mark.core
def testCoreIndex_CheckThese(nwMinimal, dummyGUI): def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
"""Test the tag checker function checkThese. """Test the tag checker function checkThese.
@@ -202,7 +204,7 @@ def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
assert theIndex.scanText(nHandle, ( assert theIndex.scanText(nHandle, (
"# Hello World!\n" "# Hello World!\n"
"@pov: Jane\n" "@pov: Jane\n"
"@invalid: John\n" # Checks for issue #688 "@invalid: John\n" # Checks for issue #688
)) ))
assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]} assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!" assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
@@ -236,6 +238,7 @@ def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
# END Test testCoreIndex_CheckThese # END Test testCoreIndex_CheckThese
@pytest.mark.core @pytest.mark.core
def testCoreIndex_ScanText(nwMinimal, dummyGUI): def testCoreIndex_ScanText(nwMinimal, dummyGUI):
"""Check the index text scanner. """Check the index text scanner.
@@ -309,29 +312,29 @@ def testCoreIndex_ScanText(nwMinimal, dummyGUI):
"#### Title Four\n\n" "#### Title Four\n\n"
"% synopsis: Synopsis Four.\n\n" "% synopsis: Synopsis Four.\n\n"
"Paragraph Four.\n\n" "Paragraph Four.\n\n"
"##### Title Five\n\n" # Not interpreted as a title, the hashes are counted as a word "##### Title Five\n\n" # Not interpreted as a title, the hashes are counted as a word
"Paragraph Five.\n\n" "Paragraph Five.\n\n"
)) ))
assert theIndex._refIndex[nHandle].get("T000000", None) is not None # Always there assert theIndex._refIndex[nHandle].get("T000000", None) is not None # Always there
assert theIndex._refIndex[nHandle].get("T000001", None) is not None # Heading 1 assert theIndex._refIndex[nHandle].get("T000001", None) is not None # Heading 1
assert theIndex._refIndex[nHandle].get("T000002", None) is None assert theIndex._refIndex[nHandle].get("T000002", None) is None
assert theIndex._refIndex[nHandle].get("T000003", None) is None assert theIndex._refIndex[nHandle].get("T000003", None) is None
assert theIndex._refIndex[nHandle].get("T000004", None) is None assert theIndex._refIndex[nHandle].get("T000004", None) is None
assert theIndex._refIndex[nHandle].get("T000005", None) is None assert theIndex._refIndex[nHandle].get("T000005", None) is None
assert theIndex._refIndex[nHandle].get("T000006", None) is None assert theIndex._refIndex[nHandle].get("T000006", None) is None
assert theIndex._refIndex[nHandle].get("T000007", None) is not None # Heading 2 assert theIndex._refIndex[nHandle].get("T000007", None) is not None # Heading 2
assert theIndex._refIndex[nHandle].get("T000008", None) is None assert theIndex._refIndex[nHandle].get("T000008", None) is None
assert theIndex._refIndex[nHandle].get("T000009", None) is None assert theIndex._refIndex[nHandle].get("T000009", None) is None
assert theIndex._refIndex[nHandle].get("T000010", None) is None assert theIndex._refIndex[nHandle].get("T000010", None) is None
assert theIndex._refIndex[nHandle].get("T000011", None) is None assert theIndex._refIndex[nHandle].get("T000011", None) is None
assert theIndex._refIndex[nHandle].get("T000012", None) is None assert theIndex._refIndex[nHandle].get("T000012", None) is None
assert theIndex._refIndex[nHandle].get("T000013", None) is not None # Heading 3 assert theIndex._refIndex[nHandle].get("T000013", None) is not None # Heading 3
assert theIndex._refIndex[nHandle].get("T000014", None) is None assert theIndex._refIndex[nHandle].get("T000014", None) is None
assert theIndex._refIndex[nHandle].get("T000015", None) is None assert theIndex._refIndex[nHandle].get("T000015", None) is None
assert theIndex._refIndex[nHandle].get("T000016", None) is None assert theIndex._refIndex[nHandle].get("T000016", None) is None
assert theIndex._refIndex[nHandle].get("T000017", None) is None assert theIndex._refIndex[nHandle].get("T000017", None) is None
assert theIndex._refIndex[nHandle].get("T000018", None) is None assert theIndex._refIndex[nHandle].get("T000018", None) is None
assert theIndex._refIndex[nHandle].get("T000019", None) is not None # Heading 4 assert theIndex._refIndex[nHandle].get("T000019", None) is not None # Heading 4
assert theIndex._refIndex[nHandle].get("T000020", None) is None assert theIndex._refIndex[nHandle].get("T000020", None) is None
assert theIndex._refIndex[nHandle].get("T000021", None) is None assert theIndex._refIndex[nHandle].get("T000021", None) is None
assert theIndex._refIndex[nHandle].get("T000022", None) is None assert theIndex._refIndex[nHandle].get("T000022", None) is None
@@ -400,9 +403,9 @@ def testCoreIndex_ScanText(nwMinimal, dummyGUI):
assert theIndex.scanText(sHandle, ( assert theIndex.scanText(sHandle, (
"# Title One\n\n" "# Title One\n\n"
"@pov: One\n\n" # Valid "@pov: One\n\n" # Valid
"@char: Two\n\n" # Invalid tag "@char: Two\n\n" # Invalid tag
"@:\n\n" # Invalid line "@:\n\n" # Invalid line
"% synopsis: Synopsis One.\n\n" "% synopsis: Synopsis One.\n\n"
"Paragraph One.\n\n" "Paragraph One.\n\n"
)) ))
@@ -441,6 +444,7 @@ def testCoreIndex_ScanText(nwMinimal, dummyGUI):
# END Test testCoreIndex_ScanText # END Test testCoreIndex_ScanText
@pytest.mark.core @pytest.mark.core
def testCoreIndex_ExtractData(nwMinimal, dummyGUI): def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
"""Check the index data extraction functions. """Check the index data extraction functions.
@@ -499,9 +503,9 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
# The novel file should have the correct counts # The novel file should have the correct counts
cC, wC, pC = theIndex.getCounts(nHandle) cC, wC, pC = theIndex.getCounts(nHandle)
assert cC == 62 # Characters in text and title only assert cC == 62 # Characters in text and title only
assert wC == 12 # Words in text and title only assert wC == 12 # Words in text and title only
assert pC == 2 # Paragraphs in text only assert pC == 2 # Paragraphs in text only
# getReferences # getReferences
# ============= # =============
@@ -673,6 +677,7 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
# END Test testCoreIndex_ExtractData # END Test testCoreIndex_ExtractData
@pytest.mark.core @pytest.mark.core
def testCoreIndex_CheckTagIndex(dummyGUI): def testCoreIndex_CheckTagIndex(dummyGUI):
"""Test the tag index checker. """Test the tag index checker.
@@ -737,6 +742,7 @@ def testCoreIndex_CheckTagIndex(dummyGUI):
# END Test testCoreIndex_CheckTagIndex # END Test testCoreIndex_CheckTagIndex
@pytest.mark.core @pytest.mark.core
def testCoreIndex_CheckRefIndex(dummyGUI): def testCoreIndex_CheckRefIndex(dummyGUI):
"""Test the reference index checker. """Test the reference index checker.
@@ -859,6 +865,7 @@ def testCoreIndex_CheckRefIndex(dummyGUI):
# END Test testCoreIndex_CheckRefIndex # END Test testCoreIndex_CheckRefIndex
@pytest.mark.core @pytest.mark.core
def testCoreIndex_CheckNovelNoteIndex(dummyGUI): def testCoreIndex_CheckNovelNoteIndex(dummyGUI):
"""Test the novel and note index checkers. """Test the novel and note index checkers.
@@ -1117,6 +1124,7 @@ def testCoreIndex_CheckNovelNoteIndex(dummyGUI):
# END Test testCoreIndex_CheckNovelNoteIndex # END Test testCoreIndex_CheckNovelNoteIndex
@pytest.mark.core @pytest.mark.core
def testCoreIndex_CheckTextCounts(dummyGUI): def testCoreIndex_CheckTextCounts(dummyGUI):
"""Test the text counts checker. """Test the text counts checker.
@@ -1173,6 +1181,7 @@ def testCoreIndex_CheckTextCounts(dummyGUI):
# END Test testCoreIndex_CheckTextCounts # END Test testCoreIndex_CheckTextCounts
@pytest.mark.core @pytest.mark.core
def testCoreIndex_CountWords(): def testCoreIndex_CountWords():
"""Test the word counter and the exclusion filers. """Test the word counter and the exclusion filers.
+9 -4
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter NWItem Class Tester novelWriter NWItem Class Tester
================================= =================================
@@ -28,6 +27,7 @@ from nw.core import NWProject
from nw.core.item import NWItem from nw.core.item import NWItem
from nw.enum import nwItemClass, nwItemType, nwItemLayout from nw.enum import nwItemClass, nwItemType, nwItemLayout
@pytest.mark.core @pytest.mark.core
def testCoreItem_Setters(dummyGUI): def testCoreItem_Setters(dummyGUI):
"""Test all the simple setters for the NWItem class. """Test all the simple setters for the NWItem class.
@@ -165,6 +165,7 @@ def testCoreItem_Setters(dummyGUI):
# END Test testCoreItem_Setters # END Test testCoreItem_Setters
@pytest.mark.core @pytest.mark.core
def testCoreItem_TypeSetter(dummyGUI): def testCoreItem_TypeSetter(dummyGUI):
"""Test the setter for all the nwItemType values for the NWItem """Test the setter for all the nwItemType values for the NWItem
@@ -193,6 +194,7 @@ def testCoreItem_TypeSetter(dummyGUI):
# END Test testCoreItem_TypeSetter # END Test testCoreItem_TypeSetter
@pytest.mark.core @pytest.mark.core
def testCoreItem_ClassSetter(dummyGUI): def testCoreItem_ClassSetter(dummyGUI):
"""Test the setter for all the nwItemClass values for the NWItem """Test the setter for all the nwItemClass values for the NWItem
@@ -233,6 +235,7 @@ def testCoreItem_ClassSetter(dummyGUI):
# END Test testCoreItem_ClassSetter # END Test testCoreItem_ClassSetter
@pytest.mark.core @pytest.mark.core
def testCoreItem_LayoutSetter(dummyGUI): def testCoreItem_LayoutSetter(dummyGUI):
"""Test the setter for all the nwItemLayout values for the NWItem """Test the setter for all the nwItemLayout values for the NWItem
@@ -269,6 +272,7 @@ def testCoreItem_LayoutSetter(dummyGUI):
# END Test testCoreItem_LayoutSetter # END Test testCoreItem_LayoutSetter
@pytest.mark.core @pytest.mark.core
def testCoreItem_XMLPackUnpack(dummyGUI, caplog): def testCoreItem_XMLPackUnpack(dummyGUI, caplog):
"""Test packing and unpacking XML objects for the NWItem class. """Test packing and unpacking XML objects for the NWItem class.
@@ -368,16 +372,17 @@ def testCoreItem_XMLPackUnpack(dummyGUI, caplog):
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
# Errors # Errors
# ======
## Not an Item # Not an Item
mockXml = etree.SubElement(nwXML, "stuff") mockXml = etree.SubElement(nwXML, "stuff")
assert theItem.unpackXML(mockXml) is False assert theItem.unpackXML(mockXml) is False
## Item without Handle # Item without Handle
mockXml = etree.SubElement(nwXML, "item", attrib={"stuff": "nah"}) mockXml = etree.SubElement(nwXML, "item", attrib={"stuff": "nah"})
assert theItem.unpackXML(mockXml) is False assert theItem.unpackXML(mockXml) is False
## Item with Invalid SubElement is Accepted w/Error # Item with Invalid SubElement is Accepted w/Error
mockXml = etree.SubElement(nwXML, "item", attrib={"handle": "0123456789abc"}) mockXml = etree.SubElement(nwXML, "item", attrib={"handle": "0123456789abc"})
xParam = etree.SubElement(mockXml, "invalid") xParam = etree.SubElement(mockXml, "invalid")
xParam.text = "stuff" xParam.text = "stuff"
+3 -2
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter OptionState Class Tester novelWriter OptionState Class Tester
====================================== ======================================
@@ -30,6 +29,7 @@ from nw.core import NWProject
from nw.core.options import OptionState from nw.core.options import OptionState
from nw.constants import nwFiles from nw.constants import nwFiles
@pytest.mark.core @pytest.mark.core
def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir): def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir):
"""Test loading and saving from the OptionState class. """Test loading and saving from the OptionState class.
@@ -100,8 +100,9 @@ def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir):
# END Test testCoreOptions_LoadSave # END Test testCoreOptions_LoadSave
@pytest.mark.core @pytest.mark.core
def testCoreOptions_SetGet(monkeypatch, dummyGUI, tmpDir): def testCoreOptions_SetGet(dummyGUI):
"""Test setting and getting values from the OptionState class. """Test setting and getting values from the OptionState class.
""" """
theProject = NWProject(dummyGUI) theProject = NWProject(dummyGUI)
+53 -37
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter NWProject Class Tester novelWriter NWProject Class Tester
==================================== ====================================
@@ -35,8 +34,9 @@ from nw.enum import nwItemClass, nwItemType, nwItemLayout
from nw.common import formatTimeStamp from nw.common import formatTimeStamp
from nw.constants import nwFiles from nw.constants import nwFiles
@pytest.mark.core @pytest.mark.core
def testCoreProject_NewMinimal(fncDir, outDir, refDir, tmpDir, dummyGUI): def testCoreProject_NewMinimal(fncDir, outDir, refDir, dummyGUI):
"""Create a new project from a project wizard dictionary. With """Create a new project from a project wizard dictionary. With
default setting, creating a Minimal project. default setting, creating a Minimal project.
""" """
@@ -83,6 +83,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, tmpDir, dummyGUI):
# END Test testCoreProject_NewMinimal # END Test testCoreProject_NewMinimal
@pytest.mark.core @pytest.mark.core
def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI): def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
"""Create a new project from a project wizard dictionary. """Create a new project from a project wizard dictionary.
@@ -124,6 +125,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
# END Test testCoreProject_NewCustomA # END Test testCoreProject_NewCustomA
@pytest.mark.core @pytest.mark.core
def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI): def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
"""Create a new project from a project wizard dictionary. """Create a new project from a project wizard dictionary.
@@ -165,6 +167,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
# END Test testCoreProject_NewCustomB # END Test testCoreProject_NewCustomB
@pytest.mark.core @pytest.mark.core
def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir): def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir):
"""Check that we can create a new project can be created from the """Check that we can create a new project can be created from the
@@ -213,6 +216,7 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir):
# END Test testCoreProject_NewSampleA # END Test testCoreProject_NewSampleA
@pytest.mark.core @pytest.mark.core
def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir): def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir):
"""Check that we can create a new project can be created from the """Check that we can create a new project can be created from the
@@ -250,6 +254,7 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir):
# END Test testCoreProject_NewSampleB # END Test testCoreProject_NewSampleB
@pytest.mark.core @pytest.mark.core
def testCoreProject_NewRoot(fncDir, outDir, refDir, dummyGUI): def testCoreProject_NewRoot(fncDir, outDir, refDir, dummyGUI):
"""Check that new root folders can be added to the project. """Check that new root folders can be added to the project.
@@ -286,6 +291,7 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, dummyGUI):
# END Test testCoreProject_NewRoot # END Test testCoreProject_NewRoot
@pytest.mark.core @pytest.mark.core
def testCoreProject_NewFile(fncDir, outDir, refDir, dummyGUI): def testCoreProject_NewFile(fncDir, outDir, refDir, dummyGUI):
"""Check that new files can be added to the project. """Check that new files can be added to the project.
@@ -315,6 +321,7 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, dummyGUI):
# END Test testCoreProject_NewFile # END Test testCoreProject_NewFile
@pytest.mark.core @pytest.mark.core
def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI): def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
"""Test opening a project. """Test opening a project.
@@ -436,6 +443,7 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
# END Test testCoreProject_Open # END Test testCoreProject_Open
@pytest.mark.core @pytest.mark.core
def testCoreProject_Save(monkeypatch, nwMinimal, dummyGUI, refDir): def testCoreProject_Save(monkeypatch, nwMinimal, dummyGUI, refDir):
"""Test saving a project. """Test saving a project.
@@ -481,6 +489,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, dummyGUI, refDir):
# END Test testCoreProject_Save # END Test testCoreProject_Save
@pytest.mark.core @pytest.mark.core
def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI): def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI):
"""Test lock file functions for the project folder. """Test lock file functions for the project folder.
@@ -540,6 +549,7 @@ def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI):
# END Test testCoreProject_LockFile # END Test testCoreProject_LockFile
@pytest.mark.core @pytest.mark.core
def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI): def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI):
"""Test helper functions for the project folder. """Test helper functions for the project folder.
@@ -554,7 +564,7 @@ def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI):
# Block user's home folder # Block user's home folder
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("os.path.expanduser", lambda *args, **kwargs: fncDir) mp.setattr("os.path.expanduser", lambda *a, **k: fncDir)
assert theProject.ensureFolderStructure() is False assert theProject.ensureFolderStructure() is False
# Create a file to block meta folder # Create a file to block meta folder
@@ -583,6 +593,7 @@ def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI):
# END Test testCoreProject_Helpers # END Test testCoreProject_Helpers
@pytest.mark.core @pytest.mark.core
def testCoreProject_AccessItems(nwMinimal, dummyGUI): def testCoreProject_AccessItems(nwMinimal, dummyGUI):
"""Test helper functions for the project folder. """Test helper functions for the project folder.
@@ -592,24 +603,24 @@ def testCoreProject_AccessItems(nwMinimal, dummyGUI):
# Move Novel ROOT to after its files # Move Novel ROOT to after its files
oldOrder = [ oldOrder = [
"a508bb932959c", # ROOT: Novel "a508bb932959c", # ROOT: Novel
"a35baf2e93843", # FILE: Title Page "a35baf2e93843", # FILE: Title Page
"a6d311a93600a", # FOLDER: New Chapter "a6d311a93600a", # FOLDER: New Chapter
"f5ab3e30151e1", # FILE: New Chapter "f5ab3e30151e1", # FILE: New Chapter
"8c659a11cd429", # FILE: New Scene "8c659a11cd429", # FILE: New Scene
"7695ce551d265", # ROOT: Plot "7695ce551d265", # ROOT: Plot
"afb3043c7b2b3", # ROOT: Characters "afb3043c7b2b3", # ROOT: Characters
"9d5247ab588e0", # ROOT: World "9d5247ab588e0", # ROOT: World
] ]
newOrder = [ newOrder = [
"a35baf2e93843", # FILE: Title Page "a35baf2e93843", # FILE: Title Page
"f5ab3e30151e1", # FILE: New Chapter "f5ab3e30151e1", # FILE: New Chapter
"8c659a11cd429", # FILE: New Scene "8c659a11cd429", # FILE: New Scene
"a6d311a93600a", # FOLDER: New Chapter "a6d311a93600a", # FOLDER: New Chapter
"a508bb932959c", # ROOT: Novel "a508bb932959c", # ROOT: Novel
"7695ce551d265", # ROOT: Plot "7695ce551d265", # ROOT: Plot
"afb3043c7b2b3", # ROOT: Characters "afb3043c7b2b3", # ROOT: Characters
"9d5247ab588e0", # ROOT: World "9d5247ab588e0", # ROOT: World
] ]
assert theProject.projTree.handles() == oldOrder assert theProject.projTree.handles() == oldOrder
assert theProject.setTreeOrder(newOrder) assert theProject.setTreeOrder(newOrder)
@@ -628,20 +639,21 @@ def testCoreProject_AccessItems(nwMinimal, dummyGUI):
retOrder.append(tItem.itemHandle) retOrder.append(tItem.itemHandle)
assert retOrder == [ assert retOrder == [
"a508bb932959c", # ROOT: Novel "a508bb932959c", # ROOT: Novel
"7695ce551d265", # ROOT: Plot "7695ce551d265", # ROOT: Plot
"afb3043c7b2b3", # ROOT: Characters "afb3043c7b2b3", # ROOT: Characters
"9d5247ab588e0", # ROOT: World "9d5247ab588e0", # ROOT: World
nHandle, # FILE: Test File nHandle, # FILE: Test File
"a35baf2e93843", # FILE: Title Page "a35baf2e93843", # FILE: Title Page
"a6d311a93600a", # FOLDER: New Chapter "a6d311a93600a", # FOLDER: New Chapter
"f5ab3e30151e1", # FILE: New Chapter "f5ab3e30151e1", # FILE: New Chapter
"8c659a11cd429", # FILE: New Scene "8c659a11cd429", # FILE: New Scene
] ]
assert theProject.projTree[nHandle].itemParent is None assert theProject.projTree[nHandle].itemParent is None
# END Test testCoreProject_AccessItems # END Test testCoreProject_AccessItems
@pytest.mark.core @pytest.mark.core
def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
"""Test other project class methods and functions. """Test other project class methods and functions.
@@ -789,10 +801,10 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
theProject.projTree["8c659a11cd429"].setStatus("Finished") theProject.projTree["8c659a11cd429"].setStatus("Finished")
newList = [ newList = [
("New", 1, 1, 1, "New"), ("New", 1, 1, 1, "New"),
("Draft", 2, 2, 2, "Note"), # These are swapped ("Draft", 2, 2, 2, "Note"), # These are swapped
("Note", 3, 3, 3, "Draft"), # These are swapped ("Note", 3, 3, 3, "Draft"), # These are swapped
("Edited", 4, 4, 4, "Finished"), # Renamed ("Edited", 4, 4, 4, "Finished"), # Renamed
("Finished", 5, 5, 5, None), # New, with reused name ("Finished", 5, 5, 5, None), # New, with reused name
] ]
assert theProject.setStatusColours(newList) assert theProject.setStatusColours(newList)
assert theProject.statusItems._theLabels == [ assert theProject.statusItems._theLabels == [
@@ -801,10 +813,10 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
assert theProject.statusItems._theColours == [ assert theProject.statusItems._theColours == [
(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5) (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5)
] ]
assert theProject.projTree["a35baf2e93843"].itemStatus == "Edited" # Renamed assert theProject.projTree["a35baf2e93843"].itemStatus == "Edited" # Renamed
assert theProject.projTree["a6d311a93600a"].itemStatus == "Note" # Swapped assert theProject.projTree["a6d311a93600a"].itemStatus == "Note" # Swapped
assert theProject.projTree["f5ab3e30151e1"].itemStatus == "Draft" # Swapped assert theProject.projTree["f5ab3e30151e1"].itemStatus == "Draft" # Swapped
assert theProject.projTree["8c659a11cd429"].itemStatus == "Edited" # Renamed assert theProject.projTree["8c659a11cd429"].itemStatus == "Edited" # Renamed
# Change importance # Change importance
fHandle = theProject.newFile("Jane Doe", nwItemClass.CHARACTER, "afb3043c7b2b3") fHandle = theProject.newFile("Jane Doe", nwItemClass.CHARACTER, "afb3043c7b2b3")
@@ -839,7 +851,7 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
# Session stats # Session stats
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("os.path.isdir", lambda *args, **kwargs: False) mp.setattr("os.path.isdir", lambda *a, **k: False)
assert not theProject._appendSessionStats(idleTime=0) assert not theProject._appendSessionStats(idleTime=0)
# Block open # Block open
@@ -892,6 +904,7 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
# END Test testCoreProject_Methods # END Test testCoreProject_Methods
@pytest.mark.core @pytest.mark.core
def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum): def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
"""Check that files in the content folder that are not tracked in """Check that files in the content folder that are not tracked in
@@ -968,6 +981,7 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
# END Test testCoreProject_OrphanedFiles # END Test testCoreProject_OrphanedFiles
@pytest.mark.core @pytest.mark.core
def testCoreProject_OldFormat(dummyGUI, nwOldProj): def testCoreProject_OldFormat(dummyGUI, nwOldProj):
"""Test that a project folder structure of version 1.0 can be """Test that a project folder structure of version 1.0 can be
@@ -1057,6 +1071,7 @@ def testCoreProject_OldFormat(dummyGUI, nwOldProj):
# END Test testCoreProject_OldFormat # END Test testCoreProject_OldFormat
@pytest.mark.core @pytest.mark.core
def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir): def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir):
"""Test the functins that handle legacy data folders and structure """Test the functins that handle legacy data folders and structure
@@ -1162,6 +1177,7 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir):
# END Test testCoreProject_LegacyData # END Test testCoreProject_LegacyData
@pytest.mark.core @pytest.mark.core
def testCoreProject_Backup(monkeypatch, dummyGUI, nwMinimal, tmpDir): def testCoreProject_Backup(monkeypatch, dummyGUI, nwMinimal, tmpDir):
"""Test the automated backup feature of the project class. The test """Test the automated backup feature of the project class. The test
+3 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Spell Check Classes Tester novelWriter Spell Check Classes Tester
======================================== ========================================
@@ -29,6 +28,7 @@ from tools import readFile, writeFile
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
@pytest.mark.core @pytest.mark.core
def testCoreSpell_Super(monkeypatch, tmpDir): def testCoreSpell_Super(monkeypatch, tmpDir):
"""Test the spell checker super class """Test the spell checker super class
@@ -70,6 +70,7 @@ def testCoreSpell_Super(monkeypatch, tmpDir):
# END Test testCoreSpell_Super # END Test testCoreSpell_Super
@pytest.mark.core @pytest.mark.core
def testCoreSpell_Enchant(monkeypatch, tmpDir): def testCoreSpell_Enchant(monkeypatch, tmpDir):
"""Test the pyenchant spell checker """Test the pyenchant spell checker
@@ -114,6 +115,7 @@ def testCoreSpell_Enchant(monkeypatch, tmpDir):
# END Test testCoreSpell_Enchant # END Test testCoreSpell_Enchant
@pytest.mark.core @pytest.mark.core
def testCoreSpell_Simple(monkeypatch, tmpDir): def testCoreSpell_Simple(monkeypatch, tmpDir):
"""Test the fallback simple spell checker """Test the fallback simple spell checker
+2 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter NWStatus Class Tester novelWriter NWStatus Class Tester
=================================== ===================================
@@ -26,6 +25,7 @@ from lxml import etree
from nw.core.status import NWStatus from nw.core.status import NWStatus
@pytest.mark.core @pytest.mark.core
def testCoreStatus_Entries(): def testCoreStatus_Entries():
"""Test all the simple setters for the NWItem class. """Test all the simple setters for the NWItem class.
@@ -100,6 +100,7 @@ def testCoreStatus_Entries():
# END Test testCoreStatus_Entries # END Test testCoreStatus_Entries
@pytest.mark.core @pytest.mark.core
def testCoreStatus_XMLPackUnpack(): def testCoreStatus_XMLPackUnpack():
"""Test all the simple setters for the NWItem class. """Test all the simple setters for the NWItem class.
+5 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter ToHtml Class Tester novelWriter ToHtml Class Tester
================================= =================================
@@ -27,6 +26,7 @@ from tools import readFile
from nw.core import NWProject, NWIndex, ToHtml from nw.core import NWProject, NWIndex, ToHtml
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_Format(dummyGUI): def testCoreToHtml_Format(dummyGUI):
"""Test all the formatters for the ToHtml class. """Test all the formatters for the ToHtml class.
@@ -79,6 +79,7 @@ def testCoreToHtml_Format(dummyGUI):
# END Test testCoreToHtml_Format # END Test testCoreToHtml_Format
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_Convert(dummyGUI): def testCoreToHtml_Convert(dummyGUI):
"""Test the converter of the ToHtml class. """Test the converter of the ToHtml class.
@@ -344,6 +345,8 @@ def testCoreToHtml_Convert(dummyGUI):
# END Test testCoreToHtml_Convert # END Test testCoreToHtml_Convert
@pytest.mark.core
def testCoreToHtml_Complex(dummyGUI, fncDir): def testCoreToHtml_Complex(dummyGUI, fncDir):
"""Test the ave method of the ToHtml class. """Test the ave method of the ToHtml class.
""" """
@@ -416,6 +419,7 @@ def testCoreToHtml_Complex(dummyGUI, fncDir):
# END Test testCoreToHtml_Save # END Test testCoreToHtml_Save
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_Methods(dummyGUI): def testCoreToHtml_Methods(dummyGUI):
"""Test all the other methods of the ToHtml class. """Test all the other methods of the ToHtml class.
+4 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Tokenizer Class Tester novelWriter Tokenizer Class Tester
==================================== ====================================
@@ -28,6 +27,7 @@ from tools import readFile
from nw.core import NWProject, NWDoc from nw.core import NWProject, NWDoc
from nw.core.tokenizer import Tokenizer from nw.core.tokenizer import Tokenizer
@pytest.mark.core @pytest.mark.core
def testCoreToken_Setters(dummyGUI): def testCoreToken_Setters(dummyGUI):
"""Test all the setters for the Tokenizer class. """Test all the setters for the Tokenizer class.
@@ -111,6 +111,7 @@ def testCoreToken_Setters(dummyGUI):
# END Test testCoreToken_Setters # END Test testCoreToken_Setters
@pytest.mark.core @pytest.mark.core
def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI): def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
"""Test handling files and text in the Tokenizer class. """Test handling files and text in the Tokenizer class.
@@ -193,6 +194,7 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
# END Test testCoreToken_TextOps # END Test testCoreToken_TextOps
@pytest.mark.core @pytest.mark.core
def testCoreToken_Tokenize(dummyGUI): def testCoreToken_Tokenize(dummyGUI):
"""Test the tokenization of the Tokenizer class. """Test the tokenization of the Tokenizer class.
@@ -494,6 +496,7 @@ def testCoreToken_Tokenize(dummyGUI):
# END Test testCoreToken_Tokenize # END Test testCoreToken_Tokenize
@pytest.mark.core @pytest.mark.core
def testCoreToken_Headers(dummyGUI): def testCoreToken_Headers(dummyGUI):
"""Test the header and page parser of the Tokenizer class. """Test the header and page parser of the Tokenizer class.
+2 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter ToOdt Class Tester novelWriter ToOdt Class Tester
================================= =================================
@@ -35,6 +34,7 @@ XML_NS = [
' xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"', ' xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"',
] ]
def xmlToText(xElem): def xmlToText(xElem):
"""Get the text content of an XML element. """Get the text content of an XML element.
""" """
@@ -43,6 +43,7 @@ def xmlToText(xElem):
rTxt = rTxt.replace(nSpace, "") rTxt = rTxt.replace(nSpace, "")
return rTxt return rTxt
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_Convert(dummyGUI): def testCoreToOdt_Convert(dummyGUI):
"""Test the converter of the ToHtml class. """Test the converter of the ToHtml class.
+12 -4
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter NWTree Class Tester novelWriter NWTree Class Tester
================================= =================================
@@ -30,6 +29,7 @@ from nw.core.project import NWProject, NWItem, NWTree
from nw.enum import nwItemClass, nwItemType, nwItemLayout from nw.enum import nwItemClass, nwItemType, nwItemLayout
from nw.constants import nwFiles from nw.constants import nwFiles
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def dummyItems(dummyGUI): def dummyItems(dummyGUI):
"""Create a list of mock items. """Create a list of mock items.
@@ -106,6 +106,7 @@ def dummyItems(dummyGUI):
return theItems return theItems
@pytest.mark.core @pytest.mark.core
def testCoreTree_BuildTree(dummyGUI, dummyItems): def testCoreTree_BuildTree(dummyGUI, dummyItems):
"""Test building a project tree from a list of items. """Test building a project tree from a list of items.
@@ -202,6 +203,7 @@ def testCoreTree_BuildTree(dummyGUI, dummyItems):
# END Test testCoreTree_BuildTree # END Test testCoreTree_BuildTree
@pytest.mark.core @pytest.mark.core
def testCoreTree_Methods(dummyGUI, dummyItems): def testCoreTree_Methods(dummyGUI, dummyItems):
"""Test bvarious class methods. """Test bvarious class methods.
@@ -258,6 +260,7 @@ def testCoreTree_Methods(dummyGUI, dummyItems):
# END Test testCoreTree_Methods # END Test testCoreTree_Methods
@pytest.mark.core @pytest.mark.core
def testCoreTree_UpdateItemLayout(dummyGUI, dummyItems): def testCoreTree_UpdateItemLayout(dummyGUI, dummyItems):
"""Test building a project tree from a list of items. """Test building a project tree from a list of items.
@@ -271,9 +274,9 @@ def testCoreTree_UpdateItemLayout(dummyGUI, dummyItems):
assert len(theTree) == len(dummyItems) assert len(theTree) == len(dummyItems)
# Check rejected items # Check rejected items
assert not theTree.updateItemLayout("0000000000000", "H1") # Non-existent handle assert not theTree.updateItemLayout("0000000000000", "H1") # Non-existent handle
assert not theTree.updateItemLayout("a000000000004", "H2") # Character file assert not theTree.updateItemLayout("a000000000004", "H2") # Character file
assert not theTree.updateItemLayout("c000000000002", "H0") # Wrong header level assert not theTree.updateItemLayout("c000000000002", "H0") # Wrong header level
cHandle = "c000000000002" cHandle = "c000000000002"
@@ -383,6 +386,7 @@ def testCoreTree_UpdateItemLayout(dummyGUI, dummyItems):
# END Test testCoreTree_UpdateItemLayout # END Test testCoreTree_UpdateItemLayout
@pytest.mark.core @pytest.mark.core
def testCoreTree_MakeHandles(monkeypatch, dummyGUI): def testCoreTree_MakeHandles(monkeypatch, dummyGUI):
"""Test generating item handles. """Test generating item handles.
@@ -425,6 +429,7 @@ def testCoreTree_MakeHandles(monkeypatch, dummyGUI):
# END Test testCoreTree_MakeHandles # END Test testCoreTree_MakeHandles
@pytest.mark.core @pytest.mark.core
def testCoreTree_Stats(dummyGUI, dummyItems): def testCoreTree_Stats(dummyGUI, dummyItems):
"""Test project stats methods. """Test project stats methods.
@@ -451,6 +456,7 @@ def testCoreTree_Stats(dummyGUI, dummyItems):
# END Test testCoreTree_Stats # END Test testCoreTree_Stats
@pytest.mark.core @pytest.mark.core
def testCoreTree_Reorder(dummyGUI, dummyItems): def testCoreTree_Reorder(dummyGUI, dummyItems):
"""Test changing tree order. """Test changing tree order.
@@ -482,6 +488,7 @@ def testCoreTree_Reorder(dummyGUI, dummyItems):
# END Test testCoreTree_Reorder # END Test testCoreTree_Reorder
@pytest.mark.core @pytest.mark.core
def testCoreTree_XMLPackUnpack(dummyGUI, dummyItems): def testCoreTree_XMLPackUnpack(dummyGUI, dummyItems):
"""Test packing and unpacking the tree to and from XML. """Test packing and unpacking the tree to and from XML.
@@ -537,6 +544,7 @@ def testCoreTree_XMLPackUnpack(dummyGUI, dummyItems):
# END Test testCoreTree_XMLPackUnpack # END Test testCoreTree_XMLPackUnpack
@pytest.mark.core @pytest.mark.core
def testCoreTree_ToCFile(monkeypatch, dummyGUI, dummyItems, tmpDir): def testCoreTree_ToCFile(monkeypatch, dummyGUI, dummyItems, tmpDir):
"""Test writing the ToC.txt file. """Test writing the ToC.txt file.
+3 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter About Dialog Class Tester novelWriter About Dialog Class Tester
======================================= =======================================
@@ -32,12 +31,13 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgAbout_Dialog(qtbot, monkeypatch, nwGUI): def testDlgAbout_Dialog(qtbot, monkeypatch, nwGUI):
"""Test the full about dialogs. """Test the full about dialogs.
""" """
# NW About # NW About
monkeypatch.setattr(GuiAbout, "exec_", lambda *args: None) monkeypatch.setattr(GuiAbout, "exec_", lambda *a: None)
nwGUI.mainMenu.aAboutNW.activate(QAction.Trigger) nwGUI.mainMenu.aAboutNW.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000)
@@ -61,7 +61,7 @@ def testDlgAbout_Dialog(qtbot, monkeypatch, nwGUI):
assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes
# Qt About # Qt About
monkeypatch.setattr(QMessageBox, "aboutQt", lambda *args, **kwargs: None) monkeypatch.setattr(QMessageBox, "aboutQt", lambda *a, **k: None)
nwGUI.mainMenu.aAboutQt.activate(QAction.Trigger) nwGUI.mainMenu.aAboutQt.activate(QAction.Trigger)
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
+3 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Other Dialog Classes Tester novelWriter Other Dialog Classes Tester
========================================= =========================================
@@ -31,12 +30,13 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI, nwMinimal): def testDlgOther_QuoteSelect(monkeypatch, nwGUI):
"""Test the quote symbols dialog. """Test the quote symbols dialog.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
nwQuot = GuiQuoteSelect(nwGUI) nwQuot = GuiQuoteSelect(nwGUI)
nwQuot.show() nwQuot.show()
+5 -5
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Item Editor Dialog Class Tester novelWriter Item Editor Dialog Class Tester
============================================= =============================================
@@ -36,8 +35,9 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDir): def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
"""Test the full item editor dialog. """Test the full item editor dialog.
""" """
projFile = os.path.join(fncProj, "nwProject.nwx") projFile = os.path.join(fncProj, "nwProject.nwx")
@@ -45,8 +45,8 @@ def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir,
compFile = os.path.join(refDir, "guiItemEditor_Dialog_nwProject.nwx") compFile = os.path.join(refDir, "guiItemEditor_Dialog_nwProject.nwx")
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create new, save, open project # Create new, save, open project
nwGUI.theProject.projTree.setSeed(42) nwGUI.theProject.projTree.setSeed(42)
@@ -54,7 +54,7 @@ def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir,
assert nwGUI.openDocument("0e17daca5f3e1") assert nwGUI.openDocument("0e17daca5f3e1")
assert nwGUI.treeView.setSelectedHandle("0e17daca5f3e1", doScroll=True) assert nwGUI.treeView.setSelectedHandle("0e17daca5f3e1", doScroll=True)
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *args: None) monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: None)
nwGUI.mainMenu.aEditItem.activate(QAction.Trigger) nwGUI.mainMenu.aEditItem.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiItemEditor") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiItemEditor") is not None, timeout=1000)
+2 -2
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Merge and Split Dialog Classes Tester novelWriter Merge and Split Dialog Classes Tester
=================================================== ===================================================
@@ -36,8 +35,9 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj): def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj):
"""Test the merge documents tool. """Test the merge documents tool.
""" """
# Block message box # Block message box
+7 -7
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Preferences Dialog Class Tester novelWriter Preferences Dialog Class Tester
============================================= =============================================
@@ -40,13 +39,14 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir): def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
"""Test the load project wizard. """Test the load project wizard.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
# Must create a clean config and GUI object as the test-wide # Must create a clean config and GUI object as the test-wide
# nw.CONFIG object is created on import an can be tainted by other tests # nw.CONFIG object is created on import an can be tainted by other tests
@@ -69,8 +69,8 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
assert theConf.confPath == fncDir assert theConf.confPath == fncDir
theConf.spellTool = nwConst.SP_INTERNAL theConf.spellTool = nwConst.SP_INTERNAL
monkeypatch.setattr(GuiPreferences, "exec_", lambda *args: None) monkeypatch.setattr(GuiPreferences, "exec_", lambda *a: None)
monkeypatch.setattr(GuiPreferences, "result", lambda *args: QDialog.Accepted) monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger) nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
@@ -125,9 +125,9 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
# Check Browse button # Check Browse button
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "") monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "")
assert not tabProjects._backupFolder() assert not tabProjects._backupFolder()
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "some/dir") monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "some/dir")
qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton) qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton)
qtbot.wait(keyDelay) qtbot.wait(keyDelay)
+4 -4
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Load Dialog Class Tester novelWriter Project Load Dialog Class Tester
============================================== ==============================================
@@ -37,6 +36,7 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal): def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the load project wizard. """Test the load project wizard.
@@ -48,8 +48,8 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
assert nwGUI.closeProject() assert nwGUI.closeProject()
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *args: None) monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
monkeypatch.setattr(GuiProjectLoad, "result", lambda *args: QDialog.Accepted) monkeypatch.setattr(GuiProjectLoad, "result", lambda *a: QDialog.Accepted)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger) nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
@@ -106,7 +106,7 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1 assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
getFile = os.path.join(nwMinimal, "nwProject.nwx") getFile = os.path.join(nwMinimal, "nwProject.nwx")
monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwargs: (getFile, None)) monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: (getFile, None))
qtbot.mouseClick(nwLoad.browseButton, Qt.LeftButton) qtbot.mouseClick(nwLoad.browseButton, Qt.LeftButton)
assert nwLoad.openPath == nwMinimal assert nwLoad.openPath == nwMinimal
assert nwLoad.openState == nwLoad.OPEN_STATE assert nwLoad.openState == nwLoad.OPEN_STATE
+6 -6
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Settings Dialog Class Tester novelWriter Project Settings Dialog Class Tester
================================================== ==================================================
@@ -38,6 +37,7 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDir, refDir): def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDir, refDir):
"""Test the full project settings dialog. """Test the full project settings dialog.
@@ -47,8 +47,8 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDi
compFile = os.path.join(refDir, "guiProjSettings_Dialog_nwProject.nwx") compFile = os.path.join(refDir, "guiProjSettings_Dialog_nwProject.nwx")
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Check that we cannot open when there is no project # Check that we cannot open when there is no project
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
@@ -64,8 +64,8 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDi
nwGUI.theProject.setAutoReplace({"A": "B", "C": "D"}) nwGUI.theProject.setAutoReplace({"A": "B", "C": "D"})
# Get the dialog object # Get the dialog object
monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *args: None) monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None)
monkeypatch.setattr(GuiProjectSettings, "result", lambda *args: QDialog.Accepted) monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted)
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
@@ -135,7 +135,7 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDi
assert projEdit.tabStatus.listBox.topLevelItemCount() == 3 assert projEdit.tabStatus.listBox.topLevelItemCount() == 3
# Add a new item # Add a new item
monkeypatch.setattr(QColorDialog, "getColor", lambda *args: QColor(20, 30, 40)) monkeypatch.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
qtbot.mouseClick(projEdit.tabStatus.addButton, Qt.LeftButton) qtbot.mouseClick(projEdit.tabStatus.addButton, Qt.LeftButton)
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True) projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
for n in range(8): for n in range(8):
+2 -2
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Merge and Split Dialog Classes Tester novelWriter Merge and Split Dialog Classes Tester
=================================================== ===================================================
@@ -37,8 +36,9 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj): def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj):
"""Test the split document tool. """Test the split document tool.
""" """
# Block message box # Block message box
+10 -8
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Other Dialog Classes Tester novelWriter Other Dialog Classes Tester
========================================= =========================================
@@ -21,6 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
import os import os
import pytest
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog, QMessageBox, QAction from PyQt5.QtWidgets import QDialog, QMessageBox, QAction
@@ -35,14 +35,16 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal, tmpDir):
@pytest.mark.gui
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
"""test the word list editor. """test the word list editor.
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiWordList, "exec_", lambda *args: None) monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None)
monkeypatch.setattr(GuiWordList, "result", lambda *args: QDialog.Accepted) monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(GuiWordList, "accept", lambda *args: None) monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
# Open project # Open project
nwGUI.openProject(nwMinimal) nwGUI.openProject(nwMinimal)
@@ -66,7 +68,7 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal, tmpDir):
"word_a\n" "word_a\n"
"word_c\n" "word_c\n"
"word_g\n" "word_g\n"
" \n" # Should be ignored " \n" # Should be ignored
"word_f\n" "word_f\n"
"word_b\n" "word_b\n"
)) ))
+14 -13
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Main GUI Editor Class Tester novelWriter Main GUI Editor Class Tester
========================================== ==========================================
@@ -20,8 +19,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
import pytest
import os import os
import pytest
from shutil import copyfile from shutil import copyfile
from tools import cmpFiles from tools import cmpFiles
@@ -39,17 +38,18 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir): def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
"""Test the document editor. """Test the document editor.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *args: None) monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: None)
monkeypatch.setattr(GuiItemEditor, "result", lambda *args: QDialog.Accepted) monkeypatch.setattr(GuiItemEditor, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *args: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
# Create new, save, close project # Create new, save, close project
nwGUI.theProject.projTree.setSeed(42) nwGUI.theProject.projTree.setSeed(42)
@@ -315,7 +315,7 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
assert nwGUI.treeView.deleteItem() assert nwGUI.treeView.deleteItem()
assert nwGUI.treeView.setSelectedHandle(newHandle) assert nwGUI.treeView.setSelectedHandle(newHandle)
assert nwGUI.treeView.deleteItem() assert nwGUI.treeView.deleteItem()
assert nwGUI.theProject.projTree["2fca346db6561"] is not None # Trash assert nwGUI.theProject.projTree["2fca346db6561"] is not None # Trash
assert nwGUI.saveProject() assert nwGUI.saveProject()
# Check the files # Check the files
@@ -353,13 +353,14 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
# END Test testGuiEditor_Main # END Test testGuiEditor_Main
@pytest.mark.gui @pytest.mark.gui
def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the document editor search functionality. """Test the document editor search functionality.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *args: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
nwGUI.theProject.projTree.setSeed(42) nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.openProject(nwLipsum) assert nwGUI.openProject(nwLipsum)
@@ -414,7 +415,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
# Set Invalid RegEx # Set Invalid RegEx
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[") assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[")
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
assert nwGUI.docEditor.getCursorPosition() < 3 # No result assert nwGUI.docEditor.getCursorPosition() < 3 # No result
# Set Valid RegEx # Set Valid RegEx
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus") assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus")
@@ -503,7 +504,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
# Next Match # Next Match
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
assert nwGUI.docEditor.docHandle() == "2426c6f0ca922" # Next document assert nwGUI.docEditor.docHandle() == "2426c6f0ca922" # Next document
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
+3 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Main GUI Viewer Class Tester novelWriter Main GUI Viewer Class Tester
========================================== ==========================================
@@ -32,13 +31,14 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the document viewer. """Test the document viewer.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
# Open project # Open project
nwGUI.theProject.projTree.setSeed(42) nwGUI.theProject.projTree.setSeed(42)
+19 -14
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Main GUI Main Menu Class Tester novelWriter Main GUI Main Menu Class Tester
============================================= =============================================
@@ -35,6 +34,7 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the main menu Edit and Format entries. """Test the main menu Edit and Format entries.
@@ -113,44 +113,45 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
# Block Formats # Block Formats
# =============
assert nwGUI.docEditor.setCursorPosition(30) assert nwGUI.docEditor.setCursorPosition(30)
## Header 1 # Header 1
nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger) nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger)
fmtStr = "# Pellentesque nec erat ut nulla posuere commodo." fmtStr = "# Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[27:76] == fmtStr assert nwGUI.docEditor.getText()[27:76] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Header 2 # Header 2
nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger) nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger)
fmtStr = "## Pellentesque nec erat ut nulla posuere commodo." fmtStr = "## Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[27:77] == fmtStr assert nwGUI.docEditor.getText()[27:77] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Header 3 # Header 3
nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger) nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger)
fmtStr = "### Pellentesque nec erat ut nulla posuere commodo." fmtStr = "### Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[27:78] == fmtStr assert nwGUI.docEditor.getText()[27:78] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Header 4 # Header 4
nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger) nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger)
fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo." fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[27:79] == fmtStr assert nwGUI.docEditor.getText()[27:79] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Clear Format # Clear Format
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[27:74] == cleanText assert nwGUI.docEditor.getText()[27:74] == cleanText
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Comment On # Comment On
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
fmtStr = "% Pellentesque nec erat ut nulla posuere commodo." fmtStr = "% Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[27:76] == fmtStr assert nwGUI.docEditor.getText()[27:76] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Comment Off # Comment Off
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[27:74] == cleanText assert nwGUI.docEditor.getText()[27:74] == cleanText
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
@@ -228,41 +229,43 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docEditor.isEmpty() assert nwGUI.docEditor.isEmpty()
# Alignment & Indent # Alignment & Indent
# ==================
cleanText = "A single, short paragraph.\n\n" cleanText = "A single, short paragraph.\n\n"
nwGUI.docEditor.setText(cleanText) nwGUI.docEditor.setText(cleanText)
assert nwGUI.docEditor.setCursorPosition(0) assert nwGUI.docEditor.setCursorPosition(0)
## Left Align # Left Align
nwGUI.mainMenu.aFmtAlignLeft.activate(QAction.Trigger) nwGUI.mainMenu.aFmtAlignLeft.activate(QAction.Trigger)
fmtStr = "A single, short paragraph. <<" fmtStr = "A single, short paragraph. <<"
assert nwGUI.docEditor.getText()[:29] == fmtStr assert nwGUI.docEditor.getText()[:29] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Right Align # Right Align
nwGUI.mainMenu.aFmtAlignRight.activate(QAction.Trigger) nwGUI.mainMenu.aFmtAlignRight.activate(QAction.Trigger)
fmtStr = ">> A single, short paragraph." fmtStr = ">> A single, short paragraph."
assert nwGUI.docEditor.getText()[:29] == fmtStr assert nwGUI.docEditor.getText()[:29] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Centre Align # Centre Align
nwGUI.mainMenu.aFmtAlignCentre.activate(QAction.Trigger) nwGUI.mainMenu.aFmtAlignCentre.activate(QAction.Trigger)
fmtStr = ">> A single, short paragraph. <<" fmtStr = ">> A single, short paragraph. <<"
assert nwGUI.docEditor.getText()[:32] == fmtStr assert nwGUI.docEditor.getText()[:32] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Left Indent # Left Indent
nwGUI.mainMenu.aFmtIndentLeft.activate(QAction.Trigger) nwGUI.mainMenu.aFmtIndentLeft.activate(QAction.Trigger)
fmtStr = "> A single, short paragraph." fmtStr = "> A single, short paragraph."
assert nwGUI.docEditor.getText()[:28] == fmtStr assert nwGUI.docEditor.getText()[:28] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## Right Indent # Right Indent
nwGUI.mainMenu.aFmtIndentRight.activate(QAction.Trigger) nwGUI.mainMenu.aFmtIndentRight.activate(QAction.Trigger)
fmtStr = "> A single, short paragraph. <" fmtStr = "> A single, short paragraph. <"
assert nwGUI.docEditor.getText()[:30] == fmtStr assert nwGUI.docEditor.getText()[:30] == fmtStr
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
## No Format # No Format
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[:30] == cleanText assert nwGUI.docEditor.getText()[:30] == cleanText
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
@@ -368,6 +371,7 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
# END Test testGuiMenu_EditFormat # END Test testGuiMenu_EditFormat
@pytest.mark.gui @pytest.mark.gui
def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the context menus. """Test the context menus.
@@ -452,6 +456,7 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
# END Test testGuiMenu_ContextMenus # END Test testGuiMenu_ContextMenus
@pytest.mark.gui @pytest.mark.gui
def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj): def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
"""Test the Insert menu. """Test the Insert menu.
+4 -4
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Main GUI Novel Tree Class Tester novelWriter Main GUI Novel Tree Class Tester
============================================== ==============================================
@@ -28,13 +27,14 @@ from tools import writeFile
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
@pytest.mark.gui @pytest.mark.gui
def testGuiNovelTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal): def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test navigating the novel tree. """Test navigating the novel tree.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
nwGUI.openProject(nwMinimal) nwGUI.openProject(nwMinimal)
nwGUI.theProject.projTree.setSeed(42) nwGUI.theProject.projTree.setSeed(42)
+3 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Main GUI Outline Class Tester novelWriter Main GUI Outline Class Tester
=========================================== ===========================================
@@ -31,13 +30,14 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the outline view. """Test the outline view.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
assert nwGUI.openProject(nwLipsum) assert nwGUI.openProject(nwLipsum)
nwGUI.mainConf.lastPath = nwLipsum nwGUI.mainConf.lastPath = nwLipsum
+5 -5
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Project Details Dialog Class Tester novelWriter Project Details Dialog Class Tester
================================================= =================================================
@@ -32,15 +31,16 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the project details dialog. """Test the project details dialog.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "warning", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Create a project to work on # Create a project to work on
assert nwGUI.openProject(nwLipsum) assert nwGUI.openProject(nwLipsum)
+16 -16
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Main GUI Project Tree Class Tester novelWriter Main GUI Project Tree Class Tester
================================================ ================================================
@@ -32,16 +31,17 @@ from nw.guimain import GuiMain
from nw.gui.projtree import GuiProjectTree from nw.gui.projtree import GuiProjectTree
from nw.enum import nwItemType, nwItemClass from nw.enum import nwItemType, nwItemClass
@pytest.mark.gui @pytest.mark.gui
def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal): def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
"""Test adding and removing items from the project tree. """Test adding and removing items from the project tree.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "warning", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiMain, "editItem", lambda *args: None) monkeypatch.setattr(GuiMain, "editItem", lambda *a: None)
nwGUI.theProject.projTree.setSeed(42) nwGUI.theProject.projTree.setSeed(42)
nwTree = nwGUI.treeView nwTree = nwGUI.treeView
@@ -82,8 +82,8 @@ def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
] ]
# Add roots # Add roots
assert not nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) # Duplicate assert not nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) # Duplicate
assert nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.CUSTOM) # Valid assert nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.CUSTOM) # Valid
# Change max depth and try to add a subfolder that is too deep # Change max depth and try to add a subfolder that is too deep
monkeypatch.setattr("nw.constants.nwConst.MAX_DEPTH", 2) monkeypatch.setattr("nw.constants.nwConst.MAX_DEPTH", 2)
@@ -98,12 +98,12 @@ def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
nwTree.setSelectedHandle("8c659a11cd429") nwTree.setSelectedHandle("8c659a11cd429")
# Shift focus and try to move item # Shift focus and try to move item
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: False) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
assert not nwTree.moveTreeItem(1) assert not nwTree.moveTreeItem(1)
assert nwTree.getTreeFromHandle("a6d311a93600a") == [ assert nwTree.getTreeFromHandle("a6d311a93600a") == [
"a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "44cb730c42048", "71ee45a3c0db9" "a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "44cb730c42048", "71ee45a3c0db9"
] ]
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Move second item up twice (should give same result) # Move second item up twice (should give same result)
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger)
@@ -168,12 +168,12 @@ def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
# Delete the items we added earlier # Delete the items we added earlier
nwTree.clearSelection() nwTree.clearSelection()
assert not nwTree.emptyTrash() # No folder yet assert not nwTree.emptyTrash() # No folder yet
assert not nwTree.deleteItem(None) assert not nwTree.deleteItem(None)
assert not nwTree.deleteItem("1111111111111") assert not nwTree.deleteItem("1111111111111")
assert nwTree.deleteItem("73475cb40a568") # New File assert nwTree.deleteItem("73475cb40a568") # New File
assert nwTree.deleteItem("71ee45a3c0db9") # New Folder assert nwTree.deleteItem("71ee45a3c0db9") # New Folder
assert nwTree.deleteItem("811786ad1ae74") # Custom Root assert nwTree.deleteItem("811786ad1ae74") # Custom Root
assert "73475cb40a568" in nwGUI.theProject.projTree._treeOrder assert "73475cb40a568" in nwGUI.theProject.projTree._treeOrder
assert "71ee45a3c0db9" not in nwGUI.theProject.projTree._treeOrder assert "71ee45a3c0db9" not in nwGUI.theProject.projTree._treeOrder
assert "811786ad1ae74" not in nwGUI.theProject.projTree._treeOrder assert "811786ad1ae74" not in nwGUI.theProject.projTree._treeOrder
@@ -181,7 +181,7 @@ def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
# The file is in trash, empty it # The file is in trash, empty it
assert os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd")) assert os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd"))
assert nwTree.emptyTrash() assert nwTree.emptyTrash()
assert not nwTree.emptyTrash() # Already empty assert not nwTree.emptyTrash() # Already empty
assert not os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd")) assert not os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd"))
assert "73475cb40a568" not in nwGUI.theProject.projTree._treeOrder assert "73475cb40a568" not in nwGUI.theProject.projTree._treeOrder
@@ -228,7 +228,7 @@ def testGuiProjTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
nwTree.clearSelection() nwTree.clearSelection()
# Add a file with no parent, and fail to find a suitable parent item # Add a file with no parent, and fail to find a suitable parent item
monkeypatch.setattr("nw.core.tree.NWTree.findRoot", lambda *args: None) monkeypatch.setattr("nw.core.tree.NWTree.findRoot", lambda *a: None)
assert not nwTree.newTreeItem(nwItemType.FILE, nwItemClass.NOVEL) assert not nwTree.newTreeItem(nwItemType.FILE, nwItemClass.NOVEL)
assert not nwTree.newTreeItem(nwItemType.FOLDER, nwItemClass.NOVEL) assert not nwTree.newTreeItem(nwItemType.FOLDER, nwItemClass.NOVEL)
+2 -2
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter GUI Theme and Icons Classes Tester novelWriter GUI Theme and Icons Classes Tester
================================================ ================================================
@@ -30,12 +29,13 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir): def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir):
"""Test the theme and icon classes. """Test the theme and icon classes.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]) nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal])
qtbot.addWidget(nwGUI) qtbot.addWidget(nwGUI)
+6 -6
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Build Dialog Class Tester novelWriter Build Dialog Class Tester
======================================= =======================================
@@ -35,14 +34,15 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir): def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
"""Test the build tool. """Test the build tool.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda a, b, c, **kwargs: (c, None)) monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda a, b, c, **k: (c, None))
# Check that we cannot open when there is no project # Check that we cannot open when there is no project
nwGUI.mainMenu.aBuildProject.activate(QAction.Trigger) nwGUI.mainMenu.aBuildProject.activate(QAction.Trigger)
@@ -69,7 +69,7 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# Non-existent path # Non-existent path
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("os.path.expanduser", lambda *args, **kwargs: nwLipsum) mp.setattr("os.path.expanduser", lambda *a, **k: nwLipsum)
assert nwGUI.mainConf.lastPath != nwLipsum assert nwGUI.mainConf.lastPath != nwLipsum
nwGUI.mainConf.lastPath = "no_such_path" nwGUI.mainConf.lastPath = "no_such_path"
assert nwBuild._saveDocument(nwBuild.FMT_NWD) assert nwBuild._saveDocument(nwBuild.FMT_NWD)
@@ -77,7 +77,7 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# No path selected # No path selected
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QFileDialog, "getSaveFileName", lambda *args, **kwargs: ("", "")) mp.setattr(QFileDialog, "getSaveFileName", lambda *a, **k: ("", ""))
assert not nwBuild._saveDocument(nwBuild.FMT_NWD) assert not nwBuild._saveDocument(nwBuild.FMT_NWD)
# Default Settings # Default Settings
+8 -8
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter New Project Wizard Class Tester novelWriter New Project Wizard Class Tester
============================================= =============================================
@@ -39,13 +38,14 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testToolProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal): def testToolProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the new project wizard. """Test the new project wizard.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
if sys.platform.startswith("darwin"): if sys.platform.startswith("darwin"):
# Disable for macOS because the test segfaults on QWizard.show() # Disable for macOS because the test segfaults on QWizard.show()
@@ -62,22 +62,22 @@ def testToolProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
# Close project, but call with invalid path # Close project, but call with invalid path
assert nwGUI.closeProject() assert nwGUI.closeProject()
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: None) mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: None)
assert not nwGUI.newProject() assert not nwGUI.newProject()
# Now, with an empty dictionary # Now, with an empty dictionary
mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: {}) mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {})
assert not nwGUI.newProject() assert not nwGUI.newProject()
# Now, with a non-empty folder # Now, with a non-empty folder
mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal}) mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": nwMinimal})
assert not nwGUI.newProject() assert not nwGUI.newProject()
## ##
# Test the Wizard # Test the Wizard
## ##
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *args: None) monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
nwGUI.mainConf.lastPath = " " nwGUI.mainConf.lastPath = " "
nwGUI.closeProject() nwGUI.closeProject()
@@ -184,7 +184,7 @@ def testToolProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
# Final Page # Final Page
finalPage = nwWiz.currentPage() finalPage = nwWiz.currentPage()
assert isinstance(finalPage, ProjWizardFinalPage) assert isinstance(finalPage, ProjWizardFinalPage)
assert nwWiz.button(QWizard.FinishButton).isEnabled() # But we don't click it assert nwWiz.button(QWizard.FinishButton).isEnabled() # But we don't click it
# Check Data # Check Data
projData = nwGUI._assembleProjectWizardData(nwWiz) projData = nwGUI._assembleProjectWizardData(nwWiz)
+7 -7
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Writing Stats Dialog Class Tester novelWriter Writing Stats Dialog Class Tester
=============================================== ===============================================
@@ -37,15 +36,16 @@ keyDelay = 2
typeDelay = 1 typeDelay = 1
stepDelay = 20 stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj): def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
"""Test the full writing stats tool. """Test the full writing stats tool.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "warning", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Create a project to work on # Create a project to work on
assert nwGUI.newProject({"projPath": fncProj}) assert nwGUI.newProject({"projPath": fncProj})
@@ -116,13 +116,13 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
sessLog.populateGUI() sessLog.populateGUI()
# Make the saving fail # Make the saving fail
monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda *args, **kwargs: ("", "")) monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda *a, **k: ("", ""))
assert not sessLog._saveData(sessLog.FMT_CSV) assert not sessLog._saveData(sessLog.FMT_CSV)
assert not sessLog._saveData(sessLog.FMT_JSON) assert not sessLog._saveData(sessLog.FMT_JSON)
assert not sessLog._saveData(None) assert not sessLog._saveData(None)
# Make the save succeed # Make the save succeed
monkeypatch.setattr("os.path.expanduser", lambda *args: fncDir) monkeypatch.setattr("os.path.expanduser", lambda *a: fncDir)
monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda ss, tt, pp, options: (pp, "")) monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda ss, tt, pp, options: (pp, ""))
sessLog.listBox.sortByColumn(sessLog.C_TIME, 0) sessLog.listBox.sortByColumn(sessLog.C_TIME, 0)
+5 -1
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" """
novelWriter Test Suite Tools novelWriter Test Suite Tools
============================== ==============================
@@ -25,6 +24,7 @@ import shutil
from PyQt5.QtWidgets import qApp from PyQt5.QtWidgets import qApp
def cmpFiles(fileOne, fileTwo, ignoreLines=None): def cmpFiles(fileOne, fileTwo, ignoreLines=None):
"""Compare two files, but optionally ignore lines given by a list. """Compare two files, but optionally ignore lines given by a list.
""" """
@@ -70,6 +70,7 @@ def cmpFiles(fileOne, fileTwo, ignoreLines=None):
return not diffFound return not diffFound
def getGuiItem(theName): def getGuiItem(theName):
"""Returns a QtWidget based on its objectName. """Returns a QtWidget based on its objectName.
""" """
@@ -78,18 +79,21 @@ def getGuiItem(theName):
return qWidget return qWidget
return None return None
def readFile(fileName): def readFile(fileName):
"""Returns the content of a file as a string. """Returns the content of a file as a string.
""" """
with open(fileName, mode="r", encoding="utf8") as inFile: with open(fileName, mode="r", encoding="utf8") as inFile:
return inFile.read() return inFile.read()
def writeFile(fileName, fileData): def writeFile(fileName, fileData):
"""Write the contents of a string to a file. """Write the contents of a string to a file.
""" """
with open(fileName, mode="w", encoding="utf8") as outFile: with open(fileName, mode="w", encoding="utf8") as outFile:
outFile.write(fileData) outFile.write(fileData)
def cleanProject(projPath): def cleanProject(projPath):
"""Delete all generated files in a project. """Delete all generated files in a project.
""" """