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