novelWriter 2.5.1__py3-none-any.whl → 2.6b1__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.5.1.dist-info → novelWriter-2.6b1.dist-info}/METADATA +2 -1
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/RECORD +61 -56
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +3 -3
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +1 -0
- novelwriter/assets/icons/typicons_dark/mixed_copy.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +1 -0
- novelwriter/assets/icons/typicons_light/mixed_copy.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +63 -0
- novelwriter/config.py +10 -3
- novelwriter/constants.py +153 -60
- novelwriter/core/buildsettings.py +66 -39
- novelwriter/core/coretools.py +34 -22
- novelwriter/core/docbuild.py +130 -169
- novelwriter/core/index.py +29 -18
- novelwriter/core/item.py +2 -2
- novelwriter/core/options.py +4 -1
- novelwriter/core/spellcheck.py +9 -14
- novelwriter/dialogs/preferences.py +45 -32
- novelwriter/dialogs/projectsettings.py +3 -3
- novelwriter/enum.py +29 -23
- novelwriter/extensions/configlayout.py +24 -11
- novelwriter/extensions/modified.py +13 -1
- novelwriter/extensions/pagedsidebar.py +5 -5
- novelwriter/formats/shared.py +155 -0
- novelwriter/formats/todocx.py +1195 -0
- novelwriter/formats/tohtml.py +452 -0
- novelwriter/{core → formats}/tokenizer.py +483 -485
- novelwriter/formats/tomarkdown.py +217 -0
- novelwriter/{core → formats}/toodt.py +270 -320
- novelwriter/formats/toqdoc.py +436 -0
- novelwriter/formats/toraw.py +91 -0
- novelwriter/gui/doceditor.py +240 -193
- novelwriter/gui/dochighlight.py +96 -84
- novelwriter/gui/docviewer.py +56 -30
- novelwriter/gui/docviewerpanel.py +3 -3
- novelwriter/gui/editordocument.py +17 -2
- novelwriter/gui/itemdetails.py +8 -4
- novelwriter/gui/mainmenu.py +121 -60
- novelwriter/gui/noveltree.py +35 -37
- novelwriter/gui/outline.py +186 -238
- novelwriter/gui/projtree.py +142 -131
- novelwriter/gui/sidebar.py +7 -6
- novelwriter/gui/theme.py +5 -4
- novelwriter/guimain.py +43 -155
- novelwriter/shared.py +14 -4
- novelwriter/text/counting.py +2 -0
- novelwriter/text/patterns.py +155 -59
- novelwriter/tools/manusbuild.py +1 -1
- novelwriter/tools/manuscript.py +121 -78
- novelwriter/tools/manussettings.py +403 -260
- novelwriter/tools/welcome.py +4 -4
- novelwriter/tools/writingstats.py +3 -3
- novelwriter/types.py +16 -6
- novelwriter/core/tohtml.py +0 -530
- novelwriter/core/tomarkdown.py +0 -252
- novelwriter/core/toqdoc.py +0 -419
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/top_level.txt +0 -0
novelwriter/constants.py
CHANGED
@@ -60,11 +60,14 @@ class nwConst:
|
|
60
60
|
|
61
61
|
class nwRegEx:
|
62
62
|
|
63
|
+
URL = r"https?://(?:www\.|(?!www))[\w/()@:%_\+-.~#?&=]+"
|
64
|
+
WORDS = r"\b[^\s\-\+\/–—\[\]:]+\b"
|
65
|
+
BREAK = r"(?i)(?<!\\)(\[br\]\n?)"
|
63
66
|
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
64
67
|
FMT_EB = r"(?<![\w\\])(\*{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
65
68
|
FMT_ST = r"(?<![\w\\])(~{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)"
|
66
|
-
FMT_SC = r"(?i)(?<!\\)(\[
|
67
|
-
FMT_SV = r"(?i)(?<!\\)(\[(?:footnote):)(.+?)(?<!\\)(\])"
|
69
|
+
FMT_SC = r"(?i)(?<!\\)(\[(?:b|/b|i|/i|s|/s|u|/u|m|/m|sup|/sup|sub|/sub|br)\])"
|
70
|
+
FMT_SV = r"(?i)(?<!\\)(\[(?:footnote|field):)(.+?)(?<!\\)(\])"
|
68
71
|
|
69
72
|
|
70
73
|
class nwShortcode:
|
@@ -83,21 +86,46 @@ class nwShortcode:
|
|
83
86
|
SUP_C = "[/sup]"
|
84
87
|
SUB_O = "[sub]"
|
85
88
|
SUB_C = "[/sub]"
|
89
|
+
BREAK = "[br]"
|
86
90
|
|
87
91
|
FOOTNOTE_B = "[footnote:"
|
92
|
+
FIELD_B = "[field:"
|
88
93
|
|
89
94
|
COMMENT_STYLES = {
|
90
95
|
nwComment.FOOTNOTE: "[footnote:{0}]",
|
91
96
|
nwComment.COMMENT: "[comment:{0}]",
|
92
97
|
}
|
93
98
|
|
99
|
+
FIELD_VALUE = "[field:{0}]"
|
94
100
|
|
95
|
-
|
101
|
+
|
102
|
+
class nwStyles:
|
96
103
|
|
97
104
|
H_VALID = ("H0", "H1", "H2", "H3", "H4")
|
98
105
|
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
|
99
106
|
H_SIZES = {0: 2.50, 1: 2.00, 2: 1.75, 3: 1.50, 4: 1.25}
|
100
107
|
|
108
|
+
T_LABEL = {
|
109
|
+
"H0": QT_TRANSLATE_NOOP("Constant", "Title"),
|
110
|
+
"H1": QT_TRANSLATE_NOOP("Constant", "Heading 1 (Partition)"),
|
111
|
+
"H2": QT_TRANSLATE_NOOP("Constant", "Heading 2 (Chapter)"),
|
112
|
+
"H3": QT_TRANSLATE_NOOP("Constant", "Heading 3 (Scene)"),
|
113
|
+
"H4": QT_TRANSLATE_NOOP("Constant", "Heading 4 (Section)"),
|
114
|
+
"TT": QT_TRANSLATE_NOOP("Constant", "Text Paragraph"),
|
115
|
+
"SP": QT_TRANSLATE_NOOP("Constant", "Scene Separator"),
|
116
|
+
}
|
117
|
+
T_MARGIN = {
|
118
|
+
"H0": (1.42, 0.50), # Title margins
|
119
|
+
"H1": (1.42, 0.50), # Heading 1 margins
|
120
|
+
"H2": (1.67, 0.50), # Heading 2 margins
|
121
|
+
"H3": (1.17, 0.50), # Heading 3 margins
|
122
|
+
"H4": (1.17, 0.50), # Heading 4 margins
|
123
|
+
"TT": (0.00, 0.58), # Text margins
|
124
|
+
"SP": (1.17, 1.17), # Separator margins
|
125
|
+
"MT": (0.00, 0.58), # Meta margins
|
126
|
+
"FT": (1.42, 0.47), # Footnote margins
|
127
|
+
}
|
128
|
+
|
101
129
|
|
102
130
|
class nwFiles:
|
103
131
|
|
@@ -121,34 +149,40 @@ class nwFiles:
|
|
121
149
|
|
122
150
|
class nwKeyWords:
|
123
151
|
|
124
|
-
TAG_KEY
|
125
|
-
POV_KEY
|
126
|
-
FOCUS_KEY
|
127
|
-
CHAR_KEY
|
128
|
-
PLOT_KEY
|
129
|
-
TIME_KEY
|
130
|
-
WORLD_KEY
|
131
|
-
OBJECT_KEY
|
132
|
-
ENTITY_KEY
|
133
|
-
CUSTOM_KEY
|
152
|
+
TAG_KEY = "@tag"
|
153
|
+
POV_KEY = "@pov"
|
154
|
+
FOCUS_KEY = "@focus"
|
155
|
+
CHAR_KEY = "@char"
|
156
|
+
PLOT_KEY = "@plot"
|
157
|
+
TIME_KEY = "@time"
|
158
|
+
WORLD_KEY = "@location"
|
159
|
+
OBJECT_KEY = "@object"
|
160
|
+
ENTITY_KEY = "@entity"
|
161
|
+
CUSTOM_KEY = "@custom"
|
162
|
+
STORY_KEY = "@story"
|
163
|
+
MENTION_KEY = "@mention"
|
164
|
+
|
165
|
+
# Note: The order here affects the order of menu entries
|
166
|
+
ALL_KEYS = [
|
167
|
+
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
|
168
|
+
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY,
|
169
|
+
]
|
134
170
|
|
135
171
|
# Set of Valid Keys
|
136
|
-
VALID_KEYS =
|
137
|
-
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY,
|
138
|
-
WORLD_KEY, OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY
|
139
|
-
}
|
172
|
+
VALID_KEYS = set(ALL_KEYS)
|
140
173
|
|
141
174
|
# Map from Keys to Item Class
|
142
175
|
KEY_CLASS = {
|
143
|
-
POV_KEY:
|
144
|
-
FOCUS_KEY:
|
145
|
-
CHAR_KEY:
|
146
|
-
PLOT_KEY:
|
147
|
-
TIME_KEY:
|
148
|
-
WORLD_KEY:
|
149
|
-
OBJECT_KEY:
|
150
|
-
ENTITY_KEY:
|
151
|
-
CUSTOM_KEY:
|
176
|
+
POV_KEY: nwItemClass.CHARACTER,
|
177
|
+
FOCUS_KEY: nwItemClass.CHARACTER,
|
178
|
+
CHAR_KEY: nwItemClass.CHARACTER,
|
179
|
+
PLOT_KEY: nwItemClass.PLOT,
|
180
|
+
TIME_KEY: nwItemClass.TIMELINE,
|
181
|
+
WORLD_KEY: nwItemClass.WORLD,
|
182
|
+
OBJECT_KEY: nwItemClass.OBJECT,
|
183
|
+
ENTITY_KEY: nwItemClass.ENTITY,
|
184
|
+
CUSTOM_KEY: nwItemClass.CUSTOM,
|
185
|
+
STORY_KEY: nwItemClass.NOVEL,
|
152
186
|
}
|
153
187
|
|
154
188
|
|
@@ -165,6 +199,29 @@ class nwLists:
|
|
165
199
|
]
|
166
200
|
|
167
201
|
|
202
|
+
class nwStats:
|
203
|
+
|
204
|
+
CHARS_ALL = "allChars"
|
205
|
+
CHARS_TEXT = "textChars"
|
206
|
+
CHARS_TITLE = "titleChars"
|
207
|
+
PARAGRAPHS = "paragraphCount"
|
208
|
+
TITLES = "titleCount"
|
209
|
+
WCHARS_ALL = "allWordChars"
|
210
|
+
WCHARS_TEXT = "textWordChars"
|
211
|
+
WCHARS_TITLE = "titleWordChars"
|
212
|
+
WORDS_ALL = "allWords"
|
213
|
+
WORDS_TEXT = "textWords"
|
214
|
+
WORDS_TITLE = "titleWords"
|
215
|
+
|
216
|
+
# Note: The order here affects the order of menu entries
|
217
|
+
ALL_FIELDS = [
|
218
|
+
WORDS_ALL, WORDS_TEXT, WORDS_TITLE,
|
219
|
+
CHARS_ALL, CHARS_TEXT, CHARS_TITLE,
|
220
|
+
WCHARS_ALL, WCHARS_TEXT, WCHARS_TITLE,
|
221
|
+
PARAGRAPHS, TITLES,
|
222
|
+
]
|
223
|
+
|
224
|
+
|
168
225
|
class nwLabels:
|
169
226
|
|
170
227
|
CLASS_NAME = {
|
@@ -212,53 +269,89 @@ class nwLabels:
|
|
212
269
|
"note": QT_TRANSLATE_NOOP("Constant", "Project Note"),
|
213
270
|
}
|
214
271
|
KEY_NAME = {
|
215
|
-
nwKeyWords.TAG_KEY:
|
216
|
-
nwKeyWords.POV_KEY:
|
217
|
-
nwKeyWords.FOCUS_KEY:
|
218
|
-
nwKeyWords.CHAR_KEY:
|
219
|
-
nwKeyWords.PLOT_KEY:
|
220
|
-
nwKeyWords.TIME_KEY:
|
221
|
-
nwKeyWords.WORLD_KEY:
|
222
|
-
nwKeyWords.OBJECT_KEY:
|
223
|
-
nwKeyWords.ENTITY_KEY:
|
224
|
-
nwKeyWords.CUSTOM_KEY:
|
272
|
+
nwKeyWords.TAG_KEY: QT_TRANSLATE_NOOP("Constant", "Tag"),
|
273
|
+
nwKeyWords.POV_KEY: QT_TRANSLATE_NOOP("Constant", "Point of View"),
|
274
|
+
nwKeyWords.FOCUS_KEY: QT_TRANSLATE_NOOP("Constant", "Focus"),
|
275
|
+
nwKeyWords.CHAR_KEY: QT_TRANSLATE_NOOP("Constant", "Characters"),
|
276
|
+
nwKeyWords.PLOT_KEY: QT_TRANSLATE_NOOP("Constant", "Plot"),
|
277
|
+
nwKeyWords.TIME_KEY: QT_TRANSLATE_NOOP("Constant", "Timeline"),
|
278
|
+
nwKeyWords.WORLD_KEY: QT_TRANSLATE_NOOP("Constant", "Locations"),
|
279
|
+
nwKeyWords.OBJECT_KEY: QT_TRANSLATE_NOOP("Constant", "Objects"),
|
280
|
+
nwKeyWords.ENTITY_KEY: QT_TRANSLATE_NOOP("Constant", "Entities"),
|
281
|
+
nwKeyWords.CUSTOM_KEY: QT_TRANSLATE_NOOP("Constant", "Custom"),
|
282
|
+
nwKeyWords.STORY_KEY: QT_TRANSLATE_NOOP("Constant", "Story"),
|
283
|
+
nwKeyWords.MENTION_KEY: QT_TRANSLATE_NOOP("Constant", "Mentions"),
|
284
|
+
}
|
285
|
+
KEY_SHORTCUT = {
|
286
|
+
nwKeyWords.TAG_KEY: "Ctrl+K, G",
|
287
|
+
nwKeyWords.POV_KEY: "Ctrl+K, V",
|
288
|
+
nwKeyWords.FOCUS_KEY: "Ctrl+K, F",
|
289
|
+
nwKeyWords.CHAR_KEY: "Ctrl+K, C",
|
290
|
+
nwKeyWords.PLOT_KEY: "Ctrl+K, P",
|
291
|
+
nwKeyWords.TIME_KEY: "Ctrl+K, T",
|
292
|
+
nwKeyWords.WORLD_KEY: "Ctrl+K, L",
|
293
|
+
nwKeyWords.OBJECT_KEY: "Ctrl+K, O",
|
294
|
+
nwKeyWords.ENTITY_KEY: "Ctrl+K, E",
|
295
|
+
nwKeyWords.CUSTOM_KEY: "Ctrl+K, X",
|
296
|
+
nwKeyWords.STORY_KEY: "Ctrl+K, N",
|
297
|
+
nwKeyWords.MENTION_KEY: "Ctrl+K, M",
|
225
298
|
}
|
226
299
|
OUTLINE_COLS = {
|
227
|
-
nwOutline.TITLE:
|
228
|
-
nwOutline.LEVEL:
|
229
|
-
nwOutline.LABEL:
|
230
|
-
nwOutline.LINE:
|
231
|
-
nwOutline.
|
232
|
-
nwOutline.
|
233
|
-
nwOutline.
|
234
|
-
nwOutline.
|
235
|
-
nwOutline.
|
236
|
-
nwOutline.
|
237
|
-
nwOutline.
|
238
|
-
nwOutline.
|
239
|
-
nwOutline.
|
240
|
-
nwOutline.
|
241
|
-
nwOutline.
|
242
|
-
nwOutline.
|
243
|
-
nwOutline.
|
300
|
+
nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"),
|
301
|
+
nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"),
|
302
|
+
nwOutline.LABEL: QT_TRANSLATE_NOOP("Constant", "Document"),
|
303
|
+
nwOutline.LINE: QT_TRANSLATE_NOOP("Constant", "Line"),
|
304
|
+
nwOutline.STATUS: QT_TRANSLATE_NOOP("Constant", "Status"),
|
305
|
+
nwOutline.CCOUNT: QT_TRANSLATE_NOOP("Constant", "Chars"),
|
306
|
+
nwOutline.WCOUNT: QT_TRANSLATE_NOOP("Constant", "Words"),
|
307
|
+
nwOutline.PCOUNT: QT_TRANSLATE_NOOP("Constant", "Pars"),
|
308
|
+
nwOutline.POV: QT_TRANSLATE_NOOP("Constant", "POV"),
|
309
|
+
nwOutline.FOCUS: QT_TRANSLATE_NOOP("Constant", "Focus"),
|
310
|
+
nwOutline.CHAR: KEY_NAME[nwKeyWords.CHAR_KEY],
|
311
|
+
nwOutline.PLOT: KEY_NAME[nwKeyWords.PLOT_KEY],
|
312
|
+
nwOutline.WORLD: KEY_NAME[nwKeyWords.WORLD_KEY],
|
313
|
+
nwOutline.TIME: KEY_NAME[nwKeyWords.TIME_KEY],
|
314
|
+
nwOutline.OBJECT: KEY_NAME[nwKeyWords.OBJECT_KEY],
|
315
|
+
nwOutline.ENTITY: KEY_NAME[nwKeyWords.ENTITY_KEY],
|
316
|
+
nwOutline.CUSTOM: KEY_NAME[nwKeyWords.CUSTOM_KEY],
|
317
|
+
nwOutline.STORY: KEY_NAME[nwKeyWords.STORY_KEY],
|
318
|
+
nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY],
|
319
|
+
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
|
320
|
+
}
|
321
|
+
STATS_NAME = {
|
322
|
+
nwStats.CHARS_ALL: QT_TRANSLATE_NOOP("Constant", "Characters"),
|
323
|
+
nwStats.CHARS_TEXT: QT_TRANSLATE_NOOP("Constant", "Characters in Text"),
|
324
|
+
nwStats.CHARS_TITLE: QT_TRANSLATE_NOOP("Constant", "Characters in Headings"),
|
325
|
+
nwStats.PARAGRAPHS: QT_TRANSLATE_NOOP("Constant", "Paragraphs"),
|
326
|
+
nwStats.TITLES: QT_TRANSLATE_NOOP("Constant", "Headings"),
|
327
|
+
nwStats.WCHARS_ALL: QT_TRANSLATE_NOOP("Constant", "Characters, No Spaces"),
|
328
|
+
nwStats.WCHARS_TEXT: QT_TRANSLATE_NOOP("Constant", "Characters in Text, No Spaces"),
|
329
|
+
nwStats.WCHARS_TITLE: QT_TRANSLATE_NOOP("Constant", "Characters in Headings, No Spaces"),
|
330
|
+
nwStats.WORDS_ALL: QT_TRANSLATE_NOOP("Constant", "Words"),
|
331
|
+
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Constant", "Words in Text"),
|
332
|
+
nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Constant", "Words in Headings"),
|
244
333
|
}
|
245
334
|
BUILD_FMT = {
|
246
335
|
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
|
247
336
|
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
|
248
|
-
nwBuildFmt.
|
337
|
+
nwBuildFmt.DOCX: QT_TRANSLATE_NOOP("Constant", "Microsoft Word Document (.docx)"),
|
338
|
+
nwBuildFmt.HTML: QT_TRANSLATE_NOOP("Constant", "HTML 5 (.html)"),
|
249
339
|
nwBuildFmt.NWD: QT_TRANSLATE_NOOP("Constant", "novelWriter Markup (.txt)"),
|
250
340
|
nwBuildFmt.STD_MD: QT_TRANSLATE_NOOP("Constant", "Standard Markdown (.md)"),
|
251
341
|
nwBuildFmt.EXT_MD: QT_TRANSLATE_NOOP("Constant", "Extended Markdown (.md)"),
|
252
|
-
nwBuildFmt.
|
342
|
+
nwBuildFmt.PDF: QT_TRANSLATE_NOOP("Constant", "Portable Document Format (.pdf)"),
|
343
|
+
nwBuildFmt.J_HTML: QT_TRANSLATE_NOOP("Constant", "JSON + HTML 5 (.json)"),
|
253
344
|
nwBuildFmt.J_NWD: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markup (.json)"),
|
254
345
|
}
|
255
346
|
BUILD_EXT = {
|
256
347
|
nwBuildFmt.ODT: ".odt",
|
257
348
|
nwBuildFmt.FODT: ".fodt",
|
349
|
+
nwBuildFmt.DOCX: ".docx",
|
258
350
|
nwBuildFmt.HTML: ".html",
|
259
351
|
nwBuildFmt.NWD: ".txt",
|
260
352
|
nwBuildFmt.STD_MD: ".md",
|
261
353
|
nwBuildFmt.EXT_MD: ".md",
|
354
|
+
nwBuildFmt.PDF: ".pdf",
|
262
355
|
nwBuildFmt.J_HTML: ".json",
|
263
356
|
nwBuildFmt.J_NWD: ".json",
|
264
357
|
}
|
@@ -343,11 +436,11 @@ class nwHeadFmt:
|
|
343
436
|
CHAR_POV, CHAR_FOCUS
|
344
437
|
]
|
345
438
|
|
346
|
-
#
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
439
|
+
# Document Page Header
|
440
|
+
DOC_PROJECT = "{Project}"
|
441
|
+
DOC_AUTHOR = "{Author}"
|
442
|
+
DOC_PAGE = "{Page}"
|
443
|
+
DOC_AUTO = "{Project} / {Author} / {Page}"
|
351
444
|
|
352
445
|
|
353
446
|
class nwQuotes:
|
@@ -36,7 +36,7 @@ from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
|
36
36
|
|
37
37
|
from novelwriter import CONFIG
|
38
38
|
from novelwriter.common import checkUuid, isHandle, jsonEncode
|
39
|
-
from novelwriter.constants import nwFiles, nwHeadFmt
|
39
|
+
from novelwriter.constants import nwFiles, nwHeadFmt, nwStyles
|
40
40
|
from novelwriter.core.project import NWProject
|
41
41
|
from novelwriter.enum import nwBuildFmt
|
42
42
|
from novelwriter.error import logException
|
@@ -45,29 +45,30 @@ logger = logging.getLogger(__name__)
|
|
45
45
|
|
46
46
|
# The Settings Template
|
47
47
|
# =====================
|
48
|
-
# Each entry contains a tuple on the form:
|
49
|
-
# (type, default, [min value, max value])
|
48
|
+
# Each entry contains a tuple on the form: (type, default)
|
50
49
|
|
51
|
-
SETTINGS_TEMPLATE = {
|
50
|
+
SETTINGS_TEMPLATE: dict[str, tuple[type, str | int | float | bool]] = {
|
52
51
|
"filter.includeNovel": (bool, True),
|
53
52
|
"filter.includeNotes": (bool, False),
|
54
53
|
"filter.includeInactive": (bool, False),
|
55
|
-
"headings.
|
54
|
+
"headings.fmtPart": (str, nwHeadFmt.TITLE),
|
56
55
|
"headings.fmtChapter": (str, nwHeadFmt.TITLE),
|
57
56
|
"headings.fmtUnnumbered": (str, nwHeadFmt.TITLE),
|
58
57
|
"headings.fmtScene": (str, "* * *"),
|
59
58
|
"headings.fmtAltScene": (str, ""),
|
60
59
|
"headings.fmtSection": (str, ""),
|
61
|
-
"headings.
|
60
|
+
"headings.hidePart": (bool, False),
|
62
61
|
"headings.hideChapter": (bool, False),
|
63
62
|
"headings.hideUnnumbered": (bool, False),
|
64
63
|
"headings.hideScene": (bool, False),
|
65
64
|
"headings.hideAltScene": (bool, False),
|
66
65
|
"headings.hideSection": (bool, True),
|
67
66
|
"headings.centerTitle": (bool, True),
|
67
|
+
"headings.centerPart": (bool, True),
|
68
68
|
"headings.centerChapter": (bool, False),
|
69
69
|
"headings.centerScene": (bool, False),
|
70
|
-
"headings.breakTitle": (bool,
|
70
|
+
"headings.breakTitle": (bool, False),
|
71
|
+
"headings.breakPart": (bool, True),
|
71
72
|
"headings.breakChapter": (bool, True),
|
72
73
|
"headings.breakScene": (bool, False),
|
73
74
|
"text.includeSynopsis": (bool, False),
|
@@ -77,7 +78,7 @@ SETTINGS_TEMPLATE = {
|
|
77
78
|
"text.ignoredKeywords": (str, ""),
|
78
79
|
"text.addNoteHeadings": (bool, True),
|
79
80
|
"format.textFont": (str, CONFIG.textFont.toString()),
|
80
|
-
"format.lineHeight": (float, 1.15
|
81
|
+
"format.lineHeight": (float, 1.15),
|
81
82
|
"format.justifyText": (bool, False),
|
82
83
|
"format.stripUnicode": (bool, False),
|
83
84
|
"format.replaceTabs": (bool, False),
|
@@ -86,6 +87,20 @@ SETTINGS_TEMPLATE = {
|
|
86
87
|
"format.firstLineIndent": (bool, False),
|
87
88
|
"format.firstIndentWidth": (float, 1.4),
|
88
89
|
"format.indentFirstPar": (bool, False),
|
90
|
+
"format.titleMarginT": (float, nwStyles.T_MARGIN["H0"][0]),
|
91
|
+
"format.titleMarginB": (float, nwStyles.T_MARGIN["H0"][1]),
|
92
|
+
"format.h1MarginT": (float, nwStyles.T_MARGIN["H1"][0]),
|
93
|
+
"format.h1MarginB": (float, nwStyles.T_MARGIN["H1"][1]),
|
94
|
+
"format.h2MarginT": (float, nwStyles.T_MARGIN["H2"][0]),
|
95
|
+
"format.h2MarginB": (float, nwStyles.T_MARGIN["H2"][1]),
|
96
|
+
"format.h3MarginT": (float, nwStyles.T_MARGIN["H3"][0]),
|
97
|
+
"format.h3MarginB": (float, nwStyles.T_MARGIN["H3"][1]),
|
98
|
+
"format.h4MarginT": (float, nwStyles.T_MARGIN["H4"][0]),
|
99
|
+
"format.h4MarginB": (float, nwStyles.T_MARGIN["H4"][1]),
|
100
|
+
"format.textMarginT": (float, nwStyles.T_MARGIN["TT"][0]),
|
101
|
+
"format.textMarginB": (float, nwStyles.T_MARGIN["TT"][1]),
|
102
|
+
"format.sepMarginT": (float, nwStyles.T_MARGIN["SP"][0]),
|
103
|
+
"format.sepMarginB": (float, nwStyles.T_MARGIN["SP"][1]),
|
89
104
|
"format.pageUnit": (str, "cm"),
|
90
105
|
"format.pageSize": (str, "A4"),
|
91
106
|
"format.pageWidth": (float, 21.0),
|
@@ -94,9 +109,11 @@ SETTINGS_TEMPLATE = {
|
|
94
109
|
"format.bottomMargin": (float, 2.0),
|
95
110
|
"format.leftMargin": (float, 2.0),
|
96
111
|
"format.rightMargin": (float, 2.0),
|
97
|
-
"
|
98
|
-
"
|
99
|
-
"
|
112
|
+
"doc.pageHeader": (str, nwHeadFmt.DOC_AUTO),
|
113
|
+
"doc.pageCountOffset": (int, 0),
|
114
|
+
"doc.colorHeadings": (bool, True),
|
115
|
+
"doc.scaleHeadings": (bool, True),
|
116
|
+
"doc.boldHeadings": (bool, True),
|
100
117
|
"html.addStyles": (bool, True),
|
101
118
|
"html.preserveTabs": (bool, False),
|
102
119
|
}
|
@@ -108,12 +125,16 @@ SETTINGS_LABELS = {
|
|
108
125
|
"filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"),
|
109
126
|
|
110
127
|
"headings": QT_TRANSLATE_NOOP("Builds", "Headings"),
|
111
|
-
"headings.
|
128
|
+
"headings.fmtPart": QT_TRANSLATE_NOOP("Builds", "Partition Format"),
|
112
129
|
"headings.fmtChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Format"),
|
113
130
|
"headings.fmtUnnumbered": QT_TRANSLATE_NOOP("Builds", "Unnumbered Format"),
|
114
131
|
"headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Format"),
|
115
132
|
"headings.fmtAltScene": QT_TRANSLATE_NOOP("Builds", "Alt. Scene Format"),
|
116
133
|
"headings.fmtSection": QT_TRANSLATE_NOOP("Builds", "Section Format"),
|
134
|
+
"headings.styleTitle": QT_TRANSLATE_NOOP("Builds", "Title Styling"),
|
135
|
+
"headings.stylePart": QT_TRANSLATE_NOOP("Builds", "Partition Styling"),
|
136
|
+
"headings.styleChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Styling"),
|
137
|
+
"headings.styleScene": QT_TRANSLATE_NOOP("Builds", "Scene Styling"),
|
117
138
|
|
118
139
|
"text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"),
|
119
140
|
"text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"),
|
@@ -121,13 +142,11 @@ SETTINGS_LABELS = {
|
|
121
142
|
"text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"),
|
122
143
|
"text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"),
|
123
144
|
"text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"),
|
124
|
-
"text.grpInsert": QT_TRANSLATE_NOOP("Builds", "Insert Content"),
|
125
145
|
"text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"),
|
126
146
|
|
127
147
|
"format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"),
|
128
148
|
"format.textFont": QT_TRANSLATE_NOOP("Builds", "Text Font"),
|
129
149
|
"format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"),
|
130
|
-
"format.grpOptions": QT_TRANSLATE_NOOP("Builds", "Text Options"),
|
131
150
|
"format.justifyText": QT_TRANSLATE_NOOP("Builds", "Justify Text Margins"),
|
132
151
|
"format.stripUnicode": QT_TRANSLATE_NOOP("Builds", "Replace Unicode Characters"),
|
133
152
|
"format.replaceTabs": QT_TRANSLATE_NOOP("Builds", "Replace Tabs with Spaces"),
|
@@ -139,26 +158,31 @@ SETTINGS_LABELS = {
|
|
139
158
|
"format.firstIndentWidth": QT_TRANSLATE_NOOP("Builds", "Indent Width"),
|
140
159
|
"format.indentFirstPar": QT_TRANSLATE_NOOP("Builds", "Indent First Paragraph"),
|
141
160
|
|
161
|
+
"format.grpMargins": QT_TRANSLATE_NOOP("Builds", "Text Margins"),
|
162
|
+
|
142
163
|
"format.grpPage": QT_TRANSLATE_NOOP("Builds", "Page Layout"),
|
143
164
|
"format.pageUnit": QT_TRANSLATE_NOOP("Builds", "Unit"),
|
144
165
|
"format.pageSize": QT_TRANSLATE_NOOP("Builds", "Page Size"),
|
145
|
-
"format.
|
146
|
-
|
147
|
-
"
|
148
|
-
"
|
149
|
-
"
|
150
|
-
"
|
151
|
-
|
152
|
-
"
|
153
|
-
|
154
|
-
"
|
155
|
-
"odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"),
|
156
|
-
|
157
|
-
"html": QT_TRANSLATE_NOOP("Builds", "HTML (.html)"),
|
166
|
+
"format.pageMargins": QT_TRANSLATE_NOOP("Builds", "Page Margins"),
|
167
|
+
|
168
|
+
"doc": QT_TRANSLATE_NOOP("Builds", "Document Style"),
|
169
|
+
"doc.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"),
|
170
|
+
"doc.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"),
|
171
|
+
"doc.colorHeadings": QT_TRANSLATE_NOOP("Builds", "Add Colours to Headings"),
|
172
|
+
"doc.scaleHeadings": QT_TRANSLATE_NOOP("Builds", "Increase Size of Headings"),
|
173
|
+
"doc.boldHeadings": QT_TRANSLATE_NOOP("Builds", "Bold Headings"),
|
174
|
+
|
175
|
+
"html": QT_TRANSLATE_NOOP("Builds", "HTML Options"),
|
158
176
|
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"),
|
159
177
|
"html.preserveTabs": QT_TRANSLATE_NOOP("Builds", "Preserve Tab Characters"),
|
160
178
|
}
|
161
179
|
|
180
|
+
RENAMED = {
|
181
|
+
"odt.addColours": "doc.addColours",
|
182
|
+
"odt.pageHeader": "doc.pageHeader",
|
183
|
+
"odt.pageCountOffset": "doc.pageCountOffset",
|
184
|
+
}
|
185
|
+
|
162
186
|
|
163
187
|
class FilterMode(Enum):
|
164
188
|
"""The decision reason for an item in a filtered project."""
|
@@ -348,18 +372,12 @@ class BuildSettings:
|
|
348
372
|
self._changed = True
|
349
373
|
return
|
350
374
|
|
351
|
-
def setValue(self, key: str, value: str | int |
|
375
|
+
def setValue(self, key: str, value: str | int | float | bool) -> None:
|
352
376
|
"""Set a specific value for a build setting."""
|
353
|
-
if key
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
return False
|
358
|
-
if len(definition) == 4 and isinstance(value, (int, float)):
|
359
|
-
value = min(max(value, definition[2]), definition[3])
|
360
|
-
self._changed = value != self._settings[key]
|
361
|
-
self._settings[key] = value
|
362
|
-
return True
|
377
|
+
if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
|
378
|
+
self._changed = value != self._settings[key]
|
379
|
+
self._settings[key] = value
|
380
|
+
return
|
363
381
|
|
364
382
|
##
|
365
383
|
# Methods
|
@@ -478,12 +496,21 @@ class BuildSettings:
|
|
478
496
|
self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()}
|
479
497
|
if isinstance(settings, dict):
|
480
498
|
for key, value in settings.items():
|
481
|
-
self.setValue(key, value)
|
499
|
+
self.setValue(RENAMED.get(key, key), value)
|
482
500
|
|
483
501
|
self._changed = False
|
484
502
|
|
485
503
|
return
|
486
504
|
|
505
|
+
@classmethod
|
506
|
+
def duplicate(cls, source: BuildSettings) -> BuildSettings:
|
507
|
+
"""Make a copy of another build."""
|
508
|
+
cls = BuildSettings()
|
509
|
+
cls.unpack(source.pack())
|
510
|
+
cls._uuid = str(uuid.uuid4())
|
511
|
+
cls._name = f"{source.name} 2"
|
512
|
+
return cls
|
513
|
+
|
487
514
|
|
488
515
|
class BuildCollection:
|
489
516
|
"""Core: Build Collection Class
|
novelwriter/core/coretools.py
CHANGED
@@ -27,6 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
27
27
|
from __future__ import annotations
|
28
28
|
|
29
29
|
import logging
|
30
|
+
import re
|
30
31
|
import shutil
|
31
32
|
|
32
33
|
from collections.abc import Iterable
|
@@ -34,11 +35,11 @@ from functools import partial
|
|
34
35
|
from pathlib import Path
|
35
36
|
from zipfile import ZipFile, is_zipfile
|
36
37
|
|
37
|
-
from PyQt5.QtCore import QCoreApplication
|
38
|
+
from PyQt5.QtCore import QCoreApplication
|
38
39
|
|
39
40
|
from novelwriter import CONFIG, SHARED
|
40
41
|
from novelwriter.common import isHandle, minmax, simplified
|
41
|
-
from novelwriter.constants import nwConst, nwFiles, nwItemClass
|
42
|
+
from novelwriter.constants import nwConst, nwFiles, nwItemClass, nwStats
|
42
43
|
from novelwriter.core.item import NWItem
|
43
44
|
from novelwriter.core.project import NWProject
|
44
45
|
from novelwriter.core.storage import NWStorageCreate
|
@@ -297,8 +298,8 @@ class DocDuplicator:
|
|
297
298
|
class DocSearch:
|
298
299
|
|
299
300
|
def __init__(self) -> None:
|
300
|
-
self._regEx =
|
301
|
-
self.
|
301
|
+
self._regEx = re.compile("")
|
302
|
+
self._opts = re.UNICODE | re.IGNORECASE
|
302
303
|
self._words = False
|
303
304
|
self._escape = True
|
304
305
|
return
|
@@ -309,10 +310,9 @@ class DocSearch:
|
|
309
310
|
|
310
311
|
def setCaseSensitive(self, state: bool) -> None:
|
311
312
|
"""Set the case sensitive search flag."""
|
312
|
-
|
313
|
+
self._opts = re.UNICODE
|
313
314
|
if not state:
|
314
|
-
|
315
|
-
self._regEx.setPatternOptions(opts)
|
315
|
+
self._opts |= re.IGNORECASE
|
316
316
|
return
|
317
317
|
|
318
318
|
def setWholeWords(self, state: bool) -> None:
|
@@ -329,8 +329,8 @@ class DocSearch:
|
|
329
329
|
self, project: NWProject, search: str
|
330
330
|
) -> Iterable[tuple[NWItem, list[tuple[int, int, str]], bool]]:
|
331
331
|
"""Iteratively search through documents in a project."""
|
332
|
-
self._regEx.
|
333
|
-
logger.debug("Searching with pattern '%s'", self._regEx.pattern
|
332
|
+
self._regEx = re.compile(self._buildPattern(search), self._opts)
|
333
|
+
logger.debug("Searching with pattern '%s'", self._regEx.pattern)
|
334
334
|
storage = project.storage
|
335
335
|
for item in project.tree:
|
336
336
|
if item.isFileType():
|
@@ -340,14 +340,12 @@ class DocSearch:
|
|
340
340
|
|
341
341
|
def searchText(self, text: str) -> tuple[list[tuple[int, int, str]], bool]:
|
342
342
|
"""Search a piece of text for RegEx matches."""
|
343
|
-
rxItt = self._regEx.globalMatch(text)
|
344
343
|
count = 0
|
345
344
|
capped = False
|
346
345
|
results = []
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
num = rxMatch.capturedLength()
|
346
|
+
for res in self._regEx.finditer(text):
|
347
|
+
pos = res.start(0)
|
348
|
+
num = len(res.group(0))
|
351
349
|
lim = text[:pos].rfind("\n") + 1
|
352
350
|
cut = text[lim:pos].rfind(" ") + lim + 1
|
353
351
|
context = text[cut:cut+100].partition("\n")[0]
|
@@ -366,7 +364,7 @@ class DocSearch:
|
|
366
364
|
def _buildPattern(self, search: str) -> str:
|
367
365
|
"""Build the search pattern string."""
|
368
366
|
if self._escape:
|
369
|
-
search =
|
367
|
+
search = re.escape(search)
|
370
368
|
if self._words:
|
371
369
|
search = f"(?:^|\\b){search}(?:$|\\b)"
|
372
370
|
return search
|
@@ -430,7 +428,6 @@ class ProjectBuilder:
|
|
430
428
|
|
431
429
|
lblNewProject = self.tr("New Project")
|
432
430
|
lblTitlePage = self.tr("Title Page")
|
433
|
-
lblByAuthors = self.tr("By")
|
434
431
|
|
435
432
|
# Settings
|
436
433
|
project.data.setUuid(None)
|
@@ -443,14 +440,29 @@ class ProjectBuilder:
|
|
443
440
|
# Add Root Folders
|
444
441
|
hNovelRoot = project.newRoot(nwItemClass.NOVEL)
|
445
442
|
hTitlePage = project.newFile(lblTitlePage, hNovelRoot)
|
446
|
-
novelTitle = project.data.name
|
447
|
-
|
448
|
-
titlePage = f"#! {novelTitle}\n\n"
|
449
|
-
if project.data.author:
|
450
|
-
titlePage += f">> {lblByAuthors} {project.data.author} <<\n\n"
|
451
443
|
|
444
|
+
# Generate Title Page
|
452
445
|
aDoc = project.storage.getDocument(hTitlePage)
|
453
|
-
aDoc.writeDocument(
|
446
|
+
aDoc.writeDocument((
|
447
|
+
"{author}[br]\n"
|
448
|
+
"{address} 1[br]\n"
|
449
|
+
"{address} 2 <<\n"
|
450
|
+
"\n"
|
451
|
+
"[vspace:5]\n"
|
452
|
+
"\n"
|
453
|
+
"#! {title}\n"
|
454
|
+
"\n"
|
455
|
+
">> **{by} {author}** <<\n"
|
456
|
+
"\n"
|
457
|
+
">> {count}: [field:{field}] <<\n"
|
458
|
+
).format(
|
459
|
+
author=project.data.author or "None",
|
460
|
+
address=self.tr("Address"),
|
461
|
+
title=project.data.name or "None",
|
462
|
+
by=self.tr("By"),
|
463
|
+
count=self.tr("Word Count"),
|
464
|
+
field=nwStats.WORDS_TEXT,
|
465
|
+
))
|
454
466
|
|
455
467
|
# Create a project structure based on selected root folders
|
456
468
|
# and a number of chapters and scenes selected in the
|