novelWriter 2.5.1__py3-none-any.whl → 2.6b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/METADATA +2 -1
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/RECORD +61 -56
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/WHEEL +1 -1
 - novelwriter/__init__.py +3 -3
 - novelwriter/assets/i18n/project_en_GB.json +1 -0
 - novelwriter/assets/icons/typicons_dark/icons.conf +1 -0
 - novelwriter/assets/icons/typicons_dark/mixed_copy.svg +4 -0
 - novelwriter/assets/icons/typicons_light/icons.conf +1 -0
 - novelwriter/assets/icons/typicons_light/mixed_copy.svg +4 -0
 - novelwriter/assets/manual.pdf +0 -0
 - novelwriter/assets/sample.zip +0 -0
 - novelwriter/assets/themes/default_light.conf +2 -2
 - novelwriter/common.py +63 -0
 - novelwriter/config.py +10 -3
 - novelwriter/constants.py +153 -60
 - novelwriter/core/buildsettings.py +66 -39
 - novelwriter/core/coretools.py +34 -22
 - novelwriter/core/docbuild.py +130 -169
 - novelwriter/core/index.py +29 -18
 - novelwriter/core/item.py +2 -2
 - novelwriter/core/options.py +4 -1
 - novelwriter/core/spellcheck.py +9 -14
 - novelwriter/dialogs/preferences.py +45 -32
 - novelwriter/dialogs/projectsettings.py +3 -3
 - novelwriter/enum.py +29 -23
 - novelwriter/extensions/configlayout.py +24 -11
 - novelwriter/extensions/modified.py +13 -1
 - novelwriter/extensions/pagedsidebar.py +5 -5
 - novelwriter/formats/shared.py +155 -0
 - novelwriter/formats/todocx.py +1195 -0
 - novelwriter/formats/tohtml.py +452 -0
 - novelwriter/{core → formats}/tokenizer.py +483 -485
 - novelwriter/formats/tomarkdown.py +217 -0
 - novelwriter/{core → formats}/toodt.py +270 -320
 - novelwriter/formats/toqdoc.py +436 -0
 - novelwriter/formats/toraw.py +91 -0
 - novelwriter/gui/doceditor.py +240 -193
 - novelwriter/gui/dochighlight.py +96 -84
 - novelwriter/gui/docviewer.py +56 -30
 - novelwriter/gui/docviewerpanel.py +3 -3
 - novelwriter/gui/editordocument.py +17 -2
 - novelwriter/gui/itemdetails.py +8 -4
 - novelwriter/gui/mainmenu.py +121 -60
 - novelwriter/gui/noveltree.py +35 -37
 - novelwriter/gui/outline.py +186 -238
 - novelwriter/gui/projtree.py +142 -131
 - novelwriter/gui/sidebar.py +7 -6
 - novelwriter/gui/theme.py +5 -4
 - novelwriter/guimain.py +43 -155
 - novelwriter/shared.py +14 -4
 - novelwriter/text/counting.py +2 -0
 - novelwriter/text/patterns.py +155 -59
 - novelwriter/tools/manusbuild.py +1 -1
 - novelwriter/tools/manuscript.py +121 -78
 - novelwriter/tools/manussettings.py +403 -260
 - novelwriter/tools/welcome.py +4 -4
 - novelwriter/tools/writingstats.py +3 -3
 - novelwriter/types.py +16 -6
 - novelwriter/core/tohtml.py +0 -530
 - novelwriter/core/tomarkdown.py +0 -252
 - novelwriter/core/toqdoc.py +0 -419
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/LICENSE.md +0 -0
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/entry_points.txt +0 -0
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/top_level.txt +0 -0
 
    
        novelwriter/core/docbuild.py
    CHANGED
    
    | 
         @@ -35,13 +35,15 @@ from novelwriter.constants import nwLabels 
     | 
|
| 
       35 
35 
     | 
    
         
             
            from novelwriter.core.buildsettings import BuildSettings
         
     | 
| 
       36 
36 
     | 
    
         
             
            from novelwriter.core.item import NWItem
         
     | 
| 
       37 
37 
     | 
    
         
             
            from novelwriter.core.project import NWProject
         
     | 
| 
       38 
     | 
    
         
            -
            from novelwriter.core.tohtml import ToHtml
         
     | 
| 
       39 
     | 
    
         
            -
            from novelwriter.core.tokenizer import Tokenizer
         
     | 
| 
       40 
     | 
    
         
            -
            from novelwriter.core.tomarkdown import ToMarkdown
         
     | 
| 
       41 
     | 
    
         
            -
            from novelwriter.core.toodt import ToOdt
         
     | 
| 
       42 
     | 
    
         
            -
            from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
         
     | 
| 
       43 
38 
     | 
    
         
             
            from novelwriter.enum import nwBuildFmt
         
     | 
| 
       44 
39 
     | 
    
         
             
            from novelwriter.error import formatException, logException
         
     | 
| 
      
 40 
     | 
    
         
            +
            from novelwriter.formats.todocx import ToDocX
         
     | 
| 
      
 41 
     | 
    
         
            +
            from novelwriter.formats.tohtml import ToHtml
         
     | 
| 
      
 42 
     | 
    
         
            +
            from novelwriter.formats.tokenizer import Tokenizer
         
     | 
| 
      
 43 
     | 
    
         
            +
            from novelwriter.formats.tomarkdown import ToMarkdown
         
     | 
| 
      
 44 
     | 
    
         
            +
            from novelwriter.formats.toodt import ToOdt
         
     | 
| 
      
 45 
     | 
    
         
            +
            from novelwriter.formats.toqdoc import ToQTextDocument
         
     | 
| 
      
 46 
     | 
    
         
            +
            from novelwriter.formats.toraw import ToRaw
         
     | 
| 
       45 
47 
     | 
    
         | 
| 
       46 
48 
     | 
    
         
             
            logger = logging.getLogger(__name__)
         
     | 
| 
       47 
49 
     | 
    
         | 
| 
         @@ -85,20 +87,6 @@ class NWBuildDocument: 
     | 
|
| 
       85 
