novelWriter 2.7.4__py3-none-any.whl → 2.8b1__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/__init__.py +8 -7
 - novelwriter/assets/icons/font_awesome.icons +22 -4
 - novelwriter/assets/icons/material_filled_normal.icons +20 -2
 - novelwriter/assets/icons/material_filled_thin.icons +20 -2
 - novelwriter/assets/icons/material_rounded_normal.icons +20 -2
 - novelwriter/assets/icons/material_rounded_thin.icons +20 -2
 - novelwriter/assets/icons/material_sharp_normal.icons +20 -2
 - novelwriter/assets/icons/material_sharp_thin.icons +20 -2
 - novelwriter/assets/icons/remix_filled.icons +20 -2
 - novelwriter/assets/icons/remix_outline.icons +20 -2
 - novelwriter/assets/images/welcome.webp +0 -0
 - novelwriter/assets/manual.pdf +0 -0
 - novelwriter/assets/manual_fr.pdf +0 -0
 - novelwriter/assets/sample.zip +0 -0
 - novelwriter/assets/text/credits_en.htm +61 -11
 - novelwriter/assets/themes/aura.conf +97 -0
 - novelwriter/assets/themes/aura_bright.conf +95 -0
 - novelwriter/assets/themes/aura_soft.conf +97 -0
 - novelwriter/assets/themes/b2t_garden_dark.conf +97 -0
 - novelwriter/assets/themes/b2t_garden_light.conf +97 -0
 - novelwriter/assets/themes/b2t_suburb_dark.conf +97 -0
 - novelwriter/assets/themes/b2t_suburb_light.conf +97 -0
 - novelwriter/assets/themes/b4t_classic_o_dark.conf +97 -0
 - novelwriter/assets/themes/b4t_classic_o_light.conf +97 -0
 - novelwriter/assets/themes/b4t_modern_c_dark.conf +97 -0
 - novelwriter/assets/themes/b4t_modern_c_light.conf +97 -0
 - novelwriter/assets/themes/blue_streak_dark.conf +97 -0
 - novelwriter/assets/themes/blue_streak_light.conf +97 -0
 - novelwriter/assets/themes/castle_day.conf +95 -0
 - novelwriter/assets/themes/castle_night.conf +95 -0
 - novelwriter/assets/themes/catppuccin_latte.conf +97 -0
 - novelwriter/assets/themes/catppuccin_mocha.conf +97 -0
 - novelwriter/assets/themes/chalky_soil.conf +95 -0
 - novelwriter/assets/themes/chernozem.conf +95 -0
 - novelwriter/assets/themes/cyberpunk_night.conf +88 -40
 - novelwriter/assets/themes/default_dark.conf +89 -41
 - novelwriter/assets/themes/default_light.conf +89 -41
 - novelwriter/assets/themes/dracula.conf +91 -42
 - novelwriter/assets/themes/espresso.conf +97 -0
 - novelwriter/assets/themes/everforest_dark.conf +97 -0
 - novelwriter/assets/themes/everforest_light.conf +97 -0
 - novelwriter/assets/themes/floral_daydream.conf +95 -0
 - novelwriter/assets/themes/floral_midnight.conf +95 -0
 - novelwriter/assets/themes/full_moon.conf +95 -0
 - novelwriter/assets/themes/grey_dark.conf +97 -0
 - novelwriter/assets/themes/grey_light.conf +97 -0
 - novelwriter/assets/themes/horizon_dark.conf +97 -0
 - novelwriter/assets/themes/horizon_light.conf +97 -0
 - novelwriter/assets/themes/jewel_case_dark.conf +95 -0
 - novelwriter/assets/themes/jewel_case_light.conf +95 -0
 - novelwriter/assets/themes/lcars.conf +97 -0
 - novelwriter/assets/themes/light_owl.conf +117 -0
 - novelwriter/assets/themes/new_moon.conf +97 -0
 - novelwriter/assets/themes/night_owl.conf +117 -0
 - novelwriter/assets/themes/noctis.conf +129 -0
 - novelwriter/assets/themes/noctis_lux.conf +129 -0
 - novelwriter/assets/themes/nord.conf +97 -0
 - novelwriter/assets/themes/nordlicht.conf +95 -0
 - novelwriter/assets/themes/otium_dark.conf +95 -0
 - novelwriter/assets/themes/otium_light.conf +95 -0
 - novelwriter/assets/themes/paragon.conf +96 -0
 - novelwriter/assets/themes/primer_light.conf +97 -0
 - novelwriter/assets/themes/primer_night.conf +97 -0
 - novelwriter/assets/themes/rose_pine.conf +97 -0
 - novelwriter/assets/themes/rose_pine_dawn.conf +97 -0
 - novelwriter/assets/themes/ruby_day.conf +95 -0
 - novelwriter/assets/themes/ruby_night.conf +95 -0
 - novelwriter/assets/themes/selenium_dark.conf +95 -0
 - novelwriter/assets/themes/selenium_light.conf +95 -0
 - novelwriter/assets/themes/sepia_dark.conf +95 -0
 - novelwriter/assets/themes/sepia_light.conf +95 -0
 - novelwriter/assets/themes/snazzy.conf +102 -40
 - novelwriter/assets/themes/solarized_dark.conf +108 -40
 - novelwriter/assets/themes/solarized_light.conf +108 -40
 - novelwriter/assets/themes/sultana_light.conf +95 -0
 - novelwriter/assets/themes/sultana_night.conf +95 -0
 - novelwriter/assets/themes/tango_dark.conf +111 -0
 - novelwriter/assets/themes/tango_light.conf +111 -0
 - novelwriter/assets/themes/tomorrow.conf +117 -0
 - novelwriter/assets/themes/tomorrow_night.conf +117 -0
 - novelwriter/assets/themes/tomorrow_night_blue.conf +117 -0
 - novelwriter/assets/themes/tomorrow_night_bright.conf +117 -0
 - novelwriter/assets/themes/tomorrow_night_eighties.conf +117 -0
 - novelwriter/assets/themes/vivid_black_green.conf +97 -0
 - novelwriter/assets/themes/vivid_black_red.conf +97 -0
 - novelwriter/assets/themes/vivid_white_green.conf +97 -0
 - novelwriter/assets/themes/vivid_white_red.conf +97 -0
 - novelwriter/assets/themes/warpgate.conf +96 -0
 - novelwriter/assets/themes/waterlily_dark.conf +95 -0
 - novelwriter/assets/themes/waterlily_light.conf +95 -0
 - novelwriter/common.py +47 -17
 - novelwriter/config.py +57 -62
 - novelwriter/constants.py +32 -6
 - novelwriter/core/buildsettings.py +3 -23
 - novelwriter/core/coretools.py +21 -25
 - novelwriter/core/docbuild.py +4 -9
 - novelwriter/core/document.py +2 -6
 - novelwriter/core/index.py +33 -53
 - novelwriter/core/indexdata.py +17 -22
 - novelwriter/core/item.py +11 -35
 - novelwriter/core/itemmodel.py +5 -21
 - novelwriter/core/novelmodel.py +3 -7
 - novelwriter/core/options.py +3 -4
 - novelwriter/core/project.py +31 -21
 - novelwriter/core/projectdata.py +2 -21
 - novelwriter/core/projectxml.py +13 -21
 - novelwriter/core/sessions.py +2 -4
 - novelwriter/core/spellcheck.py +12 -13
 - novelwriter/core/status.py +27 -20
 - novelwriter/core/storage.py +5 -10
 - novelwriter/core/tree.py +6 -15
 - novelwriter/dialogs/about.py +9 -10
 - novelwriter/dialogs/docmerge.py +17 -14
 - novelwriter/dialogs/docsplit.py +18 -14
 - novelwriter/dialogs/editlabel.py +15 -9
 - novelwriter/dialogs/preferences.py +69 -68
 - novelwriter/dialogs/projectsettings.py +88 -67
 - novelwriter/dialogs/quotes.py +15 -10
 - novelwriter/dialogs/wordlist.py +18 -21
 - novelwriter/enum.py +75 -30
 - novelwriter/error.py +6 -11
 - novelwriter/extensions/configlayout.py +8 -34
 - novelwriter/extensions/eventfilters.py +3 -3
 - novelwriter/extensions/modified.py +87 -32
 - novelwriter/extensions/novelselector.py +13 -12
 - novelwriter/extensions/pagedsidebar.py +10 -18
 - novelwriter/extensions/progressbars.py +5 -11
 - novelwriter/extensions/statusled.py +3 -6
 - novelwriter/extensions/switch.py +8 -11
 - novelwriter/extensions/switchbox.py +2 -11
 - novelwriter/extensions/versioninfo.py +6 -7
 - novelwriter/formats/shared.py +10 -2
 - novelwriter/formats/todocx.py +15 -37
 - novelwriter/formats/tohtml.py +52 -61
 - novelwriter/formats/tokenizer.py +33 -64
 - novelwriter/formats/tomarkdown.py +4 -11
 - novelwriter/formats/toodt.py +12 -71
 - novelwriter/formats/toqdoc.py +11 -21
 - novelwriter/formats/toraw.py +2 -6
 - novelwriter/gui/doceditor.py +207 -245
 - novelwriter/gui/dochighlight.py +142 -101
 - novelwriter/gui/docviewer.py +53 -84
 - novelwriter/gui/docviewerpanel.py +18 -41
 - novelwriter/gui/editordocument.py +12 -17
 - novelwriter/gui/itemdetails.py +5 -14
 - novelwriter/gui/mainmenu.py +24 -32
 - novelwriter/gui/noveltree.py +13 -51
 - novelwriter/gui/outline.py +20 -61
 - novelwriter/gui/projtree.py +40 -96
 - novelwriter/gui/search.py +9 -24
 - novelwriter/gui/sidebar.py +54 -22
 - novelwriter/gui/statusbar.py +7 -22
 - novelwriter/gui/theme.py +482 -368
 - novelwriter/guimain.py +87 -101
 - novelwriter/shared.py +79 -48
 - novelwriter/splash.py +9 -5
 - novelwriter/text/comments.py +1 -1
 - novelwriter/text/counting.py +9 -5
 - novelwriter/text/patterns.py +20 -15
 - novelwriter/tools/dictionaries.py +18 -16
 - novelwriter/tools/lipsum.py +15 -17
 - novelwriter/tools/manusbuild.py +25 -45
 - novelwriter/tools/manuscript.py +94 -95
 - novelwriter/tools/manussettings.py +149 -104
 - novelwriter/tools/noveldetails.py +10 -24
 - novelwriter/tools/welcome.py +24 -72
 - novelwriter/tools/writingstats.py +17 -26
 - novelwriter/types.py +25 -13
 - {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/METADATA +7 -7
 - novelwriter-2.8b1.dist-info/RECORD +212 -0
 - novelwriter/assets/images/welcome-dark.jpg +0 -0
 - novelwriter/assets/images/welcome-light.jpg +0 -0
 - novelwriter/assets/syntax/cyberpunk_night.conf +0 -28
 - novelwriter/assets/syntax/default_dark.conf +0 -42
 - novelwriter/assets/syntax/default_light.conf +0 -42
 - novelwriter/assets/syntax/dracula.conf +0 -44
 - novelwriter/assets/syntax/grey_dark.conf +0 -29
 - novelwriter/assets/syntax/grey_light.conf +0 -29
 - novelwriter/assets/syntax/light_owl.conf +0 -49
 - novelwriter/assets/syntax/night_owl.conf +0 -49
 - novelwriter/assets/syntax/snazzy.conf +0 -42
 - novelwriter/assets/syntax/solarized_dark.conf +0 -29
 - novelwriter/assets/syntax/solarized_light.conf +0 -29
 - novelwriter/assets/syntax/tango.conf +0 -39
 - novelwriter/assets/syntax/tomorrow.conf +0 -49
 - novelwriter/assets/syntax/tomorrow_night.conf +0 -49
 - novelwriter/assets/syntax/tomorrow_night_blue.conf +0 -49
 - novelwriter/assets/syntax/tomorrow_night_bright.conf +0 -49
 - novelwriter/assets/syntax/tomorrow_night_eighties.conf +0 -49
 - novelwriter/assets/themes/default.conf +0 -3
 - novelwriter-2.7.4.dist-info/RECORD +0 -163
 - {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/WHEEL +0 -0
 - {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/entry_points.txt +0 -0
 - {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/licenses/LICENSE.md +0 -0
 - {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/licenses/setup/LICENSE-Apache-2.0.txt +0 -0
 - {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/top_level.txt +0 -0
 
    
        novelwriter/guimain.py
    CHANGED
    
    | 
         @@ -20,7 +20,7 @@ General Public License for more details. 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
            You should have received a copy of the GNU General Public License
         
     | 
| 
       22 
22 
     | 
    
         
             
            along with this program. If not, see <https://www.gnu.org/licenses/>.
         
     | 
| 
       23 
     | 
    
         
            -
            """
         
     | 
| 
      
 23 
     | 
    
         
            +
            """  # noqa
         
     | 
| 
       24 
24 
     | 
    
         
             
            from __future__ import annotations
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
            import logging
         
     | 
| 
         @@ -30,7 +30,7 @@ from datetime import datetime 
     | 
|
| 
       30 
30 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       31 
31 
     | 
    
         
             
            from time import time
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
            from PyQt6.QtCore import Qt, QTimer, pyqtSlot
         
     | 
| 
      
 33 
     | 
    
         
            +
            from PyQt6.QtCore import QEvent, Qt, QTimer, pyqtSlot
         
     | 
| 
       34 
34 
     | 
    
         
             
            from PyQt6.QtGui import QCloseEvent, QCursor, QIcon, QShortcut
         
     | 
| 
       35 
35 
     | 
    
         
             
            from PyQt6.QtWidgets import (
         
     | 
| 
       36 
36 
     | 
    
         
             
                QApplication, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox,
         
     | 
| 
         @@ -38,13 +38,14 @@ from PyQt6.QtWidgets import ( 
     | 
|
| 
       38 
38 
     | 
    
         
             
            )
         
     | 
| 
       39 
39 
     | 
    
         | 
| 
       40 
40 
     | 
    
         
             
            from novelwriter import CONFIG, SHARED, __hexversion__, __version__
         
     | 
| 
       41 
     | 
    
         
            -
            from novelwriter.common import formatFileFilter, formatVersion, hexToInt
         
     | 
| 
      
 41 
     | 
    
         
            +
            from novelwriter.common import formatFileFilter, formatVersion, hexToInt, minmax
         
     | 
| 
       42 
42 
     | 
    
         
             
            from novelwriter.constants import nwConst
         
     | 
| 
       43 
43 
     | 
    
         
             
            from novelwriter.dialogs.about import GuiAbout
         
     | 
| 
       44 
44 
     | 
    
         
             
            from novelwriter.dialogs.preferences import GuiPreferences
         
     | 
| 
       45 
45 
     | 
    
         
             
            from novelwriter.dialogs.projectsettings import GuiProjectSettings
         
     | 
| 
       46 
46 
     | 
    
         
             
            from novelwriter.dialogs.wordlist import GuiWordList
         
     | 
| 
       47 
47 
     | 
    
         
             
            from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwFocus, nwItemType, nwView
         
     | 
| 
      
 48 
     | 
    
         
            +
            from novelwriter.extensions.progressbars import NProgressSimple
         
     | 
| 
       48 
49 
     | 
    
         
             
            from novelwriter.gui.doceditor import GuiDocEditor
         
     | 
| 
       49 
50 
     | 
    
         
             
            from novelwriter.gui.docviewer import GuiDocViewer
         
     | 
| 
       50 
51 
     | 
    
         
             
            from novelwriter.gui.docviewerpanel import GuiDocViewerPanel
         
     | 
| 
         @@ -67,16 +68,12 @@ logger = logging.getLogger(__name__) 
     | 
|
| 
       67 
68 
     | 
    
         | 
| 
       68 
69 
     | 
    
         | 
| 
       69 
70 
     | 
    
         
             
            class GuiMain(QMainWindow):
         
     | 
| 
       70 
     | 
    
         
            -
                """Main GUI Window
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                The Main GUI window class. It is the entry point of the
         
     | 
| 
       73 
     | 
    
         
            -
                application, and holds all runtime objects aside from the main
         
     | 
| 
       74 
     | 
    
         
            -
                Config instance, which is created before the Main GUI.
         
     | 
| 
      
 71 
     | 
    
         
            +
                """Main GUI Window.
         
     | 
| 
       75 
72 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
                The Main GUI  
     | 
| 
       77 
     | 
    
         
            -
                 
     | 
| 
       78 
     | 
    
         
            -
                 
     | 
| 
       79 
     | 
    
         
            -
                 
     | 
| 
      
 73 
     | 
    
         
            +
                The Main GUI window class is the entry point of the application. It
         
     | 
| 
      
 74 
     | 
    
         
            +
                is split up into GUI components, assembled in the init function.
         
     | 
| 
      
 75 
     | 
    
         
            +
                Tools and dialog windows are created on demand, but may be cached by
         
     | 
| 
      
 76 
     | 
    
         
            +
                the Qt library and reused unless explicitly freed after use.
         
     | 
| 
       80 
77 
     | 
    
         
             
                """
         
     | 
| 
       81 
78 
     | 
    
         | 
| 
       82 
79 
     | 
    
         
             
                def __init__(self) -> None:
         
     | 
| 
         @@ -103,7 +100,7 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       103 
100 
     | 
    
         
             
                    SHARED.initSharedData(self)
         
     | 
| 
       104 
101 
     | 
    
         | 
| 
       105 
102 
     | 
    
         
             
                    # Prepare Main Window
         
     | 
| 
       106 
     | 
    
         
            -
                    self. 
     | 
| 
      
 103 
     | 
    
         
            +
                    self._setWindowSize(CONFIG.mainWinSize)
         
     | 
| 
       107 
104 
     | 
    
         
             
                    self._updateWindowTitle()
         
     | 
| 
       108 
105 
     | 
    
         | 
| 
       109 
106 
     | 
    
         
             
                    nwIcon = CONFIG.assetPath("icons") / "novelwriter.svg"
         
     | 
| 
         @@ -169,9 +166,9 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       169 
166 
     | 
    
         
             
                    self.splitMain.addWidget(self.splitDocs)
         
     | 
| 
       170 
167 
     | 
    
         
             
                    self.splitMain.setOpaqueResize(False)
         
     | 
| 
       171 
168 
     | 
    
         
             
                    self.splitMain.setHandleWidth(4)
         
     | 
| 
       172 
     | 
    
         
            -
                    self.splitMain.setSizes(CONFIG.mainPanePos)
         
     | 
| 
       173 
     | 
    
         
            -
                    self.splitMain.setCollapsible(0, False)
         
     | 
| 
      
 169 
     | 
    
         
            +
                    self.splitMain.setSizes([max(s, 100) for s in CONFIG.mainPanePos])
         
     | 
| 
       174 
170 
     | 
    
         
             
                    self.splitMain.setCollapsible(0, False)
         
     | 
| 
      
 171 
     | 
    
         
            +
                    self.splitMain.setCollapsible(1, False)
         
     | 
| 
       175 
172 
     | 
    
         
             
                    self.splitMain.setStretchFactor(1, 0)
         
     | 
| 
       176 
173 
     | 
    
         
             
                    self.splitMain.setStretchFactor(1, 1)
         
     | 
| 
       177 
174 
     | 
    
         | 
| 
         @@ -185,6 +182,10 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       185 
182 
     | 
    
         
             
                    self.splitView.setVisible(False)
         
     | 
| 
       186 
183 
     | 
    
         
             
                    self.docEditor.closeSearch()
         
     | 
| 
       187 
184 
     | 
    
         | 
| 
      
 185 
     | 
    
         
            +
                    # Progress Bar
         
     | 
| 
      
 186 
     | 
    
         
            +
                    self.mainProgress = NProgressSimple(self)
         
     | 
| 
      
 187 
     | 
    
         
            +
                    self.mainProgress.setFixedHeight(2)
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
       188 
189 
     | 
    
         
             
                    # Assemble Main Window Elements
         
     | 
| 
       189 
190 
     | 
    
         
             
                    self.mainBox = QHBoxLayout()
         
     | 
| 
       190 
191 
     | 
    
         
             
                    self.mainBox.addWidget(self.sideBar)
         
     | 
| 
         @@ -192,8 +193,14 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       192 
193 
     | 
    
         
             
                    self.mainBox.setContentsMargins(0, 0, 0, 0)
         
     | 
| 
       193 
194 
     | 
    
         
             
                    self.mainBox.setSpacing(0)
         
     | 
| 
       194 
195 
     | 
    
         | 
| 
      
 196 
     | 
    
         
            +
                    self.outerBox = QVBoxLayout()
         
     | 
| 
      
 197 
     | 
    
         
            +
                    self.outerBox.addLayout(self.mainBox)
         
     | 
| 
      
 198 
     | 
    
         
            +
                    self.outerBox.addWidget(self.mainProgress)
         
     | 
| 
      
 199 
     | 
    
         
            +
                    self.outerBox.setContentsMargins(0, 0, 0, 0)
         
     | 
| 
      
 200 
     | 
    
         
            +
                    self.outerBox.setSpacing(0)
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
       195 
202 
     | 
    
         
             
                    self.mainWidget = QWidget(self)
         
     | 
| 
       196 
     | 
    
         
            -
                    self.mainWidget.setLayout(self. 
     | 
| 
      
 203 
     | 
    
         
            +
                    self.mainWidget.setLayout(self.outerBox)
         
     | 
| 
       197 
204 
     | 
    
         | 
| 
       198 
205 
     | 
    
         
             
                    self.setMenuBar(self.mainMenu)
         
     | 
| 
       199 
206 
     | 
    
         
             
                    self.setCentralWidget(self.mainWidget)
         
     | 
| 
         @@ -316,13 +323,10 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       316 
323 
     | 
    
         
             
                    self.mainStatus.setStatusMessage(self.tr("novelWriter is ready ..."))
         
     | 
| 
       317 
324 
     | 
    
         
             
                    CONFIG.splashMessage("novelWriter is ready ...")
         
     | 
| 
       318 
325 
     | 
    
         | 
| 
       319 
     | 
    
         
            -
                    return
         
     | 
| 
       320 
     | 
    
         
            -
             
     | 
| 
       321 
326 
     | 
    
         
             
                def initMain(self) -> None:
         
     | 
| 
       322 
327 
     | 
    
         
             
                    """Initialise elements that depend on user settings."""
         
     | 
| 
       323 
328 
     | 
    
         
             
                    self.asProjTimer.setInterval(int(CONFIG.autoSaveProj*1000))
         
     | 
| 
       324 
329 
     | 
    
         
             
                    self.asDocTimer.setInterval(int(CONFIG.autoSaveDoc*1000))
         
     | 
| 
       325 
     | 
    
         
            -
                    return
         
     | 
| 
       326 
330 
     | 
    
         | 
| 
       327 
331 
     | 
    
         
             
                def postLaunchTasks(self, cmdOpen: str | None) -> None:
         
     | 
| 
       328 
332 
     | 
    
         
             
                    """Process tasks after the main window has been created."""
         
     | 
| 
         @@ -344,8 +348,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       344 
348 
     | 
    
         
             
                    # before showing any dialogs
         
     | 
| 
       345 
349 
     | 
    
         
             
                    QTimer.singleShot(50, self.showPostLaunchDialogs)
         
     | 
| 
       346 
350 
     | 
    
         | 
| 
       347 
     | 
    
         
            -
                    return
         
     | 
| 
       348 
     | 
    
         
            -
             
     | 
| 
       349 
351 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       350 
352 
     | 
    
         
             
                def showPostLaunchDialogs(self) -> None:
         
     | 
| 
       351 
353 
     | 
    
         
             
                    """Show post launch dialogs."""
         
     | 
| 
         @@ -363,8 +365,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       363 
365 
     | 
    
         
             
                        ).format(f"<a href='{nwConst.URL_RELEASES}'>", "</a>")
         
     | 
| 
       364 
366 
     | 
    
         
             
                        SHARED.info(f"{trVersion}<br>{trRelease}")
         
     | 
| 
       365 
367 
     | 
    
         | 
| 
       366 
     | 
    
         
            -
                    return
         
     | 
| 
       367 
     | 
    
         
            -
             
     | 
| 
       368 
368 
     | 
    
         
             
                ##
         
     | 
| 
       369 
369 
     | 
    
         
             
                #  Project Actions
         
     | 
| 
       370 
370 
     | 
    
         
             
                ##
         
     | 
| 
         @@ -497,7 +497,8 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       497 
497 
     | 
    
         | 
| 
       498 
498 
     | 
    
         
             
                    # Check if we need to rebuild the index
         
     | 
| 
       499 
499 
     | 
    
         
             
                    if SHARED.project.index.indexBroken:
         
     | 
| 
       500 
     | 
    
         
            -
                        SHARED. 
     | 
| 
      
 500 
     | 
    
         
            +
                        if not SHARED.project.index.indexUpgrade:
         
     | 
| 
      
 501 
     | 
    
         
            +
                            SHARED.warn(self.tr("The project index is broken. Rebuilding index."))
         
     | 
| 
       501 
502 
     | 
    
         
             
                        self.rebuildIndex()
         
     | 
| 
       502 
503 
     | 
    
         | 
| 
       503 
504 
     | 
    
         
             
                    # Make sure the changed status is set to false on things opened
         
     | 
| 
         @@ -527,7 +528,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       527 
528 
     | 
    
         
             
                            SHARED.setFocusMode(False)
         
     | 
| 
       528 
529 
     | 
    
         
             
                        self.saveDocument()
         
     | 
| 
       529 
530 
     | 
    
         
             
                        self.docEditor.clearEditor()
         
     | 
| 
       530 
     | 
    
         
            -
                    return
         
     | 
| 
       531 
531 
     | 
    
         | 
| 
       532 
532 
     | 
    
         
             
                def openDocument(
         
     | 
| 
       533 
533 
     | 
    
         
             
                    self,
         
     | 
| 
         @@ -584,7 +584,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       584 
584 
     | 
    
         
             
                            self.openDocument(nHandle, tLine=1, doScroll=True)
         
     | 
| 
       585 
585 
     | 
    
         
             
                        elif wrapAround:
         
     | 
| 
       586 
586 
     | 
    
         
             
                            self.openDocument(fHandle, tLine=1, doScroll=True)
         
     | 
| 
       587 
     | 
    
         
            -
                    return
         
     | 
| 
       588 
587 
     | 
    
         | 
| 
       589 
588 
     | 
    
         
             
                def saveDocument(self, force: bool = False) -> None:
         
     | 
| 
       590 
589 
     | 
    
         
             
                    """Save the current documents."""
         
     | 
| 
         @@ -592,13 +591,11 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       592 
591 
     | 
    
         
             
                        self.docEditor.saveCursorPosition()
         
     | 
| 
       593 
592 
     | 
    
         
             
                        if force or self.docEditor.docChanged:
         
     | 
| 
       594 
593 
     | 
    
         
             
                            self.docEditor.saveText()
         
     | 
| 
       595 
     | 
    
         
            -
                    return
         
     | 
| 
       596 
594 
     | 
    
         | 
| 
       597 
595 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       598 
596 
     | 
    
         
             
                def forceSaveDocument(self) -> None:
         
     | 
| 
       599 
597 
     | 
    
         
             
                    """Save document even of it has not changed."""
         
     | 
| 
       600 
598 
     | 
    
         
             
                    self.saveDocument(force=True)
         
     | 
| 
       601 
     | 
    
         
            -
                    return
         
     | 
| 
       602 
599 
     | 
    
         | 
| 
       603 
600 
     | 
    
         
             
                def viewDocument(self, tHandle: str | None = None, sTitle: str | None = None) -> bool:
         
     | 
| 
       604 
601 
     | 
    
         
             
                    """Load a document for viewing in the view panel."""
         
     | 
| 
         @@ -733,7 +730,7 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       733 
730 
     | 
    
         | 
| 
       734 
731 
     | 
    
         
             
                    return
         
     | 
| 
       735 
732 
     | 
    
         | 
| 
       736 
     | 
    
         
            -
                def rebuildIndex(self 
     | 
| 
      
 733 
     | 
    
         
            +
                def rebuildIndex(self) -> None:
         
     | 
| 
       737 
734 
     | 
    
         
             
                    """Rebuild the entire index."""
         
     | 
| 
       738 
735 
     | 
    
         
             
                    if SHARED.hasProject:
         
     | 
| 
       739 
736 
     | 
    
         
             
                        logger.info("Rebuilding index ...")
         
     | 
| 
         @@ -750,10 +747,7 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       750 
747 
     | 
    
         
             
                        self._updateStatusWordCount()
         
     | 
| 
       751 
748 
     | 
    
         
             
                        QApplication.restoreOverrideCursor()
         
     | 
| 
       752 
749 
     | 
    
         | 
| 
       753 
     | 
    
         
            -
                         
     | 
| 
       754 
     | 
    
         
            -
                            SHARED.info(self.tr("The project index has been successfully rebuilt."))
         
     | 
| 
       755 
     | 
    
         
            -
             
     | 
| 
       756 
     | 
    
         
            -
                    return
         
     | 
| 
      
 750 
     | 
    
         
            +
                        SHARED.info(self.tr("The project index has been successfully rebuilt."))
         
     | 
| 
       757 
751 
     | 
    
         | 
| 
       758 
752 
     | 
    
         
             
                ##
         
     | 
| 
       759 
753 
     | 
    
         
             
                #  Main Dialogs
         
     | 
| 
         @@ -765,7 +759,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       765 
759 
     | 
    
         
             
                    dialog = GuiWelcome(self)
         
     | 
| 
       766 
760 
     | 
    
         
             
                    dialog.openProjectRequest.connect(self._openProjectFromWelcome)
         
     | 
| 
       767 
761 
     | 
    
         
             
                    dialog.exec()
         
     | 
| 
       768 
     | 
    
         
            -
                    return
         
     | 
| 
       769 
762 
     | 
    
         | 
| 
       770 
763 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       771 
764 
     | 
    
         
             
                def showPreferencesDialog(self) -> None:
         
     | 
| 
         @@ -773,7 +766,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       773 
766 
     | 
    
         
             
                    dialog = GuiPreferences(self)
         
     | 
| 
       774 
767 
     | 
    
         
             
                    dialog.newPreferencesReady.connect(self._processConfigChanges)
         
     | 
| 
       775 
768 
     | 
    
         
             
                    dialog.exec()
         
     | 
| 
       776 
     | 
    
         
            -
                    return
         
     | 
| 
       777 
769 
     | 
    
         | 
| 
       778 
770 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       779 
771 
     | 
    
         
             
                @pyqtSlot(int)
         
     | 
| 
         @@ -783,7 +775,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       783 
775 
     | 
    
         
             
                        dialog = GuiProjectSettings(self, gotoPage=focusTab)
         
     | 
| 
       784 
776 
     | 
    
         
             
                        dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
         
     | 
| 
       785 
777 
     | 
    
         
             
                        dialog.exec()
         
     | 
| 
       786 
     | 
    
         
            -
                    return
         
     | 
| 
       787 
778 
     | 
    
         | 
| 
       788 
779 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       789 
780 
     | 
    
         
             
                def showNovelDetailsDialog(self) -> None:
         
     | 
| 
         @@ -792,7 +783,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       792 
783 
     | 
    
         
             
                        dialog = GuiNovelDetails(self)
         
     | 
| 
       793 
784 
     | 
    
         
             
                        dialog.activateDialog()
         
     | 
| 
       794 
785 
     | 
    
         
             
                        dialog.updateValues()
         
     | 
| 
       795 
     | 
    
         
            -
                    return
         
     | 
| 
       796 
786 
     | 
    
         | 
| 
       797 
787 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       798 
788 
     | 
    
         
             
                def showBuildManuscriptDialog(self) -> None:
         
     | 
| 
         @@ -802,7 +792,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       802 
792 
     | 
    
         
             
                            dialog = GuiManuscript(self)
         
     | 
| 
       803 
793 
     | 
    
         
             
                        dialog.activateDialog()
         
     | 
| 
       804 
794 
     | 
    
         
             
                        dialog.loadContent()
         
     | 
| 
       805 
     | 
    
         
            -
                    return
         
     | 
| 
       806 
795 
     | 
    
         | 
| 
       807 
796 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       808 
797 
     | 
    
         
             
                def showProjectWordListDialog(self) -> None:
         
     | 
| 
         @@ -811,7 +800,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       811 
800 
     | 
    
         
             
                        dialog = GuiWordList(self)
         
     | 
| 
       812 
801 
     | 
    
         
             
                        dialog.newWordListReady.connect(self._processWordListChanges)
         
     | 
| 
       813 
802 
     | 
    
         
             
                        dialog.exec()
         
     | 
| 
       814 
     | 
    
         
            -
                    return
         
     | 
| 
       815 
803 
     | 
    
         | 
| 
       816 
804 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       817 
805 
     | 
    
         
             
                def showWritingStatsDialog(self) -> None:
         
     | 
| 
         @@ -821,31 +809,32 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       821 
809 
     | 
    
         
             
                            dialog = GuiWritingStats(self)
         
     | 
| 
       822 
810 
     | 
    
         
             
                        dialog.activateDialog()
         
     | 
| 
       823 
811 
     | 
    
         
             
                        dialog.populateGUI()
         
     | 
| 
       824 
     | 
    
         
            -
                    return
         
     | 
| 
       825 
812 
     | 
    
         | 
| 
       826 
813 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       827 
814 
     | 
    
         
             
                def showAboutNWDialog(self) -> None:
         
     | 
| 
       828 
815 
     | 
    
         
             
                    """Show the novelWriter about dialog."""
         
     | 
| 
       829 
816 
     | 
    
         
             
                    dialog = GuiAbout(self)
         
     | 
| 
       830 
817 
     | 
    
         
             
                    dialog.exec()
         
     | 
| 
       831 
     | 
    
         
            -
                    return
         
     | 
| 
       832 
818 
     | 
    
         | 
| 
       833 
819 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       834 
820 
     | 
    
         
             
                def showAboutQtDialog(self) -> None:
         
     | 
| 
       835 
821 
     | 
    
         
             
                    """Show the Qt about dialog."""
         
     | 
| 
       836 
822 
     | 
    
         
             
                    msgBox = QMessageBox(self)
         
     | 
| 
       837 
823 
     | 
    
         
             
                    msgBox.aboutQt(self, "About Qt")
         
     | 
| 
       838 
     | 
    
         
            -
                    return
         
     | 
| 
       839 
824 
     | 
    
         | 
| 
       840 
825 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       841 
826 
     | 
    
         
             
                def showDictionariesDialog(self) -> None:
         
     | 
| 
       842 
     | 
    
         
            -
                    """Show the download dictionaries dialog 
     | 
| 
      
 827 
     | 
    
         
            +
                    """Show the download dictionaries dialog and update language
         
     | 
| 
      
 828 
     | 
    
         
            +
                    list when closed.
         
     | 
| 
      
 829 
     | 
    
         
            +
                    """
         
     | 
| 
       843 
830 
     | 
    
         
             
                    dialog = GuiDictionaries(self)
         
     | 
| 
       844 
831 
     | 
    
         
             
                    dialog.activateDialog()
         
     | 
| 
       845 
832 
     | 
    
         
             
                    if not dialog.initDialog():
         
     | 
| 
       846 
833 
     | 
    
         
             
                        dialog.close()
         
     | 
| 
       847 
834 
     | 
    
         
             
                        SHARED.error(self.tr("Could not initialise the dialog."))
         
     | 
| 
       848 
     | 
    
         
            -
             
     | 
| 
      
 835 
     | 
    
         
            +
                        return
         
     | 
| 
      
 836 
     | 
    
         
            +
             
     | 
| 
      
 837 
     | 
    
         
            +
                    dialog.finished.connect(self.mainMenu.updateSpellCheckLanguages)
         
     | 
| 
       849 
838 
     | 
    
         | 
| 
       850 
839 
     | 
    
         
             
                ##
         
     | 
| 
       851 
840 
     | 
    
         
             
                #  Main Window Actions
         
     | 
| 
         @@ -902,16 +891,50 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       902 
891 
     | 
    
         | 
| 
       903 
892 
     | 
    
         
             
                    return not self.splitView.isVisible()
         
     | 
| 
       904 
893 
     | 
    
         | 
| 
      
 894 
     | 
    
         
            +
                def checkThemeUpdate(self) -> None:
         
     | 
| 
      
 895 
     | 
    
         
            +
                    """Load theme if mode changed."""
         
     | 
| 
      
 896 
     | 
    
         
            +
                    if SHARED.theme.loadTheme():
         
     | 
| 
      
 897 
     | 
    
         
            +
                        self.refreshThemeColors(syntax=True)
         
     | 
| 
      
 898 
     | 
    
         
            +
                        self.docEditor.initEditor()
         
     | 
| 
      
 899 
     | 
    
         
            +
                        self.docViewer.initViewer()
         
     | 
| 
      
 900 
     | 
    
         
            +
             
     | 
| 
      
 901 
     | 
    
         
            +
                def refreshThemeColors(self, syntax: bool = False, force: bool = False) -> None:
         
     | 
| 
      
 902 
     | 
    
         
            +
                    """Refresh the GUI theme."""
         
     | 
| 
      
 903 
     | 
    
         
            +
                    SHARED.theme.loadTheme(force=force)
         
     | 
| 
      
 904 
     | 
    
         
            +
                    SHARED.project.updateTheme()
         
     | 
| 
      
 905 
     | 
    
         
            +
                    self.setPalette(QApplication.palette())
         
     | 
| 
      
 906 
     | 
    
         
            +
                    self.docEditor.updateTheme()
         
     | 
| 
      
 907 
     | 
    
         
            +
                    self.docViewer.updateTheme()
         
     | 
| 
      
 908 
     | 
    
         
            +
                    self.docViewerPanel.updateTheme()
         
     | 
| 
      
 909 
     | 
    
         
            +
                    self.sideBar.updateTheme()
         
     | 
| 
      
 910 
     | 
    
         
            +
                    self.projView.updateTheme()
         
     | 
| 
      
 911 
     | 
    
         
            +
                    self.novelView.updateTheme()
         
     | 
| 
      
 912 
     | 
    
         
            +
                    self.projSearch.updateTheme()
         
     | 
| 
      
 913 
     | 
    
         
            +
                    self.outlineView.updateTheme()
         
     | 
| 
      
 914 
     | 
    
         
            +
                    self.itemDetails.updateTheme()
         
     | 
| 
      
 915 
     | 
    
         
            +
                    self.mainStatus.updateTheme()
         
     | 
| 
      
 916 
     | 
    
         
            +
                    SHARED.project.tree.refreshAllItems()
         
     | 
| 
      
 917 
     | 
    
         
            +
             
     | 
| 
      
 918 
     | 
    
         
            +
                    if dialog := SHARED.findTopLevelWidget(GuiManuscript):
         
     | 
| 
      
 919 
     | 
    
         
            +
                        dialog.updateTheme()
         
     | 
| 
      
 920 
     | 
    
         
            +
             
     | 
| 
      
 921 
     | 
    
         
            +
                    if syntax:
         
     | 
| 
      
 922 
     | 
    
         
            +
                        self.docEditor.updateSyntaxColors()
         
     | 
| 
      
 923 
     | 
    
         
            +
             
     | 
| 
       905 
924 
     | 
    
         
             
                ##
         
     | 
| 
       906 
925 
     | 
    
         
             
                #  Events
         
     | 
| 
       907 
926 
     | 
    
         
             
                ##
         
     | 
| 
       908 
927 
     | 
    
         | 
| 
      
 928 
     | 
    
         
            +
                def changeEvent(self, event: QEvent) -> None:
         
     | 
| 
      
 929 
     | 
    
         
            +
                    """Capture application change events."""
         
     | 
| 
      
 930 
     | 
    
         
            +
                    if int(event.type()) == 210:  # ThemeChange
         
     | 
| 
      
 931 
     | 
    
         
            +
                        self.checkThemeUpdate()
         
     | 
| 
      
 932 
     | 
    
         
            +
             
     | 
| 
       909 
933 
     | 
    
         
             
                def closeEvent(self, event: QCloseEvent) -> None:
         
     | 
| 
       910 
934 
     | 
    
         
             
                    """Capture the closing event of the GUI and call the close
         
     | 
| 
       911 
935 
     | 
    
         
             
                    function to handle all the close process steps.
         
     | 
| 
       912 
936 
     | 
    
         
             
                    """
         
     | 
| 
       913 
937 
     | 
    
         
             
                    event.accept() if self.closeMain() else event.ignore()
         
     | 
| 
       914 
     | 
    
         
            -
                    return
         
     | 
| 
       915 
938 
     | 
    
         | 
| 
       916 
939 
     | 
    
         
             
                ##
         
     | 
| 
       917 
940 
     | 
    
         
             
                #  Public Slots
         
     | 
| 
         @@ -919,30 +942,26 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       919 
942 
     | 
    
         | 
| 
       920 
943 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       921 
944 
     | 
    
         
             
                def toggleFullScreenMode(self) -> None:
         
     | 
| 
       922 
     | 
    
         
            -
                    """Toggle full screen mode"""
         
     | 
| 
      
 945 
     | 
    
         
            +
                    """Toggle full screen mode."""
         
     | 
| 
       923 
946 
     | 
    
         
             
                    self.setWindowState(self.windowState() ^ Qt.WindowState.WindowFullScreen)
         
     | 
| 
       924 
     | 
    
         
            -
                    return
         
     | 
| 
       925 
947 
     | 
    
         | 
| 
       926 
948 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       927 
949 
     | 
    
         
             
                def closeDocEditor(self) -> None:
         
     | 
| 
       928 
950 
     | 
    
         
             
                    """Close the document editor. This does not hide the editor."""
         
     | 
| 
       929 
951 
     | 
    
         
             
                    self.closeDocument()
         
     | 
| 
       930 
952 
     | 
    
         
             
                    SHARED.project.data.setLastHandle(None, "editor")
         
     | 
| 
       931 
     | 
    
         
            -
                    return
         
     | 
| 
       932 
953 
     | 
    
         | 
| 
       933 
954 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       934 
955 
     | 
    
         
             
                def closeDocViewer(self) -> None:
         
     | 
| 
       935 
956 
     | 
    
         
             
                    """Close the document viewer."""
         
     | 
| 
       936 
957 
     | 
    
         
             
                    self.closeViewerPanel()
         
     | 
| 
       937 
958 
     | 
    
         
             
                    SHARED.project.data.setLastHandle(None, "viewer")
         
     | 
| 
       938 
     | 
    
         
            -
                    return
         
     | 
| 
       939 
959 
     | 
    
         | 
| 
       940 
960 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       941 
961 
     | 
    
         
             
                def toggleFocusMode(self) -> None:
         
     | 
| 
       942 
962 
     | 
    
         
             
                    """Toggle focus mode."""
         
     | 
| 
       943 
963 
     | 
    
         
             
                    if self.docEditor.docHandle:
         
     | 
| 
       944 
964 
     | 
    
         
             
                        SHARED.setFocusMode(not SHARED.focusMode)
         
     | 
| 
       945 
     | 
    
         
            -
                    return
         
     | 
| 
       946 
965 
     | 
    
         | 
| 
       947 
966 
     | 
    
         
             
                ##
         
     | 
| 
       948 
967 
     | 
    
         
             
                #  Private Slots
         
     | 
| 
         @@ -960,7 +979,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       960 
979 
     | 
    
         
             
                            docViewer = True
         
     | 
| 
       961 
980 
     | 
    
         
             
                        self.docEditor.changeFocusState(docEditor)
         
     | 
| 
       962 
981 
     | 
    
         
             
                        self.docViewer.changeFocusState(docViewer)
         
     | 
| 
       963 
     | 
    
         
            -
                    return
         
     | 
| 
       964 
982 
     | 
    
         | 
| 
       965 
983 
     | 
    
         
             
                @pyqtSlot(bool)
         
     | 
| 
       966 
984 
     | 
    
         
             
                def _focusModeChanged(self, focusMode: bool) -> None:
         
     | 
| 
         @@ -991,7 +1009,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       991 
1009 
     | 
    
         | 
| 
       992 
1010 
     | 
    
         
             
                    if cursorVisible:
         
     | 
| 
       993 
1011 
     | 
    
         
             
                        self.docEditor.ensureCursorVisibleNoCentre()
         
     | 
| 
       994 
     | 
    
         
            -
                    return
         
     | 
| 
       995 
1012 
     | 
    
         | 
| 
       996 
1013 
     | 
    
         
             
                @pyqtSlot(nwFocus)
         
     | 
| 
       997 
1014 
     | 
    
         
             
                def _switchFocus(self, paneNo: nwFocus) -> None:
         
     | 
| 
         @@ -1017,16 +1034,16 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1017 
1034 
     | 
    
         
             
                        fP = self.projView.treeHasFocus()
         
     | 
| 
       1018 
1035 
     | 
    
         
             
                        fN = self.novelView.treeHasFocus()
         
     | 
| 
       1019 
1036 
     | 
    
         | 
| 
       1020 
     | 
    
         
            -
                        self._changeView(nwView.EDITOR)
         
     | 
| 
      
 1037 
     | 
    
         
            +
                        self._changeView(nwView.EDITOR, exitFocus=True)
         
     | 
| 
       1021 
1038 
     | 
    
         
             
                        if (vM and ((vP and fP) or (vN and not fN))) or (not vM and vN):
         
     | 
| 
       1022 
     | 
    
         
            -
                            self._changeView(nwView.NOVEL)
         
     | 
| 
      
 1039 
     | 
    
         
            +
                            self._changeView(nwView.NOVEL, exitFocus=True)
         
     | 
| 
       1023 
1040 
     | 
    
         
             
                            self.novelView.setTreeFocus()
         
     | 
| 
       1024 
1041 
     | 
    
         
             
                        else:
         
     | 
| 
       1025 
     | 
    
         
            -
                            self._changeView(nwView.PROJECT)
         
     | 
| 
      
 1042 
     | 
    
         
            +
                            self._changeView(nwView.PROJECT, exitFocus=True)
         
     | 
| 
       1026 
1043 
     | 
    
         
             
                            self.projView.setTreeFocus()
         
     | 
| 
       1027 
1044 
     | 
    
         | 
| 
       1028 
1045 
     | 
    
         
             
                    elif paneNo == nwFocus.DOCUMENT:
         
     | 
| 
       1029 
     | 
    
         
            -
                        self._changeView(nwView.EDITOR)
         
     | 
| 
      
 1046 
     | 
    
         
            +
                        self._changeView(nwView.EDITOR, exitFocus=True)
         
     | 
| 
       1030 
1047 
     | 
    
         
             
                        hasViewer = self.splitView.isVisible()
         
     | 
| 
       1031 
1048 
     | 
    
         
             
                        if hasViewer and self.docEditor.anyFocus():
         
     | 
| 
       1032 
1049 
     | 
    
         
             
                            self.docViewer.setFocus()
         
     | 
| 
         @@ -1036,11 +1053,9 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1036 
1053 
     | 
    
         
             
                            self.docEditor.setFocus()
         
     | 
| 
       1037 
1054 
     | 
    
         | 
| 
       1038 
1055 
     | 
    
         
             
                    elif paneNo == nwFocus.OUTLINE:
         
     | 
| 
       1039 
     | 
    
         
            -
                        self._changeView(nwView.OUTLINE)
         
     | 
| 
      
 1056 
     | 
    
         
            +
                        self._changeView(nwView.OUTLINE, exitFocus=True)
         
     | 
| 
       1040 
1057 
     | 
    
         
             
                        self.outlineView.setTreeFocus()
         
     | 
| 
       1041 
1058 
     | 
    
         | 
| 
       1042 
     | 
    
         
            -
                    return
         
     | 
| 
       1043 
     | 
    
         
            -
             
     | 
| 
       1044 
1059 
     | 
    
         
             
                @pyqtSlot(bool, bool, bool, bool)
         
     | 
| 
       1045 
1060 
     | 
    
         
             
                def _processConfigChanges(self, restart: bool, tree: bool, theme: bool, syntax: bool) -> None:
         
     | 
| 
       1046 
1061 
     | 
    
         
             
                    """Refresh GUI based on flags from the Preferences dialog."""
         
     | 
| 
         @@ -1054,23 +1069,7 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1054 
1069 
     | 
    
         
             
                        self.novelView.refreshCurrentTree()
         
     | 
| 
       1055 
1070 
     | 
    
         | 
| 
       1056 
1071 
     | 
    
         
             
                    if theme:
         
     | 
| 
       1057 
     | 
    
         
            -
                         
     | 
| 
       1058 
     | 
    
         
            -
                        self.setPalette(QApplication.palette())
         
     | 
| 
       1059 
     | 
    
         
            -
                        self.docEditor.updateTheme()
         
     | 
| 
       1060 
     | 
    
         
            -
                        self.docViewer.updateTheme()
         
     | 
| 
       1061 
     | 
    
         
            -
                        self.docViewerPanel.updateTheme()
         
     | 
| 
       1062 
     | 
    
         
            -
                        self.sideBar.updateTheme()
         
     | 
| 
       1063 
     | 
    
         
            -
                        self.projView.updateTheme()
         
     | 
| 
       1064 
     | 
    
         
            -
                        self.novelView.updateTheme()
         
     | 
| 
       1065 
     | 
    
         
            -
                        self.projSearch.updateTheme()
         
     | 
| 
       1066 
     | 
    
         
            -
                        self.outlineView.updateTheme()
         
     | 
| 
       1067 
     | 
    
         
            -
                        self.itemDetails.updateTheme()
         
     | 
| 
       1068 
     | 
    
         
            -
                        self.mainStatus.updateTheme()
         
     | 
| 
       1069 
     | 
    
         
            -
                        SHARED.project.tree.refreshAllItems()
         
     | 
| 
       1070 
     | 
    
         
            -
             
     | 
| 
       1071 
     | 
    
         
            -
                    if syntax:
         
     | 
| 
       1072 
     | 
    
         
            -
                        SHARED.theme.loadSyntax()
         
     | 
| 
       1073 
     | 
    
         
            -
                        self.docEditor.updateSyntaxColors()
         
     | 
| 
      
 1072 
     | 
    
         
            +
                        self.refreshThemeColors(syntax=syntax, force=True)
         
     | 
| 
       1074 
1073 
     | 
    
         | 
| 
       1075 
1074 
     | 
    
         
             
                    self.docEditor.initEditor()
         
     | 
| 
       1076 
1075 
     | 
    
         
             
                    self.docViewer.initViewer()
         
     | 
| 
         @@ -1088,8 +1087,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1088 
1087 
     | 
    
         
             
                            "Some changes will not be applied until novelWriter has been restarted."
         
     | 
| 
       1089 
1088 
     | 
    
         
             
                        ))
         
     | 
| 
       1090 
1089 
     | 
    
         | 
| 
       1091 
     | 
    
         
            -
                    return
         
     | 
| 
       1092 
     | 
    
         
            -
             
     | 
| 
       1093 
1090 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1094 
1091 
     | 
    
         
             
                def _processProjectSettingsChanges(self) -> None:
         
     | 
| 
       1095 
1092 
     | 
    
         
             
                    """Refresh data dependent on project settings."""
         
     | 
| 
         @@ -1097,7 +1094,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1097 
1094 
     | 
    
         
             
                    SHARED.updateSpellCheckLanguage()
         
     | 
| 
       1098 
1095 
     | 
    
         
             
                    self.itemDetails.refreshDetails()
         
     | 
| 
       1099 
1096 
     | 
    
         
             
                    self._updateWindowTitle(SHARED.project.data.name)
         
     | 
| 
       1100 
     | 
    
         
            -
                    return
         
     | 
| 
       1101 
1097 
     | 
    
         | 
| 
       1102 
1098 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1103 
1099 
     | 
    
         
             
                def _processWordListChanges(self) -> None:
         
     | 
| 
         @@ -1105,7 +1101,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1105 
1101 
     | 
    
         
             
                    logger.debug("Reloading word list")
         
     | 
| 
       1106 
1102 
     | 
    
         
             
                    SHARED.updateSpellCheckLanguage(reload=True)
         
     | 
| 
       1107 
1103 
     | 
    
         
             
                    self.docEditor.spellCheckDocument()
         
     | 
| 
       1108 
     | 
    
         
            -
                    return
         
     | 
| 
       1109 
1104 
     | 
    
         | 
| 
       1110 
1105 
     | 
    
         
             
                @pyqtSlot(str, nwDocMode)
         
     | 
| 
       1111 
1106 
     | 
    
         
             
                def _followTag(self, tag: str, mode: nwDocMode) -> None:
         
     | 
| 
         @@ -1124,7 +1119,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1124 
1119 
     | 
    
         
             
                            self.openDocument(tHandle, sTitle=sTitle)
         
     | 
| 
       1125 
1120 
     | 
    
         
             
                        elif mode == nwDocMode.VIEW:
         
     | 
| 
       1126 
1121 
     | 
    
         
             
                            self.viewDocument(tHandle=tHandle, sTitle=sTitle)
         
     | 
| 
       1127 
     | 
    
         
            -
                    return
         
     | 
| 
       1128 
1122 
     | 
    
         | 
| 
       1129 
1123 
     | 
    
         
             
                @pyqtSlot(Path)
         
     | 
| 
       1130 
1124 
     | 
    
         
             
                def _openProjectFromWelcome(self, path: Path) -> None:
         
     | 
| 
         @@ -1133,7 +1127,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1133 
1127 
     | 
    
         
             
                    self.openProject(path)
         
     | 
| 
       1134 
1128 
     | 
    
         
             
                    if not SHARED.hasProject:
         
     | 
| 
       1135 
1129 
     | 
    
         
             
                        self.showWelcomeDialog()
         
     | 
| 
       1136 
     | 
    
         
            -
                    return
         
     | 
| 
       1137 
1130 
     | 
    
         | 
| 
       1138 
1131 
     | 
    
         
             
                @pyqtSlot(str, nwDocMode, str, bool)
         
     | 
| 
       1139 
1132 
     | 
    
         
             
                def _openDocument(self, tHandle: str, mode: nwDocMode, sTitle: str, setFocus: bool) -> None:
         
     | 
| 
         @@ -1143,7 +1136,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1143 
1136 
     | 
    
         
             
                            self.openDocument(tHandle, sTitle=sTitle, changeFocus=setFocus)
         
     | 
| 
       1144 
1137 
     | 
    
         
             
                        elif mode == nwDocMode.VIEW:
         
     | 
| 
       1145 
1138 
     | 
    
         
             
                            self.viewDocument(tHandle=tHandle, sTitle=sTitle)
         
     | 
| 
       1146 
     | 
    
         
            -
                    return
         
     | 
| 
       1147 
1139 
     | 
    
         | 
| 
       1148 
1140 
     | 
    
         
             
                @pyqtSlot(str, int, int, bool)
         
     | 
| 
       1149 
1141 
     | 
    
         
             
                def _openDocumentSelection(
         
     | 
| 
         @@ -1152,7 +1144,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1152 
1144 
     | 
    
         
             
                    """Open a document and select a section of the text."""
         
     | 
| 
       1153 
1145 
     | 
    
         
             
                    if self.openDocument(tHandle, changeFocus=changeFocus):
         
     | 
| 
       1154 
1146 
     | 
    
         
             
                        self.docEditor.setCursorSelection(selStart, selLength)
         
     | 
| 
       1155 
     | 
    
         
            -
                    return
         
     | 
| 
       1156 
1147 
     | 
    
         | 
| 
       1157 
1148 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1158 
1149 
     | 
    
         
             
                def _reloadViewer(self) -> None:
         
     | 
| 
         @@ -1161,11 +1152,13 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1161 
1152 
     | 
    
         
             
                        # If the two panels have the same document, save any changes in the editor
         
     | 
| 
       1162 
1153 
     | 
    
         
             
                        self.saveDocument()
         
     | 
| 
       1163 
1154 
     | 
    
         
             
                    self.docViewer.reloadText()
         
     | 
| 
       1164 
     | 
    
         
            -
                    return
         
     | 
| 
       1165 
1155 
     | 
    
         | 
| 
       1166 
1156 
     | 
    
         
             
                @pyqtSlot(nwView)
         
     | 
| 
       1167 
     | 
    
         
            -
                def _changeView(self, view: nwView) -> None:
         
     | 
| 
      
 1157 
     | 
    
         
            +
                def _changeView(self, view: nwView, exitFocus: bool = False) -> None:
         
     | 
| 
       1168 
1158 
     | 
    
         
             
                    """Handle the requested change of view from the GuiViewBar."""
         
     | 
| 
      
 1159 
     | 
    
         
            +
                    if exitFocus:
         
     | 
| 
      
 1160 
     | 
    
         
            +
                        SHARED.setFocusMode(False)
         
     | 
| 
      
 1161 
     | 
    
         
            +
             
     | 
| 
       1169 
1162 
     | 
    
         
             
                    if view == nwView.EDITOR:
         
     | 
| 
       1170 
1163 
     | 
    
         
             
                        # Only change the main stack, but not the project stack
         
     | 
| 
       1171 
1164 
     | 
    
         
             
                        self.mainStack.setCurrentWidget(self.splitMain)
         
     | 
| 
         @@ -1189,8 +1182,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1189 
1182 
     | 
    
         
             
                    isNovel = self.projStack.currentWidget() == self.novelView
         
     | 
| 
       1190 
1183 
     | 
    
         
             
                    self.novelView.setActive(isMain and isNovel)
         
     | 
| 
       1191 
1184 
     | 
    
         | 
| 
       1192 
     | 
    
         
            -
                    return
         
     | 
| 
       1193 
     | 
    
         
            -
             
     | 
| 
       1194 
1185 
     | 
    
         
             
                @pyqtSlot(nwDocAction)
         
     | 
| 
       1195 
1186 
     | 
    
         
             
                def _passDocumentAction(self, action: nwDocAction) -> None:
         
     | 
| 
       1196 
1187 
     | 
    
         
             
                    """Pass on a document action to the editor or viewer based on
         
     | 
| 
         @@ -1200,7 +1191,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1200 
1191 
     | 
    
         
             
                        self.docEditor.docAction(action)
         
     | 
| 
       1201 
1192 
     | 
    
         
             
                    elif self.docViewer.hasFocus():
         
     | 
| 
       1202 
1193 
     | 
    
         
             
                        self.docViewer.docAction(action)
         
     | 
| 
       1203 
     | 
    
         
            -
                    return
         
     | 
| 
       1204 
1194 
     | 
    
         | 
| 
       1205 
1195 
     | 
    
         
             
                @pyqtSlot(str)
         
     | 
| 
       1206 
1196 
     | 
    
         
             
                @pyqtSlot(nwDocInsert)
         
     | 
| 
         @@ -1210,14 +1200,12 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1210 
1200 
     | 
    
         
             
                    """
         
     | 
| 
       1211 
1201 
     | 
    
         
             
                    if self.docEditor.hasFocus():
         
     | 
| 
       1212 
1202 
     | 
    
         
             
                        self.docEditor.insertText(content)
         
     | 
| 
       1213 
     | 
    
         
            -
                    return
         
     | 
| 
       1214 
1203 
     | 
    
         | 
| 
       1215 
1204 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1216 
1205 
     | 
    
         
             
                def _toggleViewerPanelVisibility(self) -> None:
         
     | 
| 
       1217 
1206 
     | 
    
         
             
                    """Toggle the visibility of the document viewer panel."""
         
     | 
| 
       1218 
1207 
     | 
    
         
             
                    CONFIG.showViewerPanel = not CONFIG.showViewerPanel
         
     | 
| 
       1219 
1208 
     | 
    
         
             
                    self.docViewerPanel.setVisible(CONFIG.showViewerPanel)
         
     | 
| 
       1220 
     | 
    
         
            -
                    return
         
     | 
| 
       1221 
1209 
     | 
    
         | 
| 
       1222 
1210 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1223 
1211 
     | 
    
         
             
                def _timeTick(self) -> None:
         
     | 
| 
         @@ -1233,7 +1221,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1233 
1221 
     | 
    
         
             
                            self._updateStatusWordCount()
         
     | 
| 
       1234 
1222 
     | 
    
         
             
                            if CONFIG.memInfo:  # pragma: no cover
         
     | 
| 
       1235 
1223 
     | 
    
         
             
                                self.mainStatus.memInfo()
         
     | 
| 
       1236 
     | 
    
         
            -
                    return
         
     | 
| 
       1237 
1224 
     | 
    
         | 
| 
       1238 
1225 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1239 
1226 
     | 
    
         
             
                def _autoSaveProject(self) -> None:
         
     | 
| 
         @@ -1244,7 +1231,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1244 
1231 
     | 
    
         
             
                    if doSave:
         
     | 
| 
       1245 
1232 
     | 
    
         
             
                        logger.debug("Auto-saving project")
         
     | 
| 
       1246 
1233 
     | 
    
         
             
                        self.saveProject(autoSave=True)
         
     | 
| 
       1247 
     | 
    
         
            -
                    return
         
     | 
| 
       1248 
1234 
     | 
    
         | 
| 
       1249 
1235 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1250 
1236 
     | 
    
         
             
                def _autoSaveDocument(self) -> None:
         
     | 
| 
         @@ -1252,7 +1238,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1252 
1238 
     | 
    
         
             
                    if SHARED.hasProject and self.docEditor.docChanged:
         
     | 
| 
       1253 
1239 
     | 
    
         
             
                        logger.debug("Auto-saving document")
         
     | 
| 
       1254 
1240 
     | 
    
         
             
                        self.saveDocument()
         
     | 
| 
       1255 
     | 
    
         
            -
                    return
         
     | 
| 
       1256 
1241 
     | 
    
         | 
| 
       1257 
1242 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1258 
1243 
     | 
    
         
             
                def _updateStatusWordCount(self) -> None:
         
     | 
| 
         @@ -1282,8 +1267,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1282 
1267 
     | 
    
         | 
| 
       1283 
1268 
     | 
    
         
             
                        self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
         
     | 
| 
       1284 
1269 
     | 
    
         | 
| 
       1285 
     | 
    
         
            -
                    return
         
     | 
| 
       1286 
     | 
    
         
            -
             
     | 
| 
       1287 
1270 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1288 
1271 
     | 
    
         
             
                def _keyPressReturn(self) -> None:
         
     | 
| 
       1289 
1272 
     | 
    
         
             
                    """Process a return or enter keypress in the main window."""
         
     | 
| 
         @@ -1291,7 +1274,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1291 
1274 
     | 
    
         
             
                        self.projSearch.processReturn()
         
     | 
| 
       1292 
1275 
     | 
    
         
             
                    else:
         
     | 
| 
       1293 
1276 
     | 
    
         
             
                        self.openSelectedItem()
         
     | 
| 
       1294 
     | 
    
         
            -
                    return
         
     | 
| 
       1295 
1277 
     | 
    
         | 
| 
       1296 
1278 
     | 
    
         
             
                @pyqtSlot()
         
     | 
| 
       1297 
1279 
     | 
    
         
             
                def _keyPressEscape(self) -> None:
         
     | 
| 
         @@ -1300,7 +1282,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1300 
1282 
     | 
    
         
             
                        self.docEditor.closeSearch()
         
     | 
| 
       1301 
1283 
     | 
    
         
             
                    elif SHARED.focusMode:
         
     | 
| 
       1302 
1284 
     | 
    
         
             
                        SHARED.setFocusMode(False)
         
     | 
| 
       1303 
     | 
    
         
            -
                    return
         
     | 
| 
       1304 
1285 
     | 
    
         | 
| 
       1305 
1286 
     | 
    
         
             
                @pyqtSlot(int)
         
     | 
| 
       1306 
1287 
     | 
    
         
             
                def _mainStackChanged(self, index: int) -> None:
         
     | 
| 
         @@ -1308,7 +1289,6 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1308 
1289 
     | 
    
         
             
                    if self.mainStack.widget(index) == self.outlineView:
         
     | 
| 
       1309 
1290 
     | 
    
         
             
                        if SHARED.hasProject:
         
     | 
| 
       1310 
1291 
     | 
    
         
             
                            self.outlineView.refreshTree()
         
     | 
| 
       1311 
     | 
    
         
            -
                    return
         
     | 
| 
       1312 
1292 
     | 
    
         | 
| 
       1313 
1293 
     | 
    
         
             
                @pyqtSlot(int)
         
     | 
| 
       1314 
1294 
     | 
    
         
             
                def _projStackChanged(self, index: int) -> None:
         
     | 
| 
         @@ -1320,13 +1300,19 @@ class GuiMain(QMainWindow): 
     | 
|
| 
       1320 
1300 
     | 
    
         
             
                    elif widget == self.novelView:
         
     | 
| 
       1321 
1301 
     | 
    
         
             
                        sHandle, _ = self.novelView.getSelectedHandle()
         
     | 
| 
       1322 
1302 
     | 
    
         
             
                    self.itemDetails.updateViewBox(sHandle)
         
     | 
| 
       1323 
     | 
    
         
            -
                    return
         
     | 
| 
       1324 
1303 
     | 
    
         | 
| 
       1325 
1304 
     | 
    
         
             
                ##
         
     | 
| 
       1326 
1305 
     | 
    
         
             
                #  Internal Functions
         
     | 
| 
       1327 
1306 
     | 
    
         
             
                ##
         
     | 
| 
       1328 
1307 
     | 
    
         | 
| 
      
 1308 
     | 
    
         
            +
                def _setWindowSize(self, size: list[int]) -> None:
         
     | 
| 
      
 1309 
     | 
    
         
            +
                    """Set the main window size."""
         
     | 
| 
      
 1310 
     | 
    
         
            +
                    if len(size) == 2 and (screen := SHARED.mainScreen):
         
     | 
| 
      
 1311 
     | 
    
         
            +
                        availSize = screen.availableSize()
         
     | 
| 
      
 1312 
     | 
    
         
            +
                        width = minmax(size[0], 900, availSize.width())
         
     | 
| 
      
 1313 
     | 
    
         
            +
                        height = minmax(size[1], 500, availSize.height())
         
     | 
| 
      
 1314 
     | 
    
         
            +
                        self.resize(width, height)
         
     | 
| 
      
 1315 
     | 
    
         
            +
             
     | 
| 
       1329 
1316 
     | 
    
         
             
                def _updateWindowTitle(self, projName: str | None = None) -> None:
         
     | 
| 
       1330 
1317 
     | 
    
         
             
                    """Set the window title and add the project's name."""
         
     | 
| 
       1331 
1318 
     | 
    
         
             
                    self.setWindowTitle(" - ".join(filter(None, [projName, CONFIG.appName])))
         
     | 
| 
       1332 
     | 
    
         
            -
                    return
         
     |