novelWriter 2.2rc1__py3-none-any.whl → 2.3__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.
Files changed (162) hide show
  1. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/METADATA +1 -1
  2. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/RECORD +149 -132
  3. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/WHEEL +1 -1
  4. novelWriter-2.3.dist-info/entry_points.txt +2 -0
  5. novelwriter/__init__.py +11 -6
  6. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  7. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  8. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  9. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  10. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  11. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  12. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  13. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  14. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  15. novelwriter/assets/i18n/project_de_DE.json +1 -0
  16. novelwriter/assets/i18n/project_en_US.json +1 -0
  17. novelwriter/assets/i18n/project_es_419.json +11 -0
  18. novelwriter/assets/i18n/project_fr_FR.json +11 -0
  19. novelwriter/assets/i18n/project_it_IT.json +11 -0
  20. novelwriter/assets/i18n/project_ja_JP.json +2 -1
  21. novelwriter/assets/i18n/project_nb_NO.json +1 -0
  22. novelwriter/assets/i18n/project_nl_NL.json +11 -0
  23. novelwriter/assets/i18n/project_pt_BR.json +11 -0
  24. novelwriter/assets/i18n/project_zh_CN.json +11 -0
  25. novelwriter/assets/icons/typicons_dark/icons.conf +11 -2
  26. novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +6 -0
  27. novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
  28. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
  29. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
  30. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
  31. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
  32. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
  33. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
  34. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
  35. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
  36. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
  37. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
  38. novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
  39. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
  40. novelwriter/assets/icons/typicons_dark/typ_th-list.svg +9 -0
  41. novelwriter/assets/icons/typicons_light/icons.conf +11 -2
  42. novelwriter/assets/icons/typicons_light/mixed_document-new.svg +6 -0
  43. novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
  44. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
  45. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
  46. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
  47. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
  48. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
  49. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
  50. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
  51. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
  52. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
  53. novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
  54. novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
  55. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
  56. novelwriter/assets/icons/typicons_light/typ_th-list.svg +9 -0
  57. novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
  58. novelwriter/assets/images/novelwriter-text-light.svg +4 -0
  59. novelwriter/assets/images/welcome-dark.jpg +0 -0
  60. novelwriter/assets/images/welcome-light.jpg +0 -0
  61. novelwriter/assets/manual.pdf +0 -0
  62. novelwriter/assets/sample.zip +0 -0
  63. novelwriter/assets/syntax/cyberpunk_night.conf +26 -0
  64. novelwriter/assets/syntax/default_dark.conf +1 -0
  65. novelwriter/assets/syntax/default_light.conf +1 -0
  66. novelwriter/assets/syntax/grey_dark.conf +1 -0
  67. novelwriter/assets/syntax/grey_light.conf +1 -0
  68. novelwriter/assets/syntax/light_owl.conf +1 -0
  69. novelwriter/assets/syntax/night_owl.conf +1 -0
  70. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  71. novelwriter/assets/syntax/solarized_light.conf +1 -0
  72. novelwriter/assets/syntax/tango.conf +23 -0
  73. novelwriter/assets/syntax/tomorrow.conf +1 -0
  74. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  75. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  76. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  77. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  78. novelwriter/assets/text/credits_en.htm +4 -2
  79. novelwriter/assets/themes/cyberpunk_night.conf +29 -0
  80. novelwriter/assets/themes/default_dark.conf +2 -2
  81. novelwriter/assets/themes/default_light.conf +2 -2
  82. novelwriter/common.py +64 -66
  83. novelwriter/config.py +39 -44
  84. novelwriter/constants.py +39 -17
  85. novelwriter/core/buildsettings.py +8 -8
  86. novelwriter/core/coretools.py +198 -157
  87. novelwriter/core/docbuild.py +7 -4
  88. novelwriter/core/document.py +7 -7
  89. novelwriter/core/index.py +90 -57
  90. novelwriter/core/item.py +23 -5
  91. novelwriter/core/options.py +11 -10
  92. novelwriter/core/project.py +73 -47
  93. novelwriter/core/projectdata.py +3 -16
  94. novelwriter/core/projectxml.py +14 -42
  95. novelwriter/core/sessions.py +4 -3
  96. novelwriter/core/spellcheck.py +6 -4
  97. novelwriter/core/status.py +5 -4
  98. novelwriter/core/storage.py +183 -141
  99. novelwriter/core/tohtml.py +6 -4
  100. novelwriter/core/tokenizer.py +110 -83
  101. novelwriter/core/tomd.py +2 -2
  102. novelwriter/core/toodt.py +41 -31
  103. novelwriter/core/tree.py +5 -4
  104. novelwriter/dialogs/about.py +88 -179
  105. novelwriter/dialogs/docmerge.py +30 -20
  106. novelwriter/dialogs/docsplit.py +33 -22
  107. novelwriter/dialogs/editlabel.py +20 -8
  108. novelwriter/dialogs/preferences.py +562 -725
  109. novelwriter/dialogs/{projsettings.py → projectsettings.py} +301 -270
  110. novelwriter/dialogs/quotes.py +47 -36
  111. novelwriter/dialogs/wordlist.py +128 -59
  112. novelwriter/enum.py +25 -22
  113. novelwriter/error.py +2 -2
  114. novelwriter/extensions/circularprogress.py +12 -12
  115. novelwriter/extensions/configlayout.py +185 -146
  116. novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
  117. novelwriter/extensions/modified.py +81 -0
  118. novelwriter/extensions/novelselector.py +27 -13
  119. novelwriter/extensions/pagedsidebar.py +15 -20
  120. novelwriter/extensions/simpleprogress.py +8 -9
  121. novelwriter/extensions/statusled.py +9 -9
  122. novelwriter/extensions/switch.py +32 -64
  123. novelwriter/extensions/switchbox.py +2 -7
  124. novelwriter/extensions/versioninfo.py +153 -0
  125. novelwriter/gui/doceditor.py +250 -214
  126. novelwriter/gui/dochighlight.py +66 -94
  127. novelwriter/gui/docviewer.py +71 -98
  128. novelwriter/gui/docviewerpanel.py +140 -47
  129. novelwriter/gui/editordocument.py +3 -3
  130. novelwriter/gui/itemdetails.py +9 -9
  131. novelwriter/gui/mainmenu.py +47 -47
  132. novelwriter/gui/noveltree.py +53 -61
  133. novelwriter/gui/outline.py +100 -76
  134. novelwriter/gui/projtree.py +246 -112
  135. novelwriter/gui/sidebar.py +9 -8
  136. novelwriter/gui/statusbar.py +49 -7
  137. novelwriter/gui/theme.py +74 -76
  138. novelwriter/guimain.py +175 -330
  139. novelwriter/shared.py +68 -30
  140. novelwriter/tools/dictionaries.py +7 -8
  141. novelwriter/tools/lipsum.py +34 -28
  142. novelwriter/tools/manusbuild.py +3 -4
  143. novelwriter/tools/manuscript.py +25 -32
  144. novelwriter/tools/manussettings.py +194 -225
  145. novelwriter/tools/noveldetails.py +525 -0
  146. novelwriter/tools/welcome.py +819 -0
  147. novelwriter/tools/writingstats.py +26 -13
  148. novelWriter-2.2rc1.dist-info/entry_points.txt +0 -5
  149. novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
  150. novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
  151. novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
  152. novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
  153. novelwriter/assets/images/wizard-back.jpg +0 -0
  154. novelwriter/assets/text/gplv3_en.htm +0 -641
  155. novelwriter/assets/text/release_notes.htm +0 -17
  156. novelwriter/dialogs/projdetails.py +0 -525
  157. novelwriter/dialogs/projload.py +0 -298
  158. novelwriter/dialogs/updates.py +0 -182
  159. novelwriter/extensions/pageddialog.py +0 -130
  160. novelwriter/tools/projwizard.py +0 -478
  161. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/LICENSE.md +0 -0
  162. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,26 @@
