novelWriter 2.7b1__py3-none-any.whl → 2.7.1__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 +17 -4
- novelwriter/assets/i18n/nw_cs_CZ.qm +0 -0
- 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_ru_RU.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_cs_CZ.json +2 -0
- novelwriter/assets/i18n/project_de_DE.json +3 -1
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/i18n/project_en_US.json +2 -0
- novelwriter/assets/i18n/project_it_IT.json +2 -0
- novelwriter/assets/i18n/project_ja_JP.json +2 -0
- novelwriter/assets/i18n/project_nb_NO.json +2 -0
- novelwriter/assets/i18n/project_nn_NO.json +5 -0
- novelwriter/assets/i18n/project_pl_PL.json +2 -0
- novelwriter/assets/i18n/project_pt_BR.json +2 -0
- novelwriter/assets/i18n/project_ru_RU.json +2 -0
- novelwriter/assets/i18n/project_zh_CN.json +2 -0
- novelwriter/assets/icons/remix_filled.icons +1 -0
- novelwriter/assets/icons/remix_outline.icons +1 -0
- novelwriter/assets/images/splash.png +0 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/manual_fr.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/snazzy.conf +3 -3
- novelwriter/assets/text/credits_en.htm +6 -0
- novelwriter/assets/themes/snazzy.conf +48 -0
- novelwriter/common.py +10 -1
- novelwriter/config.py +96 -25
- novelwriter/constants.py +21 -0
- novelwriter/core/buildsettings.py +3 -1
- novelwriter/core/coretools.py +41 -34
- novelwriter/core/docbuild.py +1 -0
- novelwriter/core/index.py +35 -5
- novelwriter/core/indexdata.py +4 -1
- novelwriter/core/item.py +35 -24
- novelwriter/core/itemmodel.py +17 -13
- novelwriter/core/novelmodel.py +2 -0
- novelwriter/core/project.py +14 -14
- novelwriter/core/projectdata.py +42 -24
- novelwriter/core/projectxml.py +17 -7
- novelwriter/core/sessions.py +29 -13
- novelwriter/core/tree.py +9 -5
- novelwriter/dialogs/docmerge.py +2 -1
- novelwriter/dialogs/docsplit.py +6 -3
- novelwriter/dialogs/editlabel.py +11 -8
- novelwriter/dialogs/preferences.py +37 -26
- novelwriter/dialogs/projectsettings.py +3 -0
- novelwriter/extensions/configlayout.py +6 -2
- novelwriter/extensions/switch.py +16 -15
- novelwriter/extensions/switchbox.py +1 -0
- novelwriter/formats/tokenizer.py +2 -1
- novelwriter/gui/doceditor.py +106 -47
- novelwriter/gui/noveltree.py +11 -5
- novelwriter/gui/outline.py +9 -1
- novelwriter/gui/projtree.py +1 -0
- novelwriter/gui/search.py +1 -0
- novelwriter/gui/statusbar.py +14 -6
- novelwriter/gui/theme.py +25 -10
- novelwriter/guimain.py +29 -9
- novelwriter/splash.py +74 -0
- novelwriter/tools/lipsum.py +2 -1
- novelwriter/tools/manuscript.py +1 -1
- novelwriter/tools/manussettings.py +52 -20
- novelwriter/tools/noveldetails.py +9 -8
- novelwriter/tools/welcome.py +1 -0
- novelwriter/tools/writingstats.py +68 -45
- {novelwriter-2.7b1.dist-info → novelwriter-2.7.1.dist-info}/METADATA +2 -2
- {novelwriter-2.7b1.dist-info → novelwriter-2.7.1.dist-info}/RECORD +81 -78
- {novelwriter-2.7b1.dist-info → novelwriter-2.7.1.dist-info}/WHEEL +1 -1
- {novelwriter-2.7b1.dist-info → novelwriter-2.7.1.dist-info}/entry_points.txt +0 -0
- {novelwriter-2.7b1.dist-info → novelwriter-2.7.1.dist-info}/licenses/LICENSE.md +0 -0
- {novelwriter-2.7b1.dist-info → novelwriter-2.7.1.dist-info}/top_level.txt +0 -0
novelwriter/__init__.py
CHANGED
@@ -35,6 +35,7 @@ from PyQt6.QtWidgets import QApplication, QErrorMessage
|
|
35
35
|
from novelwriter.config import Config
|
36
36
|
from novelwriter.error import exceptionHandler
|
37
37
|
from novelwriter.shared import SharedData
|
38
|
+
from novelwriter.splash import NSplashScreen
|
38
39
|
|
39
40
|
if TYPE_CHECKING:
|
40
41
|
from novelwriter.guimain import GuiMain
|
@@ -48,9 +49,9 @@ __license__ = "GPLv3"
|
|
48
49
|
__author__ = "Veronica Berglyd Olsen"
|
49
50
|
__maintainer__ = "Veronica Berglyd Olsen"
|
50
51
|
__email__ = "code@vkbo.net"
|
51
|
-
__version__ = "2.
|
52
|
-
__hexversion__ = "
|
53
|
-
__date__ = "2025-
|
52
|
+
__version__ = "2.7.1"
|
53
|
+
__hexversion__ = "0x020701f0"
|
54
|
+
__date__ = "2025-06-09"
|
54
55
|
__status__ = "Stable"
|
55
56
|
__domain__ = "novelwriter.io"
|
56
57
|
|
@@ -267,13 +268,25 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|
267
268
|
# Connect the exception handler before making the main GUI
|
268
269
|
sys.excepthook = exceptionHandler
|
269
270
|
|
271
|
+
splash = NSplashScreen()
|
272
|
+
splash.show()
|
273
|
+
|
274
|
+
splash.showStatus("")
|
275
|
+
splash.showStatus("Starting novelWriter ...")
|
276
|
+
|
270
277
|
# Run Config steps that require the QApplication
|
271
|
-
CONFIG.loadConfig()
|
278
|
+
CONFIG.loadConfig(splash)
|
272
279
|
CONFIG.initLocalisation(app)
|
273
280
|
SHARED.initTheme(GuiTheme())
|
274
281
|
|
275
282
|
# Launch main GUI
|
276
283
|
nwGUI = GuiMain()
|
284
|
+
nwGUI.showNormal()
|
285
|
+
splash.finish(nwGUI)
|
286
|
+
|
287
|
+
CONFIG.finishStartup()
|
288
|
+
del splash
|
289
|
+
|
277
290
|
nwGUI.postLaunchTasks(cmdOpen)
|
278
291
|
|
279
292
|
sys.exit(app.exec())
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,8 +1,10 @@
|
|
1
1
|
{
|
2
|
-
"Synopsis": "
|
2
|
+
"Synopsis": "Zusammenfassung",
|
3
3
|
"Short Description": "Kurzbeschreibung",
|
4
4
|
"Footnotes": "Fußnoten",
|
5
5
|
"Comment": "Kommentar",
|
6
|
+
"Story Structure": "Struktur",
|
7
|
+
"Note": "Notiz",
|
6
8
|
"Notes": "Notizen",
|
7
9
|
"Tag": "Schlagwort",
|
8
10
|
"Point of View": "Perspektive",
|
@@ -1,6 +1,10 @@
|
|
1
1
|
{
|
2
2
|
"Synopsis": "Samandrag",
|
3
|
+
"Short Description": "Kort beskriving",
|
4
|
+
"Footnotes": "Fotnotar",
|
3
5
|
"Comment": "Kommentar",
|
6
|
+
"Story Structure": "Tekst-element",
|
7
|
+
"Note": "Notat",
|
4
8
|
"Notes": "Notat",
|
5
9
|
"Tag": "Knagg",
|
6
10
|
"Point of View": "Perspektiv",
|
@@ -12,6 +16,7 @@
|
|
12
16
|
"Objects": "Objekt",
|
13
17
|
"Entities": "Eining",
|
14
18
|
"Custom": "Anna",
|
19
|
+
"New Page": "Ny side",
|
15
20
|
"0": "null",
|
16
21
|
"1": "ein",
|
17
22
|
"2": "to",
|
@@ -1,6 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"Synopsis": "Сводка",
|
3
3
|
"Short Description": "Краткое описание",
|
4
|
+
"Footnotes": "Примечания",
|
4
5
|
"Comment": "Комментарий",
|
5
6
|
"Notes": "Заметки",
|
6
7
|
"Tag": "Тэг",
|
@@ -13,6 +14,7 @@
|
|
13
14
|
"Objects": "Объекты",
|
14
15
|
"Entities": "Сущности",
|
15
16
|
"Custom": "Другое",
|
17
|
+
"New Page": "Новая страница",
|
16
18
|
"0": "Ноль",
|
17
19
|
"1": "Один",
|
18
20
|
"2": "Два",
|
@@ -94,6 +94,7 @@ icon:noncheckable = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 2
|
|
94
94
|
icon:open = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M16 2L21 7V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5447 3 21.0082V2.9918C3 2.44405 3.44495 2 3.9934 2H16ZM13 12H16L12 8L8 12H11V16H13V12Z" /></svg>
|
95
95
|
icon:panel = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M22 16V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V16H22ZM21 3C21.5523 3 22 3.44772 22 4V14H2V4C2 3.44772 2.44772 3 3 3H21Z" /></svg>
|
96
96
|
icon:pin = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M22.3126 10.1753L20.8984 11.5895L20.1913 10.8824L15.9486 15.125L15.2415 18.6606L13.8273 20.0748L9.58466 15.8321L4.63492 20.7819L3.2207 19.3677L8.17045 14.4179L3.92781 10.1753L5.34202 8.76107L8.87756 8.05396L13.1202 3.81132L12.4131 3.10422L13.8273 1.69L22.3126 10.1753Z" /></svg>
|
97
|
+
icon:project_copy = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6ZM6.9998 11V13H12.9998V11H6.9998ZM6.9998 15V17H12.9998V15H6.9998Z" /></svg>
|
97
98
|
icon:quote = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M19.4167 6.67891C20.4469 7.77257 21.0001 9 21.0001 10.9897C21.0001 14.4891 18.5436 17.6263 14.9695 19.1768L14.0768 17.7992C17.4121 15.9946 18.0639 13.6539 18.3245 12.178C17.7875 12.4557 17.0845 12.5533 16.3954 12.4895C14.591 12.3222 13.1689 10.8409 13.1689 9C13.1689 7.067 14.7359 5.5 16.6689 5.5C17.742 5.5 18.7681 5.99045 19.4167 6.67891ZM9.41669 6.67891C10.4469 7.77257 11.0001 9 11.0001 10.9897C11.0001 14.4891 8.54359 17.6263 4.96951 19.1768L4.07682 17.7992C7.41206 15.9946 8.06392 13.6539 8.32447 12.178C7.78747 12.4557 7.08452 12.5533 6.39539 12.4895C4.59102 12.3222 3.16895 10.8409 3.16895 9C3.16895 7.067 4.73595 5.5 6.66895 5.5C7.742 5.5 8.76814 5.99045 9.41669 6.67891Z" /></svg>
|
98
99
|
icon:refresh = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M5.46257 4.43262C7.21556 2.91688 9.5007 2 12 2C17.5228 2 22 6.47715 22 12C22 14.1361 21.3302 16.1158 20.1892 17.7406L17 12H20C20 7.58172 16.4183 4 12 4C9.84982 4 7.89777 4.84827 6.46023 6.22842L5.46257 4.43262ZM18.5374 19.5674C16.7844 21.0831 14.4993 22 12 22C6.47715 22 2 17.5228 2 12C2 9.86386 2.66979 7.88416 3.8108 6.25944L7 12H4C4 16.4183 7.58172 20 12 20C14.1502 20 16.1022 19.1517 17.5398 17.7716L18.5374 19.5674Z" /></svg>
|
99
100
|
icon:remove = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M19 11H5V13H19V11Z" /></svg>
|
@@ -94,6 +94,7 @@ icon:noncheckable = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 2
|
|
94
94
|
icon:open = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M15 4H5V20H19V8H15V4ZM3 2.9918C3 2.44405 3.44749 2 3.9985 2H16L20.9997 7L21 20.9925C21 21.5489 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5447 3 21.0082V2.9918ZM13 12V16H11V12H8L12 8L16 12H13Z" /></svg>
|
95
95
|
icon:panel = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M21 3C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3H21ZM4 16V19H20V16H4ZM4 14H20V5H4V14Z" /></svg>
|
96
96
|
icon:pin = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M13.8273 1.69L22.3126 10.1753L20.8984 11.5895L20.1913 10.8824L15.9486 15.125L15.2415 18.6606L13.8273 20.0748L9.58466 15.8321L4.63492 20.7819L3.2207 19.3677L8.17045 14.4179L3.92781 10.1753L5.34202 8.76107L8.87756 8.05396L13.1202 3.81132L12.4131 3.10422L13.8273 1.69ZM14.5344 5.22554L9.86358 9.89637L7.0417 10.4607L13.5418 16.9609L14.1062 14.139L18.7771 9.46818L14.5344 5.22554Z" /></svg>
|
97
|
+
icon:project_copy = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM5.00242 8L5.00019 20H14.9998V8H5.00242ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6ZM7 11H13V13H7V11ZM7 15H13V17H7V15Z" /></svg>
|
97
98
|
icon:quote = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M19.4167 6.67891C20.4469 7.77257 21.0001 9 21.0001 10.9897C21.0001 14.4891 18.5436 17.6263 14.9695 19.1768L14.0768 17.7992C17.4121 15.9946 18.0639 13.6539 18.3245 12.178C17.7875 12.4557 17.0845 12.5533 16.3954 12.4895C14.591 12.3222 13.1689 10.8409 13.1689 9C13.1689 7.067 14.7359 5.5 16.6689 5.5C17.742 5.5 18.7681 5.99045 19.4167 6.67891ZM9.41669 6.67891C10.4469 7.77257 11.0001 9 11.0001 10.9897C11.0001 14.4891 8.54359 17.6263 4.96951 19.1768L4.07682 17.7992C7.41206 15.9946 8.06392 13.6539 8.32447 12.178C7.78747 12.4557 7.08452 12.5533 6.39539 12.4895C4.59102 12.3222 3.16895 10.8409 3.16895 9C3.16895 7.067 4.73595 5.5 6.66895 5.5C7.742 5.5 8.76814 5.99045 9.41669 6.67891Z" /></svg>
|
98
99
|
icon:refresh = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M5.46257 4.43262C7.21556 2.91688 9.5007 2 12 2C17.5228 2 22 6.47715 22 12C22 14.1361 21.3302 16.1158 20.1892 17.7406L17 12H20C20 7.58172 16.4183 4 12 4C9.84982 4 7.89777 4.84827 6.46023 6.22842L5.46257 4.43262ZM18.5374 19.5674C16.7844 21.0831 14.4993 22 12 22C6.47715 22 2 17.5228 2 12C2 9.86386 2.66979 7.88416 3.8108 6.25944L7 12H4C4 16.4183 7.58172 20 12 20C14.1502 20 16.1022 19.1517 17.5398 17.7716L18.5374 19.5674Z" /></svg>
|
99
100
|
icon:remove = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000" height="128" width="128"><path d="M5 11V13H19V11H5Z" /></svg>
|
Binary file
|
novelwriter/assets/manual.pdf
CHANGED
Binary file
|
novelwriter/assets/manual_fr.pdf
CHANGED
Binary file
|
novelwriter/assets/sample.zip
CHANGED
Binary file
|
@@ -25,15 +25,15 @@ text = 86, 88, 105
|
|
25
25
|
link = 9, 161, 237
|
26
26
|
headertext = 45, 174, 88
|
27
27
|
headertag = 45, 174, 88, 160
|
28
|
-
emphasis =
|
29
|
-
dialog =
|
28
|
+
emphasis = 247, 103, 187
|
29
|
+
dialog = 9, 161, 237
|
30
30
|
altdialog = 207, 156, 0
|
31
31
|
note = 120, 187, 185
|
32
32
|
hidden = 145, 148, 162
|
33
33
|
shortcode = 247, 103, 187
|
34
34
|
keyword = 9, 161, 237
|
35
35
|
tag = 45, 174, 88
|
36
|
-
value =
|
36
|
+
value = 207, 156, 0
|
37
37
|
optional = 207, 156, 0
|
38
38
|
spellcheckline = 255, 92, 87
|
39
39
|
errorline = 45, 174, 88
|
@@ -47,6 +47,12 @@ translators for the languages currently available:</p>
|
|
47
47
|
<li><b>Portuguese:</b> Oli Maia (olimaia)</li>
|
48
48
|
</ul>
|
49
49
|
|
50
|
+
<p>Translations of the documentation:</p>
|
51
|
+
|
52
|
+
<ul>
|
53
|
+
<li><b>French:</b> Jean-Michel Heras (Karduin)</li>
|
54
|
+
</ul>
|
55
|
+
|
50
56
|
<p>Translations are managed on <a href="https://crowdin.com/project/novelwriter">Crowdin</a>, and
|
51
57
|
more contributions are listed on the project's Members page.</p>
|
52
58
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
[Main]
|
2
|
+
name = Snazzy Light
|
3
|
+
author = Veronica Berglyd Olsen (adaptation)
|
4
|
+
credit = Florian Reuschel (color theme)
|
5
|
+
url = https://github.com/loilo/vscode-snazzy-light
|
6
|
+
license = MIT License
|
7
|
+
licenseurl = https://github.com/loilo/vscode-snazzy-light/blob/master/LICENSE
|
8
|
+
|
9
|
+
[Icons]
|
10
|
+
default = 86, 88, 105
|
11
|
+
faded = 84, 85, 84
|
12
|
+
red = 255, 92, 87
|
13
|
+
orange = 245, 185, 0
|
14
|
+
yellow = 207, 156, 0
|
15
|
+
green = 45, 174, 88
|
16
|
+
aqua = 19, 187, 183
|
17
|
+
blue = 9, 161, 237
|
18
|
+
purple = 247, 103, 187
|
19
|
+
|
20
|
+
[Project]
|
21
|
+
root = 9, 161, 237
|
22
|
+
folder = 207, 156, 0
|
23
|
+
file = 84, 85, 84
|
24
|
+
title = 45, 174, 88
|
25
|
+
chapter = 255, 92, 87
|
26
|
+
scene = 9, 161, 237
|
27
|
+
note = 207, 156, 0
|
28
|
+
|
29
|
+
[Palette]
|
30
|
+
window = 243, 244, 245
|
31
|
+
windowtext = 86, 88, 105
|
32
|
+
base = 250, 251, 252
|
33
|
+
alternatebase = 234, 234, 235
|
34
|
+
text = 86, 88, 105
|
35
|
+
tooltipbase = 245, 233, 194
|
36
|
+
tooltiptext = 86, 88, 105
|
37
|
+
button = 250, 251, 252
|
38
|
+
buttontext = 86, 88, 105
|
39
|
+
brighttext = 255, 255, 255
|
40
|
+
highlight = 9, 161, 237
|
41
|
+
highlightedtext = 255, 255, 255
|
42
|
+
link = 9, 161, 237
|
43
|
+
linkvisited = 9, 161, 237
|
44
|
+
|
45
|
+
[GUI]
|
46
|
+
helptext = 9, 161, 237
|
47
|
+
fadedtext = 84, 85, 84
|
48
|
+
errortext = 255, 92, 87
|
novelwriter/common.py
CHANGED
@@ -40,7 +40,7 @@ from PyQt6.QtCore import QCoreApplication, QMimeData, QUrl
|
|
40
40
|
from PyQt6.QtGui import QAction, QDesktopServices, QFont, QFontDatabase, QFontInfo
|
41
41
|
from PyQt6.QtWidgets import QMenu, QMenuBar, QWidget
|
42
42
|
|
43
|
-
from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
|
43
|
+
from novelwriter.constants import nwConst, nwLabels, nwQuotes, nwUnicode, trConst
|
44
44
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
45
45
|
from novelwriter.error import logException
|
46
46
|
|
@@ -298,6 +298,15 @@ def uniqueCompact(text: str) -> str:
|
|
298
298
|
return "".join(sorted(set(compact(text))))
|
299
299
|
|
300
300
|
|
301
|
+
def processDialogSymbols(symbols: str) -> str:
|
302
|
+
"""Process dialogue line symbols."""
|
303
|
+
result = ""
|
304
|
+
for c in uniqueCompact(symbols):
|
305
|
+
if c in nwQuotes.ALLOWED:
|
306
|
+
result += c
|
307
|
+
return result
|
308
|
+
|
309
|
+
|
301
310
|
def elide(text: str, length: int) -> str:
|
302
311
|
"""Elide a piece of text to a maximum length."""
|
303
312
|
if len(text) > (cut := max(4, length)):
|
novelwriter/config.py
CHANGED
@@ -34,23 +34,24 @@ from time import time
|
|
34
34
|
from typing import TYPE_CHECKING, Final
|
35
35
|
|
36
36
|
from PyQt6.QtCore import (
|
37
|
-
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR,
|
38
|
-
QLocale, QStandardPaths, QSysInfo, QTranslator
|
37
|
+
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QDate,
|
38
|
+
QDateTime, QLibraryInfo, QLocale, QStandardPaths, QSysInfo, QTranslator
|
39
39
|
)
|
40
|
-
from PyQt6.QtGui import QFont, QFontDatabase
|
40
|
+
from PyQt6.QtGui import QFont, QFontDatabase, QFontMetrics
|
41
41
|
from PyQt6.QtWidgets import QApplication
|
42
42
|
|
43
43
|
from novelwriter.common import (
|
44
44
|
NWConfigParser, checkInt, checkPath, describeFont, fontMatcher,
|
45
|
-
formatTimeStamp
|
45
|
+
formatTimeStamp, processDialogSymbols, simplified
|
46
46
|
)
|
47
|
-
from novelwriter.constants import nwFiles, nwUnicode
|
47
|
+
from novelwriter.constants import nwFiles, nwHtmlUnicode, nwQuotes, nwUnicode
|
48
48
|
from novelwriter.error import formatException, logException
|
49
49
|
|
50
50
|
if TYPE_CHECKING:
|
51
51
|
from datetime import datetime
|
52
52
|
|
53
53
|
from novelwriter.core.projectdata import NWProjectData
|
54
|
+
from novelwriter.splash import NSplashScreen
|
54
55
|
|
55
56
|
logger = logging.getLogger(__name__)
|
56
57
|
|
@@ -64,16 +65,16 @@ class Config:
|
|
64
65
|
|
65
66
|
__slots__ = (
|
66
67
|
"_appPath", "_appRoot", "_backPath", "_backupPath", "_confPath", "_dLocale", "_dShortDate",
|
67
|
-
"_dShortDateTime", "_dataPath", "_errData", "_hasError", "_homePath", "
|
68
|
-
"_nwLangPath", "_qLocale", "_qtLangPath", "_qtTrans", "_recentPaths",
|
69
|
-
"
|
70
|
-
"askBeforeBackup", "askBeforeExit", "autoSaveDoc", "autoSaveProj",
|
71
|
-
"autoScrollPos", "autoSelect", "backupOnClose", "cursorWidth", "dialogLine",
|
72
|
-
"doJustify", "doReplace", "doReplaceDQuote", "doReplaceDash",
|
73
|
-
"doReplaceSQuote", "emphLabels", "fmtApostrophe", "fmtDQuoteClose",
|
74
|
-
"fmtPadAfter", "fmtPadBefore", "fmtPadThin", "fmtSQuoteClose",
|
75
|
-
"focusWidth", "guiFont", "guiLocale", "guiSyntax", "guiTheme",
|
76
|
-
"hideFocusFooter", "hideHScroll", "hideVScroll", "highlightEmph", "hostName",
|
68
|
+
"_dShortDateTime", "_dataPath", "_errData", "_hasError", "_homePath", "_lastAuthor",
|
69
|
+
"_manuals", "_nwLangPath", "_qLocale", "_qtLangPath", "_qtTrans", "_recentPaths",
|
70
|
+
"_recentProjects", "_splash", "allowOpenDial", "altDialogClose", "altDialogOpen",
|
71
|
+
"appHandle", "appName", "askBeforeBackup", "askBeforeExit", "autoSaveDoc", "autoSaveProj",
|
72
|
+
"autoScroll", "autoScrollPos", "autoSelect", "backupOnClose", "cursorWidth", "dialogLine",
|
73
|
+
"dialogStyle", "doJustify", "doReplace", "doReplaceDQuote", "doReplaceDash",
|
74
|
+
"doReplaceDots", "doReplaceSQuote", "emphLabels", "fmtApostrophe", "fmtDQuoteClose",
|
75
|
+
"fmtDQuoteOpen", "fmtPadAfter", "fmtPadBefore", "fmtPadThin", "fmtSQuoteClose",
|
76
|
+
"fmtSQuoteOpen", "focusWidth", "guiFont", "guiLocale", "guiSyntax", "guiTheme",
|
77
|
+
"hasEnchant", "hideFocusFooter", "hideHScroll", "hideVScroll", "highlightEmph", "hostName",
|
77
78
|
"iconColDocs", "iconColTree", "iconTheme", "incNotesWCount", "isDebug", "kernelVer",
|
78
79
|
"lastNotes", "mainPanePos", "mainWinSize", "memInfo", "narratorBreak", "narratorDialog",
|
79
80
|
"nativeFont", "osDarwin", "osLinux", "osType", "osUnknown", "osWindows", "outlinePanePos",
|
@@ -94,6 +95,8 @@ class Config:
|
|
94
95
|
# Initialisation
|
95
96
|
# ==============
|
96
97
|
|
98
|
+
self._splash = None
|
99
|
+
|
97
100
|
# Set Application Variables
|
98
101
|
self.appName = "novelWriter"
|
99
102
|
self.appHandle = "novelwriter"
|
@@ -146,6 +149,7 @@ class Config:
|
|
146
149
|
|
147
150
|
self._recentProjects = RecentProjects(self)
|
148
151
|
self._recentPaths = RecentPaths(self)
|
152
|
+
self._lastAuthor = ""
|
149
153
|
|
150
154
|
# General GUI Settings
|
151
155
|
self.guiLocale = self._qLocale.name()
|
@@ -318,6 +322,11 @@ class Config:
|
|
318
322
|
def recentProjects(self) -> RecentProjects:
|
319
323
|
return self._recentProjects
|
320
324
|
|
325
|
+
@property
|
326
|
+
def lastAuthor(self) -> str:
|
327
|
+
"""Return the last author name used."""
|
328
|
+
return simplified(self._lastAuthor)
|
329
|
+
|
321
330
|
##
|
322
331
|
# Getters
|
323
332
|
##
|
@@ -333,6 +342,11 @@ class Config:
|
|
333
342
|
# Setters
|
334
343
|
##
|
335
344
|
|
345
|
+
def setLastAuthor(self, value: str) -> None:
|
346
|
+
"""Set tle last used author name."""
|
347
|
+
self._lastAuthor = simplified(value)
|
348
|
+
return
|
349
|
+
|
336
350
|
def setMainWinSize(self, width: int, height: int) -> None:
|
337
351
|
"""Set the size of the main window, but only if the change is
|
338
352
|
larger than 5 pixels. The OS window manager will sometimes
|
@@ -464,11 +478,16 @@ class Config:
|
|
464
478
|
|
465
479
|
def localDate(self, value: datetime) -> str:
|
466
480
|
"""Return a localised date format."""
|
467
|
-
|
481
|
+
# Explicitly convert the date first, see bug #2325
|
482
|
+
return self._dLocale.toString(QDate(value.year, value.month, value.day), self._dShortDate)
|
468
483
|
|
469
484
|
def localDateTime(self, value: datetime) -> str:
|
470
485
|
"""Return a localised datetime format."""
|
471
|
-
|
486
|
+
# Explicitly convert the datetime first, see bug #2325
|
487
|
+
return self._dLocale.toString(
|
488
|
+
QDateTime(value.year, value.month, value.day, value.hour, value.minute, value.second),
|
489
|
+
self._dShortDateTime,
|
490
|
+
)
|
472
491
|
|
473
492
|
def listLanguages(self, lngSet: int) -> list[tuple[str, str]]:
|
474
493
|
"""List localisation files in the i18n folder. The default GUI
|
@@ -497,6 +516,12 @@ class Config:
|
|
497
516
|
|
498
517
|
return sorted(langList.items(), key=lambda x: x[0])
|
499
518
|
|
519
|
+
def splashMessage(self, message: str) -> None:
|
520
|
+
"""Send a message to the splash screen."""
|
521
|
+
if self._splash:
|
522
|
+
self._splash.showStatus(message)
|
523
|
+
return
|
524
|
+
|
500
525
|
##
|
501
526
|
# Config Actions
|
502
527
|
##
|
@@ -543,6 +568,8 @@ class Config:
|
|
543
568
|
|
544
569
|
def initLocalisation(self, nwApp: QApplication) -> None:
|
545
570
|
"""Initialise the localisation of the GUI."""
|
571
|
+
self.splashMessage("Loading localisation ...")
|
572
|
+
|
546
573
|
self._qLocale = QLocale(self.guiLocale)
|
547
574
|
QLocale.setDefault(self._qLocale)
|
548
575
|
self._qtTrans = {}
|
@@ -568,8 +595,11 @@ class Config:
|
|
568
595
|
|
569
596
|
return
|
570
597
|
|
571
|
-
def loadConfig(self) -> bool:
|
598
|
+
def loadConfig(self, splash: NSplashScreen | None = None) -> bool:
|
572
599
|
"""Load preferences from file and replace default settings."""
|
600
|
+
self._splash = splash
|
601
|
+
self.splashMessage("Loading user configuration ...")
|
602
|
+
|
573
603
|
logger.debug("Loading config file")
|
574
604
|
|
575
605
|
conf = NWConfigParser()
|
@@ -626,6 +656,7 @@ class Config:
|
|
626
656
|
self.backupOnClose = conf.rdBool(sec, "backuponclose", self.backupOnClose)
|
627
657
|
self.askBeforeBackup = conf.rdBool(sec, "askbeforebackup", self.askBeforeBackup)
|
628
658
|
self.askBeforeExit = conf.rdBool(sec, "askbeforeexit", self.askBeforeExit)
|
659
|
+
self._lastAuthor = conf.rdStr(sec, "lastauthor", self._lastAuthor)
|
629
660
|
|
630
661
|
# Editor
|
631
662
|
sec = "Editor"
|
@@ -661,9 +692,9 @@ class Config:
|
|
661
692
|
self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
|
662
693
|
self.dialogStyle = conf.rdInt(sec, "dialogstyle", self.dialogStyle)
|
663
694
|
self.allowOpenDial = conf.rdBool(sec, "allowopendial", self.allowOpenDial)
|
664
|
-
|
665
|
-
|
666
|
-
|
695
|
+
dialogLine = conf.rdStr(sec, "dialogline", self.dialogLine)
|
696
|
+
narratorBreak = conf.rdStr(sec, "narratorbreak", self.narratorBreak)
|
697
|
+
narratorDialog = conf.rdStr(sec, "narratordialog", self.narratorDialog)
|
667
698
|
self.altDialogOpen = conf.rdStr(sec, "altdialogopen", self.altDialogOpen)
|
668
699
|
self.altDialogClose = conf.rdStr(sec, "altdialogclose", self.altDialogClose)
|
669
700
|
self.highlightEmph = conf.rdBool(sec, "highlightemph", self.highlightEmph)
|
@@ -690,6 +721,9 @@ class Config:
|
|
690
721
|
# Check Values
|
691
722
|
# ============
|
692
723
|
|
724
|
+
self._prepareFont(self.guiFont, "GUI")
|
725
|
+
self._prepareFont(self.textFont, "document")
|
726
|
+
|
693
727
|
# If we're using straight quotes, disable auto-replace
|
694
728
|
if self.fmtSQuoteOpen == self.fmtSQuoteClose == "'" and self.doReplaceSQuote:
|
695
729
|
logger.info("Using straight single quotes, so disabling auto-replace")
|
@@ -699,6 +733,10 @@ class Config:
|
|
699
733
|
logger.info("Using straight double quotes, so disabling auto-replace")
|
700
734
|
self.doReplaceDQuote = False
|
701
735
|
|
736
|
+
self.dialogLine = processDialogSymbols(dialogLine)
|
737
|
+
self.narratorBreak = narratorBreak if narratorBreak in nwQuotes.DASHES else ""
|
738
|
+
self.narratorDialog = narratorDialog if narratorDialog in nwQuotes.DASHES else ""
|
739
|
+
|
702
740
|
return True
|
703
741
|
|
704
742
|
def saveConfig(self) -> bool:
|
@@ -743,6 +781,7 @@ class Config:
|
|
743
781
|
"backuponclose": str(self.backupOnClose),
|
744
782
|
"askbeforebackup": str(self.askBeforeBackup),
|
745
783
|
"askbeforeexit": str(self.askBeforeExit),
|
784
|
+
"lastauthor": str(self._lastAuthor),
|
746
785
|
}
|
747
786
|
|
748
787
|
conf["Editor"] = {
|
@@ -820,6 +859,11 @@ class Config:
|
|
820
859
|
|
821
860
|
return True
|
822
861
|
|
862
|
+
def finishStartup(self) -> None:
|
863
|
+
"""Call after startup is complete."""
|
864
|
+
self._splash = None
|
865
|
+
return
|
866
|
+
|
823
867
|
##
|
824
868
|
# Internal Functions
|
825
869
|
##
|
@@ -842,6 +886,17 @@ class Config:
|
|
842
886
|
logger.debug("Checking package 'pyenchant': OK")
|
843
887
|
return
|
844
888
|
|
889
|
+
def _prepareFont(self, font: QFont, kind: str) -> None:
|
890
|
+
"""Check Unicode availability in font. This also initialises any
|
891
|
+
alternative character used for missing glyphs. See #2315.
|
892
|
+
"""
|
893
|
+
self.splashMessage(f"Initialising {kind} font: {font.family()}")
|
894
|
+
metrics = QFontMetrics(font)
|
895
|
+
for char in nwHtmlUnicode.U_TO_H.keys():
|
896
|
+
if not metrics.inFont(char): # type: ignore
|
897
|
+
logger.warning("No glyph U+%04x in font", ord(char)) # pragma: no cover
|
898
|
+
return
|
899
|
+
|
845
900
|
|
846
901
|
class RecentProjects:
|
847
902
|
|
@@ -864,9 +919,10 @@ class RecentProjects:
|
|
864
919
|
puuid = str(entry.get("uuid", ""))
|
865
920
|
title = str(entry.get("title", ""))
|
866
921
|
words = checkInt(entry.get("words", 0), 0)
|
922
|
+
chars = checkInt(entry.get("chars", 0), 0)
|
867
923
|
saved = checkInt(entry.get("time", 0), 0)
|
868
924
|
if path and title:
|
869
|
-
self._setEntry(puuid, path, title, words, saved)
|
925
|
+
self._setEntry(puuid, path, title, words, chars, saved)
|
870
926
|
except Exception:
|
871
927
|
logger.error("Could not load recent project cache")
|
872
928
|
logException()
|
@@ -899,7 +955,14 @@ class RecentProjects:
|
|
899
955
|
try:
|
900
956
|
if (remove := self._map.get(data.uuid)) and (remove != str(path)):
|
901
957
|
self.remove(remove)
|
902
|
-
self._setEntry(
|
958
|
+
self._setEntry(
|
959
|
+
data.uuid,
|
960
|
+
str(path),
|
961
|
+
data.name,
|
962
|
+
sum(data.currCounts[:2]),
|
963
|
+
sum(data.currCounts[2:]),
|
964
|
+
int(saved),
|
965
|
+
)
|
903
966
|
self.saveCache()
|
904
967
|
except Exception:
|
905
968
|
pass
|
@@ -912,9 +975,17 @@ class RecentProjects:
|
|
912
975
|
self.saveCache()
|
913
976
|
return
|
914
977
|
|
915
|
-
def _setEntry(
|
978
|
+
def _setEntry(
|
979
|
+
self, puuid: str, path: str, title: str, words: int, chars: int, saved: int
|
980
|
+
) -> None:
|
916
981
|
"""Set an entry in the recent projects record."""
|
917
|
-
self._data[path] = {
|
982
|
+
self._data[path] = {
|
983
|
+
"uuid": puuid,
|
984
|
+
"title": title,
|
985
|
+
"words": words,
|
986
|
+
"chars": chars,
|
987
|
+
"time": saved,
|
988
|
+
}
|
918
989
|
if puuid:
|
919
990
|
self._map[puuid] = path
|
920
991
|
return
|