Improve GUI text (#1727)

This commit is contained in:
Veronica Berglyd Olsen
2024-03-09 21:01:08 +01:00
committed by GitHub
17 changed files with 110 additions and 114 deletions
+8 -8
View File
@@ -307,7 +307,7 @@ class NWIndex:
nTitle = 0 # Line Number of the previous title nTitle = 0 # Line Number of the previous title
cTitle = TT_NONE # Tag of the current title cTitle = TT_NONE # Tag of the current title
pTitle = TT_NONE # Tag of the previous title pTitle = TT_NONE # Tag of the previous title
canSetHeader = True # First header has not yet been set canSetHead = True # First heading has not yet been set
lines = text.splitlines() lines = text.splitlines()
for n, line in enumerate(lines, start=1): for n, line in enumerate(lines, start=1):
@@ -320,9 +320,9 @@ class NWIndex:
if hDepth == "H0": if hDepth == "H0":
continue continue
if canSetHeader: if canSetHead:
nwItem.setMainHeading(hDepth) nwItem.setMainHeading(hDepth)
canSetHeader = False canSetHead = False
cTitle = self._itemIndex.addItemHeading(tHandle, n, hDepth, hText) cTitle = self._itemIndex.addItemHeading(tHandle, n, hDepth, hText)
if cTitle != TT_NONE: if cTitle != TT_NONE:
@@ -383,7 +383,7 @@ class NWIndex:
return return
def _splitHeading(self, line: str) -> tuple[str, str]: def _splitHeading(self, line: str) -> tuple[str, str]:
"""Split a heading into its header level and text value.""" """Split a heading into its heading level and text value."""
if line.startswith("# "): if line.startswith("# "):
return "H1", line[2:].strip() return "H1", line[2:].strip()
elif line.startswith("## "): elif line.startswith("## "):
@@ -515,8 +515,8 @@ class NWIndex:
"""Get the index data for a given item.""" """Get the index data for a given item."""
return self._itemIndex[tHandle] return self._itemIndex[tHandle]
def getItemHeader(self, tHandle: str, sTitle: str) -> IndexHeading | None: def getItemHeading(self, tHandle: str, sTitle: str) -> IndexHeading | None:
"""Get the header entry for a specific item and heading.""" """Get the heading entry for a specific item and heading."""
tItem = self._itemIndex[tHandle] tItem = self._itemIndex[tHandle]
if isinstance(tItem, IndexItem): if isinstance(tItem, IndexItem):
return tItem[sTitle] return tItem[sTitle]
@@ -825,7 +825,7 @@ class ItemIndex:
class around a single storage dictionary with a set of utility class around a single storage dictionary with a set of utility
functions for setting and accessing the index data. Each indexed functions for setting and accessing the index data. Each indexed
item is stored in an IndexItem object, which again holds an item is stored in an IndexItem object, which again holds an
IndexHeading object for each header of the text. IndexHeading object for each heading of the text.
""" """
__slots__ = ("_project", "_items") __slots__ = ("_project", "_items")
@@ -1200,7 +1200,7 @@ class IndexHeading:
## ##
def setLevel(self, level: str) -> None: def setLevel(self, level: str) -> None:
"""Set the level of the header if it's a valid value.""" """Set the level of the heading if it's a valid value."""
if level in nwHeaders.H_VALID: if level in nwHeaders.H_VALID:
self._level = level self._level = level
return return
+33 -33
View File
@@ -89,10 +89,10 @@ class Tokenizer(ABC):
T_KEYWORD = 5 # Command line T_KEYWORD = 5 # Command line
T_TITLE = 6 # Title T_TITLE = 6 # Title
T_UNNUM = 7 # Unnumbered T_UNNUM = 7 # Unnumbered
T_HEAD1 = 8 # Header 1 T_HEAD1 = 8 # Heading 1
T_HEAD2 = 9 # Header 2 T_HEAD2 = 9 # Heading 2
T_HEAD3 = 10 # Header 3 T_HEAD3 = 10 # Heading 3
T_HEAD4 = 11 # Header 4 T_HEAD4 = 11 # Heading 4
T_TEXT = 12 # Text line T_TEXT = 12 # Text line
T_SEP = 13 # Scene separator T_SEP = 13 # Scene separator
T_SKIP = 14 # Paragraph break T_SKIP = 14 # Paragraph break
@@ -158,10 +158,10 @@ class Tokenizer(ABC):
self._fmtScene = nwHeadFmt.TITLE # Formatting for scenes self._fmtScene = nwHeadFmt.TITLE # Formatting for scenes
self._fmtSection = nwHeadFmt.TITLE # Formatting for sections self._fmtSection = nwHeadFmt.TITLE # Formatting for sections
self._hideScene = False # Do not include scene headers self._hideScene = False # Do not include scene headings
self._hideSection = False # Do not include section headers self._hideSection = False # Do not include section headings
self._linkHeaders = False # Add an anchor before headers self._linkHeaders = False # Add an anchor before headings
self._titleStyle = self.A_CENTRE | self.A_PBB self._titleStyle = self.A_CENTRE | self.A_PBB
self._chapterStyle = self.A_PBB self._chapterStyle = self.A_PBB
@@ -309,22 +309,22 @@ class Tokenizer(ABC):
return return
def setHead1Margins(self, upper: float, lower: float) -> None: def setHead1Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 1 margin.""" """Set the upper and lower heading 1 margin."""
self._marginHead1 = (float(upper), float(lower)) self._marginHead1 = (float(upper), float(lower))
return return
def setHead2Margins(self, upper: float, lower: float) -> None: def setHead2Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 2 margin.""" """Set the upper and lower heading 2 margin."""
self._marginHead2 = (float(upper), float(lower)) self._marginHead2 = (float(upper), float(lower))
return return
def setHead3Margins(self, upper: float, lower: float) -> None: def setHead3Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 3 margin.""" """Set the upper and lower heading 3 margin."""
self._marginHead3 = (float(upper), float(lower)) self._marginHead3 = (float(upper), float(lower))
return return
def setHead4Margins(self, upper: float, lower: float) -> None: def setHead4Margins(self, upper: float, lower: float) -> None:
"""Set the upper and lower header 4 margin.""" """Set the upper and lower heading 4 margin."""
self._marginHead4 = (float(upper), float(lower)) self._marginHead4 = (float(upper), float(lower))
return return
@@ -338,8 +338,8 @@ class Tokenizer(ABC):
self._marginMeta = (float(upper), float(lower)) self._marginMeta = (float(upper), float(lower))
return return
def setLinkHeaders(self, state: bool) -> None: def setLinkHeadings(self, state: bool) -> None:
"""Enable or disable adding an anchor before headers.""" """Enable or disable adding an anchor before headings."""
self._linkHeaders = state self._linkHeaders = state
return return
@@ -442,7 +442,7 @@ class Tokenizer(ABC):
def tokenizeText(self) -> None: def tokenizeText(self) -> None:
"""Scan the text for either lines starting with specific """Scan the text for either lines starting with specific
characters that indicate headers, comments, commands etc, or characters that indicate headings, comments, commands etc, or
just contain plain text. In the case of plain text, apply the just contain plain text. In the case of plain text, apply the
same RegExes that the syntax highlighter uses and save the same RegExes that the syntax highlighter uses and save the
locations of these formatting tags into the token array. locations of these formatting tags into the token array.
@@ -450,7 +450,7 @@ class Tokenizer(ABC):
The format of the token list is an entry with a five-tuple for The format of the token list is an entry with a five-tuple for
each line in the file. The tuple is as follows: each line in the file. The tuple is as follows:
1: The type of the block, self.T_* 1: The type of the block, self.T_*
2: The header number under which the text is placed 2: The heading number under which the text is placed
3: The text content of the block, without leading tags 3: The text content of the block, without leading tags
4: The internal formatting map of the text, self.FMT_* 4: The internal formatting map of the text, self.FMT_*
5: The style of the block, self.A_* 5: The style of the block, self.A_*
@@ -557,9 +557,9 @@ class Tokenizer(ABC):
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:2] == "# ": elif aLine[:2] == "# ":
# Partition Headers # Partition Headings
# ================= # ==================
# Partition headers are only formatted in novel documents, and # Partition headings are only formatted in novel documents, and
# otherwise unchanged. Scene separators are disabled # otherwise unchanged. Scene separators are disabled
# immediately after partitions, and scene numbers are reset. # immediately after partitions, and scene numbers are reset.
@@ -579,12 +579,12 @@ class Tokenizer(ABC):
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:3] == "## ": elif aLine[:3] == "## ":
# Chapter Headers # Chapter Headings
# =============== # ================
# Chapter headers are only formatted in novel documents, and # Chapter headings are only formatted in novel documents, and
# otherwise unchanged. Chapter numbers are bumped before the # otherwise unchanged. Chapter numbers are bumped before the
# heading is formatted. Scene separators are disabled # heading is formatted. Scene separators are disabled
# immediately after chapter headers, and scene numbers are # immediately after chapter headings, and scene numbers are
# reset. # reset.
nHead += 1 nHead += 1
@@ -604,15 +604,15 @@ class Tokenizer(ABC):
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:4] == "### ": elif aLine[:4] == "### ":
# Scene Headers # Scene Headings
# ============= # ==============
# Scene headers in novel documents are treated as centred # Scene headings in novel documents are treated as centred
# separators if the formatting does not change the text. If the # separators if the formatting does not change the text. If the
# format is empty, the scene can be hidden or a blank paragraph # format is empty, the scene can be hidden or a blank paragraph
# (skip). When the scene title has static text or no text, it # (skip). When the scene title has static text or no text, it
# is always ignored if the noSep flag is set. This prevents # is always ignored if the noSep flag is set. This prevents
# separators immediately after other titles. Scene numbers are # separators immediately after other titles. Scene numbers are
# always incremented before formatting. For notes, the header # always incremented before formatting. For notes, the heading
# is unchanged. # is unchanged.
nHead += 1 nHead += 1
@@ -639,12 +639,12 @@ class Tokenizer(ABC):
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:5] == "#### ": elif aLine[:5] == "#### ":
# Section Headers # Section Headings
# =============== # =================
# Section headers in novel docs are treated as centred # Section headings in novel docs are treated as centred
# separators if the formatting does not change the text. If the # separators if the formatting does not change the text. If the
# format is empty, the section can be hidden or a blank # format is empty, the section can be hidden or a blank
# paragraph (skip). For notes, the header is unchanged. # paragraph (skip). For notes, the heading is unchanged.
nHead += 1 nHead += 1
tText = aLine[5:].strip() tText = aLine[5:].strip()
@@ -682,10 +682,10 @@ class Tokenizer(ABC):
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:4] == "##! ": elif aLine[:4] == "##! ":
# Unnumbered Chapter Header # Unnumbered Chapter Headings
# ========================= # ===========================
# Unnumbered chapters are only meaningful in Novel docs, so if # Unnumbered chapters are only meaningful in Novel docs, so if
# we're in a note, we convert them to a plain level 2 header. # we're in a note, we convert them to a plain level 2 heading.
nHead += 1 nHead += 1
tText = aLine[4:].strip() tText = aLine[4:].strip()
+6 -6
View File
@@ -57,7 +57,7 @@ class GuiDocSplit(QDialog):
self.setWindowTitle(self.tr("Split Document")) self.setWindowTitle(self.tr("Split Document"))
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers"))) self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headings")))
self.helpLabel = NColourLabel( self.helpLabel = NColourLabel(
self.tr("Select the maximum level to split into files."), self.tr("Select the maximum level to split into files."),
SHARED.theme.helpText, parent=self, wrap=True SHARED.theme.helpText, parent=self, wrap=True
@@ -74,17 +74,17 @@ class GuiDocSplit(QDialog):
intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True) intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True)
docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True) docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True)
# Header Selection # Heading Selection
self.listBox = QListWidget() self.listBox = QListWidget()
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop) self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
self.listBox.setMinimumWidth(CONFIG.pxInt(400)) self.listBox.setMinimumWidth(CONFIG.pxInt(400))
self.listBox.setMinimumHeight(CONFIG.pxInt(180)) self.listBox.setMinimumHeight(CONFIG.pxInt(180))
self.splitLevel = QComboBox(self) self.splitLevel = QComboBox(self)
self.splitLevel.addItem(self.tr("Split on Header Level 1 (Title)"), 1) self.splitLevel.addItem(self.tr("Split on Heading Level 1 (Partition)"), 1)
self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2) self.splitLevel.addItem(self.tr("Split up to Heading Level 2 (Chapter)"), 2)
self.splitLevel.addItem(self.tr("Split up to Header Level 3 (Scene)"), 3) self.splitLevel.addItem(self.tr("Split up to Heading Level 3 (Scene)"), 3)
self.splitLevel.addItem(self.tr("Split up to Header Level 4 (Section)"), 4) self.splitLevel.addItem(self.tr("Split up to Heading Level 4 (Section)"), 4)
spIndex = self.splitLevel.findData(spLevel) spIndex = self.splitLevel.findData(spLevel)
if spIndex != -1: if spIndex != -1:
self.splitLevel.setCurrentIndex(spIndex) self.splitLevel.setCurrentIndex(spIndex)
+14 -14
View File
@@ -90,10 +90,10 @@ class GuiDocHighlighter(QSyntaxHighlighter):
"header2": self._makeFormat(SHARED.theme.colHead, "bold", 1.6), "header2": self._makeFormat(SHARED.theme.colHead, "bold", 1.6),
"header3": self._makeFormat(SHARED.theme.colHead, "bold", 1.4), "header3": self._makeFormat(SHARED.theme.colHead, "bold", 1.4),
"header4": self._makeFormat(SHARED.theme.colHead, "bold", 1.2), "header4": self._makeFormat(SHARED.theme.colHead, "bold", 1.2),
"header1h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.8), "head1h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.8),
"header2h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.6), "head2h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.6),
"header3h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.4), "head3h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.4),
"header4h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.2), "head4h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.2),
"bold": self._makeFormat(colEmph, "bold"), "bold": self._makeFormat(colEmph, "bold"),
"italic": self._makeFormat(colEmph, "italic"), "italic": self._makeFormat(colEmph, "italic"),
"strike": self._makeFormat(SHARED.theme.colHidden, "strike"), "strike": self._makeFormat(SHARED.theme.colHidden, "strike"),
@@ -303,28 +303,28 @@ class GuiDocHighlighter(QSyntaxHighlighter):
elif text.startswith(("# ", "#! ", "## ", "##! ", "### ", "#### ")): elif text.startswith(("# ", "#! ", "## ", "##! ", "### ", "#### ")):
self.setCurrentBlockState(self.BLOCK_TITLE) self.setCurrentBlockState(self.BLOCK_TITLE)
if text.startswith("# "): # Header 1 if text.startswith("# "): # Heading 1
self.setFormat(0, 1, self._hStyles["header1h"]) self.setFormat(0, 1, self._hStyles["head1h"])
self.setFormat(1, len(text), self._hStyles["header1"]) self.setFormat(1, len(text), self._hStyles["header1"])
elif text.startswith("## "): # Header 2 elif text.startswith("## "): # Heading 2
self.setFormat(0, 2, self._hStyles["header2h"]) self.setFormat(0, 2, self._hStyles["head2h"])
self.setFormat(2, len(text), self._hStyles["header2"]) self.setFormat(2, len(text), self._hStyles["header2"])
elif text.startswith("### "): # Header 3 elif text.startswith("### "): # Heading 3
self.setFormat(0, 3, self._hStyles["header3h"]) self.setFormat(0, 3, self._hStyles["head3h"])
self.setFormat(3, len(text), self._hStyles["header3"]) self.setFormat(3, len(text), self._hStyles["header3"])
elif text.startswith("#### "): # Header 4 elif text.startswith("#### "): # Heading 4
self.setFormat(0, 4, self._hStyles["header4h"]) self.setFormat(0, 4, self._hStyles["head4h"])
self.setFormat(4, len(text), self._hStyles["header4"]) self.setFormat(4, len(text), self._hStyles["header4"])
elif text.startswith("#! "): # Title elif text.startswith("#! "): # Title
self.setFormat(0, 2, self._hStyles["header1h"]) self.setFormat(0, 2, self._hStyles["head1h"])
self.setFormat(2, len(text), self._hStyles["header1"]) self.setFormat(2, len(text), self._hStyles["header1"])
elif text.startswith("##! "): # Unnumbered elif text.startswith("##! "): # Unnumbered
self.setFormat(0, 3, self._hStyles["header2h"]) self.setFormat(0, 3, self._hStyles["head2h"])
self.setFormat(3, len(text), self._hStyles["header2"]) self.setFormat(3, len(text), self._hStyles["header2"])
elif text.startswith("%"): # Comments elif text.startswith("%"): # Comments
+1 -1
View File
@@ -202,7 +202,7 @@ class GuiDocViewer(QTextBrowser):
sPos = self.verticalScrollBar().value() sPos = self.verticalScrollBar().value()
aDoc = ToHtml(SHARED.project) aDoc = ToHtml(SHARED.project)
aDoc.setPreview(CONFIG.viewComments, CONFIG.viewSynopsis) aDoc.setPreview(CONFIG.viewComments, CONFIG.viewSynopsis)
aDoc.setLinkHeaders(True) aDoc.setLinkHeadings(True)
# Be extra careful here to prevent crashes when first opening a # Be extra careful here to prevent crashes when first opening a
# project as a crash here leaves no way of recovering. # project as a crash here leaves no way of recovering.
+12 -12
View File
@@ -693,29 +693,29 @@ class GuiMainMenu(QMenuBar):
# Format > Separator # Format > Separator
self.fmtMenu.addSeparator() self.fmtMenu.addSeparator()
# Format > Header 1 (Partition) # Format > Heading 1 (Partition)
self.aFmtHead1 = self.fmtMenu.addAction(self.tr("Header 1 (Partition)")) self.aFmtHead1 = self.fmtMenu.addAction(self.tr("Heading 1 (Partition)"))
self.aFmtHead1.setShortcut("Ctrl+1") self.aFmtHead1.setShortcut("Ctrl+1")
self.aFmtHead1.triggered.connect( self.aFmtHead1.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H1) lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H1)
) )
# Format > Header 2 (Chapter) # Format > Heading 2 (Chapter)
self.aFmtHead2 = self.fmtMenu.addAction(self.tr("Header 2 (Chapter)")) self.aFmtHead2 = self.fmtMenu.addAction(self.tr("Heading 2 (Chapter)"))
self.aFmtHead2.setShortcut("Ctrl+2") self.aFmtHead2.setShortcut("Ctrl+2")
self.aFmtHead2.triggered.connect( self.aFmtHead2.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H2) lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H2)
) )
# Format > Header 3 (Scene) # Format > Heading 3 (Scene)
self.aFmtHead3 = self.fmtMenu.addAction(self.tr("Header 3 (Scene)")) self.aFmtHead3 = self.fmtMenu.addAction(self.tr("Heading 3 (Scene)"))
self.aFmtHead3.setShortcut("Ctrl+3") self.aFmtHead3.setShortcut("Ctrl+3")
self.aFmtHead3.triggered.connect( self.aFmtHead3.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H3) lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H3)
) )
# Format > Header 4 (Section) # Format > Heading 4 (Section)
self.aFmtHead4 = self.fmtMenu.addAction(self.tr("Header 4 (Section)")) self.aFmtHead4 = self.fmtMenu.addAction(self.tr("Heading 4 (Section)"))
self.aFmtHead4.setShortcut("Ctrl+4") self.aFmtHead4.setShortcut("Ctrl+4")
self.aFmtHead4.triggered.connect( self.aFmtHead4.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H4) lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H4)
@@ -804,14 +804,14 @@ class GuiMainMenu(QMenuBar):
# Format > Separator # Format > Separator
self.fmtMenu.addSeparator() self.fmtMenu.addSeparator()
# Format > Replace Single Quotes # Format > Replace Straight Single Quotes
self.aFmtReplSng = self.fmtMenu.addAction(self.tr("Convert Single Quotes")) self.aFmtReplSng = self.fmtMenu.addAction(self.tr("Replace Straight Single Quotes"))
self.aFmtReplSng.triggered.connect( self.aFmtReplSng.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.REPL_SNG) lambda: self.requestDocAction.emit(nwDocAction.REPL_SNG)
) )
# Format > Replace Double Quotes # Format > Replace Straight Double Quotes
self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("Convert Double Quotes")) self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("Replace Straight Double Quotes"))
self.aFmtReplDbl.triggered.connect( self.aFmtReplDbl.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.REPL_DBL) lambda: self.requestDocAction.emit(nwDocAction.REPL_DBL)
) )
+1 -1
View File
@@ -750,7 +750,7 @@ class GuiNovelTree(QTreeWidget):
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle) logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
pIndex = SHARED.project.index pIndex = SHARED.project.index
novIdx = pIndex.getItemHeader(tHandle, sTitle) novIdx = pIndex.getItemHeading(tHandle, sTitle)
refTags = pIndex.getReferences(tHandle, sTitle) refTags = pIndex.getReferences(tHandle, sTitle)
if not novIdx: if not novIdx:
return return
+1 -1
View File
@@ -1020,7 +1020,7 @@ class GuiOutlineDetails(QScrollArea):
""" """
pIndex = SHARED.project.index pIndex = SHARED.project.index
nwItem = SHARED.project.tree[tHandle] nwItem = SHARED.project.tree[tHandle]
novIdx = pIndex.getItemHeader(tHandle, sTitle) novIdx = pIndex.getItemHeading(tHandle, sTitle)
novRefs = pIndex.getReferences(tHandle, sTitle) novRefs = pIndex.getReferences(tHandle, sTitle)
if nwItem and novIdx: if nwItem and novIdx:
self.titleLabel.setText("<b>%s</b>" % self.tr(self.LVL_MAP.get(novIdx.level, "H1"))) self.titleLabel.setText("<b>%s</b>" % self.tr(self.LVL_MAP.get(novIdx.level, "H1")))
+5 -7
View File
@@ -1834,7 +1834,7 @@ class _TreeContextMenu(QMenu):
def _itemHeader(self) -> None: def _itemHeader(self) -> None:
"""Check if there is a header that can be used for rename.""" """Check if there is a header that can be used for rename."""
if hItem := SHARED.project.index.getItemHeader(self._handle, "T0001"): if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
action = self.addAction(self.tr("Rename to Heading")) action = self.addAction(self.tr("Rename to Heading"))
action.triggered.connect( action.triggered.connect(
lambda: self.projTree.renameTreeItem(self._handle, hItem.title) lambda: self.projTree.renameTreeItem(self._handle, hItem.title)
@@ -1933,7 +1933,7 @@ class _TreeContextMenu(QMenu):
action.triggered.connect(lambda: tree._mergeDocuments(tHandle, True)) action.triggered.connect(lambda: tree._mergeDocuments(tHandle, True))
if isFile: if isFile:
action = menu.addAction(self.tr("Split Document by Headers")) action = menu.addAction(self.tr("Split Document by Headings"))
action.triggered.connect(lambda: tree._splitDocument(tHandle)) action.triggered.connect(lambda: tree._splitDocument(tHandle))
return return
@@ -1947,11 +1947,9 @@ class _TreeContextMenu(QMenu):
action.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, True)) action.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, True))
action = self.addAction(self.tr("Collapse All")) action = self.addAction(self.tr("Collapse All"))
action.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, False)) action.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, False))
action = self.addAction(self.tr("Duplicate from Here"))
action.triggered.connect(lambda: tree._duplicateFromHandle(tHandle)) action = self.addAction(self.tr("Duplicate"))
elif isFile: action.triggered.connect(lambda: tree._duplicateFromHandle(tHandle))
action = self.addAction(self.tr("Duplicate Document"))
action.triggered.connect(lambda: tree._duplicateFromHandle(tHandle))
if self._item.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild): if self._item.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild):
action = self.addAction(self.tr("Delete Permanently")) action = self.addAction(self.tr("Delete Permanently"))
+3 -3
View File
@@ -642,7 +642,7 @@ class GuiIcons:
def getItemIcon(self, tType: nwItemType, tClass: nwItemClass, def getItemIcon(self, tType: nwItemType, tClass: nwItemClass,
tLayout: nwItemLayout, hLevel: str = "H0") -> QIcon: tLayout: nwItemLayout, hLevel: str = "H0") -> QIcon:
"""Get the correct icon for a project item based on type, class """Get the correct icon for a project item based on type, class
and header level and heading level
""" """
iconName = None iconName = None
if tType == nwItemType.ROOT: if tType == nwItemType.ROOT:
@@ -668,7 +668,7 @@ class GuiIcons:
return self.getIcon(iconName) return self.getIcon(iconName)
def getHeaderDecoration(self, hLevel: int) -> QPixmap: def getHeaderDecoration(self, hLevel: int) -> QPixmap:
"""Get the decoration for a specific header level.""" """Get the decoration for a specific heading level."""
if not self._headerDec: if not self._headerDec:
iPx = self.mainTheme.baseIconSize iPx = self.mainTheme.baseIconSize
self._headerDec = [ self._headerDec = [
@@ -681,7 +681,7 @@ class GuiIcons:
return self._headerDec[minmax(hLevel, 0, 4)] return self._headerDec[minmax(hLevel, 0, 4)]
def getHeaderDecorationNarrow(self, hLevel: int) -> QPixmap: def getHeaderDecorationNarrow(self, hLevel: int) -> QPixmap:
"""Get the narrow decoration for a specific header level.""" """Get the narrow decoration for a specific heading level."""
if not self._headerDecNarrow: if not self._headerDecNarrow:
iPx = self.mainTheme.baseIconSize iPx = self.mainTheme.baseIconSize
self._headerDecNarrow = [ self._headerDecNarrow = [
+2 -2
View File
@@ -736,7 +736,7 @@ class GuiMain(QMainWindow):
return False return False
if tHandle is not None and sTitle is not None: if tHandle is not None and sTitle is not None:
hItem = SHARED.project.index.getItemHeader(tHandle, sTitle) hItem = SHARED.project.index.getItemHeading(tHandle, sTitle)
if hItem is not None: if hItem is not None:
tLine = hItem.line tLine = hItem.line
@@ -1144,7 +1144,7 @@ class GuiMain(QMainWindow):
if tHandle is not None: if tHandle is not None:
if mode == nwDocMode.EDIT: if mode == nwDocMode.EDIT:
tLine = None tLine = None
hItem = SHARED.project.index.getItemHeader(tHandle, sTitle) hItem = SHARED.project.index.getItemHeading(tHandle, sTitle)
if hItem is not None: if hItem is not None:
tLine = hItem.line tLine = hItem.line
self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus) self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus)
+1 -1
View File
@@ -72,7 +72,7 @@ def preProcessText(text: str, keepHeaders: bool = True) -> list[str]:
def standardCounter(text: str) -> tuple[int, int, int]: def standardCounter(text: str) -> tuple[int, int, int]:
"""A counter that counts paragraphs, words and characters. """A counter that counts paragraphs, words and characters.
This is the standard counter that includes headers in the word and This is the standard counter that includes headings in the word and
character counts. character counts.
""" """
cCount = 0 cCount = 0
+4 -4
View File
@@ -1011,10 +1011,10 @@ class _StatsWidget(QWidget):
self.leftForm = QFormLayout() self.leftForm = QFormLayout()
self.leftForm.addRow(self.tr("Words"), self.maxTotalWords) self.leftForm.addRow(self.tr("Words"), self.maxTotalWords)
self.leftForm.addRow(self.tr("Header Words"), self.maxHeaderWords) self.leftForm.addRow(self.tr("Heading Words"), self.maxHeaderWords)
self.leftForm.addRow(self.tr("Body Text Words"), self.maxTextWords) self.leftForm.addRow(self.tr("Body Text Words"), self.maxTextWords)
self.leftForm.addRow("", QLabel(self)) self.leftForm.addRow("", QLabel(self))
self.leftForm.addRow(self.tr("Headers"), self.maxTitleCount) self.leftForm.addRow(self.tr("Headings"), self.maxTitleCount)
self.leftForm.addRow(self.tr("Paragraphs"), self.maxParCount) self.leftForm.addRow(self.tr("Paragraphs"), self.maxParCount)
self.leftForm.setHorizontalSpacing(hPx) self.leftForm.setHorizontalSpacing(hPx)
self.leftForm.setVerticalSpacing(vPx) self.leftForm.setVerticalSpacing(vPx)
@@ -1038,10 +1038,10 @@ class _StatsWidget(QWidget):
self.rightForm = QFormLayout() self.rightForm = QFormLayout()
self.rightForm.addRow(self.tr("Characters"), self.maxTotalChars) self.rightForm.addRow(self.tr("Characters"), self.maxTotalChars)
self.rightForm.addRow(self.tr("Header Characters"), self.maxHeaderChars) self.rightForm.addRow(self.tr("Heading Characters"), self.maxHeaderChars)
self.rightForm.addRow(self.tr("Body Text Characters"), self.maxTextChars) self.rightForm.addRow(self.tr("Body Text Characters"), self.maxTextChars)
self.rightForm.addRow(self.tr("Characters, No Spaces"), self.maxTotalWordChars) self.rightForm.addRow(self.tr("Characters, No Spaces"), self.maxTotalWordChars)
self.rightForm.addRow(self.tr("Header Characters, No Spaces"), self.maxHeaderWordChars) self.rightForm.addRow(self.tr("Heading Characters, No Spaces"), self.maxHeaderWordChars)
self.rightForm.addRow(self.tr("Body Text Characters, No Spaces"), self.maxTextWordChars) self.rightForm.addRow(self.tr("Body Text Characters, No Spaces"), self.maxTextWordChars)
self.rightForm.setHorizontalSpacing(hPx) self.rightForm.setHorizontalSpacing(hPx)
self.rightForm.setVerticalSpacing(vPx) self.rightForm.setVerticalSpacing(vPx)
+7 -7
View File
@@ -252,7 +252,7 @@ def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd):
assert index._tagsIndex.tagHandle("Jane") == cHandle assert index._tagsIndex.tagHandle("Jane") == cHandle
assert index._tagsIndex.tagHeading("Jane") == "T0001" assert index._tagsIndex.tagHeading("Jane") == "T0001"
assert index._tagsIndex.tagClass("Jane") == "CHARACTER" assert index._tagsIndex.tagClass("Jane") == "CHARACTER"
assert index.getItemHeader(nHandle, "T0001").title == "Hello World!" # type: ignore assert index.getItemHeading(nHandle, "T0001").title == "Hello World!" # type: ignore
assert index.getReferences(nHandle, "T0001") == { assert index.getReferences(nHandle, "T0001") == {
"@char": [], "@char": [],
"@custom": [], "@custom": [],
@@ -382,7 +382,7 @@ def testCoreIndex_ScanText(mockGUI, fncPath, mockRnd):
assert index._tagsIndex.tagHandle("Jane") == cHandle assert index._tagsIndex.tagHandle("Jane") == cHandle
assert index._tagsIndex.tagHeading("Jane") == "T0001" assert index._tagsIndex.tagHeading("Jane") == "T0001"
assert index._tagsIndex.tagClass("Jane") == "CHARACTER" assert index._tagsIndex.tagClass("Jane") == "CHARACTER"
assert index.getItemHeader(nHandle, "T0001").title == "Hello World!" # type: ignore assert index.getItemHeading(nHandle, "T0001").title == "Hello World!" # type: ignore
# Title Indexing # Title Indexing
# ============== # ==============
@@ -560,8 +560,8 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
assert isinstance(cHandle, str) assert isinstance(cHandle, str)
assert isinstance(dHandle, str) assert isinstance(dHandle, str)
assert index.getItemHeader("", "") is None assert index.getItemHeading("", "") is None
assert index.getItemHeader(C.hNovelRoot, "") is None assert index.getItemHeading(C.hNovelRoot, "") is None
assert index.scanText(cHandle, ( assert index.scanText(cHandle, (
"# Jane Smith\n" "# Jane Smith\n"
@@ -685,11 +685,11 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
assert list(index.getTagsData()) == [( assert list(index.getTagsData()) == [(
"jane", "Jane", "CHARACTER", "jane", "Jane", "CHARACTER",
index.getItemData(cHandle), index.getItemData(cHandle),
index.getItemHeader(cHandle, "T0001") index.getItemHeading(cHandle, "T0001")
), ( ), (
"john", "John", "CHARACTER", "john", "John", "CHARACTER",
index.getItemData(dHandle), index.getItemData(dHandle),
index.getItemHeader(dHandle, "T0001") index.getItemHeading(dHandle, "T0001")
)] )]
# getSingleTag # getSingleTag
@@ -697,7 +697,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
assert index.getSingleTag("jane") == ( assert index.getSingleTag("jane") == (
"Jane", "CHARACTER", "Jane", "CHARACTER",
index.getItemData(cHandle), index.getItemData(cHandle),
index.getItemHeader(cHandle, "T0001") index.getItemHeading(cHandle, "T0001")
) )
assert index.getSingleTag("foobar") == ("", "", None, None) assert index.getSingleTag("foobar") == ("", "", None, None)
+3 -3
View File
@@ -89,7 +89,7 @@ def testCoreToHtml_ConvertHeaders(mockGUI):
html._isNovel = False html._isNovel = False
html._isNote = True html._isNote = True
html._isFirst = True html._isFirst = True
html.setLinkHeaders(True) html.setLinkHeadings(True)
# Header 1 # Header 1
html._text = "# Heading One\n" html._text = "# Heading One\n"
@@ -270,7 +270,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
html._isNovel = True html._isNovel = True
html._isNote = False html._isNote = False
html.setLinkHeaders(True) html.setLinkHeadings(True)
# Special Titles # Special Titles
# ============== # ==============
@@ -319,7 +319,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
# Alignment # Alignment
# ========= # =========
html.setLinkHeaders(False) html.setLinkHeadings(False)
# Align Left # Align Left
html.setStyles(False) html.setStyles(False)
+1 -1
View File
@@ -86,7 +86,7 @@ def testCoreToken_Setters(mockGUI):
tokens.setHead4Margins(2.0, 2.0) tokens.setHead4Margins(2.0, 2.0)
tokens.setTextMargins(2.0, 2.0) tokens.setTextMargins(2.0, 2.0)
tokens.setMetaMargins(2.0, 2.0) tokens.setMetaMargins(2.0, 2.0)
tokens.setLinkHeaders(True) tokens.setLinkHeadings(True)
tokens.setBodyText(False) tokens.setBodyText(False)
tokens.setSynopsis(True) tokens.setSynopsis(True)
tokens.setComments(True) tokens.setComments(True)
+8 -10
View File
@@ -1196,7 +1196,7 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
actions = [x.text() for x in ctxMenu.actions() if x.text()] actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [ assert actions == [
"Create New ...", "Rename", "Set Status to ...", "Expand All", "Create New ...", "Rename", "Set Status to ...", "Expand All",
"Collapse All", "Duplicate from Here", "Delete Permanently", "Collapse All", "Duplicate", "Delete Permanently",
] ]
# Context Menu on Folder Item # Context Menu on Folder Item
@@ -1207,7 +1207,7 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
actions = [x.text() for x in ctxMenu.actions() if x.text()] actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [ assert actions == [
"Create New ...", "Rename", "Set Status to ...", "Transform ...", "Expand All", "Create New ...", "Rename", "Set Status to ...", "Transform ...", "Expand All",
"Collapse All", "Duplicate from Here", "Move to Trash", "Collapse All", "Duplicate", "Move to Trash",
] ]
def getTransformSubMenu(menu: QMenu) -> list[str]: def getTransformSubMenu(menu: QMenu) -> list[str]:
@@ -1225,11 +1225,11 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
assert actions == [ assert actions == [
"Open Document", "View Document", "Create New ...", "Rename", "Rename to Heading", "Open Document", "View Document", "Create New ...", "Rename", "Rename to Heading",
"Toggle Active", "Set Status to ...", "Transform ...", "Expand All", "Collapse All", "Toggle Active", "Set Status to ...", "Transform ...", "Expand All", "Collapse All",
"Duplicate from Here", "Move to Trash", "Duplicate", "Move to Trash",
] ]
assert getTransformSubMenu(ctxMenu) == [ assert getTransformSubMenu(ctxMenu) == [
"Convert to Project Note", "Merge Child Items into Self", "Convert to Project Note", "Merge Child Items into Self",
"Merge Child Items into New", "Split Document by Headers" "Merge Child Items into New", "Split Document by Headings"
] ]
# Context Menu on Note File Item in Character Folder # Context Menu on Note File Item in Character Folder
@@ -1240,11 +1240,10 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
actions = [x.text() for x in ctxMenu.actions() if x.text()] actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [ assert actions == [
"Open Document", "View Document", "Create New ...", "Rename", "Rename to Heading", "Open Document", "View Document", "Create New ...", "Rename", "Rename to Heading",
"Toggle Active", "Set Importance to ...", "Transform ...", "Duplicate Document", "Toggle Active", "Set Importance to ...", "Transform ...", "Duplicate", "Move to Trash",
"Move to Trash",
] ]
assert getTransformSubMenu(ctxMenu) == [ assert getTransformSubMenu(ctxMenu) == [
"Split Document by Headers", "Split Document by Headings",
] ]
# Context Menu on Note File Item in Novel Tree # Context Menu on Note File Item in Novel Tree
@@ -1255,11 +1254,10 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
actions = [x.text() for x in ctxMenu.actions() if x.text()] actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [ assert actions == [
"Open Document", "View Document", "Create New ...", "Rename", "Rename to Heading", "Open Document", "View Document", "Create New ...", "Rename", "Rename to Heading",
"Toggle Active", "Set Status to ...", "Transform ...", "Duplicate Document", "Toggle Active", "Set Status to ...", "Transform ...", "Duplicate", "Move to Trash",
"Move to Trash",
] ]
assert getTransformSubMenu(ctxMenu) == [ assert getTransformSubMenu(ctxMenu) == [
"Convert to Novel Document", "Split Document by Headers", "Convert to Novel Document", "Split Document by Headings",
] ]
# Context Menu on Multiple Items, Clicked on Document # Context Menu on Multiple Items, Clicked on Document