1
+ [Main]
2
+ name = Cyberpunk Night
3
+ author = Anders Lemvigh
4
+ url = https://github.com/alemvigh
5
+ license = CC BY-SA 4.0
6
+ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
7
+
8
+ [Syntax]
9
+ background = 0, 0, 0
10
+ text = 150, 150, 150
11
+ link = 77, 077, 255
12
+ headertext = 255, 255, 255
13
+ headertag = 50, 0, 180
14
+ emphasis = 0, 255, 255
15
+ straightquotes = 7, 27, 219
16
+ doublequotes = 0, 255, 0
17
+ singlequotes = 0, 140, 255
18
+ hidden = 77, 77, 100
19
+ shortcode = 255, 255, 0
20
+ keyword = 255, 100, 255
21
+ value = 255, 150, 10
22
+ spellcheckline = 242, 72, 23
23
+ errorline = 186, 218, 4
24
+ replacetag = 0, 0, 180
25
+ modifier = 144, 142, 176
26
+ optional = 180, 180, 180
@@ -20,6 +20,7 @@ hidden = 150, 150, 150
20
20
  shortcode = 0, 155, 200
21
21
  keyword = 200, 46, 0
22
22
  value = 184, 200, 0
23
+ optional = 0, 155, 200
23
24
  spellcheckline = 200, 46, 0