87 
     | 
    
         
             
                    """
         
     | 
| 
       86 
88 
     | 
    
         
             
                    return self._cache
         
     | 
| 
       87 
89 
     | 
    
         | 
| 
       88 
     | 
    
         
            -
                ##
         
     | 
| 
       89 
     | 
    
         
            -
                #  Setters
         
     | 
| 
       90 
     | 
    
         
            -
                ##
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                def setCountEnabled(self, state: bool) -> None:
         
     | 
| 
       93 
     | 
    
         
            -
                    """Turn on/off stats for builds."""
         
     | 
| 
       94 
     | 
    
         
            -
                    self._count = state
         
     | 
| 
       95 
     | 
    
         
            -
                    return
         
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
                def setBuildOutline(self, state: bool) -> None:
         
     | 
| 
       98 
     | 
    
         
            -
                    """Turn on/off outline for builds."""
         
     | 
| 
       99 
     | 
    
         
            -
                    self._outline = state
         
     | 
| 
       100 
     | 
    
         
            -
                    return
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
90 
     | 
    
         
             
                ##
         
     | 
| 
       103 
91 
     | 
    
         
             
                #  Special Methods
         
     | 
| 
       104 
92 
     | 
    
         
             
                ##
         
     | 
| 
         @@ -125,182 +113,116 @@ class NWBuildDocument: 
     | 
|
| 
       125 
113 
     | 
    
         
             
                            self._queue.append(item.itemHandle)
         
     | 
| 
       126 
114 
     | 
    
         
             
                    return
         
     | 
| 
       127 
115 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
                def iterBuildPreview(self,  
     | 
| 
      
 116 
     | 
    
         
            +
                def iterBuildPreview(self, newPage: bool) -> Iterable[tuple[int, bool]]:
         
     | 
| 
       129 
117 
     | 
    
         
             
                    """Build a preview QTextDocument."""
         
     | 
| 
       130 
118 
     | 
    
         
             
                    makeObj = ToQTextDocument(self._project)
         
     | 
| 
       131 
119 
     | 
    
         
             
                    filtered = self._setupBuild(makeObj)
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
                    self._outline = True
         
     | 
| 
       134 
     | 
    
         
            -
                    self._count = True
         
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
       136 
     | 
    
         
            -
                    font = QFont()
         
     | 
| 
       137 
     | 
    
         
            -
                    font.fromString(self._build.getStr("format.textFont"))
         
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                    makeObj.initDocument(font, theme)
         
     | 
| 
       140 
     | 
    
         
            -
                    for i, tHandle in enumerate(self._queue):
         
     | 
| 
       141 
     | 
    
         
            -
                        self._error = None
         
     | 
| 
       142 
     | 
    
         
            -
                        if filtered.get(tHandle, (False, 0))[0]:
         
     | 
| 
       143 
     | 
    
         
            -
                            yield i, self._doBuild(makeObj, tHandle)
         
     | 
| 
       144 
     | 
    
         
            -
                        else:
         
     | 
| 
       145 
     | 
    
         
            -
                            yield i, False
         
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                    makeObj.appendFootnotes()
         
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
                    self._error = None
         
     | 
| 
       150 
     | 
    
         
            -
                    self._cache = makeObj
         
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
                    return
         
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
                def iterBuild(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]:
         
     | 
| 
       155 
     | 
    
         
            -
                    """Wrapper for builders based on format."""
         
     | 
| 
       156 
     | 
    
         
            -
                    if bFormat in (nwBuildFmt.ODT, nwBuildFmt.FODT):
         
     | 
| 
       157 
     | 
    
         
            -
                        yield from self.iterBuildOpenDocument(path, bFormat == nwBuildFmt.FODT)
         
     | 
| 
       158 
     | 
    
         
            -
                    elif bFormat in (nwBuildFmt.HTML, nwBuildFmt.J_HTML):
         
     | 
| 
       159 
     | 
    
         
            -
                        yield from self.iterBuildHTML(path, asJson=bFormat == nwBuildFmt.J_HTML)
         
     | 
| 
       160 
     | 
    
         
            -
                    elif bFormat in (nwBuildFmt.STD_MD, nwBuildFmt.EXT_MD):
         
     | 
| 
       161 
     | 
    
         
            -
                        yield from self.iterBuildMarkdown(path, bFormat == nwBuildFmt.EXT_MD)
         
     | 
| 
       162 
     | 
    
         
            -
                    elif bFormat in (nwBuildFmt.NWD, nwBuildFmt.J_NWD):
         
     | 
| 
       163 
     | 
    
         
            -
                        yield from self.iterBuildNWD(path, asJson=bFormat == nwBuildFmt.J_NWD)
         
     | 
| 
       164 
     | 
    
         
            -
                    return
         
     | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
       166 
     | 
    
         
            -
                def iterBuildOpenDocument(self, path: Path, isFlat: bool) -> Iterable[tuple[int, bool]]:
         
     | 
| 
       167 
     | 
    
         
            -
                    """Build an Open Document file."""
         
     | 
| 
       168 
     | 
    
         
            -
                    makeObj = ToOdt(self._project, isFlat=isFlat)
         
     | 
| 
       169 
     | 
    
         
            -
                    filtered = self._setupBuild(makeObj)
         
     | 
| 
       170 
120 
     | 
    
         
             
                    makeObj.initDocument()
         
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
                     
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
     | 
    
         
            -
                        if filtered.get(tHandle, (False, 0))[0]:
         
     | 
| 
       175 
     | 
    
         
            -
                            yield i, self._doBuild(makeObj, tHandle)
         
     | 
| 
       176 
     | 
    
         
            -
                        else:
         
     | 
| 
       177 
     | 
    
         
            -
                            yield i, False
         
     | 
| 
       178 
     | 
    
         
            -
             
     | 
| 
      
 121 
     | 
    
         
            +
                    makeObj.setShowNewPage(newPage)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    self._outline = True
         
     | 
| 
      
 123 
     | 
    
         
            +
                    yield from self._iterBuild(makeObj, filtered)
         
     | 
| 
       179 
124 
     | 
    
         
             
                    makeObj.closeDocument()
         
     | 
| 
       180 
     | 
    
         
            -
             
     | 
| 
       181 
125 
     | 
    
         
             
                    self._error = None
         
     | 
| 
       182 
126 
     | 
    
         
             
                    self._cache = makeObj
         
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
                    try:
         
     | 
| 
       185 
     | 
    
         
            -
                        if isFlat:
         
     | 
| 
       186 
     | 
    
         
            -
                            makeObj.saveFlatXML(path)
         
     | 
| 
       187 
     | 
    
         
            -
                        else:
         
     | 
| 
       188 
     | 
    
         
            -
                            makeObj.saveOpenDocText(path)
         
     | 
| 
       189 
     | 
    
         
            -
                    except Exception as exc:
         
     | 
| 
       190 
     | 
    
         
            -
                        logException()
         
     | 
| 
       191 
     | 
    
         
            -
                        self._error = formatException(exc)
         
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
       193 
127 
     | 
    
         
             
                    return
         
     | 
| 
       194 
128 
     | 
    
         | 
| 
       195 
     | 
    
         
            -
                def  
     | 
| 
       196 
     | 
    
         
            -
                    """ 
     | 
| 
       197 
     | 
    
         
            -
                    is used for generating build previews.
         
     | 
