novelWriter 2.2rc1__py3-none-any.whl → 2.3b1__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.2rc1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +141 -129
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +11 -6
- 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_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_de_DE.json +1 -0
- novelwriter/assets/i18n/project_en_US.json +1 -0
- novelwriter/assets/i18n/project_es_419.json +11 -0
- novelwriter/assets/i18n/project_fr_FR.json +11 -0
- novelwriter/assets/i18n/project_it_IT.json +11 -0
- novelwriter/assets/i18n/project_ja_JP.json +2 -1
- novelwriter/assets/i18n/project_nb_NO.json +1 -0
- novelwriter/assets/i18n/project_zh_CN.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +9 -2
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
- novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +9 -2
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
- novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
- novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
- novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
- novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
- novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
- novelwriter/assets/images/novelwriter-text-light.svg +4 -0
- novelwriter/assets/images/welcome-dark.jpg +0 -0
- novelwriter/assets/images/welcome-light.jpg +0 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/default_dark.conf +1 -0
- novelwriter/assets/syntax/default_light.conf +1 -0
- novelwriter/assets/syntax/grey_dark.conf +1 -0
- novelwriter/assets/syntax/grey_light.conf +1 -0
- novelwriter/assets/syntax/light_owl.conf +1 -0
- novelwriter/assets/syntax/night_owl.conf +1 -0
- novelwriter/assets/syntax/solarized_dark.conf +1 -0
- novelwriter/assets/syntax/solarized_light.conf +1 -0
- novelwriter/assets/syntax/tomorrow.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
- novelwriter/assets/text/credits_en.htm +4 -2
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +64 -66
- novelwriter/config.py +39 -44
- novelwriter/constants.py +39 -17
- novelwriter/core/buildsettings.py +8 -8
- novelwriter/core/coretools.py +194 -155
- novelwriter/core/docbuild.py +7 -4
- novelwriter/core/document.py +7 -7
- novelwriter/core/index.py +90 -57
- novelwriter/core/item.py +23 -5
- novelwriter/core/options.py +11 -10
- novelwriter/core/project.py +72 -47
- novelwriter/core/projectdata.py +3 -16
- novelwriter/core/projectxml.py +14 -42
- novelwriter/core/sessions.py +4 -3
- novelwriter/core/spellcheck.py +6 -4
- novelwriter/core/status.py +5 -4
- novelwriter/core/storage.py +179 -141
- novelwriter/core/tohtml.py +6 -4
- novelwriter/core/tokenizer.py +74 -46
- novelwriter/core/tomd.py +2 -2
- novelwriter/core/toodt.py +41 -31
- novelwriter/core/tree.py +5 -4
- novelwriter/dialogs/about.py +88 -179
- novelwriter/dialogs/docmerge.py +30 -20
- novelwriter/dialogs/docsplit.py +33 -22
- novelwriter/dialogs/editlabel.py +20 -8
- novelwriter/dialogs/preferences.py +562 -725
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +301 -270
- novelwriter/dialogs/quotes.py +47 -36
- novelwriter/dialogs/wordlist.py +128 -59
- novelwriter/enum.py +25 -22
- novelwriter/error.py +2 -2
- novelwriter/extensions/circularprogress.py +12 -12
- novelwriter/extensions/configlayout.py +185 -146
- novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +27 -13
- novelwriter/extensions/pagedsidebar.py +15 -20
- novelwriter/extensions/simpleprogress.py +8 -9
- novelwriter/extensions/statusled.py +9 -9
- novelwriter/extensions/switch.py +32 -64
- novelwriter/extensions/switchbox.py +2 -7
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +250 -214
- novelwriter/gui/dochighlight.py +66 -94
- novelwriter/gui/docviewer.py +71 -98
- novelwriter/gui/docviewerpanel.py +140 -47
- novelwriter/gui/editordocument.py +3 -3
- novelwriter/gui/itemdetails.py +9 -9
- novelwriter/gui/mainmenu.py +47 -46
- novelwriter/gui/noveltree.py +53 -61
- novelwriter/gui/outline.py +100 -76
- novelwriter/gui/projtree.py +193 -67
- novelwriter/gui/sidebar.py +9 -8
- novelwriter/gui/statusbar.py +49 -7
- novelwriter/gui/theme.py +65 -74
- novelwriter/guimain.py +173 -330
- novelwriter/shared.py +68 -30
- novelwriter/tools/dictionaries.py +7 -8
- novelwriter/tools/lipsum.py +34 -28
- novelwriter/tools/manusbuild.py +3 -4
- novelwriter/tools/manuscript.py +25 -32
- novelwriter/tools/manussettings.py +194 -225
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +802 -0
- novelwriter/tools/writingstats.py +26 -13
- novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
- novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
- novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
- novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -17
- novelwriter/dialogs/projdetails.py +0 -525
- novelwriter/dialogs/projload.py +0 -298
- novelwriter/dialogs/updates.py +0 -182
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
@@ -2,8 +2,6 @@
|
|
2
2
|
<html>
|
3
3
|
<body>
|
4
4
|
|
5
|
-
<h2>Credits</h2>
|
6
|
-
|
7
5
|
<h3>Main Developer</h3>
|
8
6
|
<p>Veronica Berglyd Olsen (<a href="https://github.com/vkbo">@vkbo</a>)</p>
|
9
7
|
|
@@ -17,6 +15,10 @@
|
|
17
15
|
<p>For other contributions, see the project's
|
18
16
|
<a href="https://github.com/vkbo/novelWriter/graphs/contributors">Contributors</a> page.</p>
|
19
17
|
|
18
|
+
<h3>Artwork</h3>
|
19
|
+
|
20
|
+
<p>The artwork on the Welcome dialog was created by <a href="https://louisdurrant.art">Louis Durrant</a>.</p>
|
21
|
+
|
20
22
|
<h3>Translations</h3>
|
21
23
|
|
22
24
|
<p>The default language is English (UK) with English (US) as an option. These are the original
|
@@ -21,8 +21,8 @@ buttontext = 204, 204, 204
|
|
21
21
|
brighttext = 62, 62, 62
|
22
22
|
highlight = 44, 152, 247
|
23
23
|
highlightedtext = 255, 255, 255
|
24
|
-
link =
|
25
|
-
linkvisited =
|
24
|
+
link = 102, 153, 204
|
25
|
+
linkvisited = 102, 153, 204
|
26
26
|
|
27
27
|
[GUI]
|
28
28
|
helptext = 164, 164, 164
|
novelwriter/common.py
CHANGED
@@ -3,10 +3,10 @@ novelWriter – Common Functions
|
|
3
3
|
==============================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2019-05-12 [0.1]
|
6
|
+
Created: 2019-05-12 [0.1.0]
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -29,7 +29,7 @@ import logging
|
|
29
29
|
import unicodedata
|
30
30
|
import xml.etree.ElementTree as ET
|
31
31
|
|
32
|
-
from typing import Any, Literal
|
32
|
+
from typing import TYPE_CHECKING, Any, Literal
|
33
33
|
from pathlib import Path
|
34
34
|
from datetime import datetime
|
35
35
|
from configparser import ConfigParser
|
@@ -38,18 +38,20 @@ from urllib.request import pathname2url
|
|
38
38
|
|
39
39
|
from PyQt5.QtGui import QDesktopServices
|
40
40
|
from PyQt5.QtCore import QCoreApplication, QUrl
|
41
|
-
from PyQt5.QtWidgets import QWidget, qApp
|
42
41
|
|
43
42
|
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
44
43
|
from novelwriter.error import logException
|
45
|
-
from novelwriter.constants import nwConst, nwUnicode
|
44
|
+
from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
|
45
|
+
|
46
|
+
if TYPE_CHECKING: # pragma: no cover
|
47
|
+
from typing import TypeGuard # Requires Python 3.10
|
46
48
|
|
47
49
|
logger = logging.getLogger(__name__)
|
48
50
|
|
49
51
|
|
50
|
-
|
52
|
+
##
|
51
53
|
# Checker Functions
|
52
|
-
|
54
|
+
##
|
53
55
|
|
54
56
|
def checkStringNone(value: Any, default: str | None) -> str | None:
|
55
57
|
"""Check if a variable is a string or a None."""
|
@@ -105,15 +107,6 @@ def checkBool(value: Any, default: bool) -> bool:
|
|
105
107
|
return default
|
106
108
|
|
107
109
|
|
108
|
-
def checkHandle(value, default, allowNone=False):
|
109
|
-
"""Check if a value is a handle."""
|
110
|
-
if allowNone and (value is None or value == "None"):
|
111
|
-
return None
|
112
|
-
if isHandle(value):
|
113
|
-
return str(value)
|
114
|
-
return default
|
115
|
-
|
116
|
-
|
117
110
|
def checkUuid(value: Any, default: str) -> str:
|
118
111
|
"""Try to process a value as an UUID, or return a default."""
|
119
112
|
try:
|
@@ -132,11 +125,11 @@ def checkPath(value: Any, default: Path) -> Path:
|
|
132
125
|
return default
|
133
126
|
|
134
127
|
|
135
|
-
|
128
|
+
##
|
136
129
|
# Validator Functions
|
137
|
-
|
130
|
+
##
|
138
131
|
|
139
|
-
def isHandle(value: Any) ->
|
132
|
+
def isHandle(value: Any) -> TypeGuard[str]:
|
140
133
|
"""Check if a string is a valid novelWriter handle.
|
141
134
|
Note: This is case sensitive. Must be lower case!
|
142
135
|
"""
|
@@ -150,7 +143,7 @@ def isHandle(value: Any) -> bool:
|
|
150
143
|
return True
|
151
144
|
|
152
145
|
|
153
|
-
def isTitleTag(value: Any) ->
|
146
|
+
def isTitleTag(value: Any) -> TypeGuard[str]:
|
154
147
|
"""Check if a string is a valid title tag string."""
|
155
148
|
if not isinstance(value, str):
|
156
149
|
return False
|
@@ -164,19 +157,19 @@ def isTitleTag(value: Any) -> bool:
|
|
164
157
|
return True
|
165
158
|
|
166
159
|
|
167
|
-
def isItemClass(value:
|
160
|
+
def isItemClass(value: Any) -> TypeGuard[str]:
|
168
161
|
"""Check if a string is a valid nwItemClass identifier."""
|
169
|
-
return value in nwItemClass.__members__
|
162
|
+
return isinstance(value, str) and value in nwItemClass.__members__
|
170
163
|
|
171
164
|
|
172
|
-
def isItemType(value:
|
165
|
+
def isItemType(value: Any) -> TypeGuard[str]:
|
173
166
|
"""Check if a string is a valid nwItemType identifier."""
|
174
|
-
return value in nwItemType.__members__
|
167
|
+
return isinstance(value, str) and value in nwItemType.__members__
|
175
168
|
|
176
169
|
|
177
|
-
def isItemLayout(value:
|
170
|
+
def isItemLayout(value: Any) -> TypeGuard[str]:
|
178
171
|
"""Check if a string is a valid nwItemLayout identifier."""
|
179
|
-
return value in nwItemLayout.__members__
|
172
|
+
return isinstance(value, str) and value in nwItemLayout.__members__
|
180
173
|
|
181
174
|
|
182
175
|
def hexToInt(value: Any, default: int = 0) -> int:
|
@@ -190,8 +183,7 @@ def hexToInt(value: Any, default: int = 0) -> int:
|
|
190
183
|
|
191
184
|
|
192
185
|
def minmax(value: int, minVal: int, maxVal: int) -> int:
|
193
|
-
"""
|
194
|
-
"""
|
186
|
+
"""Check that an value is between min and max value (inclusive)."""
|
195
187
|
return min(maxVal, max(minVal, value))
|
196
188
|
|
197
189
|
|
@@ -205,26 +197,26 @@ def checkIntTuple(value: int, valid: tuple | list | set, default: int) -> int:
|
|
205
197
|
return default
|
206
198
|
|
207
199
|
|
208
|
-
|
200
|
+
##
|
209
201
|
# Formatting Functions
|
210
|
-
|
202
|
+
##
|
211
203
|
|
212
204
|
def formatInt(value: int) -> str:
|
213
205
|
"""Formats an integer with k, M, G etc."""
|
214
206
|
if not isinstance(value, int):
|
215
207
|
return "ERR"
|
216
208
|
|
217
|
-
|
218
|
-
if
|
209
|
+
fVal = float(value)
|
210
|
+
if fVal > 1000.0:
|
219
211
|
for pF in ["k", "M", "G", "T", "P", "E"]:
|
220
|
-
|
221
|
-
if
|
222
|
-
if
|
223
|
-
return f"{
|
224
|
-
elif
|
225
|
-
return f"{
|
212
|
+
fVal /= 1000.0
|
213
|
+
if fVal < 1000.0:
|
214
|
+
if fVal < 10.0:
|
215
|
+
return f"{fVal:4.2f}{nwUnicode.U_THSP}{pF}"
|
216
|
+
elif fVal < 100.0:
|
217
|
+
return f"{fVal:4.1f}{nwUnicode.U_THSP}{pF}"
|
226
218
|
else:
|
227
|
-
return f"{
|
219
|
+
return f"{fVal:3.0f}{nwUnicode.U_THSP}{pF}"
|
228
220
|
|
229
221
|
return str(value)
|
230
222
|
|
@@ -251,9 +243,27 @@ def formatTime(t: int) -> str:
|
|
251
243
|
return "ERROR"
|
252
244
|
|
253
245
|
|
254
|
-
|
246
|
+
def formatVersion(value: str) -> str:
|
247
|
+
"""Format a version number into a more human readable form."""
|
248
|
+
return value.lower().replace("a", " Alpha ").replace("b", " Beta ").replace("rc", " RC ")
|
249
|
+
|
250
|
+
|
251
|
+
def formatFileFilter(extensions: list[str | tuple[str, str]]) -> str:
|
252
|
+
"""Format a list of extensions, or extension + label pairs into a
|
253
|
+
QFileDialog extensions filter.
|
254
|
+
"""
|
255
|
+
result = []
|
256
|
+
for ext in extensions:
|
257
|
+
if isinstance(ext, str):
|
258
|
+
result.append(f"{trConst(nwLabels.FILE_FILTERS.get(ext))} ({ext})")
|
259
|
+
elif isinstance(ext, tuple) and len(ext) == 2:
|
260
|
+
result.append(f"{ext[0]} ({ext[1]})")
|
261
|
+
return ";;".join(result)
|
262
|
+
|
263
|
+
|
264
|
+
##
|
255
265
|
# String Functions
|
256
|
-
|
266
|
+
##
|
257
267
|
|
258
268
|
def simplified(text: str) -> str:
|
259
269
|
"""Take a string and strip leading and trailing whitespaces, and
|
@@ -271,22 +281,22 @@ def transferCase(source: str, target: str) -> str:
|
|
271
281
|
"""Transfers the case of the source word to the target word. This
|
272
282
|
will consider all upper or lower, and first char capitalisation.
|
273
283
|
"""
|
274
|
-
|
284
|
+
result = target
|
275
285
|
|
276
286
|
if not isinstance(source, str) or not isinstance(target, str):
|
277
|
-
return
|
287
|
+
return result
|
278
288
|
if len(target) < 1 or len(source) < 1:
|
279
|
-
return
|
289
|
+
return result
|
280
290
|
|
281
291
|
if source.istitle():
|
282
|
-
|
292
|
+
result = target.title()
|
283
293
|
|
284
294
|
if source.isupper():
|
285
|
-
|
295
|
+
result = target.upper()
|
286
296
|
elif source.islower():
|
287
|
-
|
297
|
+
result = target.lower()
|
288
298
|
|
289
|
-
return
|
299
|
+
return result
|
290
300
|
|
291
301
|
|
292
302
|
def fuzzyTime(seconds: int) -> str:
|
@@ -372,9 +382,9 @@ def numberToRoman(value: int, toLower: bool = False) -> str:
|
|
372
382
|
return roman.lower() if toLower else roman
|
373
383
|
|
374
384
|
|
375
|
-
|
385
|
+
##
|
376
386
|
# Encoder Functions
|
377
|
-
|
387
|
+
##
|
378
388
|
|
379
389
|
def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
|
380
390
|
"""Encode a dictionary, list or tuple as a json object or array, and
|
@@ -464,9 +474,9 @@ def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
|
|
464
474
|
return
|
465
475
|
|
466
476
|
|
467
|
-
|
477
|
+
##
|
468
478
|
# File and File System Functions
|
469
|
-
|
479
|
+
##
|
470
480
|
|
471
481
|
def readTextFile(path: str | Path) -> str:
|
472
482
|
"""Read the content of a text file in a robust manner."""
|
@@ -508,21 +518,9 @@ def openExternalPath(path: Path) -> bool:
|
|
508
518
|
return False
|
509
519
|
|
510
520
|
|
511
|
-
|
512
|
-
# Other Functions
|
513
|
-
# =============================================================================================== #
|
514
|
-
|
515
|
-
def getGuiItem(objName: str) -> QWidget | None:
|
516
|
-
"""Returns a QtWidget based on its objectName."""
|
517
|
-
for qWidget in qApp.topLevelWidgets():
|
518
|
-
if qWidget.objectName() == objName:
|
519
|
-
return qWidget
|
520
|
-
return None
|
521
|
-
|
522
|
-
|
523
|
-
# =============================================================================================== #
|
521
|
+
##
|
524
522
|
# Classes
|
525
|
-
|
523
|
+
##
|
526
524
|
|
527
525
|
class NWConfigParser(ConfigParser):
|
528
526
|
"""Common: Adapted Config Parser
|
novelwriter/config.py
CHANGED
@@ -7,7 +7,7 @@ Created: 2018-09-22 [0.0.1] Config
|
|
7
7
|
Created: 2022-11-09 [2.0rc2] RecentProjects
|
8
8
|
|
9
9
|
This file is a part of novelWriter
|
10
|
-
Copyright 2018–
|
10
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
11
11
|
|
12
12
|
This program is free software: you can redistribute it and/or modify
|
13
13
|
it under the terms of the GNU General Public License as published by
|
@@ -110,8 +110,8 @@ class Config:
|
|
110
110
|
|
111
111
|
# Size Settings
|
112
112
|
self._mainWinSize = [1200, 650] # Last size of the main GUI window
|
113
|
+
self._welcomeSize = [800, 550] # Last size of the welcome window
|
113
114
|
self._prefsWinSize = [700, 615] # Last size of the Preferences dialog
|
114
|
-
self._projLoadCols = [280, 60, 160] # Last columns widths of the Project Load dialog
|
115
115
|
self._mainPanePos = [300, 800] # Last position of the main window splitter
|
116
116
|
self._viewPanePos = [500, 150] # Last position of the document viewer splitter
|
117
117
|
self._outlnPanePos = [500, 150] # Last position of the outline panel splitter
|
@@ -150,9 +150,6 @@ class Config:
|
|
150
150
|
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
|
151
151
|
self.scrollPastEnd = True # Scroll past end of document, and centre cursor
|
152
152
|
|
153
|
-
self.wordCountTimer = 5.0 # Interval for word count update in seconds
|
154
|
-
self.incNotesWCount = True # The status bar word count includes notes
|
155
|
-
|
156
153
|
self.highlightQuotes = True # Highlight text in quotes
|
157
154
|
self.allowOpenSQuote = False # Allow open-ended single quotes
|
158
155
|
self.allowOpenDQuote = True # Allow open-ended double quotes
|
@@ -160,6 +157,7 @@ class Config:
|
|
160
157
|
|
161
158
|
self.stopWhenIdle = True # Stop the status bar clock when the user is idle
|
162
159
|
self.userIdleTime = 300 # Time of inactivity to consider user idle
|
160
|
+
self.incNotesWCount = True # The status bar word count includes notes
|
163
161
|
|
164
162
|
# User-Selected Symbol Settings
|
165
163
|
self.fmtApostrophe = nwUnicode.U_RSQUO
|
@@ -225,7 +223,8 @@ class Config:
|
|
225
223
|
# Other System Info
|
226
224
|
self.hostName = QSysInfo.machineHostName()
|
227
225
|
self.kernelVer = QSysInfo.kernelVersion()
|
228
|
-
self.isDebug = False
|
226
|
+
self.isDebug = False # True if running in debug mode
|
227
|
+
self.memInfo = False # True if displaying mem info in status bar
|
229
228
|
|
230
229
|
# Packages
|
231
230
|
self.hasEnchant = False # The pyenchant package
|
@@ -249,12 +248,12 @@ class Config:
|
|
249
248
|
return [int(x*self.guiScale) for x in self._mainWinSize]
|
250
249
|
|
251
250
|
@property
|
252
|
-
def
|
253
|
-
return [int(x*self.guiScale) for x in self.
|
251
|
+
def welcomeWinSize(self) -> list[int]:
|
252
|
+
return [int(x*self.guiScale) for x in self._welcomeSize]
|
254
253
|
|
255
254
|
@property
|
256
|
-
def
|
257
|
-
return [int(x*self.guiScale) for x in self.
|
255
|
+
def preferencesWinSize(self) -> list[int]:
|
256
|
+
return [int(x*self.guiScale) for x in self._prefsWinSize]
|
258
257
|
|
259
258
|
@property
|
260
259
|
def mainPanePos(self) -> list[int]:
|
@@ -305,17 +304,18 @@ class Config:
|
|
305
304
|
self._mainWinSize[1] = height
|
306
305
|
return
|
307
306
|
|
307
|
+
def setWelcomeWinSize(self, width: int, height: int) -> None:
|
308
|
+
"""Set the size of the Preferences dialog window."""
|
309
|
+
self._welcomeSize[0] = int(width/self.guiScale)
|
310
|
+
self._welcomeSize[1] = int(height/self.guiScale)
|
311
|
+
return
|
312
|
+
|
308
313
|
def setPreferencesWinSize(self, width: int, height: int) -> None:
|
309
314
|
"""Set the size of the Preferences dialog window."""
|
310
315
|
self._prefsWinSize[0] = int(width/self.guiScale)
|
311
316
|
self._prefsWinSize[1] = int(height/self.guiScale)
|
312
317
|
return
|
313
318
|
|
314
|
-
def setProjLoadColWidths(self, widths: list[int]) -> None:
|
315
|
-
"""Set the column widths of the Load Project dialog."""
|
316
|
-
self._projLoadCols = [int(x/self.guiScale) for x in widths]
|
317
|
-
return
|
318
|
-
|
319
319
|
def setMainPanePos(self, pos: list[int]) -> None:
|
320
320
|
"""Set the position of the main GUI splitter."""
|
321
321
|
self._mainPanePos = [int(x/self.guiScale) for x in pos]
|
@@ -409,10 +409,10 @@ class Config:
|
|
409
409
|
"""Compile and return error messages from the initialisation of
|
410
410
|
the Config class, and clear the error buffer.
|
411
411
|
"""
|
412
|
-
|
412
|
+
message = "<br>".join(self._errData)
|
413
413
|
self._hasError = False
|
414
414
|
self._errData = []
|
415
|
-
return
|
415
|
+
return message
|
416
416
|
|
417
417
|
def listLanguages(self, lngSet: int) -> list[tuple[str, str]]:
|
418
418
|
"""List localisation files in the i18n folder. The default GUI
|
@@ -485,7 +485,6 @@ class Config:
|
|
485
485
|
|
486
486
|
self._recentObj.loadCache()
|
487
487
|
self._checkOptionalPackages()
|
488
|
-
self.isDebug = logger.getEffectiveLevel() == logging.DEBUG
|
489
488
|
|
490
489
|
logger.debug("Config instance initialised")
|
491
490
|
|
@@ -545,8 +544,8 @@ class Config:
|
|
545
544
|
# Sizes
|
546
545
|
sec = "Sizes"
|
547
546
|
self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize)
|
547
|
+
self._welcomeSize = conf.rdIntList(sec, "welcome", self._welcomeSize)
|
548
548
|
self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize)
|
549
|
-
self._projLoadCols = conf.rdIntList(sec, "projloadcols", self._projLoadCols)
|
550
549
|
self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos)
|
551
550
|
self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos)
|
552
551
|
self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos)
|
@@ -590,7 +589,6 @@ class Config:
|
|
590
589
|
self.showTabsNSpaces = conf.rdBool(sec, "showtabsnspaces", self.showTabsNSpaces)
|
591
590
|
self.showLineEndings = conf.rdBool(sec, "showlineendings", self.showLineEndings)
|
592
591
|
self.showMultiSpaces = conf.rdBool(sec, "showmultispaces", self.showMultiSpaces)
|
593
|
-
self.wordCountTimer = conf.rdFlt(sec, "wordcounttimer", self.wordCountTimer)
|
594
592
|
self.incNotesWCount = conf.rdBool(sec, "incnoteswcount", self.incNotesWCount)
|
595
593
|
self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
|
596
594
|
self.highlightQuotes = conf.rdBool(sec, "highlightquotes", self.highlightQuotes)
|
@@ -635,7 +633,7 @@ class Config:
|
|
635
633
|
conf = NWConfigParser()
|
636
634
|
|
637
635
|
conf["Meta"] = {
|
638
|
-
"timestamp":
|
636
|
+
"timestamp": formatTimeStamp(time()),
|
639
637
|
}
|
640
638
|
|
641
639
|
conf["Main"] = {
|
@@ -651,12 +649,12 @@ class Config:
|
|
651
649
|
}
|
652
650
|
|
653
651
|
conf["Sizes"] = {
|
654
|
-
"mainwindow":
|
655
|
-
"
|
656
|
-
"
|
657
|
-
"mainpane":
|
658
|
-
"viewpane":
|
659
|
-
"outlinepane":
|
652
|
+
"mainwindow": self._packList(self._mainWinSize),
|
653
|
+
"welcome": self._packList(self._welcomeSize),
|
654
|
+
"preferences": self._packList(self._prefsWinSize),
|
655
|
+
"mainpane": self._packList(self._mainPanePos),
|
656
|
+
"viewpane": self._packList(self._viewPanePos),
|
657
|
+
"outlinepane": self._packList(self._outlnPanePos),
|
660
658
|
}
|
661
659
|
|
662
660
|
conf["Project"] = {
|
@@ -697,7 +695,6 @@ class Config:
|
|
697
695
|
"showtabsnspaces": str(self.showTabsNSpaces),
|
698
696
|
"showlineendings": str(self.showLineEndings),
|
699
697
|
"showmultispaces": str(self.showMultiSpaces),
|
700
|
-
"wordcounttimer": str(self.wordCountTimer),
|
701
698
|
"incnoteswcount": str(self.incNotesWCount),
|
702
699
|
"showfullpath": str(self.showFullPath),
|
703
700
|
"highlightquotes": str(self.highlightQuotes),
|
@@ -774,22 +771,20 @@ class RecentProjects:
|
|
774
771
|
self._data = {}
|
775
772
|
|
776
773
|
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
|
777
|
-
if
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
logException()
|
792
|
-
return False
|
774
|
+
if cacheFile.is_file():
|
775
|
+
try:
|
776
|
+
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
777
|
+
data = json.load(inFile)
|
778
|
+
for path, entry in data.items():
|
779
|
+
self._data[path] = {
|
780
|
+
"title": entry.get("title", ""),
|
781
|
+
"words": entry.get("words", 0),
|
782
|
+
"time": entry.get("time", 0),
|
783
|
+
}
|
784
|
+
except Exception:
|
785
|
+
logger.error("Could not load recent project cache")
|
786
|
+
logException()
|
787
|
+
return False
|
793
788
|
|
794
789
|
return True
|
795
790
|
|
novelwriter/constants.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2019-04-28 [0.0.1]
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -43,12 +43,12 @@ class nwConst:
|
|
43
43
|
FMT_DSTAMP = "%Y-%m-%d" # Date only format
|
44
44
|
|
45
45
|
# URLs
|
46
|
-
URL_WEB
|
47
|
-
URL_DOCS
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
URL_WEB = "https://novelwriter.io"
|
47
|
+
URL_DOCS = "https://docs.novelwriter.io"
|
48
|
+
URL_RELEASES = "https://releases.novelwriter.io"
|
49
|
+
URL_CODE = "https://github.com/vkbo/novelWriter"
|
50
|
+
URL_REPORT = "https://github.com/vkbo/novelWriter/issues"
|
51
|
+
URL_HELP = "https://github.com/vkbo/novelWriter/discussions"
|
52
52
|
|
53
53
|
# Requests
|
54
54
|
USER_AGENT = "Mozilla/5.0 (compatible; novelWriter (Python))"
|
@@ -56,6 +56,9 @@ class nwConst:
|
|
56
56
|
# Gui Settings
|
57
57
|
STATUS_MSG_TIMEOUT = 15000 # milliseconds
|
58
58
|
|
59
|
+
# Dialogs
|
60
|
+
DLG_FINISHED = 2
|
61
|
+
|
59
62
|
# END Class nwConst
|
60
63
|
|
61
64
|
|
@@ -107,7 +110,6 @@ class nwFiles:
|
|
107
110
|
|
108
111
|
# Project Root Files
|
109
112
|
PROJ_FILE = "nwProject.nwx"
|
110
|
-
PROJ_BACKUP = "nwProject.bak"
|
111
113
|
PROJ_LOCK = "nwProject.lock"
|
112
114
|
TOC_TXT = "ToC.txt"
|
113
115
|
|
@@ -184,6 +186,7 @@ class nwLabels:
|
|
184
186
|
nwItemClass.ENTITY: QT_TRANSLATE_NOOP("Constant", "Entities"),
|
185
187
|
nwItemClass.CUSTOM: QT_TRANSLATE_NOOP("Constant", "Custom"),
|
186
188
|
nwItemClass.ARCHIVE: QT_TRANSLATE_NOOP("Constant", "Archive"),
|
189
|
+
nwItemClass.TEMPLATE: QT_TRANSLATE_NOOP("Constant", "Templates"),
|
187
190
|
nwItemClass.TRASH: QT_TRANSLATE_NOOP("Constant", "Trash"),
|
188
191
|
}
|
189
192
|
CLASS_ICON = {
|
@@ -197,6 +200,7 @@ class nwLabels:
|
|
197
200
|
nwItemClass.ENTITY: "cls_entity",
|
198
201
|
nwItemClass.CUSTOM: "cls_custom",
|
199
202
|
nwItemClass.ARCHIVE: "cls_archive",
|
203
|
+
nwItemClass.TEMPLATE: "cls_template",
|
200
204
|
nwItemClass.TRASH: "cls_trash",
|
201
205
|
}
|
202
206
|
LAYOUT_NAME = {
|
@@ -266,6 +270,13 @@ class nwLabels:
|
|
266
270
|
nwBuildFmt.J_HTML: ".json",
|
267
271
|
nwBuildFmt.J_NWD: ".json",
|
268
272
|
}
|
273
|
+
FILE_FILTERS = {
|
274
|
+
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
|
275
|
+
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
|
276
|
+
"*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
|
277
|
+
"*.csv": QT_TRANSLATE_NOOP("Constant", "CSV files"),
|
278
|
+
"*": QT_TRANSLATE_NOOP("Constant", "All files"),
|
279
|
+
}
|
269
280
|
UNIT_NAME = {
|
270
281
|
"mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"),
|
271
282
|
"cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"),
|
@@ -298,16 +309,27 @@ class nwLabels:
|
|
298
309
|
|
299
310
|
class nwHeadFmt:
|
300
311
|
|
301
|
-
BR
|
302
|
-
TITLE
|
303
|
-
CH_NUM
|
304
|
-
CH_WORD
|
305
|
-
CH_ROMU
|
306
|
-
CH_ROML
|
307
|
-
SC_NUM
|
308
|
-
SC_ABS
|
312
|
+
BR = "{BR}"
|
313
|
+
TITLE = "{Title}"
|
314
|
+
CH_NUM = "{Chapter}"
|
315
|
+
CH_WORD = "{Chapter:Word}"
|
316
|
+
CH_ROMU = "{Chapter:URoman}"
|
317
|
+
CH_ROML = "{Chapter:LRoman}"
|
318
|
+
SC_NUM = "{Scene}"
|
319
|
+
SC_ABS = "{Scene:Abs}"
|
320
|
+
CHAR_POV = "{Char:POV}"
|
321
|
+
CHAR_FOCUS = "{Char:Focus}"
|
322
|
+
|
323
|
+
PAGE_HEADERS = [
|
324
|
+
TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS,
|
325
|
+
CHAR_POV, CHAR_FOCUS
|
326
|
+
]
|
309
327
|
|
310
|
-
|
328
|
+
# ODT Document Page Header
|
329
|
+
ODT_PROJECT = "{Project}"
|
330
|
+
ODT_AUTHOR = "{Author}"
|
331
|
+
ODT_PAGE = "{Page}"
|
332
|
+
ODT_AUTO = "{Project} / {Author} / {Page}"
|
311
333
|
|
312
334
|
# END Class nwHeadFmt
|
313
335
|
|