24
25
  errorline = 46, 200, 0
25
26
  replacetag = 0, 184, 46
@@ -20,6 +20,7 @@ hidden = 100, 100, 100
20
20
  shortcode = 0, 100, 0
21
21
  keyword = 200, 50, 50
22
22
  value = 50, 150, 50
23
+ optional = 0, 100, 0
23
24
  spellcheckline = 200, 0, 0
24
25
  errorline = 0, 150, 0
25
26
  replacetag = 0, 150, 0
@@ -20,6 +20,7 @@ hidden = 150, 150, 150
20
20
  shortcode = 225, 225, 225
21
21
  keyword = 225, 225, 225
22
22
  value = 200, 200, 200
23
+ optional = 225, 225, 225
23
24
  spellcheckline = 200, 46, 0
24
25
  errorline = 46, 200, 0
25
26
  replacetag = 225, 225, 225
@@ -20,6 +20,7 @@ hidden = 100, 100, 100
20
20
  shortcode = 0, 0, 0
21
21
  keyword = 0, 0, 0
22
22
  value = 20, 20, 20
23
+ optional = 0, 0, 0
23
24
  spellcheckline = 200, 0, 0
24
25
  errorline = 0, 150, 0
25
26
  replacetag = 0, 0, 0
@@ -40,6 +40,7 @@ hidden = 152, 159, 177
40
40
  shortcode = 40, 142, 215
41
41
  keyword = 222, 61, 58
42
42
  value = 150, 74, 193
43
+ optional = 40, 142, 215
43
44
  spellcheckline = 222, 61, 58
44
45
  errorline = 8, 145, 106
45
46
  replacetag = 42, 162, 152
@@ -40,6 +40,7 @@ hidden = 99, 119, 119
40
40
  shortcode = 130, 170, 255
41
41
  keyword = 247, 140, 108
42
42
  value = 199, 146, 234
43
+ optional = 130, 170, 255
43
44
  spellcheckline = 247, 140, 108
44
45
  errorline = 173, 219, 103
45
46
  replacetag = 127, 219, 202
@@ -20,6 +20,7 @@ hidden = 147, 161, 161
20
20
  shortcode = 147, 161, 161
21
21
  keyword = 133, 153, 0
22
22
  value = 203, 75, 22
23
+ optional = 147, 161, 161
23
24
  spellcheckline = 203, 75, 22
24
25
  errorline = 220, 50, 47
25
26
  replacetag = 133, 153, 0
@@ -20,6 +20,7 @@ hidden = 88, 110, 117
20
20
  shortcode = 88, 110, 117
21
21
  keyword = 133, 153, 0
22
22
  value = 203, 75, 22
23
+ optional = 88, 110, 117
23
24
  spellcheckline = 203, 75, 22
24
25
  errorline = 220, 50, 47
25
26
  replacetag = 133, 153, 0
@@ -0,0 +1,23 @@
1
+ [Main]
2
+ name = Tango
3
+ author = Veronica Berglyd Olsen (adaptation)
4
+
5
+ [Syntax]
6
+ background = 48, 48, 47
7
+ text = 238, 238, 236
8
+ link = 115, 159, 207
9
+ headertext = 115, 159, 207
10
+ headertag = 52, 101, 164
11
+ emphasis = 196, 160, 0
12
+ straightquotes = 239, 41, 41
13
+ doublequotes = 138, 226, 52
14
+ singlequotes = 252, 233, 79
15
+ hidden = 143, 143, 141
16
+ shortcode = 115, 159, 207
17
+ keyword = 239, 41, 41
18
+ value = 173, 127, 168
19
+ optional = 115, 159, 207
20
+ spellcheckline = 239, 41, 41
21
+ errorline = 138, 226, 52
22
+ replacetag = 51, 226, 226
23
+ modifier = 196, 160, 0
@@ -40,6 +40,7 @@ hidden = 142, 144, 140
40
40
  shortcode = 66, 113, 174
41
41
  keyword = 240, 40, 41
42
42
  value = 137, 89, 168
43
+ optional = 66, 113, 174
43
44
  spellcheckline = 240, 40, 41
44
45
  errorline = 113, 140, 0
45
46
  replacetag = 62, 153, 159
@@ -40,6 +40,7 @@ hidden = 150, 152, 150
40
40
  shortcode = 129, 162, 190
41
41
  keyword = 204, 102, 102
42
42
  value = 178, 148, 187
43
+ optional = 129, 162, 190
43
44
  spellcheckline = 204, 102, 102
