diff --git a/novelwriter/constants.py b/novelwriter/constants.py
index 130378bd..96e3db99 100644
--- a/novelwriter/constants.py
+++ b/novelwriter/constants.py
@@ -405,6 +405,7 @@ class nwUnicode:
U_THNBSP = "\u202f" # Thin non-breaking space
U_ENSP = "\u2002" # Short (en) space
U_EMSP = "\u2003" # Long (em) space
+ U_MMSP = "\u205f" # Medium mathematical space
U_LSEP = "\u2028" # Line separator
U_PSEP = "\u2029" # Paragraph separator
diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py
index d38f3347..589050c1 100644
--- a/novelwriter/core/buildsettings.py
+++ b/novelwriter/core/buildsettings.py
@@ -49,50 +49,53 @@ logger = logging.getLogger(__name__)
# (type, default, [min value, max value])
SETTINGS_TEMPLATE = {
- "filter.includeNovel": (bool, True),
- "filter.includeNotes": (bool, False),
- "filter.includeInactive": (bool, False),
- "headings.fmtTitle": (str, nwHeadFmt.TITLE),
- "headings.fmtChapter": (str, nwHeadFmt.TITLE),
- "headings.fmtUnnumbered": (str, nwHeadFmt.TITLE),
- "headings.fmtScene": (str, "* * *"),
- "headings.fmtHardScene": (str, ""),
- "headings.fmtSection": (str, ""),
- "headings.hideScene": (bool, False),
- "headings.hideHardScene": (bool, False),
- "headings.hideSection": (bool, True),
- "headings.centerTitle": (bool, True),
- "headings.centerChapter": (bool, False),
- "headings.centerScene": (bool, False),
- "headings.breakTitle": (bool, True),
- "headings.breakChapter": (bool, True),
- "headings.breakScene": (bool, False),
- "text.includeSynopsis": (bool, False),
- "text.includeComments": (bool, False),
- "text.includeKeywords": (bool, False),
- "text.includeBodyText": (bool, True),
- "text.ignoredKeywords": (str, ""),
- "text.addNoteHeadings": (bool, True),
- "format.textFont": (str, CONFIG.textFont),
- "format.textSize": (int, 12),
- "format.lineHeight": (float, 1.15, 0.75, 3.0),
- "format.justifyText": (bool, False),
- "format.stripUnicode": (bool, False),
- "format.replaceTabs": (bool, False),
- "format.pageUnit": (str, "cm"),
- "format.pageSize": (str, "A4"),
- "format.pageWidth": (float, 21.0),
- "format.pageHeight": (float, 29.7),
- "format.topMargin": (float, 2.0),
- "format.bottomMargin": (float, 2.0),
- "format.leftMargin": (float, 2.0),
- "format.rightMargin": (float, 2.0),
- "odt.addColours": (bool, True),
- "odt.pageHeader": (str, nwHeadFmt.ODT_AUTO),
- "odt.pageCountOffset": (int, 0),
- "md.preserveBreaks": (bool, True),
- "html.addStyles": (bool, True),
- "html.preserveTabs": (bool, False),
+ "filter.includeNovel": (bool, True),
+ "filter.includeNotes": (bool, False),
+ "filter.includeInactive": (bool, False),
+ "headings.fmtTitle": (str, nwHeadFmt.TITLE),
+ "headings.fmtChapter": (str, nwHeadFmt.TITLE),
+ "headings.fmtUnnumbered": (str, nwHeadFmt.TITLE),
+ "headings.fmtScene": (str, "* * *"),
+ "headings.fmtHardScene": (str, ""),
+ "headings.fmtSection": (str, ""),
+ "headings.hideTitle": (bool, False),
+ "headings.hideChapter": (bool, False),
+ "headings.hideUnnumbered": (bool, False),
+ "headings.hideScene": (bool, False),
+ "headings.hideHardScene": (bool, False),
+ "headings.hideSection": (bool, True),
+ "headings.centerTitle": (bool, True),
+ "headings.centerChapter": (bool, False),
+ "headings.centerScene": (bool, False),
+ "headings.breakTitle": (bool, True),
+ "headings.breakChapter": (bool, True),
+ "headings.breakScene": (bool, False),
+ "text.includeSynopsis": (bool, False),
+ "text.includeComments": (bool, False),
+ "text.includeKeywords": (bool, False),
+ "text.includeBodyText": (bool, True),
+ "text.ignoredKeywords": (str, ""),
+ "text.addNoteHeadings": (bool, True),
+ "format.textFont": (str, CONFIG.textFont),
+ "format.textSize": (int, 12),
+ "format.lineHeight": (float, 1.15, 0.75, 3.0),
+ "format.justifyText": (bool, False),
+ "format.stripUnicode": (bool, False),
+ "format.replaceTabs": (bool, False),
+ "format.pageUnit": (str, "cm"),
+ "format.pageSize": (str, "A4"),
+ "format.pageWidth": (float, 21.0),
+ "format.pageHeight": (float, 29.7),
+ "format.topMargin": (float, 2.0),
+ "format.bottomMargin": (float, 2.0),
+ "format.leftMargin": (float, 2.0),
+ "format.rightMargin": (float, 2.0),
+ "odt.addColours": (bool, True),
+ "odt.pageHeader": (str, nwHeadFmt.ODT_AUTO),
+ "odt.pageCountOffset": (int, 0),
+ "md.preserveBreaks": (bool, True),
+ "html.addStyles": (bool, True),
+ "html.preserveTabs": (bool, False),
}
SETTINGS_LABELS = {
@@ -108,9 +111,6 @@ SETTINGS_LABELS = {
"headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Format"),
"headings.fmtHardScene": QT_TRANSLATE_NOOP("Builds", "Hard Scene Format"),
"headings.fmtSection": QT_TRANSLATE_NOOP("Builds", "Section Format"),
- "headings.hideScene": QT_TRANSLATE_NOOP("Builds", "Hide Scene Breaks"),
- "headings.hideHardScene": QT_TRANSLATE_NOOP("Builds", "Hide Hard Scene Breaks"),
- "headings.hideSection": QT_TRANSLATE_NOOP("Builds", "Hide Section Breaks"),
"text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"),
"text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"),
diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py
index 23b10791..19457b81 100644
--- a/novelwriter/core/docbuild.py
+++ b/novelwriter/core/docbuild.py
@@ -275,9 +275,18 @@ class NWBuildDocument:
fontInfo = QFontInfo(bldFont)
textFixed = fontInfo.fixedPitch()
- bldObj.setTitleFormat(self._build.getStr("headings.fmtTitle"))
- bldObj.setChapterFormat(self._build.getStr("headings.fmtChapter"))
- bldObj.setUnNumberedFormat(self._build.getStr("headings.fmtUnnumbered"))
+ bldObj.setTitleFormat(
+ self._build.getStr("headings.fmtTitle"),
+ self._build.getBool("headings.hideTitle")
+ )
+ bldObj.setChapterFormat(
+ self._build.getStr("headings.fmtChapter"),
+ self._build.getBool("headings.hideChapter")
+ )
+ bldObj.setUnNumberedFormat(
+ self._build.getStr("headings.fmtUnnumbered"),
+ self._build.getBool("headings.hideUnnumbered")
+ )
bldObj.setSceneFormat(
self._build.getStr("headings.fmtScene"),
self._build.getBool("headings.hideScene")
diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py
index 8e1a67ca..4922e43b 100644
--- a/novelwriter/core/tohtml.py
+++ b/novelwriter/core/tohtml.py
@@ -256,10 +256,6 @@ class ToHtml(Tokenizer):
tHead = tText.replace(nwHeadFmt.BR, "
")
lines.append(f"
{aNm}{tHead}
\n")
- elif tType == self.T_UNNUM:
- tHead = tText.replace(nwHeadFmt.BR, "
")
- lines.append(f"<{h2}{hStyle}>{aNm}{tHead}{h2}>\n")
-
elif tType == self.T_HEAD1:
tHead = tText.replace(nwHeadFmt.BR, "
")
lines.append(f"<{h1}{h1Cl}{hStyle}>{aNm}{tHead}{h1}>\n")
diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py
index 3d52e19a..ac44471c 100644
--- a/novelwriter/core/tokenizer.py
+++ b/novelwriter/core/tokenizer.py
@@ -88,14 +88,13 @@ class Tokenizer(ABC):
T_COMMENT = 4 # Comment line
T_KEYWORD = 5 # Command line
T_TITLE = 6 # Title
- T_UNNUM = 7 # Unnumbered
- T_HEAD1 = 8 # Heading 1
- T_HEAD2 = 9 # Heading 2
- T_HEAD3 = 10 # Heading 3
- T_HEAD4 = 11 # Heading 4
- T_TEXT = 12 # Text line
- T_SEP = 13 # Scene separator
- T_SKIP = 14 # Paragraph break
+ T_HEAD1 = 7 # Heading 1
+ T_HEAD2 = 8 # Heading 2
+ T_HEAD3 = 9 # Heading 3
+ T_HEAD4 = 10 # Heading 4
+ T_TEXT = 11 # Text line
+ T_SEP = 12 # Scene separator
+ T_SKIP = 13 # Paragraph break
# Block Style
A_NONE = 0x0000 # No special style
@@ -111,7 +110,7 @@ class Tokenizer(ABC):
A_IND_R = 0x0200 # Right indentation
# Lookups
- L_HEADINGS = [T_TITLE, T_UNNUM, T_HEAD1, T_HEAD2, T_HEAD3, T_HEAD4]
+ L_HEADINGS = [T_TITLE, T_HEAD1, T_HEAD2, T_HEAD3, T_HEAD4]
def __init__(self, project: NWProject) -> None:
@@ -159,6 +158,9 @@ class Tokenizer(ABC):
self._fmtHScene = nwHeadFmt.TITLE # Formatting for hard scenes
self._fmtSection = nwHeadFmt.TITLE # Formatting for sections
+ self._hideTitle = False # Do not include title headings
+ self._hideChapter = False # Do not include chapter headings
+ self._hideUnNum = False # Do not include unnumbered headings
self._hideScene = False # Do not include scene headings
self._hideHScene = False # Do not include hard scene headings
self._hideSection = False # Do not include section headings
@@ -171,7 +173,7 @@ class Tokenizer(ABC):
# Instance Variables
self._hFormatter = HeadingFormatter(self._project)
- self._noSep = False # Flag to indicate that we don't want a scene separator
+ self._noSep = True # Flag to indicate that we don't want a scene separator
# This File
self._isNone = False # Document has unknown layout
@@ -217,7 +219,7 @@ class Tokenizer(ABC):
return self._result
@property
- def allMarkdown(self) -> list:
+ def allMarkdown(self) -> list[str]:
"""The combined novelWriter Markdown text."""
return self._allMarkdown
@@ -227,7 +229,7 @@ class Tokenizer(ABC):
return self._counts
@property
- def errData(self) -> list:
+ def errData(self) -> list[str]:
"""The error data."""
return self._errData
@@ -235,34 +237,37 @@ class Tokenizer(ABC):
# Setters
##
- def setTitleFormat(self, hFormat: str) -> None:
+ def setTitleFormat(self, hFormat: str, hide: bool = False) -> None:
"""Set the title format pattern."""
self._fmtTitle = hFormat.strip()
+ self._hideTitle = hide
return
- def setChapterFormat(self, hFormat: str) -> None:
+ def setChapterFormat(self, hFormat: str, hide: bool = False) -> None:
"""Set the chapter format pattern."""
self._fmtChapter = hFormat.strip()
+ self._hideChapter = hide
return
- def setUnNumberedFormat(self, hFormat: str) -> None:
+ def setUnNumberedFormat(self, hFormat: str, hide: bool = False) -> None:
"""Set the unnumbered format pattern."""
self._fmtUnNum = hFormat.strip()
+ self._hideUnNum = hide
return
- def setSceneFormat(self, hFormat: str, hide: bool) -> None:
+ def setSceneFormat(self, hFormat: str, hide: bool = False) -> None:
"""Set the scene format pattern and hidden status."""
self._fmtScene = hFormat.strip()
self._hideScene = hide
return
- def setHardSceneFormat(self, hFormat: str, hide: bool) -> None:
+ def setHardSceneFormat(self, hFormat: str, hide: bool = False) -> None:
"""Set the hard scene format pattern and hidden status."""
self._fmtHScene = hFormat.strip()
self._hideHScene = hide
return
- def setSectionFormat(self, hFormat: str, hide: bool) -> None:
+ def setSectionFormat(self, hFormat: str, hide: bool = False) -> None:
"""Set the section format pattern and hidden status."""
self._fmtSection = hFormat.strip()
self._hideSection = hide
@@ -579,10 +584,16 @@ class Tokenizer(ABC):
tText = aLine[2:].strip()
tType = self.T_HEAD1 if isPlain else self.T_TITLE
tStyle = self.A_NONE if isPlain else (self.A_PBB | self.A_CENTRE)
+ sHide = self._hideTitle if isPlain else False
if self._isNovel:
- if isPlain:
+ if sHide:
+ tText = ""
+ tType = self.T_EMPTY
+ tStyle = self.A_NONE
+ elif isPlain:
tText = self._hFormatter.apply(self._fmtTitle, tText, nHead)
tStyle = self._titleStyle
+ if isPlain:
self._hFormatter.resetScene()
else:
self._hFormatter.resetAll()
@@ -609,15 +620,19 @@ class Tokenizer(ABC):
tText = aLine[3:].strip()
tType = self.T_HEAD2
tStyle = self.A_NONE
+ sHide = self._hideChapter if isPlain else self._hideUnNum
tFormat = self._fmtChapter if isPlain else self._fmtUnNum
if self._isNovel:
if isPlain:
self._hFormatter.incChapter()
- tText = self._hFormatter.apply(tFormat, tText, nHead)
- tType = self.T_HEAD2 if isPlain else self.T_UNNUM
- tStyle = self._chapterStyle
- self._noSep = True
+ if sHide:
+ tText = ""
+ tType = self.T_EMPTY
+ else:
+ tText = self._hFormatter.apply(tFormat, tText, nHead)
+ tStyle = self._chapterStyle
self._hFormatter.resetScene()
+ self._noSep = True
self._tokens.append((
tType, nHead, tText, [], tStyle
@@ -646,15 +661,18 @@ class Tokenizer(ABC):
tFormat = self._fmtScene if isPlain else self._fmtHScene
if self._isNovel:
self._hFormatter.incScene()
- tText = self._hFormatter.apply(tFormat, tText, nHead)
- tStyle = self._sceneStyle
- if tText == "":
- tType = self.T_EMPTY if self._noSep or sHide else self.T_SKIP
- tStyle = self.A_NONE
- elif tText == tFormat:
- tText = "" if self._noSep else tText
- tType = self.T_EMPTY if self._noSep else self.T_SEP
- tStyle = self.A_NONE if self._noSep else self.A_CENTRE
+ if sHide:
+ tText = ""
+ tType = self.T_EMPTY
+ else:
+ tText = self._hFormatter.apply(tFormat, tText, nHead)
+ tStyle = self._sceneStyle
+ if tText == "": # Empty Format
+ tType = self.T_EMPTY if self._noSep else self.T_SKIP
+ elif tText == tFormat: # Static Format
+ tText = "" if self._noSep else tText
+ tType = self.T_EMPTY if self._noSep else self.T_SEP
+ tStyle = self.A_NONE if self._noSep else self.A_CENTRE
self._noSep = False
self._tokens.append((
@@ -676,12 +694,16 @@ class Tokenizer(ABC):
tType = self.T_HEAD4
tStyle = self.A_NONE
if self._isNovel:
- tText = self._hFormatter.apply(self._fmtSection, tText, nHead)
- if tText == "":
- tType = self.T_EMPTY if self._hideSection else self.T_SKIP
- elif tText == self._fmtSection:
- tType = self.T_SEP
- tStyle = self.A_CENTRE
+ if self._hideSection:
+ tText = ""
+ tType = self.T_EMPTY
+ else:
+ tText = self._hFormatter.apply(self._fmtSection, tText, nHead)
+ if tText == "": # Empty Format
+ tType = self.T_SKIP
+ elif tText == self._fmtSection: # Static Format
+ tType = self.T_SEP
+ tStyle = self.A_CENTRE
self._tokens.append((
tType, nHead, tText, [], tStyle
diff --git a/novelwriter/core/tomd.py b/novelwriter/core/tomd.py
index a57ba18f..82fdb78a 100644
--- a/novelwriter/core/tomd.py
+++ b/novelwriter/core/tomd.py
@@ -27,7 +27,7 @@ import logging
from pathlib import Path
-from novelwriter.constants import nwHeadFmt, nwLabels
+from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode
from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import Tokenizer
@@ -108,6 +108,7 @@ class ToMarkdown(Tokenizer):
self.FMT_SUB_B: "",
self.FMT_SUB_E: "",
}
+ cSkip = ""
else:
# Extended Markdown
mdTags = {
@@ -126,6 +127,7 @@ class ToMarkdown(Tokenizer):
self.FMT_SUB_B: "~",
self.FMT_SUB_E: "~",
}
+ cSkip = nwUnicode.U_MMSP
self._result = ""
@@ -136,7 +138,7 @@ class ToMarkdown(Tokenizer):
for tType, _, tText, tFormat, tStyle in self._tokens:
if tType == self.T_EMPTY:
- if len(para) > 0:
+ if para:
tTemp = (lineSep.join(para)).rstrip(" ")
lines.append(f"{tTemp}\n\n")
para = []
@@ -145,10 +147,6 @@ class ToMarkdown(Tokenizer):
tHead = tText.replace(nwHeadFmt.BR, "\n")
lines.append(f"# {tHead}\n\n")
- elif tType == self.T_UNNUM:
- tHead = tText.replace(nwHeadFmt.BR, "\n")
- lines.append(f"## {tHead}\n\n")
-
elif tType == self.T_HEAD1:
tHead = tText.replace(nwHeadFmt.BR, "\n")
lines.append(f"# {tHead}\n\n")
@@ -169,7 +167,7 @@ class ToMarkdown(Tokenizer):
lines.append(f"{tText}\n\n")
elif tType == self.T_SKIP:
- lines.append("\n\n\n")
+ lines.append(f"{cSkip}\n\n")
elif tType == self.T_TEXT:
tTemp = tText
diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py
index 494b12c4..e3ddebdf 100644
--- a/novelwriter/core/toodt.py
+++ b/novelwriter/core/toodt.py
@@ -450,10 +450,6 @@ class ToOdt(Tokenizer):
tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addTextPar("Title", oStyle, tHead, isHead=False) # Title must be text:p
- elif tType == self.T_UNNUM:
- tHead = tText.replace(nwHeadFmt.BR, "\n")
- self._addTextPar("Heading_20_2", oStyle, tHead, isHead=True, oLevel="2")
-
elif tType == self.T_HEAD1:
tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addTextPar("Heading_20_1", oStyle, tHead, isHead=True, oLevel="1")
diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py
index 3f52e9c8..0e89b3c6 100644
--- a/novelwriter/tools/manuscript.py
+++ b/novelwriter/tools/manuscript.py
@@ -605,23 +605,26 @@ class _DetailsWidget(QWidget):
hFmt.resetScene()
hFmt.incScene()
title = self.tr("Title")
+ hidden = self.tr("Hidden")
item = QTreeWidgetItem()
item.setText(0, build.getLabel("headings"))
item.setText(1, "")
self.listView.addTopLevelItem(item)
- for key in [
- "headings.fmtTitle", "headings.fmtChapter", "headings.fmtUnnumbered",
- "headings.fmtScene", "headings.fmtHardScene", "headings.fmtSection"
+ for hFormat, hHide in [
+ ("headings.fmtTitle", "headings.hideTitle"),
+ ("headings.fmtChapter", "headings.hideChapter"),
+ ("headings.fmtUnnumbered", "headings.hideUnnumbered"),
+ ("headings.fmtScene", "headings.hideScene"),
+ ("headings.fmtHardScene", "headings.hideHardScene"),
+ ("headings.fmtSection", "headings.hideSection"),
]:
sub = QTreeWidgetItem()
- sub.setText(0, build.getLabel(key))
- sub.setText(1, hFmt.apply(build.getStr(key), title, 0))
- item.addChild(sub)
- for key in ["headings.hideScene", "headings.hideHardScene", "headings.hideSection"]:
- sub = QTreeWidgetItem()
- sub.setText(0, build.getLabel(key))
- sub.setIcon(1, on if build.getBool(key) else off)
+ sub.setText(0, build.getLabel(hFormat))
+ if build.getBool(hHide):
+ sub.setText(1, f"[{hidden}]")
+ else:
+ sub.setText(1, hFmt.apply(build.getStr(hFormat), title, 0))
item.addChild(sub)
# Text Content
diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py
index b298adbd..d072c5fb 100644
--- a/novelwriter/tools/manussettings.py
+++ b/novelwriter/tools/manussettings.py
@@ -601,7 +601,7 @@ class _HeadingsTab(NScrollablePage):
# Format Boxes
# ============
self.formatBox = QGridLayout()
- self.formatBox.setHorizontalSpacing(vSp)
+ self.formatBox.setHorizontalSpacing(bSp)
# Title Heading
self.lblTitle = QLabel(self._build.getLabel("headings.fmtTitle"), self)
@@ -610,14 +610,15 @@ class _HeadingsTab(NScrollablePage):
self.btnTitle = QToolButton(self)
self.btnTitle.setIcon(SHARED.theme.getIcon("edit"))
self.btnTitle.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE))
-
- wrapTitle = QHBoxLayout()
- wrapTitle.addWidget(self.fmtTitle)
- wrapTitle.addWidget(self.btnTitle)
- wrapTitle.setSpacing(bSp)
+ self.hdeTitle = QLabel(self.tr("Hide"), self)
+ self.hdeTitle.setIndent(bSp)
+ self.swtTitle = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblTitle, 0, 0)
- self.formatBox.addLayout(wrapTitle, 0, 1)
+ self.formatBox.addWidget(self.fmtTitle, 0, 1)
+ self.formatBox.addWidget(self.btnTitle, 0, 2)
+ self.formatBox.addWidget(self.hdeTitle, 0, 3)
+ self.formatBox.addWidget(self.swtTitle, 0, 4)
# Chapter Heading
self.lblChapter = QLabel(self._build.getLabel("headings.fmtChapter"), self)
@@ -626,14 +627,15 @@ class _HeadingsTab(NScrollablePage):
self.btnChapter = QToolButton(self)
self.btnChapter.setIcon(SHARED.theme.getIcon("edit"))
self.btnChapter.clicked.connect(lambda: self._editHeading(self.EDIT_CHAPTER))
-
- wrapChapter = QHBoxLayout()
- wrapChapter.addWidget(self.fmtChapter)
- wrapChapter.addWidget(self.btnChapter)
- wrapChapter.setSpacing(bSp)
+ self.hdeChapter = QLabel(self.tr("Hide"), self)
+ self.hdeChapter.setIndent(bSp)
+ self.swtChapter = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblChapter, 1, 0)
- self.formatBox.addLayout(wrapChapter, 1, 1)
+ self.formatBox.addWidget(self.fmtChapter, 1, 1)
+ self.formatBox.addWidget(self.btnChapter, 1, 2)
+ self.formatBox.addWidget(self.hdeChapter, 1, 3)
+ self.formatBox.addWidget(self.swtChapter, 1, 4)
# Unnumbered Chapter Heading
self.lblUnnumbered = QLabel(self._build.getLabel("headings.fmtUnnumbered"), self)
@@ -642,95 +644,66 @@ class _HeadingsTab(NScrollablePage):
self.btnUnnumbered = QToolButton(self)
self.btnUnnumbered.setIcon(SHARED.theme.getIcon("edit"))
self.btnUnnumbered.clicked.connect(lambda: self._editHeading(self.EDIT_UNNUM))
-
- wrapUnnumbered = QHBoxLayout()
- wrapUnnumbered.addWidget(self.fmtUnnumbered)
- wrapUnnumbered.addWidget(self.btnUnnumbered)
- wrapUnnumbered.setSpacing(bSp)
+ self.hdeUnnumbered = QLabel(self.tr("Hide"), self)
+ self.hdeUnnumbered.setIndent(bSp)
+ self.swtUnnumbered = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblUnnumbered, 2, 0)
- self.formatBox.addLayout(wrapUnnumbered, 2, 1)
+ self.formatBox.addWidget(self.fmtUnnumbered, 2, 1)
+ self.formatBox.addWidget(self.btnUnnumbered, 2, 2)
+ self.formatBox.addWidget(self.hdeUnnumbered, 2, 3)
+ self.formatBox.addWidget(self.swtUnnumbered, 2, 4)
# Scene Heading
- sceneHideTip = self._build.getLabel("headings.hideScene")
self.lblScene = QLabel(self._build.getLabel("headings.fmtScene"), self)
self.fmtScene = QLineEdit("", self)
self.fmtScene.setReadOnly(True)
self.btnScene = QToolButton(self)
self.btnScene.setIcon(SHARED.theme.getIcon("edit"))
self.btnScene.clicked.connect(lambda: self._editHeading(self.EDIT_SCENE))
- self.hdeScene = QLabel(self.tr("Hide"))
- self.hdeScene.setToolTip(sceneHideTip)
+ self.hdeScene = QLabel(self.tr("Hide"), self)
+ self.hdeScene.setIndent(bSp)
self.swtScene = NSwitch(self, height=iPx)
- self.swtScene.setToolTip(sceneHideTip)
-
- wrapScene = QHBoxLayout()
- wrapScene.addWidget(self.fmtScene)
- wrapScene.addWidget(self.btnScene)
- wrapScene.setSpacing(bSp)
-
- wrapSceneHide = QHBoxLayout()
- wrapSceneHide.addWidget(self.hdeScene)
- wrapSceneHide.addWidget(self.swtScene)
- wrapSceneHide.setSpacing(bSp)
self.formatBox.addWidget(self.lblScene, 3, 0)
- self.formatBox.addLayout(wrapScene, 3, 1)
- self.formatBox.addLayout(wrapSceneHide, 3, 2)
+ self.formatBox.addWidget(self.fmtScene, 3, 1)
+ self.formatBox.addWidget(self.btnScene, 3, 2)
+ self.formatBox.addWidget(self.hdeScene, 3, 3)
+ self.formatBox.addWidget(self.swtScene, 3, 4)
# Alt Scene Heading
- hardSceneHideTip = self._build.getLabel("headings.hideHardScene")
self.lblHScene = QLabel(self._build.getLabel("headings.fmtHardScene"), self)
self.fmtHScene = QLineEdit("", self)
self.fmtHScene.setReadOnly(True)
self.btnHScene = QToolButton(self)
self.btnHScene.setIcon(SHARED.theme.getIcon("edit"))
self.btnHScene.clicked.connect(lambda: self._editHeading(self.EDIT_HSCENE))
- self.hdeHScene = QLabel(self.tr("Hide"))
- self.hdeHScene.setToolTip(hardSceneHideTip)
+ self.hdeHScene = QLabel(self.tr("Hide"), self)
+ self.hdeHScene.setIndent(bSp)
self.swtHScene = NSwitch(self, height=iPx)
- self.swtHScene.setToolTip(hardSceneHideTip)
-
- wrapHScene = QHBoxLayout()
- wrapHScene.addWidget(self.fmtHScene)
- wrapHScene.addWidget(self.btnHScene)
- wrapHScene.setSpacing(bSp)
-
- wrapHSceneHide = QHBoxLayout()
- wrapHSceneHide.addWidget(self.hdeHScene)
- wrapHSceneHide.addWidget(self.swtHScene)
- wrapHSceneHide.setSpacing(bSp)
self.formatBox.addWidget(self.lblHScene, 4, 0)
- self.formatBox.addLayout(wrapHScene, 4, 1)
- self.formatBox.addLayout(wrapHSceneHide, 4, 2)
+ self.formatBox.addWidget(self.fmtHScene, 4, 1)
+ self.formatBox.addWidget(self.btnHScene, 4, 2)
+ self.formatBox.addWidget(self.hdeHScene, 4, 3)
+ self.formatBox.addWidget(self.swtHScene, 4, 4)
# Section Heading
- sectionHideTip = self._build.getLabel("headings.hideSection")
self.lblSection = QLabel(self._build.getLabel("headings.fmtSection"), self)
self.fmtSection = QLineEdit("", self)
self.fmtSection.setReadOnly(True)
self.btnSection = QToolButton(self)
self.btnSection.setIcon(SHARED.theme.getIcon("edit"))
self.btnSection.clicked.connect(lambda: self._editHeading(self.EDIT_SECTION))
- self.hdeSection = QLabel(self.tr("Hide"))
- self.hdeSection.setToolTip(sectionHideTip)
+ self.hdeSection = QLabel(self.tr("Hide"), self)
+ self.hdeSection.setIndent(bSp)
self.swtSection = NSwitch(self, height=iPx)
- self.swtSection.setToolTip(sectionHideTip)
-
- wrapSection = QHBoxLayout()
- wrapSection.addWidget(self.fmtSection)
- wrapSection.addWidget(self.btnSection)
- wrapSection.setSpacing(bSp)
-
- wrapSectionHide = QHBoxLayout()
- wrapSectionHide.addWidget(self.hdeSection)
- wrapSectionHide.addWidget(self.swtSection)
- wrapSectionHide.setSpacing(bSp)
self.formatBox.addWidget(self.lblSection, 5, 0)
- self.formatBox.addLayout(wrapSection, 5, 1)
- self.formatBox.addLayout(wrapSectionHide, 5, 2)
+ self.formatBox.addWidget(self.fmtSection, 5, 1)
+ self.formatBox.addWidget(self.btnSection, 5, 2)
+ self.formatBox.addWidget(self.hdeSection, 5, 3)
+ self.formatBox.addWidget(self.swtSection, 5, 4)
# Edit Form
# =========
@@ -860,9 +833,14 @@ class _HeadingsTab(NScrollablePage):
self.fmtScene.setText(self._build.getStr("headings.fmtScene"))
self.fmtHScene.setText(self._build.getStr("headings.fmtHardScene"))
self.fmtSection.setText(self._build.getStr("headings.fmtSection"))
+
+ self.swtTitle.setChecked(self._build.getBool("headings.hideTitle"))
+ self.swtChapter.setChecked(self._build.getBool("headings.hideChapter"))
+ self.swtUnnumbered.setChecked(self._build.getBool("headings.hideUnnumbered"))
self.swtScene.setChecked(self._build.getBool("headings.hideScene"))
self.swtHScene.setChecked(self._build.getBool("headings.hideHardScene"))
self.swtSection.setChecked(self._build.getBool("headings.hideSection"))
+
self.centerTitle.setChecked(self._build.getBool("headings.centerTitle"))
self.centerChapter.setChecked(self._build.getBool("headings.centerChapter"))
self.centerScene.setChecked(self._build.getBool("headings.centerScene"))
@@ -873,9 +851,13 @@ class _HeadingsTab(NScrollablePage):
def saveContent(self) -> None:
"""Save choices back into build object."""
+ self._build.setValue("headings.hideTitle", self.swtTitle.isChecked())
+ self._build.setValue("headings.hideChapter", self.swtChapter.isChecked())
+ self._build.setValue("headings.hideUnnumbered", self.swtUnnumbered.isChecked())
self._build.setValue("headings.hideScene", self.swtScene.isChecked())
self._build.setValue("headings.hideHardScene", self.swtHScene.isChecked())
self._build.setValue("headings.hideSection", self.swtSection.isChecked())
+
self._build.setValue("headings.centerTitle", self.centerTitle.isChecked())
self._build.setValue("headings.centerChapter", self.centerChapter.isChecked())
self._build.setValue("headings.centerScene", self.centerScene.isChecked())
diff --git a/tests/test_core/test_core_tohtml.py b/tests/test_core/test_core_tohtml.py
index ef6d9b3e..da80e880 100644
--- a/tests/test_core/test_core_tohtml.py
+++ b/tests/test_core/test_core_tohtml.py
@@ -288,7 +288,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
# Unnumbered
html._tokens = [
- (html.T_UNNUM, 1, "Prologue", None, html.A_PBB),
+ (html.T_HEAD2, 1, "Prologue", None, html.A_PBB),
(html.T_EMPTY, 1, "", None, html.A_NONE),
]
html.doConvert()
diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py
index 03343d0c..b3b69774 100644
--- a/tests/test_core/test_core_tokenizer.py
+++ b/tests/test_core/test_core_tokenizer.py
@@ -62,7 +62,11 @@ def testCoreToken_Setters(mockGUI):
assert tokens._marginHead4 == (0.584, 0.500)
assert tokens._marginText == (0.000, 0.584)
assert tokens._marginMeta == (0.000, 0.584)
+ assert tokens._hideTitle is False
+ assert tokens._hideChapter is False
+ assert tokens._hideUnNum is False
assert tokens._hideScene is False
+ assert tokens._hideHScene is False
assert tokens._hideSection is False
assert tokens._linkHeaders is False
assert tokens._doBodyText is True
@@ -71,9 +75,9 @@ def testCoreToken_Setters(mockGUI):
assert tokens._doKeywords is False
# Set new values
- tokens.setTitleFormat(f"T: {nwHeadFmt.TITLE}")
- tokens.setChapterFormat(f"C: {nwHeadFmt.TITLE}")
- tokens.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}")
+ tokens.setTitleFormat(f"T: {nwHeadFmt.TITLE}", True)
+ tokens.setChapterFormat(f"C: {nwHeadFmt.TITLE}", True)
+ tokens.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}", True)
tokens.setSceneFormat(f"S: {nwHeadFmt.TITLE}", True)
tokens.setHardSceneFormat(f"H: {nwHeadFmt.TITLE}", True)
tokens.setSectionFormat(f"X: {nwHeadFmt.TITLE}", True)
@@ -114,7 +118,11 @@ def testCoreToken_Setters(mockGUI):
assert tokens._marginHead4 == (2.0, 2.0)
assert tokens._marginText == (2.0, 2.0)
assert tokens._marginMeta == (2.0, 2.0)
+ assert tokens._hideTitle is True
+ assert tokens._hideChapter is True
+ assert tokens._hideUnNum is True
assert tokens._hideScene is True
+ assert tokens._hideHScene is True
assert tokens._hideSection is True
assert tokens._linkHeaders is True
assert tokens._doBodyText is False
@@ -122,6 +130,12 @@ def testCoreToken_Setters(mockGUI):
assert tokens._doComments is True
assert tokens._doKeywords is True
+ # Properties
+ assert tokens.result == ""
+ assert tokens.allMarkdown == []
+ assert tokens.textStats == {}
+ assert tokens.errData == []
+
# Check Limits
tokens.setLineHeight(0.0)
assert tokens._lineHeight == 0.5
@@ -419,7 +433,7 @@ def testCoreToken_HeaderFormat(mockGUI):
tokens.tokenizeText()
assert tokens._tokens == [
- (Tokenizer.T_UNNUM, 1, "Prologue", [], Tokenizer.A_PBB),
+ (Tokenizer.T_HEAD2, 1, "Prologue", [], Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
]
assert tokens.allMarkdown[-1] == "##! Prologue\n\n"
@@ -1340,7 +1354,7 @@ def testCoreToken_ProcessHeaders(mockGUI):
tokens.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}")
tokens.tokenizeText()
assert tokens._tokens == [
- (Tokenizer.T_UNNUM, 1, "U: Prologue", [], Tokenizer.A_PBB),
+ (Tokenizer.T_HEAD2, 1, "U: Prologue", [], Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
]
@@ -1720,10 +1734,8 @@ def testCoreToken_CountStats(mockGUI, ipsumText):
@pytest.mark.core
-def testCoreToken_HeaderCounterAndVisibility(mockGUI):
- """Test the header counter and visibility of the Tokenizer class.
- This is a special test to cover issue #1704.
- """
+def testCoreToken_SceneSeparators(mockGUI):
+ """Test the section and scene separators of the Tokenizer class."""
project = NWProject()
project.data.setLanguage("en")
project._loadProjectLocalisation()
@@ -1841,6 +1853,172 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"Text\n\n"
)
+ # Separators with Scenes Only
+ # ===========================
+ # Requires a fresh builder class
+ md = ToMarkdown(project)
+ md.setExtendedMarkdown()
+ md._isNone = False
+ md._isNote = False
+ md._isNovel = True
+
+ md._text = (
+ "### Scene One\n\n"
+ "Text\n\n"
+ "### Scene Two\n\n"
+ "Text\n\n"
+ "### Scene Three\n\n"
+ "Text\n\n"
+ "###! Scene Four\n\n"
+ "Text\n\n"
+ )
+
+ md.setSceneFormat("", False)
+ md.setHardSceneFormat("* * *", False)
+ md.tokenizeText()
+ md.doConvert()
+ assert md.result == (
+ "Text\n\n"
+ "\u205f\n\n"
+ "Text\n\n"
+ "\u205f\n\n"
+ "Text\n\n"
+ "* * *\n\n"
+ "Text\n\n"
+ )
+
+# END Test testCoreToken_SceneSeparators
+
+
+@pytest.mark.core
+def testCoreToken_HeaderVisibility(mockGUI):
+ """Test the heading visibility settings of the Tokenizer class."""
+ project = NWProject()
+ project.data.setLanguage("en")
+ project._loadProjectLocalisation()
+ md = ToMarkdown(project)
+
+ md._text = (
+ "#! Novel\n\n"
+ "# Title One\n\n"
+ "##! Prologue\n\n"
+ "Text\n\n"
+ "## Chapter One\n\n"
+ "### Scene One\n\n"
+ "Text\n\n"
+ "### Scene Two\n\n"
+ "#### Section Two\n\n"
+ "Text\n\n"
+ "## Chapter Two\n\n"
+ "### Scene Three\n\n"
+ "Text\n\n"
+ "###! Scene Four\n\n"
+ "Text\n\n"
+ )
+
+ # Novel Files
+ # ===========
+
+ md._isNone = False
+ md._isNote = False
+ md._isNovel = True
+
+ # Show All
+ md.setTitleFormat(nwHeadFmt.TITLE, False)
+ md.setChapterFormat(nwHeadFmt.TITLE, False)
+ md.setUnNumberedFormat(nwHeadFmt.TITLE, False)
+ md.setSceneFormat(nwHeadFmt.TITLE, False)
+ md.setHardSceneFormat(nwHeadFmt.TITLE, False)
+ md.setSectionFormat(nwHeadFmt.TITLE, False)
+
+ md.tokenizeText()
+ md.doConvert()
+ assert md.result == (
+ "# Novel\n\n"
+ "# Title One\n\n"
+ "## Prologue\n\n"
+ "Text\n\n"
+ "## Chapter One\n\n"
+ "### Scene One\n\n"
+ "Text\n\n"
+ "### Scene Two\n\n"
+ "#### Section Two\n\n"
+ "Text\n\n"
+ "## Chapter Two\n\n"
+ "### Scene Three\n\n"
+ "Text\n\n"
+ "### Scene Four\n\n"
+ "Text\n\n"
+ )
+
+ # Hide All
+ md.setTitleFormat(nwHeadFmt.TITLE, True)
+ md.setChapterFormat(nwHeadFmt.TITLE, True)
+ md.setUnNumberedFormat(nwHeadFmt.TITLE, True)
+ md.setSceneFormat(nwHeadFmt.TITLE, True)
+ md.setHardSceneFormat(nwHeadFmt.TITLE, True)
+ md.setSectionFormat(nwHeadFmt.TITLE, True)
+
+ md.tokenizeText()
+ md.doConvert()
+ assert md.result == (
+ "# Novel\n\n"
+ "Text\n\n"
+ "Text\n\n"
+ "Text\n\n"
+ "Text\n\n"
+ "Text\n\n"
+ )
+
+ # Note Files
+ # ==========
+
+ md._isNone = False
+ md._isNote = True
+ md._isNovel = False
+
+ # Hide All
+ md.setTitleFormat(nwHeadFmt.TITLE, True)
+ md.setChapterFormat(nwHeadFmt.TITLE, True)
+ md.setUnNumberedFormat(nwHeadFmt.TITLE, True)
+ md.setSceneFormat(nwHeadFmt.TITLE, True)
+ md.setHardSceneFormat(nwHeadFmt.TITLE, True)
+ md.setSectionFormat(nwHeadFmt.TITLE, True)
+
+ md.tokenizeText()
+ md.doConvert()
+ assert md.result == (
+ "# Novel\n\n"
+ "# Title One\n\n"
+ "## Prologue\n\n"
+ "Text\n\n"
+ "## Chapter One\n\n"
+ "### Scene One\n\n"
+ "Text\n\n"
+ "### Scene Two\n\n"
+ "#### Section Two\n\n"
+ "Text\n\n"
+ "## Chapter Two\n\n"
+ "### Scene Three\n\n"
+ "Text\n\n"
+ "### Scene Four\n\n"
+ "Text\n\n"
+ )
+
+# END Test testCoreToken_HeaderVisibility
+
+
+@pytest.mark.core
+def testCoreToken_CounterHandling(mockGUI):
+ """Test the heading counter of the Tokenizer class."""
+ project = NWProject()
+ project.data.setLanguage("en")
+ project._loadProjectLocalisation()
+ md = ToMarkdown(project)
+ md._isNone = False
+ md._isNote = False
+ md._isNovel = True
+
# Counter Handling, Novel Titles
# ==============================
# This also checks that only numbered chapters bump the counter
@@ -1875,6 +2053,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
)
md.setTitleFormat(f"T: {nwHeadFmt.TITLE}")
md.setChapterFormat(f"C {nwHeadFmt.CH_NUM}: {nwHeadFmt.TITLE}")
+ md.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}")
md.setSceneFormat(
f"S {nwHeadFmt.CH_NUM}.{nwHeadFmt.SC_NUM} ({nwHeadFmt.SC_ABS}): {nwHeadFmt.TITLE}", False
)
@@ -1888,7 +2067,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
md.doConvert()
assert md.result == (
"# Novel One\n\n"
- "## Prologue\n\n"
+ "## U: Prologue\n\n"
"Text\n\n"
"## C 1: Chapter One\n\n"
"### S 1.1 (1): Scene One\n\n"
@@ -1901,7 +2080,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"### H 2.2 (4): Scene Four\n\n"
"Text\n\n"
"# Novel Two\n\n"
- "## Prologue\n\n"
+ "## U: Prologue\n\n"
"Text\n\n"
"## C 1: Chapter One\n\n"
"### S 1.1 (1): Scene One\n\n"
@@ -1915,11 +2094,11 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"Text\n\n"
)
-# END Test testCoreToken_HeaderCounterAndVisibility
+# END Test testCoreToken_CounterHandling
@pytest.mark.core
-def testCoreIndex_HeadingFormatter(fncPath, mockRnd):
+def testCoreToken_HeadingFormatter(fncPath, mockRnd):
"""Check the HeadingFormatter class."""
project = NWProject()
project.setProjectLang("en_GB")
@@ -2023,4 +2202,4 @@ def testCoreIndex_HeadingFormatter(fncPath, mockRnd):
cFormat = f"Chapter {nwHeadFmt.CH_NUM}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.CHAR_FOCUS}"
assert formatter.apply(cFormat, "Hi Bob", 0) == "Chapter 2, Scene 3 - Focus"
-# END Test testCoreIndex_HeadingFormatter
+# END Test testCoreToken_HeadingFormatter
diff --git a/tests/test_core/test_core_tomd.py b/tests/test_core/test_core_tomd.py
index b368ad68..c67caf63 100644
--- a/tests/test_core/test_core_tomd.py
+++ b/tests/test_core/test_core_tomd.py
@@ -213,14 +213,6 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
toMD.doConvert()
assert toMD.result == "# A Title\n\n"
- # Unnumbered
- toMD._tokens = [
- (toMD.T_UNNUM, 1, "Prologue", None, toMD.A_PBB),
- (toMD.T_EMPTY, 1, "", None, toMD.A_NONE),
- ]
- toMD.doConvert()
- assert toMD.result == "## Prologue\n\n"
-
# Separators
# ==========
@@ -238,7 +230,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
(toMD.T_EMPTY, 1, "", None, toMD.A_NONE),
]
toMD.doConvert()
- assert toMD.result == "\n\n\n"
+ assert toMD.result == "\n\n"
# END Test testCoreToMarkdown_ConvertDirect