Allow inserting characters in manuscript headers (#1677)
This commit is contained in:
@@ -174,6 +174,7 @@ A syntax theme ``.conf`` file consists of the following settings:
|
||||
shortcode = 0, 0, 0
|
||||
keyword = 0, 0, 0
|
||||
value = 0, 0, 0
|
||||
optional = 0, 0, 0
|
||||
spellcheckline = 0, 0, 0
|
||||
errorline = 0, 0, 0
|
||||
replacetag = 0, 0, 0
|
||||
@@ -186,3 +187,7 @@ to black, except ``background`` which defaults to white,
|
||||
.. versionadded:: 2.2
|
||||
The `shortcode` syntax colour entry was added, so you need to update your custom themes if you
|
||||
made any before version 2.2.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
The `optional` syntax colour entry was added, so you need to update your custom themes if you
|
||||
made any before version 2.3.
|
||||
|
||||
@@ -47,16 +47,28 @@ How to Use Tags
|
||||
===============
|
||||
|
||||
A "tag" in novelWriter is a word or phrase that you define as belonging to a heading. Tags are set
|
||||
by using the ``@tags`` :term:`keyword`. The full format of a tag is ``@tag: tagname``, where
|
||||
``tagname`` is an identifier of your choosing. You can only set *one* tag per heading, and the tag
|
||||
has to be unique across all documents in the project.
|
||||
by using the ``@tags`` :term:`keyword`.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
Tags are now case insensitive.
|
||||
The general format of a tag is ``@tag: tagName``.
|
||||
|
||||
After the tags have been defined, they can then be referenced in the novel documents, or
|
||||
cross-referenced in other notes. they will also show up in the :guilabel:`Outline View` and in the
|
||||
back-reference panel when a document is opened in the viewer.
|
||||
The full format of a tag is ``@tag: tagName | displayName``.
|
||||
|
||||
``tagName`` (Required)
|
||||
This is a unique identifier of your choosing. It is the value you use later for making
|
||||
references back to this document, or section of the document.
|
||||
|
||||
``displayName`` (Optional)
|
||||
This is an optional display name used for the tag. When you build your manuscript, you can for
|
||||
instance insert the point of view character name into chapter headings. By default, the
|
||||
``tagName`` value is used in headings, but if you use a shortened format internally in your
|
||||
project, you can use this to specify a more suitable format for your manuscript.
|
||||
|
||||
You can only set *one* tag per heading, and the tag has to be unique across *all* documents in the
|
||||
project.
|
||||
|
||||
After a tag has been defined, it can be referenced in novel documents, or cross-referenced in other
|
||||
notes. Tags will also show up in the :guilabel:`Outline View` and in the back-reference panel when
|
||||
a document is opened in the viewer.
|
||||
|
||||
The syntax highlighter will indicate to you that the keyword is correctly used and that the tag is
|
||||
allowed, that is, the tag is unique. Duplicate tags should be detected as long as the index is up
|
||||
@@ -68,22 +80,30 @@ there for you to use in whatever way you wish. Of course, the content of the doc
|
||||
to the manuscript, or an outline document. If you want to compile a single document of all your
|
||||
notes, you can do this from the :guilabel:`Manuscript Build` tool.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
Tags are no longer case sensitive. The tags are by default displayed with the capitalisation you
|
||||
use when defining the tag, but you don't have to use the same capitalisation when referencing
|
||||
it later.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
Tags can have an optional display name for manuscript builds.
|
||||
|
||||
Example of a heading with a tag for a character of the story:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# Jane Doe
|
||||
# Character: Jane Doe
|
||||
|
||||
@tag: Jane
|
||||
@tag: Jane | Jane Doe
|
||||
|
||||
Some information about the character Jane Doe.
|
||||
|
||||
When this is done in a document in a :term:`Root Folder` of type "Characters", the tag is
|
||||
automatically treated as an available character in your project, and you will be able to reference
|
||||
it in any of your other documents using the reference keywords for characters. It will also show up
|
||||
in the Character tab in the Reference panel below the document viewer, and in the reference
|
||||
auto-completer menu in the editor when you fill in references. See :ref:`a_ui_view` and
|
||||
:ref:`a_references_completer`.
|
||||
automatically treated as an available character in your project with the value "Jane", and you will
|
||||
be able to reference it in any of your other documents using the reference keywords for characters.
|
||||
It will also show up in the Character tab in the Reference panel below the document viewer, and in
|
||||
the reference auto-completer menu in the editor when you fill in references. See :ref:`a_ui_view`
|
||||
and :ref:`a_references_completer`.
|
||||
|
||||
It is the root folder type that defines what category of story elements the tag is indexed under.
|
||||
See the :ref:`a_proj_roots` section for an overview of available root folder types. They are also
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 150, 150, 150
|
||||
shortcode = 0, 155, 200
|
||||
keyword = 200, 46, 0
|
||||
value = 184, 200, 0
|
||||
optional = 0, 155, 200
|
||||
spellcheckline = 200, 46, 0
|
||||
errorline = 46, 200, 0
|
||||
replacetag = 0, 184, 46
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 100, 100, 100
|
||||
shortcode = 0, 100, 0
|
||||
keyword = 200, 50, 50
|
||||
value = 50, 150, 50
|
||||
optional = 0, 100, 0
|
||||
spellcheckline = 200, 0, 0
|
||||
errorline = 0, 150, 0
|
||||
replacetag = 0, 150, 0
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 150, 150, 150
|
||||
shortcode = 225, 225, 225
|
||||
keyword = 225, 225, 225
|
||||
value = 200, 200, 200
|
||||
optional = 225, 225, 225
|
||||
spellcheckline = 200, 46, 0
|
||||
errorline = 46, 200, 0
|
||||
replacetag = 225, 225, 225
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 100, 100, 100
|
||||
shortcode = 0, 0, 0
|
||||
keyword = 0, 0, 0
|
||||
value = 20, 20, 20
|
||||
optional = 0, 0, 0
|
||||
spellcheckline = 200, 0, 0
|
||||
errorline = 0, 150, 0
|
||||
replacetag = 0, 0, 0
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 152, 159, 177
|
||||
shortcode = 40, 142, 215
|
||||
keyword = 222, 61, 58
|
||||
value = 150, 74, 193
|
||||
optional = 40, 142, 215
|
||||
spellcheckline = 222, 61, 58
|
||||
errorline = 8, 145, 106
|
||||
replacetag = 42, 162, 152
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 99, 119, 119
|
||||
shortcode = 130, 170, 255
|
||||
keyword = 247, 140, 108
|
||||
value = 199, 146, 234
|
||||
optional = 130, 170, 255
|
||||
spellcheckline = 247, 140, 108
|
||||
errorline = 173, 219, 103
|
||||
replacetag = 127, 219, 202
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 147, 161, 161
|
||||
shortcode = 147, 161, 161
|
||||
keyword = 133, 153, 0
|
||||
value = 203, 75, 22
|
||||
optional = 147, 161, 161
|
||||
spellcheckline = 203, 75, 22
|
||||
errorline = 220, 50, 47
|
||||
replacetag = 133, 153, 0
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 88, 110, 117
|
||||
shortcode = 88, 110, 117
|
||||
keyword = 133, 153, 0
|
||||
value = 203, 75, 22
|
||||
optional = 88, 110, 117
|
||||
spellcheckline = 203, 75, 22
|
||||
errorline = 220, 50, 47
|
||||
replacetag = 133, 153, 0
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 142, 144, 140
|
||||
shortcode = 66, 113, 174
|
||||
keyword = 240, 40, 41
|
||||
value = 137, 89, 168
|
||||
optional = 66, 113, 174
|
||||
spellcheckline = 240, 40, 41
|
||||
errorline = 113, 140, 0
|
||||
replacetag = 62, 153, 159
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 150, 152, 150
|
||||
shortcode = 129, 162, 190
|
||||
keyword = 204, 102, 102
|
||||
value = 178, 148, 187
|
||||
optional = 129, 162, 190
|
||||
spellcheckline = 204, 102, 102
|
||||
errorline = 181, 189, 104
|
||||
replacetag = 138, 190, 183
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 114, 133, 183
|
||||
shortcode = 187, 218, 255
|
||||
keyword = 255, 157, 164
|
||||
value = 235, 187, 255
|
||||
optional = 187, 218, 255
|
||||
spellcheckline = 255, 157, 164
|
||||
errorline = 209, 241, 169
|
||||
replacetag = 153, 255, 255
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 150, 152, 150
|
||||
shortcode = 122, 166, 218
|
||||
keyword = 213, 78, 83
|
||||
value = 195, 151, 216
|
||||
optional = 122, 166, 218
|
||||
spellcheckline = 213, 78, 83
|
||||
errorline = 185, 202, 74
|
||||
replacetag = 112, 192, 177
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 153, 153, 153
|
||||
shortcode = 102, 153, 204
|
||||
keyword = 242, 119, 122
|
||||
value = 204, 153, 204
|
||||
optional = 102, 153, 204
|
||||
spellcheckline = 242, 119, 122
|
||||
errorline = 153, 204, 153
|
||||
replacetag = 102, 204, 204
|
||||
|
||||
+13
-20
@@ -29,7 +29,7 @@ import logging
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from typing import Any, Literal
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from configparser import ConfigParser
|
||||
@@ -43,6 +43,9 @@ from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.constants import nwConst, nwUnicode
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import TypeGuard # Requires Python 3.10
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -104,15 +107,6 @@ def checkBool(value: Any, default: bool) -> bool:
|
||||
return default
|
||||
|
||||
|
||||
def checkHandle(value, default, allowNone=False):
|
||||
"""Check if a value is a handle."""
|
||||
if allowNone and (value is None or value == "None"):
|
||||
return None
|
||||
if isHandle(value):
|
||||
return str(value)
|
||||
return default
|
||||
|
||||
|
||||
def checkUuid(value: Any, default: str) -> str:
|
||||
"""Try to process a value as an UUID, or return a default."""
|
||||
try:
|
||||
@@ -135,7 +129,7 @@ def checkPath(value: Any, default: Path) -> Path:
|
||||
# Validator Functions
|
||||
##
|
||||
|
||||
def isHandle(value: Any) -> bool:
|
||||
def isHandle(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid novelWriter handle.
|
||||
Note: This is case sensitive. Must be lower case!
|
||||
"""
|
||||
@@ -149,7 +143,7 @@ def isHandle(value: Any) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def isTitleTag(value: Any) -> bool:
|
||||
def isTitleTag(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid title tag string."""
|
||||
if not isinstance(value, str):
|
||||
return False
|
||||
@@ -163,19 +157,19 @@ def isTitleTag(value: Any) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def isItemClass(value: str) -> bool:
|
||||
def isItemClass(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid nwItemClass identifier."""
|
||||
return value in nwItemClass.__members__
|
||||
return isinstance(value, str) and value in nwItemClass.__members__
|
||||
|
||||
|
||||
def isItemType(value: str) -> bool:
|
||||
def isItemType(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid nwItemType identifier."""
|
||||
return value in nwItemType.__members__
|
||||
return isinstance(value, str) and value in nwItemType.__members__
|
||||
|
||||
|
||||
def isItemLayout(value: str) -> bool:
|
||||
def isItemLayout(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid nwItemLayout identifier."""
|
||||
return value in nwItemLayout.__members__
|
||||
return isinstance(value, str) and value in nwItemLayout.__members__
|
||||
|
||||
|
||||
def hexToInt(value: Any, default: int = 0) -> int:
|
||||
@@ -189,8 +183,7 @@ def hexToInt(value: Any, default: int = 0) -> int:
|
||||
|
||||
|
||||
def minmax(value: int, minVal: int, maxVal: int) -> int:
|
||||
"""Make sure an integer is between min and max value (inclusive).
|
||||
"""
|
||||
"""Check that an value is between min and max value (inclusive)."""
|
||||
return min(maxVal, max(minVal, value))
|
||||
|
||||
|
||||
|
||||
@@ -300,16 +300,21 @@ class nwLabels:
|
||||
|
||||
class nwHeadFmt:
|
||||
|
||||
BR = "{BR}"
|
||||
TITLE = "{Title}"
|
||||
CH_NUM = "{Chapter}"
|
||||
CH_WORD = "{Chapter:Word}"
|
||||
CH_ROMU = "{Chapter:URoman}"
|
||||
CH_ROML = "{Chapter:LRoman}"
|
||||
SC_NUM = "{Scene}"
|
||||
SC_ABS = "{Scene:Abs}"
|
||||
BR = "{BR}"
|
||||
TITLE = "{Title}"
|
||||
CH_NUM = "{Chapter}"
|
||||
CH_WORD = "{Chapter:Word}"
|
||||
CH_ROMU = "{Chapter:URoman}"
|
||||
CH_ROML = "{Chapter:LRoman}"
|
||||
SC_NUM = "{Scene}"
|
||||
SC_ABS = "{Scene:Abs}"
|
||||
CHAR_POV = "{Char:POV}"
|
||||
CHAR_FOCUS = "{Char:Focus}"
|
||||
|
||||
PAGE_HEADERS = [TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS]
|
||||
PAGE_HEADERS = [
|
||||
TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS,
|
||||
CHAR_POV, CHAR_FOCUS
|
||||
]
|
||||
|
||||
# ODT Document Page Header
|
||||
ODT_PROJECT = "{Project}"
|
||||
|
||||
+58
-33
@@ -420,10 +420,10 @@ class NWIndex:
|
||||
return
|
||||
|
||||
if tBits[0] == nwKeyWords.TAG_KEY:
|
||||
tagName = tBits[1]
|
||||
self._tagsIndex.add(tagName, tHandle, sTitle, itemClass)
|
||||
self._itemIndex.setHeadingTag(tHandle, sTitle, tagName)
|
||||
tags[tagName.lower()] = True
|
||||
tagKey, displayName = self.parseValue(tBits[1])
|
||||
self._tagsIndex.add(tagKey, displayName, tHandle, sTitle, itemClass.name)
|
||||
self._itemIndex.setHeadingTag(tHandle, sTitle, tagKey)
|
||||
tags[tagKey.lower()] = True
|
||||
else:
|
||||
self._itemIndex.addHeadingRef(tHandle, sTitle, tBits[1:], tBits[0])
|
||||
|
||||
@@ -471,10 +471,8 @@ class NWIndex:
|
||||
|
||||
return True, tBits, tPos
|
||||
|
||||
def checkThese(self, tBits: list[str], nwItem: NWItem) -> list[bool]:
|
||||
"""Check the tags against the index to see if they are valid
|
||||
tags. This is needed for syntax highlighting.
|
||||
"""
|
||||
def checkThese(self, tBits: list[str], tHandle: str) -> list[bool]:
|
||||
"""Check tags against the index to see if they are valid."""
|
||||
nBits = len(tBits)
|
||||
isGood = [False]*nBits
|
||||
if nBits == 0:
|
||||
@@ -488,19 +486,25 @@ class NWIndex:
|
||||
# For a tag, only the first value is accepted, the rest are ignored
|
||||
if tBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
|
||||
if tBits[1] in self._tagsIndex:
|
||||
isGood[1] = self._tagsIndex.tagHandle(tBits[1]) == nwItem.itemHandle
|
||||
isGood[1] = self._tagsIndex.tagHandle(tBits[1]) == tHandle
|
||||
else:
|
||||
isGood[1] = True
|
||||
return isGood
|
||||
|
||||
# If we're still here, we check that the references exist
|
||||
# Class references cannot have the | symbol in them
|
||||
refKey = nwKeyWords.KEY_CLASS[tBits[0]].name
|
||||
for n in range(1, nBits):
|
||||
if tBits[n] in self._tagsIndex:
|
||||
isGood[n] = self._tagsIndex.tagClass(tBits[n]) == refKey
|
||||
if (aBit := tBits[n]) in self._tagsIndex:
|
||||
isGood[n] = self._tagsIndex.tagClass(aBit) == refKey and "|" not in aBit
|
||||
|
||||
return isGood
|
||||
|
||||
def parseValue(self, text: str) -> tuple[str, str]:
|
||||
"""Parse a single value into a name and display part."""
|
||||
name, _, display = text.partition("|")
|
||||
return name.rstrip(), display.lstrip()
|
||||
|
||||
##
|
||||
# Extract Data
|
||||
##
|
||||
@@ -615,9 +619,18 @@ class NWIndex:
|
||||
for refType in refTypes:
|
||||
if refType in tRefs:
|
||||
tRefs[refType].append(self._tagsIndex.tagName(aTag))
|
||||
|
||||
return tRefs
|
||||
|
||||
def getReferenceForHeader(self, tHandle: str, nHead: int, keyClass: str) -> list[str]:
|
||||
"""Get the display names for a tags class for insertion into a
|
||||
heading by one of the build classes.
|
||||
"""
|
||||
if iItem := self._itemIndex[tHandle]:
|
||||
if hItem := iItem[f"T{nHead:04d}"]:
|
||||
hRefs = [k for k, v in hItem.references.items() if keyClass in v]
|
||||
return [self._tagsIndex.tagDisplay(k) for k in hRefs]
|
||||
return []
|
||||
|
||||
def getBackReferenceList(self, tHandle: str) -> dict[str, tuple[str, IndexHeading]]:
|
||||
"""Build a dict of files referring back to our file."""
|
||||
if tHandle is None or tHandle not in self._itemIndex:
|
||||
@@ -715,17 +728,26 @@ class TagsIndex:
|
||||
"""Return a dictionary view of all tags."""
|
||||
return self._tags.items()
|
||||
|
||||
def add(self, tagKey: str, tHandle: str, sTitle: str, itemClass: nwItemClass) -> None:
|
||||
def add(self, tagKey: str, displayName: str, tHandle: str,
|
||||
sTitle: str, className: str) -> None:
|
||||
"""Add a key to the index and set all values."""
|
||||
self._tags[tagKey.lower()] = {
|
||||
"name": tagKey, "handle": tHandle, "heading": sTitle, "class": itemClass.name
|
||||
"name": tagKey,
|
||||
"display": displayName or tagKey,
|
||||
"handle": tHandle,
|
||||
"heading": sTitle,
|
||||
"class": className,
|
||||
}
|
||||
return
|
||||
|
||||
def tagName(self, tagKey: str) -> str:
|
||||
"""Get the display name of a given tag."""
|
||||
"""Get the name of a given tag."""
|
||||
return self._tags.get(tagKey.lower(), {}).get("name", "")
|
||||
|
||||
def tagDisplay(self, tagKey: str) -> str:
|
||||
"""Get the display name of a given tag."""
|
||||
return self._tags.get(tagKey.lower(), {}).get("display", "")
|
||||
|
||||
def tagHandle(self, tagKey: str) -> str | None:
|
||||
"""Get the handle of a given tag."""
|
||||
return self._tags.get(tagKey.lower(), {}).get("handle", None)
|
||||
@@ -760,27 +782,30 @@ class TagsIndex:
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("tagsIndex is not a dict")
|
||||
|
||||
for tagKey, tagData in data.items():
|
||||
if not isinstance(tagKey, str):
|
||||
raise ValueError("tagsIndex keys must be a strings")
|
||||
if "name" not in tagData:
|
||||
raise KeyError("A tagIndex item is missing a name entry")
|
||||
if "handle" not in tagData:
|
||||
raise KeyError("A tagIndex item is missing a handle entry")
|
||||
if "heading" not in tagData:
|
||||
raise KeyError("A tagIndex item is missing a heading entry")
|
||||
if "class" not in tagData:
|
||||
raise KeyError("A tagIndex item is missing a class entry")
|
||||
if tagData["name"].lower() != tagKey:
|
||||
raise ValueError("tagsIndex name must match key")
|
||||
if not isHandle(tagData["handle"]):
|
||||
for key, entry in data.items():
|
||||
if not isinstance(key, str):
|
||||
raise ValueError("tagsIndex keys must be a string")
|
||||
if not isinstance(entry, dict):
|
||||
raise ValueError("tagsIndex entry is not a dict")
|
||||
|
||||
name = entry.get("name")
|
||||
display = entry.get("display")
|
||||
handle = entry.get("handle")
|
||||
heading = entry.get("heading")
|
||||
className = entry.get("class")
|
||||
|
||||
if not isinstance(name, str):
|
||||
raise ValueError("tagsIndex name is not a string")
|
||||
if not isinstance(display, str):
|
||||
raise ValueError("tagsIndex display is not a string")
|
||||
if not isHandle(handle):
|
||||
raise ValueError("tagsIndex handle must be a handle")
|
||||
if not isTitleTag(tagData["heading"]):
|
||||
if not isTitleTag(heading):
|
||||
raise ValueError("tagsIndex heading must be a title tag")
|
||||
if not isItemClass(tagData["class"]):
|
||||
if not isItemClass(className):
|
||||
raise ValueError("tagsIndex handle must be an nwItemClass")
|
||||
|
||||
self._tags = data
|
||||
self.add(name, display, handle, heading, className)
|
||||
|
||||
return
|
||||
|
||||
@@ -1165,7 +1190,7 @@ class IndexHeading:
|
||||
return self._tag
|
||||
|
||||
@property
|
||||
def references(self) -> dict:
|
||||
def references(self) -> dict[str, set[str]]:
|
||||
return self._refs
|
||||
|
||||
##
|
||||
|
||||
@@ -484,7 +484,10 @@ class ToHtml(Tokenizer):
|
||||
result = f"<span class='tags'>{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:</span> "
|
||||
if len(bits) > 1:
|
||||
if bits[0] == nwKeyWords.TAG_KEY:
|
||||
result += f"<a name='tag_{bits[1]}'>{bits[1]}</a>"
|
||||
one, two = self._project.index.parseValue(bits[1])
|
||||
result += f"<a name='tag_{one}'>{one}</a>"
|
||||
if two:
|
||||
result += f" | <span class='optional'>{two}</a>"
|
||||
else:
|
||||
if self._genMode == self.M_PREVIEW:
|
||||
result += ", ".join(f"<a href='#{bits[0][1:]}={t}'>{t}</a>" for t in bits[1:])
|
||||
|
||||
@@ -37,7 +37,9 @@ from PyQt5.QtCore import QCoreApplication, QRegularExpression
|
||||
|
||||
from novelwriter.enum import nwComment, nwItemLayout
|
||||
from novelwriter.common import formatTimeStamp, numberToRoman, checkInt
|
||||
from novelwriter.constants import nwHeadFmt, nwRegEx, nwShortcode, nwUnicode
|
||||
from novelwriter.constants import (
|
||||
nwHeadFmt, nwKeyWords, nwLabels, nwRegEx, nwShortcode, nwUnicode, trConst
|
||||
)
|
||||
from novelwriter.core.index import processComment
|
||||
from novelwriter.core.project import NWProject
|
||||
|
||||
@@ -112,13 +114,15 @@ class Tokenizer(ABC):
|
||||
|
||||
# Data Variables
|
||||
self._text = "" # The raw text to be tokenized
|
||||
self._nwItem = None # The NWItem associated with the handle
|
||||
self._tokens = [] # The list of the processed tokens
|
||||
self._nwItem = None # The NWItem currently being processed
|
||||
self._result = "" # The result of the last document
|
||||
|
||||
self._keepMarkdown = False # Whether to keep the markdown text
|
||||
self._allMarkdown = [] # The result novelWriter markdown of all documents
|
||||
|
||||
# Processed Tokens
|
||||
self._tokens: list[tuple[int, int, str, list[tuple[int, int]], int]] = []
|
||||
|
||||
# User Settings
|
||||
self._textFont = "Serif" # Output text font
|
||||
self._textSize = 11 # Output text size
|
||||
@@ -350,7 +354,7 @@ class Tokenizer(ABC):
|
||||
title = f"{trNotes}: {tItem.itemName}"
|
||||
self._tokens = []
|
||||
self._tokens.append((
|
||||
self.T_TITLE, 0, title, None, textAlign
|
||||
self.T_TITLE, 0, title, [], textAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
self._allMarkdown.append(f"# {title}\n\n")
|
||||
@@ -418,7 +422,7 @@ class Tokenizer(ABC):
|
||||
# Check for blank lines
|
||||
if len(sLine) == 0:
|
||||
self._tokens.append((
|
||||
self.T_EMPTY, nHead, "", None, self.A_NONE
|
||||
self.T_EMPTY, nHead, "", [], self.A_NONE
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("\n")
|
||||
@@ -446,7 +450,7 @@ class Tokenizer(ABC):
|
||||
|
||||
elif sLine == "[vspace]":
|
||||
self._tokens.append(
|
||||
(self.T_SKIP, nHead, "", None, sAlign)
|
||||
(self.T_SKIP, nHead, "", [], sAlign)
|
||||
)
|
||||
continue
|
||||
|
||||
@@ -454,11 +458,11 @@ class Tokenizer(ABC):
|
||||
nSkip = checkInt(sLine[8:-1], 0)
|
||||
if nSkip >= 1:
|
||||
self._tokens.append(
|
||||
(self.T_SKIP, nHead, "", None, sAlign)
|
||||
(self.T_SKIP, nHead, "", [], sAlign)
|
||||
)
|
||||
if nSkip > 1:
|
||||
self._tokens += (nSkip - 1) * [
|
||||
(self.T_SKIP, nHead, "", None, self.A_NONE)
|
||||
(self.T_SKIP, nHead, "", [], self.A_NONE)
|
||||
]
|
||||
continue
|
||||
|
||||
@@ -466,26 +470,26 @@ class Tokenizer(ABC):
|
||||
cStyle, cText, _ = processComment(aLine)
|
||||
if cStyle == nwComment.SYNOPSIS:
|
||||
self._tokens.append((
|
||||
self.T_SYNOPSIS, nHead, cText, None, sAlign
|
||||
self.T_SYNOPSIS, nHead, cText, [], sAlign
|
||||
))
|
||||
if self._doSynopsis and self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
elif cStyle == nwComment.SHORT:
|
||||
self._tokens.append((
|
||||
self.T_SHORT, nHead, cText, None, sAlign
|
||||
self.T_SHORT, nHead, cText, [], sAlign
|
||||
))
|
||||
if self._doSynopsis and self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
else:
|
||||
self._tokens.append((
|
||||
self.T_COMMENT, nHead, cText, None, sAlign
|
||||
self.T_COMMENT, nHead, cText, [], sAlign
|
||||
))
|
||||
if self._doComments and self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[0] == "@":
|
||||
self._tokens.append((
|
||||
self.T_KEYWORD, nHead, aLine[1:].strip(), None, sAlign
|
||||
self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign
|
||||
))
|
||||
if self._doKeywords and self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -497,7 +501,7 @@ class Tokenizer(ABC):
|
||||
|
||||
nHead += 1
|
||||
self._tokens.append((
|
||||
self.T_HEAD1, nHead, aLine[2:].strip(), None, sAlign
|
||||
self.T_HEAD1, nHead, aLine[2:].strip(), [], sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -508,7 +512,7 @@ class Tokenizer(ABC):
|
||||
|
||||
nHead += 1
|
||||
self._tokens.append((
|
||||
self.T_HEAD2, nHead, aLine[3:].strip(), None, sAlign
|
||||
self.T_HEAD2, nHead, aLine[3:].strip(), [], sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -516,7 +520,7 @@ class Tokenizer(ABC):
|
||||
elif aLine[:4] == "### ":
|
||||
nHead += 1
|
||||
self._tokens.append((
|
||||
self.T_HEAD3, nHead, aLine[4:].strip(), None, sAlign
|
||||
self.T_HEAD3, nHead, aLine[4:].strip(), [], sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -524,7 +528,7 @@ class Tokenizer(ABC):
|
||||
elif aLine[:5] == "#### ":
|
||||
nHead += 1
|
||||
self._tokens.append((
|
||||
self.T_HEAD4, nHead, aLine[5:].strip(), None, sAlign
|
||||
self.T_HEAD4, nHead, aLine[5:].strip(), [], sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -537,7 +541,7 @@ class Tokenizer(ABC):
|
||||
tStyle = self.T_HEAD1
|
||||
|
||||
self._tokens.append((
|
||||
tStyle, nHead, aLine[3:].strip(), None, sAlign | self.A_CENTRE
|
||||
tStyle, nHead, aLine[3:].strip(), [], sAlign | self.A_CENTRE
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -551,7 +555,7 @@ class Tokenizer(ABC):
|
||||
tStyle = self.T_HEAD2
|
||||
|
||||
self._tokens.append((
|
||||
tStyle, nHead, aLine[4:].strip(), None, sAlign
|
||||
tStyle, nHead, aLine[4:].strip(), [], sAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
@@ -614,7 +618,7 @@ class Tokenizer(ABC):
|
||||
|
||||
# Always add an empty line at the end of the file
|
||||
self._tokens.append((
|
||||
self.T_EMPTY, nHead, "", None, self.A_NONE
|
||||
self.T_EMPTY, nHead, "", [], self.A_NONE
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
tmpMarkdown.append("\n")
|
||||
@@ -626,8 +630,8 @@ class Tokenizer(ABC):
|
||||
# ===========
|
||||
# Some items need a second pass
|
||||
|
||||
pToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
|
||||
nToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
|
||||
pToken = (self.T_EMPTY, 0, "", [], self.A_NONE)
|
||||
nToken = (self.T_EMPTY, 0, "", [], self.A_NONE)
|
||||
tCount = len(self._tokens)
|
||||
for n, tToken in enumerate(self._tokens):
|
||||
|
||||
@@ -655,6 +659,8 @@ class Tokenizer(ABC):
|
||||
if not self._isNovel:
|
||||
return False
|
||||
|
||||
self._hFormatter.setHandle(self._nwItem.itemHandle if self._nwItem else None)
|
||||
|
||||
for n, tToken in enumerate(self._tokens):
|
||||
|
||||
# In case we see text before a scene, we reset the flag
|
||||
@@ -664,9 +670,9 @@ class Tokenizer(ABC):
|
||||
elif tToken[0] == self.T_HEAD1:
|
||||
# Partition
|
||||
|
||||
tTemp = self._hFormatter.apply(self._fmtTitle, tToken[2])
|
||||
tTemp = self._hFormatter.apply(self._fmtTitle, tToken[2], tToken[1])
|
||||
self._tokens[n] = (
|
||||
tToken[0], tToken[1], tTemp, None, tToken[4]
|
||||
tToken[0], tToken[1], tTemp, [], tToken[4]
|
||||
)
|
||||
|
||||
elif tToken[0] in (self.T_HEAD2, self.T_UNNUM):
|
||||
@@ -674,14 +680,14 @@ class Tokenizer(ABC):
|
||||
|
||||
# Numbered or Unnumbered
|
||||
if tToken[0] == self.T_UNNUM:
|
||||
tTemp = self._hFormatter.apply(self._fmtUnNum, tToken[2])
|
||||
tTemp = self._hFormatter.apply(self._fmtUnNum, tToken[2], tToken[1])
|
||||
else:
|
||||
self._hFormatter.incChapter()
|
||||
tTemp = self._hFormatter.apply(self._fmtChapter, tToken[2])
|
||||
tTemp = self._hFormatter.apply(self._fmtChapter, tToken[2], tToken[1])
|
||||
|
||||
# Format the chapter header
|
||||
self._tokens[n] = (
|
||||
tToken[0], tToken[1], tTemp, None, tToken[4]
|
||||
tToken[0], tToken[1], tTemp, [], tToken[4]
|
||||
)
|
||||
|
||||
# Set scene variables
|
||||
@@ -693,32 +699,32 @@ class Tokenizer(ABC):
|
||||
|
||||
self._hFormatter.incScene()
|
||||
|
||||
tTemp = self._hFormatter.apply(self._fmtScene, tToken[2])
|
||||
tTemp = self._hFormatter.apply(self._fmtScene, tToken[2], tToken[1])
|
||||
if tTemp == "" and self._hideScene:
|
||||
self._tokens[n] = (
|
||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||
self.T_EMPTY, tToken[1], "", [], self.A_NONE
|
||||
)
|
||||
elif tTemp == "" and not self._hideScene:
|
||||
if self._firstScene:
|
||||
self._tokens[n] = (
|
||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||
self.T_EMPTY, tToken[1], "", [], self.A_NONE
|
||||
)
|
||||
else:
|
||||
self._tokens[n] = (
|
||||
self.T_SKIP, tToken[1], "", None, tToken[4]
|
||||
self.T_SKIP, tToken[1], "", [], tToken[4]
|
||||
)
|
||||
elif tTemp == self._fmtScene:
|
||||
if self._firstScene:
|
||||
self._tokens[n] = (
|
||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||
self.T_EMPTY, tToken[1], "", [], self.A_NONE
|
||||
)
|
||||
else:
|
||||
self._tokens[n] = (
|
||||
self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE
|
||||
self.T_SEP, tToken[1], tTemp, [], tToken[4] | self.A_CENTRE
|
||||
)
|
||||
else:
|
||||
self._tokens[n] = (
|
||||
tToken[0], tToken[1], tTemp, None, tToken[4]
|
||||
tToken[0], tToken[1], tTemp, [], tToken[4]
|
||||
)
|
||||
|
||||
self._firstScene = False
|
||||
@@ -726,22 +732,22 @@ class Tokenizer(ABC):
|
||||
elif tToken[0] == self.T_HEAD4:
|
||||
# Section
|
||||
|
||||
tTemp = self._hFormatter.apply(self._fmtSection, tToken[2])
|
||||
tTemp = self._hFormatter.apply(self._fmtSection, tToken[2], tToken[1])
|
||||
if tTemp == "" and self._hideSection:
|
||||
self._tokens[n] = (
|
||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||
self.T_EMPTY, tToken[1], "", [], self.A_NONE
|
||||
)
|
||||
elif tTemp == "" and not self._hideSection:
|
||||
self._tokens[n] = (
|
||||
self.T_SKIP, tToken[1], "", None, tToken[4]
|
||||
self.T_SKIP, tToken[1], "", [], tToken[4]
|
||||
)
|
||||
elif tTemp == self._fmtSection:
|
||||
self._tokens[n] = (
|
||||
self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE
|
||||
self.T_SEP, tToken[1], tTemp, [], tToken[4] | self.A_CENTRE
|
||||
)
|
||||
else:
|
||||
self._tokens[n] = (
|
||||
tToken[0], tToken[1], tTemp, None, tToken[4]
|
||||
tToken[0], tToken[1], tTemp, [], tToken[4]
|
||||
)
|
||||
|
||||
return True
|
||||
@@ -817,11 +823,17 @@ class HeadingFormatter:
|
||||
|
||||
def __init__(self, project: NWProject) -> None:
|
||||
self._project = project
|
||||
self._handle = None
|
||||
self._chCount = 0
|
||||
self._scChCount = 0
|
||||
self._scAbsCount = 0
|
||||
return
|
||||
|
||||
def setHandle(self, tHandle: str | None) -> None:
|
||||
"""Set the handle currently being processed."""
|
||||
self._handle = tHandle
|
||||
return
|
||||
|
||||
def incChapter(self) -> None:
|
||||
"""Increment the chapter counter."""
|
||||
self._chCount += 1
|
||||
@@ -838,7 +850,7 @@ class HeadingFormatter:
|
||||
self._scChCount = 0
|
||||
return
|
||||
|
||||
def apply(self, hFormat: str, text: str) -> str:
|
||||
def apply(self, hFormat: str, text: str, nHead: int) -> str:
|
||||
"""Apply formatting to a specific heading."""
|
||||
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
||||
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
|
||||
@@ -854,6 +866,19 @@ class HeadingFormatter:
|
||||
chRom = numberToRoman(self._chCount, toLower=False)
|
||||
hFormat = hFormat.replace(nwHeadFmt.CH_ROMU, chRom)
|
||||
|
||||
if nwHeadFmt.CHAR_POV in hFormat or nwHeadFmt.CHAR_FOCUS in hFormat:
|
||||
if self._handle and nHead > 0:
|
||||
index = self._project.index
|
||||
pList = index.getReferenceForHeader(self._handle, nHead, nwKeyWords.POV_KEY)
|
||||
fList = index.getReferenceForHeader(self._handle, nHead, nwKeyWords.FOCUS_KEY)
|
||||
pText = pList[0] if pList else nwUnicode.U_ENDASH
|
||||
fText = fList[0] if fList else nwUnicode.U_ENDASH
|
||||
else:
|
||||
pText = trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
|
||||
fText = trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
|
||||
hFormat = hFormat.replace(nwHeadFmt.CHAR_POV, pText)
|
||||
hFormat = hFormat.replace(nwHeadFmt.CHAR_FOCUS, fText)
|
||||
|
||||
return hFormat
|
||||
|
||||
# END Class HeadingFormatter
|
||||
|
||||
@@ -50,7 +50,7 @@ def logException() -> None:
|
||||
"""Log the content of an exception message."""
|
||||
exType, exValue, _ = sys.exc_info()
|
||||
if exType is not None:
|
||||
logger.error("%s: %s", exType.__name__, str(exValue))
|
||||
logger.error(f"{exType.__name__}: {str(exValue)}", stacklevel=2)
|
||||
return
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary
|
||||
from novelwriter.common import minmax, transferCase
|
||||
from novelwriter.constants import nwKeyWords, nwLabels, nwShortcode, nwUnicode, trConst
|
||||
from novelwriter.core.item import NWItem
|
||||
from novelwriter.core.index import countWords
|
||||
from novelwriter.tools.lipsum import GuiLipsum
|
||||
from novelwriter.core.document import NWDocument
|
||||
@@ -1835,16 +1834,16 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
if len(text) == 0:
|
||||
return nwTrinary.NEUTRAL
|
||||
|
||||
if text.startswith("@") and isinstance(self._nwItem, NWItem):
|
||||
if text.startswith("@") and self._docHandle:
|
||||
|
||||
isGood, tBits, tPos = SHARED.project.index.scanThis(text)
|
||||
if not isGood:
|
||||
if not isGood or not tBits or tBits[0] == nwKeyWords.TAG_KEY:
|
||||
return nwTrinary.NEUTRAL
|
||||
|
||||
tag = ""
|
||||
exist = False
|
||||
cPos = cursor.selectionStart() - block.position()
|
||||
tExist = SHARED.project.index.checkThese(tBits, self._nwItem)
|
||||
tExist = SHARED.project.index.checkThese(tBits, self._docHandle)
|
||||
for sTag, sPos, sExist in zip(reversed(tBits), reversed(tPos), reversed(tExist)):
|
||||
if cPos >= sPos:
|
||||
# The cursor is between the start of two tags
|
||||
|
||||
@@ -35,10 +35,10 @@ from PyQt5.QtGui import (
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.enum import nwComment
|
||||
from novelwriter.common import checkInt
|
||||
from novelwriter.constants import nwRegEx, nwUnicode
|
||||
from novelwriter.core.index import processComment
|
||||
from novelwriter.enum import nwComment
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -107,7 +107,8 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
"code": self._makeFormat(SHARED.theme.colCode),
|
||||
"keyword": self._makeFormat(SHARED.theme.colKey),
|
||||
"modifier": self._makeFormat(SHARED.theme.colMod),
|
||||
"value": self._makeFormat(SHARED.theme.colVal, "underline"),
|
||||
"value": self._makeFormat(SHARED.theme.colVal),
|
||||
"optional": self._makeFormat(SHARED.theme.colOpt),
|
||||
"codevalue": self._makeFormat(SHARED.theme.colVal),
|
||||
"codeinval": self._makeFormat(None, "errline"),
|
||||
}
|
||||
@@ -278,23 +279,23 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
|
||||
if text.startswith("@"): # Keywords and commands
|
||||
self.setCurrentBlockState(self.BLOCK_META)
|
||||
if self._tItem:
|
||||
pIndex = SHARED.project.index
|
||||
isValid, bits, pos = pIndex.scanThis(text)
|
||||
isGood = pIndex.checkThese(bits, self._tItem)
|
||||
if isValid:
|
||||
for n, bit in enumerate(bits):
|
||||
xPos = pos[n]
|
||||
xLen = len(bit)
|
||||
if isGood[n]:
|
||||
if n == 0:
|
||||
self.setFormat(xPos, xLen, self._hStyles["keyword"])
|
||||
else:
|
||||
self.setFormat(xPos, xLen, self._hStyles["value"])
|
||||
else:
|
||||
kwFmt = self.format(xPos)
|
||||
kwFmt.merge(self._hStyles["codeinval"])
|
||||
self.setFormat(xPos, xLen, kwFmt)
|
||||
index = SHARED.project.index
|
||||
isValid, bits, pos = index.scanThis(text)
|
||||
isGood = index.checkThese(bits, self._tHandle)
|
||||
if isValid:
|
||||
for n, bit in enumerate(bits):
|
||||
xPos = pos[n]
|
||||
xLen = len(bit)
|
||||
if not isGood[n]:
|
||||
self.setFormat(xPos, xLen, self._hStyles["codeinval"])
|
||||
elif n == 0:
|
||||
self.setFormat(xPos, xLen, self._hStyles["keyword"])
|
||||
else:
|
||||
one, two = index.parseValue(bit)
|
||||
self.setFormat(xPos, len(one), self._hStyles["value"])
|
||||
if two:
|
||||
yPos = xPos + len(bit) - len(two)
|
||||
self.setFormat(yPos, len(two), self._hStyles["optional"])
|
||||
|
||||
# We never want to run the spell checker on keyword/values,
|
||||
# so we force a return here
|
||||
@@ -406,8 +407,6 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
if "errline" in styles:
|
||||
charFormat.setUnderlineColor(SHARED.theme.colError)
|
||||
charFormat.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
|
||||
if "underline" in styles:
|
||||
charFormat.setFontUnderline(True)
|
||||
if "background" in styles and color is not None:
|
||||
charFormat.setBackground(QBrush(color, Qt.SolidPattern))
|
||||
|
||||
|
||||
@@ -461,15 +461,16 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
def _makeStyleSheet(self) -> None:
|
||||
"""Generate an appropriate style sheet for the document viewer,
|
||||
based on the current syntax highlighter theme,
|
||||
based on the current syntax highlighter theme.
|
||||
"""
|
||||
colText = SHARED.theme.colText
|
||||
colHead = SHARED.theme.colHead
|
||||
colVal = SHARED.theme.colVal
|
||||
colVals = SHARED.theme.colVal
|
||||
colEmph = SHARED.theme.colEmph
|
||||
colKey = SHARED.theme.colKey
|
||||
colHidden = SHARED.theme.colHidden
|
||||
colMod = SHARED.theme.colMod
|
||||
colKeys = SHARED.theme.colKey
|
||||
colHide = SHARED.theme.colHidden
|
||||
colMods = SHARED.theme.colMod
|
||||
colOpts = SHARED.theme.colOpt
|
||||
styleSheet = (
|
||||
"body {{"
|
||||
" color: rgb({tColR}, {tColG}, {tColB});"
|
||||
@@ -486,6 +487,9 @@ class GuiDocViewer(QTextBrowser):
|
||||
".tags {{"
|
||||
" color: rgb({kColR}, {kColG}, {kColB});"
|
||||
"}}\n"
|
||||
".optional {{"
|
||||
" color: rgb({oColR}, {oColG}, {oColB});"
|
||||
"}}\n"
|
||||
".comment {{"
|
||||
" color: rgb({cColR}, {cColG}, {cColB});"
|
||||
"}}\n"
|
||||
@@ -496,27 +500,14 @@ class GuiDocViewer(QTextBrowser):
|
||||
" text-align: center;"
|
||||
"}}\n"
|
||||
).format(
|
||||
tColR=colText.red(),
|
||||
tColG=colText.green(),
|
||||
tColB=colText.blue(),
|
||||
hColR=colHead.red(),
|
||||
hColG=colHead.green(),
|
||||
hColB=colHead.blue(),
|
||||
aColR=colVal.red(),
|
||||
aColG=colVal.green(),
|
||||
aColB=colVal.blue(),
|
||||
eColR=colEmph.red(),
|
||||
eColG=colEmph.green(),
|
||||
eColB=colEmph.blue(),
|
||||
kColR=colKey.red(),
|
||||
kColG=colKey.green(),
|
||||
kColB=colKey.blue(),
|
||||
cColR=colHidden.red(),
|
||||
cColG=colHidden.green(),
|
||||
cColB=colHidden.blue(),
|
||||
mColR=colMod.red(),
|
||||
mColG=colMod.green(),
|
||||
mColB=colMod.blue(),
|
||||
tColR=colText.red(), tColG=colText.green(), tColB=colText.blue(),
|
||||
hColR=colHead.red(), hColG=colHead.green(), hColB=colHead.blue(),
|
||||
aColR=colVals.red(), aColG=colVals.green(), aColB=colVals.blue(),
|
||||
eColR=colEmph.red(), eColG=colEmph.green(), eColB=colEmph.blue(),
|
||||
kColR=colKeys.red(), kColG=colKeys.green(), kColB=colKeys.blue(),
|
||||
cColR=colHide.red(), cColG=colHide.green(), cColB=colHide.blue(),
|
||||
mColR=colMods.red(), mColG=colMods.green(), mColB=colMods.blue(),
|
||||
oColR=colOpts.red(), oColG=colOpts.green(), oColB=colOpts.blue(),
|
||||
)
|
||||
self.document().setDefaultStyleSheet(styleSheet)
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ class GuiTheme:
|
||||
self.colCode = QColor(0, 0, 0)
|
||||
self.colKey = QColor(0, 0, 0)
|
||||
self.colVal = QColor(0, 0, 0)
|
||||
self.colOpt = QColor(0, 0, 0)
|
||||
self.colSpell = QColor(0, 0, 0)
|
||||
self.colError = QColor(0, 0, 0)
|
||||
self.colRepTag = QColor(0, 0, 0)
|
||||
@@ -323,6 +324,7 @@ class GuiTheme:
|
||||
self.colCode = self._parseColour(confParser, cnfSec, "shortcode")
|
||||
self.colKey = self._parseColour(confParser, cnfSec, "keyword")
|
||||
self.colVal = self._parseColour(confParser, cnfSec, "value")
|
||||
self.colOpt = self._parseColour(confParser, cnfSec, "optional")
|
||||
self.colSpell = self._parseColour(confParser, cnfSec, "spellcheckline")
|
||||
self.colError = self._parseColour(confParser, cnfSec, "errorline")
|
||||
self.colRepTag = self._parseColour(confParser, cnfSec, "replacetag")
|
||||
|
||||
@@ -604,7 +604,7 @@ class _DetailsWidget(QWidget):
|
||||
for key in entries:
|
||||
sub = QTreeWidgetItem()
|
||||
sub.setText(0, build.getLabel(key))
|
||||
sub.setText(1, hFmt.apply(build.getStr(key), title))
|
||||
sub.setText(1, hFmt.apply(build.getStr(key), title, 0))
|
||||
item.addChild(sub)
|
||||
for key in ["headings.hideScene", "headings.hideSection"]:
|
||||
sub = QTreeWidgetItem()
|
||||
|
||||
@@ -722,6 +722,8 @@ class _HeadingsTab(NScrollablePage):
|
||||
self.aInsChRomL = self.menuInsert.addAction(self.tr("Chapter Number (Lower Case Roman)"))
|
||||
self.aInsScNum = self.menuInsert.addAction(self.tr("Scene Number (In Chapter)"))
|
||||
self.aInsScAbs = self.menuInsert.addAction(self.tr("Scene Number (Absolute)"))
|
||||
self.aInsCharPOV = self.menuInsert.addAction(self.tr("Point of View Character"))
|
||||
self.aInsCharFocus = self.menuInsert.addAction(self.tr("Focus Character"))
|
||||
|
||||
self.aInsTitle.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.TITLE))
|
||||
self.aInsChNum.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_NUM))
|
||||
@@ -730,6 +732,8 @@ class _HeadingsTab(NScrollablePage):
|
||||
self.aInsChRomL.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_ROML))
|
||||
self.aInsScNum.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.SC_NUM))
|
||||
self.aInsScAbs.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.SC_ABS))
|
||||
self.aInsCharPOV.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CHAR_POV))
|
||||
self.aInsCharFocus.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CHAR_FOCUS))
|
||||
|
||||
self.btnInsert = QPushButton(self.tr("Insert"))
|
||||
self.btnInsert.setMenu(self.menuInsert)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%%~name: John Smith
|
||||
%%~path: f7e2d9f330615/14298de4d9524
|
||||
%%~kind: CHARACTER/NOTE
|
||||
%%~hash: fda91c416d874aa41a47fceaedb3d62f040c7e32
|
||||
%%~date: Unknown/2023-11-25 18:16:13
|
||||
%%~hash: f85b4da6ba4a557db0c4d5f1b59f33ba01542606
|
||||
%%~date: Unknown/2024-01-30 10:58:18
|
||||
# John Smith
|
||||
|
||||
@tag: John
|
||||
@tag: John | John Smith
|
||||
|
||||
% Short: The sidekick
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%%~name: Jane Smith
|
||||
%%~path: f7e2d9f330615/bb2c23b3c42cc
|
||||
%%~kind: CHARACTER/NOTE
|
||||
%%~hash: b7291713899bd0356617a36ae606b08e5fee1b65
|
||||
%%~date: Unknown/2023-11-25 18:16:07
|
||||
%%~hash: 10f072a4bf7389b0c178a4181e50833d39b945af
|
||||
%%~date: Unknown/2024-01-30 11:05:37
|
||||
# Jane Smith
|
||||
|
||||
@tag: Jane
|
||||
@tag: Jane | Jane Smith
|
||||
|
||||
% Short: The heroine
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.3a3" hexVersion="0x020300a3" fileVersion="1.5" fileRevision="2" timeStamp="2024-01-27 18:20:02">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1621" autoCount="255" editTime="81261">
|
||||
<novelWriterXML appVersion="2.3a3" hexVersion="0x020300a3" fileVersion="1.5" fileRevision="2" timeStamp="2024-01-29 17:42:31">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1660" autoCount="256" editTime="81969">
|
||||
<name>Sample Project</name>
|
||||
<author>Jane Smith</author>
|
||||
</project>
|
||||
@@ -57,7 +57,7 @@
|
||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="2901" wordCount="513" paraCount="15" cursorPos="392" />
|
||||
<meta expanded="no" heading="H3" charCount="2901" wordCount="513" paraCount="15" cursorPos="66" />
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
@@ -101,11 +101,11 @@
|
||||
<name status="sf12341" import="ia857f0">Main Characters</name>
|
||||
</item>
|
||||
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="48" />
|
||||
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="15" />
|
||||
<name status="sf12341" import="icfb3a5" active="yes">John Smith</name>
|
||||
</item>
|
||||
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="47" />
|
||||
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="31" />
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%%~name: Ancient Europe
|
||||
%%~path: 60bdf227455cc/04468803b92e1
|
||||
%%~kind: WORLD/NOTE
|
||||
%%~hash: b4318c2de40a1fc4055d18f8d02696b9cf171ea2
|
||||
%%~date: Unknown/Unknown
|
||||
%%~hash: 7b746485a1d06c64a5e0bf671e3141806ed51a54
|
||||
%%~date: Unknown/2024-01-30 21:36:44
|
||||
# Ancient Europe
|
||||
|
||||
@tag: Europe
|
||||
@tag: Europe | Ancient Europe
|
||||
|
||||
Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%%~name: Mr. Nobody
|
||||
%%~path: 67a8707f2f249/4c4f28287af27
|
||||
%%~kind: CHARACTER/NOTE
|
||||
%%~hash: 07a86c2001669de7d7a7545286c22b963e3ccf63
|
||||
%%~date: Unknown/Unknown
|
||||
%%~hash: 2a9b9751e8207b719bfa6233ebe04e24928ff8a8
|
||||
%%~date: Unknown/2024-01-30 21:36:26
|
||||
# Nobody Owens
|
||||
|
||||
@tag: Bod
|
||||
@tag: Bod | Nobody Owens
|
||||
@plot: Main
|
||||
|
||||
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
%%~name: Prologue
|
||||
%%~path: b3643d0f92e32/88d59a277361b
|
||||
%%~kind: NOVEL/DOCUMENT
|
||||
%%~hash: 19a2aa95b07ce10eaea753c46569929439648c63
|
||||
%%~date: Unknown/Unknown
|
||||
%%~hash: 5f965566ba82bbb83b8aa24f3ab7efde0c5e61cf
|
||||
%%~date: Unknown/2024-01-30 21:36:00
|
||||
##! Prologue
|
||||
|
||||
% Synopsis:Explanation from the lipsum.com website.
|
||||
% Synopsis: Explanation from the lipsum.com website.
|
||||
|
||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
%%~path: 45e6b01ca35c1/fb609cd8319dc
|
||||
%%~kind: NOVEL/DOCUMENT
|
||||
%%~hash: 5dabeaa7a58238a6ad99ce73176b1bdfad1a71b7
|
||||
%%~date: Unknown/Unknown
|
||||
%%~date: Unknown/2024-01-30 21:35:57
|
||||
## Chapter One
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.1-beta1" hexVersion="0x020100b1" fileVersion="1.5" fileRevision="1" timeStamp="2023-08-25 18:03:37">
|
||||
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="43" autoCount="24" editTime="1935">
|
||||
<novelWriterXML appVersion="2.3a3" hexVersion="0x020300a3" fileVersion="1.5" fileRevision="2" timeStamp="2024-01-30 21:37:01">
|
||||
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="44" autoCount="25" editTime="2039">
|
||||
<name>Lorem Ipsum</name>
|
||||
<title>Lorem Ipsum</title>
|
||||
<author>lipsum.com</author>
|
||||
</project>
|
||||
<settings>
|
||||
@@ -46,7 +45,7 @@
|
||||
<name status="sedd043" import="i613591" active="yes">Front Matter</name>
|
||||
</item>
|
||||
<item handle="88d59a277361b" parent="b3643d0f92e32" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H2" charCount="584" wordCount="92" paraCount="1" cursorPos="4" />
|
||||
<meta expanded="no" heading="H2" charCount="584" wordCount="92" paraCount="1" cursorPos="16" />
|
||||
<name status="s92a87b" import="i613591" active="yes">Prologue</name>
|
||||
</item>
|
||||
<item handle="db7e733775d4d" parent="b3643d0f92e32" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
@@ -58,11 +57,11 @@
|
||||
<name status="s92a87b" import="i613591">Chapter One</name>
|
||||
</item>
|
||||
<item handle="fb609cd8319dc" parent="45e6b01ca35c1" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H2" charCount="419" wordCount="67" paraCount="1" cursorPos="56" />
|
||||
<meta expanded="no" heading="H2" charCount="419" wordCount="67" paraCount="1" cursorPos="59" />
|
||||
<name status="s92a87b" import="i613591" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="88243afbe5ed8" parent="45e6b01ca35c1" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="2758" wordCount="404" paraCount="4" cursorPos="1528" />
|
||||
<meta expanded="no" heading="H3" charCount="2758" wordCount="404" paraCount="4" cursorPos="57" />
|
||||
<name status="sedd043" import="i613591" active="yes">Scene One</name>
|
||||
</item>
|
||||
<item handle="f96ec11c6a3da" parent="45e6b01ca35c1" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
@@ -98,7 +97,7 @@
|
||||
<name status="sbaa94f" import="i613591">Characters</name>
|
||||
</item>
|
||||
<item handle="4c4f28287af27" parent="67a8707f2f249" root="67a8707f2f249" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="1864" wordCount="284" paraCount="3" cursorPos="1883" />
|
||||
<meta expanded="no" heading="H1" charCount="1864" wordCount="284" paraCount="3" cursorPos="40" />
|
||||
<name status="sbaa94f" import="i613591" active="yes">Mr. Nobody</name>
|
||||
</item>
|
||||
<item handle="6c6afb1247750" parent="None" root="6c6afb1247750" order="2" type="ROOT" class="PLOT">
|
||||
@@ -114,7 +113,7 @@
|
||||
<name status="sbaa94f" import="i613591">World</name>
|
||||
</item>
|
||||
<item handle="04468803b92e1" parent="60bdf227455cc" root="60bdf227455cc" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="1792" />
|
||||
<meta expanded="no" heading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="47" />
|
||||
<name status="sbaa94f" import="i613591" active="yes">Ancient Europe</name>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"novelWriter.tagsIndex": {
|
||||
"bod": {"name": "Bod", "handle": "4c4f28287af27", "heading": "T0001", "class": "CHARACTER"},
|
||||
"main": {"name": "Main", "handle": "2426c6f0ca922", "heading": "T0001", "class": "PLOT"},
|
||||
"europe": {"name": "Europe", "handle": "04468803b92e1", "heading": "T0001", "class": "WORLD"}
|
||||
"bod": {"name": "Bod", "display": "Nobody Owens", "handle": "4c4f28287af27", "heading": "T0001", "class": "CHARACTER"},
|
||||
"main": {"name": "Main", "display": "Main", "handle": "2426c6f0ca922", "heading": "T0001", "class": "PLOT"},
|
||||
"europe": {"name": "Europe", "display": "Ancient Europe", "handle": "04468803b92e1", "heading": "T0001", "class": "WORLD"}
|
||||
},
|
||||
"novelWriter.itemIndex": {
|
||||
"7a992350f3eb6": {
|
||||
|
||||
@@ -150,7 +150,7 @@ Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacin
|
||||
|
||||
# Nobody Owens
|
||||
|
||||
**Tag:** Bod
|
||||
**Tag:** Bod | Nobody Owens
|
||||
**Plot:** Main
|
||||
|
||||
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
|
||||
@@ -173,7 +173,7 @@ Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam.
|
||||
|
||||
# Ancient Europe
|
||||
|
||||
**Tag:** Europe
|
||||
**Tag:** Europe | Ancient Europe
|
||||
|
||||
Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ a {color: rgb(66, 113, 174);}
|
||||
<p>Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.</p>
|
||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Notes: Characters</h1>
|
||||
<h1>Nobody Owens</h1>
|
||||
<p style='margin-bottom: 0;'><span class='tags'>Tag:</span> <a name='tag_Bod'>Bod</a></p>
|
||||
<p style='margin-bottom: 0;'><span class='tags'>Tag:</span> <a name='tag_Bod'>Bod</a> | <span class='optional'>Nobody Owens</a></p>
|
||||
<p style='margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
||||
<p>Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.</p>
|
||||
<p>Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.</p>
|
||||
@@ -116,7 +116,7 @@ a {color: rgb(66, 113, 174);}
|
||||
<p>Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.</p>
|
||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Notes: World</h1>
|
||||
<h1>Ancient Europe</h1>
|
||||
<p><span class='tags'>Tag:</span> <a name='tag_Europe'>Europe</a></p>
|
||||
<p><span class='tags'>Tag:</span> <a name='tag_Europe'>Europe</a> | <span class='optional'>Ancient Europe</a></p>
|
||||
<p>Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.</p>
|
||||
<p>Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.</p>
|
||||
<p>Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.</p>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"meta": {
|
||||
"projectName": "Lorem Ipsum",
|
||||
"novelAuthor": "lipsum.com",
|
||||
"buildTime": 1706306020,
|
||||
"buildTimeStr": "2024-01-26 22:53:40"
|
||||
"buildTime": 1706648755,
|
||||
"buildTimeStr": "2024-01-30 22:05:55"
|
||||
},
|
||||
"text": {
|
||||
"css": [
|
||||
@@ -132,7 +132,7 @@
|
||||
],
|
||||
[
|
||||
"<h1>Nobody Owens</h1>",
|
||||
"<p style='margin-bottom: 0;'><span class='tags'>Tag:</span> <a name='tag_Bod'>Bod</a></p>",
|
||||
"<p style='margin-bottom: 0;'><span class='tags'>Tag:</span> <a name='tag_Bod'>Bod</a> | <span class='optional'>Nobody Owens</a></p>",
|
||||
"<p style='margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>",
|
||||
"<p>Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.</p>",
|
||||
"<p>Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.</p>",
|
||||
@@ -152,7 +152,7 @@
|
||||
],
|
||||
[
|
||||
"<h1>Ancient Europe</h1>",
|
||||
"<p><span class='tags'>Tag:</span> <a name='tag_Europe'>Europe</a></p>",
|
||||
"<p><span class='tags'>Tag:</span> <a name='tag_Europe'>Europe</a> | <span class='optional'>Ancient Europe</a></p>",
|
||||
"<p>Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.</p>",
|
||||
"<p>Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.</p>",
|
||||
"<p>Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.</p>"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"meta": {
|
||||
"projectName": "Lorem Ipsum",
|
||||
"novelAuthor": "lipsum.com",
|
||||
"buildTime": 1706306020,
|
||||
"buildTimeStr": "2024-01-26 22:53:40"
|
||||
"buildTime": 1706648873,
|
||||
"buildTimeStr": "2024-01-30 22:07:53"
|
||||
},
|
||||
"text": {
|
||||
"nwd": [
|
||||
@@ -27,7 +27,7 @@
|
||||
[
|
||||
"##! Prologue",
|
||||
"",
|
||||
"% Synopsis:Explanation from the lipsum.com website.",
|
||||
"% Synopsis: Explanation from the lipsum.com website.",
|
||||
"",
|
||||
"_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
|
||||
],
|
||||
@@ -174,7 +174,7 @@
|
||||
[
|
||||
"# Nobody Owens",
|
||||
"",
|
||||
"@tag: Bod",
|
||||
"@tag: Bod | Nobody Owens",
|
||||
"@plot: Main",
|
||||
"",
|
||||
"Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.",
|
||||
@@ -201,7 +201,7 @@
|
||||
[
|
||||
"# Ancient Europe",
|
||||
"",
|
||||
"@tag: Europe",
|
||||
"@tag: Europe | Ancient Europe",
|
||||
"",
|
||||
"Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.",
|
||||
"",
|
||||
|
||||
@@ -15,7 +15,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t
|
||||
|
||||
##! Prologue
|
||||
|
||||
% Synopsis:Explanation from the lipsum.com website.
|
||||
% Synopsis: Explanation from the lipsum.com website.
|
||||
|
||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
|
||||
@@ -77,11 +77,11 @@ Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et,
|
||||
|
||||
% Exctracted from the lipsum.com website.
|
||||
|
||||
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.
|
||||
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.
|
||||
|
||||
The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.
|
||||
The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.
|
||||
|
||||
Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
|
||||
Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
|
||||
|
||||
## Chapter Two
|
||||
|
||||
@@ -151,7 +151,7 @@ Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacin
|
||||
|
||||
# Nobody Owens
|
||||
|
||||
@tag: Bod
|
||||
@tag: Bod | Nobody Owens
|
||||
@plot: Main
|
||||
|
||||
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
|
||||
@@ -174,7 +174,7 @@ Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam.
|
||||
|
||||
# Ancient Europe
|
||||
|
||||
@tag: Europe
|
||||
@tag: Europe | Ancient Europe
|
||||
|
||||
Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta>
|
||||
<meta:creation-date>2023-07-19T23:55:28</meta:creation-date>
|
||||
<meta:generator>novelWriter/2.1-beta1</meta:generator>
|
||||
<meta:creation-date>2024-01-30T21:56:12</meta:creation-date>
|
||||
<meta:generator>novelWriter/2.3a3</meta:generator>
|
||||
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
||||
<meta:editing-cycles>42</meta:editing-cycles>
|
||||
<meta:editing-duration>P0DT0H31M53S</meta:editing-duration>
|
||||
<meta:editing-cycles>44</meta:editing-cycles>
|
||||
<meta:editing-duration>P0DT0H33M59S</meta:editing-duration>
|
||||
<dc:title>Lorem Ipsum</dc:title>
|
||||
<dc:date>2023-07-19T23:55:28</dc:date>
|
||||
<dc:date>2024-01-30T21:56:12</dc:date>
|
||||
<dc:creator>lipsum.com</dc:creator>
|
||||
</office:meta>
|
||||
<office:font-face-decls>
|
||||
@@ -192,7 +192,7 @@
|
||||
<text:p text:style-name="Text_20_body">Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.</text:p>
|
||||
<text:p text:style-name="P7">Notes: Characters</text:p>
|
||||
<text:h text:style-name="Heading_20_1" text:outline-level="1">Nobody Owens</text:h>
|
||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Tag:</text:span> Bod</text:p>
|
||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Tag:</text:span> Bod | Nobody Owens</text:p>
|
||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
||||
<text:p text:style-name="Text_20_body">Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.</text:p>
|
||||
<text:p text:style-name="Text_20_body">Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.</text:p>
|
||||
@@ -204,7 +204,7 @@
|
||||
<text:p text:style-name="Text_20_body">Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.</text:p>
|
||||
<text:p text:style-name="P7">Notes: World</text:p>
|
||||
<text:h text:style-name="Heading_20_1" text:outline-level="1">Ancient Europe</text:h>
|
||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Tag:</text:span> Europe</text:p>
|
||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Tag:</text:span> Europe | Ancient Europe</text:p>
|
||||
<text:p text:style-name="Text_20_body">Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.</text:p>
|
||||
<text:p text:style-name="Text_20_body">Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.</text:p>
|
||||
<text:p text:style-name="Text_20_body">Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.</text:p>
|
||||
|
||||
@@ -150,7 +150,7 @@ Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacin
|
||||
|
||||
# Nobody Owens
|
||||
|
||||
**Tag:** Bod
|
||||
**Tag:** Bod | Nobody Owens
|
||||
**Plot:** Main
|
||||
|
||||
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
|
||||
@@ -173,7 +173,7 @@ Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam.
|
||||
|
||||
# Ancient Europe
|
||||
|
||||
**Tag:** Europe
|
||||
**Tag:** Europe | Ancient Europe
|
||||
|
||||
Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtCore import QUrl
|
||||
|
||||
from novelwriter.common import (
|
||||
checkBool, checkFloat, checkHandle, checkInt, checkIntTuple, checkPath,
|
||||
checkString, checkStringNone, checkUuid, formatInt, formatTime,
|
||||
formatTimeStamp, formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass,
|
||||
checkBool, checkFloat, checkInt, checkIntTuple, checkPath, checkString,
|
||||
checkStringNone, checkUuid, formatInt, formatTime, formatTimeStamp,
|
||||
formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass,
|
||||
isItemLayout, isItemType, isTitleTag, jsonEncode, makeFileNameSafe, minmax,
|
||||
numberToRoman, NWConfigParser, openExternalPath, readTextFile, simplified,
|
||||
transferCase, xmlIndent, yesNo
|
||||
@@ -151,19 +151,6 @@ def testBaseCommon_checkBool():
|
||||
# END Test testBaseCommon_checkBool
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_checkHandle():
|
||||
"""Test the checkHandle function."""
|
||||
assert checkHandle("None", 1, True) is None
|
||||
assert checkHandle("None", 1, False) == 1
|
||||
assert checkHandle(None, 1, True) is None
|
||||
assert checkHandle(None, 1, False) == 1
|
||||
assert checkHandle("47666c91c7ccf", None, False) == "47666c91c7ccf"
|
||||
assert checkHandle("h7666c91c7ccf", None, False) is None
|
||||
|
||||
# END Test testBaseCommon_checkHandle
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_checkUuid():
|
||||
"""Test the checkUuid function."""
|
||||
@@ -236,7 +223,7 @@ def testBaseCommon_isItemClass():
|
||||
|
||||
# Invalid
|
||||
assert isItemClass("None") is False
|
||||
assert isItemClass(None) is False # type: ignore
|
||||
assert isItemClass(None) is False
|
||||
assert isItemClass("STUFF") is False
|
||||
|
||||
# END Test testBaseCommon_isItemClass
|
||||
@@ -682,6 +669,7 @@ def testBaseCommon_NWConfigParser(fncPath):
|
||||
"list1 = a, b, c\n"
|
||||
"list2 = 17, 18, 19\n"
|
||||
"float1 = 4.2\n"
|
||||
f"path1 = {fncPath}\n"
|
||||
))
|
||||
|
||||
cfgParser = NWConfigParser()
|
||||
@@ -724,6 +712,9 @@ def testBaseCommon_NWConfigParser(fncPath):
|
||||
assert cfgParser.rdFlt("nope", "intopt1", 13.0) == 13.0
|
||||
assert cfgParser.rdFlt("main", "blabla", 13.0) == 13.0
|
||||
|
||||
# Read Path
|
||||
assert cfgParser.rdPath("main", "path1", Path.home()) == fncPath
|
||||
|
||||
# Read String List
|
||||
assert cfgParser.rdStrList("main", "list1", []) == []
|
||||
assert cfgParser.rdStrList("main", "list1", ["x"]) == ["a"]
|
||||
|
||||
@@ -269,26 +269,30 @@ def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd):
|
||||
assert nItem.mainHeading == "H1"
|
||||
|
||||
# Zero Items
|
||||
assert index.checkThese([], cItem) == []
|
||||
assert index.checkThese([], cHandle) == []
|
||||
|
||||
# One Item
|
||||
assert index.checkThese(["@tag"], cItem) == [True]
|
||||
assert index.checkThese(["@who"], cItem) == [False]
|
||||
assert index.checkThese(["@tag"], cHandle) == [True]
|
||||
assert index.checkThese(["@who"], cHandle) == [False]
|
||||
|
||||
# Two Items
|
||||
assert index.checkThese(["@tag", "Jane"], cItem) == [True, True]
|
||||
assert index.checkThese(["@tag", "John"], cItem) == [True, True]
|
||||
assert index.checkThese(["@tag", "Jane"], nItem) == [True, False]
|
||||
assert index.checkThese(["@tag", "John"], nItem) == [True, True]
|
||||
assert index.checkThese(["@pov", "John"], nItem) == [True, False]
|
||||
assert index.checkThese(["@pov", "Jane"], nItem) == [True, True]
|
||||
assert index.checkThese(["@ pov", "Jane"], nItem) == [False, False]
|
||||
assert index.checkThese(["@what", "Jane"], nItem) == [False, False]
|
||||
assert index.checkThese(["@tag", "Jane"], cHandle) == [True, True]
|
||||
assert index.checkThese(["@tag", "John"], cHandle) == [True, True]
|
||||
assert index.checkThese(["@tag", "Jane"], nHandle) == [True, False]
|
||||
assert index.checkThese(["@tag", "John"], nHandle) == [True, True]
|
||||
assert index.checkThese(["@pov", "John"], nHandle) == [True, False]
|
||||
assert index.checkThese(["@pov", "Jane"], nHandle) == [True, True]
|
||||
assert index.checkThese(["@ pov", "Jane"], nHandle) == [False, False]
|
||||
assert index.checkThese(["@what", "Jane"], nHandle) == [False, False]
|
||||
|
||||
# Three Items
|
||||
assert index.checkThese(["@tag", "Jane", "John"], cItem) == [True, True, False]
|
||||
assert index.checkThese(["@who", "Jane", "John"], cItem) == [False, False, False]
|
||||
assert index.checkThese(["@pov", "Jane", "John"], nItem) == [True, True, False]
|
||||
assert index.checkThese(["@tag", "Jane", "John"], cHandle) == [True, True, False]
|
||||
assert index.checkThese(["@who", "Jane", "John"], cHandle) == [False, False, False]
|
||||
assert index.checkThese(["@pov", "Jane", "John"], nHandle) == [True, True, False]
|
||||
|
||||
# Parse a Checked Value
|
||||
assert index.parseValue("Jane | Jane Smith") == ("Jane", "Jane Smith")
|
||||
assert index.parseValue("Jane | Jane Smith") == ("Jane", "Jane Smith")
|
||||
|
||||
project.closeProject()
|
||||
|
||||
@@ -544,8 +548,10 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
||||
|
||||
nHandle = project.newFile("Hello", C.hNovelRoot)
|
||||
cHandle = project.newFile("Jane", C.hCharRoot)
|
||||
dHandle = project.newFile("John", C.hCharRoot)
|
||||
assert isinstance(nHandle, str)
|
||||
assert isinstance(cHandle, str)
|
||||
assert isinstance(dHandle, str)
|
||||
|
||||
assert index.getItemHeader("", "") is None
|
||||
assert index.getItemHeader(C.hNovelRoot, "") is None
|
||||
@@ -554,10 +560,14 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
||||
"# Jane Smith\n"
|
||||
"@tag: Jane\n"
|
||||
))
|
||||
assert index.scanText(dHandle, (
|
||||
"# John Smith\n"
|
||||
"@tag: John\n"
|
||||
))
|
||||
assert index.scanText(nHandle, (
|
||||
"# Hello World!\n"
|
||||
"@pov: Jane\n"
|
||||
"@char: Jane\n\n"
|
||||
"@char: Jane, John\n\n"
|
||||
"% this is a comment\n\n"
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n"
|
||||
@@ -625,7 +635,14 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
||||
# The novel file should now refer to Jane as @pov and @char
|
||||
refs = index.getReferences(nHandle)
|
||||
assert refs["@pov"] == ["Jane"]
|
||||
assert refs["@char"] == ["Jane"]
|
||||
assert refs["@char"] == ["Jane", "John"]
|
||||
|
||||
# getReferenceForHeader
|
||||
# =====================
|
||||
assert index.getReferenceForHeader(nHandle, 1, "@pov") == ["Jane"]
|
||||
assert index.getReferenceForHeader(nHandle, 1, "@char") == ["Jane", "John"]
|
||||
assert index.getReferenceForHeader(nHandle, 1, "@focus") == []
|
||||
assert index.getReferenceForHeader("00000", 1, "@focus") == []
|
||||
|
||||
# getBackReferenceList
|
||||
# ====================
|
||||
@@ -644,7 +661,8 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
||||
# ============
|
||||
|
||||
assert index.getTagSource("Jane") == (cHandle, "T0001")
|
||||
assert index.getTagSource("John") == (None, "T0000")
|
||||
assert index.getTagSource("John") == (dHandle, "T0001")
|
||||
assert index.getTagSource("Hans") == (None, "T0000")
|
||||
|
||||
# getDocumentTags
|
||||
# ===============
|
||||
@@ -653,7 +671,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
||||
|
||||
# getClassTags
|
||||
# ============
|
||||
assert index.getClassTags(nwItemClass.CHARACTER) == ["Jane"]
|
||||
assert index.getClassTags(nwItemClass.CHARACTER) == ["Jane", "John"]
|
||||
|
||||
# getTagsData
|
||||
# ===========
|
||||
@@ -661,6 +679,10 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
||||
"jane", "Jane", "CHARACTER",
|
||||
index.getItemData(cHandle),
|
||||
index.getItemHeader(cHandle, "T0001")
|
||||
), (
|
||||
"john", "John", "CHARACTER",
|
||||
index.getItemData(dHandle),
|
||||
index.getItemHeader(dHandle, "T0001")
|
||||
)]
|
||||
|
||||
# getSingleTag
|
||||
@@ -849,18 +871,21 @@ def testCoreIndex_TagsIndex():
|
||||
content = {
|
||||
"tag1": {
|
||||
"name": "Tag1",
|
||||
"display": "Tag 1",
|
||||
"handle": "0000000000001",
|
||||
"heading": "T0001",
|
||||
"class": nwItemClass.NOVEL.name,
|
||||
},
|
||||
"tag2": {
|
||||
"name": "Tag2",
|
||||
"display": "Tag 2",
|
||||
"handle": "0000000000002",
|
||||
"heading": "T0002",
|
||||
"class": nwItemClass.CHARACTER.name,
|
||||
},
|
||||
"tag3": {
|
||||
"name": "Tag3",
|
||||
"display": "Tag 3",
|
||||
"handle": "0000000000003",
|
||||
"heading": "T0003",
|
||||
"class": nwItemClass.PLOT.name,
|
||||
@@ -868,9 +893,9 @@ def testCoreIndex_TagsIndex():
|
||||
}
|
||||
|
||||
# Add data
|
||||
tagsIndex.add("Tag1", "0000000000001", "T0001", nwItemClass.NOVEL)
|
||||
tagsIndex.add("Tag2", "0000000000002", "T0002", nwItemClass.CHARACTER)
|
||||
tagsIndex.add("Tag3", "0000000000003", "T0003", nwItemClass.PLOT)
|
||||
tagsIndex.add("Tag1", "Tag 1", "0000000000001", "T0001", "NOVEL")
|
||||
tagsIndex.add("Tag2", "Tag 2", "0000000000002", "T0002", "CHARACTER")
|
||||
tagsIndex.add("Tag3", "Tag 3", "0000000000003", "T0003", "PLOT")
|
||||
assert tagsIndex._tags == content
|
||||
|
||||
# Get items
|
||||
@@ -885,6 +910,18 @@ def testCoreIndex_TagsIndex():
|
||||
assert "Tag3" in tagsIndex
|
||||
assert "Tag4" not in tagsIndex
|
||||
|
||||
# Read back names
|
||||
assert tagsIndex.tagName("Tag1") == "Tag1"
|
||||
assert tagsIndex.tagName("Tag2") == "Tag2"
|
||||
assert tagsIndex.tagName("Tag3") == "Tag3"
|
||||
assert tagsIndex.tagName("Tag4") == ""
|
||||
|
||||
# Read back display names
|
||||
assert tagsIndex.tagDisplay("Tag1") == "Tag 1"
|
||||
assert tagsIndex.tagDisplay("Tag2") == "Tag 2"
|
||||
assert tagsIndex.tagDisplay("Tag3") == "Tag 3"
|
||||
assert tagsIndex.tagDisplay("Tag4") == ""
|
||||
|
||||
# Read back handles
|
||||
assert tagsIndex.tagHandle("Tag1") == "0000000000001"
|
||||
assert tagsIndex.tagHandle("Tag2") == "0000000000002"
|
||||
@@ -935,59 +972,39 @@ def testCoreIndex_TagsIndex():
|
||||
with pytest.raises(ValueError):
|
||||
tagsIndex.unpackData({
|
||||
1234: {
|
||||
"name": "1234",
|
||||
"handle": "0000000000001",
|
||||
"heading": "T0001",
|
||||
"class": "NOVEL",
|
||||
}
|
||||
})
|
||||
|
||||
# Missing name
|
||||
with pytest.raises(KeyError):
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"handle": "0000000000001",
|
||||
"heading": "T0001",
|
||||
"class": "NOVEL",
|
||||
}
|
||||
})
|
||||
|
||||
# Missing handle
|
||||
with pytest.raises(KeyError):
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"name": "Tag1",
|
||||
"display": "Tag1",
|
||||
"handle": "0000000000001",
|
||||
"heading": "T0001",
|
||||
"class": "NOVEL",
|
||||
}
|
||||
})
|
||||
|
||||
# Missing heading
|
||||
with pytest.raises(KeyError):
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"name": "Tag1",
|
||||
"handle": "0000000000001",
|
||||
"class": "NOVEL",
|
||||
}
|
||||
})
|
||||
|
||||
# Missing class
|
||||
with pytest.raises(KeyError):
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"name": "Tag1",
|
||||
"handle": "0000000000001",
|
||||
"heading": "T0001",
|
||||
}
|
||||
})
|
||||
|
||||
# Invalid key case
|
||||
# Invalid entry
|
||||
with pytest.raises(ValueError):
|
||||
tagsIndex.unpackData({
|
||||
"Tag1": {
|
||||
"tag1": None
|
||||
})
|
||||
|
||||
# Invalid name
|
||||
with pytest.raises(ValueError):
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"name": 1234,
|
||||
"display": "Tag1",
|
||||
"handle": "0000000000001",
|
||||
"heading": "T0001",
|
||||
"class": "NOVEL",
|
||||
}
|
||||
})
|
||||
|
||||
# Invalid display
|
||||
with pytest.raises(ValueError):
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"name": "Tag1",
|
||||
"handle": "blablabla",
|
||||
"display": 1234,
|
||||
"handle": "0000000000001",
|
||||
"heading": "T0001",
|
||||
"class": "NOVEL",
|
||||
}
|
||||
@@ -998,6 +1015,7 @@ def testCoreIndex_TagsIndex():
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"name": "Tag1",
|
||||
"display": "Tag1",
|
||||
"handle": "blablabla",
|
||||
"heading": "T0001",
|
||||
"class": "NOVEL",
|
||||
@@ -1009,8 +1027,9 @@ def testCoreIndex_TagsIndex():
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"name": "Tag1",
|
||||
"display": "Tag1",
|
||||
"handle": "0000000000001",
|
||||
"heading": "blabla",
|
||||
"heading": "stuff",
|
||||
"class": "NOVEL",
|
||||
}
|
||||
})
|
||||
@@ -1020,9 +1039,10 @@ def testCoreIndex_TagsIndex():
|
||||
tagsIndex.unpackData({
|
||||
"tag1": {
|
||||
"name": "Tag1",
|
||||
"display": "Tag1",
|
||||
"handle": "0000000000001",
|
||||
"heading": "T0001",
|
||||
"class": "blabla",
|
||||
"class": "STUFF",
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ from tools import C, buildTestProject, readFile
|
||||
|
||||
from novelwriter.constants import nwHeadFmt
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.tokenizer import Tokenizer, stripEscape
|
||||
from novelwriter.core.tokenizer import HeadingFormatter, Tokenizer, stripEscape
|
||||
|
||||
|
||||
class BareTokenizer(Tokenizer):
|
||||
@@ -170,14 +170,14 @@ def testCoreToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath):
|
||||
assert tokens.addRootHeading(C.hPlotRoot) is True
|
||||
assert tokens.allMarkdown[-1] == "# Notes: Plot\n\n"
|
||||
assert tokens._tokens[-1] == (
|
||||
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE
|
||||
Tokenizer.T_TITLE, 0, "Notes: Plot", [], Tokenizer.A_CENTRE
|
||||
)
|
||||
|
||||
# Not First Page
|
||||
assert tokens.addRootHeading(C.hPlotRoot) is True
|
||||
assert tokens.allMarkdown[-1] == "# Notes: Plot\n\n"
|
||||
assert tokens._tokens[-1] == (
|
||||
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB
|
||||
Tokenizer.T_TITLE, 0, "Notes: Plot", [], Tokenizer.A_CENTRE | Tokenizer.A_PBB
|
||||
)
|
||||
|
||||
# Set Text
|
||||
@@ -247,8 +247,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_TITLE, 1, "Novel Title", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TITLE, 1, "Novel Title", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "#! Novel Title\n\n"
|
||||
|
||||
@@ -260,8 +260,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Note Title", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Note Title", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "#! Note Title\n\n"
|
||||
|
||||
@@ -276,8 +276,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Novel Title", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Novel Title", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "# Novel Title\n\n"
|
||||
|
||||
@@ -289,8 +289,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Note Title", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Note Title", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "# Note Title\n\n"
|
||||
|
||||
@@ -304,8 +304,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter One", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter One", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "## Chapter One\n\n"
|
||||
|
||||
@@ -316,8 +316,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Heading 2", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD2, 1, "Heading 2", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "## Heading 2\n\n"
|
||||
|
||||
@@ -331,8 +331,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "Scene One", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD3, 1, "Scene One", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "### Scene One\n\n"
|
||||
|
||||
@@ -343,8 +343,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "Heading 3", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD3, 1, "Heading 3", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "### Heading 3\n\n"
|
||||
|
||||
@@ -358,8 +358,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD4, 1, "A Section", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD4, 1, "A Section", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "#### A Section\n\n"
|
||||
|
||||
@@ -370,8 +370,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD4, 1, "Heading 4", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD4, 1, "Heading 4", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "#### Heading 4\n\n"
|
||||
|
||||
@@ -385,8 +385,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_TITLE, 1, "Title", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TITLE, 1, "Title", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "#! Title\n\n"
|
||||
|
||||
@@ -397,8 +397,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "#! Title\n\n"
|
||||
|
||||
@@ -412,8 +412,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_UNNUM, 1, "Prologue", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_UNNUM, 1, "Prologue", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "##! Prologue\n\n"
|
||||
|
||||
@@ -424,8 +424,8 @@ def testCoreToken_HeaderFormat(mockGUI):
|
||||
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Prologue", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD2, 1, "Prologue", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "##! Prologue\n\n"
|
||||
|
||||
@@ -443,8 +443,8 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
tokens._text = "% A comment\n"
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_COMMENT, 0, "A comment", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_COMMENT, 0, "A comment", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "\n"
|
||||
|
||||
@@ -456,14 +456,14 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
tokens._text = "%synopsis: The synopsis\n"
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_SYNOPSIS, 0, "The synopsis", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SYNOPSIS, 0, "The synopsis", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
tokens._text = "% synopsis: The synopsis\n"
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_SYNOPSIS, 0, "The synopsis", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SYNOPSIS, 0, "The synopsis", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "\n"
|
||||
|
||||
@@ -476,8 +476,8 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
tokens._text = "% short: A short description\n"
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_SHORT, 0, "A short description", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SHORT, 0, "A short description", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "\n"
|
||||
|
||||
@@ -489,8 +489,8 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
tokens._text = "@char: Bod\n"
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_KEYWORD, 0, "char: Bod", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_KEYWORD, 0, "char: Bod", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "\n"
|
||||
|
||||
@@ -504,10 +504,10 @@ def testCoreToken_MetaFormat(mockGUI):
|
||||
styMid = Tokenizer.A_NONE | Tokenizer.A_Z_BTMMRG | Tokenizer.A_Z_TOPMRG
|
||||
styBtm = Tokenizer.A_NONE | Tokenizer.A_Z_TOPMRG
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_KEYWORD, 0, "pov: Bod", None, styTop),
|
||||
(Tokenizer.T_KEYWORD, 0, "plot: Main", None, styMid),
|
||||
(Tokenizer.T_KEYWORD, 0, "location: Europe", None, styBtm),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_KEYWORD, 0, "pov: Bod", [], styTop),
|
||||
(Tokenizer.T_KEYWORD, 0, "plot: Main", [], styMid),
|
||||
(Tokenizer.T_KEYWORD, 0, "location: Europe", [], styBtm),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
||||
|
||||
@@ -537,22 +537,22 @@ def testCoreToken_MarginFormat(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_TEXT, 0, "Some regular text", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Some left-aligned text", [], Tokenizer.A_LEFT),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Some right-aligned text", [], Tokenizer.A_RIGHT),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Some centered text", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Left-indented block", [], Tokenizer.A_IND_L),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Right-indented block", [], Tokenizer.A_IND_R),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Double-indented block", [], dblIndent),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "Right-indent, right-aligned", [], rIndAlign),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == (
|
||||
"Some regular text\n\n"
|
||||
@@ -672,18 +672,18 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_TEXT, 0, "Some plain text", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 0, "on two lines", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "Some plain text\non two lines\n\n\n\n"
|
||||
|
||||
tokens.setBodyText(False)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "\n\n\n"
|
||||
tokens.setBodyText(True)
|
||||
@@ -701,7 +701,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "Some **bolded text** on this lines\n\n"
|
||||
|
||||
@@ -717,7 +717,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "Some _italic text_ on this lines\n\n"
|
||||
|
||||
@@ -735,7 +735,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "Some **_bold italic text_** on this lines\n\n"
|
||||
|
||||
@@ -751,7 +751,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == "Some ~~strikethrough text~~ on this lines\n\n"
|
||||
|
||||
@@ -771,7 +771,7 @@ def testCoreToken_TextFormat(mockGUI):
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 0, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
assert tokens.allMarkdown[-1] == (
|
||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
|
||||
@@ -792,12 +792,12 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
# ========
|
||||
|
||||
correctResp = [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 2, "Title Two", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 2, "Title Two", [], Tokenizer.A_CENTRE | Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 2, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 2, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Command wo/Space
|
||||
@@ -840,13 +840,13 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Multiple Empty Paragraphs
|
||||
@@ -860,13 +860,13 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Three Skips
|
||||
@@ -877,15 +877,15 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Malformed Command, Case 1
|
||||
@@ -896,12 +896,12 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Malformed Command, Case 2
|
||||
@@ -912,12 +912,12 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Malformed Command, Case 3
|
||||
@@ -928,12 +928,12 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Empty Paragraph and Page Break
|
||||
@@ -948,14 +948,14 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], 0),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Multiple Skip
|
||||
@@ -967,16 +967,16 @@ def testCoreToken_SpecialFormat(mockGUI):
|
||||
)
|
||||
tokens.tokenizeText()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "Title One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], 0),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# END Test testCoreToken_SpecialFormat
|
||||
@@ -1019,8 +1019,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "T: Part One", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "T: Part One", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H1: Title, Not First Page
|
||||
@@ -1030,8 +1030,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "T: Part One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD1, 1, "T: Part One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Chapters
|
||||
@@ -1043,8 +1043,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "C: Chapter One", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD2, 1, "C: Chapter One", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H2: Unnumbered Chapter
|
||||
@@ -1053,8 +1053,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_UNNUM, 1, "U: Prologue", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_UNNUM, 1, "U: Prologue", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H2: Chapter Word Number
|
||||
@@ -1064,8 +1064,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter One", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter One", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H2: Chapter Roman Number Upper Case
|
||||
@@ -1074,8 +1074,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter II", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter II", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H2: Chapter Roman Number Lower Case
|
||||
@@ -1084,8 +1084,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter iii", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter iii", [], Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Scenes
|
||||
@@ -1097,8 +1097,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "S: Scene One", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD3, 1, "S: Scene One", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene Hidden wo/Format
|
||||
@@ -1107,8 +1107,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene wo/Format, first
|
||||
@@ -1118,8 +1118,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene wo/Format, not first
|
||||
@@ -1129,8 +1129,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene Separator, first
|
||||
@@ -1140,8 +1140,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene Separator, not first
|
||||
@@ -1151,8 +1151,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_SEP, 1, "* * *", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SEP, 1, "* * *", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene w/Absolute Number
|
||||
@@ -1163,8 +1163,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "Scene 1", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD3, 1, "Scene 1", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene w/Chapter Number
|
||||
@@ -1175,8 +1175,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "Scene 3.2", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD3, 1, "Scene 3.2", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Sections
|
||||
@@ -1188,8 +1188,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H4: Section Visible wo/Format
|
||||
@@ -1198,8 +1198,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SKIP, 1, "", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H4: Section w/Format
|
||||
@@ -1208,8 +1208,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_HEAD4, 1, "X: A Section", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_HEAD4, 1, "X: A Section", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H4: Section Separator
|
||||
@@ -1218,8 +1218,8 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
tokens.tokenizeText()
|
||||
tokens.doHeaders()
|
||||
assert tokens._tokens == [
|
||||
(Tokenizer.T_SEP, 1, "* * *", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_SEP, 1, "* * *", [], Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", [], Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Check the first scene detector
|
||||
@@ -1231,3 +1231,105 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
assert tokens._firstScene is False
|
||||
|
||||
# END Test testCoreToken_ProcessHeaders
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_HeadingFormatter(fncPath, mockRnd):
|
||||
"""Check the HeadingFormatter class."""
|
||||
project = NWProject()
|
||||
project.setProjectLang("en_GB")
|
||||
mockRnd.reset()
|
||||
buildTestProject(project, fncPath)
|
||||
|
||||
nHandle = project.newFile("Hello", C.hNovelRoot)
|
||||
cHandle = project.newFile("Jane", C.hCharRoot)
|
||||
dHandle = project.newFile("John", C.hCharRoot)
|
||||
|
||||
assert isinstance(nHandle, str)
|
||||
assert isinstance(cHandle, str)
|
||||
assert isinstance(dHandle, str)
|
||||
|
||||
assert project.index.scanText(cHandle, (
|
||||
"# Jane Smith\n"
|
||||
"@tag: Jane | Jane Smith\n"
|
||||
))
|
||||
assert project.index.scanText(dHandle, (
|
||||
"# John Smith\n"
|
||||
"@tag: John | John Smith\n"
|
||||
))
|
||||
assert project.index.scanText(nHandle, (
|
||||
"## Hi Bob\n"
|
||||
"@pov: Jane\n"
|
||||
"@focus: John\n"
|
||||
"@char: Jane, John\n\n"
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n"
|
||||
))
|
||||
|
||||
formatter = HeadingFormatter(project)
|
||||
formatter.setHandle(nHandle)
|
||||
|
||||
# Counters
|
||||
# ========
|
||||
cFormat = (
|
||||
f"Chapter {nwHeadFmt.CH_NUM}.{nwHeadFmt.SC_NUM} - "
|
||||
f"Scene {nwHeadFmt.SC_ABS} - {nwHeadFmt.TITLE}"
|
||||
)
|
||||
|
||||
# Initial
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter 0.0 - Scene 0 - Hi Bob"
|
||||
|
||||
# First Chapter and Scene
|
||||
formatter.incChapter()
|
||||
formatter.incScene()
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter 1.1 - Scene 1 - Hi Bob"
|
||||
|
||||
# Next Scenes
|
||||
formatter.incScene()
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter 1.2 - Scene 2 - Hi Bob"
|
||||
formatter.incScene()
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter 1.3 - Scene 3 - Hi Bob"
|
||||
formatter.incScene()
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter 1.4 - Scene 4 - Hi Bob"
|
||||
|
||||
# Next Chapter and Scene
|
||||
formatter.incChapter()
|
||||
formatter.resetScene()
|
||||
formatter.incScene()
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter 2.1 - Scene 5 - Hi Bob"
|
||||
|
||||
# Special Formats
|
||||
# ===============
|
||||
formatter._chCount = 2
|
||||
formatter._scChCount = 3
|
||||
formatter._scAbsCount = 8
|
||||
|
||||
# Chapter Number Word
|
||||
cFormat = f"Chapter {nwHeadFmt.CH_WORD}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.TITLE}"
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter Two, Scene 3 - Hi Bob"
|
||||
|
||||
# Chapter Upper Case Roman
|
||||
cFormat = f"Chapter {nwHeadFmt.CH_ROMU}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.TITLE}"
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter II, Scene 3 - Hi Bob"
|
||||
|
||||
# Chapter Lower Case Roman
|
||||
cFormat = f"Chapter {nwHeadFmt.CH_ROML}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.TITLE}"
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter ii, Scene 3 - Hi Bob"
|
||||
|
||||
# Chapter w/PoV Character
|
||||
cFormat = f"Chapter {nwHeadFmt.CH_NUM}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.CHAR_POV}"
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter 2, Scene 3 - Jane Smith"
|
||||
|
||||
# Chapter w/Focus Character
|
||||
cFormat = f"Chapter {nwHeadFmt.CH_NUM}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.CHAR_FOCUS}"
|
||||
assert formatter.apply(cFormat, "Hi Bob", 1) == "Chapter 2, Scene 3 - John Smith"
|
||||
|
||||
# Chapter w/Fallback PoV
|
||||
cFormat = f"Chapter {nwHeadFmt.CH_NUM}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.CHAR_POV}"
|
||||
assert formatter.apply(cFormat, "Hi Bob", 0) == "Chapter 2, Scene 3 - Point of View"
|
||||
|
||||
# Chapter w/Fallback Focus
|
||||
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
|
||||
|
||||
@@ -1615,7 +1615,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
origText = nwGUI.docEditor.getText()
|
||||
|
||||
# Select the Word "est"
|
||||
nwGUI.docEditor.setCursorPosition(630)
|
||||
nwGUI.docEditor.setCursorPosition(645)
|
||||
nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor)
|
||||
cursor = nwGUI.docEditor.textCursor()
|
||||
assert cursor.selectedText() == "est"
|
||||
@@ -1628,11 +1628,11 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
# Find next by enter key
|
||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True)
|
||||
qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=KEY_DELAY)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1284) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1299) < 3
|
||||
|
||||
# Find next by button
|
||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1498) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1513) < 3
|
||||
|
||||
# Activate loop search
|
||||
nwGUI.docEditor.docSearch.toggleLoop.activate(QAction.Trigger)
|
||||
@@ -1641,7 +1641,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
|
||||
# Find next by menu Search > Find Next
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 647) < 3
|
||||
|
||||
# Close search
|
||||
nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger)
|
||||
@@ -1680,13 +1680,13 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
# Set valid RegEx
|
||||
nwGUI.docEditor.docSearch.setSearchText(r"\bSus")
|
||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 223) < 3
|
||||
|
||||
# Find next and then prev
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 309) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 324) < 3
|
||||
nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 223) < 3
|
||||
|
||||
# Make RegEx case sensitive
|
||||
nwGUI.docEditor.docSearch.toggleCase.activate(QAction.Trigger)
|
||||
@@ -1695,11 +1695,11 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
|
||||
# Find next/prev (one result)
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 611) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 626) < 3
|
||||
nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 611) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 626) < 3
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 611) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 626) < 3
|
||||
|
||||
# Trigger replace
|
||||
nwGUI.mainMenu.aReplace.activate(QAction.Trigger)
|
||||
@@ -1716,22 +1716,22 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
assert nwGUI.docEditor.docSearch.doMatchCap is True
|
||||
|
||||
# Replace "Sus" with "Foo" via menu
|
||||
nwGUI.docEditor.setCursorPosition(590)
|
||||
nwGUI.docEditor.setCursorPosition(605)
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
nwGUI.mainMenu.aReplaceNext.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[608:619] == "Foopendisse"
|
||||
assert nwGUI.docEditor.getText()[623:634] == "Foopendisse"
|
||||
|
||||
# Find next/prev to loop file
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 223) < 3
|
||||
nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1790) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1805) < 3
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 223) < 3
|
||||
|
||||
# Replace "sus" with "foo" via replace button
|
||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||
assert nwGUI.docEditor.getText()[205:213] == "foocipit"
|
||||
assert nwGUI.docEditor.getText()[220:228] == "foocipit"
|
||||
|
||||
# Revert last two replaces
|
||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO)
|
||||
@@ -1745,7 +1745,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
|
||||
# Close search and select "est" again
|
||||
nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger)
|
||||
nwGUI.docEditor.setCursorPosition(630)
|
||||
nwGUI.docEditor.setCursorPosition(645)
|
||||
nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor)
|
||||
cursor = nwGUI.docEditor.textCursor()
|
||||
assert cursor.selectedText() == "est"
|
||||
@@ -1762,9 +1762,9 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
|
||||
# Only one match
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 647) < 3
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 647) < 3
|
||||
|
||||
# Enable next doc search
|
||||
nwGUI.docEditor.docSearch.toggleProject.activate(QAction.Trigger)
|
||||
|
||||
@@ -46,146 +46,146 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
|
||||
# Split By Chapter
|
||||
assert nwGUI.openDocument("4c4f28287af27") is True
|
||||
nwGUI.docEditor.setCursorPosition(42)
|
||||
cleanText = nwGUI.docEditor.getText()[39:86]
|
||||
nwGUI.docEditor.setCursorPosition(57)
|
||||
cleanText = nwGUI.docEditor.getText()[54:101]
|
||||
|
||||
# Bold
|
||||
nwGUI.mainMenu.aFmtBold.activate(QAction.Trigger)
|
||||
fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:105] == fmtStr
|
||||
nwGUI.mainMenu.aFmtBold.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Italic
|
||||
nwGUI.mainMenu.aFmtItalic.activate(QAction.Trigger)
|
||||
fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:103] == fmtStr
|
||||
nwGUI.mainMenu.aFmtItalic.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Strikethrough
|
||||
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
|
||||
fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:105] == fmtStr
|
||||
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Should get us back to plain
|
||||
nwGUI.mainMenu.aFmtBold.activate(QAction.Trigger)
|
||||
nwGUI.mainMenu.aFmtItalic.activate(QAction.Trigger)
|
||||
nwGUI.mainMenu.aFmtItalic.activate(QAction.Trigger)
|
||||
nwGUI.mainMenu.aFmtBold.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Double Quotes
|
||||
nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger)
|
||||
fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:103] == fmtStr
|
||||
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Single Quotes
|
||||
nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger)
|
||||
fmtStr = "‘Pellentesque’ nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:103] == fmtStr
|
||||
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Block Formats
|
||||
# =============
|
||||
# cSpell:ignore Pellentesque erat nulla posuere commodo
|
||||
nwGUI.docEditor.setCursorPosition(42)
|
||||
nwGUI.docEditor.setCursorPosition(57)
|
||||
|
||||
# Header 1
|
||||
nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger)
|
||||
fmtStr = "# Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:103] == fmtStr
|
||||
|
||||
# Header 2
|
||||
nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger)
|
||||
fmtStr = "## Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:89] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:104] == fmtStr
|
||||
|
||||
# Header 3
|
||||
nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger)
|
||||
fmtStr = "### Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:105] == fmtStr
|
||||
|
||||
# Header 4
|
||||
nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger)
|
||||
fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:91] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:106] == fmtStr
|
||||
|
||||
# Title Format
|
||||
nwGUI.mainMenu.aFmtTitle.activate(QAction.Trigger)
|
||||
fmtStr = "#! Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:89] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:104] == fmtStr
|
||||
|
||||
# Unnumbered Chapter
|
||||
nwGUI.mainMenu.aFmtUnNum.activate(QAction.Trigger)
|
||||
fmtStr = "##! Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:105] == fmtStr
|
||||
|
||||
# Clear Format
|
||||
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Comment On
|
||||
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
|
||||
fmtStr = "% Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:103] == fmtStr
|
||||
|
||||
# Comment Off
|
||||
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Check comment with no space before text
|
||||
nwGUI.docEditor.setCursorPosition(39)
|
||||
nwGUI.docEditor.setCursorPosition(54)
|
||||
assert nwGUI.docEditor.insertText("%")
|
||||
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:87] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:102] == fmtStr
|
||||
|
||||
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Undo/Redo
|
||||
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
||||
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
|
||||
assert nwGUI.docEditor.getText()[39:87] == fmtStr
|
||||
assert nwGUI.docEditor.getText()[54:102] == fmtStr
|
||||
nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||
assert nwGUI.docEditor.getText()[54:101] == cleanText
|
||||
|
||||
# Cut, Copy and Paste
|
||||
nwGUI.docEditor.setCursorPosition(39)
|
||||
nwGUI.docEditor.setCursorPosition(54)
|
||||
nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor)
|
||||
|
||||
nwGUI.mainMenu.aEditCut.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:89] == (
|
||||
assert nwGUI.docEditor.getText()[54:104] == (
|
||||
" nec erat ut nulla posuere commodo. Curabitur nisi"
|
||||
)
|
||||
|
||||
nwGUI.mainMenu.aEditPaste.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:89] == (
|
||||
assert nwGUI.docEditor.getText()[54:104] == (
|
||||
"Pellentesque nec erat ut nulla posuere commodo. Cu"
|
||||
)
|
||||
|
||||
nwGUI.docEditor.setCursorPosition(39)
|
||||
nwGUI.docEditor.setCursorPosition(54)
|
||||
nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor)
|
||||
|
||||
nwGUI.mainMenu.aEditCopy.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:89] == (
|
||||
assert nwGUI.docEditor.getText()[54:104] == (
|
||||
"Pellentesque nec erat ut nulla posuere commodo. Cu"
|
||||
)
|
||||
|
||||
nwGUI.docEditor.setCursorPosition(39)
|
||||
nwGUI.docEditor.setCursorPosition(54)
|
||||
nwGUI.mainMenu.aEditPaste.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText()[39:89] == (
|
||||
assert nwGUI.docEditor.getText()[54:104] == (
|
||||
"PellentesquePellentesque nec erat ut nulla posuere"
|
||||
)
|
||||
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
||||
|
||||
# Select Paragraph/All
|
||||
nwGUI.docEditor.setCursorPosition(42)
|
||||
nwGUI.docEditor.setCursorPosition(57)
|
||||
nwGUI.mainMenu.aSelectPar.activate(QAction.Trigger)
|
||||
cursor = nwGUI.docEditor.textCursor()
|
||||
assert cursor.selectedText() == (
|
||||
@@ -198,10 +198,10 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
"nunc lacus, imperdiet nec posuere ac, interdum non lectus."
|
||||
)
|
||||
|
||||
nwGUI.docEditor.setCursorPosition(42)
|
||||
nwGUI.docEditor.setCursorPosition(57)
|
||||
nwGUI.mainMenu.aSelectAll.activate(QAction.Trigger)
|
||||
cursor = nwGUI.docEditor.textCursor()
|
||||
assert len(cursor.selectedText()) == 1895
|
||||
assert len(cursor.selectedText()) == 1910
|
||||
|
||||
# Clear the Text
|
||||
nwGUI.docEditor.clear()
|
||||
@@ -349,7 +349,7 @@ def testGuiMenu_ContextMenus(qtbot, nwGUI, prjLipsum):
|
||||
|
||||
# Editor Context Menu
|
||||
cursor = nwGUI.docEditor.textCursor()
|
||||
cursor.setPosition(112)
|
||||
cursor.setPosition(127)
|
||||
nwGUI.docEditor.setTextCursor(cursor)
|
||||
rect = nwGUI.docEditor.cursorRect()
|
||||
|
||||
@@ -376,7 +376,7 @@ def testGuiMenu_ContextMenus(qtbot, nwGUI, prjLipsum):
|
||||
assert nwGUI.viewDocument("4c4f28287af27")
|
||||
|
||||
cursor = nwGUI.docViewer.textCursor()
|
||||
cursor.setPosition(112)
|
||||
cursor.setPosition(127)
|
||||
nwGUI.docViewer.setTextCursor(cursor)
|
||||
rect = nwGUI.docViewer.cursorRect()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user