44
45
  errorline = 181, 189, 104
45
46
  replacetag = 138, 190, 183
@@ -40,6 +40,7 @@ hidden = 114, 133, 183
40
40
  shortcode = 187, 218, 255
41
41
  keyword = 255, 157, 164
42
42
  value = 235, 187, 255
43
+ optional = 187, 218, 255
43
44
  spellcheckline = 255, 157, 164
44
45
  errorline = 209, 241, 169
45
46
  replacetag = 153, 255, 255
@@ -40,6 +40,7 @@ hidden = 150, 152, 150
40
40
  shortcode = 122, 166, 218
41
41
  keyword = 213, 78, 83
42
42
  value = 195, 151, 216
43
+ optional = 122, 166, 218
43
44
  spellcheckline = 213, 78, 83
44
45
  errorline = 185, 202, 74
45
46
  replacetag = 112, 192, 177
@@ -40,6 +40,7 @@ hidden = 153, 153, 153
40
40
  shortcode = 102, 153, 204
41
41
  keyword = 242, 119, 122
42
42
  value = 204, 153, 204
43
+ optional = 102, 153, 204
43
44
  spellcheckline = 242, 119, 122
44
45
  errorline = 153, 204, 153
45
46
  replacetag = 102, 204, 204
@@ -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
@@ -0,0 +1,29 @@
1
+ [Main]
2
+ name = Cyberpunk Night
3
+ description = A taste of the future 80s
4
+ author = Anders Lemvigh
5
+ url = https://github.com/alemvigh
6
+ license = CC BY-SA 4.0
7
+ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
8
+ icontheme = typicons_dark
9
+
10
+ [Palette]
11
+ window = 0, 0, 0
12
+ windowtext = 150, 150, 150
13
+ base = 0, 0, 0
14
+ alternatebase = 30, 20, 45
15
+ text = 150, 150, 150
16
+ tooltipbase = 40, 20, 70
17
+ tooltiptext = 255, 255, 255
18
+ button = 5, 0, 10
19
+ buttontext = 150, 150, 150
20
+ brighttext = 255, 255, 255
21
+ highlight = 50, 30, 80
22
+ highlightedtext = 255, 255, 255
23
+ link = 77, 77, 255
24
+ linkvisited = 50, 0, 80
25
+
26
+ [GUI]
27
+ statusnone = 50, 50, 50
28
+ statussaved = 77, 255, 77
29
+ statusunsaved = 255, 77, 77
@@ -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 = 44, 152, 247
25
- linkvisited = 44, 152, 247
24
+ link = 102, 153, 204
25
+ linkvisited = 102, 153, 204
26
26
 
27
27
  [GUI]
28
28
  helptext = 164, 164, 164
@@ -21,8 +21,8 @@ buttontext = 0, 0, 0
21
21
  brighttext = 255, 255, 255
22
22
  highlight = 48, 135, 198
23
23
  highlightedtext = 255, 255, 255
24
- link = 0, 84, 255
25
- linkvisited = 0, 84, 255
24
+ link = 66, 113, 174
25
+ linkvisited = 66, 113, 174
26
26
 
27
27
  [GUI]
28
28
  helptext = 92, 92, 92
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–2023, Veronica Berglyd Olsen
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) -> bool:
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) -> bool:
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: str) -> bool:
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: str) -> bool:
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: str) -> bool:
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
- """Make sure an integer is between min and max value (inclusive).
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
- theVal = float(value)
218
- if theVal > 1000.0:
209
+ fVal = float(value)
210
+ if fVal > 1000.0:
219
211
  for pF in ["k", "M", "G", "T", "P", "E"]:
220
- theVal /= 1000.0
221
- if theVal < 1000.0:
222
- if theVal < 10.0:
223
- return f"{theVal:4.2f}{nwUnicode.U_THSP}{pF}"
224
- elif theVal < 100.0:
225
- return f"{theVal:4.1f}{nwUnicode.U_THSP}{pF}"
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"{theVal:3.0f}{nwUnicode.U_THSP}{pF}"
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
- theResult = target
284
+ result = target
275
285
 
276
286
  if not isinstance(source, str) or not isinstance(target, str):
277
- return theResult
287
+ return result
278
288
  if len(target) < 1 or len(source) < 1:
279
- return theResult
289
+ return result
280
290
 
281
291
  if source.istitle():
282
- theResult = target.title()
292
+ result = target.title()
283
293
 
284
294
  if source.isupper():
285
- theResult = target.upper()
295
+ result = target.upper()
286
296
  elif source.islower():
287
- theResult = target.lower()
297
+ result = target.lower()
288
298
 
289
- return theResult
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