novelWriter 2.4.3__py3-none-any.whl → 2.5__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.4.3.dist-info → novelWriter-2.5.dist-info}/METADATA +4 -5
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/RECORD +122 -112
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +33 -39
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/i18n/project_pl_PL.json +116 -0
- novelwriter/assets/i18n/project_pt_BR.json +74 -74
- novelwriter/assets/icons/typicons_dark/icons.conf +2 -0
- novelwriter/assets/icons/typicons_dark/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_quote.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +2 -0
- novelwriter/assets/icons/typicons_light/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_quote.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +5 -3
- novelwriter/assets/syntax/default_dark.conf +32 -18
- novelwriter/assets/syntax/default_light.conf +24 -10
- novelwriter/assets/syntax/dracula.conf +44 -0
- novelwriter/assets/syntax/grey_dark.conf +5 -4
- novelwriter/assets/syntax/grey_light.conf +5 -4
- novelwriter/assets/syntax/light_owl.conf +7 -6
- novelwriter/assets/syntax/night_owl.conf +7 -6
- novelwriter/assets/syntax/snazzy.conf +42 -0
- novelwriter/assets/syntax/solarized_dark.conf +4 -3
- novelwriter/assets/syntax/solarized_light.conf +4 -3
- novelwriter/assets/syntax/tango.conf +27 -11
- novelwriter/assets/syntax/tomorrow.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night.conf +7 -6
- novelwriter/assets/syntax/tomorrow_night_blue.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_bright.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +6 -5
- novelwriter/assets/text/credits_en.htm +52 -41
- novelwriter/assets/themes/cyberpunk_night.conf +3 -0
- novelwriter/assets/themes/default_dark.conf +2 -0
- novelwriter/assets/themes/default_light.conf +2 -0
- novelwriter/assets/themes/dracula.conf +48 -0
- novelwriter/assets/themes/solarized_dark.conf +2 -0
- novelwriter/assets/themes/solarized_light.conf +2 -0
- novelwriter/common.py +33 -12
- novelwriter/config.py +184 -98
- novelwriter/constants.py +47 -35
- novelwriter/core/buildsettings.py +68 -69
- novelwriter/core/coretools.py +5 -23
- novelwriter/core/docbuild.py +52 -40
- novelwriter/core/document.py +3 -5
- novelwriter/core/index.py +115 -45
- novelwriter/core/item.py +8 -19
- novelwriter/core/options.py +2 -4
- novelwriter/core/project.py +37 -61
- novelwriter/core/projectdata.py +1 -3
- novelwriter/core/projectxml.py +12 -15
- novelwriter/core/sessions.py +3 -5
- novelwriter/core/spellcheck.py +4 -9
- novelwriter/core/status.py +211 -164
- novelwriter/core/storage.py +0 -8
- novelwriter/core/tohtml.py +139 -105
- novelwriter/core/tokenizer.py +278 -122
- novelwriter/core/{tomd.py → tomarkdown.py} +97 -78
- novelwriter/core/toodt.py +257 -166
- novelwriter/core/toqdoc.py +419 -0
- novelwriter/core/tree.py +5 -7
- novelwriter/dialogs/about.py +11 -18
- novelwriter/dialogs/docmerge.py +17 -19
- novelwriter/dialogs/docsplit.py +17 -19
- novelwriter/dialogs/editlabel.py +6 -10
- novelwriter/dialogs/preferences.py +200 -164
- novelwriter/dialogs/projectsettings.py +225 -189
- novelwriter/dialogs/quotes.py +12 -9
- novelwriter/dialogs/wordlist.py +9 -15
- novelwriter/enum.py +35 -30
- novelwriter/error.py +8 -15
- novelwriter/extensions/configlayout.py +55 -21
- novelwriter/extensions/eventfilters.py +1 -5
- novelwriter/extensions/modified.py +70 -14
- novelwriter/extensions/novelselector.py +1 -3
- novelwriter/extensions/pagedsidebar.py +9 -12
- novelwriter/extensions/{circularprogress.py → progressbars.py} +30 -8
- novelwriter/extensions/statusled.py +40 -26
- novelwriter/extensions/switch.py +4 -6
- novelwriter/extensions/switchbox.py +7 -6
- novelwriter/extensions/versioninfo.py +3 -9
- novelwriter/gui/doceditor.py +120 -139
- novelwriter/gui/dochighlight.py +231 -186
- novelwriter/gui/docviewer.py +69 -108
- novelwriter/gui/docviewerpanel.py +3 -10
- novelwriter/gui/editordocument.py +1 -3
- novelwriter/gui/itemdetails.py +7 -11
- novelwriter/gui/mainmenu.py +22 -18
- novelwriter/gui/noveltree.py +11 -24
- novelwriter/gui/outline.py +15 -26
- novelwriter/gui/projtree.py +39 -65
- novelwriter/gui/search.py +10 -3
- novelwriter/gui/sidebar.py +2 -6
- novelwriter/gui/statusbar.py +29 -37
- novelwriter/gui/theme.py +26 -48
- novelwriter/guimain.py +162 -160
- novelwriter/shared.py +36 -19
- novelwriter/text/patterns.py +113 -0
- novelwriter/tools/dictionaries.py +10 -20
- novelwriter/tools/lipsum.py +10 -16
- novelwriter/tools/manusbuild.py +9 -11
- novelwriter/tools/manuscript.py +75 -149
- novelwriter/tools/manussettings.py +74 -76
- novelwriter/tools/noveldetails.py +16 -21
- novelwriter/tools/welcome.py +21 -26
- novelwriter/tools/writingstats.py +9 -12
- novelwriter/types.py +49 -4
- novelwriter/extensions/simpleprogress.py +0 -55
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/top_level.txt +0 -0
novelwriter/config.py
CHANGED
@@ -5,6 +5,7 @@ novelWriter – Config Class
|
|
5
5
|
File History:
|
6
6
|
Created: 2018-09-22 [0.0.1] Config
|
7
7
|
Created: 2022-11-09 [2.0rc2] RecentProjects
|
8
|
+
Created: 2024-06-16 [2.5rc1] RecentPaths
|
8
9
|
|
9
10
|
This file is a part of novelWriter
|
10
11
|
Copyright 2018–2024, Veronica Berglyd Olsen
|
@@ -24,24 +25,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
24
25
|
"""
|
25
26
|
from __future__ import annotations
|
26
27
|
|
27
|
-
import sys
|
28
28
|
import json
|
29
29
|
import logging
|
30
|
+
import sys
|
30
31
|
|
31
|
-
from time import time
|
32
|
-
from pathlib import Path
|
33
32
|
from datetime import datetime
|
33
|
+
from pathlib import Path
|
34
|
+
from time import time
|
34
35
|
|
35
|
-
from PyQt5.QtGui import QFontDatabase
|
36
36
|
from PyQt5.QtCore import (
|
37
37
|
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
|
38
38
|
QLocale, QStandardPaths, QSysInfo, QTranslator
|
39
39
|
)
|
40
|
+
from PyQt5.QtGui import QFont, QFontDatabase
|
40
41
|
from PyQt5.QtWidgets import QApplication
|
41
42
|
|
42
|
-
from novelwriter.
|
43
|
-
from novelwriter.common import NWConfigParser, checkInt, checkPath, formatTimeStamp
|
43
|
+
from novelwriter.common import NWConfigParser, checkInt, checkPath, describeFont, formatTimeStamp
|
44
44
|
from novelwriter.constants import nwFiles, nwUnicode
|
45
|
+
from novelwriter.error import formatException, logException
|
45
46
|
|
46
47
|
logger = logging.getLogger(__name__)
|
47
48
|
|
@@ -103,18 +104,19 @@ class Config:
|
|
103
104
|
# User Settings
|
104
105
|
# =============
|
105
106
|
|
106
|
-
self.
|
107
|
+
self._recentProjects = RecentProjects(self)
|
108
|
+
self._recentPaths = RecentPaths(self)
|
107
109
|
|
108
110
|
# General GUI Settings
|
109
|
-
self.guiLocale
|
110
|
-
self.guiTheme
|
111
|
-
self.guiSyntax
|
112
|
-
self.guiFont
|
113
|
-
self.
|
114
|
-
self.
|
115
|
-
self.
|
116
|
-
self.
|
117
|
-
self.
|
111
|
+
self.guiLocale = self._qLocale.name()
|
112
|
+
self.guiTheme = "default" # GUI theme
|
113
|
+
self.guiSyntax = "default_light" # Syntax theme
|
114
|
+
self.guiFont = QFont() # Main GUI font
|
115
|
+
self.guiScale = 1.0 # Set automatically by Theme class
|
116
|
+
self.hideVScroll = False # Hide vertical scroll bars on main widgets
|
117
|
+
self.hideHScroll = False # Hide horizontal scroll bars on main widgets
|
118
|
+
self.lastNotes = "0x0" # The latest release notes that have been shown
|
119
|
+
self.nativeFont = True # Use native font dialog
|
118
120
|
|
119
121
|
# Size Settings
|
120
122
|
self._mainWinSize = [1200, 650] # Last size of the main GUI window
|
@@ -132,40 +134,42 @@ class Config:
|
|
132
134
|
self.askBeforeBackup = True # Flag for asking before running automatic backup
|
133
135
|
|
134
136
|
# Text Editor Settings
|
135
|
-
self.textFont =
|
136
|
-
self.
|
137
|
-
self.
|
138
|
-
self.
|
139
|
-
|
140
|
-
|
141
|
-
self.
|
142
|
-
self.
|
143
|
-
self.
|
144
|
-
|
145
|
-
|
146
|
-
self.
|
147
|
-
self.
|
148
|
-
self.
|
149
|
-
|
150
|
-
|
151
|
-
self.
|
152
|
-
self.
|
153
|
-
self.
|
154
|
-
self.
|
155
|
-
|
156
|
-
|
157
|
-
self.
|
158
|
-
self.
|
159
|
-
|
160
|
-
|
161
|
-
self.
|
162
|
-
self.
|
163
|
-
self.
|
164
|
-
self.
|
165
|
-
|
166
|
-
self.
|
167
|
-
|
168
|
-
self.
|
137
|
+
self.textFont = QFont() # Editor font
|
138
|
+
self.textWidth = 700 # Editor text width
|
139
|
+
self.textMargin = 40 # Editor/viewer text margin
|
140
|
+
self.tabWidth = 40 # Editor tabulator width
|
141
|
+
|
142
|
+
self.focusWidth = 800 # Focus Mode text width
|
143
|
+
self.hideFocusFooter = False # Hide document footer in Focus Mode
|
144
|
+
self.showFullPath = True # Show full document path in editor header
|
145
|
+
self.autoSelect = True # Auto-select word when applying format with no selection
|
146
|
+
|
147
|
+
self.doJustify = False # Justify text
|
148
|
+
self.showTabsNSpaces = False # Show tabs and spaces in editor
|
149
|
+
self.showLineEndings = False # Show line endings in editor
|
150
|
+
self.showMultiSpaces = True # Highlight multiple spaces in the text
|
151
|
+
|
152
|
+
self.doReplace = True # Enable auto-replace as you type
|
153
|
+
self.doReplaceSQuote = True # Smart single quotes
|
154
|
+
self.doReplaceDQuote = True # Smart double quotes
|
155
|
+
self.doReplaceDash = True # Replace multiple hyphens with dashes
|
156
|
+
self.doReplaceDots = True # Replace three dots with ellipsis
|
157
|
+
|
158
|
+
self.autoScroll = False # Typewriter-like scrolling
|
159
|
+
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
|
160
|
+
self.scrollPastEnd = True # Scroll past end of document, and centre cursor
|
161
|
+
|
162
|
+
self.dialogStyle = 2 # Quote type to use for dialogue
|
163
|
+
self.allowOpenDial = True # Allow open-ended dialogue quotes
|
164
|
+
self.narratorBreak = "" # Symbol to use for narrator break
|
165
|
+
self.dialogLine = "" # Symbol to use for dialogue line
|
166
|
+
self.altDialogOpen = "" # Alternative dialog symbol, open
|
167
|
+
self.altDialogClose = "" # Alternative dialog symbol, close
|
168
|
+
self.highlightEmph = True # Add colour to text emphasis
|
169
|
+
|
170
|
+
self.stopWhenIdle = True # Stop the status bar clock when the user is idle
|
171
|
+
self.userIdleTime = 300 # Time of inactivity to consider user idle
|
172
|
+
self.incNotesWCount = True # The status bar word count includes notes
|
169
173
|
|
170
174
|
# User-Selected Symbol Settings
|
171
175
|
self.fmtApostrophe = nwUnicode.U_RSQUO
|
@@ -178,7 +182,6 @@ class Config:
|
|
178
182
|
self.fmtPadThin = False
|
179
183
|
|
180
184
|
# User Paths
|
181
|
-
self._lastPath = self._homePath # The user's last used path
|
182
185
|
self._backupPath = self._backPath # Backup path to use, can be none
|
183
186
|
|
184
187
|
# Spell Checking Settings
|
@@ -251,7 +254,7 @@ class Config:
|
|
251
254
|
|
252
255
|
@property
|
253
256
|
def recentProjects(self) -> RecentProjects:
|
254
|
-
return self.
|
257
|
+
return self._recentProjects
|
255
258
|
|
256
259
|
@property
|
257
260
|
def mainWinSize(self) -> list[int]:
|
@@ -341,7 +344,7 @@ class Config:
|
|
341
344
|
self._outlnPanePos = [int(x/self.guiScale) for x in pos]
|
342
345
|
return
|
343
346
|
|
344
|
-
def setLastPath(self, path: str | Path) -> None:
|
347
|
+
def setLastPath(self, key: str, path: str | Path) -> None:
|
345
348
|
"""Set the last used path. Only the folder is saved, so if the
|
346
349
|
path is not a folder, the parent of the path is used instead.
|
347
350
|
"""
|
@@ -350,8 +353,7 @@ class Config:
|
|
350
353
|
if not path.is_dir():
|
351
354
|
path = path.parent
|
352
355
|
if path.is_dir():
|
353
|
-
self.
|
354
|
-
logger.debug("Last path updated: %s" % self._lastPath)
|
356
|
+
self._recentPaths.setPath(key, path)
|
355
357
|
return
|
356
358
|
|
357
359
|
def setBackupPath(self, path: Path | str) -> None:
|
@@ -359,23 +361,53 @@ class Config:
|
|
359
361
|
self._backupPath = checkPath(path, self._backPath)
|
360
362
|
return
|
361
363
|
|
362
|
-
def
|
364
|
+
def setGuiFont(self, value: QFont | str | None) -> None:
|
365
|
+
"""Update the GUI's font style from settings."""
|
366
|
+
if isinstance(value, QFont):
|
367
|
+
self.guiFont = value
|
368
|
+
elif value and isinstance(value, str):
|
369
|
+
self.guiFont = QFont()
|
370
|
+
self.guiFont.fromString(value)
|
371
|
+
else:
|
372
|
+
font = QFont()
|
373
|
+
fontDB = QFontDatabase()
|
374
|
+
if self.osWindows and "Arial" in fontDB.families():
|
375
|
+
# On Windows we default to Arial if possible
|
376
|
+
font.setFamily("Arial")
|
377
|
+
font.setPointSize(10)
|
378
|
+
else:
|
379
|
+
font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
|
380
|
+
self.guiFont = font
|
381
|
+
logger.debug("GUI font set to: %s", describeFont(font))
|
382
|
+
|
383
|
+
QApplication.setFont(self.guiFont)
|
384
|
+
|
385
|
+
return
|
386
|
+
|
387
|
+
def setTextFont(self, value: QFont | str | None) -> None:
|
363
388
|
"""Set the text font if it exists. If it doesn't, or is None,
|
364
389
|
set to default font.
|
365
390
|
"""
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
391
|
+
if isinstance(value, QFont):
|
392
|
+
self.textFont = value
|
393
|
+
elif value and isinstance(value, str):
|
394
|
+
self.textFont = QFont()
|
395
|
+
self.textFont.fromString(value)
|
396
|
+
else:
|
397
|
+
fontDB = QFontDatabase()
|
398
|
+
fontFam = fontDB.families()
|
371
399
|
if self.osWindows and "Arial" in fontFam:
|
372
|
-
|
400
|
+
font = QFont()
|
401
|
+
font.setFamily("Arial")
|
402
|
+
font.setPointSize(12)
|
373
403
|
elif self.osDarwin and "Helvetica" in fontFam:
|
374
|
-
|
404
|
+
font = QFont()
|
405
|
+
font.setFamily("Helvetica")
|
406
|
+
font.setPointSize(12)
|
375
407
|
else:
|
376
|
-
|
377
|
-
|
378
|
-
|
408
|
+
font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
|
409
|
+
self.textFont = font
|
410
|
+
logger.debug("Text font set to: %s", describeFont(font))
|
379
411
|
return
|
380
412
|
|
381
413
|
##
|
@@ -406,11 +438,12 @@ class Config:
|
|
406
438
|
return self._appPath / "assets" / target
|
407
439
|
return self._appPath / "assets"
|
408
440
|
|
409
|
-
def lastPath(self) -> Path:
|
441
|
+
def lastPath(self, key: str) -> Path:
|
410
442
|
"""Return the last path used by the user, if it exists."""
|
411
|
-
if
|
412
|
-
|
413
|
-
|
443
|
+
if path := self._recentPaths.getPath(key):
|
444
|
+
asPath = Path(path)
|
445
|
+
if asPath.is_dir():
|
446
|
+
return asPath
|
414
447
|
return self._homePath
|
415
448
|
|
416
449
|
def backupPath(self) -> Path:
|
@@ -484,7 +517,6 @@ class Config:
|
|
484
517
|
logger.debug("Data Path: %s", self._dataPath)
|
485
518
|
logger.debug("App Root: %s", self._appRoot)
|
486
519
|
logger.debug("App Path: %s", self._appPath)
|
487
|
-
logger.debug("Last Path: %s", self._lastPath)
|
488
520
|
logger.debug("PDF Manual: %s", self.pdfDocs)
|
489
521
|
|
490
522
|
# If the config and data folders don't exist, create them
|
@@ -499,13 +531,8 @@ class Config:
|
|
499
531
|
(self._dataPath / "syntax").mkdir(exist_ok=True)
|
500
532
|
(self._dataPath / "themes").mkdir(exist_ok=True)
|
501
533
|
|
502
|
-
|
503
|
-
|
504
|
-
self.loadConfig()
|
505
|
-
else:
|
506
|
-
self.saveConfig()
|
507
|
-
|
508
|
-
self._recentObj.loadCache()
|
534
|
+
self._recentPaths.loadCache()
|
535
|
+
self._recentProjects.loadCache()
|
509
536
|
self._checkOptionalPackages()
|
510
537
|
|
511
538
|
logger.debug("Config instance initialised")
|
@@ -545,6 +572,14 @@ class Config:
|
|
545
572
|
|
546
573
|
conf = NWConfigParser()
|
547
574
|
cnfPath = self._confPath / nwFiles.CONF_FILE
|
575
|
+
|
576
|
+
if not cnfPath.exists():
|
577
|
+
# Initial file, so we just create one from defaults
|
578
|
+
self.setGuiFont(None)
|
579
|
+
self.setTextFont(None)
|
580
|
+
self.saveConfig()
|
581
|
+
return True
|
582
|
+
|
548
583
|
try:
|
549
584
|
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
550
585
|
conf.read_file(inFile)
|
@@ -558,15 +593,14 @@ class Config:
|
|
558
593
|
|
559
594
|
# Main
|
560
595
|
sec = "Main"
|
596
|
+
self.setGuiFont(conf.rdStr(sec, "font", ""))
|
561
597
|
self.guiTheme = conf.rdStr(sec, "theme", self.guiTheme)
|
562
598
|
self.guiSyntax = conf.rdStr(sec, "syntax", self.guiSyntax)
|
563
|
-
self.guiFont = conf.rdStr(sec, "font", self.guiFont)
|
564
|
-
self.guiFontSize = conf.rdInt(sec, "fontsize", self.guiFontSize)
|
565
599
|
self.guiLocale = conf.rdStr(sec, "localisation", self.guiLocale)
|
566
600
|
self.hideVScroll = conf.rdBool(sec, "hidevscroll", self.hideVScroll)
|
567
601
|
self.hideHScroll = conf.rdBool(sec, "hidehscroll", self.hideHScroll)
|
568
602
|
self.lastNotes = conf.rdStr(sec, "lastnotes", self.lastNotes)
|
569
|
-
self.
|
603
|
+
self.nativeFont = conf.rdBool(sec, "nativefont", self.nativeFont)
|
570
604
|
|
571
605
|
# Sizes
|
572
606
|
sec = "Sizes"
|
@@ -588,8 +622,7 @@ class Config:
|
|
588
622
|
|
589
623
|
# Editor
|
590
624
|
sec = "Editor"
|
591
|
-
self.
|
592
|
-
self.textSize = conf.rdInt(sec, "textsize", self.textSize)
|
625
|
+
self.setTextFont(conf.rdStr(sec, "textfont", ""))
|
593
626
|
self.textWidth = conf.rdInt(sec, "width", self.textWidth)
|
594
627
|
self.textMargin = conf.rdInt(sec, "margin", self.textMargin)
|
595
628
|
self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth)
|
@@ -618,9 +651,12 @@ class Config:
|
|
618
651
|
self.showMultiSpaces = conf.rdBool(sec, "showmultispaces", self.showMultiSpaces)
|
619
652
|
self.incNotesWCount = conf.rdBool(sec, "incnoteswcount", self.incNotesWCount)
|
620
653
|
self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
|
621
|
-
self.
|
622
|
-
self.
|
623
|
-
self.
|
654
|
+
self.dialogStyle = conf.rdInt(sec, "dialogstyle", self.dialogStyle)
|
655
|
+
self.allowOpenDial = conf.rdBool(sec, "allowopendial", self.allowOpenDial)
|
656
|
+
self.narratorBreak = conf.rdStr(sec, "narratorbreak", self.narratorBreak)
|
657
|
+
self.altDialogOpen = conf.rdStr(sec, "altdialogopen", self.altDialogOpen)
|
658
|
+
self.altDialogClose = conf.rdStr(sec, "altdialogclose", self.altDialogClose)
|
659
|
+
self.dialogLine = conf.rdStr(sec, "dialogline", self.dialogLine)
|
624
660
|
self.highlightEmph = conf.rdBool(sec, "highlightemph", self.highlightEmph)
|
625
661
|
self.stopWhenIdle = conf.rdBool(sec, "stopwhenidle", self.stopWhenIdle)
|
626
662
|
self.userIdleTime = conf.rdInt(sec, "useridletime", self.userIdleTime)
|
@@ -666,15 +702,14 @@ class Config:
|
|
666
702
|
}
|
667
703
|
|
668
704
|
conf["Main"] = {
|
705
|
+
"font": self.guiFont.toString(),
|
669
706
|
"theme": str(self.guiTheme),
|
670
707
|
"syntax": str(self.guiSyntax),
|
671
|
-
"font": str(self.guiFont),
|
672
|
-
"fontsize": str(self.guiFontSize),
|
673
708
|
"localisation": str(self.guiLocale),
|
674
709
|
"hidevscroll": str(self.hideVScroll),
|
675
710
|
"hidehscroll": str(self.hideHScroll),
|
676
711
|
"lastnotes": str(self.lastNotes),
|
677
|
-
"
|
712
|
+
"nativefont": str(self.nativeFont),
|
678
713
|
}
|
679
714
|
|
680
715
|
conf["Sizes"] = {
|
@@ -696,8 +731,7 @@ class Config:
|
|
696
731
|
}
|
697
732
|
|
698
733
|
conf["Editor"] = {
|
699
|
-
"textfont":
|
700
|
-
"textsize": str(self.textSize),
|
734
|
+
"textfont": self.textFont.toString(),
|
701
735
|
"width": str(self.textWidth),
|
702
736
|
"margin": str(self.textMargin),
|
703
737
|
"tabwidth": str(self.tabWidth),
|
@@ -726,9 +760,12 @@ class Config:
|
|
726
760
|
"showmultispaces": str(self.showMultiSpaces),
|
727
761
|
"incnoteswcount": str(self.incNotesWCount),
|
728
762
|
"showfullpath": str(self.showFullPath),
|
729
|
-
"
|
730
|
-
"
|
731
|
-
"
|
763
|
+
"dialogstyle": str(self.dialogStyle),
|
764
|
+
"allowopendial": str(self.allowOpenDial),
|
765
|
+
"narratorbreak": str(self.narratorBreak),
|
766
|
+
"altdialogopen": str(self.altDialogOpen),
|
767
|
+
"altdialogclose": str(self.altDialogClose),
|
768
|
+
"dialogline": str(self.dialogLine),
|
732
769
|
"highlightemph": str(self.highlightEmph),
|
733
770
|
"stopwhenidle": str(self.stopWhenIdle),
|
734
771
|
"useridletime": str(self.userIdleTime),
|
@@ -773,7 +810,7 @@ class Config:
|
|
773
810
|
"""Pack a list of items into a comma-separated string for saving
|
774
811
|
to the config file.
|
775
812
|
"""
|
776
|
-
return ", ".join(
|
813
|
+
return ", ".join(str(inVal) for inVal in data)
|
777
814
|
|
778
815
|
def _checkOptionalPackages(self) -> None:
|
779
816
|
"""Check optional packages used by some features."""
|
@@ -787,8 +824,6 @@ class Config:
|
|
787
824
|
logger.debug("Checking package 'pyenchant': OK")
|
788
825
|
return
|
789
826
|
|
790
|
-
# END Class Config
|
791
|
-
|
792
827
|
|
793
828
|
class RecentProjects:
|
794
829
|
|
@@ -858,4 +893,55 @@ class RecentProjects:
|
|
858
893
|
self.saveCache()
|
859
894
|
return
|
860
895
|
|
861
|
-
|
896
|
+
|
897
|
+
class RecentPaths:
|
898
|
+
|
899
|
+
KEYS = ["default", "project", "import", "outline", "stats"]
|
900
|
+
|
901
|
+
def __init__(self, config: Config) -> None:
|
902
|
+
self._conf = config
|
903
|
+
self._data = {}
|
904
|
+
return
|
905
|
+
|
906
|
+
def setPath(self, key: str, path: Path | str) -> None:
|
907
|
+
"""Set a path for a given key, and save the cache."""
|
908
|
+
if key in self.KEYS:
|
909
|
+
self._data[key] = str(path)
|
910
|
+
self.saveCache()
|
911
|
+
return
|
912
|
+
|
913
|
+
def getPath(self, key: str) -> str | None:
|
914
|
+
"""Get a path for a given key, or return None."""
|
915
|
+
return self._data.get(key)
|
916
|
+
|
917
|
+
def loadCache(self) -> bool:
|
918
|
+
"""Load the cache file for recent paths."""
|
919
|
+
self._data = {}
|
920
|
+
cacheFile = self._conf.dataPath(nwFiles.RECENT_PATH)
|
921
|
+
if cacheFile.is_file():
|
922
|
+
try:
|
923
|
+
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
924
|
+
data = json.load(inFile)
|
925
|
+
if isinstance(data, dict):
|
926
|
+
for key, path in data.items():
|
927
|
+
if key in self.KEYS and isinstance(path, str):
|
928
|
+
self._data[key] = path
|
929
|
+
except Exception:
|
930
|
+
logger.error("Could not load recent paths cache")
|
931
|
+
logException()
|
932
|
+
return False
|
933
|
+
return True
|
934
|
+
|
935
|
+
def saveCache(self) -> bool:
|
936
|
+
"""Save the cache dictionary of recent paths."""
|
937
|
+
cacheFile = self._conf.dataPath(nwFiles.RECENT_PATH)
|
938
|
+
cacheTemp = cacheFile.with_suffix(".tmp")
|
939
|
+
try:
|
940
|
+
with open(cacheTemp, mode="w+", encoding="utf-8") as outFile:
|
941
|
+
json.dump(self._data, outFile, indent=2)
|
942
|
+
cacheTemp.replace(cacheFile)
|
943
|
+
except Exception:
|
944
|
+
logger.error("Could not save recent paths cache")
|
945
|
+
logException()
|
946
|
+
return False
|
947
|
+
return True
|
novelwriter/constants.py
CHANGED
@@ -23,9 +23,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
23
23
|
"""
|
24
24
|
from __future__ import annotations
|
25
25
|
|
26
|
-
from PyQt5.QtCore import
|
26
|
+
from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
27
27
|
|
28
|
-
from novelwriter.enum import
|
28
|
+
from novelwriter.enum import (
|
29
|
+
nwBuildFmt, nwComment, nwItemClass, nwItemLayout, nwOutline, nwStatusShape
|
30
|
+
)
|
29
31
|
|
30
32
|
|
31
33
|
def trConst(text: str) -> str:
|
@@ -55,21 +57,14 @@ class nwConst:
|
|
55
57
|
STATUS_MSG_TIMEOUT = 15000 # milliseconds
|
56
58
|
MAX_SEARCH_RESULT = 1000
|
57
59
|
|
58
|
-
# Dialogs
|
59
|
-
DLG_FINISHED = 2
|
60
|
-
|
61
|
-
# END Class nwConst
|
62
|
-
|
63
60
|
|
64
61
|
class nwRegEx:
|
65
62
|
|
66
63
|
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
67
|
-
FMT_EB = r"(?<![\w\\])(
|
68
|
-
FMT_ST = r"(?<![\w\\])(
|
69
|
-
FMT_SC = r"(?i)(?<!\\)(\[[\/\!]?(?:i|
|
70
|
-
FMT_SV = r"(?<!\\)(\[(
|
71
|
-
|
72
|
-
# END Class nwRegEx
|
64
|
+
FMT_EB = r"(?<![\w\\])(\*{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
65
|
+
FMT_ST = r"(?<![\w\\])(~{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)"
|
66
|
+
FMT_SC = r"(?i)(?<!\\)(\[[\/\!]?(?:b|i|s|u|m|sup|sub)\])"
|
67
|
+
FMT_SV = r"(?i)(?<!\\)(\[(?:footnote):)(.+?)(?<!\\)(\])"
|
73
68
|
|
74
69
|
|
75
70
|
class nwShortcode:
|
@@ -89,15 +84,19 @@ class nwShortcode:
|
|
89
84
|
SUB_O = "[sub]"
|
90
85
|
SUB_C = "[/sub]"
|
91
86
|
|
92
|
-
|
87
|
+
FOOTNOTE_B = "[footnote:"
|
88
|
+
|
89
|
+
COMMENT_STYLES = {
|
90
|
+
nwComment.FOOTNOTE: "[footnote:{0}]",
|
91
|
+
nwComment.COMMENT: "[comment:{0}]",
|
92
|
+
}
|
93
93
|
|
94
94
|
|
95
95
|
class nwHeaders:
|
96
96
|
|
97
97
|
H_VALID = ("H0", "H1", "H2", "H3", "H4")
|
98
98
|
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
|
99
|
-
|
100
|
-
# END Class nwHeaders
|
99
|
+
H_SIZES = {0: 2.50, 1: 2.00, 2: 1.75, 3: 1.50, 4: 1.25}
|
101
100
|
|
102
101
|
|
103
102
|
class nwFiles:
|
@@ -105,6 +104,7 @@ class nwFiles:
|
|
105
104
|
# Config Files
|
106
105
|
CONF_FILE = "novelwriter.conf"
|
107
106
|
RECENT_FILE = "recentProjects.json"
|
107
|
+
RECENT_PATH = "recentPaths.json"
|
108
108
|
|
109
109
|
# Project Root Files
|
110
110
|
PROJ_FILE = "nwProject.nwx"
|
@@ -118,8 +118,6 @@ class nwFiles:
|
|
118
118
|
DICT_FILE = "userdict.json"
|
119
119
|
SESS_FILE = "sessions.jsonl"
|
120
120
|
|
121
|
-
# END Class nwFiles
|
122
|
-
|
123
121
|
|
124
122
|
class nwKeyWords:
|
125
123
|
|
@@ -153,8 +151,6 @@ class nwKeyWords:
|
|
153
151
|
CUSTOM_KEY: nwItemClass.CUSTOM,
|
154
152
|
}
|
155
153
|
|
156
|
-
# END Class nwKeyWords
|
157
|
-
|
158
154
|
|
159
155
|
class nwLists:
|
160
156
|
|
@@ -168,8 +164,6 @@ class nwLists:
|
|
168
164
|
nwItemClass.CUSTOM,
|
169
165
|
]
|
170
166
|
|
171
|
-
# END Class nwLists
|
172
|
-
|
173
167
|
|
174
168
|
class nwLabels:
|
175
169
|
|
@@ -268,6 +262,34 @@ class nwLabels:
|
|
268
262
|
nwBuildFmt.J_HTML: ".json",
|
269
263
|
nwBuildFmt.J_NWD: ".json",
|
270
264
|
}
|
265
|
+
SHAPES_PLAIN = {
|
266
|
+
nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
|
267
|
+
nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
|
268
|
+
nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
|
269
|
+
nwStatusShape.DIAMOND: QT_TRANSLATE_NOOP("Constant", "Diamond"),
|
270
|
+
nwStatusShape.PENTAGON: QT_TRANSLATE_NOOP("Constant", "Pentagon"),
|
271
|
+
nwStatusShape.HEXAGON: QT_TRANSLATE_NOOP("Constant", "Hexagon"),
|
272
|
+
nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
|
273
|
+
nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
|
274
|
+
}
|
275
|
+
SHAPES_CIRCLE = {
|
276
|
+
nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "1/4 Circle"),
|
277
|
+
nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Half Circle"),
|
278
|
+
nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "3/4 Circle"),
|
279
|
+
nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Full Circle"),
|
280
|
+
}
|
281
|
+
SHAPES_BARS = {
|
282
|
+
nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
|
283
|
+
nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
|
284
|
+
nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
|
285
|
+
nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Constant", "4 Bars"),
|
286
|
+
}
|
287
|
+
SHAPES_BLOCKS = {
|
288
|
+
nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Constant", "1 Block"),
|
289
|
+
nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Constant", "2 Blocks"),
|
290
|
+
nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Constant", "3 Blocks"),
|
291
|
+
nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Constant", "4 Blocks"),
|
292
|
+
}
|
271
293
|
FILE_FILTERS = {
|
272
294
|
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
|
273
295
|
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
|
@@ -302,8 +324,6 @@ class nwLabels:
|
|
302
324
|
"Custom": (-1.0, -1.0),
|
303
325
|
}
|
304
326
|
|
305
|
-
# END Class nwLabels
|
306
|
-
|
307
327
|
|
308
328
|
class nwHeadFmt:
|
309
329
|
|
@@ -329,8 +349,6 @@ class nwHeadFmt:
|
|
329
349
|
ODT_PAGE = "{Page}"
|
330
350
|
ODT_AUTO = "{Project} / {Author} / {Page}"
|
331
351
|
|
332
|
-
# END Class nwHeadFmt
|
333
|
-
|
334
352
|
|
335
353
|
class nwQuotes:
|
336
354
|
"""Allowed quotation marks.
|
@@ -361,8 +379,6 @@ class nwQuotes:
|
|
361
379
|
"\u300f": QT_TRANSLATE_NOOP("Constant", "Right white corner bracket"),
|
362
380
|
}
|
363
381
|
|
364
|
-
# END Class nwQuotes
|
365
|
-
|
366
382
|
|
367
383
|
class nwUnicode:
|
368
384
|
"""Supported unicode character constants and their HTML equivalents."""
|
@@ -396,7 +412,7 @@ class nwUnicode:
|
|
396
412
|
U_EMDASH = "\u2014" # Long dash
|
397
413
|
U_HBAR = "\u2015" # Horizontal bar
|
398
414
|
U_HELLIP = "\u2026" # Ellipsis
|
399
|
-
|
415
|
+
U_MAPOS = "\u02bc" # Modifier letter single apostrophe
|
400
416
|
U_PRIME = "\u2032" # Prime
|
401
417
|
U_DPRIME = "\u2033" # Double prime
|
402
418
|
|
@@ -463,7 +479,7 @@ class nwUnicode:
|
|
463
479
|
H_EMDASH = "—"
|
464
480
|
H_HBAR = "―"
|
465
481
|
H_HELLIP = "…"
|
466
|
-
|
482
|
+
H_MAPOS = "ʼ"
|
467
483
|
H_PRIME = "′"
|
468
484
|
H_DPRIME = "″"
|
469
485
|
|
@@ -497,8 +513,6 @@ class nwUnicode:
|
|
497
513
|
H_LTRI = "◀"
|
498
514
|
H_LTRIS = "◂"
|
499
515
|
|
500
|
-
# END Class nwUnicode
|
501
|
-
|
502
516
|
|
503
517
|
class nwHtmlUnicode():
|
504
518
|
|
@@ -530,7 +544,7 @@ class nwHtmlUnicode():
|
|
530
544
|
nwUnicode.U_EMDASH: nwUnicode.H_EMDASH,
|
531
545
|
nwUnicode.U_HBAR: nwUnicode.H_HBAR,
|
532
546
|
nwUnicode.U_HELLIP: nwUnicode.H_HELLIP,
|
533
|
-
nwUnicode.
|
547
|
+
nwUnicode.U_MAPOS: nwUnicode.H_MAPOS,
|
534
548
|
nwUnicode.U_PRIME: nwUnicode.H_PRIME,
|
535
549
|
nwUnicode.U_DPRIME: nwUnicode.H_DPRIME,
|
536
550
|
|
@@ -554,5 +568,3 @@ class nwHtmlUnicode():
|
|
554
568
|
nwUnicode.U_TIMES: nwUnicode.H_TIMES,
|
555
569
|
nwUnicode.U_DIVIDE: nwUnicode.H_DIVIDE,
|
556
570
|
}
|
557
|
-
|
558
|
-
# END Class nwHtmlUnicode
|