| 
       198 
     | 
    
         
            -
                    """
         
     | 
| 
       199 
     | 
    
         
            -
                    makeObj = ToHtml(self._project)
         
     | 
| 
       200 
     | 
    
         
            -
                    filtered = self._setupBuild(makeObj)
         
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
                    for i, tHandle in enumerate(self._queue):
         
     | 
| 
       203 
     | 
    
         
            -
                        self._error = None
         
     | 
| 
       204 
     | 
    
         
            -
                        if filtered.get(tHandle, (False, 0))[0]:
         
     | 
| 
       205 
     | 
    
         
            -
                            yield i, self._doBuild(makeObj, tHandle)
         
     | 
| 
       206 
     | 
    
         
            -
                        else:
         
     | 
| 
       207 
     | 
    
         
            -
                            yield i, False
         
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
                    makeObj.appendFootnotes()
         
     | 
| 
       210 
     | 
    
         
            -
             
     | 
| 
       211 
     | 
    
         
            -
                    if not self._build.getBool("html.preserveTabs"):
         
     | 
| 
       212 
     | 
    
         
            -
                        makeObj.replaceTabs()
         
     | 
| 
       213 
     | 
    
         
            -
             
     | 
| 
      
 129 
     | 
    
         
            +
                def iterBuildDocument(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]:
         
     | 
| 
      
 130 
     | 
    
         
            +
                    """Wrapper for builders based on format."""
         
     | 
| 
       214 
131 
     | 
    
         
             
                    self._error = None
         
     | 
| 
       215 
     | 
    
         
            -
                    self._cache =  
     | 
| 
       216 
     | 
    
         
            -
             
     | 
| 
       217 
     | 
    
         
            -
                    if isinstance(path, Path):
         
     | 
| 
       218 
     | 
    
         
            -
                        try:
         
     | 
| 
       219 
     | 
    
         
            -
                            if asJson:
         
     | 
| 
       220 
     | 
    
         
            -
                                makeObj.saveHtmlJson(path)
         
     | 
| 
       221 
     | 
    
         
            -
                            else:
         
     | 
| 
       222 
     | 
    
         
            -
                                makeObj.saveHtml5(path)
         
     | 
| 
       223 
     | 
    
         
            -
                        except Exception as exc:
         
     | 
| 
       224 
     | 
    
         
            -
                            logException()
         
     | 
| 
       225 
     | 
    
         
            -
                            self._error = formatException(exc)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    self._cache = None
         
     | 
| 
       226 
133 
     | 
    
         | 
| 
       227 
     | 
    
         
            -
                     
     | 
| 
      
 134 
     | 
    
         
            +
                    if bFormat in (nwBuildFmt.J_HTML, nwBuildFmt.J_NWD):
         
     | 
| 
      
 135 
     | 
    
         
            +
                        # Ensure that JSON output has the correct extension
         
     | 
| 
      
 136 
     | 
    
         
            +
                        path = path.with_suffix(".json")
         
     | 
| 
       228 
137 
     | 
    
         | 
| 
       229 
     | 
    
         
            -
             
     | 
| 
       230 
     | 
    
         
            -
             
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
       232 
     | 
    
         
            -
             
     | 
| 
      
 138 
     | 
    
         
            +
                    if bFormat in (nwBuildFmt.ODT, nwBuildFmt.FODT):
         
     | 
| 
      
 139 
     | 
    
         
            +
                        makeObj = ToOdt(self._project, bFormat == nwBuildFmt.FODT)
         
     | 
| 
      
 140 
     | 
    
         
            +
                        filtered = self._setupBuild(makeObj)
         
     | 
| 
      
 141 
     | 
    
         
            +
                        makeObj.initDocument()
         
     | 
| 
      
 142 
     | 
    
         
            +
                        yield from self._iterBuild(makeObj, filtered)
         
     | 
| 
      
 143 
     | 
    
         
            +
                        makeObj.closeDocument()
         
     | 
| 
       233 
144 
     | 
    
         | 
| 
       234 
     | 
    
         
            -
                     
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
       236 
     | 
    
         
            -
                         
     | 
| 
      
 145 
     | 
    
         
            +
                    elif bFormat in (nwBuildFmt.HTML, nwBuildFmt.J_HTML):
         
     | 
| 
      
 146 
     | 
    
         
            +
                        makeObj = ToHtml(self._project)
         
     | 
| 
      
 147 
     | 
    
         
            +
                        filtered = self._setupBuild(makeObj)
         
     | 
| 
      
 148 
     | 
    
         
            +
                        makeObj.initDocument()
         
     | 
| 
      
 149 
     | 
    
         
            +
                        yield from self._iterBuild(makeObj, filtered)
         
     | 
| 
      
 150 
     | 
    
         
            +
                        makeObj.closeDocument()
         
     | 
| 
      
 151 
     | 
    
         
            +
                        if not self._build.getBool("html.preserveTabs"):
         
     | 
| 
      
 152 
     | 
    
         
            +
                            makeObj.replaceTabs()
         
     | 
| 
       237 
153 
     | 
    
         | 
| 
       238 
     | 
    
         
            -
                     
     | 
| 
       239 
     | 
    
         
            -
                        self. 
     | 
| 
       240 
     | 
    
         
            -
                         
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
                         
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
      
 154 
     | 
    
         
            +
                    elif bFormat in (nwBuildFmt.STD_MD, nwBuildFmt.EXT_MD):
         
     | 
| 
      
 155 
     | 
    
         
            +
                        makeObj = ToMarkdown(self._project, bFormat == nwBuildFmt.EXT_MD)
         
     | 
| 
      
 156 
     | 
    
         
            +
                        filtered = self._setupBuild(makeObj)
         
     | 
| 
      
 157 
     | 
    
         
            +
                        yield from self._iterBuild(makeObj, filtered)
         
     | 
| 
      
 158 
     | 
    
         
            +
                        makeObj.closeDocument()
         
     | 
| 
      
 159 
     | 
    
         
            +
                        if self._build.getBool("format.replaceTabs"):
         
     | 
| 
      
 160 
     | 
    
         
            +
                            makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
         
     | 
| 
       244 
161 
     | 
    
         | 
| 
       245 
     | 
    
         
            -
                     
     | 
| 
      
 162 
     | 
    
         
            +
                    elif bFormat in (nwBuildFmt.NWD, nwBuildFmt.J_NWD):
         
     | 
| 
      
 163 
     | 
    
         
            +
                        makeObj = ToRaw(self._project)
         
     | 
| 
      
 164 
     | 
    
         
            +
                        filtered = self._setupBuild(makeObj)
         
     | 
| 
      
 165 
     | 
    
         
            +
                        yield from self._iterBuild(makeObj, filtered)
         
     | 
| 
      
 166 
     | 
    
         
            +
                        makeObj.closeDocument()
         
     | 
| 
      
 167 
     | 
    
         
            +
                        if self._build.getBool("format.replaceTabs"):
         
     | 
| 
      
 168 
     | 
    
         
            +
                            makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                    elif bFormat == nwBuildFmt.DOCX:
         
     | 
| 
      
 171 
     | 
    
         
            +
                        makeObj = ToDocX(self._project)
         
     | 
| 
      
 172 
     | 
    
         
            +
                        filtered = self._setupBuild(makeObj)
         
     | 
| 
      
 173 
     | 
    
         
            +
                        makeObj.initDocument()
         
     | 
| 
      
 174 
     | 
    
         
            +
                        yield from self._iterBuild(makeObj, filtered)
         
     | 
| 
      
 175 
     | 
    
         
            +
                        makeObj.closeDocument()
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                    elif bFormat == nwBuildFmt.PDF:
         
     | 
| 
      
 178 
     | 
    
         
            +
                        makeObj = ToQTextDocument(self._project)
         
     | 
| 
      
 179 
     | 
    
         
            +
                        filtered = self._setupBuild(makeObj)
         
     | 
| 
      
 180 
     | 
    
         
            +
                        makeObj.initDocument()
         
     | 
| 
      
 181 
     | 
    
         
            +
                        yield from self._iterBuild(makeObj, filtered)
         
     | 
| 
      
 182 
     | 
    
         
            +
                        makeObj.closeDocument()
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 185 
     | 
    
         
            +
                        logger.error("Unsupported document format")
         
     | 
| 
      
 186 
     | 
    
         
            +
                        return
         
     | 
| 
       246 
187 
     | 
    
         | 
| 
       247 
188 
     | 
    
         
             
                    self._error = None
         
     | 
| 
       248 
189 
     | 
    
         
             
                    self._cache = makeObj
         
     | 
| 
       249 
190 
     | 
    
         | 
| 
       250 
191 
     | 
    
         
             
                    try:
         
     | 
| 
       251 
     | 
    
         
            -
                        makeObj. 
     | 
| 
      
 192 
     | 
    
         
            +
                        makeObj.saveDocument(path)
         
     | 
| 
       252 
193 
     | 
    
         
             
                    except Exception as exc:
         
     | 
| 
       253 
194 
     | 
    
         
             
                        logException()
         
     | 
| 
       254 
195 
     | 
    
         
             
                        self._error = formatException(exc)
         
     | 
| 
       255 
196 
     | 
    
         | 
| 
       256 
197 
     | 
    
         
             
                    return
         
     | 
| 
       257 
198 
     | 
    
         | 
| 
       258 
     | 
    
         
            -
                 
     | 
| 
       259 
     | 
    
         
            -
             
     | 
| 
       260 
     | 
    
         
            -
             
     | 
| 
       261 
     | 
    
         
            -
                    filtered = self._setupBuild(makeObj)
         
     | 
| 
       262 
     | 
    
         
            -
             
     | 
| 
       263 
     | 
    
         
            -
                    makeObj.setKeepMarkdown(True)
         
     | 
| 
      
 199 
     | 
    
         
            +
                ##
         
     | 
| 
      
 200 
     | 
    
         
            +
                #  Internal Functions
         
     | 
| 
      
 201 
     | 
    
         
            +
                ##
         
     | 
| 
       264 
202 
     | 
    
         | 
| 
      
 203 
     | 
    
         
            +
                def _iterBuild(self, makeObj: Tokenizer, filtered: dict) -> Iterable[tuple[int, bool]]:
         
     | 
| 
      
 204 
     | 
    
         
            +
                    """Iterate over buildable documents."""
         
     | 
| 
      
 205 
     | 
    
         
            +
                    self._count = True
         
     | 
| 
       265 
206 
     | 
    
         
             
                    for i, tHandle in enumerate(self._queue):
         
     | 
| 
       266 
207 
     | 
    
         
             
                        self._error = None
         
     | 
| 
       267 
208 
     | 
    
         
             
                        if filtered.get(tHandle, (False, 0))[0]:
         
     | 
| 
       268 
     | 
    
         
            -
                            yield i, self._doBuild(makeObj, tHandle 
     | 
| 
      
 209 
     | 
    
         
            +
                            yield i, self._doBuild(makeObj, tHandle)
         
     | 
| 
       269 
210 
     | 
    
         
             
                        else:
         
     | 
| 
       270 
211 
     | 
    
         
             
                            yield i, False
         
     | 
| 
       271 
     | 
    
         
            -
             
     | 
| 
       272 
     | 
    
         
            -
                    if self._build.getBool("format.replaceTabs"):
         
     | 
| 
       273 
     | 
    
         
            -
                        makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
         
     | 
| 
       274 
     | 
    
         
            -
             
     | 
| 
       275 
     | 
    
         
            -
                    self._error = None
         
     | 
| 
       276 
     | 
    
         
            -
                    self._cache = makeObj
         
     | 
| 
       277 
     | 
    
         
            -
             
     | 
| 
       278 
     | 
    
         
            -
                    if isinstance(path, Path):
         
     | 
| 
       279 
     | 
    
         
            -
                        try:
         
     | 
| 
       280 
     | 
    
         
            -
                            if asJson:
         
     | 
| 
       281 
     | 
    
         
            -
                                makeObj.saveRawMarkdownJSON(path)
         
     | 
| 
       282 
     | 
    
         
            -
                            else:
         
     | 
| 
       283 
     | 
    
         
            -
                                makeObj.saveRawMarkdown(path)
         
     | 
| 
       284 
     | 
    
         
            -
                        except Exception as exc:
         
     | 
| 
       285 
     | 
    
         
            -
                            logException()
         
     | 
| 
       286 
     | 
    
         
            -
                            self._error = formatException(exc)
         
     | 
| 
       287 
     | 
    
         
            -
             
     | 
| 
       288 
212 
     | 
    
         
             
                    return
         
     | 
| 
       289 
213 
     | 
    
         | 
| 
       290 
     | 
    
         
            -
                ##
         
     | 
| 
       291 
     | 
    
         
            -
                #  Internal Functions
         
     | 
| 
       292 
     | 
    
         
            -
                ##
         
     | 
| 
       293 
     | 
    
         
            -
             
     | 
| 
       294 
214 
     | 
    
         
             
                def _setupBuild(self, bldObj: Tokenizer) -> dict:
         
     | 
| 
       295 
215 
     | 
    
         
             
                    """Configure the build object."""
         
     | 
| 
       296 
216 
     | 
    
         
             
                    # Get Settings
         
     | 
| 
       297 
217 
     | 
    
         
             
                    textFont = QFont(CONFIG.textFont)
         
     | 
| 
       298 
218 
     | 
    
         
             
                    textFont.fromString(self._build.getStr("format.textFont"))
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
       299 
220 
     | 
    
         
             
                    bldObj.setFont(textFont)
         
     | 
| 
      
 221 
     | 
    
         
            +
                    bldObj.setLanguage(self._project.data.language)
         
     | 
| 
       300 
222 
     | 
    
         | 
| 
       301 
     | 
    
         
            -
                    bldObj. 
     | 
| 
       302 
     | 
    
         
            -
                        self._build.getStr("headings. 
     | 
| 
       303 
     | 
    
         
            -
                        self._build.getBool("headings. 
     | 
| 
      
 223 
     | 
    
         
            +
                    bldObj.setPartitionFormat(
         
     | 
| 
      
 224 
     | 
    
         
            +
                        self._build.getStr("headings.fmtPart"),
         
     | 
| 
      
 225 
     | 
    
         
            +
                        self._build.getBool("headings.hidePart")
         
     | 
| 
       304 
226 
     | 
    
         
             
                    )
         
     | 
| 
       305 
227 
     | 
    
         
             
                    bldObj.setChapterFormat(
         
     | 
| 
       306 
228 
     | 
    
         
             
                        self._build.getStr("headings.fmtChapter"),
         
     | 
| 
         @@ -326,6 +248,10 @@ class NWBuildDocument: 
     | 
|
| 
       326 
248 
     | 
    
         
             
                        self._build.getBool("headings.centerTitle"),
         
     | 
| 
       327 
249 
     | 
    
         
             
                        self._build.getBool("headings.breakTitle")
         
     | 
| 
       328 
250 
     | 
    
         
             
                    )
         
     | 
| 
      
 251 
     | 
    
         
            +
                    bldObj.setPartitionStyle(
         
     | 
| 
      
 252 
     | 
    
         
            +
                        self._build.getBool("headings.centerPart"),
         
     | 
| 
      
 253 
     | 
    
         
            +
                        self._build.getBool("headings.breakPart")
         
     | 
| 
      
 254 
     | 
    
         
            +
                    )
         
     | 
| 
       329 
255 
     | 
    
         
             
                    bldObj.setChapterStyle(
         
     | 
| 
       330 
256 
     | 
    
         
             
                        self._build.getBool("headings.centerChapter"),
         
     | 
| 
       331 
257 
     | 
    
         
             
                        self._build.getBool("headings.breakChapter")
         
     | 
| 
         @@ -338,12 +264,46 @@ class NWBuildDocument: 
     | 
|
| 
       338 
264 
     | 
    
         
             
                    bldObj.setJustify(self._build.getBool("format.justifyText"))
         
     | 
| 
       339 
265 
     | 
    
         
             
                    bldObj.setLineHeight(self._build.getFloat("format.lineHeight"))
         
     | 
| 
       340 
266 
     | 
    
         
             
                    bldObj.setKeepLineBreaks(self._build.getBool("format.keepBreaks"))
         
     | 
| 
       341 
     | 
    
         
            -
                    bldObj. 
     | 
| 
      
 267 
     | 
    
         
            +
                    bldObj.setDialogHighlight(self._build.getBool("format.showDialogue"))
         
     | 
| 
       342 
268 
     | 
    
         
             
                    bldObj.setFirstLineIndent(
         
     | 
| 
       343 
269 
     | 
    
         
             
                        self._build.getBool("format.firstLineIndent"),
         
     | 
| 
       344 
270 
     | 
    
         
             
                        self._build.getFloat("format.firstIndentWidth"),
         
     | 
| 
       345 
271 
     | 
    
         
             
                        self._build.getBool("format.indentFirstPar"),
         
     | 
| 
       346 
272 
     | 
    
         
             
                    )
         
     | 
| 
      
 273 
     | 
    
         
            +
                    bldObj.setHeadingStyles(
         
     | 
| 
      
 274 
     | 
    
         
            +
                        self._build.getBool("doc.colorHeadings"),
         
     | 
| 
      
 275 
     | 
    
         
            +
                        self._build.getBool("doc.scaleHeadings"),
         
     | 
| 
      
 276 
     | 
    
         
            +
                        self._build.getBool("doc.boldHeadings"),
         
     | 
| 
      
 277 
     | 
    
         
            +
                    )
         
     | 
| 
      
 278 
     | 
    
         
            +
             
     | 
| 
      
 279 
     | 
    
         
            +
                    bldObj.setTitleMargins(
         
     | 
| 
      
 280 
     | 
    
         
            +
                        self._build.getFloat("format.titleMarginT"),
         
     | 
| 
      
 281 
     | 
    
         
            +
                        self._build.getFloat("format.titleMarginB"),
         
     | 
| 
      
 282 
     | 
    
         
            +
                    )
         
     | 
| 
      
 283 
     | 
    
         
            +
                    bldObj.setHead1Margins(
         
     | 
| 
      
 284 
     | 
    
         
            +
                        self._build.getFloat("format.h1MarginT"),
         
     | 
| 
      
 285 
     | 
    
         
            +
                        self._build.getFloat("format.h1MarginB"),
         
     | 
| 
      
 286 
     | 
    
         
            +
                    )
         
     | 
| 
      
 287 
     | 
    
         
            +
                    bldObj.setHead2Margins(
         
     | 
| 
      
 288 
     | 
    
         
            +
                        self._build.getFloat("format.h2MarginT"),
         
     | 
| 
      
 289 
     | 
    
         
            +
                        self._build.getFloat("format.h2MarginB"),
         
     | 
| 
      
 290 
     | 
    
         
            +
                    )
         
     | 
| 
      
 291 
     | 
    
         
            +
                    bldObj.setHead3Margins(
         
     | 
| 
      
 292 
     | 
    
         
            +
                        self._build.getFloat("format.h3MarginT"),
         
     | 
| 
      
 293 
     | 
    
         
            +
                        self._build.getFloat("format.h3MarginB"),
         
     | 
| 
      
 294 
     | 
    
         
            +
                    )
         
     | 
| 
      
 295 
     | 
    
         
            +
                    bldObj.setHead4Margins(
         
     | 
| 
      
 296 
     | 
    
         
            +
                        self._build.getFloat("format.h4MarginT"),
         
     | 
| 
      
 297 
     | 
    
         
            +
                        self._build.getFloat("format.h4MarginB"),
         
     | 
| 
      
 298 
     | 
    
         
            +
                    )
         
     | 
| 
      
 299 
     | 
    
         
            +
                    bldObj.setTextMargins(
         
     | 
| 
      
 300 
     | 
    
         
            +
                        self._build.getFloat("format.textMarginT"),
         
     | 
| 
      
 301 
     | 
    
         
            +
                        self._build.getFloat("format.textMarginB"),
         
     | 
| 
      
 302 
     | 
    
         
            +
                    )
         
     | 
| 
      
 303 
     | 
    
         
            +
                    bldObj.setSeparatorMargins(
         
     | 
| 
      
 304 
     | 
    
         
            +
                        self._build.getFloat("format.sepMarginT"),
         
     | 
| 
      
 305 
     | 
    
         
            +
                        self._build.getFloat("format.sepMarginB"),
         
     | 
| 
      
 306 
     | 
    
         
            +
                    )
         
     | 
| 
       347 
307 
     | 
    
         | 
| 
       348 
308 
     | 
    
         
             
                    bldObj.setBodyText(self._build.getBool("text.includeBodyText"))
         
     | 
| 
       349 
309 
     | 
    
         
             
                    bldObj.setSynopsis(self._build.getBool("text.includeSynopsis"))
         
     | 
| 
         @@ -355,13 +315,13 @@ class NWBuildDocument: 
     | 
|
| 
       355 
315 
     | 
    
         
             
                        bldObj.setStyles(self._build.getBool("html.addStyles"))
         
     | 
| 
       356 
316 
     | 
    
         
             
                        bldObj.setReplaceUnicode(self._build.getBool("format.stripUnicode"))
         
     | 
| 
       357 
317 
     | 
    
         | 
| 
       358 
     | 
    
         
            -
                    if isinstance(bldObj, ToOdt):
         
     | 
| 
       359 
     | 
    
         
            -
                        bldObj.setColourHeaders(self._build.getBool("odt.addColours"))
         
     | 
| 
       360 
     | 
    
         
            -
                        bldObj.setLanguage(self._project.data.language)
         
     | 
| 
      
 318 
     | 
    
         
            +
                    if isinstance(bldObj, (ToOdt, ToDocX)):
         
     | 
| 
       361 
319 
     | 
    
         
             
                        bldObj.setHeaderFormat(
         
     | 
| 
       362 
     | 
    
         
            -
                            self._build.getStr(" 
     | 
| 
      
 320 
     | 
    
         
            +
                            self._build.getStr("doc.pageHeader"),
         
     | 
| 
      
 321 
     | 
    
         
            +
                            self._build.getInt("doc.pageCountOffset"),
         
     | 
| 
       363 
322 
     | 
    
         
             
                        )
         
     | 
| 
       364 
323 
     | 
    
         | 
| 
      
 324 
     | 
    
         
            +
                    if isinstance(bldObj, (ToOdt, ToDocX, ToQTextDocument)):
         
     | 
| 
       365 
325 
     | 
    
         
             
                        scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0)
         
     | 
| 
       366 
326 
     | 
    
         
             
                        pW, pH = nwLabels.PAPER_SIZE.get(self._build.getStr("format.pageSize"), (-1.0, -1.0))
         
     | 
| 
       367 
327 
     | 
    
         
             
                        bldObj.setPageLayout(
         
     | 
| 
         @@ -384,14 +344,17 @@ class NWBuildDocument: 
     | 
|
| 
       384 
344 
     | 
    
         
             
                    tItem = self._project.tree[tHandle]
         
     | 
| 
       385 
345 
     | 
    
         
             
                    if isinstance(tItem, NWItem):
         
     | 
| 
       386 
346 
     | 
    
         
             
                        try:
         
     | 
| 
       387 
     | 
    
         
            -
                            if tItem.isRootType() 
     | 
| 
       388 
     | 
    
         
            -
                                 
     | 
| 
       389 
     | 
    
         
            -
             
     | 
| 
       390 
     | 
    
         
            -
             
     | 
| 
       391 
     | 
    
         
            -
             
     | 
| 
       392 
     | 
    
         
            -
                                     
     | 
| 
       393 
     | 
    
         
            -
             
     | 
| 
       394 
     | 
    
         
            -
                                     
     | 
| 
      
 347 
     | 
    
         
            +
                            if tItem.isRootType():
         
     | 
| 
      
 348 
     | 
    
         
            +
                                if tItem.isNovelLike():
         
     | 
| 
      
 349 
     | 
    
         
            +
                                    bldObj.setBreakNext()
         
     | 
| 
      
 350 
     | 
    
         
            +
                                else:
         
     | 
| 
      
 351 
     | 
    
         
            +
                                    bldObj.addRootHeading(tHandle)
         
     | 
| 
      
 352 
     | 
    
         
            +
                                    if convert:
         
     | 
| 
      
 353 
     | 
    
         
            +
                                        bldObj.doConvert()
         
     | 
| 
      
 354 
     | 
    
         
            +
                                    if self._count:
         
     | 
| 
      
 355 
     | 
    
         
            +
                                        bldObj.countStats()
         
     | 
| 
      
 356 
     | 
    
         
            +
                                    if self._outline:
         
     | 
| 
      
 357 
     | 
    
         
            +
                                        bldObj.buildOutline()
         
     | 
| 
       395 
358 
     | 
    
         
             
                            elif tItem.isFileType():
         
     | 
| 
       396 
359 
     | 
    
         
             
                                bldObj.setText(tHandle)
         
     | 
| 
       397 
360 
     | 
    
         
             
                                bldObj.doPreProcessing()
         
     | 
| 
         @@ -402,8 +365,6 @@ class NWBuildDocument: 
     | 
|
| 
       402 
365 
     | 
    
         
             
                                    bldObj.buildOutline()
         
     | 
| 
       403 
366 
     | 
    
         
             
                                if convert:
         
     | 
| 
       404 
367 
     | 
    
         
             
                                    bldObj.doConvert()
         
     | 
| 
       405 
     | 
    
         
            -
                            else:
         
     | 
| 
       406 
     | 
    
         
            -
                                logger.info(f"Build: Skipping '{tHandle}'")
         
     | 
| 
       407 
368 
     | 
    
         | 
| 
       408 
369 
     | 
    
         
             
                        except Exception:
         
     | 
| 
       409 
370 
     | 
    
         
             
                            self._error = f"Build: Failed to build '{tHandle}'"
         
     | 
    
        novelwriter/core/index.py
    CHANGED
    
    | 
         @@ -40,7 +40,7 @@ from novelwriter import SHARED 
     | 
|
| 
       40 
40 
     | 
    
         
             
            from novelwriter.common import (
         
     | 
| 
       41 
41 
     | 
    
         
             
                checkInt, isHandle, isItemClass, isListInstance, isTitleTag, jsonEncode
         
     | 
| 
       42 
42 
     | 
    
         
             
            )
         
     | 
| 
       43 
     | 
    
         
            -
            from novelwriter.constants import nwFiles,  
     | 
| 
      
 43 
     | 
    
         
            +
            from novelwriter.constants import nwFiles, nwKeyWords, nwStyles
         
     | 
| 
       44 
44 
     | 
    
         
             
            from novelwriter.enum import nwComment, nwItemClass, nwItemLayout, nwItemType
         
     | 
| 
       45 
45 
     | 
    
         
             
            from novelwriter.error import logException
         
     | 
| 
       46 
46 
     | 
    
         
             
            from novelwriter.text.counting import standardCounter
         
     | 
| 
         @@ -487,13 +487,14 @@ class NWIndex: 
     | 
|
| 
       487 
487 
     | 
    
         
             
                    if nBits == 0:
         
     | 
| 
       488 
488 
     | 
    
         
             
                        return []
         
     | 
| 
       489 
489 
     | 
    
         | 
| 
       490 
     | 
    
         
            -
                    # Check that the  
     | 
| 
       491 
     | 
    
         
            -
                     
     | 
| 
      
 490 
     | 
    
         
            +
                    # Check that the keyword is valid
         
     | 
| 
      
 491 
     | 
    
         
            +
                    kBit = tBits[0]
         
     | 
| 
      
 492 
     | 
    
         
            +
                    isGood[0] = kBit in nwKeyWords.VALID_KEYS
         
     | 
| 
       492 
493 
     | 
    
         
             
                    if not isGood[0] or nBits == 1:
         
     | 
| 
       493 
494 
     | 
    
         
             
                        return isGood
         
     | 
| 
       494 
495 
     | 
    
         | 
| 
       495 
496 
     | 
    
         
             
                    # For a tag, only the first value is accepted, the rest are ignored
         
     | 
| 
       496 
     | 
    
         
            -
                    if  
     | 
| 
      
 497 
     | 
    
         
            +
                    if kBit == nwKeyWords.TAG_KEY and nBits > 1:
         
     | 
| 
       497 
498 
     | 
    
         
             
                        check, _ = self.parseValue(tBits[1])
         
     | 
| 
       498 
499 
     | 
    
         
             
                        if check in self._tagsIndex:
         
     | 
| 
       499 
500 
     | 
    
         
             
                            isGood[1] = self._tagsIndex.tagHandle(check) == tHandle
         
     | 
| 
         @@ -501,12 +502,16 @@ class NWIndex: 
     | 
|
| 
       501 
502 
     | 
    
         
             
                            isGood[1] = True
         
     | 
| 
       502 
503 
     | 
    
         
             
                        return isGood
         
     | 
| 
       503 
504 
     | 
    
         | 
| 
      
 505 
     | 
    
         
            +
                    if kBit == nwKeyWords.MENTION_KEY and nBits > 1:
         
     | 
| 
      
 506 
     | 
    
         
            +
                        isGood[1:nBits] = [aBit in self._tagsIndex for aBit in tBits[1:nBits]]
         
     | 
| 
      
 507 
     | 
    
         
            +
                        return isGood
         
     | 
| 
      
 508 
     | 
    
         
            +
             
     | 
| 
       504 
509 
     | 
    
         
             
                    # If we're still here, we check that the references exist
         
     | 
| 
       505 
510 
     | 
    
         
             
                    # Class references cannot have the | symbol in them
         
     | 
| 
       506 
     | 
    
         
            -
                     
     | 
| 
       507 
     | 
    
         
            -
             
     | 
| 
       508 
     | 
    
         
            -
             
     | 
| 
       509 
     | 
    
         
            -
             
     | 
| 
      
 511 
     | 
    
         
            +
                    if rClass := nwKeyWords.KEY_CLASS.get(kBit):
         
     | 
| 
      
 512 
     | 
    
         
            +
                        for n in range(1, nBits):
         
     | 
| 
      
 513 
     | 
    
         
            +
                            if (aBit := tBits[n]) in self._tagsIndex:
         
     | 
| 
      
 514 
     | 
    
         
            +
                                isGood[n] = self._tagsIndex.tagClass(aBit) == rClass.name and "|" not in aBit
         
     | 
| 
       510 
515 
     | 
    
         | 
| 
       511 
516 
     | 
    
         
             
                    return isGood
         
     | 
| 
       512 
517 
     | 
    
         | 
| 
         @@ -569,7 +574,7 @@ class NWIndex: 
     | 
|
| 
       569 
574 
     | 
    
         
             
                    for _, _, hItem in self._itemIndex.iterNovelStructure(
         
     | 
| 
       570 
575 
     | 
    
         
             
                        rHandle=rootHandle, activeOnly=activeOnly
         
     | 
| 
       571 
576 
     | 
    
         
             
                    ):
         
     | 
| 
       572 
     | 
    
         
            -
                        iLevel =  
     | 
| 
      
 577 
     | 
    
         
            +
                        iLevel = nwStyles.H_LEVEL.get(hItem.level, 0)
         
     | 
| 
       573 
578 
     | 
    
         
             
                        hCount[iLevel] += 1
         
     | 
| 
       574 
579 
     | 
    
         
             
                    return hCount
         
     | 
| 
       575 
580 
     | 
    
         | 
| 
         @@ -591,7 +596,7 @@ class NWIndex: 
     | 
|
| 
       591 
596 
     | 
    
         
             
                        rHandle=rHandle, activeOnly=activeOnly
         
     | 
| 
       592 
597 
     | 
    
         
             
                    ):
         
     | 
| 
       593 
598 
     | 
    
         
             
                        tKey = f"{tHandle}:{sTitle}"
         
     | 
| 
       594 
     | 
    
         
            -
                        iLevel =  
     | 
| 
      
 599 
     | 
    
         
            +
                        iLevel = nwStyles.H_LEVEL.get(hItem.level, 0)
         
     | 
| 
       595 
600 
     | 
    
         
             
                        if iLevel > maxDepth:
         
     | 
| 
       596 
601 
     | 
    
         
             
                            if pKey in tData:
         
     | 
| 
       597 
602 
     | 
    
         
             
                                tData[pKey]["words"] += hItem.wordCount
         
     | 
| 
         @@ -635,7 +640,7 @@ class NWIndex: 
     | 
|
| 
       635 
640 
     | 
    
         
             
                    """Extract all references made in a file, and optionally title
         
     | 
