Add more linting rules (#2287)
This commit is contained in:
@@ -23,11 +23,15 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: pip install -r requirements.txt -r requirements-dev.txt
|
run: pip install -r requirements.txt -r requirements-dev.txt
|
||||||
- name: Run Ruff
|
- name: Ruff Check
|
||||||
run: |
|
run: |
|
||||||
ruff --version
|
ruff --version
|
||||||
ruff check
|
ruff check
|
||||||
- name: Run Pyright
|
- name: Pyright Check
|
||||||
run: |
|
run: |
|
||||||
pyright --version
|
pyright --version
|
||||||
pyright
|
pyright
|
||||||
|
- name: Isort Check
|
||||||
|
run: |
|
||||||
|
isort --version
|
||||||
|
isort --check .
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@ initFile = os.path.join(
|
|||||||
os.path.dirname(__file__), os.pardir, os.pardir,
|
os.path.dirname(__file__), os.pardir, os.pardir,
|
||||||
"novelwriter", "__init__.py"
|
"novelwriter", "__init__.py"
|
||||||
)
|
)
|
||||||
with open(initFile) as inFile:
|
with open(initFile, encoding="utf-8") as inFile:
|
||||||
for aLine in inFile:
|
for aLine in inFile:
|
||||||
if aLine.startswith("__version__"):
|
if aLine.startswith("__version__"):
|
||||||
release = aLine.split('"')[1].strip()
|
release = aLine.split('"')[1].strip()
|
||||||
|
|||||||
+2
-2
@@ -7,8 +7,8 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import PyQt6.QtCore # noqa: F401
|
import PyQt6.QtCore
|
||||||
import PyQt6.QtGui # noqa: F401
|
import PyQt6.QtGui
|
||||||
import PyQt6.QtWidgets # noqa: F401
|
import PyQt6.QtWidgets # noqa: F401
|
||||||
except Exception:
|
except Exception:
|
||||||
print("ERROR: Failed to load dependency PyQt6")
|
print("ERROR: Failed to load dependency PyQt6")
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from novelwriter.config import Config
|
|||||||
from novelwriter.error import exceptionHandler
|
from novelwriter.error import exceptionHandler
|
||||||
from novelwriter.shared import SharedData
|
from novelwriter.shared import SharedData
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.guimain import GuiMain
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
# Package Meta
|
# Package Meta
|
||||||
@@ -148,7 +148,7 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
inOpts, inRemain = getopt.getopt(sysArgs, shortOpt, longOpt)
|
inOpts, inRemain = getopt.getopt(sysArgs, shortOpt, longOpt)
|
||||||
except getopt.GetoptError as exc:
|
except getopt.GetoptError as exc:
|
||||||
print(helpMsg)
|
print(helpMsg)
|
||||||
print(f"ERROR: {str(exc)}")
|
print(f"ERROR: {exc!s}")
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
if len(inRemain) > 0:
|
if len(inRemain) > 0:
|
||||||
|
|||||||
+11
-9
@@ -29,11 +29,10 @@ import unicodedata
|
|||||||
import uuid
|
import uuid
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from collections.abc import Callable
|
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Literal, TypeGuard, TypeVar
|
from typing import TYPE_CHECKING, Any, Literal, TypeGuard, TypeVar
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
from urllib.request import pathname2url
|
from urllib.request import pathname2url
|
||||||
|
|
||||||
@@ -45,6 +44,9 @@ from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
|
|||||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_Type = TypeVar("_Type")
|
_Type = TypeVar("_Type")
|
||||||
@@ -347,7 +349,7 @@ def fuzzyTime(seconds: int) -> str:
|
|||||||
elif seconds < 3300: # 55 minutes
|
elif seconds < 3300: # 55 minutes
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} minutes ago"
|
"Common", "{0} minutes ago"
|
||||||
).format(int(round(seconds/60)))
|
).format(round(seconds/60))
|
||||||
elif seconds < 5400: # 90 minutes
|
elif seconds < 5400: # 90 minutes
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "an hour ago"
|
"Common", "an hour ago"
|
||||||
@@ -355,7 +357,7 @@ def fuzzyTime(seconds: int) -> str:
|
|||||||
elif seconds < 84600: # 23.5 hours
|
elif seconds < 84600: # 23.5 hours
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} hours ago"
|
"Common", "{0} hours ago"
|
||||||
).format(int(round(seconds/3600)))
|
).format(round(seconds/3600))
|
||||||
elif seconds < 129600: # 1.5 days
|
elif seconds < 129600: # 1.5 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a day ago"
|
"Common", "a day ago"
|
||||||
@@ -363,7 +365,7 @@ def fuzzyTime(seconds: int) -> str:
|
|||||||
elif seconds < 561600: # 6.5 days
|
elif seconds < 561600: # 6.5 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} days ago"
|
"Common", "{0} days ago"
|
||||||
).format(int(round(seconds/86400)))
|
).format(round(seconds/86400))
|
||||||
elif seconds < 907200: # 10.5 days
|
elif seconds < 907200: # 10.5 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a week ago"
|
"Common", "a week ago"
|
||||||
@@ -371,7 +373,7 @@ def fuzzyTime(seconds: int) -> str:
|
|||||||
elif seconds < 2419200: # 28 days
|
elif seconds < 2419200: # 28 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} weeks ago"
|
"Common", "{0} weeks ago"
|
||||||
).format(int(round(seconds/604800)))
|
).format(round(seconds/604800))
|
||||||
elif seconds < 3888000: # 45 days
|
elif seconds < 3888000: # 45 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a month ago"
|
"Common", "a month ago"
|
||||||
@@ -379,7 +381,7 @@ def fuzzyTime(seconds: int) -> str:
|
|||||||
elif seconds < 29808000: # 345 days
|
elif seconds < 29808000: # 345 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} months ago"
|
"Common", "{0} months ago"
|
||||||
).format(int(round(seconds/2592000)))
|
).format(round(seconds/2592000))
|
||||||
elif seconds < 47336400: # 1.5 years
|
elif seconds < 47336400: # 1.5 years
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a year ago"
|
"Common", "a year ago"
|
||||||
@@ -387,7 +389,7 @@ def fuzzyTime(seconds: int) -> str:
|
|||||||
else:
|
else:
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} years ago"
|
"Common", "{0} years ago"
|
||||||
).format(int(round(seconds/31557600)))
|
).format(round(seconds/31557600))
|
||||||
|
|
||||||
|
|
||||||
def numberToRoman(value: int, toLower: bool = False) -> str:
|
def numberToRoman(value: int, toLower: bool = False) -> str:
|
||||||
@@ -423,7 +425,7 @@ def describeFont(font: QFont) -> str:
|
|||||||
info = QFontInfo(font)
|
info = QFontInfo(font)
|
||||||
family = info.family()
|
family = info.family()
|
||||||
styles = [v for v in info.styleName().split() if v not in family]
|
styles = [v for v in info.styleName().split() if v not in family]
|
||||||
return " ".join([f"{info.pointSize()} pt", family] + styles)
|
return " ".join([f"{info.pointSize()} pt", family, *styles])
|
||||||
return "Error"
|
return "Error"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+30
-27
@@ -29,10 +29,9 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import time
|
from time import time
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Final
|
||||||
|
|
||||||
from PyQt6.QtCore import (
|
from PyQt6.QtCore import (
|
||||||
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
|
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
|
||||||
@@ -48,7 +47,9 @@ from novelwriter.common import (
|
|||||||
from novelwriter.constants import nwFiles, nwUnicode
|
from novelwriter.constants import nwFiles, nwUnicode
|
||||||
from novelwriter.error import formatException, logException
|
from novelwriter.error import formatException, logException
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from novelwriter.core.projectdata import NWProjectData
|
from novelwriter.core.projectdata import NWProjectData
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -62,29 +63,27 @@ DEF_TREECOL = "theme"
|
|||||||
class Config:
|
class Config:
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_confPath", "_dataPath", "_homePath", "_backPath", "_appPath", "_appRoot", "_hasError",
|
"_appPath", "_appRoot", "_backPath", "_backupPath", "_confPath", "_dLocale", "_dShortDate",
|
||||||
"_errData", "_nwLangPath", "_qtLangPath", "_qLocale", "_dLocale", "_dShortDate",
|
"_dShortDateTime", "_dataPath", "_errData", "_hasError", "_homePath", "_manuals",
|
||||||
"_dShortDateTime", "_qtTrans", "_manuals", "_recentProjects", "_recentPaths",
|
"_nwLangPath", "_qLocale", "_qtLangPath", "_qtTrans", "_recentPaths", "_recentProjects",
|
||||||
"_backupPath",
|
"allowOpenDial", "altDialogClose", "altDialogOpen", "appHandle", "appName",
|
||||||
|
"askBeforeBackup", "askBeforeExit", "autoSaveDoc", "autoSaveProj", "autoScroll",
|
||||||
"appName", "appHandle", "guiLocale", "guiTheme", "guiSyntax", "guiFont", "hideVScroll",
|
"autoScrollPos", "autoSelect", "backupOnClose", "cursorWidth", "dialogLine", "dialogStyle",
|
||||||
"hideHScroll", "lastNotes", "nativeFont", "useCharCount", "iconTheme", "iconColTree",
|
"doJustify", "doReplace", "doReplaceDQuote", "doReplaceDash", "doReplaceDots",
|
||||||
"iconColDocs", "mainWinSize", "welcomeWinSize", "prefsWinSize", "mainPanePos",
|
"doReplaceSQuote", "emphLabels", "fmtApostrophe", "fmtDQuoteClose", "fmtDQuoteOpen",
|
||||||
"viewPanePos", "outlinePanePos", "autoSaveProj", "autoSaveDoc", "emphLabels",
|
"fmtPadAfter", "fmtPadBefore", "fmtPadThin", "fmtSQuoteClose", "fmtSQuoteOpen",
|
||||||
"backupOnClose", "askBeforeBackup", "askBeforeExit", "textFont", "textWidth", "textMargin",
|
"focusWidth", "guiFont", "guiLocale", "guiSyntax", "guiTheme", "hasEnchant",
|
||||||
"tabWidth", "cursorWidth", "focusWidth", "hideFocusFooter", "showFullPath", "autoSelect",
|
"hideFocusFooter", "hideHScroll", "hideVScroll", "highlightEmph", "hostName",
|
||||||
"doJustify", "showTabsNSpaces", "showLineEndings", "showMultiSpaces", "doReplace",
|
"iconColDocs", "iconColTree", "iconTheme", "incNotesWCount", "isDebug", "kernelVer",
|
||||||
"doReplaceSQuote", "doReplaceDQuote", "doReplaceDash", "doReplaceDots", "autoScroll",
|
"lastNotes", "mainPanePos", "mainWinSize", "memInfo", "narratorBreak", "narratorDialog",
|
||||||
"autoScrollPos", "scrollPastEnd", "dialogStyle", "allowOpenDial", "dialogLine",
|
"nativeFont", "osDarwin", "osLinux", "osType", "osUnknown", "osWindows", "outlinePanePos",
|
||||||
"narratorBreak", "narratorDialog", "altDialogOpen", "altDialogClose", "highlightEmph",
|
"prefsWinSize", "scrollPastEnd", "searchCase", "searchLoop", "searchMatchCap",
|
||||||
"stopWhenIdle", "userIdleTime", "incNotesWCount", "fmtApostrophe", "fmtSQuoteOpen",
|
"searchNextFile", "searchProjCase", "searchProjRegEx", "searchProjWord", "searchRegEx",
|
||||||
"fmtSQuoteClose", "fmtDQuoteOpen", "fmtDQuoteClose", "fmtPadBefore", "fmtPadAfter",
|
"searchWord", "showEditToolBar", "showFullPath", "showLineEndings", "showMultiSpaces",
|
||||||
"fmtPadThin", "spellLanguage", "showViewerPanel", "showEditToolBar", "showSessionTime",
|
"showSessionTime", "showTabsNSpaces", "showViewerPanel", "spellLanguage", "stopWhenIdle",
|
||||||
"viewComments", "viewSynopsis", "searchCase", "searchWord", "searchRegEx", "searchLoop",
|
"tabWidth", "textFont", "textMargin", "textWidth", "useCharCount", "userIdleTime",
|
||||||
"searchNextFile", "searchMatchCap", "searchProjCase", "searchProjWord", "searchProjRegEx",
|
"verPyQtString", "verPyQtValue", "verPyString", "verQtString", "verQtValue",
|
||||||
"verQtString", "verQtValue", "verPyQtString", "verPyQtValue", "verPyString", "osType",
|
"viewComments", "viewPanePos", "viewSynopsis", "welcomeWinSize",
|
||||||
"osLinux", "osWindows", "osDarwin", "osUnknown", "hostName", "kernelVer", "isDebug",
|
|
||||||
"memInfo", "hasEnchant",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
LANG_NW = 1
|
LANG_NW = 1
|
||||||
@@ -306,6 +305,10 @@ class Config:
|
|||||||
def pdfDocs(self) -> Path | None:
|
def pdfDocs(self) -> Path | None:
|
||||||
return self._manuals.get(f"manual_{self.locale.name()}", self._manuals.get("manual"))
|
return self._manuals.get(f"manual_{self.locale.name()}", self._manuals.get("manual"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nwLangPath(self) -> Path:
|
||||||
|
return self._nwLangPath
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def locale(self) -> QLocale:
|
def locale(self) -> QLocale:
|
||||||
return self._dLocale
|
return self._dLocale
|
||||||
@@ -918,7 +921,7 @@ class RecentProjects:
|
|||||||
|
|
||||||
class RecentPaths:
|
class RecentPaths:
|
||||||
|
|
||||||
KEYS = ["default", "project", "import", "outline", "stats"]
|
KEYS: Final[list[str]] = ["default", "project", "import", "outline", "stats"]
|
||||||
|
|
||||||
def __init__(self, config: Config) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
self._conf = config
|
self._conf = config
|
||||||
|
|||||||
+37
-35
@@ -23,6 +23,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
||||||
|
|
||||||
from novelwriter.enum import (
|
from novelwriter.enum import (
|
||||||
@@ -99,7 +101,7 @@ class nwShortcode:
|
|||||||
FOOTNOTE_B = "[footnote:"
|
FOOTNOTE_B = "[footnote:"
|
||||||
FIELD_B = "[field:"
|
FIELD_B = "[field:"
|
||||||
|
|
||||||
COMMENT_STYLES = {
|
COMMENT_STYLES: Final[dict[nwComment, str]] = {
|
||||||
nwComment.FOOTNOTE: "[footnote:{0}]",
|
nwComment.FOOTNOTE: "[footnote:{0}]",
|
||||||
nwComment.COMMENT: "[comment:{0}]",
|
nwComment.COMMENT: "[comment:{0}]",
|
||||||
}
|
}
|
||||||
@@ -110,13 +112,13 @@ class nwShortcode:
|
|||||||
class nwStyles:
|
class nwStyles:
|
||||||
|
|
||||||
H_VALID = ("H0", "H1", "H2", "H3", "H4")
|
H_VALID = ("H0", "H1", "H2", "H3", "H4")
|
||||||
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
|
H_LEVEL: Final[dict[str, int]] = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
|
||||||
H_SIZES = {0: 2.50, 1: 2.00, 2: 1.75, 3: 1.50, 4: 1.25}
|
H_SIZES: Final[dict[int, float]] = {0: 2.50, 1: 2.00, 2: 1.75, 3: 1.50, 4: 1.25}
|
||||||
|
|
||||||
T_NORMAL = 1.0
|
T_NORMAL = 1.0
|
||||||
T_SMALL = 0.8
|
T_SMALL = 0.8
|
||||||
|
|
||||||
T_LABEL = {
|
T_LABEL: Final[dict[str, str]] = {
|
||||||
"H0": QT_TRANSLATE_NOOP("Constant", "Title"),
|
"H0": QT_TRANSLATE_NOOP("Constant", "Title"),
|
||||||
"H1": QT_TRANSLATE_NOOP("Constant", "Heading 1 (Partition)"),
|
"H1": QT_TRANSLATE_NOOP("Constant", "Heading 1 (Partition)"),
|
||||||
"H2": QT_TRANSLATE_NOOP("Constant", "Heading 2 (Chapter)"),
|
"H2": QT_TRANSLATE_NOOP("Constant", "Heading 2 (Chapter)"),
|
||||||
@@ -125,7 +127,7 @@ class nwStyles:
|
|||||||
"TT": QT_TRANSLATE_NOOP("Constant", "Text Paragraph"),
|
"TT": QT_TRANSLATE_NOOP("Constant", "Text Paragraph"),
|
||||||
"SP": QT_TRANSLATE_NOOP("Constant", "Scene Separator"),
|
"SP": QT_TRANSLATE_NOOP("Constant", "Scene Separator"),
|
||||||
}
|
}
|
||||||
T_MARGIN = {
|
T_MARGIN: Final[dict[str, tuple[float, float]]] = {
|
||||||
"H0": (1.50, 0.60), # Title margins (top, bottom)
|
"H0": (1.50, 0.60), # Title margins (top, bottom)
|
||||||
"H1": (1.50, 0.60), # Heading 1 margins (top, bottom)
|
"H1": (1.50, 0.60), # Heading 1 margins (top, bottom)
|
||||||
"H2": (1.50, 0.60), # Heading 2 margins (top, bottom)
|
"H2": (1.50, 0.60), # Heading 2 margins (top, bottom)
|
||||||
@@ -174,20 +176,20 @@ class nwKeyWords:
|
|||||||
MENTION_KEY = "@mention"
|
MENTION_KEY = "@mention"
|
||||||
|
|
||||||
# Note: The order here affects the order of menu entries
|
# Note: The order here affects the order of menu entries
|
||||||
ALL_KEYS = [
|
ALL_KEYS: Final[list[str]] = [
|
||||||
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
|
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
|
||||||
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY,
|
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY,
|
||||||
]
|
]
|
||||||
CAN_CREATE = [
|
CAN_CREATE: Final[list[str]] = [
|
||||||
POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
|
POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
|
||||||
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY,
|
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Set of Valid Keys
|
# Set of Valid Keys
|
||||||
VALID_KEYS = set(ALL_KEYS)
|
VALID_KEYS: Final[set[str]] = set(ALL_KEYS)
|
||||||
|
|
||||||
# Map from Keys to Item Class
|
# Map from Keys to Item Class
|
||||||
KEY_CLASS = {
|
KEY_CLASS: Final[dict[str, nwItemClass]] = {
|
||||||
POV_KEY: nwItemClass.CHARACTER,
|
POV_KEY: nwItemClass.CHARACTER,
|
||||||
FOCUS_KEY: nwItemClass.CHARACTER,
|
FOCUS_KEY: nwItemClass.CHARACTER,
|
||||||
CHAR_KEY: nwItemClass.CHARACTER,
|
CHAR_KEY: nwItemClass.CHARACTER,
|
||||||
@@ -203,7 +205,7 @@ class nwKeyWords:
|
|||||||
|
|
||||||
class nwLists:
|
class nwLists:
|
||||||
|
|
||||||
USER_CLASSES = [
|
USER_CLASSES: Final[list[nwItemClass]] = [
|
||||||
nwItemClass.CHARACTER,
|
nwItemClass.CHARACTER,
|
||||||
nwItemClass.PLOT,
|
nwItemClass.PLOT,
|
||||||
nwItemClass.WORLD,
|
nwItemClass.WORLD,
|
||||||
@@ -229,7 +231,7 @@ class nwStats:
|
|||||||
WORDS_TITLE = "titleWords"
|
WORDS_TITLE = "titleWords"
|
||||||
|
|
||||||
# Note: The order here affects the order of menu entries
|
# Note: The order here affects the order of menu entries
|
||||||
ALL_FIELDS = [
|
ALL_FIELDS: Final[list[str]] = [
|
||||||
WORDS, WORDS_TEXT, WORDS_TITLE,
|
WORDS, WORDS_TEXT, WORDS_TITLE,
|
||||||
CHARS, CHARS_TEXT, CHARS_TITLE,
|
CHARS, CHARS_TEXT, CHARS_TITLE,
|
||||||
WCHARS_ALL, WCHARS_TEXT, WCHARS_TITLE,
|
WCHARS_ALL, WCHARS_TEXT, WCHARS_TITLE,
|
||||||
@@ -239,7 +241,7 @@ class nwStats:
|
|||||||
|
|
||||||
class nwLabels:
|
class nwLabels:
|
||||||
|
|
||||||
CLASS_NAME = {
|
CLASS_NAME: Final[dict[nwItemClass, str]] = {
|
||||||
nwItemClass.NO_CLASS: QT_TRANSLATE_NOOP("Constant", "None"),
|
nwItemClass.NO_CLASS: QT_TRANSLATE_NOOP("Constant", "None"),
|
||||||
nwItemClass.NOVEL: QT_TRANSLATE_NOOP("Constant", "Novel"),
|
nwItemClass.NOVEL: QT_TRANSLATE_NOOP("Constant", "Novel"),
|
||||||
nwItemClass.PLOT: QT_TRANSLATE_NOOP("Constant", "Plot"),
|
nwItemClass.PLOT: QT_TRANSLATE_NOOP("Constant", "Plot"),
|
||||||
@@ -253,7 +255,7 @@ class nwLabels:
|
|||||||
nwItemClass.TEMPLATE: QT_TRANSLATE_NOOP("Constant", "Templates"),
|
nwItemClass.TEMPLATE: QT_TRANSLATE_NOOP("Constant", "Templates"),
|
||||||
nwItemClass.TRASH: QT_TRANSLATE_NOOP("Constant", "Trash"),
|
nwItemClass.TRASH: QT_TRANSLATE_NOOP("Constant", "Trash"),
|
||||||
}
|
}
|
||||||
CLASS_ICON = {
|
CLASS_ICON: Final[dict[nwItemClass, str]] = {
|
||||||
nwItemClass.NO_CLASS: "cls_none",
|
nwItemClass.NO_CLASS: "cls_none",
|
||||||
nwItemClass.NOVEL: "cls_novel",
|
nwItemClass.NOVEL: "cls_novel",
|
||||||
nwItemClass.PLOT: "cls_plot",
|
nwItemClass.PLOT: "cls_plot",
|
||||||
@@ -267,12 +269,12 @@ class nwLabels:
|
|||||||
nwItemClass.TEMPLATE: "cls_template",
|
nwItemClass.TEMPLATE: "cls_template",
|
||||||
nwItemClass.TRASH: "cls_trash",
|
nwItemClass.TRASH: "cls_trash",
|
||||||
}
|
}
|
||||||
LAYOUT_NAME = {
|
LAYOUT_NAME: Final[dict[nwItemLayout, str]] = {
|
||||||
nwItemLayout.NO_LAYOUT: QT_TRANSLATE_NOOP("Constant", "None"),
|
nwItemLayout.NO_LAYOUT: QT_TRANSLATE_NOOP("Constant", "None"),
|
||||||
nwItemLayout.DOCUMENT: QT_TRANSLATE_NOOP("Constant", "Novel Document"),
|
nwItemLayout.DOCUMENT: QT_TRANSLATE_NOOP("Constant", "Novel Document"),
|
||||||
nwItemLayout.NOTE: QT_TRANSLATE_NOOP("Constant", "Project Note"),
|
nwItemLayout.NOTE: QT_TRANSLATE_NOOP("Constant", "Project Note"),
|
||||||
}
|
}
|
||||||
ITEM_DESCRIPTION = {
|
ITEM_DESCRIPTION: Final[dict[str, str]] = {
|
||||||
"none": QT_TRANSLATE_NOOP("Constant", "None"),
|
"none": QT_TRANSLATE_NOOP("Constant", "None"),
|
||||||
"root": QT_TRANSLATE_NOOP("Constant", "Root Folder"),
|
"root": QT_TRANSLATE_NOOP("Constant", "Root Folder"),
|
||||||
"folder": QT_TRANSLATE_NOOP("Constant", "Folder"),
|
"folder": QT_TRANSLATE_NOOP("Constant", "Folder"),
|
||||||
@@ -283,11 +285,11 @@ class nwLabels:
|
|||||||
"doc_h4": QT_TRANSLATE_NOOP("Constant", "Novel Section"),
|
"doc_h4": QT_TRANSLATE_NOOP("Constant", "Novel Section"),
|
||||||
"note": QT_TRANSLATE_NOOP("Constant", "Project Note"),
|
"note": QT_TRANSLATE_NOOP("Constant", "Project Note"),
|
||||||
}
|
}
|
||||||
ACTIVE_NAME = {
|
ACTIVE_NAME: Final[dict[str, str]] = {
|
||||||
"checked": QT_TRANSLATE_NOOP("Constant", "Active"),
|
"checked": QT_TRANSLATE_NOOP("Constant", "Active"),
|
||||||
"unchecked": QT_TRANSLATE_NOOP("Constant", "Inactive"),
|
"unchecked": QT_TRANSLATE_NOOP("Constant", "Inactive"),
|
||||||
}
|
}
|
||||||
KEY_NAME = {
|
KEY_NAME: Final[dict[str, str]] = {
|
||||||
nwKeyWords.TAG_KEY: QT_TRANSLATE_NOOP("Constant", "Tag"),
|
nwKeyWords.TAG_KEY: QT_TRANSLATE_NOOP("Constant", "Tag"),
|
||||||
nwKeyWords.POV_KEY: QT_TRANSLATE_NOOP("Constant", "Point of View"),
|
nwKeyWords.POV_KEY: QT_TRANSLATE_NOOP("Constant", "Point of View"),
|
||||||
nwKeyWords.FOCUS_KEY: QT_TRANSLATE_NOOP("Constant", "Focus"),
|
nwKeyWords.FOCUS_KEY: QT_TRANSLATE_NOOP("Constant", "Focus"),
|
||||||
@@ -301,7 +303,7 @@ class nwLabels:
|
|||||||
nwKeyWords.STORY_KEY: QT_TRANSLATE_NOOP("Constant", "Story"),
|
nwKeyWords.STORY_KEY: QT_TRANSLATE_NOOP("Constant", "Story"),
|
||||||
nwKeyWords.MENTION_KEY: QT_TRANSLATE_NOOP("Constant", "Mentions"),
|
nwKeyWords.MENTION_KEY: QT_TRANSLATE_NOOP("Constant", "Mentions"),
|
||||||
}
|
}
|
||||||
KEY_SHORTCUT = {
|
KEY_SHORTCUT: Final[dict[str, str]] = {
|
||||||
nwKeyWords.TAG_KEY: "Ctrl+K, G",
|
nwKeyWords.TAG_KEY: "Ctrl+K, G",
|
||||||
nwKeyWords.POV_KEY: "Ctrl+K, V",
|
nwKeyWords.POV_KEY: "Ctrl+K, V",
|
||||||
nwKeyWords.FOCUS_KEY: "Ctrl+K, F",
|
nwKeyWords.FOCUS_KEY: "Ctrl+K, F",
|
||||||
@@ -315,7 +317,7 @@ class nwLabels:
|
|||||||
nwKeyWords.STORY_KEY: "Ctrl+K, N",
|
nwKeyWords.STORY_KEY: "Ctrl+K, N",
|
||||||
nwKeyWords.MENTION_KEY: "Ctrl+K, M",
|
nwKeyWords.MENTION_KEY: "Ctrl+K, M",
|
||||||
}
|
}
|
||||||
OUTLINE_COLS = {
|
OUTLINE_COLS: Final[dict[nwOutline, str]] = {
|
||||||
nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"),
|
nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"),
|
||||||
nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"),
|
nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"),
|
||||||
nwOutline.LABEL: QT_TRANSLATE_NOOP("Constant", "Document"),
|
nwOutline.LABEL: QT_TRANSLATE_NOOP("Constant", "Document"),
|
||||||
@@ -337,7 +339,7 @@ class nwLabels:
|
|||||||
nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY],
|
nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY],
|
||||||
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
|
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
|
||||||
}
|
}
|
||||||
STATS_NAME = {
|
STATS_NAME: Final[dict[str, str]] = {
|
||||||
nwStats.CHARS: QT_TRANSLATE_NOOP("Stats", "Characters"),
|
nwStats.CHARS: QT_TRANSLATE_NOOP("Stats", "Characters"),
|
||||||
nwStats.CHARS_TEXT: QT_TRANSLATE_NOOP("Stats", "Characters in Text"),
|
nwStats.CHARS_TEXT: QT_TRANSLATE_NOOP("Stats", "Characters in Text"),
|
||||||
nwStats.CHARS_TITLE: QT_TRANSLATE_NOOP("Stats", "Characters in Headings"),
|
nwStats.CHARS_TITLE: QT_TRANSLATE_NOOP("Stats", "Characters in Headings"),
|
||||||
@@ -350,7 +352,7 @@ class nwLabels:
|
|||||||
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
|
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
|
||||||
nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"),
|
nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"),
|
||||||
}
|
}
|
||||||
BUILD_FMT = {
|
BUILD_FMT: Final[dict[nwBuildFmt, str]] = {
|
||||||
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
|
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
|
||||||
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
|
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
|
||||||
nwBuildFmt.DOCX: QT_TRANSLATE_NOOP("Constant", "Microsoft Word Document (.docx)"),
|
nwBuildFmt.DOCX: QT_TRANSLATE_NOOP("Constant", "Microsoft Word Document (.docx)"),
|
||||||
@@ -362,7 +364,7 @@ class nwLabels:
|
|||||||
nwBuildFmt.J_HTML: QT_TRANSLATE_NOOP("Constant", "JSON + HTML 5 (.json)"),
|
nwBuildFmt.J_HTML: QT_TRANSLATE_NOOP("Constant", "JSON + HTML 5 (.json)"),
|
||||||
nwBuildFmt.J_NWD: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markup (.json)"),
|
nwBuildFmt.J_NWD: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markup (.json)"),
|
||||||
}
|
}
|
||||||
BUILD_EXT = {
|
BUILD_EXT: Final[dict[nwBuildFmt, str]] = {
|
||||||
nwBuildFmt.ODT: ".odt",
|
nwBuildFmt.ODT: ".odt",
|
||||||
nwBuildFmt.FODT: ".fodt",
|
nwBuildFmt.FODT: ".fodt",
|
||||||
nwBuildFmt.DOCX: ".docx",
|
nwBuildFmt.DOCX: ".docx",
|
||||||
@@ -374,7 +376,7 @@ class nwLabels:
|
|||||||
nwBuildFmt.J_HTML: ".json",
|
nwBuildFmt.J_HTML: ".json",
|
||||||
nwBuildFmt.J_NWD: ".json",
|
nwBuildFmt.J_NWD: ".json",
|
||||||
}
|
}
|
||||||
SHAPES_PLAIN = {
|
SHAPES_PLAIN: Final[dict[nwStatusShape, str]] = {
|
||||||
nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
|
nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
|
||||||
nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
|
nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
|
||||||
nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
|
nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
|
||||||
@@ -384,42 +386,42 @@ class nwLabels:
|
|||||||
nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
|
nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
|
||||||
nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
|
nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
|
||||||
}
|
}
|
||||||
SHAPES_CIRCLE = {
|
SHAPES_CIRCLE: Final[dict[nwStatusShape, str]] = {
|
||||||
nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "1/4 Circle"),
|
nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "1/4 Circle"),
|
||||||
nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Half Circle"),
|
nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Half Circle"),
|
||||||
nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "3/4 Circle"),
|
nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "3/4 Circle"),
|
||||||
nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Full Circle"),
|
nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Full Circle"),
|
||||||
}
|
}
|
||||||
SHAPES_BARS = {
|
SHAPES_BARS: Final[dict[nwStatusShape, str]] = {
|
||||||
nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
|
nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
|
||||||
nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
|
nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
|
||||||
nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
|
nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
|
||||||
nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Constant", "4 Bars"),
|
nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Constant", "4 Bars"),
|
||||||
}
|
}
|
||||||
SHAPES_BLOCKS = {
|
SHAPES_BLOCKS: Final[dict[nwStatusShape, str]] = {
|
||||||
nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Constant", "1 Block"),
|
nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Constant", "1 Block"),
|
||||||
nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Constant", "2 Blocks"),
|
nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Constant", "2 Blocks"),
|
||||||
nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Constant", "3 Blocks"),
|
nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Constant", "3 Blocks"),
|
||||||
nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Constant", "4 Blocks"),
|
nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Constant", "4 Blocks"),
|
||||||
}
|
}
|
||||||
FILE_FILTERS = {
|
FILE_FILTERS: Final[dict[str, str]] = {
|
||||||
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
|
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
|
||||||
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
|
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
|
||||||
"*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
|
"*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
|
||||||
"*.csv": QT_TRANSLATE_NOOP("Constant", "CSV files"),
|
"*.csv": QT_TRANSLATE_NOOP("Constant", "CSV files"),
|
||||||
"*": QT_TRANSLATE_NOOP("Constant", "All files"),
|
"*": QT_TRANSLATE_NOOP("Constant", "All files"),
|
||||||
}
|
}
|
||||||
UNIT_NAME = {
|
UNIT_NAME: Final[dict[str, str]] = {
|
||||||
"mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"),
|
"mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"),
|
||||||
"cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"),
|
"cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"),
|
||||||
"in": QT_TRANSLATE_NOOP("Constant", "Inches"),
|
"in": QT_TRANSLATE_NOOP("Constant", "Inches"),
|
||||||
}
|
}
|
||||||
UNIT_SCALE = {
|
UNIT_SCALE: Final[dict[str, float]] = {
|
||||||
"mm": 1.0,
|
"mm": 1.0,
|
||||||
"cm": 10.0,
|
"cm": 10.0,
|
||||||
"in": 25.4,
|
"in": 25.4,
|
||||||
}
|
}
|
||||||
PAPER_NAME = {
|
PAPER_NAME: Final[dict[str, str]] = {
|
||||||
"A4": QT_TRANSLATE_NOOP("Constant", "A4"),
|
"A4": QT_TRANSLATE_NOOP("Constant", "A4"),
|
||||||
"A5": QT_TRANSLATE_NOOP("Constant", "A5"),
|
"A5": QT_TRANSLATE_NOOP("Constant", "A5"),
|
||||||
"A6": QT_TRANSLATE_NOOP("Constant", "A6"),
|
"A6": QT_TRANSLATE_NOOP("Constant", "A6"),
|
||||||
@@ -427,7 +429,7 @@ class nwLabels:
|
|||||||
"Letter": QT_TRANSLATE_NOOP("Constant", "US Letter"),
|
"Letter": QT_TRANSLATE_NOOP("Constant", "US Letter"),
|
||||||
"Custom": QT_TRANSLATE_NOOP("Constant", "Custom"),
|
"Custom": QT_TRANSLATE_NOOP("Constant", "Custom"),
|
||||||
}
|
}
|
||||||
PAPER_SIZE = {
|
PAPER_SIZE: Final[dict[str, tuple[float, float]]] = {
|
||||||
"A4": (210.0, 297.0),
|
"A4": (210.0, 297.0),
|
||||||
"A5": (148.0, 210.0),
|
"A5": (148.0, 210.0),
|
||||||
"A6": (105.0, 148.0),
|
"A6": (105.0, 148.0),
|
||||||
@@ -435,7 +437,7 @@ class nwLabels:
|
|||||||
"Letter": (215.9, 279.4),
|
"Letter": (215.9, 279.4),
|
||||||
"Custom": (-1.0, -1.0),
|
"Custom": (-1.0, -1.0),
|
||||||
}
|
}
|
||||||
THEME_COLORS = {
|
THEME_COLORS: Final[dict[str, str]] = {
|
||||||
"theme": QT_TRANSLATE_NOOP("Constant", "Theme Colours"),
|
"theme": QT_TRANSLATE_NOOP("Constant", "Theme Colours"),
|
||||||
"default": QT_TRANSLATE_NOOP("Constant", "Foreground Colour"),
|
"default": QT_TRANSLATE_NOOP("Constant", "Foreground Colour"),
|
||||||
"faded": QT_TRANSLATE_NOOP("Constant", "Faded Colour"),
|
"faded": QT_TRANSLATE_NOOP("Constant", "Faded Colour"),
|
||||||
@@ -462,7 +464,7 @@ class nwHeadFmt:
|
|||||||
CHAR_POV = "{Char:POV}"
|
CHAR_POV = "{Char:POV}"
|
||||||
CHAR_FOCUS = "{Char:Focus}"
|
CHAR_FOCUS = "{Char:Focus}"
|
||||||
|
|
||||||
PAGE_HEADERS = [
|
PAGE_HEADERS: Final[list[str]] = [
|
||||||
TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS,
|
TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS,
|
||||||
CHAR_POV, CHAR_FOCUS
|
CHAR_POV, CHAR_FOCUS
|
||||||
]
|
]
|
||||||
@@ -478,7 +480,7 @@ class nwQuotes:
|
|||||||
"""Allowed quotation marks.
|
"""Allowed quotation marks.
|
||||||
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
||||||
"""
|
"""
|
||||||
SYMBOLS = {
|
SYMBOLS: Final[dict[str, str]] = {
|
||||||
"\u0027": QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"),
|
"\u0027": QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"),
|
||||||
"\u0022": QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"),
|
"\u0022": QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"),
|
||||||
|
|
||||||
@@ -645,7 +647,7 @@ class nwUnicode:
|
|||||||
|
|
||||||
class nwHtmlUnicode:
|
class nwHtmlUnicode:
|
||||||
|
|
||||||
U_TO_H = {
|
U_TO_H: Final[dict[str, str]] = {
|
||||||
# Quotes
|
# Quotes
|
||||||
nwUnicode.U_QUOT: nwUnicode.H_QUOT,
|
nwUnicode.U_QUOT: nwUnicode.H_QUOT,
|
||||||
nwUnicode.U_APOS: nwUnicode.H_APOS,
|
nwUnicode.U_APOS: nwUnicode.H_APOS,
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from collections.abc import Iterable
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
||||||
|
|
||||||
@@ -41,6 +41,9 @@ from novelwriter.core.project import NWProject
|
|||||||
from novelwriter.enum import nwBuildFmt
|
from novelwriter.enum import nwBuildFmt
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
T_BuildValue = str | int | float | bool
|
T_BuildValue = str | int | float | bool
|
||||||
@@ -227,9 +230,9 @@ class BuildSettings:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def fromDict(cls, data: dict) -> BuildSettings:
|
def fromDict(cls, data: dict) -> BuildSettings:
|
||||||
"""Create a build settings object from a dict."""
|
"""Create a build settings object from a dict."""
|
||||||
cls = BuildSettings()
|
new = cls()
|
||||||
cls.unpack(data)
|
new.unpack(data)
|
||||||
return cls
|
return new
|
||||||
|
|
||||||
##
|
##
|
||||||
# Properties
|
# Properties
|
||||||
@@ -514,11 +517,11 @@ class BuildSettings:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def duplicate(cls, source: BuildSettings) -> BuildSettings:
|
def duplicate(cls, source: BuildSettings) -> BuildSettings:
|
||||||
"""Make a copy of another build."""
|
"""Make a copy of another build."""
|
||||||
cls = BuildSettings()
|
new = cls()
|
||||||
cls.unpack(source.pack())
|
new.unpack(source.pack())
|
||||||
cls._uuid = str(uuid.uuid4())
|
new._uuid = str(uuid.uuid4())
|
||||||
cls._name = f"{source.name} 2"
|
new._name = f"{source.name} 2"
|
||||||
return cls
|
return new
|
||||||
|
|
||||||
|
|
||||||
class BuildCollection:
|
class BuildCollection:
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from collections.abc import Iterable
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from zipfile import ZipFile, is_zipfile
|
from zipfile import ZipFile, is_zipfile
|
||||||
|
|
||||||
from PyQt6.QtCore import QCoreApplication
|
from PyQt6.QtCore import QCoreApplication
|
||||||
@@ -40,10 +40,14 @@ from PyQt6.QtCore import QCoreApplication
|
|||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import isHandle, minmax, simplified
|
from novelwriter.common import isHandle, minmax, simplified
|
||||||
from novelwriter.constants import nwConst, nwFiles, nwItemClass, nwStats
|
from novelwriter.constants import nwConst, nwFiles, nwItemClass, nwStats
|
||||||
from novelwriter.core.item import NWItem
|
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.storage import NWStorageCreate
|
from novelwriter.core.storage import NWStorageCreate
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
|
from novelwriter.core.item import NWItem
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -284,7 +288,7 @@ class DocDuplicator:
|
|||||||
class DocSearch:
|
class DocSearch:
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._regEx = re.compile("")
|
self._regEx = re.compile(r"")
|
||||||
self._opts = re.UNICODE | re.IGNORECASE
|
self._opts = re.UNICODE | re.IGNORECASE
|
||||||
self._words = False
|
self._words = False
|
||||||
self._escape = True
|
self._escape = True
|
||||||
@@ -381,7 +385,7 @@ class ProjectBuilder:
|
|||||||
path = data.get("path", None) or None
|
path = data.get("path", None) or None
|
||||||
if isinstance(path, str | Path):
|
if isinstance(path, str | Path):
|
||||||
self._path = Path(path).resolve()
|
self._path = Path(path).resolve()
|
||||||
if data.get("sample", False):
|
if data.get("sample"):
|
||||||
return self._extractSampleProject(self._path)
|
return self._extractSampleProject(self._path)
|
||||||
elif data.get("template"):
|
elif data.get("template"):
|
||||||
return self._copyProject(self._path, data)
|
return self._copyProject(self._path, data)
|
||||||
|
|||||||
@@ -25,26 +25,30 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from collections.abc import Iterable
|
from typing import TYPE_CHECKING
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from PyQt6.QtGui import QFont
|
from PyQt6.QtGui import QFont
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.constants import nwLabels
|
from novelwriter.constants import nwLabels
|
||||||
from novelwriter.core.buildsettings import BuildSettings
|
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.enum import nwBuildFmt
|
from novelwriter.enum import nwBuildFmt
|
||||||
from novelwriter.error import formatException, logException
|
from novelwriter.error import formatException, logException
|
||||||
from novelwriter.formats.todocx import ToDocX
|
from novelwriter.formats.todocx import ToDocX
|
||||||
from novelwriter.formats.tohtml import ToHtml
|
from novelwriter.formats.tohtml import ToHtml
|
||||||
from novelwriter.formats.tokenizer import Tokenizer
|
|
||||||
from novelwriter.formats.tomarkdown import ToMarkdown
|
from novelwriter.formats.tomarkdown import ToMarkdown
|
||||||
from novelwriter.formats.toodt import ToOdt
|
from novelwriter.formats.toodt import ToOdt
|
||||||
from novelwriter.formats.toqdoc import ToQTextDocument
|
from novelwriter.formats.toqdoc import ToQTextDocument
|
||||||
from novelwriter.formats.toraw import ToRaw
|
from novelwriter.formats.toraw import ToRaw
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterable
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from novelwriter.core.buildsettings import BuildSettings
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
from novelwriter.formats.tokenizer import Tokenizer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -56,8 +60,8 @@ class NWBuildDocument:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_project", "_build", "_queue", "_error", "_cache", "_count",
|
"_build", "_cache", "_count", "_error", "_outline", "_project",
|
||||||
"_outline",
|
"_queue",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, project: NWProject, build: BuildSettings) -> None:
|
def __init__(self, project: NWProject, build: BuildSettings) -> None:
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ from time import time
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from novelwriter.common import formatTimeStamp, isHandle
|
from novelwriter.common import formatTimeStamp, isHandle
|
||||||
from novelwriter.core.item import NWItem
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||||
from novelwriter.error import formatException, logException
|
from novelwriter.error import formatException, logException
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from collections.abc import ItemsView, Iterable
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import time
|
from time import time
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
@@ -44,7 +43,9 @@ from novelwriter.error import logException
|
|||||||
from novelwriter.text.comments import processComment
|
from novelwriter.text.comments import processComment
|
||||||
from novelwriter.text.counting import standardCounter
|
from novelwriter.text.counting import standardCounter
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import ItemsView, Iterable
|
||||||
|
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
@@ -906,7 +907,7 @@ class ItemIndex:
|
|||||||
IndexHeading object for each heading of the text.
|
IndexHeading object for each heading of the text.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ("_project", "_tags", "_items")
|
__slots__ = ("_items", "_project", "_tags")
|
||||||
|
|
||||||
def __init__(self, project: NWProject, tagsIndex: TagsIndex) -> None:
|
def __init__(self, project: NWProject, tagsIndex: TagsIndex) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
|
|||||||
@@ -28,14 +28,15 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from collections.abc import ItemsView, Sequence
|
|
||||||
from typing import TYPE_CHECKING, Literal
|
from typing import TYPE_CHECKING, Literal
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.common import checkInt, isListInstance, isTitleTag
|
from novelwriter.common import checkInt, isListInstance, isTitleTag
|
||||||
from novelwriter.constants import nwKeyWords, nwStyles
|
from novelwriter.constants import nwKeyWords, nwStyles
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import ItemsView, Sequence
|
||||||
|
|
||||||
from novelwriter.core.index import TagsIndex
|
from novelwriter.core.index import TagsIndex
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ class IndexNode:
|
|||||||
must be reset each time the item is re-indexed.
|
must be reset each time the item is re-indexed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ("_tags", "_handle", "_item", "_headings", "_notes", "_count")
|
__slots__ = ("_count", "_handle", "_headings", "_item", "_notes", "_tags")
|
||||||
|
|
||||||
def __init__(self, tagsIndex: TagsIndex, tHandle: str, nwItem: NWItem) -> None:
|
def __init__(self, tagsIndex: TagsIndex, tHandle: str, nwItem: NWItem) -> None:
|
||||||
self._tags = tagsIndex
|
self._tags = tagsIndex
|
||||||
@@ -205,8 +206,8 @@ class IndexHeading:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_tags", "_key", "_line", "_level", "_title",
|
"_comments", "_counts", "_key", "_level", "_line", "_refs", "_tag",
|
||||||
"_counts", "_tag", "_refs", "_comments",
|
"_tags", "_title",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|||||||
+26
-26
@@ -27,8 +27,6 @@ import logging
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from PyQt6.QtGui import QFont, QIcon
|
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified,
|
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified,
|
||||||
@@ -37,7 +35,9 @@ from novelwriter.common import (
|
|||||||
from novelwriter.constants import nwLabels, nwStyles, trConst
|
from novelwriter.constants import nwLabels, nwStyles, trConst
|
||||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from PyQt6.QtGui import QFont, QIcon
|
||||||
|
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -53,10 +53,10 @@ class NWItem:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_project", "_name", "_handle", "_parent", "_root", "_order",
|
"_active", "_charCount", "_class", "_cursorPos", "_expanded",
|
||||||
"_type", "_class", "_layout", "_status", "_import", "_active",
|
"_handle", "_heading", "_import", "_initCount", "_layout", "_name",
|
||||||
"_expanded", "_heading", "_charCount", "_wordCount",
|
"_order", "_paraCount", "_parent", "_project", "_root", "_status",
|
||||||
"_paraCount", "_cursorPos", "_initCount",
|
"_type", "_wordCount",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, project: NWProject, handle: str) -> None:
|
def __init__(self, project: NWProject, handle: str) -> None:
|
||||||
@@ -264,25 +264,25 @@ class NWItem:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def duplicate(cls, source: NWItem, handle: str) -> NWItem:
|
def duplicate(cls, source: NWItem, handle: str) -> NWItem:
|
||||||
"""Make a copy of an item."""
|
"""Make a copy of an item."""
|
||||||
cls = NWItem(source._project, handle)
|
new = cls(source._project, handle)
|
||||||
cls._name = source._name
|
new._name = source._name
|
||||||
cls._parent = source._parent
|
new._parent = source._parent
|
||||||
cls._root = source._root
|
new._root = source._root
|
||||||
cls._order = source._order
|
new._order = source._order
|
||||||
cls._type = source._type
|
new._type = source._type
|
||||||
cls._class = source._class
|
new._class = source._class
|
||||||
cls._layout = source._layout
|
new._layout = source._layout
|
||||||
cls._status = source._status
|
new._status = source._status
|
||||||
cls._import = source._import
|
new._import = source._import
|
||||||
cls._active = source._active
|
new._active = source._active
|
||||||
cls._expanded = source._expanded
|
new._expanded = source._expanded
|
||||||
cls._heading = source._heading
|
new._heading = source._heading
|
||||||
cls._charCount = source._charCount
|
new._charCount = source._charCount
|
||||||
cls._wordCount = source._wordCount
|
new._wordCount = source._wordCount
|
||||||
cls._paraCount = source._paraCount
|
new._paraCount = source._paraCount
|
||||||
cls._cursorPos = source._cursorPos
|
new._cursorPos = source._cursorPos
|
||||||
cls._initCount = source._initCount
|
new._initCount = source._initCount
|
||||||
return cls
|
return new
|
||||||
|
|
||||||
##
|
##
|
||||||
# Action Methods
|
# Action Methods
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from novelwriter.core.item import NWItem
|
|||||||
from novelwriter.enum import nwItemClass
|
from novelwriter.enum import nwItemClass
|
||||||
from novelwriter.types import QtAlignRight
|
from novelwriter.types import QtAlignRight
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.core.tree import NWTree
|
from novelwriter.core.tree import NWTree
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -89,7 +89,7 @@ class ProjectNode:
|
|||||||
C_ACTIVE = 2
|
C_ACTIVE = 2
|
||||||
C_STATUS = 3
|
C_STATUS = 3
|
||||||
|
|
||||||
__slots__ = ("_item", "_children", "_parent", "_row", "_cache", "_flags", "_count")
|
__slots__ = ("_cache", "_children", "_count", "_flags", "_item", "_parent", "_row")
|
||||||
|
|
||||||
def __init__(self, item: NWItem) -> None:
|
def __init__(self, item: NWItem) -> None:
|
||||||
self._item = item
|
self._item = item
|
||||||
@@ -162,7 +162,7 @@ class ProjectNode:
|
|||||||
|
|
||||||
def updateCount(self, propagate: bool = True) -> None:
|
def updateCount(self, propagate: bool = True) -> None:
|
||||||
"""Update counts, and propagate upwards in the tree."""
|
"""Update counts, and propagate upwards in the tree."""
|
||||||
self._count = self._item.wordCount + sum(c._count for c in self._children)
|
self._count = self._item.wordCount + sum(c._count for c in self._children) # noqa: SLF001
|
||||||
self._cache[C_COUNT_TEXT] = f"{self._count:n}"
|
self._cache[C_COUNT_TEXT] = f"{self._count:n}"
|
||||||
if propagate and (parent := self._parent):
|
if propagate and (parent := self._parent):
|
||||||
parent.updateCount()
|
parent.updateCount()
|
||||||
@@ -257,13 +257,13 @@ class ProjectNode:
|
|||||||
"""Recursively add all nodes to a list."""
|
"""Recursively add all nodes to a list."""
|
||||||
for node in self._children:
|
for node in self._children:
|
||||||
children.append(node)
|
children.append(node)
|
||||||
node._recursiveAppendChildren(children)
|
node._recursiveAppendChildren(children) # noqa: SLF001
|
||||||
return
|
return
|
||||||
|
|
||||||
def _refreshChildrenPos(self) -> None:
|
def _refreshChildrenPos(self) -> None:
|
||||||
"""Update the row value on all children."""
|
"""Update the row value on all children."""
|
||||||
for n, child in enumerate(self._children):
|
for n, child in enumerate(self._children):
|
||||||
child._row = n
|
child._row = n # noqa: SLF001
|
||||||
child.item.setOrder(n)
|
child.item.setOrder(n)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -290,12 +290,12 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
methods needed primarily by the project tree GUI component.
|
methods needed primarily by the project tree GUI component.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ("_tree", "_root")
|
__slots__ = ("_root", "_tree")
|
||||||
|
|
||||||
def __init__(self, tree: NWTree) -> None:
|
def __init__(self, tree: NWTree) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._tree = tree
|
self._tree = tree
|
||||||
self._root = ProjectNode(NWItem(tree._project, INV_ROOT))
|
self._root = ProjectNode(NWItem(tree.project, INV_ROOT))
|
||||||
self._root.item.setName("Invisible Root")
|
self._root.item.setName("Invisible Root")
|
||||||
logger.debug("Ready: ProjectModel")
|
logger.debug("Ready: ProjectModel")
|
||||||
return
|
return
|
||||||
@@ -391,10 +391,10 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
"""Process mime data drop."""
|
"""Process mime data drop."""
|
||||||
if self.canDropMimeData(data, action, row, column, parent):
|
if self.canDropMimeData(data, action, row, column, parent):
|
||||||
items = []
|
items = [
|
||||||
for handle in decodeMimeHandles(data):
|
index for handle in decodeMimeHandles(data)
|
||||||
if (index := self.indexFromHandle(handle)).isValid():
|
if (index := self.indexFromHandle(handle)).isValid()
|
||||||
items.append(index)
|
]
|
||||||
self.multiMove(items, parent, row)
|
self.multiMove(items, parent, row)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -490,7 +490,7 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
if temp := self.removeChild(index.parent(), index.row()):
|
if temp := self.removeChild(index.parent(), index.row()):
|
||||||
self.insertChild(temp, target, pos)
|
self.insertChild(temp, target, pos)
|
||||||
for child in reversed(node.allChildren()):
|
for child in reversed(node.allChildren()):
|
||||||
node._updateRelationships(child)
|
node._updateRelationships(child) # noqa: SLF001
|
||||||
child.item.notifyToRefresh()
|
child.item.notifyToRefresh()
|
||||||
node.item.notifyToRefresh()
|
node.item.notifyToRefresh()
|
||||||
return
|
return
|
||||||
@@ -501,16 +501,15 @@ class ProjectModel(QAbstractItemModel):
|
|||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
"""Clear the project model."""
|
"""Clear the project model."""
|
||||||
self._root._children.clear()
|
self._root.children.clear()
|
||||||
return
|
return
|
||||||
|
|
||||||
def allExpanded(self) -> list[QModelIndex]:
|
def allExpanded(self) -> list[QModelIndex]:
|
||||||
"""Return a list of all expanded items."""
|
"""Return a list of all expanded items."""
|
||||||
expanded = []
|
return [
|
||||||
for node in self._root.allChildren():
|
self.createIndex(node.row(), 0, node) for node in self._root.allChildren()
|
||||||
if node._item.isExpanded:
|
if node.item.isExpanded
|
||||||
expanded.append(self.createIndex(node.row(), 0, node))
|
]
|
||||||
return expanded
|
|
||||||
|
|
||||||
def trashSelection(self, indices: list[QModelIndex]) -> bool:
|
def trashSelection(self, indices: list[QModelIndex]) -> bool:
|
||||||
"""Check if a selection of indices are all in trash or not."""
|
"""Check if a selection of indices are all in trash or not."""
|
||||||
|
|||||||
@@ -25,15 +25,19 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
|
from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
|
||||||
from PyQt6.QtGui import QIcon, QPixmap
|
from PyQt6.QtGui import QIcon, QPixmap
|
||||||
|
|
||||||
from novelwriter import SHARED
|
from novelwriter import SHARED
|
||||||
from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst
|
from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst
|
||||||
from novelwriter.core.indexdata import IndexHeading, IndexNode
|
|
||||||
from novelwriter.enum import nwNovelExtra
|
from novelwriter.enum import nwNovelExtra
|
||||||
from novelwriter.types import QtAlignRight
|
from novelwriter.types import QtAlignRight
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from novelwriter.core.indexdata import IndexHeading, IndexNode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
C_FACTOR = 0x0100
|
C_FACTOR = 0x0100
|
||||||
@@ -50,7 +54,7 @@ T_NodeData = str | QIcon | QPixmap | Qt.AlignmentFlag | None
|
|||||||
|
|
||||||
class NovelModel(QAbstractTableModel):
|
class NovelModel(QAbstractTableModel):
|
||||||
|
|
||||||
__slots__ = ("_rows", "_more", "_columns", "_extraKey", "_extraLabel")
|
__slots__ = ("_columns", "_extraKey", "_extraLabel", "_more", "_rows")
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ from novelwriter.common import checkBool, checkFloat, checkInt, checkString, jso
|
|||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from enum import Enum
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QCoreApplication
|
from PyQt6.QtCore import QCoreApplication
|
||||||
|
|
||||||
@@ -44,12 +45,14 @@ from novelwriter.core.options import OptionState
|
|||||||
from novelwriter.core.projectdata import NWProjectData
|
from novelwriter.core.projectdata import NWProjectData
|
||||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
||||||
from novelwriter.core.sessions import NWSessionLog
|
from novelwriter.core.sessions import NWSessionLog
|
||||||
from novelwriter.core.status import T_StatusKind, T_UpdateEntry
|
|
||||||
from novelwriter.core.storage import NWStorage, NWStorageOpen
|
from novelwriter.core.storage import NWStorage, NWStorageOpen
|
||||||
from novelwriter.core.tree import NWTree
|
from novelwriter.core.tree import NWTree
|
||||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from novelwriter.core.status import T_StatusKind, T_UpdateEntry
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -64,8 +67,8 @@ class NWProjectState(Enum):
|
|||||||
class NWProject:
|
class NWProject:
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_options", "_storage", "_data", "_tree", "_index", "_session",
|
"_changed", "_data", "_index", "_langData", "_options", "_session",
|
||||||
"_langData", "_changed", "_valid", "_state", "tr",
|
"_state", "_storage", "_tree", "_valid", "tr",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@@ -557,13 +560,13 @@ class NWProject:
|
|||||||
|
|
||||||
def _loadProjectLocalisation(self) -> bool:
|
def _loadProjectLocalisation(self) -> bool:
|
||||||
"""Load the language data for the current project language."""
|
"""Load the language data for the current project language."""
|
||||||
if self._data.language is None or CONFIG._nwLangPath is None:
|
if self._data.language is None or CONFIG.nwLangPath is None:
|
||||||
self._langData = {}
|
self._langData = {}
|
||||||
return False
|
return False
|
||||||
|
|
||||||
langFile = Path(CONFIG._nwLangPath) / f"project_{self._data.language}.json"
|
langFile = Path(CONFIG.nwLangPath) / f"project_{self._data.language}.json"
|
||||||
if not langFile.is_file():
|
if not langFile.is_file():
|
||||||
langFile = Path(CONFIG._nwLangPath) / "project_en_GB.json"
|
langFile = Path(CONFIG.nwLangPath) / "project_en_GB.json"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(langFile, mode="r", encoding="utf-8") as inFile:
|
with open(langFile, mode="r", encoding="utf-8") as inFile:
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from novelwriter.common import (
|
|||||||
)
|
)
|
||||||
from novelwriter.core.status import NWStatus
|
from novelwriter.core.status import NWStatus
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ from novelwriter.common import (
|
|||||||
hexToInt, simplified, xmlIndent, yesNo
|
hexToInt, simplified, xmlIndent, yesNo
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.core.projectdata import NWProjectData
|
from novelwriter.core.projectdata import NWProjectData
|
||||||
from novelwriter.core.status import NWStatus
|
from novelwriter.core.status import NWStatus
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from collections.abc import Iterable
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import time
|
from time import time
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
@@ -35,7 +34,9 @@ from novelwriter.common import formatTimeStamp
|
|||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from collections.abc import Iterator
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
@@ -36,7 +35,9 @@ from PyQt6.QtCore import QLocale
|
|||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterator
|
||||||
|
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ import dataclasses
|
|||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from collections.abc import Iterable
|
from typing import TYPE_CHECKING, Literal, TypeGuard
|
||||||
from typing import Literal, TypeGuard
|
|
||||||
|
|
||||||
from PyQt6.QtCore import QPointF, Qt
|
from PyQt6.QtCore import QPointF, Qt
|
||||||
from PyQt6.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygonF
|
from PyQt6.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygonF
|
||||||
@@ -39,6 +38,9 @@ from novelwriter.common import simplified
|
|||||||
from novelwriter.enum import nwStatusShape
|
from novelwriter.enum import nwStatusShape
|
||||||
from novelwriter.types import QtPaintAntiAlias, QtTransparent
|
from novelwriter.types import QtPaintAntiAlias, QtTransparent
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -54,10 +56,10 @@ class StatusEntry:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def duplicate(cls, source: StatusEntry) -> StatusEntry:
|
def duplicate(cls, source: StatusEntry) -> StatusEntry:
|
||||||
"""Create a deep copy of the source object."""
|
"""Create a deep copy of the source object."""
|
||||||
cls = dataclasses.replace(source)
|
status = dataclasses.replace(source)
|
||||||
cls.color = QColor(source.color)
|
status.color = QColor(source.color)
|
||||||
cls.icon = QIcon(source.icon)
|
status.icon = QIcon(source.icon)
|
||||||
return cls
|
return status
|
||||||
|
|
||||||
|
|
||||||
NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
|
NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
|
||||||
@@ -71,7 +73,7 @@ class NWStatus:
|
|||||||
STATUS = "s"
|
STATUS = "s"
|
||||||
IMPORT = "i"
|
IMPORT = "i"
|
||||||
|
|
||||||
__slots__ = ("_store", "_default", "_prefix", "_height")
|
__slots__ = ("_default", "_height", "_prefix", "_store")
|
||||||
|
|
||||||
def __init__(self, prefix: T_StatusKind) -> None:
|
def __init__(self, prefix: T_StatusKind) -> None:
|
||||||
self._store: dict[str, StatusEntry] = {}
|
self._store: dict[str, StatusEntry] = {}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
|
|||||||
from novelwriter.core.spellcheck import UserDictionary
|
from novelwriter.core.spellcheck import UserDictionary
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from collections.abc import Iterable, Iterator
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Literal, overload
|
from typing import TYPE_CHECKING, Literal, overload
|
||||||
|
|
||||||
@@ -40,7 +39,9 @@ from novelwriter.core.itemmodel import ProjectModel, ProjectNode
|
|||||||
from novelwriter.enum import nwChange, nwItemClass, nwItemLayout, nwItemType
|
from novelwriter.enum import nwChange, nwItemClass, nwItemLayout, nwItemType
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterable, Iterator
|
||||||
|
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -60,7 +61,7 @@ class NWTree:
|
|||||||
also used for file names.
|
also used for file names.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ("_project", "_model", "_items", "_nodes", "_trash", "_ready")
|
__slots__ = ("_items", "_model", "_nodes", "_project", "_ready", "_trash")
|
||||||
|
|
||||||
def __init__(self, project: NWProject) -> None:
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
@@ -107,6 +108,11 @@ class NWTree:
|
|||||||
# Properties
|
# Properties
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@property
|
||||||
|
def project(self) -> NWProject:
|
||||||
|
"""Return the parent project."""
|
||||||
|
return self._project
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def trash(self) -> ProjectNode | None:
|
def trash(self) -> ProjectNode | None:
|
||||||
"""Return trash node, if it exists."""
|
"""Return trash node, if it exists."""
|
||||||
@@ -396,7 +402,7 @@ class NWTree:
|
|||||||
|
|
||||||
def checkType(self, tHandle: str, itemType: nwItemType) -> bool:
|
def checkType(self, tHandle: str, itemType: nwItemType) -> bool:
|
||||||
"""Check if item exists and is of the specified item type."""
|
"""Check if item exists and is of the specified item type."""
|
||||||
if tItem := self.__getitem__(tHandle):
|
if tItem := self[tHandle]:
|
||||||
return tItem.itemType == itemType
|
return tItem.itemType == itemType
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -414,8 +420,7 @@ class NWTree:
|
|||||||
node = parent
|
node = parent
|
||||||
else:
|
else:
|
||||||
return path
|
return path
|
||||||
else:
|
logger.error("Max project tree depth reached")
|
||||||
logger.error("Max project tree depth reached")
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def subTree(self, tHandle: str) -> list[str]:
|
def subTree(self, tHandle: str) -> list[str]:
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt6.QtGui import QCloseEvent
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QDialogButtonBox, QHBoxLayout, QLabel, QTextBrowser, QVBoxLayout, QWidget
|
QDialogButtonBox, QHBoxLayout, QLabel, QTextBrowser, QVBoxLayout, QWidget
|
||||||
)
|
)
|
||||||
@@ -37,6 +38,9 @@ from novelwriter.extensions.modified import NDialog
|
|||||||
from novelwriter.extensions.versioninfo import VersionInfoWidget
|
from novelwriter.extensions.versioninfo import VersionInfoWidget
|
||||||
from novelwriter.types import QtAlignRightTop, QtDialogClose, QtHexArgb
|
from novelwriter.types import QtAlignRightTop, QtDialogClose, QtHexArgb
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from PyQt6.QtGui import QCloseEvent
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -131,11 +131,11 @@ class GuiDocMerge(NDialog):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def getData(cls, parent: QWidget, handle: str, items: list[str]) -> tuple[dict, bool]:
|
def getData(cls, parent: QWidget, handle: str, items: list[str]) -> tuple[dict, bool]:
|
||||||
"""Pop the dialog and return the result."""
|
"""Pop the dialog and return the result."""
|
||||||
cls = GuiDocMerge(parent, handle, items)
|
dialog = cls(parent, handle, items)
|
||||||
cls.exec()
|
dialog.exec()
|
||||||
data = cls.data()
|
data = dialog.data()
|
||||||
accepted = cls.result() == QtAccepted
|
accepted = dialog.result() == QtAccepted
|
||||||
cls.softDelete()
|
dialog.softDelete()
|
||||||
return data, accepted
|
return data, accepted
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -178,11 +178,11 @@ class GuiDocSplit(NDialog):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def getData(cls, parent: QWidget, handle: str) -> tuple[dict, list[str], bool]:
|
def getData(cls, parent: QWidget, handle: str) -> tuple[dict, list[str], bool]:
|
||||||
"""Pop the dialog and return the result."""
|
"""Pop the dialog and return the result."""
|
||||||
cls = GuiDocSplit(parent, handle)
|
dialog = cls(parent, handle)
|
||||||
cls.exec()
|
dialog.exec()
|
||||||
data, text = cls.data()
|
data, text = dialog.data()
|
||||||
accepted = cls.result() == QtAccepted
|
accepted = dialog.result() == QtAccepted
|
||||||
cls.softDelete()
|
dialog.softDelete()
|
||||||
return data, text, accepted
|
return data, text, accepted
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -82,9 +82,9 @@ class GuiEditLabel(NDialog):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def getLabel(cls, parent: QWidget, text: str) -> tuple[str, bool]:
|
def getLabel(cls, parent: QWidget, text: str) -> tuple[str, bool]:
|
||||||
"""Pop the dialog and return the result."""
|
"""Pop the dialog and return the result."""
|
||||||
cls = GuiEditLabel(parent, text=text)
|
dialog = cls(parent, text=text)
|
||||||
cls.exec()
|
dialog.exec()
|
||||||
label = cls.itemLabel
|
label = dialog.itemLabel
|
||||||
accepted = cls.result() == QtAccepted
|
accepted = dialog.result() == QtAccepted
|
||||||
cls.softDelete()
|
dialog.softDelete()
|
||||||
return label, accepted
|
return label, accepted
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ class GuiPreferences(NDialog):
|
|||||||
self.textWidth.setSingleStep(10)
|
self.textWidth.setSingleStep(10)
|
||||||
self.textWidth.setValue(CONFIG.textWidth)
|
self.textWidth.setValue(CONFIG.textWidth)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Maximum text width in \"Normal Mode\""), self.textWidth,
|
self.tr('Maximum text width in "Normal Mode"'), self.textWidth,
|
||||||
self.tr("Set to 0 to disable this feature."), unit=self.tr("px")
|
self.tr("Set to 0 to disable this feature."), unit=self.tr("px")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ class GuiPreferences(NDialog):
|
|||||||
self.focusWidth.setSingleStep(10)
|
self.focusWidth.setSingleStep(10)
|
||||||
self.focusWidth.setValue(CONFIG.focusWidth)
|
self.focusWidth.setValue(CONFIG.focusWidth)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Maximum text width in \"Focus Mode\""), self.focusWidth,
|
self.tr('Maximum text width in "Focus Mode"'), self.focusWidth,
|
||||||
self.tr("The maximum width cannot be disabled."), unit=self.tr("px")
|
self.tr("The maximum width cannot be disabled."), unit=self.tr("px")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -451,7 +451,7 @@ class GuiPreferences(NDialog):
|
|||||||
self.hideFocusFooter = NSwitch(self)
|
self.hideFocusFooter = NSwitch(self)
|
||||||
self.hideFocusFooter.setChecked(CONFIG.hideFocusFooter)
|
self.hideFocusFooter.setChecked(CONFIG.hideFocusFooter)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Hide document footer in \"Focus Mode\""), self.hideFocusFooter,
|
self.tr('Hide document footer in "Focus Mode"'), self.hideFocusFooter,
|
||||||
self.tr("Hide the information bar in the document editor.")
|
self.tr("Hide the information bar in the document editor.")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -122,11 +122,11 @@ class GuiQuoteSelect(NDialog):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def getQuote(cls, parent: QWidget, current: str = "") -> tuple[str, bool]:
|
def getQuote(cls, parent: QWidget, current: str = "") -> tuple[str, bool]:
|
||||||
"""Pop the dialog and return the result."""
|
"""Pop the dialog and return the result."""
|
||||||
cls = GuiQuoteSelect(parent, current=current)
|
dialog = cls(parent, current=current)
|
||||||
cls.exec()
|
dialog.exec()
|
||||||
quote = cls._selected
|
quote = dialog._selected
|
||||||
accepted = cls.result() == QtAccepted
|
accepted = dialog.result() == QtAccepted
|
||||||
cls.softDelete()
|
dialog.softDelete()
|
||||||
return quote, accepted
|
return quote, accepted
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||||
from PyQt6.QtGui import QCloseEvent
|
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QAbstractItemView, QApplication, QDialogButtonBox, QFileDialog,
|
QAbstractItemView, QApplication, QDialogButtonBox, QFileDialog,
|
||||||
QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout, QWidget
|
QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout, QWidget
|
||||||
@@ -41,6 +41,9 @@ from novelwriter.extensions.configlayout import NColorLabel
|
|||||||
from novelwriter.extensions.modified import NDialog, NIconToolButton
|
from novelwriter.extensions.modified import NDialog, NIconToolButton
|
||||||
from novelwriter.types import QtDialogClose, QtDialogSave
|
from novelwriter.types import QtDialogClose, QtDialogSave
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from PyQt6.QtGui import QCloseEvent
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -242,8 +245,7 @@ class GuiWordList(NDialog):
|
|||||||
|
|
||||||
def _listWords(self) -> list[str]:
|
def _listWords(self) -> list[str]:
|
||||||
"""List all words in the list box."""
|
"""List all words in the list box."""
|
||||||
result = []
|
return [
|
||||||
for i in range(self.listBox.count()):
|
word for i in range(self.listBox.count())
|
||||||
if (item := self.listBox.item(i)) and (word := item.text().strip()):
|
if (item := self.listBox.item(i)) and (word := item.text().strip())
|
||||||
result.append(word)
|
]
|
||||||
return result
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from PyQt6.QtWidgets import (
|
|||||||
QPlainTextEdit, QStyle, QWidget
|
QPlainTextEdit, QStyle, QWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -46,7 +46,7 @@ def logException() -> None:
|
|||||||
"""Log the content of an exception message."""
|
"""Log the content of an exception message."""
|
||||||
exType, exValue, _ = sys.exc_info()
|
exType, exValue, _ = sys.exc_info()
|
||||||
if exType is not None:
|
if exType is not None:
|
||||||
logger.error(f"{exType.__name__}: {str(exValue)}", stacklevel=2)
|
logger.error(f"{exType.__name__}: {exValue!s}", stacklevel=2)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ def formatException(exc: BaseException) -> str:
|
|||||||
"""Format an exception as a string the same way the default
|
"""Format an exception as a string the same way the default
|
||||||
exception handler does.
|
exception handler does.
|
||||||
"""
|
"""
|
||||||
return f"{type(exc).__name__}: {str(exc)}"
|
return f"{type(exc).__name__}: {exc!s}"
|
||||||
|
|
||||||
|
|
||||||
class NWErrorMessage(QDialog):
|
class NWErrorMessage(QDialog):
|
||||||
@@ -151,7 +151,7 @@ class NWErrorMessage(QDialog):
|
|||||||
f"Python: {sys.version.split()[0]} ({sys.hexversion:#x})\n"
|
f"Python: {sys.version.split()[0]} ({sys.hexversion:#x})\n"
|
||||||
f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n"
|
f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n"
|
||||||
f"enchant: {enchantVersion}\n\n"
|
f"enchant: {enchantVersion}\n\n"
|
||||||
f"{exType.__name__}:\n{str(exValue)}\n\n"
|
f"{exType.__name__}:\n{exValue!s}\n\n"
|
||||||
f"Traceback:\n{txtTrace}\n"
|
f"Traceback:\n{txtTrace}\n"
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -24,9 +24,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QEvent, QObject
|
from PyQt6.QtCore import QEvent, QObject
|
||||||
from PyQt6.QtGui import QStatusTipEvent, QWheelEvent
|
from PyQt6.QtGui import QStatusTipEvent, QWheelEvent
|
||||||
from PyQt6.QtWidgets import QWidget
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from PyQt6.QtWidgets import QWidget
|
||||||
|
|
||||||
|
|
||||||
class WheelEventFilter(QObject):
|
class WheelEventFilter(QObject):
|
||||||
@@ -40,7 +44,7 @@ class WheelEventFilter(QObject):
|
|||||||
Reference: https://stackoverflow.com/a/17739995/5825851
|
Reference: https://stackoverflow.com/a/17739995/5825851
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ("_parent", "_locked")
|
__slots__ = ("_locked", "_parent")
|
||||||
|
|
||||||
def __init__(self, parent: QWidget) -> None:
|
def __init__(self, parent: QWidget) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|||||||
@@ -27,11 +27,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot
|
||||||
from PyQt6.QtGui import QMouseEvent, QWheelEvent
|
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QSpinBox,
|
QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QSpinBox,
|
||||||
QToolButton, QTreeView, QWidget
|
QToolButton, QTreeView, QWidget
|
||||||
@@ -40,7 +38,11 @@ from PyQt6.QtWidgets import (
|
|||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.types import QtMouseLeft, QtMouseMiddle
|
from novelwriter.types import QtMouseLeft, QtMouseMiddle
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from PyQt6.QtGui import QMouseEvent, QWheelEvent
|
||||||
|
|
||||||
from novelwriter.guimain import GuiMain
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class NPagedSideBar(QToolBar):
|
|||||||
|
|
||||||
class _PagedToolButton(QToolButton):
|
class _PagedToolButton(QToolButton):
|
||||||
|
|
||||||
__slots__ = ("_bH", "_tM", "_aH")
|
__slots__ = ("_aH", "_bH", "_tM")
|
||||||
|
|
||||||
def __init__(self, parent: QWidget) -> None:
|
def __init__(self, parent: QWidget) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ class NProgressCircle(QProgressBar):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_text", "_point", "_dRect", "_cRect", "_dPen", "_dBrush",
|
"_bPen", "_cPen", "_cRect", "_dBrush", "_dPen", "_dRect", "_point",
|
||||||
"_cPen", "_bPen", "_tColor"
|
"_tColor", "_text",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, parent: QWidget, size: int, point: int) -> None:
|
def __init__(self, parent: QWidget, size: int, point: int) -> None:
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class StatusLED(QAbstractButton):
|
class StatusLED(QAbstractButton):
|
||||||
|
|
||||||
__slots__ = ("_neutral", "_postitve", "_negative", "_color", "_state")
|
__slots__ = ("_color", "_negative", "_neutral", "_postitve", "_state")
|
||||||
|
|
||||||
def __init__(self, sW: int, sH: int, parent: QWidget | None = None) -> None:
|
def __init__(self, sW: int, sH: int, parent: QWidget | None = None) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from novelwriter.types import QtMouseLeft, QtNoPen, QtPaintAntiAlias, QtSizeFixe
|
|||||||
|
|
||||||
class NSwitch(QAbstractButton):
|
class NSwitch(QAbstractButton):
|
||||||
|
|
||||||
__slots__ = ("_xW", "_xH", "_xR", "_rH", "_rR", "_offset")
|
__slots__ = ("_offset", "_rH", "_rR", "_xH", "_xR", "_xW")
|
||||||
|
|
||||||
def __init__(self, parent: QWidget, height: int = 0) -> None:
|
def __init__(self, parent: QWidget, height: int = 0) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import pyqtSignal
|
from PyQt6.QtCore import pyqtSignal
|
||||||
from PyQt6.QtGui import QIcon
|
|
||||||
from PyQt6.QtWidgets import QGridLayout, QLabel, QScrollArea, QWidget
|
from PyQt6.QtWidgets import QGridLayout, QLabel, QScrollArea, QWidget
|
||||||
|
|
||||||
from novelwriter.extensions.switch import NSwitch
|
from novelwriter.extensions.switch import NSwitch
|
||||||
@@ -33,6 +34,9 @@ from novelwriter.types import (
|
|||||||
QtSizeMinimumExpanding
|
QtSizeMinimumExpanding
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from PyQt6.QtGui import QIcon
|
||||||
|
|
||||||
|
|
||||||
class NSwitchBox(QScrollArea):
|
class NSwitchBox(QScrollArea):
|
||||||
"""Extension: Switch Box Widget
|
"""Extension: Switch Box Widget
|
||||||
|
|||||||
@@ -29,21 +29,25 @@ import re
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from typing import TYPE_CHECKING, NamedTuple
|
||||||
from typing import NamedTuple
|
|
||||||
from zipfile import ZIP_DEFLATED, ZipFile
|
from zipfile import ZIP_DEFLATED, ZipFile
|
||||||
|
|
||||||
from PyQt6.QtCore import QMargins, QSize
|
from PyQt6.QtCore import QMargins, QSize
|
||||||
from PyQt6.QtGui import QColor
|
|
||||||
|
|
||||||
from novelwriter import __version__
|
from novelwriter import __version__
|
||||||
from novelwriter.common import firstFloat, xmlElement, xmlSubElem
|
from novelwriter.common import firstFloat, xmlElement, xmlSubElem
|
||||||
from novelwriter.constants import nwHeadFmt, nwStyles
|
from novelwriter.constants import nwHeadFmt, nwStyles
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
||||||
from novelwriter.formats.tokenizer import Tokenizer
|
from novelwriter.formats.tokenizer import Tokenizer
|
||||||
from novelwriter.types import QtHexRgb
|
from novelwriter.types import QtHexRgb
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PyQt6.QtGui import QColor
|
||||||
|
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# RegEx
|
# RegEx
|
||||||
@@ -1052,9 +1056,9 @@ class ToDocX(Tokenizer):
|
|||||||
class DocXParagraph:
|
class DocXParagraph:
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_content", "_style", "_textAlign",
|
"_bottomMargin", "_breakAfter", "_breakBefore", "_content",
|
||||||
"_topMargin", "_bottomMargin", "_leftMargin", "_rightMargin",
|
"_footnoteRef", "_indentFirst", "_leftMargin", "_rightMargin",
|
||||||
"_indentFirst", "_breakBefore", "_breakAfter", "_footnoteRef",
|
"_style", "_textAlign", "_topMargin",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|||||||
@@ -26,16 +26,20 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from novelwriter.common import formatTimeStamp
|
from novelwriter.common import formatTimeStamp
|
||||||
from novelwriter.constants import nwHtmlUnicode, nwStyles
|
from novelwriter.constants import nwHtmlUnicode, nwStyles
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, stripEscape
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, stripEscape
|
||||||
from novelwriter.formats.tokenizer import Tokenizer
|
from novelwriter.formats.tokenizer import Tokenizer
|
||||||
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS, QtHexRgb
|
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS, QtHexRgb
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Each opener tag, with the id of its corresponding closer and tag format
|
# Each opener tag, with the id of its corresponding closer and tag format
|
||||||
@@ -311,10 +315,8 @@ class ToHtml(Tokenizer):
|
|||||||
|
|
||||||
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
|
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
|
||||||
"""Replace tabs with spaces in the html."""
|
"""Replace tabs with spaces in the html."""
|
||||||
pages = []
|
|
||||||
tabSpace = spaceChar*nSpaces
|
tabSpace = spaceChar*nSpaces
|
||||||
for aLine in self._pages:
|
pages = [aLine.replace("\t", tabSpace) for aLine in self._pages]
|
||||||
pages.append(aLine.replace("\t", tabSpace))
|
|
||||||
self._pages = pages
|
self._pages = pages
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pathlib import Path
|
from typing import TYPE_CHECKING, NamedTuple
|
||||||
from typing import NamedTuple
|
|
||||||
|
|
||||||
from PyQt6.QtCore import QLocale
|
from PyQt6.QtCore import QLocale
|
||||||
from PyQt6.QtGui import QColor, QFont
|
from PyQt6.QtGui import QColor, QFont
|
||||||
@@ -40,7 +39,6 @@ from novelwriter.constants import (
|
|||||||
nwHeadFmt, nwKeyWords, nwLabels, nwShortcode, nwStats, nwStyles, nwUnicode,
|
nwHeadFmt, nwKeyWords, nwLabels, nwShortcode, nwStats, nwStyles, nwUnicode,
|
||||||
trConst
|
trConst
|
||||||
)
|
)
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.enum import nwComment, nwItemLayout
|
from novelwriter.enum import nwComment, nwItemLayout
|
||||||
from novelwriter.formats.shared import (
|
from novelwriter.formats.shared import (
|
||||||
BlockFmt, BlockTyp, T_Block, T_Formats, T_Note, TextDocumentTheme, TextFmt
|
BlockFmt, BlockTyp, T_Block, T_Formats, T_Note, TextDocumentTheme, TextFmt
|
||||||
@@ -48,6 +46,11 @@ from novelwriter.formats.shared import (
|
|||||||
from novelwriter.text.comments import processComment
|
from novelwriter.text.comments import processComment
|
||||||
from novelwriter.text.patterns import REGEX_PATTERNS, DialogParser
|
from novelwriter.text.patterns import REGEX_PATTERNS, DialogParser
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +75,7 @@ HEADINGS = [
|
|||||||
BlockTyp.TITLE, BlockTyp.PART, BlockTyp.HEAD1,
|
BlockTyp.TITLE, BlockTyp.PART, BlockTyp.HEAD1,
|
||||||
BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4,
|
BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4,
|
||||||
]
|
]
|
||||||
SKIP_INDENT = HEADINGS + [BlockTyp.SEP, BlockTyp.SKIP]
|
SKIP_INDENT = [*HEADINGS, BlockTyp.SEP, BlockTyp.SKIP]
|
||||||
B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE)
|
B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE)
|
||||||
|
|
||||||
|
|
||||||
@@ -651,7 +654,7 @@ class Tokenizer(ABC):
|
|||||||
tText = aLine[2:].strip()
|
tText = aLine[2:].strip()
|
||||||
tType = BlockTyp.HEAD1 if isPlain else BlockTyp.TITLE
|
tType = BlockTyp.HEAD1 if isPlain else BlockTyp.TITLE
|
||||||
sHide = self._hidePart if isPlain else False
|
sHide = self._hidePart if isPlain else False
|
||||||
if not (isPlain or isNovel and sHide):
|
if not (isPlain or (isNovel and sHide)):
|
||||||
tStyle |= self._titleStyle
|
tStyle |= self._titleStyle
|
||||||
if isNovel:
|
if isNovel:
|
||||||
tType = BlockTyp.PART if isPlain else BlockTyp.TITLE
|
tType = BlockTyp.PART if isPlain else BlockTyp.TITLE
|
||||||
@@ -1124,12 +1127,10 @@ class Tokenizer(ABC):
|
|||||||
temp.append((res.end(0), 0, TextFmt.HRF_E, ""))
|
temp.append((res.end(0), 0, TextFmt.HRF_E, ""))
|
||||||
|
|
||||||
# Match Shortcodes
|
# Match Shortcodes
|
||||||
for res in REGEX_PATTERNS.shortcodePlain.finditer(text):
|
temp.extend(
|
||||||
temp.append((
|
(res.start(1), res.end(1), self._shortCodeFmt.get(res.group(1).lower(), 0), "")
|
||||||
res.start(1), res.end(1),
|
for res in REGEX_PATTERNS.shortcodePlain.finditer(text)
|
||||||
self._shortCodeFmt.get(res.group(1).lower(), 0),
|
)
|
||||||
"",
|
|
||||||
))
|
|
||||||
|
|
||||||
# Match Shortcode w/Values
|
# Match Shortcode w/Values
|
||||||
tHandle = self._handle or ""
|
tHandle = self._handle or ""
|
||||||
|
|||||||
@@ -25,13 +25,17 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pathlib import Path
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from novelwriter.constants import nwUnicode
|
from novelwriter.constants import nwUnicode
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
||||||
from novelwriter.formats.tokenizer import Tokenizer
|
from novelwriter.formats.tokenizer import Tokenizer
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,9 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from collections.abc import Sequence
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from pathlib import Path
|
from typing import TYPE_CHECKING, Final
|
||||||
from zipfile import ZIP_DEFLATED, ZipFile
|
from zipfile import ZIP_DEFLATED, ZipFile
|
||||||
|
|
||||||
from PyQt6.QtGui import QColor, QFont
|
from PyQt6.QtGui import QColor, QFont
|
||||||
@@ -40,11 +39,16 @@ from PyQt6.QtGui import QColor, QFont
|
|||||||
from novelwriter import __version__
|
from novelwriter import __version__
|
||||||
from novelwriter.common import xmlElement, xmlIndent, xmlSubElem
|
from novelwriter.common import xmlElement, xmlIndent, xmlSubElem
|
||||||
from novelwriter.constants import nwHeadFmt, nwStyles
|
from novelwriter.constants import nwHeadFmt, nwStyles
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt, stripEscape
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt, stripEscape
|
||||||
from novelwriter.formats.tokenizer import Tokenizer
|
from novelwriter.formats.tokenizer import Tokenizer
|
||||||
from novelwriter.types import FONT_STYLE, QtHexRgb
|
from novelwriter.types import FONT_STYLE, QtHexRgb
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Sequence
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Main XML NameSpaces
|
# Main XML NameSpaces
|
||||||
@@ -1000,11 +1004,11 @@ class ODTParagraphStyle:
|
|||||||
exporter. Only the used settings are exposed here to keep the class
|
exporter. Only the used settings are exposed here to keep the class
|
||||||
minimal and fast.
|
minimal and fast.
|
||||||
"""
|
"""
|
||||||
VALID_ALIGN = ["start", "center", "end", "justify", "inside", "outside", "left", "right"]
|
VALID_ALIGN: Final[list[str]] = ["start", "center", "end", "justify", "left", "right"]
|
||||||
VALID_BREAK = ["auto", "column", "page", "even-page", "odd-page", "inherit"]
|
VALID_BREAK: Final[list[str]] = ["auto", "page", "even-page", "odd-page", "inherit"]
|
||||||
VALID_LEVEL = ["1", "2", "3", "4"]
|
VALID_LEVEL: Final[list[str]] = ["1", "2", "3", "4"]
|
||||||
VALID_CLASS = ["text", "chapter", "extra"]
|
VALID_CLASS: Final[list[str]] = ["text", "chapter", "extra"]
|
||||||
VALID_WEIGHT = ["normal", "bold"] + FONT_WEIGHT_NUM
|
VALID_WEIGHT: Final[list[str]] = ["normal", "bold", *FONT_WEIGHT_NUM]
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
|
|
||||||
@@ -1207,9 +1211,9 @@ class ODTParagraphStyle:
|
|||||||
def getID(self) -> str:
|
def getID(self) -> str:
|
||||||
"""Generate a unique ID from the settings."""
|
"""Generate a unique ID from the settings."""
|
||||||
return sha256((
|
return sha256((
|
||||||
f"Paragraph:Main:{str(self._mAttr)}:"
|
f"Paragraph:Main:{self._mAttr!s}:"
|
||||||
f"Paragraph:Para:{str(self._pAttr)}:"
|
f"Paragraph:Para:{self._pAttr!s}:"
|
||||||
f"Paragraph:Text:{str(self._tAttr)}:"
|
f"Paragraph:Text:{self._tAttr!s}:"
|
||||||
).encode()).hexdigest()
|
).encode()).hexdigest()
|
||||||
|
|
||||||
def packXML(self, xParent: ET.Element) -> None:
|
def packXML(self, xParent: ET.Element) -> None:
|
||||||
@@ -1237,13 +1241,13 @@ class ODTTextStyle:
|
|||||||
Only the used settings are exposed here to keep the class minimal
|
Only the used settings are exposed here to keep the class minimal
|
||||||
and fast.
|
and fast.
|
||||||
"""
|
"""
|
||||||
VALID_WEIGHT = ["normal", "bold"] + FONT_WEIGHT_NUM
|
VALID_WEIGHT: Final[list[str]] = ["normal", "bold", *FONT_WEIGHT_NUM]
|
||||||
VALID_STYLE = ["normal", "italic", "oblique"]
|
VALID_STYLE: Final[list[str]] = ["normal", "italic", "oblique"]
|
||||||
VALID_POS = ["super", "sub"]
|
VALID_POS: Final[list[str]] = ["super", "sub"]
|
||||||
VALID_LSTYLE = ["none", "solid"]
|
VALID_LSTYLE: Final[list[str]] = ["none", "solid"]
|
||||||
VALID_LTYPE = ["single", "double"]
|
VALID_LTYPE: Final[list[str]] = ["single", "double"]
|
||||||
VALID_LWIDTH = ["auto"]
|
VALID_LWIDTH: Final[list[str]] = ["auto"]
|
||||||
VALID_LCOL = ["font-color"]
|
VALID_LCOL: Final[list[str]] = ["font-color"]
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pathlib import Path
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QMarginsF, QSizeF
|
from PyQt6.QtCore import QMarginsF, QSizeF
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
@@ -36,7 +36,6 @@ from PyQt6.QtPrintSupport import QPrinter
|
|||||||
|
|
||||||
from novelwriter import __version__
|
from novelwriter import __version__
|
||||||
from novelwriter.constants import nwStyles, nwUnicode
|
from novelwriter.constants import nwStyles, nwUnicode
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
||||||
from novelwriter.formats.tokenizer import HEADINGS, Tokenizer
|
from novelwriter.formats.tokenizer import HEADINGS, Tokenizer
|
||||||
from novelwriter.types import (
|
from novelwriter.types import (
|
||||||
@@ -45,6 +44,11 @@ from novelwriter.types import (
|
|||||||
QtPropLineHeight, QtTransparent, QtVAlignNormal, QtVAlignSub, QtVAlignSuper
|
QtPropLineHeight, QtTransparent, QtVAlignNormal, QtVAlignSub, QtVAlignSuper
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
T_TextStyle = tuple[QTextBlockFormat, QTextCharFormat]
|
T_TextStyle = tuple[QTextBlockFormat, QTextCharFormat]
|
||||||
|
|||||||
@@ -26,13 +26,17 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from novelwriter.common import formatTimeStamp
|
from novelwriter.common import formatTimeStamp
|
||||||
from novelwriter.core.project import NWProject
|
|
||||||
from novelwriter.formats.tokenizer import Tokenizer
|
from novelwriter.formats.tokenizer import Tokenizer
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -97,10 +97,10 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
"""Gui Widget: Main Document Editor"""
|
"""Gui Widget: Main Document Editor"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_nwDocument", "_nwItem", "_docChanged", "_docHandle", "_vpMargin",
|
"_autoReplace", "_completer", "_doReplace", "_docChanged", "_docHandle", "_followTag1",
|
||||||
"_lastEdit", "_lastActive", "_lastFind", "_doReplace", "_autoReplace",
|
"_followTag2", "_keyContext", "_lastActive", "_lastEdit", "_lastFind", "_nwDocument",
|
||||||
"_completer", "_qDocument", "_keyContext", "_followTag1", "_followTag2",
|
"_nwItem", "_qDocument", "_timerDoc", "_timerSel", "_vpMargin", "_wCounterDoc",
|
||||||
"_timerDoc", "_wCounterDoc", "_timerSel", "_wCounterSel",
|
"_wCounterSel",
|
||||||
)
|
)
|
||||||
|
|
||||||
MOVE_KEYS = (
|
MOVE_KEYS = (
|
||||||
@@ -760,7 +760,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
elif action == nwDocAction.REPL_SNG:
|
elif action == nwDocAction.REPL_SNG:
|
||||||
self._replaceQuotes("'", CONFIG.fmtSQuoteOpen, CONFIG.fmtSQuoteClose)
|
self._replaceQuotes("'", CONFIG.fmtSQuoteOpen, CONFIG.fmtSQuoteClose)
|
||||||
elif action == nwDocAction.REPL_DBL:
|
elif action == nwDocAction.REPL_DBL:
|
||||||
self._replaceQuotes("\"", CONFIG.fmtDQuoteOpen, CONFIG.fmtDQuoteClose)
|
self._replaceQuotes('"', CONFIG.fmtDQuoteOpen, CONFIG.fmtDQuoteClose)
|
||||||
elif action == nwDocAction.RM_BREAKS:
|
elif action == nwDocAction.RM_BREAKS:
|
||||||
self._removeInParLineBreaks()
|
self._removeInParLineBreaks()
|
||||||
elif action == nwDocAction.ALIGN_L:
|
elif action == nwDocAction.ALIGN_L:
|
||||||
@@ -2003,7 +2003,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
sPos = cPos - i - 1
|
sPos = cPos - i - 1
|
||||||
cOne = str(self._qDocument.characterAt(sPos))
|
cOne = str(self._qDocument.characterAt(sPos))
|
||||||
cTwo = str(self._qDocument.characterAt(sPos - 1))
|
cTwo = str(self._qDocument.characterAt(sPos - 1))
|
||||||
if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
|
if not (cOne.isalnum() or (cOne in apos and cTwo.isalnum())):
|
||||||
sPos += 1
|
sPos += 1
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -2013,7 +2013,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
ePos = cPos + i
|
ePos = cPos + i
|
||||||
cOne = str(self._qDocument.characterAt(ePos))
|
cOne = str(self._qDocument.characterAt(ePos))
|
||||||
cTwo = str(self._qDocument.characterAt(ePos + 1))
|
cTwo = str(self._qDocument.characterAt(ePos + 1))
|
||||||
if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
|
if not (cOne.isalnum() or (cOne in apos and cTwo.isalnum())):
|
||||||
break
|
break
|
||||||
|
|
||||||
if ePos - sPos <= 0:
|
if ePos - sPos <= 0:
|
||||||
@@ -2193,9 +2193,9 @@ class BackgroundWordCounterSignals(QObject):
|
|||||||
class TextAutoReplace:
|
class TextAutoReplace:
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_quoteSO", "_quoteSC", "_quoteDO", "_quoteDC",
|
"_doPadAfter", "_doPadBefore", "_padAfter", "_padBefore", "_padChar",
|
||||||
"_replaceSQuote", "_replaceDQuote", "_replaceDash", "_replaceDots",
|
"_quoteDC", "_quoteDO", "_quoteSC", "_quoteSO", "_replaceDQuote",
|
||||||
"_padChar", "_padBefore", "_padAfter", "_doPadBefore", "_doPadAfter",
|
"_replaceDash", "_replaceDots", "_replaceSQuote",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ BLOCK_TITLE = 4
|
|||||||
class GuiDocHighlighter(QSyntaxHighlighter):
|
class GuiDocHighlighter(QSyntaxHighlighter):
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_tHandle", "_isNovel", "_isInactive", "_spellCheck", "_spellErr",
|
"_cmnRules", "_dialogParser", "_hStyles", "_isInactive", "_isNovel",
|
||||||
"_hStyles", "_minRules", "_txtRules", "_cmnRules", "_dialogParser",
|
"_minRules", "_spellCheck", "_spellErr", "_tHandle", "_txtRules",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, document: QTextDocument) -> None:
|
def __init__(self, document: QTextDocument) -> None:
|
||||||
@@ -466,7 +466,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|||||||
|
|
||||||
class TextBlockData(QTextBlockUserData):
|
class TextBlockData(QTextBlockUserData):
|
||||||
|
|
||||||
__slots__ = ("_text", "_offset", "_metaData", "_spellErrors")
|
__slots__ = ("_metaData", "_offset", "_spellErrors", "_text")
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QModelIndex, Qt, pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import QModelIndex, Qt, pyqtSignal, pyqtSlot
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
@@ -36,12 +37,14 @@ from PyQt6.QtWidgets import (
|
|||||||
from novelwriter import SHARED
|
from novelwriter import SHARED
|
||||||
from novelwriter.common import checkInt, qtAddAction
|
from novelwriter.common import checkInt, qtAddAction
|
||||||
from novelwriter.constants import nwLabels, nwLists, nwStyles, trConst
|
from novelwriter.constants import nwLabels, nwLists, nwStyles, trConst
|
||||||
from novelwriter.core.indexdata import IndexHeading, IndexNode
|
|
||||||
from novelwriter.enum import nwChange, nwDocMode, nwItemClass
|
from novelwriter.enum import nwChange, nwDocMode, nwItemClass
|
||||||
from novelwriter.extensions.modified import NIconToolButton
|
from novelwriter.extensions.modified import NIconToolButton
|
||||||
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
|
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
|
||||||
from novelwriter.types import QtDecoration, QtHeaderFixed, QtHeaderToContents, QtUserRole
|
from novelwriter.types import QtDecoration, QtHeaderFixed, QtHeaderToContents, QtUserRole
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from novelwriter.core.indexdata import IndexHeading, IndexNode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from collections.abc import Iterable
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QObject, pyqtSlot
|
from PyQt6.QtCore import QObject, pyqtSlot
|
||||||
from PyQt6.QtGui import QTextBlock, QTextCursor, QTextDocument
|
from PyQt6.QtGui import QTextBlock, QTextCursor, QTextDocument
|
||||||
@@ -35,6 +35,9 @@ from PyQt6.QtWidgets import QApplication, QPlainTextDocumentLayout
|
|||||||
from novelwriter import SHARED
|
from novelwriter import SHARED
|
||||||
from novelwriter.gui.dochighlight import GuiDocHighlighter, TextBlockData
|
from novelwriter.gui.dochighlight import GuiDocHighlighter, TextBlockData
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ class GuiItemDetails(QWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._handle = tHandle
|
self._handle = tHandle
|
||||||
iPx = int(round(0.9*SHARED.theme.baseIconHeight))
|
iPx = round(0.9*SHARED.theme.baseIconHeight)
|
||||||
|
|
||||||
# Label
|
# Label
|
||||||
# =====
|
# =====
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ from novelwriter.constants import (
|
|||||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwFocus, nwView
|
from novelwriter.enum import nwDocAction, nwDocInsert, nwFocus, nwView
|
||||||
from novelwriter.extensions.eventfilters import StatusTipFilter
|
from novelwriter.extensions.eventfilters import StatusTipFilter
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.guimain import GuiMain
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -471,7 +471,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
|
|
||||||
# Insert > Double Prime
|
# Insert > Double Prime
|
||||||
self.aInsDPrime = qtAddAction(self.mInsPunct, self.tr("Double Prime"))
|
self.aInsDPrime = qtAddAction(self.mInsPunct, self.tr("Double Prime"))
|
||||||
self.aInsDPrime.setShortcut("Ctrl+K, Ctrl+\"")
|
self.aInsDPrime.setShortcut('Ctrl+K, Ctrl+"')
|
||||||
self.aInsDPrime.triggered.connect(
|
self.aInsDPrime.triggered.connect(
|
||||||
lambda: self.requestDocInsertText.emit(nwUnicode.U_DPRIME)
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_DPRIME)
|
||||||
)
|
)
|
||||||
@@ -680,7 +680,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
|
|
||||||
# Format > Double Quotes
|
# Format > Double Quotes
|
||||||
self.aFmtDQuote = qtAddAction(self.fmtMenu, self.tr("Wrap Double Quotes"))
|
self.aFmtDQuote = qtAddAction(self.fmtMenu, self.tr("Wrap Double Quotes"))
|
||||||
self.aFmtDQuote.setShortcut("Ctrl+\"")
|
self.aFmtDQuote.setShortcut('Ctrl+"')
|
||||||
self.aFmtDQuote.triggered.connect(
|
self.aFmtDQuote.triggered.connect(
|
||||||
lambda: self.requestDocAction.emit(nwDocAction.D_QUOTE)
|
lambda: self.requestDocAction.emit(nwDocAction.D_QUOTE)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import logging
|
|||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
from PyQt6.QtCore import QT_TRANSLATE_NOOP, Qt, pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import QT_TRANSLATE_NOOP, Qt, pyqtSignal, pyqtSlot
|
||||||
from PyQt6.QtGui import QAction, QIcon
|
from PyQt6.QtGui import QAction, QIcon
|
||||||
@@ -319,7 +320,7 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
|
|
||||||
class GuiOutlineTree(QTreeWidget):
|
class GuiOutlineTree(QTreeWidget):
|
||||||
|
|
||||||
DEF_WIDTH = {
|
DEF_WIDTH: Final[dict[nwOutline, int]] = {
|
||||||
nwOutline.TITLE: 200,
|
nwOutline.TITLE: 200,
|
||||||
nwOutline.LEVEL: 40,
|
nwOutline.LEVEL: 40,
|
||||||
nwOutline.LABEL: 150,
|
nwOutline.LABEL: 150,
|
||||||
@@ -342,7 +343,7 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
nwOutline.SYNOP: 200,
|
nwOutline.SYNOP: 200,
|
||||||
}
|
}
|
||||||
|
|
||||||
DEF_HIDDEN = {
|
DEF_HIDDEN: Final[dict[nwOutline, bool]] = {
|
||||||
nwOutline.TITLE: False,
|
nwOutline.TITLE: False,
|
||||||
nwOutline.LEVEL: True,
|
nwOutline.LEVEL: True,
|
||||||
nwOutline.LABEL: False,
|
nwOutline.LABEL: False,
|
||||||
@@ -498,7 +499,7 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
tree.
|
tree.
|
||||||
"""
|
"""
|
||||||
# If it's the first time, we always build
|
# If it's the first time, we always build
|
||||||
if self._firstView or self._firstView and overRide:
|
if self._firstView or (self._firstView and overRide):
|
||||||
self._loadHeaderState()
|
self._loadHeaderState()
|
||||||
self._populateTree(rootHandle)
|
self._populateTree(rootHandle)
|
||||||
self._firstView = False
|
self._firstView = False
|
||||||
@@ -558,7 +559,7 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
logger.info("Writing CSV file: %s", path)
|
logger.info("Writing CSV file: %s", path)
|
||||||
cols = [col for col in self._treeOrder if not self._colHidden[col]]
|
cols = [col for col in self._treeOrder if not self._colHidden[col]]
|
||||||
order = [self._colIdx[col] for col in cols]
|
order = [self._colIdx[col] for col in cols]
|
||||||
with open(path, mode="w", newline="") as csvFile:
|
with open(path, mode="w", newline="", encoding="utf-8") as csvFile:
|
||||||
writer = csv.writer(csvFile, dialect="excel", quoting=csv.QUOTE_ALL)
|
writer = csv.writer(csvFile, dialect="excel", quoting=csv.QUOTE_ALL)
|
||||||
writer.writerow([trConst(nwLabels.OUTLINE_COLS[col]) for col in cols])
|
writer.writerow([trConst(nwLabels.OUTLINE_COLS[col]) for col in cols])
|
||||||
for i in range(self.topLevelItemCount()):
|
for i in range(self.topLevelItemCount()):
|
||||||
@@ -795,7 +796,7 @@ class GuiOutlineHeaderMenu(QMenu):
|
|||||||
|
|
||||||
class GuiOutlineDetails(QScrollArea):
|
class GuiOutlineDetails(QScrollArea):
|
||||||
|
|
||||||
LVL_MAP = {
|
LVL_MAP: Final[dict[str, str]] = {
|
||||||
"H1": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"),
|
"H1": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Title"),
|
||||||
"H2": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"),
|
"H2": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Chapter"),
|
||||||
"H3": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"),
|
"H3": QT_TRANSLATE_NOOP("GuiOutlineDetails", "Scene"),
|
||||||
|
|||||||
@@ -1099,7 +1099,7 @@ class _UpdatableMenu(QMenu):
|
|||||||
|
|
||||||
class _TreeContextMenu(QMenu):
|
class _TreeContextMenu(QMenu):
|
||||||
|
|
||||||
__slots__ = ("_tree", "_view", "_node", "_item", "_model", "_handle", "_indices", "_children")
|
__slots__ = ("_children", "_handle", "_indices", "_item", "_model", "_node", "_tree", "_view")
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, projTree: GuiProjectTree, model: ProjectModel,
|
self, projTree: GuiProjectTree, model: ProjectModel,
|
||||||
@@ -1327,7 +1327,7 @@ class _TreeContextMenu(QMenu):
|
|||||||
"""Add move to Trash action."""
|
"""Add move to Trash action."""
|
||||||
if (
|
if (
|
||||||
self._model.trashSelection(self._indices)
|
self._model.trashSelection(self._indices)
|
||||||
or len(self._indices) == 1 and self._item.isRootType()
|
or (len(self._indices) == 1 and self._item.isRootType())
|
||||||
):
|
):
|
||||||
text = self.tr("Delete Permanently")
|
text = self.tr("Delete Permanently")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||||
from PyQt6.QtGui import QAction, QCursor, QKeyEvent, QPalette
|
from PyQt6.QtGui import QAction, QCursor, QKeyEvent, QPalette
|
||||||
@@ -37,12 +38,14 @@ from PyQt6.QtWidgets import (
|
|||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import checkInt, qtAddAction
|
from novelwriter.common import checkInt, qtAddAction
|
||||||
from novelwriter.core.coretools import DocSearch
|
from novelwriter.core.coretools import DocSearch
|
||||||
from novelwriter.core.item import NWItem
|
|
||||||
from novelwriter.types import (
|
from novelwriter.types import (
|
||||||
QtAlignMiddle, QtAlignRight, QtHeaderStretch, QtHeaderToContents,
|
QtAlignMiddle, QtAlignRight, QtHeaderStretch, QtHeaderToContents,
|
||||||
QtHexArgb, QtUserRole
|
QtHexArgb, QtUserRole
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from novelwriter.core.item import NWItem
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from novelwriter.extensions.eventfilters import StatusTipFilter
|
|||||||
from novelwriter.extensions.modified import NIconToolButton
|
from novelwriter.extensions.modified import NIconToolButton
|
||||||
from novelwriter.gui.theme import STYLES_BIG_TOOLBUTTON
|
from novelwriter.gui.theme import STYLES_BIG_TOOLBUTTON
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.guimain import GuiMain
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
+20
-22
@@ -27,7 +27,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from pathlib import Path
|
from typing import TYPE_CHECKING, Final
|
||||||
|
|
||||||
from PyQt6.QtCore import QSize, Qt
|
from PyQt6.QtCore import QSize, Qt
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
@@ -44,6 +44,9 @@ from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
|||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
from novelwriter.types import QtBlack, QtHexArgb, QtPaintAntiAlias, QtTransparent
|
from novelwriter.types import QtBlack, QtHexArgb, QtPaintAntiAlias, QtTransparent
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
STYLES_FLAT_TABS = "flatTabWidget"
|
STYLES_FLAT_TABS = "flatTabWidget"
|
||||||
@@ -93,18 +96,13 @@ class GuiTheme:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
# Attributes
|
"_availSyntax", "_availThemes", "_guiPalette", "_styleSheets", "_syntaxList", "_themeList",
|
||||||
"iconCache", "themeMeta", "isDarkTheme", "helpText", "fadedText", "errorText",
|
"baseButtonHeight", "baseIconHeight", "baseIconSize", "buttonIconSize", "errorText",
|
||||||
"syntaxMeta", "syntaxTheme", "guiFont", "guiFontB", "guiFontBU", "guiFontSmall",
|
"fadedText", "fontPixelSize", "fontPointSize", "getDecoration", "getHeaderDecoration",
|
||||||
"fontPointSize", "fontPixelSize", "baseIconHeight", "baseButtonHeight", "textNHeight",
|
"getHeaderDecorationNarrow", "getIcon", "getIconColor", "getItemIcon", "getPixmap",
|
||||||
"textNWidth", "baseIconSize", "buttonIconSize", "guiFontFixed",
|
"getToggleIcon", "guiFont", "guiFontB", "guiFontBU", "guiFontFixed", "guiFontSmall",
|
||||||
|
"helpText", "iconCache", "isDarkTheme", "syntaxMeta", "syntaxTheme", "textNHeight",
|
||||||
# Functions
|
"textNWidth", "themeMeta",
|
||||||
"getIcon", "getPixmap", "getItemIcon", "getIconColor", "getToggleIcon", "getDecoration",
|
|
||||||
"getHeaderDecoration", "getHeaderDecorationNarrow",
|
|
||||||
|
|
||||||
# Internal
|
|
||||||
"_guiPalette", "_themeList", "_syntaxList", "_availThemes", "_availSyntax", "_styleSheets",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@@ -164,9 +162,9 @@ class GuiTheme:
|
|||||||
fHeight = qMetric.height()
|
fHeight = qMetric.height()
|
||||||
fAscent = qMetric.ascent()
|
fAscent = qMetric.ascent()
|
||||||
self.fontPointSize = self.guiFont.pointSizeF()
|
self.fontPointSize = self.guiFont.pointSizeF()
|
||||||
self.fontPixelSize = int(round(fHeight))
|
self.fontPixelSize = round(fHeight)
|
||||||
self.baseIconHeight = int(round(fAscent))
|
self.baseIconHeight = round(fAscent)
|
||||||
self.baseButtonHeight = int(round(1.35*fAscent))
|
self.baseButtonHeight = round(1.35*fAscent)
|
||||||
self.textNHeight = qMetric.boundingRect("N").height()
|
self.textNHeight = qMetric.boundingRect("N").height()
|
||||||
self.textNWidth = qMetric.boundingRect("N").width()
|
self.textNWidth = qMetric.boundingRect("N").width()
|
||||||
|
|
||||||
@@ -202,7 +200,7 @@ class GuiTheme:
|
|||||||
qMetrics = QFontMetrics(font)
|
qMetrics = QFontMetrics(font)
|
||||||
else:
|
else:
|
||||||
qMetrics = QFontMetrics(self.guiFont)
|
qMetrics = QFontMetrics(self.guiFont)
|
||||||
return int(ceil(qMetrics.boundingRect(text).width()))
|
return ceil(qMetrics.boundingRect(text).width())
|
||||||
|
|
||||||
##
|
##
|
||||||
# Theme Methods
|
# Theme Methods
|
||||||
@@ -567,16 +565,16 @@ class GuiIcons:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"mainTheme", "themeMeta", "_svgData", "_svgColors", "_qColors",
|
"_availThemes", "_headerDec", "_headerDecNarrow", "_noIcon",
|
||||||
"_qIcons", "_headerDec", "_headerDecNarrow", "_availThemes",
|
"_qColors", "_qIcons", "_svgColors", "_svgData", "_themeList",
|
||||||
"_themeList", "_noIcon",
|
"mainTheme", "themeMeta",
|
||||||
)
|
)
|
||||||
|
|
||||||
TOGGLE_ICON_KEYS: dict[str, tuple[str, str]] = {
|
TOGGLE_ICON_KEYS: Final[dict[str, tuple[str, str]]] = {
|
||||||
"bullet": ("bullet-on", "bullet-off"),
|
"bullet": ("bullet-on", "bullet-off"),
|
||||||
"unfold": ("unfold-show", "unfold-hide"),
|
"unfold": ("unfold-show", "unfold-hide"),
|
||||||
}
|
}
|
||||||
IMAGE_MAP: dict[str, tuple[str, str]] = {
|
IMAGE_MAP: Final[dict[str, tuple[str, str]]] = {
|
||||||
"welcome": ("welcome-light.jpg", "welcome-dark.jpg"),
|
"welcome": ("welcome-light.jpg", "welcome-dark.jpg"),
|
||||||
"nw-text": ("novelwriter-text-light.svg", "novelwriter-text-dark.svg"),
|
"nw-text": ("novelwriter-text-light.svg", "novelwriter-text-dark.svg"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1009,7 +1009,7 @@ class GuiMain(QMainWindow):
|
|||||||
fN = self.novelView.treeHasFocus()
|
fN = self.novelView.treeHasFocus()
|
||||||
|
|
||||||
self._changeView(nwView.EDITOR)
|
self._changeView(nwView.EDITOR)
|
||||||
if (vM and (vP and fP or vN and not fN)) or (not vM and vN):
|
if (vM and ((vP and fP) or (vN and not fN))) or (not vM and vN):
|
||||||
self._changeView(nwView.NOVEL)
|
self._changeView(nwView.NOVEL)
|
||||||
self.novelView.setTreeFocus()
|
self.novelView.setTreeFocus()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from novelwriter.constants import nwFiles
|
|||||||
from novelwriter.core.spellcheck import NWSpellEnchant
|
from novelwriter.core.spellcheck import NWSpellEnchant
|
||||||
from novelwriter.enum import nwChange, nwItemClass
|
from novelwriter.enum import nwChange, nwItemClass
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.status import T_StatusKind
|
from novelwriter.core.status import T_StatusKind
|
||||||
from novelwriter.gui.theme import GuiTheme
|
from novelwriter.gui.theme import GuiTheme
|
||||||
@@ -54,8 +54,8 @@ NWWidget = TypeVar("NWWidget", bound=QWidget)
|
|||||||
class SharedData(QObject):
|
class SharedData(QObject):
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_gui", "_theme", "_project", "_spelling", "_lockedBy", "_lastAlert",
|
"_gui", "_idleRefTime", "_idleTime", "_lastAlert", "_lockedBy",
|
||||||
"_idleTime", "_idleRefTime",
|
"_project", "_spelling", "_theme",
|
||||||
)
|
)
|
||||||
|
|
||||||
focusModeChanged = pyqtSignal(bool)
|
focusModeChanged = pyqtSignal(bool)
|
||||||
@@ -480,7 +480,7 @@ class _GuiAlert(QMessageBox):
|
|||||||
def setException(self, exception: Exception) -> None:
|
def setException(self, exception: Exception) -> None:
|
||||||
"""Add exception details."""
|
"""Add exception details."""
|
||||||
info = self.informativeText()
|
info = self.informativeText()
|
||||||
text = f"<b>{type(exception).__name__}</b>: {str(exception)}"
|
text = f"<b>{type(exception).__name__}</b>: {exception!s}"
|
||||||
self.setInformativeText(f"{info}<br>{text}" if info else text)
|
self.setInformativeText(f"{info}<br>{text}" if info else text)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ REGEX_PATTERNS = RegExPatterns()
|
|||||||
class DialogParser:
|
class DialogParser:
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_quotes", "_dialog", "_alternate", "_enabled",
|
"_alternate", "_breakD", "_breakQ", "_dialog", "_enabled", "_mode",
|
||||||
"_narrator", "_breakD", "_breakQ", "_mode",
|
"_narrator", "_quotes",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|||||||
@@ -124,10 +124,10 @@ class GuiLipsum(NDialog):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def getLipsum(cls, parent: QWidget) -> str:
|
def getLipsum(cls, parent: QWidget) -> str:
|
||||||
"""Pop the dialog and return the lipsum text."""
|
"""Pop the dialog and return the lipsum text."""
|
||||||
cls = GuiLipsum(parent)
|
dialog = cls(parent)
|
||||||
cls.exec()
|
dialog.exec()
|
||||||
text = cls.lipsumText
|
text = dialog.lipsumText
|
||||||
cls.softDelete()
|
dialog.softDelete()
|
||||||
return text
|
return text
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import QTimer, pyqtSlot
|
from PyQt6.QtCore import QTimer, pyqtSlot
|
||||||
from PyQt6.QtGui import QCloseEvent
|
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QAbstractButton, QAbstractItemView, QDialogButtonBox, QFileDialog,
|
QAbstractButton, QAbstractItemView, QDialogButtonBox, QFileDialog,
|
||||||
QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
|
QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
|
||||||
@@ -38,7 +38,6 @@ from PyQt6.QtWidgets import (
|
|||||||
from novelwriter import SHARED
|
from novelwriter import SHARED
|
||||||
from novelwriter.common import makeFileNameSafe, openExternalPath
|
from novelwriter.common import makeFileNameSafe, openExternalPath
|
||||||
from novelwriter.constants import nwLabels
|
from novelwriter.constants import nwLabels
|
||||||
from novelwriter.core.buildsettings import BuildSettings
|
|
||||||
from novelwriter.core.docbuild import NWBuildDocument
|
from novelwriter.core.docbuild import NWBuildDocument
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.enum import nwBuildFmt
|
from novelwriter.enum import nwBuildFmt
|
||||||
@@ -46,6 +45,11 @@ from novelwriter.extensions.modified import NDialog, NIconToolButton
|
|||||||
from novelwriter.extensions.progressbars import NProgressSimple
|
from novelwriter.extensions.progressbars import NProgressSimple
|
||||||
from novelwriter.types import QtAlignCenter, QtDialogClose, QtRoleAction, QtRoleReject, QtUserRole
|
from novelwriter.types import QtAlignCenter, QtDialogClose, QtRoleAction, QtRoleReject, QtUserRole
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from PyQt6.QtGui import QCloseEvent
|
||||||
|
|
||||||
|
from novelwriter.core.buildsettings import BuildSettings
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ from novelwriter.types import (
|
|||||||
QtUserRole
|
QtUserRole
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.guimain import GuiMain
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -427,10 +427,10 @@ class GuiManuscript(NToolDialog):
|
|||||||
|
|
||||||
def _saveSettings(self) -> None:
|
def _saveSettings(self) -> None:
|
||||||
"""Save the user GUI settings."""
|
"""Save the user GUI settings."""
|
||||||
buildOrder = []
|
buildOrder = [
|
||||||
for i in range(self.buildList.count()):
|
item.data(self.D_KEY) for i in range(self.buildList.count())
|
||||||
if item := self.buildList.item(i):
|
if (item := self.buildList.item(i))
|
||||||
buildOrder.append(item.data(self.D_KEY))
|
]
|
||||||
|
|
||||||
current = self.buildList.currentItem()
|
current = self.buildList.currentItem()
|
||||||
lastBuild = current.data(self.D_KEY) if isinstance(current, QListWidgetItem) else ""
|
lastBuild = current.data(self.D_KEY) if isinstance(current, QListWidgetItem) else ""
|
||||||
@@ -744,7 +744,7 @@ class _PreviewWidget(QTextBrowser):
|
|||||||
document.setDocumentMargin(CONFIG.textMargin)
|
document.setDocumentMargin(CONFIG.textMargin)
|
||||||
|
|
||||||
self.setPlaceholderText(self.tr(
|
self.setPlaceholderText(self.tr(
|
||||||
"Press the \"Preview\" button to generate ..."
|
'Press the "Preview" button to generate ...'
|
||||||
))
|
))
|
||||||
|
|
||||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
|
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ from novelwriter.types import (
|
|||||||
QtUserRole
|
QtUserRole
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.guimain import GuiMain
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -26,8 +26,9 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt6.QtCore import pyqtSlot
|
from PyQt6.QtCore import pyqtSlot
|
||||||
from PyQt6.QtGui import QCloseEvent
|
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QAbstractItemView, QDialogButtonBox, QFormLayout, QGridLayout, QHBoxLayout,
|
QAbstractItemView, QDialogButtonBox, QFormLayout, QGridLayout, QHBoxLayout,
|
||||||
QLabel, QSpinBox, QStackedWidget, QTreeWidget, QTreeWidgetItem,
|
QLabel, QSpinBox, QStackedWidget, QTreeWidget, QTreeWidgetItem,
|
||||||
@@ -44,6 +45,9 @@ from novelwriter.extensions.pagedsidebar import NPagedSideBar
|
|||||||
from novelwriter.extensions.switch import NSwitch
|
from novelwriter.extensions.switch import NSwitch
|
||||||
from novelwriter.types import QtAlignRight, QtDecoration, QtDialogClose
|
from novelwriter.types import QtAlignRight, QtDecoration, QtDialogClose
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from PyQt6.QtGui import QCloseEvent
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ class _OpenProjectPage(QWidget):
|
|||||||
|
|
||||||
class _ProjectListItem(QStyledItemDelegate):
|
class _ProjectListItem(QStyledItemDelegate):
|
||||||
|
|
||||||
__slots__ = ("_pPx", "_hPx", "_tFont", "_dFont", "_dPen", "_icon")
|
__slots__ = ("_dFont", "_dPen", "_hPx", "_icon", "_pPx", "_tFont")
|
||||||
|
|
||||||
def __init__(self, parent: QWidget) -> None:
|
def __init__(self, parent: QWidget) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ from novelwriter.types import (
|
|||||||
QtDialogClose, QtRoleAction
|
QtDialogClose, QtRoleAction
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING:
|
||||||
from novelwriter.guimain import GuiMain
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -124,7 +124,7 @@ class GuiWritingStats(NToolDialog):
|
|||||||
self.listBox.setSortingEnabled(True)
|
self.listBox.setSortingEnabled(True)
|
||||||
|
|
||||||
# Word Bar
|
# Word Bar
|
||||||
self.barHeight = int(round(0.5*SHARED.theme.fontPixelSize))
|
self.barHeight = round(0.5*SHARED.theme.fontPixelSize)
|
||||||
self.barImage = QPixmap(self.barHeight, self.barHeight)
|
self.barImage = QPixmap(self.barHeight, self.barHeight)
|
||||||
self.barImage.fill(self.palette().highlight().color())
|
self.barImage.fill(self.palette().highlight().color())
|
||||||
|
|
||||||
|
|||||||
+39
-18
@@ -60,30 +60,48 @@ line-length = 99
|
|||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
preview = true
|
preview = true
|
||||||
|
|
||||||
|
# Rules: https://docs.astral.sh/ruff/rules
|
||||||
select = [
|
select = [
|
||||||
"A", # flake8-builtins (A)
|
"A", # flake8-builtins (A)
|
||||||
"B", # flake8-bugbear (B)
|
"ANN", # flake8-annotations (ANN)
|
||||||
"E", # pycodestyle (E)
|
"B", # flake8-bugbear (B)
|
||||||
"F", # Pyflakes (F)
|
"E", # pycodestyle (E)
|
||||||
"W", # pycodestyle (W)
|
"F", # Pyflakes (F)
|
||||||
"UP", # pyupgrade (UP)
|
"FA", # flake8-future-annotations (FA)
|
||||||
"ANN", # flake8-annotations (ANN)
|
"PERF", # Perflint (PERF)
|
||||||
"PLE", # Pylint Error (PLE)
|
"PLC", # Pylint Convention (PLC)
|
||||||
"FA", # flake8-future-annotations (FA)
|
"PLE", # Pylint Error (PLE)
|
||||||
|
"PLW", # Pylint Warning (PLW)
|
||||||
|
"Q", # flake8-quotes (Q)
|
||||||
|
"RUF", # Ruff-specific rules (RUF)
|
||||||
|
"SLF", # flake8-self (SLF)
|
||||||
|
"SLOT", # flake8-slots (SLOT)
|
||||||
|
"TC", # flake8-type-checking (TC)
|
||||||
|
"UP", # pyupgrade (UP)
|
||||||
|
"W", # pycodestyle (W)
|
||||||
]
|
]
|
||||||
ignore = [
|
ignore = [
|
||||||
"E221", # multiple-spaces-before-operator
|
"ANN401", # any-type
|
||||||
"E226", # missing-whitespace-around-arithmetic-operator
|
"E221", # multiple-spaces-before-operator
|
||||||
"E228", # missing-whitespace-around-modulo-operator
|
"E226", # missing-whitespace-around-arithmetic-operator
|
||||||
"E241", # multiple-spaces-after-comma
|
"E228", # missing-whitespace-around-modulo-operator
|
||||||
"E272", # multiple-spaces-before-keyword
|
"E241", # multiple-spaces-after-comma
|
||||||
"ANN401", # any-type
|
"E272", # multiple-spaces-before-keyword
|
||||||
"UP015", # redundant-open-modes
|
"PLC0415", # import-outside-top-level
|
||||||
"UP030", # format-literals
|
"PLC1901", # compare-to-empty-string
|
||||||
|
"PLW0108", # unnecessary-lambda
|
||||||
|
"PLW2901", # redefined-loop-name
|
||||||
|
"RUF001", # ambiguous-unicode-character-string
|
||||||
|
"RUF002", # ambiguous-unicode-character-docstring
|
||||||
|
"RUF015", # unnecessary-iterable-allocation-for-first-element
|
||||||
|
"UP015", # redundant-open-modes
|
||||||
|
"UP030", # format-literals
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.lint.per-file-ignores]
|
[tool.ruff.lint.per-file-ignores]
|
||||||
"tests/*" = ["ANN"]
|
"tests/*" = ["ANN", "SLF", "TC", "PLC2701"]
|
||||||
|
"utils/*" = ["ANN", "SLF", "TC"]
|
||||||
|
|
||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
quote-style = "double"
|
quote-style = "double"
|
||||||
@@ -111,3 +129,6 @@ branch = false
|
|||||||
|
|
||||||
[tool.coverage.report]
|
[tool.coverage.report]
|
||||||
precision = 2
|
precision = 2
|
||||||
|
exclude_also = [
|
||||||
|
"if TYPE_CHECKING:"
|
||||||
|
]
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
ruff
|
|
||||||
isort
|
isort
|
||||||
pyright
|
pyright
|
||||||
|
ruff
|
||||||
|
|||||||
+4
-4
@@ -33,10 +33,10 @@ from PyQt6.QtWidgets import QMessageBox
|
|||||||
|
|
||||||
sys.path.insert(1, str(Path(__file__).parent.parent.absolute()))
|
sys.path.insert(1, str(Path(__file__).parent.parent.absolute()))
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED # noqa: E402
|
from novelwriter import CONFIG, SHARED
|
||||||
|
|
||||||
from tests.mocked import MockGuiMain, MockTheme # noqa: E402
|
from tests.mocked import MockGuiMain, MockTheme
|
||||||
from tests.tools import cleanProject # noqa: E402
|
from tests.tools import cleanProject
|
||||||
|
|
||||||
_TST_ROOT = Path(__file__).parent
|
_TST_ROOT = Path(__file__).parent
|
||||||
_TMP_ROOT = _TST_ROOT / "temp"
|
_TMP_ROOT = _TST_ROOT / "temp"
|
||||||
@@ -89,7 +89,7 @@ def functionFixture(qtbot):
|
|||||||
shutil.rmtree(_TMP_CONF)
|
shutil.rmtree(_TMP_CONF)
|
||||||
_TMP_CONF.mkdir()
|
_TMP_CONF.mkdir()
|
||||||
|
|
||||||
CONFIG.__init__()
|
CONFIG.__init__() # noqa: PLC2801
|
||||||
CONFIG.initConfig(confPath=_TMP_CONF, dataPath=_TMP_CONF)
|
CONFIG.initConfig(confPath=_TMP_CONF, dataPath=_TMP_CONF)
|
||||||
resetConfigVars()
|
resetConfigVars()
|
||||||
logging.getLogger("novelwriter").setLevel(logging.INFO)
|
logging.getLogger("novelwriter").setLevel(logging.INFO)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pytest>=6.0.0
|
coverage>=7.2.0
|
||||||
pytest-timeout
|
|
||||||
pytest-cov
|
pytest-cov
|
||||||
pytest-qt
|
pytest-qt
|
||||||
|
pytest-timeout
|
||||||
|
pytest>=6.0.0
|
||||||
|
|||||||
@@ -556,7 +556,7 @@ def testBaseCommon_jsonEncode():
|
|||||||
# Correct types
|
# Correct types
|
||||||
assert jsonEncode([1, 2]) == "[\n 1,\n 2\n]"
|
assert jsonEncode([1, 2]) == "[\n 1,\n 2\n]"
|
||||||
assert jsonEncode((1, 2)) == "[\n 1,\n 2\n]"
|
assert jsonEncode((1, 2)) == "[\n 1,\n 2\n]"
|
||||||
assert jsonEncode({1: 2}) == "{\n \"1\": 2\n}"
|
assert jsonEncode({1: 2}) == '{\n "1": 2\n}'
|
||||||
|
|
||||||
tstDict = {
|
tstDict = {
|
||||||
"null": None,
|
"null": None,
|
||||||
|
|||||||
@@ -141,8 +141,8 @@ def testBaseConfig_InitLoadSave(monkeypatch, fncPath, tstPaths):
|
|||||||
assert newConf.guiSyntax == "bar"
|
assert newConf.guiSyntax == "bar"
|
||||||
|
|
||||||
# Test Correcting Quote Settings
|
# Test Correcting Quote Settings
|
||||||
tstConf.fmtDQuoteOpen = "\""
|
tstConf.fmtDQuoteOpen = '"'
|
||||||
tstConf.fmtDQuoteClose = "\""
|
tstConf.fmtDQuoteClose = '"'
|
||||||
tstConf.fmtSQuoteOpen = "'"
|
tstConf.fmtSQuoteOpen = "'"
|
||||||
tstConf.fmtSQuoteClose = "'"
|
tstConf.fmtSQuoteClose = "'"
|
||||||
tstConf.doReplaceDQuote = True
|
tstConf.doReplaceDQuote = True
|
||||||
|
|||||||
@@ -768,7 +768,7 @@ def testCoreTools_ProjectBuilderSample(monkeypatch, mockGUI, fncPath, tstPaths):
|
|||||||
assert builder.buildProject(data) is False
|
assert builder.buildProject(data) is False
|
||||||
|
|
||||||
# Create and open a defective zip file
|
# Create and open a defective zip file
|
||||||
with open(dstSample, mode="w+") as outFile:
|
with open(dstSample, mode="w+", encoding="utf-8") as outFile:
|
||||||
outFile.write("foo")
|
outFile.write("foo")
|
||||||
|
|
||||||
assert builder.buildProject(data) is False
|
assert builder.buildProject(data) is False
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ from tests.tools import cmpFiles, writeFile
|
|||||||
class MockProject:
|
class MockProject:
|
||||||
"""Fake project object."""
|
"""Fake project object."""
|
||||||
|
|
||||||
|
data: NWProjectData
|
||||||
|
|
||||||
def setProjectChanged(self, *a):
|
def setProjectChanged(self, *a):
|
||||||
"""Fake project method."""
|
"""Fake project method."""
|
||||||
pass
|
pass
|
||||||
@@ -219,7 +221,7 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, mockGUI, tstPaths, fncPath):
|
|||||||
|
|
||||||
packedContent = []
|
packedContent = []
|
||||||
mockProject = MockProject()
|
mockProject = MockProject()
|
||||||
mockProject.__setattr__("data", data)
|
mockProject.data = data
|
||||||
for entry in content:
|
for entry in content:
|
||||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||||
item.unpack(entry)
|
item.unpack(entry)
|
||||||
@@ -348,7 +350,7 @@ def testCoreProjectXML_ReadLegacy10(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
|
|
||||||
packedContent = []
|
packedContent = []
|
||||||
mockProject = MockProject()
|
mockProject = MockProject()
|
||||||
mockProject.__setattr__("data", data)
|
mockProject.data = data
|
||||||
status = {}
|
status = {}
|
||||||
for entry in content:
|
for entry in content:
|
||||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||||
@@ -493,7 +495,7 @@ def testCoreProjectXML_ReadLegacy11(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
|
|
||||||
packedContent = []
|
packedContent = []
|
||||||
mockProject = MockProject()
|
mockProject = MockProject()
|
||||||
mockProject.__setattr__("data", data)
|
mockProject.data = data
|
||||||
status = {}
|
status = {}
|
||||||
for entry in content:
|
for entry in content:
|
||||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||||
@@ -638,7 +640,7 @@ def testCoreProjectXML_ReadLegacy12(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
|
|
||||||
packedContent = []
|
packedContent = []
|
||||||
mockProject = MockProject()
|
mockProject = MockProject()
|
||||||
mockProject.__setattr__("data", data)
|
mockProject.data = data
|
||||||
status = {}
|
status = {}
|
||||||
for entry in content:
|
for entry in content:
|
||||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||||
@@ -786,7 +788,7 @@ def testCoreProjectXML_ReadLegacy13(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
|
|
||||||
packedContent = []
|
packedContent = []
|
||||||
mockProject = MockProject()
|
mockProject = MockProject()
|
||||||
mockProject.__setattr__("data", data)
|
mockProject.data = data
|
||||||
status = {}
|
status = {}
|
||||||
for entry in content:
|
for entry in content:
|
||||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||||
@@ -933,7 +935,7 @@ def testCoreProjectXML_ReadLegacy14(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
|
|
||||||
packedContent = []
|
packedContent = []
|
||||||
mockProject = MockProject()
|
mockProject = MockProject()
|
||||||
mockProject.__setattr__("data", data)
|
mockProject.data = data
|
||||||
status = {}
|
status = {}
|
||||||
for entry in content:
|
for entry in content:
|
||||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||||
|
|||||||
@@ -982,7 +982,7 @@ def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths, ipsumText):
|
|||||||
def prettifyXml(inFile, outFile):
|
def prettifyXml(inFile, outFile):
|
||||||
with open(outFile, mode="wb") as fStream:
|
with open(outFile, mode="wb") as fStream:
|
||||||
xml = ET.parse(inFile)
|
xml = ET.parse(inFile)
|
||||||
xmlIndent(xml)
|
xmlIndent(xml.getroot())
|
||||||
xml.write(fStream, encoding="utf-8", xml_declaration=True)
|
xml.write(fStream, encoding="utf-8", xml_declaration=True)
|
||||||
|
|
||||||
prettifyXml(maniOut, maniFile)
|
prettifyXml(maniOut, maniFile)
|
||||||
@@ -1082,10 +1082,6 @@ def testFmtToOdt_ODTParagraphStyle():
|
|||||||
assert parStyle._pAttr["text-align"] == ["fo", "end"]
|
assert parStyle._pAttr["text-align"] == ["fo", "end"]
|
||||||
parStyle.setTextAlign("justify")
|
parStyle.setTextAlign("justify")
|
||||||
assert parStyle._pAttr["text-align"] == ["fo", "justify"]
|
assert parStyle._pAttr["text-align"] == ["fo", "justify"]
|
||||||
parStyle.setTextAlign("inside")
|
|
||||||
assert parStyle._pAttr["text-align"] == ["fo", "inside"]
|
|
||||||
parStyle.setTextAlign("outside")
|
|
||||||
assert parStyle._pAttr["text-align"] == ["fo", "outside"]
|
|
||||||
parStyle.setTextAlign("left")
|
parStyle.setTextAlign("left")
|
||||||
assert parStyle._pAttr["text-align"] == ["fo", "left"]
|
assert parStyle._pAttr["text-align"] == ["fo", "left"]
|
||||||
parStyle.setTextAlign("right")
|
parStyle.setTextAlign("right")
|
||||||
@@ -1099,8 +1095,6 @@ def testFmtToOdt_ODTParagraphStyle():
|
|||||||
assert parStyle._pAttr["break-before"] == ["fo", None]
|
assert parStyle._pAttr["break-before"] == ["fo", None]
|
||||||
parStyle.setBreakBefore("auto")
|
parStyle.setBreakBefore("auto")
|
||||||
assert parStyle._pAttr["break-before"] == ["fo", "auto"]
|
assert parStyle._pAttr["break-before"] == ["fo", "auto"]
|
||||||
parStyle.setBreakBefore("column")
|
|
||||||
assert parStyle._pAttr["break-before"] == ["fo", "column"]
|
|
||||||
parStyle.setBreakBefore("page")
|
parStyle.setBreakBefore("page")
|
||||||
assert parStyle._pAttr["break-before"] == ["fo", "page"]
|
assert parStyle._pAttr["break-before"] == ["fo", "page"]
|
||||||
parStyle.setBreakBefore("even-page")
|
parStyle.setBreakBefore("even-page")
|
||||||
@@ -1118,8 +1112,6 @@ def testFmtToOdt_ODTParagraphStyle():
|
|||||||
assert parStyle._pAttr["break-after"] == ["fo", None]
|
assert parStyle._pAttr["break-after"] == ["fo", None]
|
||||||
parStyle.setBreakAfter("auto")
|
parStyle.setBreakAfter("auto")
|
||||||
assert parStyle._pAttr["break-after"] == ["fo", "auto"]
|
assert parStyle._pAttr["break-after"] == ["fo", "auto"]
|
||||||
parStyle.setBreakAfter("column")
|
|
||||||
assert parStyle._pAttr["break-after"] == ["fo", "column"]
|
|
||||||
parStyle.setBreakAfter("page")
|
parStyle.setBreakAfter("page")
|
||||||
assert parStyle._pAttr["break-after"] == ["fo", "page"]
|
assert parStyle._pAttr["break-after"] == ["fo", "page"]
|
||||||
parStyle.setBreakAfter("even-page")
|
parStyle.setBreakAfter("even-page")
|
||||||
@@ -1394,9 +1386,9 @@ def testFmtToOdt_XMLParagraph():
|
|||||||
# Plain Text
|
# Plain Text
|
||||||
xmlPar.appendText("Hello World")
|
xmlPar.appendText("Hello World")
|
||||||
assert xmlToText(xRoot) == (
|
assert xmlToText(xRoot) == (
|
||||||
'<root>'
|
"<root>"
|
||||||
'<text:p>Hello World</text:p>'
|
"<text:p>Hello World</text:p>"
|
||||||
'</root>'
|
"</root>"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Text Span
|
# Text Span
|
||||||
@@ -1431,9 +1423,9 @@ def testFmtToOdt_XMLParagraph():
|
|||||||
# Plain Text w/Line Break
|
# Plain Text w/Line Break
|
||||||
xmlPar.appendText("Hello\nWorld\n!!")
|
xmlPar.appendText("Hello\nWorld\n!!")
|
||||||
assert xmlToText(xRoot) == (
|
assert xmlToText(xRoot) == (
|
||||||
'<root>'
|
"<root>"
|
||||||
'<text:p>Hello<text:line-break />World<text:line-break />!!</text:p>'
|
"<text:p>Hello<text:line-break />World<text:line-break />!!</text:p>"
|
||||||
'</root>'
|
"</root>"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Text Span w/Line Break
|
# Text Span w/Line Break
|
||||||
@@ -1467,9 +1459,9 @@ def testFmtToOdt_XMLParagraph():
|
|||||||
# Plain Text w/Line Break
|
# Plain Text w/Line Break
|
||||||
xmlPar.appendText("Hello\tWorld\t!!")
|
xmlPar.appendText("Hello\tWorld\t!!")
|
||||||
assert xmlToText(xRoot) == (
|
assert xmlToText(xRoot) == (
|
||||||
'<root>'
|
"<root>"
|
||||||
'<text:p>Hello<text:tab />World<text:tab />!!</text:p>'
|
"<text:p>Hello<text:tab />World<text:tab />!!</text:p>"
|
||||||
'</root>'
|
"</root>"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Text Span w/Line Break
|
# Text Span w/Line Break
|
||||||
|
|||||||
@@ -763,7 +763,7 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
|
|||||||
assert docEditor.getText() == text.replace("consectetur", "\u2018consectetur\u2019")
|
assert docEditor.getText() == text.replace("consectetur", "\u2018consectetur\u2019")
|
||||||
|
|
||||||
# Replace Double Quotes
|
# Replace Double Quotes
|
||||||
repText = text.replace("consectetur", "\"consectetur\"")
|
repText = text.replace("consectetur", '"consectetur"')
|
||||||
docEditor.replaceText(repText)
|
docEditor.replaceText(repText)
|
||||||
assert docEditor.docAction(nwDocAction.SEL_ALL) is True
|
assert docEditor.docAction(nwDocAction.SEL_ALL) is True
|
||||||
assert docEditor.docAction(nwDocAction.REPL_DBL) is True
|
assert docEditor.docAction(nwDocAction.REPL_DBL) is True
|
||||||
|
|||||||
@@ -116,7 +116,8 @@ def testGuiMain_Launch(qtbot, monkeypatch, nwGUI, projPath):
|
|||||||
# Check that project open dialog launches
|
# Check that project open dialog launches
|
||||||
nwGUI.postLaunchTasks(None)
|
nwGUI.postLaunchTasks(None)
|
||||||
qtbot.waitUntil(lambda: SHARED.findTopLevelWidget(GuiWelcome) is not None, timeout=1000)
|
qtbot.waitUntil(lambda: SHARED.findTopLevelWidget(GuiWelcome) is not None, timeout=1000)
|
||||||
assert isinstance(welcome := SHARED.findTopLevelWidget(GuiWelcome), GuiWelcome)
|
welcome = SHARED.findTopLevelWidget(GuiWelcome)
|
||||||
|
assert isinstance(welcome, GuiWelcome)
|
||||||
welcome.show()
|
welcome.show()
|
||||||
welcome.close()
|
welcome.close()
|
||||||
|
|
||||||
@@ -454,7 +455,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
|||||||
# Dialogue
|
# Dialogue
|
||||||
# ========
|
# ========
|
||||||
|
|
||||||
for c in "\"Full line double quoted text.\"":
|
for c in '"Full line double quoted text."':
|
||||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
@@ -469,7 +470,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
|||||||
CONFIG.fmtPadAfter = "\u201c"
|
CONFIG.fmtPadAfter = "\u201c"
|
||||||
docEditor.initEditor()
|
docEditor.initEditor()
|
||||||
|
|
||||||
for c in "Some \"double quoted text with spaces padded\".":
|
for c in 'Some "double quoted text with spaces padded".':
|
||||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
@@ -533,17 +534,17 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
|||||||
# ================
|
# ================
|
||||||
|
|
||||||
nwGUI._switchFocus(nwView.EDITOR)
|
nwGUI._switchFocus(nwView.EDITOR)
|
||||||
for c in "\t\"Tab-indented text\"":
|
for c in '\t"Tab-indented text"':
|
||||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in ">\"Paragraph-indented text\"":
|
for c in '>"Paragraph-indented text"':
|
||||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in ">>\"Right-aligned text\"":
|
for c in '>>"Right-aligned text"':
|
||||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ def testGuiMainMenu_EditFormat(qtbot, monkeypatch, nwGUI, prjLipsum):
|
|||||||
docEditor.setPlainText(
|
docEditor.setPlainText(
|
||||||
"### New Text\n\n"
|
"### New Text\n\n"
|
||||||
"Text with 'single' quotes and 'tricky stuff's'.\n\n"
|
"Text with 'single' quotes and 'tricky stuff's'.\n\n"
|
||||||
"Also text with \"double\" quotes which are \"less tricky\".\n\n"
|
'Also text with "double" quotes which are "less tricky".\n\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
mainMenu.aSelectAll.activate(QAction.ActionEvent.Trigger)
|
mainMenu.aSelectAll.activate(QAction.ActionEvent.Trigger)
|
||||||
@@ -287,7 +287,7 @@ def testGuiMainMenu_EditFormat(qtbot, monkeypatch, nwGUI, prjLipsum):
|
|||||||
assert docEditor.getText() == (
|
assert docEditor.getText() == (
|
||||||
"### New Text\n\n"
|
"### New Text\n\n"
|
||||||
"Text with ‘single’ quotes and ‘tricky stuff’s’.\n\n"
|
"Text with ‘single’ quotes and ‘tricky stuff’s’.\n\n"
|
||||||
"Also text with \"double\" quotes which are \"less tricky\".\n\n"
|
'Also text with "double" quotes which are "less tricky".\n\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
mainMenu.aSelectAll.activate(QAction.ActionEvent.Trigger)
|
mainMenu.aSelectAll.activate(QAction.ActionEvent.Trigger)
|
||||||
@@ -347,7 +347,7 @@ def testGuiMainMenu_EditFormat(qtbot, monkeypatch, nwGUI, prjLipsum):
|
|||||||
"### New Text\n\n"
|
"### New Text\n\n"
|
||||||
"@tag: Bod\n\n"
|
"@tag: Bod\n\n"
|
||||||
"Text with 'single' quotes and 'tricky stuff's'.\n\n"
|
"Text with 'single' quotes and 'tricky stuff's'.\n\n"
|
||||||
"Also text with \"double\" quotes which are \"less tricky\".\n\n"
|
'Also text with "double" quotes which are "less tricky".\n\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cannot Format Tag
|
# Cannot Format Tag
|
||||||
@@ -363,7 +363,7 @@ def testGuiMainMenu_EditFormat(qtbot, monkeypatch, nwGUI, prjLipsum):
|
|||||||
"### New Text\n\n"
|
"### New Text\n\n"
|
||||||
"@tag: Bod\n\n"
|
"@tag: Bod\n\n"
|
||||||
"Text with 'single' quotes and 'tricky stuff's'.\n\n"
|
"Text with 'single' quotes and 'tricky stuff's'.\n\n"
|
||||||
"Also text with \"double\" quotes which are \"less tricky\".\n\n"
|
'Also text with "double" quotes which are "less tricky".\n\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
|
|||||||
@@ -29,15 +29,12 @@ from novelwriter.constants import nwUnicode
|
|||||||
from novelwriter.text.patterns import REGEX_PATTERNS, DialogParser
|
from novelwriter.text.patterns import REGEX_PATTERNS, DialogParser
|
||||||
|
|
||||||
|
|
||||||
def allMatches(regEx: re.Pattern, text: str) -> list[list[str]]:
|
def allMatches(regEx: re.Pattern, text: str) -> list[list[tuple[str, int, int]]]:
|
||||||
"""Get all matches for a regex."""
|
"""Get all matches for a regex."""
|
||||||
result = []
|
return [
|
||||||
for res in regEx.finditer(text):
|
[(res.group(n), res.start(n), res.end(n)) for n in range((res.lastindex or 0) + 1)]
|
||||||
result.append([
|
for res in regEx.finditer(text)
|
||||||
(res.group(n), res.start(n), res.end(n))
|
]
|
||||||
for n in range((res.lastindex or 0) + 1)
|
|
||||||
])
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -310,7 +307,7 @@ def testTextPatterns_DialogueStyle():
|
|||||||
assert allMatches(regEx, "one 'two' three") == []
|
assert allMatches(regEx, "one 'two' three") == []
|
||||||
|
|
||||||
# Straight double quotes are ignored
|
# Straight double quotes are ignored
|
||||||
assert allMatches(regEx, "one \"two\" three") == []
|
assert allMatches(regEx, 'one "two" three') == []
|
||||||
|
|
||||||
# Check with no whitespace, single quote
|
# Check with no whitespace, single quote
|
||||||
assert allMatches(regEx, "one\u2018two\u2019three") == [
|
assert allMatches(regEx, "one\u2018two\u2019three") == [
|
||||||
@@ -370,19 +367,19 @@ def testTextPatterns_DialoguePlain():
|
|||||||
# ======
|
# ======
|
||||||
|
|
||||||
# One double quoted string
|
# One double quoted string
|
||||||
assert allMatches(regEx, "one \"two\" three") == [
|
assert allMatches(regEx, 'one "two" three') == [
|
||||||
[("\"two\"", 4, 9)]
|
[('"two"', 4, 9)]
|
||||||
]
|
]
|
||||||
|
|
||||||
# Two double quoted strings
|
# Two double quoted strings
|
||||||
assert allMatches(regEx, "one \"two\" three \"four\" five") == [
|
assert allMatches(regEx, 'one "two" three "four" five') == [
|
||||||
[("\"two\"", 4, 9)], [("\"four\"", 16, 22)],
|
[('"two"', 4, 9)], [('"four"', 16, 22)],
|
||||||
]
|
]
|
||||||
|
|
||||||
# No space
|
# No space
|
||||||
assert allMatches(regEx, "one\"two\" three") == []
|
assert allMatches(regEx, 'one"two" three') == []
|
||||||
assert allMatches(regEx, "one \"two\"three") == []
|
assert allMatches(regEx, 'one "two"three') == []
|
||||||
assert allMatches(regEx, "one\"two\"three") == []
|
assert allMatches(regEx, 'one"two"three') == []
|
||||||
|
|
||||||
# Single
|
# Single
|
||||||
# ======
|
# ======
|
||||||
@@ -595,6 +592,6 @@ def testTextPatterns_DialogParserPolish():
|
|||||||
]
|
]
|
||||||
|
|
||||||
assert parser(
|
assert parser(
|
||||||
"And so on and so forth. However, \"text in quotation marks\" should not be "
|
'And so on and so forth. However, "text in quotation marks" should not be '
|
||||||
"highlighted at all, and if so, it should be highlighted differently."
|
"highlighted at all, and if so, it should be highlighted differently."
|
||||||
) == []
|
) == []
|
||||||
|
|||||||
@@ -83,8 +83,7 @@ def testToolManuscriptBuild_Main(
|
|||||||
if item.data(manus.D_KEY) == fmt:
|
if item.data(manus.D_KEY) == fmt:
|
||||||
manus.listFormats.setCurrentItem(item)
|
manus.listFormats.setCurrentItem(item)
|
||||||
return
|
return
|
||||||
else:
|
raise ValueError("No such key in format list")
|
||||||
raise ValueError("No such key in format list")
|
|
||||||
|
|
||||||
# Build documents
|
# Build documents
|
||||||
lastFmt = None
|
lastFmt = None
|
||||||
|
|||||||
+2
-2
@@ -180,9 +180,9 @@ def updateTranslationSources(args: argparse.Namespace) -> None:
|
|||||||
else: # Create an empty new language file
|
else: # Create an empty new language file
|
||||||
langCode = item.name[3:-3]
|
langCode = item.name[3:-3]
|
||||||
writeFile(item, (
|
writeFile(item, (
|
||||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
'<?xml version="1.0" encoding="utf-8"?>\n'
|
||||||
"<!DOCTYPE TS>\n"
|
"<!DOCTYPE TS>\n"
|
||||||
f"<TS version=\"2.0\" language=\"{langCode}\" sourcelanguage=\"en_GB\"/>\n"
|
f'<TS version="2.0" language="{langCode}" sourcelanguage="en_GB"/>\n'
|
||||||
))
|
))
|
||||||
translations.append(item)
|
translations.append(item)
|
||||||
print(f"Created: {item}")
|
print(f"Created: {item}")
|
||||||
|
|||||||
@@ -134,10 +134,10 @@ def makeDebianPackage(
|
|||||||
signArgs = [f"-k{signKey}"]
|
signArgs = [f"-k{signKey}"]
|
||||||
|
|
||||||
if sourceBuild:
|
if sourceBuild:
|
||||||
subprocess.call(["debuild", "-S"] + signArgs, cwd=outDir)
|
subprocess.call(["debuild", "-S", *signArgs], cwd=outDir)
|
||||||
toUpload(bldDir / f"{bldPkg}.tar.xz")
|
toUpload(bldDir / f"{bldPkg}.tar.xz")
|
||||||
else:
|
else:
|
||||||
subprocess.call(["dpkg-buildpackage"] + signArgs, cwd=outDir)
|
subprocess.call(["dpkg-buildpackage", *signArgs], cwd=outDir)
|
||||||
shutil.copyfile(bldDir / f"{bldPkg}.tar.xz", bldDir / f"{bldPkg}.debian.tar.xz")
|
shutil.copyfile(bldDir / f"{bldPkg}.tar.xz", bldDir / f"{bldPkg}.debian.tar.xz")
|
||||||
toUpload(bldDir / f"{bldPkg}.debian.tar.xz")
|
toUpload(bldDir / f"{bldPkg}.debian.tar.xz")
|
||||||
toUpload(bldDir / f"{bldPkg}_all.deb")
|
toUpload(bldDir / f"{bldPkg}_all.deb")
|
||||||
@@ -195,7 +195,7 @@ def launchpad(args: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
signKey = SIGN_KEY if args.sign else None
|
signKey = SIGN_KEY if args.sign else None
|
||||||
|
|
||||||
print(f"Sign Key: {str(signKey)}")
|
print(f"Sign Key: {signKey!s}")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
dputCmd = []
|
dputCmd = []
|
||||||
|
|||||||
@@ -218,9 +218,9 @@ def main(args: argparse.Namespace) -> None:
|
|||||||
"import sys\n"
|
"import sys\n"
|
||||||
"\n"
|
"\n"
|
||||||
"os.curdir = os.path.abspath(os.path.dirname(__file__))\n"
|
"os.curdir = os.path.abspath(os.path.dirname(__file__))\n"
|
||||||
"sys.path.insert(0, os.path.join(os.curdir, \"lib\"))\n"
|
'sys.path.insert(0, os.path.join(os.curdir, "lib"))\n'
|
||||||
"\n"
|
"\n"
|
||||||
"if __name__ == \"__main__\":\n"
|
'if __name__ == "__main__":\n'
|
||||||
" import novelwriter\n"
|
" import novelwriter\n"
|
||||||
" novelwriter.main(sys.argv[1:])\n"
|
" novelwriter.main(sys.argv[1:])\n"
|
||||||
))
|
))
|
||||||
|
|||||||
+1
-1
@@ -137,7 +137,7 @@ def makeCheckSum(sumFile: str, cwd: Path | None = None) -> str:
|
|||||||
shaFile = f"{sumFile}.sha256"
|
shaFile = f"{sumFile}.sha256"
|
||||||
else:
|
else:
|
||||||
shaFile = cwd / f"{sumFile}.sha256"
|
shaFile = cwd / f"{sumFile}.sha256"
|
||||||
with open(shaFile, mode="w") as fOut:
|
with open(shaFile, mode="w", encoding="utf-8") as fOut:
|
||||||
subprocess.call(["shasum", "-a", "256", sumFile], stdout=fOut, cwd=cwd)
|
subprocess.call(["shasum", "-a", "256", sumFile], stdout=fOut, cwd=cwd)
|
||||||
print(f"SHA256 Sum: {shaFile}")
|
print(f"SHA256 Sum: {shaFile}")
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|||||||
Reference in New Issue
Block a user