novelWriter 2.3.1__py3-none-any.whl → 2.4b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/METADATA +1 -1
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/RECORD +81 -70
- novelwriter/__init__.py +5 -5
- novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
- novelwriter/assets/icons/typicons_dark/typ_arrow-down.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_arrow-right.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
- novelwriter/assets/icons/typicons_light/icons.conf +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
- novelwriter/assets/icons/typicons_light/typ_arrow-down.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_arrow-right.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/default_dark.conf +1 -0
- novelwriter/assets/syntax/default_light.conf +1 -0
- novelwriter/assets/syntax/grey_dark.conf +1 -0
- novelwriter/assets/syntax/grey_light.conf +1 -0
- novelwriter/assets/syntax/light_owl.conf +1 -0
- novelwriter/assets/syntax/night_owl.conf +1 -0
- novelwriter/assets/syntax/solarized_dark.conf +1 -0
- novelwriter/assets/syntax/solarized_light.conf +1 -0
- novelwriter/assets/syntax/tomorrow.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
- novelwriter/assets/text/credits_en.htm +25 -23
- novelwriter/common.py +1 -1
- novelwriter/config.py +35 -12
- novelwriter/constants.py +5 -6
- novelwriter/core/buildsettings.py +60 -40
- novelwriter/core/coretools.py +98 -13
- novelwriter/core/docbuild.py +74 -7
- novelwriter/core/document.py +24 -3
- novelwriter/core/index.py +31 -112
- novelwriter/core/project.py +10 -15
- novelwriter/core/sessions.py +2 -2
- novelwriter/core/status.py +4 -4
- novelwriter/core/storage.py +8 -2
- novelwriter/core/tohtml.py +22 -25
- novelwriter/core/tokenizer.py +416 -232
- novelwriter/core/tomd.py +17 -8
- novelwriter/core/toodt.py +65 -7
- novelwriter/core/tree.py +8 -8
- novelwriter/dialogs/docsplit.py +7 -8
- novelwriter/dialogs/preferences.py +3 -6
- novelwriter/enum.py +17 -14
- novelwriter/extensions/modified.py +20 -2
- novelwriter/extensions/versioninfo.py +1 -1
- novelwriter/gui/doceditor.py +257 -279
- novelwriter/gui/dochighlight.py +29 -25
- novelwriter/gui/docviewer.py +139 -148
- novelwriter/gui/docviewerpanel.py +4 -24
- novelwriter/gui/editordocument.py +12 -1
- novelwriter/gui/itemdetails.py +6 -6
- novelwriter/gui/mainmenu.py +37 -16
- novelwriter/gui/noveltree.py +11 -19
- novelwriter/gui/outline.py +43 -20
- novelwriter/gui/projtree.py +35 -43
- novelwriter/gui/search.py +316 -0
- novelwriter/gui/sidebar.py +25 -30
- novelwriter/gui/theme.py +59 -6
- novelwriter/guimain.py +176 -173
- novelwriter/shared.py +26 -1
- novelwriter/text/__init__.py +3 -0
- novelwriter/text/counting.py +137 -0
- novelwriter/tools/manuscript.py +344 -55
- novelwriter/tools/manussettings.py +213 -71
- novelwriter/tools/welcome.py +1 -1
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/WHEEL +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/top_level.txt +0 -0
novelwriter/shared.py
CHANGED
@@ -30,7 +30,7 @@ from time import time
|
|
30
30
|
from typing import TYPE_CHECKING, TypeVar
|
31
31
|
from pathlib import Path
|
32
32
|
|
33
|
-
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
|
33
|
+
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal
|
34
34
|
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget
|
35
35
|
from novelwriter.common import formatFileFilter
|
36
36
|
|
@@ -57,10 +57,12 @@ class SharedData(QObject):
|
|
57
57
|
projectStatusChanged = pyqtSignal(bool)
|
58
58
|
projectStatusMessage = pyqtSignal(str)
|
59
59
|
spellLanguageChanged = pyqtSignal(str, str)
|
60
|
+
focusModeChanged = pyqtSignal(bool)
|
60
61
|
indexScannedText = pyqtSignal(str)
|
61
62
|
indexChangedTags = pyqtSignal(list, list)
|
62
63
|
indexCleared = pyqtSignal()
|
63
64
|
indexAvailable = pyqtSignal()
|
65
|
+
mainClockTick = pyqtSignal()
|
64
66
|
|
65
67
|
def __init__(self) -> None:
|
66
68
|
super().__init__()
|
@@ -76,6 +78,11 @@ class SharedData(QObject):
|
|
76
78
|
self._lastAlert = ""
|
77
79
|
self._idleTime = 0.0
|
78
80
|
self._idleRefTime = time()
|
81
|
+
self._focusMode = False
|
82
|
+
|
83
|
+
self._clock = QTimer(self)
|
84
|
+
self._clock.setInterval(1000)
|
85
|
+
self._clock.timeout.connect(lambda: self.mainClockTick.emit())
|
79
86
|
|
80
87
|
return
|
81
88
|
|
@@ -111,6 +118,11 @@ class SharedData(QObject):
|
|
111
118
|
raise Exception("SharedData class not fully initialised")
|
112
119
|
return self._spelling
|
113
120
|
|
121
|
+
@property
|
122
|
+
def focusMode(self) -> bool:
|
123
|
+
"""Return the Focus Mode state."""
|
124
|
+
return self._focusMode
|
125
|
+
|
114
126
|
@property
|
115
127
|
def hasProject(self) -> bool:
|
116
128
|
"""Return True if the project instance is populated."""
|
@@ -131,6 +143,17 @@ class SharedData(QObject):
|
|
131
143
|
"""Return the last alert message."""
|
132
144
|
return self._lastAlert
|
133
145
|
|
146
|
+
##
|
147
|
+
# Setters
|
148
|
+
##
|
149
|
+
|
150
|
+
def setFocusMode(self, state: bool) -> None:
|
151
|
+
"""Set focus mode on or off."""
|
152
|
+
if state is not self._focusMode:
|
153
|
+
self._focusMode = state
|
154
|
+
self.focusModeChanged.emit(state)
|
155
|
+
return
|
156
|
+
|
134
157
|
##
|
135
158
|
# Methods
|
136
159
|
##
|
@@ -140,6 +163,7 @@ class SharedData(QObject):
|
|
140
163
|
soon as the Main GUI is created to ensure the SHARED singleton
|
141
164
|
has the properties needed for operation.
|
142
165
|
"""
|
166
|
+
self._clock.start()
|
143
167
|
self._gui = gui
|
144
168
|
self._theme = theme
|
145
169
|
self._resetProject()
|
@@ -323,6 +347,7 @@ class SharedData(QObject):
|
|
323
347
|
self._project = NWProject()
|
324
348
|
self._spelling = NWSpellEnchant(self._project)
|
325
349
|
self.updateSpellCheckLanguage()
|
350
|
+
self._focusMode = False
|
326
351
|
return
|
327
352
|
|
328
353
|
def _resetIdleTimer(self) -> None:
|
@@ -0,0 +1,137 @@
|
|
1
|
+
"""
|
2
|
+
novelWriter – Text Counting Functions
|
3
|
+
=====================================
|
4
|
+
|
5
|
+
File History:
|
6
|
+
Created: 2019-04-22 [0.0.1] standardCounter
|
7
|
+
Rewritten: 2024-02-27 [2.4b1] preProcessText, standardCounter
|
8
|
+
Created: 2024-02-27 [2.4b1] bodyTextCounter
|
9
|
+
|
10
|
+
This file is a part of novelWriter
|
11
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
12
|
+
|
13
|
+
This program is free software: you can redistribute it and/or modify
|
14
|
+
it under the terms of the GNU General Public License as published by
|
15
|
+
the Free Software Foundation, either version 3 of the License, or
|
16
|
+
(at your option) any later version.
|
17
|
+
|
18
|
+
This program is distributed in the hope that it will be useful, but
|
19
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
20
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
21
|
+
General Public License for more details.
|
22
|
+
|
23
|
+
You should have received a copy of the GNU General Public License
|
24
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
25
|
+
"""
|
26
|
+
from __future__ import annotations
|
27
|
+
|
28
|
+
import re
|
29
|
+
|
30
|
+
from novelwriter.constants import nwRegEx, nwUnicode
|
31
|
+
|
32
|
+
RX_SC = re.compile(nwRegEx.FMT_SC)
|
33
|
+
RX_LO = re.compile(r"(?i)(?<!\\)(\[(?:vspace|newpage|new page)(:\d+)?)(?<!\\)(\])")
|
34
|
+
|
35
|
+
|
36
|
+
def preProcessText(text: str, keepHeaders: bool = True) -> list[str]:
|
37
|
+
"""Strip formatting codes from the text and split into lines."""
|
38
|
+
if not isinstance(text, str):
|
39
|
+
return []
|
40
|
+
|
41
|
+
# We need to treat dashes as word separators for counting words.
|
42
|
+
# The check+replace approach is much faster than direct replace for
|
43
|
+
# large texts, and a bit slower for small texts, but in the latter
|
44
|
+
# case it doesn't really matter.
|
45
|
+
if nwUnicode.U_ENDASH in text:
|
46
|
+
text = text.replace(nwUnicode.U_ENDASH, " ")
|
47
|
+
if nwUnicode.U_EMDASH in text:
|
48
|
+
text = text.replace(nwUnicode.U_EMDASH, " ")
|
49
|
+
|
50
|
+
ignore = "%@" if keepHeaders else "%@#"
|
51
|
+
|
52
|
+
result = []
|
53
|
+
for line in text.splitlines():
|
54
|
+
line = line.rstrip()
|
55
|
+
if line:
|
56
|
+
if line[0] in ignore:
|
57
|
+
continue
|
58
|
+
if line[0] == ">":
|
59
|
+
line = line.lstrip(">").lstrip(" ")
|
60
|
+
if line[-1] == "<":
|
61
|
+
line = line.rstrip("<").rstrip(" ")
|
62
|
+
if "[" in line:
|
63
|
+
# Strip shortcodes and special formatting
|
64
|
+
# RegEx is slow, so we do this only when necessary
|
65
|
+
line = RX_SC.sub("", line)
|
66
|
+
line = RX_LO.sub("", line)
|
67
|
+
|
68
|
+
result.append(line)
|
69
|
+
|
70
|
+
return result
|
71
|
+
|
72
|
+
|
73
|
+
def standardCounter(text: str) -> tuple[int, int, int]:
|
74
|
+
"""A counter that counts paragraphs, words and characters.
|
75
|
+
This is the standard counter that includes headings in the word and
|
76
|
+
character counts.
|
77
|
+
"""
|
78
|
+
cCount = 0
|
79
|
+
wCount = 0
|
80
|
+
pCount = 0
|
81
|
+
prevEmpty = True
|
82
|
+
|
83
|
+
for line in preProcessText(text):
|
84
|
+
|
85
|
+
countPara = True
|
86
|
+
if not line:
|
87
|
+
prevEmpty = True
|
88
|
+
continue
|
89
|
+
|
90
|
+
if line[0] == "#":
|
91
|
+
if line[:5] == "#### ":
|
92
|
+
line = line[5:]
|
93
|
+
countPara = False
|
94
|
+
elif line[:4] == "### ":
|
95
|
+
line = line[4:]
|
96
|
+
countPara = False
|
97
|
+
elif line[:3] == "## ":
|
98
|
+
line = line[3:]
|
99
|
+
countPara = False
|
100
|
+
elif line[:2] == "# ":
|
101
|
+
line = line[2:]
|
102
|
+
countPara = False
|
103
|
+
elif line[:3] == "#! ":
|
104
|
+
line = line[3:]
|
105
|
+
countPara = False
|
106
|
+
elif line[:4] == "##! ":
|
107
|
+
line = line[4:]
|
108
|
+
countPara = False
|
109
|
+
elif line[:5] == "###! ":
|
110
|
+
line = line[5:]
|
111
|
+
countPara = False
|
112
|
+
|
113
|
+
wCount += len(line.split())
|
114
|
+
cCount += len(line)
|
115
|
+
if countPara and prevEmpty:
|
116
|
+
pCount += 1
|
117
|
+
|
118
|
+
prevEmpty = not countPara
|
119
|
+
|
120
|
+
return cCount, wCount, pCount
|
121
|
+
|
122
|
+
|
123
|
+
def bodyTextCounter(text: str) -> tuple[int, int, int]:
|
124
|
+
"""A counter that counts body text words, characters, and characters
|
125
|
+
without white spaces.
|
126
|
+
"""
|
127
|
+
wCount = 0
|
128
|
+
cCount = 0
|
129
|
+
sCount = 0
|
130
|
+
|
131
|
+
for line in preProcessText(text, keepHeaders=False):
|
132
|
+
words = line.split()
|
133
|
+
wCount += len(words)
|
134
|
+
cCount += len(line)
|
135
|
+
sCount += len("".join(words))
|
136
|
+
|
137
|
+
return wCount, cCount, sCount
|