| 
       636 
641 
     | 
    
         
             
                    section.
         
     | 
| 
       637 
642 
     | 
    
         
             
                    """
         
     | 
| 
       638 
     | 
    
         
            -
                    tRefs = {x: [] for x in nwKeyWords. 
     | 
| 
      
 643 
     | 
    
         
            +
                    tRefs = {x: [] for x in nwKeyWords.VALID_KEYS}
         
     | 
| 
       639 
644 
     | 
    
         
             
                    for rTitle, hItem in self._itemIndex.iterItemHeaders(tHandle):
         
     | 
| 
       640 
645 
     | 
    
         
             
                        if sTitle is None or sTitle == rTitle:
         
     | 
| 
       641 
646 
     | 
    
         
             
                            for aTag, refTypes in hItem.references.items():
         
     | 
| 
         @@ -681,9 +686,10 @@ class NWIndex: 
     | 
|
| 
       681 
686 
     | 
    
         
             
                    """Return all tags used by a specific document."""
         
     | 
| 
       682 
687 
     | 
    
         
             
                    return self._itemIndex.allItemTags(tHandle) if tHandle else []
         
     | 
| 
       683 
688 
     | 
    
         | 
| 
       684 
     | 
    
         
            -
                def getClassTags(self, itemClass: nwItemClass) -> list[str]:
         
     | 
| 
      
 689 
     | 
    
         
            +
                def getClassTags(self, itemClass: nwItemClass | None) -> list[str]:
         
     | 
| 
       685 
690 
     | 
    
         
             
                    """Return all tags based on itemClass."""
         
     | 
| 
       686 
     | 
    
         
            -
                     
     | 
| 
      
 691 
     | 
    
         
            +
                    name = None if itemClass is None else itemClass.name
         
     | 
| 
      
 692 
     | 
    
         
            +
                    return self._tagsIndex.filterTagNames(name)
         
     | 
| 
       687 
693 
     | 
    
         | 
| 
       688 
694 
     | 
    
         
             
                def getTagsData(
         
     | 
| 
       689 
695 
     | 
    
         
             
                    self, activeOnly: bool = True
         
     | 
| 
         @@ -780,11 +786,16 @@ class TagsIndex: 
     | 
|
| 
       780 
786 
     | 
    
         
             
                    """Get the class of a given tag."""
         
     | 
| 
       781 
787 
     | 
    
         
             
                    return self._tags.get(tagKey.lower(), {}).get("class", None)
         
     | 
| 
       782 
788 
     | 
    
         | 
| 
       783 
     | 
    
         
            -
                def filterTagNames(self, className: str) -> list[str]:
         
     | 
| 
      
 789 
     | 
    
         
            +
                def filterTagNames(self, className: str | None) -> list[str]:
         
     | 
| 
       784 
790 
     | 
    
         
             
                    """Get a list of tag names for a given class."""
         
     | 
| 
       785 
     | 
    
         
            -
                     
     | 
| 
       786 
     | 
    
         
            -
                         
     | 
| 
       787 
     | 
    
         
            -
             
     | 
| 
      
 791 
     | 
    
         
            +
                    if className is None:
         
     | 
| 
      
 792 
     | 
    
         
            +
                        return [
         
     | 
| 
      
 793 
     | 
    
         
            +
                            x.get("name", "") for x in self._tags.values()
         
     | 
| 
      
 794 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 795 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 796 
     | 
    
         
            +
                        return [
         
     | 
| 
      
 797 
     | 
    
         
            +
                            x.get("name", "") for x in self._tags.values() if x.get("class", "") == className
         
     | 
| 
      
 798 
     | 
    
         
            +
                        ]
         
     | 
| 
       788 
799 
     | 
    
         | 
| 
       789 
800 
     | 
    
         
             
                ##
         
     | 
| 
       790 
801 
     | 
    
         
             
                #  Pack/Unpack
         
     | 
| 
         @@ -1251,7 +1262,7 @@ class IndexHeading: 
     | 
|
| 
       1251 
1262 
     | 
    
         | 
| 
       1252 
1263 
     | 
    
         
             
                def setLevel(self, level: str) -> None:
         
     | 
| 
       1253 
1264 
     | 
    
         
             
                    """Set the level of the heading if it's a valid value."""
         
     | 
