novelWriter 2.5.3__py3-none-any.whl → 2.6__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.3.dist-info → novelWriter-2.6.dist-info}/METADATA +2 -2
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/RECORD +123 -103
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +50 -11
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_de_DE.json +2 -0
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/i18n/project_en_US.json +2 -0
- novelwriter/assets/i18n/project_it_IT.json +2 -0
- novelwriter/assets/i18n/project_ja_JP.json +2 -0
- novelwriter/assets/i18n/project_nb_NO.json +2 -0
- novelwriter/assets/i18n/project_nl_NL.json +2 -0
- novelwriter/assets/i18n/project_pl_PL.json +2 -0
- novelwriter/assets/i18n/project_pt_BR.json +2 -0
- novelwriter/assets/i18n/project_zh_CN.json +2 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +8 -0
- novelwriter/assets/icons/typicons_dark/mixed_copy.svg +4 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +5 -0
- novelwriter/assets/icons/typicons_light/icons.conf +8 -0
- novelwriter/assets/icons/typicons_light/mixed_copy.svg +4 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_light/nw_toolbar.svg +5 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/common.py +101 -3
- novelwriter/config.py +30 -17
- novelwriter/constants.py +189 -81
- novelwriter/core/buildsettings.py +74 -40
- novelwriter/core/coretools.py +146 -148
- novelwriter/core/docbuild.py +133 -171
- novelwriter/core/document.py +1 -1
- novelwriter/core/index.py +39 -38
- novelwriter/core/item.py +42 -9
- novelwriter/core/itemmodel.py +518 -0
- novelwriter/core/options.py +5 -2
- novelwriter/core/project.py +68 -90
- novelwriter/core/projectdata.py +8 -2
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/sessions.py +1 -1
- novelwriter/core/spellcheck.py +10 -15
- novelwriter/core/status.py +24 -8
- novelwriter/core/storage.py +1 -1
- novelwriter/core/tree.py +269 -288
- novelwriter/dialogs/about.py +1 -1
- novelwriter/dialogs/docmerge.py +8 -18
- novelwriter/dialogs/docsplit.py +1 -1
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +47 -34
- novelwriter/dialogs/projectsettings.py +149 -99
- novelwriter/dialogs/quotes.py +1 -1
- novelwriter/dialogs/wordlist.py +11 -10
- novelwriter/enum.py +37 -24
- novelwriter/error.py +2 -2
- novelwriter/extensions/configlayout.py +28 -13
- novelwriter/extensions/eventfilters.py +1 -1
- novelwriter/extensions/modified.py +30 -6
- novelwriter/extensions/novelselector.py +4 -3
- novelwriter/extensions/pagedsidebar.py +9 -9
- novelwriter/extensions/progressbars.py +4 -4
- novelwriter/extensions/statusled.py +3 -3
- novelwriter/extensions/switch.py +3 -3
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +1 -1
- novelwriter/formats/shared.py +156 -0
- novelwriter/formats/todocx.py +1191 -0
- novelwriter/formats/tohtml.py +454 -0
- novelwriter/{core → formats}/tokenizer.py +497 -495
- novelwriter/formats/tomarkdown.py +218 -0
- novelwriter/{core → formats}/toodt.py +312 -433
- novelwriter/formats/toqdoc.py +486 -0
- novelwriter/formats/toraw.py +91 -0
- novelwriter/gui/doceditor.py +347 -287
- novelwriter/gui/dochighlight.py +97 -85
- novelwriter/gui/docviewer.py +90 -33
- novelwriter/gui/docviewerpanel.py +18 -26
- novelwriter/gui/editordocument.py +18 -3
- novelwriter/gui/itemdetails.py +27 -29
- novelwriter/gui/mainmenu.py +130 -64
- novelwriter/gui/noveltree.py +46 -48
- novelwriter/gui/outline.py +202 -256
- novelwriter/gui/projtree.py +590 -1242
- novelwriter/gui/search.py +11 -19
- novelwriter/gui/sidebar.py +8 -7
- novelwriter/gui/statusbar.py +20 -3
- novelwriter/gui/theme.py +11 -6
- novelwriter/guimain.py +101 -201
- novelwriter/shared.py +67 -28
- novelwriter/text/counting.py +3 -1
- novelwriter/text/patterns.py +169 -61
- novelwriter/tools/dictionaries.py +3 -3
- novelwriter/tools/lipsum.py +1 -1
- novelwriter/tools/manusbuild.py +15 -13
- novelwriter/tools/manuscript.py +121 -79
- novelwriter/tools/manussettings.py +424 -291
- novelwriter/tools/noveldetails.py +1 -1
- novelwriter/tools/welcome.py +6 -6
- novelwriter/tools/writingstats.py +4 -4
- novelwriter/types.py +25 -9
- novelwriter/core/tohtml.py +0 -530
- novelwriter/core/tomarkdown.py +0 -252
- novelwriter/core/toqdoc.py +0 -419
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/top_level.txt +0 -0
novelwriter/core/coretools.py
CHANGED
@@ -9,7 +9,7 @@ Created: 2022-11-03 [2.0rc2] ProjectBuilder
|
|
9
9
|
Created: 2023-07-20 [2.1b1] DocDuplicator
|
10
10
|
|
11
11
|
This file is a part of novelWriter
|
12
|
-
Copyright
|
12
|
+
Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
|
13
13
|
|
14
14
|
This program is free software: you can redistribute it and/or modify
|
15
15
|
it under the terms of the GNU General Public License as published by
|
@@ -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
|
@@ -55,10 +56,17 @@ class DocMerger:
|
|
55
56
|
def __init__(self, project: NWProject) -> None:
|
56
57
|
self._project = project
|
57
58
|
self._error = ""
|
58
|
-
self.
|
59
|
-
self.
|
59
|
+
self._target = None
|
60
|
+
self._text = []
|
60
61
|
return
|
61
62
|
|
63
|
+
@property
|
64
|
+
def targetHandle(self) -> str | None:
|
65
|
+
"""Get the handle of the target document."""
|
66
|
+
if self._target:
|
67
|
+
return self._target.itemHandle
|
68
|
+
return None
|
69
|
+
|
62
70
|
##
|
63
71
|
# Methods
|
64
72
|
##
|
@@ -71,63 +79,56 @@ class DocMerger:
|
|
71
79
|
"""Set the target document for the merging. Calling this
|
72
80
|
function resets the class.
|
73
81
|
"""
|
74
|
-
self.
|
75
|
-
self.
|
82
|
+
self._target = self._project.tree[tHandle]
|
83
|
+
self._text = []
|
76
84
|
return
|
77
85
|
|
78
|
-
def newTargetDoc(self,
|
86
|
+
def newTargetDoc(self, sHandle: str, label: str) -> None:
|
79
87
|
"""Create a brand new target document based on a source handle
|
80
88
|
and a new doc label. Calling this function resets the class.
|
81
89
|
"""
|
82
|
-
|
83
|
-
if
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
self._targetDoc = newHandle
|
94
|
-
self._targetText = []
|
95
|
-
|
96
|
-
return newHandle
|
90
|
+
sItem = self._project.tree[sHandle]
|
91
|
+
if sItem and sItem.itemParent:
|
92
|
+
tHandle = self._project.newFile(label, sItem.itemParent)
|
93
|
+
if nwItem := self._project.tree[tHandle]:
|
94
|
+
nwItem.setLayout(sItem.itemLayout)
|
95
|
+
nwItem.setStatus(sItem.itemStatus)
|
96
|
+
nwItem.setImport(sItem.itemImport)
|
97
|
+
nwItem.notifyToRefresh()
|
98
|
+
self._target = nwItem
|
99
|
+
self._text = []
|
100
|
+
return
|
97
101
|
|
98
|
-
def appendText(self,
|
102
|
+
def appendText(self, sHandle: str, addComment: bool, cmtPrefix: str) -> None:
|
99
103
|
"""Append text from an existing document to the text buffer."""
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n"
|
109
|
-
docText = cmtLine + docText
|
110
|
-
|
111
|
-
self._targetText.append(docText)
|
112
|
-
|
113
|
-
return True
|
104
|
+
if item := self._project.tree[sHandle]:
|
105
|
+
text = self._project.storage.getDocumentText(sHandle).rstrip("\n")
|
106
|
+
if addComment:
|
107
|
+
info = item.describeMe()
|
108
|
+
status, _ = item.getImportStatus()
|
109
|
+
text = f"% {cmtPrefix} {info}: {item.itemName} [{status}]\n\n{text}"
|
110
|
+
self._text.append(text)
|
111
|
+
return
|
114
112
|
|
115
113
|
def writeTargetDoc(self) -> bool:
|
116
114
|
"""Write the accumulated text into the designated target
|
117
115
|
document, appending any existing text.
|
118
116
|
"""
|
119
|
-
if self.
|
120
|
-
|
117
|
+
if self._target:
|
118
|
+
outDoc = self._project.storage.getDocument(self._target.itemHandle)
|
119
|
+
if text := (outDoc.readDocument() or "").rstrip("\n"):
|
120
|
+
self._text.insert(0, text)
|
121
|
+
|
122
|
+
status = outDoc.writeDocument("\n\n".join(self._text) + "\n\n")
|
123
|
+
if not status:
|
124
|
+
self._error = outDoc.getError()
|
121
125
|
|
122
|
-
|
123
|
-
|
124
|
-
self._targetText.insert(0, text)
|
126
|
+
self._project.index.reIndexHandle(self._target.itemHandle)
|
127
|
+
self._target.notifyToRefresh()
|
125
128
|
|
126
|
-
|
127
|
-
if not status:
|
128
|
-
self._error = outDoc.getError()
|
129
|
+
return status
|
129
130
|
|
130
|
-
return
|
131
|
+
return False
|
131
132
|
|
132
133
|
|
133
134
|
class DocSplitter:
|
@@ -171,23 +172,19 @@ class DocSplitter:
|
|
171
172
|
self._inFolder = False
|
172
173
|
return
|
173
174
|
|
174
|
-
def newParentFolder(self, pHandle: str, folderLabel: str) ->
|
175
|
+
def newParentFolder(self, pHandle: str, folderLabel: str) -> None:
|
175
176
|
"""Create a new folder that will be the top level parent item
|
176
177
|
for the new documents.
|
177
178
|
"""
|
178
|
-
if self._srcItem
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
self._parHandle = newHandle
|
188
|
-
self._inFolder = True
|
189
|
-
|
190
|
-
return newHandle
|
179
|
+
if self._srcItem:
|
180
|
+
nHandle = self._project.newFolder(folderLabel, pHandle)
|
181
|
+
if nwItem := self._project.tree[nHandle]:
|
182
|
+
nwItem.setStatus(self._srcItem.itemStatus)
|
183
|
+
nwItem.setImport(self._srcItem.itemImport)
|
184
|
+
nwItem.notifyToRefresh()
|
185
|
+
self._parHandle = nHandle
|
186
|
+
self._inFolder = True
|
187
|
+
return
|
191
188
|
|
192
189
|
def splitDocument(self, splitData: list, splitText: list[str]) -> None:
|
193
190
|
"""Loop through the split data record and perform the split job
|
@@ -201,58 +198,50 @@ class DocSplitter:
|
|
201
198
|
self._rawData.insert(0, (chunk, hLevel, hLabel))
|
202
199
|
return
|
203
200
|
|
204
|
-
def writeDocuments(self, docHierarchy: bool) -> Iterable[
|
201
|
+
def writeDocuments(self, docHierarchy: bool) -> Iterable[bool]:
|
205
202
|
"""An iterator that will write each document in the buffer, and
|
206
203
|
return its new handle, parent handle, and sibling handle.
|
207
204
|
"""
|
208
|
-
if self._srcHandle
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
self._error = outDoc.getError()
|
249
|
-
|
250
|
-
yield status, dHandle, nHandle
|
251
|
-
|
252
|
-
hHandle[hLevel] = dHandle
|
253
|
-
nHandle = dHandle
|
254
|
-
pLevel = hLevel
|
255
|
-
|
205
|
+
if self._srcHandle and self._srcItem and self._parHandle:
|
206
|
+
pHandle = self._parHandle
|
207
|
+
hHandle = [self._parHandle, None, None, None, None]
|
208
|
+
pLevel = 0
|
209
|
+
for docText, hLevel, docLabel in self._rawData:
|
210
|
+
|
211
|
+
hLevel = minmax(hLevel, 1, 4)
|
212
|
+
if pLevel == 0:
|
213
|
+
pLevel = hLevel
|
214
|
+
|
215
|
+
if docHierarchy:
|
216
|
+
if hLevel == 1:
|
217
|
+
pHandle = self._parHandle
|
218
|
+
elif hLevel == 2:
|
219
|
+
pHandle = hHandle[1] or hHandle[0]
|
220
|
+
elif hLevel == 3:
|
221
|
+
pHandle = hHandle[2] or hHandle[1] or hHandle[0]
|
222
|
+
elif hLevel == 4:
|
223
|
+
pHandle = hHandle[3] or hHandle[2] or hHandle[1] or hHandle[0]
|
224
|
+
|
225
|
+
if (
|
226
|
+
(dHandle := self._project.newFile(docLabel, pHandle))
|
227
|
+
and (nwItem := self._project.tree[dHandle])
|
228
|
+
):
|
229
|
+
hHandle[hLevel] = dHandle
|
230
|
+
nwItem.setStatus(self._srcItem.itemStatus)
|
231
|
+
nwItem.setImport(self._srcItem.itemImport)
|
232
|
+
|
233
|
+
outDoc = self._project.storage.getDocument(dHandle)
|
234
|
+
status = outDoc.writeDocument("\n".join(docText))
|
235
|
+
if not status:
|
236
|
+
self._error = outDoc.getError()
|
237
|
+
|
238
|
+
self._project.index.reIndexHandle(dHandle)
|
239
|
+
nwItem.notifyToRefresh()
|
240
|
+
|
241
|
+
yield status
|
242
|
+
|
243
|
+
hHandle[hLevel] = dHandle
|
244
|
+
pLevel = hLevel
|
256
245
|
return
|
257
246
|
|
258
247
|
|
@@ -269,36 +258,34 @@ class DocDuplicator:
|
|
269
258
|
# Methods
|
270
259
|
##
|
271
260
|
|
272
|
-
def duplicate(self, items: list[str]) ->
|
261
|
+
def duplicate(self, items: list[str]) -> list[str]:
|
273
262
|
"""Run through a list of items, duplicate them, and copy the
|
274
263
|
text content if they are documents.
|
275
264
|
"""
|
265
|
+
result = []
|
266
|
+
after = True
|
276
267
|
if items:
|
277
|
-
nHandle = items[0]
|
278
268
|
hMap: dict[str, str | None] = {t: None for t in items}
|
279
269
|
for tHandle in items:
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
yield newItem.itemHandle, nHandle
|
293
|
-
nHandle = None
|
294
|
-
return
|
270
|
+
if oldItem := self._project.tree[tHandle]:
|
271
|
+
pHandle = hMap.get(oldItem.itemParent or "") or oldItem.itemParent
|
272
|
+
if newItem := self._project.tree.duplicate(tHandle, pHandle, after):
|
273
|
+
hMap[tHandle] = newItem.itemHandle
|
274
|
+
if newItem.isFileType():
|
275
|
+
self._project.copyFileContent(newItem.itemHandle, tHandle)
|
276
|
+
newItem.notifyToRefresh()
|
277
|
+
result.append(newItem.itemHandle)
|
278
|
+
after = False
|
279
|
+
else:
|
280
|
+
break
|
281
|
+
return result
|
295
282
|
|
296
283
|
|
297
284
|
class DocSearch:
|
298
285
|
|
299
286
|
def __init__(self) -> None:
|
300
|
-
self._regEx =
|
301
|
-
self.
|
287
|
+
self._regEx = re.compile("")
|
288
|
+
self._opts = re.UNICODE | re.IGNORECASE
|
302
289
|
self._words = False
|
303
290
|
self._escape = True
|
304
291
|
return
|
@@ -309,10 +296,9 @@ class DocSearch:
|
|
309
296
|
|
310
297
|
def setCaseSensitive(self, state: bool) -> None:
|
311
298
|
"""Set the case sensitive search flag."""
|
312
|
-
|
299
|
+
self._opts = re.UNICODE
|
313
300
|
if not state:
|
314
|
-
|
315
|
-
self._regEx.setPatternOptions(opts)
|
301
|
+
self._opts |= re.IGNORECASE
|
316
302
|
return
|
317
303
|
|
318
304
|
def setWholeWords(self, state: bool) -> None:
|
@@ -329,8 +315,8 @@ class DocSearch:
|
|
329
315
|
self, project: NWProject, search: str
|
330
316
|
) -> Iterable[tuple[NWItem, list[tuple[int, int, str]], bool]]:
|
331
317
|
"""Iteratively search through documents in a project."""
|
332
|
-
self._regEx.
|
333
|
-
logger.debug("Searching with pattern '%s'", self._regEx.pattern
|
318
|
+
self._regEx = re.compile(self._buildPattern(search), self._opts)
|
319
|
+
logger.debug("Searching with pattern '%s'", self._regEx.pattern)
|
334
320
|
storage = project.storage
|
335
321
|
for item in project.tree:
|
336
322
|
if item.isFileType():
|
@@ -340,14 +326,12 @@ class DocSearch:
|
|
340
326
|
|
341
327
|
def searchText(self, text: str) -> tuple[list[tuple[int, int, str]], bool]:
|
342
328
|
"""Search a piece of text for RegEx matches."""
|
343
|
-
rxItt = self._regEx.globalMatch(text)
|
344
329
|
count = 0
|
345
330
|
capped = False
|
346
331
|
results = []
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
num = rxMatch.capturedLength()
|
332
|
+
for res in self._regEx.finditer(text):
|
333
|
+
pos = res.start(0)
|
334
|
+
num = len(res.group(0))
|
351
335
|
lim = text[:pos].rfind("\n") + 1
|
352
336
|
cut = text[lim:pos].rfind(" ") + lim + 1
|
353
337
|
context = text[cut:cut+100].partition("\n")[0]
|
@@ -366,7 +350,7 @@ class DocSearch:
|
|
366
350
|
def _buildPattern(self, search: str) -> str:
|
367
351
|
"""Build the search pattern string."""
|
368
352
|
if self._escape:
|
369
|
-
search =
|
353
|
+
search = re.escape(search)
|
370
354
|
if self._words:
|
371
355
|
search = f"(?:^|\\b){search}(?:$|\\b)"
|
372
356
|
return search
|
@@ -430,7 +414,6 @@ class ProjectBuilder:
|
|
430
414
|
|
431
415
|
lblNewProject = self.tr("New Project")
|
432
416
|
lblTitlePage = self.tr("Title Page")
|
433
|
-
lblByAuthors = self.tr("By")
|
434
417
|
|
435
418
|
# Settings
|
436
419
|
project.data.setUuid(None)
|
@@ -443,14 +426,29 @@ class ProjectBuilder:
|
|
443
426
|
# Add Root Folders
|
444
427
|
hNovelRoot = project.newRoot(nwItemClass.NOVEL)
|
445
428
|
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
429
|
|
430
|
+
# Generate Title Page
|
452
431
|
aDoc = project.storage.getDocument(hTitlePage)
|
453
|
-
aDoc.writeDocument(
|
432
|
+
aDoc.writeDocument((
|
433
|
+
"{author}[br]\n"
|
434
|
+
"{address} 1[br]\n"
|
435
|
+
"{address} 2 <<\n"
|
436
|
+
"\n"
|
437
|
+
"[vspace:5]\n"
|
438
|
+
"\n"
|
439
|
+
"#! {title}\n"
|
440
|
+
"\n"
|
441
|
+
">> **{by} {author}** <<\n"
|
442
|
+
"\n"
|
443
|
+
">> {count}: [field:{field}] <<\n"
|
444
|
+
).format(
|
445
|
+
author=project.data.author or "None",
|
446
|
+
address=self.tr("Address"),
|
447
|
+
title=project.data.name or "None",
|
448
|
+
by=self.tr("By"),
|
449
|
+
count=self.tr("Word Count"),
|
450
|
+
field=nwStats.WORDS_TEXT,
|
451
|
+
))
|
454
452
|
|
455
453
|
# Create a project structure based on selected root folders
|
456
454
|
# and a number of chapters and scenes selected in the
|
@@ -511,7 +509,7 @@ class ProjectBuilder:
|
|
511
509
|
|
512
510
|
# Also add the archive and trash folders
|
513
511
|
project.newRoot(nwItemClass.ARCHIVE)
|
514
|
-
project.
|
512
|
+
project.tree.trash # Triggers the creation of Trash
|
515
513
|
|
516
514
|
project.saveProject()
|
517
515
|
project.closeProject()
|