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/tokenizer.py
CHANGED
@@ -34,11 +34,13 @@ from pathlib import Path
|
|
34
34
|
from functools import partial
|
35
35
|
|
36
36
|
from PyQt5.QtCore import QCoreApplication, QRegularExpression
|
37
|
-
from novelwriter.core.index import processComment
|
38
37
|
|
39
38
|
from novelwriter.enum import nwComment, nwItemLayout
|
40
39
|
from novelwriter.common import formatTimeStamp, numberToRoman, checkInt
|
41
|
-
from novelwriter.constants import
|
40
|
+
from novelwriter.constants import (
|
41
|
+
nwHeadFmt, nwKeyWords, nwLabels, nwRegEx, nwShortcode, nwUnicode, trConst
|
42
|
+
)
|
43
|
+
from novelwriter.core.index import processComment
|
42
44
|
from novelwriter.core.project import NWProject
|
43
45
|
|
44
46
|
logger = logging.getLogger(__name__)
|
@@ -112,13 +114,15 @@ class Tokenizer(ABC):
|
|
112
114
|
|
113
115
|
# Data Variables
|
114
116
|
self._text = "" # The raw text to be tokenized
|
115
|
-
self._nwItem = None # The NWItem
|
116
|
-
self._tokens = [] # The list of the processed tokens
|
117
|
+
self._nwItem = None # The NWItem currently being processed
|
117
118
|
self._result = "" # The result of the last document
|
118
119
|
|
119
120
|
self._keepMarkdown = False # Whether to keep the markdown text
|
120
121
|
self._allMarkdown = [] # The result novelWriter markdown of all documents
|
121
122
|
|
123
|
+
# Processed Tokens
|
124
|
+
self._tokens: list[tuple[int, int, str, list[tuple[int, int]], int]] = []
|
125
|
+
|
122
126
|
# User Settings
|
123
127
|
self._textFont = "Serif" # Output text font
|
124
128
|
self._textSize = 11 # Output text size
|
@@ -194,12 +198,12 @@ class Tokenizer(ABC):
|
|
194
198
|
##
|
195
199
|
|
196
200
|
@property
|
197
|
-
def
|
201
|
+
def result(self) -> str:
|
198
202
|
"""The result of the build process."""
|
199
203
|
return self._result
|
200
204
|
|
201
205
|
@property
|
202
|
-
def
|
206
|
+
def allMarkdown(self) -> list:
|
203
207
|
"""The combined novelWriter Markdown text."""
|
204
208
|
return self._allMarkdown
|
205
209
|
|
@@ -350,7 +354,7 @@ class Tokenizer(ABC):
|
|
350
354
|
title = f"{trNotes}: {tItem.itemName}"
|
351
355
|
self._tokens = []
|
352
356
|
self._tokens.append((
|
353
|
-
self.T_TITLE, 0, title,
|
357
|
+
self.T_TITLE, 0, title, [], textAlign
|
354
358
|
))
|
355
359
|
if self._keepMarkdown:
|
356
360
|
self._allMarkdown.append(f"# {title}\n\n")
|
@@ -358,8 +362,8 @@ class Tokenizer(ABC):
|
|
358
362
|
return True
|
359
363
|
|
360
364
|
def setText(self, tHandle: str, text: str | None = None) -> bool:
|
361
|
-
"""Set the text for the tokenizer from a handle. If
|
362
|
-
|
365
|
+
"""Set the text for the tokenizer from a handle. If text is not
|
366
|
+
set, load it from the file.
|
363
367
|
"""
|
364
368
|
self._nwItem = self._project.tree[tHandle]
|
365
369
|
if self._nwItem is None:
|
@@ -418,7 +422,7 @@ class Tokenizer(ABC):
|
|
418
422
|
# Check for blank lines
|
419
423
|
if len(sLine) == 0:
|
420
424
|
self._tokens.append((
|
421
|
-
self.T_EMPTY, nHead, "",
|
425
|
+
self.T_EMPTY, nHead, "", [], self.A_NONE
|
422
426
|
))
|
423
427
|
if self._keepMarkdown:
|
424
428
|
tmpMarkdown.append("\n")
|
@@ -446,7 +450,7 @@ class Tokenizer(ABC):
|
|
446
450
|
|
447
451
|
elif sLine == "[vspace]":
|
448
452
|
self._tokens.append(
|
449
|
-
(self.T_SKIP, nHead, "",
|
453
|
+
(self.T_SKIP, nHead, "", [], sAlign)
|
450
454
|
)
|
451
455
|
continue
|
452
456
|
|
@@ -454,38 +458,42 @@ class Tokenizer(ABC):
|
|
454
458
|
nSkip = checkInt(sLine[8:-1], 0)
|
455
459
|
if nSkip >= 1:
|
456
460
|
self._tokens.append(
|
457
|
-
(self.T_SKIP, nHead, "",
|
461
|
+
(self.T_SKIP, nHead, "", [], sAlign)
|
458
462
|
)
|
459
463
|
if nSkip > 1:
|
460
464
|
self._tokens += (nSkip - 1) * [
|
461
|
-
(self.T_SKIP, nHead, "",
|
465
|
+
(self.T_SKIP, nHead, "", [], self.A_NONE)
|
462
466
|
]
|
463
467
|
continue
|
464
468
|
|
465
469
|
if aLine[0] == "%":
|
470
|
+
if aLine[1] == "~":
|
471
|
+
# Completely ignore the paragraph
|
472
|
+
continue
|
473
|
+
|
466
474
|
cStyle, cText, _ = processComment(aLine)
|
467
475
|
if cStyle == nwComment.SYNOPSIS:
|
468
476
|
self._tokens.append((
|
469
|
-
self.T_SYNOPSIS, nHead, cText,
|
477
|
+
self.T_SYNOPSIS, nHead, cText, [], sAlign
|
470
478
|
))
|
471
479
|
if self._doSynopsis and self._keepMarkdown:
|
472
480
|
tmpMarkdown.append("%s\n" % aLine)
|
473
481
|
elif cStyle == nwComment.SHORT:
|
474
482
|
self._tokens.append((
|
475
|
-
self.T_SHORT, nHead, cText,
|
483
|
+
self.T_SHORT, nHead, cText, [], sAlign
|
476
484
|
))
|
477
485
|
if self._doSynopsis and self._keepMarkdown:
|
478
486
|
tmpMarkdown.append("%s\n" % aLine)
|
479
487
|
else:
|
480
488
|
self._tokens.append((
|
481
|
-
self.T_COMMENT, nHead, cText,
|
489
|
+
self.T_COMMENT, nHead, cText, [], sAlign
|
482
490
|
))
|
483
491
|
if self._doComments and self._keepMarkdown:
|
484
492
|
tmpMarkdown.append("%s\n" % aLine)
|
485
493
|
|
486
494
|
elif aLine[0] == "@":
|
487
495
|
self._tokens.append((
|
488
|
-
self.T_KEYWORD, nHead, aLine[1:].strip(),
|
496
|
+
self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign
|
489
497
|
))
|
490
498
|
if self._doKeywords and self._keepMarkdown:
|
491
499
|
tmpMarkdown.append("%s\n" % aLine)
|
@@ -497,7 +505,7 @@ class Tokenizer(ABC):
|
|
497
505
|
|
498
506
|
nHead += 1
|
499
507
|
self._tokens.append((
|
500
|
-
self.T_HEAD1, nHead, aLine[2:].strip(),
|
508
|
+
self.T_HEAD1, nHead, aLine[2:].strip(), [], sAlign
|
501
509
|
))
|
502
510
|
if self._keepMarkdown:
|
503
511
|
tmpMarkdown.append("%s\n" % aLine)
|
@@ -508,7 +516,7 @@ class Tokenizer(ABC):
|
|
508
516
|
|
509
517
|
nHead += 1
|
510
518
|
self._tokens.append((
|
511
|
-
self.T_HEAD2, nHead, aLine[3:].strip(),
|
519
|
+
self.T_HEAD2, nHead, aLine[3:].strip(), [], sAlign
|
512
520
|
))
|
513
521
|
if self._keepMarkdown:
|
514
522
|
tmpMarkdown.append("%s\n" % aLine)
|
@@ -516,7 +524,7 @@ class Tokenizer(ABC):
|
|
516
524
|
elif aLine[:4] == "### ":
|
517
525
|
nHead += 1
|
518
526
|
self._tokens.append((
|
519
|
-
self.T_HEAD3, nHead, aLine[4:].strip(),
|
527
|
+
self.T_HEAD3, nHead, aLine[4:].strip(), [], sAlign
|
520
528
|
))
|
521
529
|
if self._keepMarkdown:
|
522
530
|
tmpMarkdown.append("%s\n" % aLine)
|
@@ -524,7 +532,7 @@ class Tokenizer(ABC):
|
|
524
532
|
elif aLine[:5] == "#### ":
|
525
533
|
nHead += 1
|
526
534
|
self._tokens.append((
|
527
|
-
self.T_HEAD4, nHead, aLine[5:].strip(),
|
535
|
+
self.T_HEAD4, nHead, aLine[5:].strip(), [], sAlign
|
528
536
|
))
|
529
537
|
if self._keepMarkdown:
|
530
538
|
tmpMarkdown.append("%s\n" % aLine)
|
@@ -537,7 +545,7 @@ class Tokenizer(ABC):
|
|
537
545
|
tStyle = self.T_HEAD1
|
538
546
|
|
539
547
|
self._tokens.append((
|
540
|
-
tStyle, nHead, aLine[3:].strip(),
|
548
|
+
tStyle, nHead, aLine[3:].strip(), [], sAlign | self.A_CENTRE
|
541
549
|
))
|
542
550
|
if self._keepMarkdown:
|
543
551
|
tmpMarkdown.append("%s\n" % aLine)
|
@@ -551,7 +559,7 @@ class Tokenizer(ABC):
|
|
551
559
|
tStyle = self.T_HEAD2
|
552
560
|
|
553
561
|
self._tokens.append((
|
554
|
-
tStyle, nHead, aLine[4:].strip(),
|
562
|
+
tStyle, nHead, aLine[4:].strip(), [], sAlign
|
555
563
|
))
|
556
564
|
if self._keepMarkdown:
|
557
565
|
tmpMarkdown.append("%s\n" % aLine)
|
@@ -614,7 +622,7 @@ class Tokenizer(ABC):
|
|
614
622
|
|
615
623
|
# Always add an empty line at the end of the file
|
616
624
|
self._tokens.append((
|
617
|
-
self.T_EMPTY, nHead, "",
|
625
|
+
self.T_EMPTY, nHead, "", [], self.A_NONE
|
618
626
|
))
|
619
627
|
if self._keepMarkdown:
|
620
628
|
tmpMarkdown.append("\n")
|
@@ -626,8 +634,8 @@ class Tokenizer(ABC):
|
|
626
634
|
# ===========
|
627
635
|
# Some items need a second pass
|
628
636
|
|
629
|
-
pToken = (self.T_EMPTY, 0, "",
|
630
|
-
nToken = (self.T_EMPTY, 0, "",
|
637
|
+
pToken = (self.T_EMPTY, 0, "", [], self.A_NONE)
|
638
|
+
nToken = (self.T_EMPTY, 0, "", [], self.A_NONE)
|
631
639
|
tCount = len(self._tokens)
|
632
640
|
for n, tToken in enumerate(self._tokens):
|
633
641
|
|
@@ -655,6 +663,8 @@ class Tokenizer(ABC):
|
|
655
663
|
if not self._isNovel:
|
656
664
|
return False
|
657
665
|
|
666
|
+
self._hFormatter.setHandle(self._nwItem.itemHandle if self._nwItem else None)
|
667
|
+
|
658
668
|
for n, tToken in enumerate(self._tokens):
|
659
669
|
|
660
670
|
# In case we see text before a scene, we reset the flag
|
@@ -664,9 +674,9 @@ class Tokenizer(ABC):
|
|
664
674
|
elif tToken[0] == self.T_HEAD1:
|
665
675
|
# Partition
|
666
676
|
|
667
|
-
tTemp = self._hFormatter.apply(self._fmtTitle, tToken[2])
|
677
|
+
tTemp = self._hFormatter.apply(self._fmtTitle, tToken[2], tToken[1])
|
668
678
|
self._tokens[n] = (
|
669
|
-
tToken[0], tToken[1], tTemp,
|
679
|
+
tToken[0], tToken[1], tTemp, [], tToken[4]
|
670
680
|
)
|
671
681
|
|
672
682
|
elif tToken[0] in (self.T_HEAD2, self.T_UNNUM):
|
@@ -674,14 +684,14 @@ class Tokenizer(ABC):
|
|
674
684
|
|
675
685
|
# Numbered or Unnumbered
|
676
686
|
if tToken[0] == self.T_UNNUM:
|
677
|
-
tTemp = self._hFormatter.apply(self._fmtUnNum, tToken[2])
|
687
|
+
tTemp = self._hFormatter.apply(self._fmtUnNum, tToken[2], tToken[1])
|
678
688
|
else:
|
679
689
|
self._hFormatter.incChapter()
|
680
|
-
tTemp = self._hFormatter.apply(self._fmtChapter, tToken[2])
|
690
|
+
tTemp = self._hFormatter.apply(self._fmtChapter, tToken[2], tToken[1])
|
681
691
|
|
682
692
|
# Format the chapter header
|
683
693
|
self._tokens[n] = (
|
684
|
-
tToken[0], tToken[1], tTemp,
|
694
|
+
tToken[0], tToken[1], tTemp, [], tToken[4]
|
685
695
|
)
|
686
696
|
|
687
697
|
# Set scene variables
|
@@ -693,32 +703,32 @@ class Tokenizer(ABC):
|
|
693
703
|
|
694
704
|
self._hFormatter.incScene()
|
695
705
|
|
696
|
-
tTemp = self._hFormatter.apply(self._fmtScene, tToken[2])
|
706
|
+
tTemp = self._hFormatter.apply(self._fmtScene, tToken[2], tToken[1])
|
697
707
|
if tTemp == "" and self._hideScene:
|
698
708
|
self._tokens[n] = (
|
699
|
-
self.T_EMPTY, tToken[1], "",
|
709
|
+
self.T_EMPTY, tToken[1], "", [], self.A_NONE
|
700
710
|
)
|
701
711
|
elif tTemp == "" and not self._hideScene:
|
702
712
|
if self._firstScene:
|
703
713
|
self._tokens[n] = (
|
704
|
-
self.T_EMPTY, tToken[1], "",
|
714
|
+
self.T_EMPTY, tToken[1], "", [], self.A_NONE
|
705
715
|
)
|
706
716
|
else:
|
707
717
|
self._tokens[n] = (
|
708
|
-
self.T_SKIP, tToken[1], "",
|
718
|
+
self.T_SKIP, tToken[1], "", [], tToken[4]
|
709
719
|
)
|
710
720
|
elif tTemp == self._fmtScene:
|
711
721
|
if self._firstScene:
|
712
722
|
self._tokens[n] = (
|
713
|
-
self.T_EMPTY, tToken[1], "",
|
723
|
+
self.T_EMPTY, tToken[1], "", [], self.A_NONE
|
714
724
|
)
|
715
725
|
else:
|
716
726
|
self._tokens[n] = (
|
717
|
-
self.T_SEP, tToken[1], tTemp,
|
727
|
+
self.T_SEP, tToken[1], tTemp, [], tToken[4] | self.A_CENTRE
|
718
728
|
)
|
719
729
|
else:
|
720
730
|
self._tokens[n] = (
|
721
|
-
tToken[0], tToken[1], tTemp,
|
731
|
+
tToken[0], tToken[1], tTemp, [], tToken[4]
|
722
732
|
)
|
723
733
|
|
724
734
|
self._firstScene = False
|
@@ -726,22 +736,22 @@ class Tokenizer(ABC):
|
|
726
736
|
elif tToken[0] == self.T_HEAD4:
|
727
737
|
# Section
|
728
738
|
|
729
|
-
tTemp = self._hFormatter.apply(self._fmtSection, tToken[2])
|
739
|
+
tTemp = self._hFormatter.apply(self._fmtSection, tToken[2], tToken[1])
|
730
740
|
if tTemp == "" and self._hideSection:
|
731
741
|
self._tokens[n] = (
|
732
|
-
self.T_EMPTY, tToken[1], "",
|
742
|
+
self.T_EMPTY, tToken[1], "", [], self.A_NONE
|
733
743
|
)
|
734
744
|
elif tTemp == "" and not self._hideSection:
|
735
745
|
self._tokens[n] = (
|
736
|
-
self.T_SKIP, tToken[1], "",
|
746
|
+
self.T_SKIP, tToken[1], "", [], tToken[4]
|
737
747
|
)
|
738
748
|
elif tTemp == self._fmtSection:
|
739
749
|
self._tokens[n] = (
|
740
|
-
self.T_SEP, tToken[1], tTemp,
|
750
|
+
self.T_SEP, tToken[1], tTemp, [], tToken[4] | self.A_CENTRE
|
741
751
|
)
|
742
752
|
else:
|
743
753
|
self._tokens[n] = (
|
744
|
-
tToken[0], tToken[1], tTemp,
|
754
|
+
tToken[0], tToken[1], tTemp, [], tToken[4]
|
745
755
|
)
|
746
756
|
|
747
757
|
return True
|
@@ -759,7 +769,6 @@ class Tokenizer(ABC):
|
|
759
769
|
data = {
|
760
770
|
"meta": {
|
761
771
|
"projectName": self._project.data.name,
|
762
|
-
"novelTitle": self._project.data.title,
|
763
772
|
"novelAuthor": self._project.data.author,
|
764
773
|
"buildTime": int(timeStamp),
|
765
774
|
"buildTimeStr": formatTimeStamp(timeStamp),
|
@@ -818,11 +827,17 @@ class HeadingFormatter:
|
|
818
827
|
|
819
828
|
def __init__(self, project: NWProject) -> None:
|
820
829
|
self._project = project
|
830
|
+
self._handle = None
|
821
831
|
self._chCount = 0
|
822
832
|
self._scChCount = 0
|
823
833
|
self._scAbsCount = 0
|
824
834
|
return
|
825
835
|
|
836
|
+
def setHandle(self, tHandle: str | None) -> None:
|
837
|
+
"""Set the handle currently being processed."""
|
838
|
+
self._handle = tHandle
|
839
|
+
return
|
840
|
+
|
826
841
|
def incChapter(self) -> None:
|
827
842
|
"""Increment the chapter counter."""
|
828
843
|
self._chCount += 1
|
@@ -839,7 +854,7 @@ class HeadingFormatter:
|
|
839
854
|
self._scChCount = 0
|
840
855
|
return
|
841
856
|
|
842
|
-
def apply(self, hFormat: str, text: str) -> str:
|
857
|
+
def apply(self, hFormat: str, text: str, nHead: int) -> str:
|
843
858
|
"""Apply formatting to a specific heading."""
|
844
859
|
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
845
860
|
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
|
@@ -855,6 +870,19 @@ class HeadingFormatter:
|
|
855
870
|
chRom = numberToRoman(self._chCount, toLower=False)
|
856
871
|
hFormat = hFormat.replace(nwHeadFmt.CH_ROMU, chRom)
|
857
872
|
|
873
|
+
if nwHeadFmt.CHAR_POV in hFormat or nwHeadFmt.CHAR_FOCUS in hFormat:
|
874
|
+
if self._handle and nHead > 0:
|
875
|
+
index = self._project.index
|
876
|
+
pList = index.getReferenceForHeader(self._handle, nHead, nwKeyWords.POV_KEY)
|
877
|
+
fList = index.getReferenceForHeader(self._handle, nHead, nwKeyWords.FOCUS_KEY)
|
878
|
+
pText = pList[0] if pList else nwUnicode.U_ENDASH
|
879
|
+
fText = fList[0] if fList else nwUnicode.U_ENDASH
|
880
|
+
else:
|
881
|
+
pText = trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
|
882
|
+
fText = trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
|
883
|
+
hFormat = hFormat.replace(nwHeadFmt.CHAR_POV, pText)
|
884
|
+
hFormat = hFormat.replace(nwHeadFmt.CHAR_FOCUS, fText)
|
885
|
+
|
858
886
|
return hFormat
|
859
887
|
|
860
888
|
# END Class HeadingFormatter
|
novelwriter/core/toodt.py
CHANGED
@@ -29,11 +29,11 @@ from __future__ import annotations
|
|
29
29
|
import logging
|
30
30
|
import xml.etree.ElementTree as ET
|
31
31
|
|
32
|
-
from typing import Sequence
|
33
32
|
from hashlib import sha256
|
34
33
|
from pathlib import Path
|
35
34
|
from zipfile import ZipFile
|
36
35
|
from datetime import datetime
|
36
|
+
from collections.abc import Sequence
|
37
37
|
|
38
38
|
from novelwriter import __version__
|
39
39
|
from novelwriter.common import xmlIndent
|
@@ -132,11 +132,12 @@ class ToOdt(Tokenizer):
|
|
132
132
|
self._errData = [] # List of errors encountered
|
133
133
|
|
134
134
|
# Properties
|
135
|
-
self._textFont
|
136
|
-
self._textSize
|
137
|
-
self._textFixed
|
138
|
-
self._colourHead
|
139
|
-
self.
|
135
|
+
self._textFont = "Liberation Serif"
|
136
|
+
self._textSize = 12
|
137
|
+
self._textFixed = False
|
138
|
+
self._colourHead = False
|
139
|
+
self._headerFormat = ""
|
140
|
+
self._pageOffset = 0
|
140
141
|
|
141
142
|
# Internal
|
142
143
|
self._fontFamily = "'Liberation Serif'"
|
@@ -222,6 +223,12 @@ class ToOdt(Tokenizer):
|
|
222
223
|
self._mDocRight = f"{right/10.0:.3f}cm"
|
223
224
|
return
|
224
225
|
|
226
|
+
def setHeaderFormat(self, format: str, offset: int) -> None:
|
227
|
+
"""Set the document header format."""
|
228
|
+
self._headerFormat = format.strip()
|
229
|
+
self._pageOffset = offset
|
230
|
+
return
|
231
|
+
|
225
232
|
##
|
226
233
|
# Class Methods
|
227
234
|
##
|
@@ -279,14 +286,6 @@ class ToOdt(Tokenizer):
|
|
279
286
|
# Clear Errors
|
280
287
|
self._errData = []
|
281
288
|
|
282
|
-
# Document Header
|
283
|
-
# ===============
|
284
|
-
|
285
|
-
if self._headerText == "":
|
286
|
-
theTitle = self._project.data.title or self._project.data.name
|
287
|
-
theAuth = self._project.data.author
|
288
|
-
self._headerText = f"{theTitle} / {theAuth} /"
|
289
|
-
|
290
289
|
# Create Roots
|
291
290
|
# ============
|
292
291
|
|
@@ -373,7 +372,7 @@ class ToOdt(Tokenizer):
|
|
373
372
|
|
374
373
|
# Dublin Core Meta Data
|
375
374
|
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "title"))
|
376
|
-
xMeta.text = self._project.data.
|
375
|
+
xMeta.text = self._project.data.name
|
377
376
|
|
378
377
|
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "date"))
|
379
378
|
xMeta.text = timeStamp
|
@@ -684,12 +683,12 @@ class ToOdt(Tokenizer):
|
|
684
683
|
return parName
|
685
684
|
|
686
685
|
oStyle.setParentStyleName(parName)
|
687
|
-
|
688
|
-
if
|
689
|
-
return self._autoPara[
|
686
|
+
pID = oStyle.getID()
|
687
|
+
if pID in self._autoPara:
|
688
|
+
return self._autoPara[pID][0]
|
690
689
|
|
691
690
|
newName = "P%d" % (len(self._autoPara) + 1)
|
692
|
-
self._autoPara[
|
691
|
+
self._autoPara[pID] = (newName, oStyle)
|
693
692
|
|
694
693
|
return newName
|
695
694
|
|
@@ -1012,17 +1011,28 @@ class ToOdt(Tokenizer):
|
|
1012
1011
|
xPage = ET.SubElement(self._xMast, _mkTag("style", "master-page"), attrib=tAttr)
|
1013
1012
|
|
1014
1013
|
# Standard Page Header
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1014
|
+
if self._headerFormat:
|
1015
|
+
pre, page, post = self._headerFormat.partition(nwHeadFmt.ODT_PAGE)
|
1016
|
+
|
1017
|
+
pre = pre.replace(nwHeadFmt.ODT_PROJECT, self._project.data.name)
|
1018
|
+
pre = pre.replace(nwHeadFmt.ODT_AUTHOR, self._project.data.author)
|
1019
|
+
post = post.replace(nwHeadFmt.ODT_PROJECT, self._project.data.name)
|
1020
|
+
post = post.replace(nwHeadFmt.ODT_AUTHOR, self._project.data.author)
|
1021
|
+
|
1022
|
+
xHead = ET.SubElement(xPage, _mkTag("style", "header"))
|
1023
|
+
xPar = ET.SubElement(xHead, _mkTag("text", "p"), attrib={
|
1024
|
+
_mkTag("text", "style-name"): "Header"
|
1025
|
+
})
|
1026
|
+
xPar.text = pre
|
1027
|
+
if page:
|
1028
|
+
attrib = {_mkTag("text", "select-page"): "current"}
|
1029
|
+
if self._pageOffset > 0:
|
1030
|
+
attrib = {_mkTag("text", "page-adjust"): str(0 - self._pageOffset)}
|
1031
|
+
xTail = ET.SubElement(xPar, _mkTag("text", "page-number"), attrib=attrib)
|
1032
|
+
xTail.text = "2"
|
1033
|
+
xTail.tail = post
|
1034
|
+
else:
|
1035
|
+
xPar.text += post
|
1026
1036
|
|
1027
1037
|
# First Page Header
|
1028
1038
|
xHead = ET.SubElement(xPage, _mkTag("style", "header-first"))
|
novelwriter/core/tree.py
CHANGED
@@ -26,8 +26,9 @@ from __future__ import annotations
|
|
26
26
|
import random
|
27
27
|
import logging
|
28
28
|
|
29
|
-
from typing import TYPE_CHECKING,
|
29
|
+
from typing import TYPE_CHECKING, Literal, overload
|
30
30
|
from pathlib import Path
|
31
|
+
from collections.abc import Iterator
|
31
32
|
|
32
33
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
33
34
|
from novelwriter.error import logException
|
@@ -500,7 +501,7 @@ class NWTree:
|
|
500
501
|
##
|
501
502
|
|
502
503
|
def _setTreeChanged(self, state: bool) -> None:
|
503
|
-
"""Set the changed flag to
|
504
|
+
"""Set the changed flag to state, and if being set to True,
|
504
505
|
propagate that state change to the parent NWProject class.
|
505
506
|
"""
|
506
507
|
self._changed = state
|