| 
       1254 
     | 
    
         
            -
                    if level in  
     | 
| 
      
 1265 
     | 
    
         
            +
                    if level in nwStyles.H_VALID:
         
     | 
| 
       1255 
1266 
     | 
    
         
             
                        self._level = level
         
     | 
| 
       1256 
1267 
     | 
    
         
             
                    return
         
     | 
| 
       1257 
1268 
     | 
    
         | 
    
        novelwriter/core/item.py
    CHANGED
    
    | 
         @@ -33,7 +33,7 @@ from novelwriter.common import ( 
     | 
|
| 
       33 
33 
     | 
    
         
             
                checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified,
         
     | 
| 
       34 
34 
     | 
    
         
             
                yesNo
         
     | 
| 
       35 
35 
     | 
    
         
             
            )
         
     | 
| 
       36 
     | 
    
         
            -
            from novelwriter.constants import  
     | 
| 
      
 36 
     | 
    
         
            +
            from novelwriter.constants import nwLabels, nwStyles, trConst
         
     | 
| 
       37 
37 
     | 
    
         
             
            from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
       39 
39 
     | 
    
         
             
            if TYPE_CHECKING:  # pragma: no cover
         
     | 
| 
         @@ -518,7 +518,7 @@ class NWItem: 
     | 
