novelWriter 2.2.1__py3-none-any.whl → 2.3b1__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.2.1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +102 -92
- novelwriter/__init__.py +4 -4
- novelwriter/assets/icons/typicons_dark/icons.conf +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +6 -0
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
- novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
- novelwriter/assets/images/novelwriter-text-light.svg +4 -0
- novelwriter/assets/images/welcome-dark.jpg +0 -0
- novelwriter/assets/images/welcome-light.jpg +0 -0
- 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 +4 -2
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +48 -37
- novelwriter/config.py +36 -41
- novelwriter/constants.py +38 -16
- novelwriter/core/buildsettings.py +7 -7
- novelwriter/core/coretools.py +192 -154
- novelwriter/core/docbuild.py +6 -3
- novelwriter/core/document.py +6 -6
- novelwriter/core/index.py +89 -56
- novelwriter/core/item.py +21 -3
- novelwriter/core/options.py +8 -7
- novelwriter/core/project.py +69 -44
- novelwriter/core/projectdata.py +1 -14
- novelwriter/core/projectxml.py +13 -41
- novelwriter/core/sessions.py +2 -1
- novelwriter/core/spellcheck.py +2 -1
- novelwriter/core/status.py +2 -1
- novelwriter/core/storage.py +178 -140
- novelwriter/core/tohtml.py +4 -2
- novelwriter/core/tokenizer.py +73 -45
- novelwriter/core/toodt.py +40 -30
- novelwriter/core/tree.py +3 -2
- novelwriter/dialogs/about.py +70 -160
- novelwriter/dialogs/docmerge.py +6 -5
- novelwriter/dialogs/docsplit.py +6 -6
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +553 -703
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
- novelwriter/dialogs/quotes.py +27 -23
- novelwriter/dialogs/wordlist.py +96 -40
- novelwriter/enum.py +20 -18
- novelwriter/error.py +1 -1
- novelwriter/extensions/circularprogress.py +11 -11
- novelwriter/extensions/configlayout.py +185 -134
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +26 -12
- novelwriter/extensions/pagedsidebar.py +14 -16
- novelwriter/extensions/simpleprogress.py +5 -5
- novelwriter/extensions/statusled.py +8 -8
- novelwriter/extensions/switch.py +31 -63
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +178 -150
- novelwriter/gui/dochighlight.py +63 -92
- novelwriter/gui/docviewer.py +49 -51
- novelwriter/gui/docviewerpanel.py +72 -24
- novelwriter/gui/itemdetails.py +7 -7
- novelwriter/gui/mainmenu.py +14 -18
- novelwriter/gui/noveltree.py +9 -8
- novelwriter/gui/outline.py +98 -75
- novelwriter/gui/projtree.py +188 -61
- novelwriter/gui/sidebar.py +3 -4
- novelwriter/gui/statusbar.py +3 -4
- novelwriter/gui/theme.py +60 -68
- novelwriter/guimain.py +49 -156
- novelwriter/shared.py +15 -1
- novelwriter/tools/dictionaries.py +5 -6
- novelwriter/tools/manuscript.py +6 -6
- novelwriter/tools/manussettings.py +192 -221
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +802 -0
- novelwriter/tools/writingstats.py +9 -9
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -60
- novelwriter/dialogs/projdetails.py +0 -518
- novelwriter/dialogs/projload.py +0 -294
- novelwriter/dialogs/updates.py +0 -172
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
novelwriter/core/coretools.py
CHANGED
@@ -29,16 +29,19 @@ from __future__ import annotations
|
|
29
29
|
import shutil
|
30
30
|
import logging
|
31
31
|
|
32
|
-
from
|
32
|
+
from pathlib import Path
|
33
33
|
from functools import partial
|
34
|
+
from zipfile import ZipFile, is_zipfile
|
35
|
+
from collections.abc import Iterable
|
34
36
|
|
35
37
|
from PyQt5.QtCore import QCoreApplication
|
36
38
|
|
37
39
|
from novelwriter import CONFIG, SHARED
|
38
|
-
from novelwriter.common import minmax, simplified
|
39
|
-
from novelwriter.constants import nwItemClass
|
40
|
+
from novelwriter.common import isHandle, minmax, simplified
|
41
|
+
from novelwriter.constants import nwFiles, nwItemClass
|
40
42
|
from novelwriter.core.item import NWItem
|
41
43
|
from novelwriter.core.project import NWProject
|
44
|
+
from novelwriter.core.storage import NWStorageCreate
|
42
45
|
|
43
46
|
logger = logging.getLogger(__name__)
|
44
47
|
|
@@ -277,28 +280,25 @@ class DocDuplicator:
|
|
277
280
|
"""Run through a list of items, duplicate them, and copy the
|
278
281
|
text content if they are documents.
|
279
282
|
"""
|
280
|
-
if
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
newItem = self._project.tree.duplicate(tHandle)
|
287
|
-
if newItem is None:
|
288
|
-
return
|
289
|
-
hMap[tHandle] = newItem.itemHandle
|
290
|
-
if newItem.itemParent in hMap:
|
291
|
-
newItem.setParent(hMap[newItem.itemParent])
|
292
|
-
self._project.tree.updateItemData(newItem.itemHandle)
|
293
|
-
if newItem.isFileType():
|
294
|
-
oldDoc = self._project.storage.getDocument(tHandle)
|
295
|
-
newDoc = self._project.storage.getDocument(newItem.itemHandle)
|
296
|
-
if newDoc.fileExists():
|
283
|
+
if items:
|
284
|
+
nHandle = items[0]
|
285
|
+
hMap: dict[str, str | None] = {t: None for t in items}
|
286
|
+
for tHandle in items:
|
287
|
+
newItem = self._project.tree.duplicate(tHandle)
|
288
|
+
if newItem is None:
|
297
289
|
return
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
290
|
+
hMap[tHandle] = newItem.itemHandle
|
291
|
+
if newItem.itemParent in hMap:
|
292
|
+
newItem.setParent(hMap[newItem.itemParent])
|
293
|
+
self._project.tree.updateItemData(newItem.itemHandle)
|
294
|
+
if newItem.isFileType():
|
295
|
+
oldDoc = self._project.storage.getDocument(tHandle)
|
296
|
+
newDoc = self._project.storage.getDocument(newItem.itemHandle)
|
297
|
+
if newDoc.fileExists():
|
298
|
+
return
|
299
|
+
newDoc.writeDocument(oldDoc.readDocument() or "")
|
300
|
+
yield newItem.itemHandle, nHandle
|
301
|
+
nHandle = None
|
302
302
|
return
|
303
303
|
|
304
304
|
# END Class DocDuplicator
|
@@ -310,55 +310,67 @@ class ProjectBuilder:
|
|
310
310
|
"""
|
311
311
|
|
312
312
|
def __init__(self) -> None:
|
313
|
-
self.
|
313
|
+
self._path = None
|
314
|
+
self.tr = partial(QCoreApplication.translate, "ProjectBuilder")
|
314
315
|
return
|
315
316
|
|
317
|
+
@property
|
318
|
+
def projPath(self) -> Path | None:
|
319
|
+
"""The path of the newly created project."""
|
320
|
+
return self._path
|
321
|
+
|
316
322
|
##
|
317
323
|
# Methods
|
318
324
|
##
|
319
325
|
|
320
326
|
def buildProject(self, data: dict) -> bool:
|
321
|
-
"""Build a project from a data dictionary
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
327
|
+
"""Build or copy a project from a data dictionary."""
|
328
|
+
if isinstance(data, dict):
|
329
|
+
path = data.get("path", None) or None
|
330
|
+
if isinstance(path, (str, Path)):
|
331
|
+
self._path = Path(path).resolve()
|
332
|
+
if data.get("sample", False):
|
333
|
+
return self._extractSampleProject(self._path)
|
334
|
+
elif data.get("template"):
|
335
|
+
return self._copyProject(self._path, data)
|
336
|
+
else:
|
337
|
+
return self._buildAndPopulate(self._path, data)
|
338
|
+
SHARED.error("A project path is required.")
|
339
|
+
return False
|
331
340
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
if popSample:
|
336
|
-
return self._extractSampleProject(data)
|
337
|
-
|
338
|
-
projPath = data.get("projPath", None)
|
339
|
-
if projPath is None:
|
340
|
-
logger.error("No project path set for the new project")
|
341
|
-
return False
|
341
|
+
##
|
342
|
+
# Internal Functions
|
343
|
+
##
|
342
344
|
|
345
|
+
def _buildAndPopulate(self, path: Path, data: dict) -> bool:
|
346
|
+
"""Build a blank project from a data dictionary."""
|
343
347
|
project = NWProject()
|
344
|
-
|
348
|
+
status = project.storage.createNewProject(path)
|
349
|
+
if status == NWStorageCreate.NOT_EMPTY:
|
350
|
+
SHARED.error(self.tr(
|
351
|
+
"The target folder is not empty. "
|
352
|
+
"Please choose another folder."
|
353
|
+
))
|
354
|
+
return False
|
355
|
+
elif status == NWStorageCreate.OS_ERROR:
|
356
|
+
SHARED.error(self.tr(
|
357
|
+
"An error occurred while trying to create the project."
|
358
|
+
), exc=project.storage.exc)
|
345
359
|
return False
|
346
360
|
|
361
|
+
self._path = project.storage.storagePath
|
362
|
+
|
347
363
|
lblNewProject = self.tr("New Project")
|
348
|
-
lblNewChapter = self.tr("New Chapter")
|
349
|
-
lblNewScene = self.tr("New Scene")
|
350
364
|
lblTitlePage = self.tr("Title Page")
|
351
365
|
lblByAuthors = self.tr("By")
|
352
366
|
|
353
367
|
# Settings
|
354
|
-
projName = data.get("
|
355
|
-
|
356
|
-
|
357
|
-
projLang = data.get("projLang", "en_GB")
|
368
|
+
projName = data.get("name", lblNewProject)
|
369
|
+
projAuthor = data.get("author", "")
|
370
|
+
projLang = data.get("language", "en_GB")
|
358
371
|
|
359
372
|
project.data.setUuid(None)
|
360
373
|
project.data.setName(projName)
|
361
|
-
project.data.setTitle(projTitle)
|
362
374
|
project.data.setAuthor(projAuthor)
|
363
375
|
project.data.setLanguage(projLang)
|
364
376
|
project.setDefaultStatusImport()
|
@@ -367,7 +379,7 @@ class ProjectBuilder:
|
|
367
379
|
# Add Root Folders
|
368
380
|
hNovelRoot = project.newRoot(nwItemClass.NOVEL)
|
369
381
|
hTitlePage = project.newFile(lblTitlePage, hNovelRoot)
|
370
|
-
novelTitle = project.data.
|
382
|
+
novelTitle = project.data.name
|
371
383
|
|
372
384
|
titlePage = f"#! {novelTitle}\n\n"
|
373
385
|
if project.data.author:
|
@@ -376,116 +388,142 @@ class ProjectBuilder:
|
|
376
388
|
aDoc = project.storage.getDocument(hTitlePage)
|
377
389
|
aDoc.writeDocument(titlePage)
|
378
390
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
addNotes = data.get("addNotes", False)
|
444
|
-
for newRoot in data.get("addRoots", []):
|
445
|
-
if newRoot in nwItemClass:
|
446
|
-
rHandle = project.newRoot(newRoot)
|
447
|
-
if addNotes:
|
448
|
-
aHandle = project.newFile(noteTitles[newRoot], rHandle)
|
449
|
-
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
|
450
|
-
aDoc = project.storage.getDocument(aHandle)
|
451
|
-
aDoc.writeDocument(
|
452
|
-
f"# {noteTitles[newRoot]}\n\n"
|
453
|
-
f"@tag: {ntTag}\n\n"
|
454
|
-
f"% Short: {bfNote}\n\n"
|
455
|
-
)
|
456
|
-
|
457
|
-
# Also add the archive and trash folders
|
458
|
-
project.newRoot(nwItemClass.ARCHIVE)
|
459
|
-
project.trashFolder()
|
460
|
-
|
461
|
-
project.saveProject()
|
462
|
-
project.closeProject()
|
391
|
+
# Create a project structure based on selected root folders
|
392
|
+
# and a number of chapters and scenes selected in the
|
393
|
+
# wizard's custom page.
|
394
|
+
|
395
|
+
# Create chapters and scenes
|
396
|
+
numChapters = data.get("chapters", 0)
|
397
|
+
numScenes = data.get("scenes", 0)
|
398
|
+
|
399
|
+
chSynop = self.tr("Summary of the chapter.")
|
400
|
+
scSynop = self.tr("Summary of the scene.")
|
401
|
+
bfNote = self.tr("A short description.")
|
402
|
+
|
403
|
+
# Create chapters
|
404
|
+
if numChapters > 0:
|
405
|
+
for ch in range(numChapters):
|
406
|
+
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
|
407
|
+
cHandle = project.newFile(chTitle, hNovelRoot)
|
408
|
+
aDoc = project.storage.getDocument(cHandle)
|
409
|
+
aDoc.writeDocument(f"## {chTitle}\n\n%Synopsis: {chSynop}\n\n")
|
410
|
+
|
411
|
+
# Create chapter scenes
|
412
|
+
if numScenes > 0 and cHandle:
|
413
|
+
for sc in range(numScenes):
|
414
|
+
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
415
|
+
sHandle = project.newFile(scTitle, cHandle)
|
416
|
+
aDoc = project.storage.getDocument(sHandle)
|
417
|
+
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
|
418
|
+
|
419
|
+
# Create scenes (no chapters)
|
420
|
+
elif numScenes > 0:
|
421
|
+
for sc in range(numScenes):
|
422
|
+
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
|
423
|
+
sHandle = project.newFile(scTitle, hNovelRoot)
|
424
|
+
aDoc = project.storage.getDocument(sHandle)
|
425
|
+
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
|
426
|
+
|
427
|
+
# Create notes folders
|
428
|
+
noteTitles = {
|
429
|
+
nwItemClass.PLOT: self.tr("Main Plot"),
|
430
|
+
nwItemClass.CHARACTER: self.tr("Protagonist"),
|
431
|
+
nwItemClass.WORLD: self.tr("Main Location"),
|
432
|
+
}
|
433
|
+
|
434
|
+
addNotes = data.get("notes", False)
|
435
|
+
for newRoot in data.get("roots", []):
|
436
|
+
if newRoot in nwItemClass:
|
437
|
+
rHandle = project.newRoot(newRoot)
|
438
|
+
if addNotes:
|
439
|
+
aHandle = project.newFile(noteTitles[newRoot], rHandle)
|
440
|
+
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
|
441
|
+
aDoc = project.storage.getDocument(aHandle)
|
442
|
+
aDoc.writeDocument(
|
443
|
+
f"# {noteTitles[newRoot]}\n\n"
|
444
|
+
f"@tag: {ntTag}\n\n"
|
445
|
+
f"%Short: {bfNote}\n\n"
|
446
|
+
)
|
447
|
+
|
448
|
+
# Also add the archive and trash folders
|
449
|
+
project.newRoot(nwItemClass.ARCHIVE)
|
450
|
+
project.trashFolder()
|
451
|
+
|
452
|
+
project.saveProject()
|
453
|
+
project.closeProject()
|
463
454
|
|
464
455
|
return True
|
465
456
|
|
466
|
-
|
467
|
-
|
468
|
-
|
457
|
+
def _copyProject(self, path: Path, data: dict) -> bool:
|
458
|
+
"""Copy an existing project content, but not the meta data, and
|
459
|
+
update new settings.
|
460
|
+
"""
|
461
|
+
source = data.get("template")
|
462
|
+
if not (isinstance(source, Path) and source.is_file()
|
463
|
+
and (source.name == nwFiles.PROJ_FILE or is_zipfile(source))):
|
464
|
+
logger.error("Could not access source project: %s", source)
|
465
|
+
return False
|
469
466
|
|
470
|
-
|
467
|
+
logger.info("Copying project: %s", source)
|
468
|
+
if path.exists():
|
469
|
+
SHARED.error(self.tr(
|
470
|
+
"The target folder already exists. "
|
471
|
+
"Please choose another folder."
|
472
|
+
))
|
473
|
+
return False
|
474
|
+
|
475
|
+
# Begin copying
|
476
|
+
srcPath = source.parent
|
477
|
+
dstPath = path.resolve()
|
478
|
+
srcCont = srcPath / "content"
|
479
|
+
dstCont = dstPath / "content"
|
480
|
+
try:
|
481
|
+
dstPath.mkdir(exist_ok=True)
|
482
|
+
dstCont.mkdir(exist_ok=True)
|
483
|
+
if is_zipfile(source):
|
484
|
+
with ZipFile(source) as zipObj:
|
485
|
+
for member in zipObj.namelist():
|
486
|
+
if member == nwFiles.PROJ_FILE:
|
487
|
+
zipObj.extract(member, dstPath)
|
488
|
+
elif member.startswith("content") and member.endswith(".nwd"):
|
489
|
+
zipObj.extract(member, dstPath)
|
490
|
+
else:
|
491
|
+
shutil.copy2(srcPath / nwFiles.PROJ_FILE, dstPath)
|
492
|
+
for item in srcCont.iterdir():
|
493
|
+
if item.is_file() and item.suffix == ".nwd" and isHandle(item.stem):
|
494
|
+
shutil.copy2(item, dstCont)
|
495
|
+
except Exception as exc:
|
496
|
+
SHARED.error(self.tr("Could not copy project files."), exc=exc)
|
497
|
+
return False
|
498
|
+
|
499
|
+
# Open the copied project and update settings
|
500
|
+
project = NWProject()
|
501
|
+
project.openProject(dstPath)
|
502
|
+
project.data.setUuid("") # Creates a fresh uuid
|
503
|
+
project.data.setName(data.get("name", "None"))
|
504
|
+
project.data.setAuthor(data.get("author", ""))
|
505
|
+
project.data.setLanguage(data.get("language", "en_GB"))
|
506
|
+
project.data.setSpellCheck(True)
|
507
|
+
project.data.setSpellLang(None)
|
508
|
+
project.data.setDoBackup(True)
|
509
|
+
project.data.setSaveCount(0)
|
510
|
+
project.data.setAutoCount(0)
|
511
|
+
project.data.setEditTime(0)
|
512
|
+
project.saveProject()
|
513
|
+
project.closeProject()
|
514
|
+
|
515
|
+
return True
|
516
|
+
|
517
|
+
def _extractSampleProject(self, path: Path) -> bool:
|
471
518
|
"""Make a copy of the sample project by extracting the
|
472
519
|
sample.zip file to the new path.
|
473
520
|
"""
|
474
|
-
|
475
|
-
if projPath is None:
|
476
|
-
logger.error("No project path set for the example project")
|
477
|
-
return False
|
478
|
-
|
479
|
-
pkgSample = CONFIG.assetPath("sample.zip")
|
480
|
-
if pkgSample.is_file():
|
521
|
+
if (sample := CONFIG.assetPath("sample.zip")).is_file():
|
481
522
|
try:
|
482
|
-
shutil.unpack_archive(
|
523
|
+
shutil.unpack_archive(sample, path)
|
483
524
|
except Exception as exc:
|
484
|
-
SHARED.error(self.tr(
|
485
|
-
"Failed to create a new example project."
|
486
|
-
), exc=exc)
|
525
|
+
SHARED.error(self.tr("Failed to create a new example project."), exc=exc)
|
487
526
|
return False
|
488
|
-
|
489
527
|
else:
|
490
528
|
SHARED.error(self.tr(
|
491
529
|
"Failed to create a new example project. "
|
novelwriter/core/docbuild.py
CHANGED
@@ -25,8 +25,8 @@ from __future__ import annotations
|
|
25
25
|
|
26
26
|
import logging
|
27
27
|
|
28
|
-
from typing import Iterable
|
29
28
|
from pathlib import Path
|
29
|
+
from collections.abc import Iterable
|
30
30
|
|
31
31
|
from PyQt5.QtGui import QFont, QFontInfo
|
32
32
|
|
@@ -248,8 +248,8 @@ class NWBuildDocument:
|
|
248
248
|
def _setupBuild(self, bldObj: Tokenizer) -> dict:
|
249
249
|
"""Configure the build object."""
|
250
250
|
# Get Settings
|
251
|
-
textFont
|
252
|
-
textSize
|
251
|
+
textFont = self._build.getStr("format.textFont")
|
252
|
+
textSize = self._build.getInt("format.textSize")
|
253
253
|
|
254
254
|
fontFamily = textFont or CONFIG.textFont
|
255
255
|
bldFont = QFont(fontFamily, textSize)
|
@@ -284,6 +284,9 @@ class NWBuildDocument:
|
|
284
284
|
if isinstance(bldObj, ToOdt):
|
285
285
|
bldObj.setColourHeaders(self._build.getBool("odt.addColours"))
|
286
286
|
bldObj.setLanguage(self._project.data.language)
|
287
|
+
bldObj.setHeaderFormat(
|
288
|
+
self._build.getStr("odt.pageHeader"), self._build.getInt("odt.pageCountOffset")
|
289
|
+
)
|
287
290
|
|
288
291
|
scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0)
|
289
292
|
pW, pH = nwLabels.PAPER_SIZE.get(self._build.getStr("format.pageSize"), (-1.0, -1.0))
|
novelwriter/core/document.py
CHANGED
@@ -52,7 +52,7 @@ class NWDocument:
|
|
52
52
|
|
53
53
|
def __init__(self, project: NWProject, tHandle: str | None) -> None:
|
54
54
|
|
55
|
-
self._project
|
55
|
+
self._project = project
|
56
56
|
|
57
57
|
self._item = None # The currently open item
|
58
58
|
self._handle = None # The handle of the currently open item
|
@@ -284,12 +284,12 @@ class NWDocument:
|
|
284
284
|
"""Parse the document meta tag and return the name, parent,
|
285
285
|
class and layout meta values.
|
286
286
|
"""
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
287
|
+
name = self._docMeta.get("name", "")
|
288
|
+
parent = self._docMeta.get("parent", None)
|
289
|
+
itemClass = self._docMeta.get("class", None)
|
290
|
+
itemLayout = self._docMeta.get("layout", None)
|
291
291
|
|
292
|
-
return
|
292
|
+
return name, parent, itemClass, itemLayout
|
293
293
|
|
294
294
|
def getError(self) -> str:
|
295
295
|
"""Return the last recorded exception."""
|