|
| 
       518 
518 
     | 
    
         | 
| 
       519 
519 
     | 
    
         
             
                def setMainHeading(self, value: str) -> None:
         
     | 
| 
       520 
520 
     | 
    
         
             
                    """Set the main heading level."""
         
     | 
| 
       521 
     | 
    
         
            -
                    if value in  
     | 
| 
      
 521 
     | 
    
         
            +
                    if value in nwStyles.H_LEVEL:
         
     | 
| 
       522 
522 
     | 
    
         
             
                        self._heading = value
         
     | 
| 
       523 
523 
     | 
    
         
             
                    return
         
     | 
| 
       524 
524 
     | 
    
         | 
    
        novelwriter/core/options.py
    CHANGED
    
    | 
         @@ -60,7 +60,7 @@ VALID_MAP: dict[str, set[str]] = { 
     | 
|
| 
       60 
60 
     | 
    
         
             
                },
         
     | 
| 
       61 
61 
     | 
    
         
             
                "GuiManuscript": {
         
     | 
| 
       62 
62 
     | 
    
         
             
                    "winWidth", "winHeight", "optsWidth", "viewWidth", "listHeight",
         
     | 
| 
       63 
     | 
    
         
            -
                    "detailsHeight", "detailsWidth", "detailsExpanded",
         
     | 
| 
      
 63 
     | 
    
         
            +
                    "detailsHeight", "detailsWidth", "detailsExpanded", "showNewPage",
         
     | 
| 
       64 
64 
     | 
    
         
             
                },
         
     | 
| 
       65 
65 
     | 
    
         
             
                "GuiManuscriptBuild": {
         
     | 
| 
       66 
66 
     | 
    
         
             
                    "winWidth", "winHeight", "fmtWidth", "sumWidth",
         
     | 
| 
         @@ -73,6 +73,9 @@ VALID_MAP: dict[str, set[str]] = { 
     | 
|
| 
       73 
73 
     | 
    
         
             
                    "widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble",
         
     | 
| 
       74 
74 
     | 
    
         
             
                    "novelRoot",
         
     | 
| 
       75 
75 
     | 
    
         
             
                },
         
     | 
| 
      
 76 
     | 
    
         
            +
                "GuiOutlineDetails": {
         
     | 
| 
      
 77 
     | 
    
         
            +
                    "detailsWidth", "tagsWidth",
         
     | 
| 
      
 78 
     | 
    
         
            +
                }
         
     | 
| 
       76 
79 
     | 
    
         
             
            }
         
     | 
| 
       77 
80 
     | 
    
         | 
| 
       78 
81 
     | 
    
         | 
    
        novelwriter/core/spellcheck.py
    CHANGED
    
    | 
         @@ -125,21 +125,16 @@ class NWSpellEnchant: 
     | 
|
| 
       125 
125 
     | 
    
         
             
                    except Exception:
         
     | 
| 
       126 
126 
     | 
    
         
             
                        return []
         
     | 
| 
       127 
127 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
                def addWord(self, word: str) ->  
     | 
| 
      
 128 
     | 
    
         
            +
                def addWord(self, word: str, save: bool = True) -> None:
         
     | 
| 
       129 
129 
     | 
    
         
             
                    """Add a word to the project dictionary."""
         
     | 
| 
       130 
     | 
    
         
            -
                    word  
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
                    added = self._userDict.add(word)
         
     | 
| 
       139 
     | 
    
         
            -
                    if added:
         
     | 
| 
       140 
     | 
    
         
            -
                        self._userDict.save()
         
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
                    return added
         
     | 
| 
      
 130 
     | 
    
         
            +
                    if word := word.strip():
         
     | 
| 
      
 131 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 132 
     | 
    
         
            +
                            self._enchant.add_to_session(word)
         
     | 
| 
      
 133 
     | 
    
         
            +
                        except Exception:
         
     | 
| 
      
 134 
     | 
    
         
            +
                            return
         
     | 
| 
      
 135 
     | 
    
         
            +
                        if save and self._userDict.add(word):
         
     | 
| 
      
 136 
     | 
    
         
            +
                            self._userDict.save()
         
     | 
| 
      
 137 
     | 
    
         
            +
                    return
         
     | 
| 
       143 
138 
     | 
    
         | 
| 
       144 
139 
     | 
    
         
             
                def listDictionaries(self) -> list[tuple[str, str]]:
         
     | 
| 
       145 
140 
     | 
    
         
             
                    """List available dictionaries."""
         
     |