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.
Files changed (123) hide show
  1. {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/METADATA +4 -5
  2. {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/RECORD +122 -112
  3. {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +33 -39
  5. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  6. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  7. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  8. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  9. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  10. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  11. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  12. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  13. novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
  14. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  15. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  16. novelwriter/assets/i18n/project_en_GB.json +1 -0
  17. novelwriter/assets/i18n/project_pl_PL.json +116 -0
  18. novelwriter/assets/i18n/project_pt_BR.json +74 -74
  19. novelwriter/assets/icons/typicons_dark/icons.conf +2 -0
  20. novelwriter/assets/icons/typicons_dark/nw_font.svg +4 -0
  21. novelwriter/assets/icons/typicons_dark/nw_quote.svg +4 -0
  22. novelwriter/assets/icons/typicons_light/icons.conf +2 -0
  23. novelwriter/assets/icons/typicons_light/nw_font.svg +4 -0
  24. novelwriter/assets/icons/typicons_light/nw_quote.svg +4 -0
  25. novelwriter/assets/manual.pdf +0 -0
  26. novelwriter/assets/sample.zip +0 -0
  27. novelwriter/assets/syntax/cyberpunk_night.conf +5 -3
  28. novelwriter/assets/syntax/default_dark.conf +32 -18
  29. novelwriter/assets/syntax/default_light.conf +24 -10
  30. novelwriter/assets/syntax/dracula.conf +44 -0
  31. novelwriter/assets/syntax/grey_dark.conf +5 -4
  32. novelwriter/assets/syntax/grey_light.conf +5 -4
  33. novelwriter/assets/syntax/light_owl.conf +7 -6
  34. novelwriter/assets/syntax/night_owl.conf +7 -6
  35. novelwriter/assets/syntax/snazzy.conf +42 -0
  36. novelwriter/assets/syntax/solarized_dark.conf +4 -3
  37. novelwriter/assets/syntax/solarized_light.conf +4 -3
  38. novelwriter/assets/syntax/tango.conf +27 -11
  39. novelwriter/assets/syntax/tomorrow.conf +6 -5
  40. novelwriter/assets/syntax/tomorrow_night.conf +7 -6
  41. novelwriter/assets/syntax/tomorrow_night_blue.conf +6 -5
  42. novelwriter/assets/syntax/tomorrow_night_bright.conf +6 -5
  43. novelwriter/assets/syntax/tomorrow_night_eighties.conf +6 -5
  44. novelwriter/assets/text/credits_en.htm +52 -41
  45. novelwriter/assets/themes/cyberpunk_night.conf +3 -0
  46. novelwriter/assets/themes/default_dark.conf +2 -0
  47. novelwriter/assets/themes/default_light.conf +2 -0
  48. novelwriter/assets/themes/dracula.conf +48 -0
  49. novelwriter/assets/themes/solarized_dark.conf +2 -0
  50. novelwriter/assets/themes/solarized_light.conf +2 -0
  51. novelwriter/common.py +33 -12
  52. novelwriter/config.py +184 -98
  53. novelwriter/constants.py +47 -35
  54. novelwriter/core/buildsettings.py +68 -69
  55. novelwriter/core/coretools.py +5 -23
  56. novelwriter/core/docbuild.py +52 -40
  57. novelwriter/core/document.py +3 -5
  58. novelwriter/core/index.py +115 -45
  59. novelwriter/core/item.py +8 -19
  60. novelwriter/core/options.py +2 -4
  61. novelwriter/core/project.py +37 -61
  62. novelwriter/core/projectdata.py +1 -3
  63. novelwriter/core/projectxml.py +12 -15
  64. novelwriter/core/sessions.py +3 -5
  65. novelwriter/core/spellcheck.py +4 -9
  66. novelwriter/core/status.py +211 -164
  67. novelwriter/core/storage.py +0 -8
  68. novelwriter/core/tohtml.py +139 -105
  69. novelwriter/core/tokenizer.py +278 -122
  70. novelwriter/core/{tomd.py → tomarkdown.py} +97 -78
  71. novelwriter/core/toodt.py +257 -166
  72. novelwriter/core/toqdoc.py +419 -0
  73. novelwriter/core/tree.py +5 -7
  74. novelwriter/dialogs/about.py +11 -18
  75. novelwriter/dialogs/docmerge.py +17 -19
  76. novelwriter/dialogs/docsplit.py +17 -19
  77. novelwriter/dialogs/editlabel.py +6 -10
  78. novelwriter/dialogs/preferences.py +200 -164
  79. novelwriter/dialogs/projectsettings.py +225 -189
  80. novelwriter/dialogs/quotes.py +12 -9
  81. novelwriter/dialogs/wordlist.py +9 -15
  82. novelwriter/enum.py +35 -30
  83. novelwriter/error.py +8 -15
  84. novelwriter/extensions/configlayout.py +55 -21
  85. novelwriter/extensions/eventfilters.py +1 -5
  86. novelwriter/extensions/modified.py +70 -14
  87. novelwriter/extensions/novelselector.py +1 -3
  88. novelwriter/extensions/pagedsidebar.py +9 -12
  89. novelwriter/extensions/{circularprogress.py → progressbars.py} +30 -8
  90. novelwriter/extensions/statusled.py +40 -26
  91. novelwriter/extensions/switch.py +4 -6
  92. novelwriter/extensions/switchbox.py +7 -6
  93. novelwriter/extensions/versioninfo.py +3 -9
  94. novelwriter/gui/doceditor.py +120 -139
  95. novelwriter/gui/dochighlight.py +231 -186
  96. novelwriter/gui/docviewer.py +69 -108
  97. novelwriter/gui/docviewerpanel.py +3 -10
  98. novelwriter/gui/editordocument.py +1 -3
  99. novelwriter/gui/itemdetails.py +7 -11
  100. novelwriter/gui/mainmenu.py +22 -18
  101. novelwriter/gui/noveltree.py +11 -24
  102. novelwriter/gui/outline.py +15 -26
  103. novelwriter/gui/projtree.py +39 -65
  104. novelwriter/gui/search.py +10 -3
  105. novelwriter/gui/sidebar.py +2 -6
  106. novelwriter/gui/statusbar.py +29 -37
  107. novelwriter/gui/theme.py +26 -48
  108. novelwriter/guimain.py +162 -160
  109. novelwriter/shared.py +36 -19
  110. novelwriter/text/patterns.py +113 -0
  111. novelwriter/tools/dictionaries.py +10 -20
  112. novelwriter/tools/lipsum.py +10 -16
  113. novelwriter/tools/manusbuild.py +9 -11
  114. novelwriter/tools/manuscript.py +75 -149
  115. novelwriter/tools/manussettings.py +74 -76
  116. novelwriter/tools/noveldetails.py +16 -21
  117. novelwriter/tools/welcome.py +21 -26
  118. novelwriter/tools/writingstats.py +9 -12
  119. novelwriter/types.py +49 -4
  120. novelwriter/extensions/simpleprogress.py +0 -55
  121. {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/LICENSE.md +0 -0
  122. {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/entry_points.txt +0 -0
  123. {novelWriter-2.4.3.dist-info → novelWriter-2.5.dist-info}/top_level.txt +0 -0
@@ -27,14 +27,14 @@ from __future__ import annotations
27
27
  import json
28
28
  import logging
29
29
 
30
- from typing import TYPE_CHECKING
31
- from pathlib import Path
32
30
  from collections.abc import Iterator
31
+ from pathlib import Path
32
+ from typing import TYPE_CHECKING
33
33
 
34
34
  from PyQt5.QtCore import QLocale
35
35
 
36
- from novelwriter.error import logException
37
36
  from novelwriter.constants import nwFiles
37
+ from novelwriter.error import logException
38
38
 
39
39
  if TYPE_CHECKING: # pragma: no cover
40
40
  from novelwriter.core.project import NWProject
@@ -164,11 +164,10 @@ class NWSpellEnchant:
164
164
  name = ""
165
165
  return tag, name
166
166
 
167
- # END Class NWSpellEnchant
168
-
169
167
 
170
168
  class FakeEnchant:
171
169
  """Fallback for when Enchant is selected, but not installed."""
170
+
172
171
  def __init__(self) -> None:
173
172
 
174
173
  class FakeProvider:
@@ -188,8 +187,6 @@ class FakeEnchant:
188
187
  def add_to_session(self, word: str) -> None:
189
188
  return
190
189
 
191
- # END Class FakeEnchant
192
-
193
190
 
194
191
  class UserDictionary:
195
192
 
@@ -240,5 +237,3 @@ class UserDictionary:
240
237
  logger.error("Failed to save user dictionary")
241
238
  logException()
242
239
  return
243
-
244
- # END Class UserDictionary
@@ -24,17 +24,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
24
24
  """
25
25
  from __future__ import annotations
26
26
 
27
- import random
27
+ import dataclasses
28
28
  import logging
29
+ import random
29
30
 
31
+ from collections.abc import Iterable
30
32
  from typing import TYPE_CHECKING, Literal
31
- from collections.abc import ItemsView, Iterable, Iterator, KeysView, ValuesView
32
33
 
33
- from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
34
- from PyQt5.QtCore import QRectF
34
+ from PyQt5.QtCore import QPointF, Qt
35
+ from PyQt5.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygonF
35
36
 
36
- from novelwriter import CONFIG
37
- from novelwriter.common import minmax, simplified
37
+ from novelwriter import SHARED
38
+ from novelwriter.common import simplified
39
+ from novelwriter.enum import nwStatusShape
38
40
  from novelwriter.types import QtPaintAnitAlias, QtTransparent
39
41
 
40
42
  if TYPE_CHECKING: # pragma: no cover
@@ -43,83 +45,92 @@ if TYPE_CHECKING: # pragma: no cover
43
45
  logger = logging.getLogger(__name__)
44
46
 
45
47
 
46
- class NWStatus:
48
+ @dataclasses.dataclass
49
+ class StatusEntry:
47
50
 
48
- STATUS = 1
49
- IMPORT = 2
51
+ name: str
52
+ color: QColor
53
+ shape: nwStatusShape
54
+ icon: QIcon
55
+ count: int = 0
50
56
 
51
- def __init__(self, kind: Literal[1, 2]) -> None:
57
+ @classmethod
58
+ def duplicate(cls, source: StatusEntry) -> StatusEntry:
59
+ """Create a deep copy of the source object."""
60
+ cls = dataclasses.replace(source)
61
+ cls.color = QColor(source.color)
62
+ cls.icon = QIcon(source.icon)
63
+ return cls
52
64
 
53
- self._type = kind
54
- self._store = {}
55
- self._default = None
56
65
 
57
- self._iPX = CONFIG.pxInt(24)
66
+ NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
67
+
58
68
 
59
- pA = CONFIG.pxInt(2)
60
- pB = CONFIG.pxInt(20)
61
- pR = float(CONFIG.pxInt(4))
62
- self._iconPath = QPainterPath()
63
- self._iconPath.addRoundedRect(QRectF(pA, pA, pB, pB), pR, pR)
69
+ class NWStatus:
64
70
 
65
- self._defaultIcon = self._createIcon(100, 100, 100)
71
+ STATUS = "s"
72
+ IMPORT = "i"
66
73
 
67
- if self._type == self.STATUS:
68
- self._prefix = "s"
69
- elif self._type == self.IMPORT:
70
- self._prefix = "i"
71
- else:
72
- raise Exception("This is a bug!")
74
+ __slots__ = ("_store", "_default", "_prefix", "_height")
73
75
 
76
+ def __init__(self, prefix: Literal["s", "i"]) -> None:
77
+ self._store: dict[str, StatusEntry] = {}
78
+ self._default = None
79
+ self._prefix = prefix[:1]
80
+ self._height = SHARED.theme.baseIconHeight
74
81
  return
75
82
 
76
- def write(self, key: str | None, name: str, col: tuple, count: int | None = None) -> str:
83
+ def __len__(self) -> int:
84
+ return len(self._store)
85
+
86
+ def __getitem__(self, key: str | None) -> StatusEntry:
87
+ """Return the entry associated with a given key."""
88
+ if key and key in self._store:
89
+ return self._store[key]
90
+ elif self._default is not None:
91
+ return self._store[self._default]
92
+ return NO_ENTRY
93
+
94
+ ##
95
+ # Methods
96
+ ##
97
+
98
+ def add(self, key: str | None, name: str, color: tuple[int, int, int],
99
+ shape: str, count: int) -> str:
77
100
  """Add or update a status entry. If the key is invalid, a new
78
101
  key is generated.
79
102
  """
80
- if not self._isKey(key):
81
- key = self._newKey()
82
- if not isinstance(col, tuple):
83
- col = (100, 100, 100)
84
- if len(col) != 3:
85
- col = (100, 100, 100)
86
-
87
- cR = minmax(col[0], 0, 255)
88
- cG = minmax(col[1], 0, 255)
89
- cB = minmax(col[2], 0, 255)
90
- name = simplified(name)
91
- if count is None:
92
- count = self._store.get(key, {}).get("count", 0)
103
+ if isinstance(color, tuple) and len(color) == 3:
104
+ qColor = QColor(*color)
105
+ else:
106
+ qColor = QColor(100, 100, 100)
107
+
108
+ try:
109
+ iShape = nwStatusShape[shape]
110
+ except KeyError:
111
+ iShape = nwStatusShape.SQUARE
93
112
 
94
- self._store[key] = {
95
- "name": name,
96
- "icon": self._createIcon(cR, cG, cB),
97
- "cols": (cR, cG, cB),
98
- "count": count,
99
- }
113
+ key = self._checkKey(key)
114
+ name = simplified(name)
115
+ icon = self.createIcon(self._height, qColor, iShape)
116
+ self._store[key] = StatusEntry(name, qColor, iShape, icon, count)
100
117
 
101
118
  if self._default is None:
102
119
  self._default = key
103
120
 
104
121
  return key
105
122
 
106
- def remove(self, key: str) -> bool:
107
- """Remove an entry in the list, except if the count > 0."""
108
- if key not in self._store:
109
- return False
110
- if self._store[key]["count"] > 0:
111
- return False
112
-
113
- del self._store[key]
123
+ def update(self, update: list[tuple[str | None, StatusEntry]]) -> None:
124
+ """Update the list of statuses, and from removed list."""
125
+ self._store.clear()
126
+ for key, entry in update:
127
+ self._store[self._checkKey(key)] = entry
114
128
 
115
- keys = list(self._store.keys())
116
- if key == self._default:
117
- if len(keys) > 0:
118
- self._default = keys[0]
119
- else:
120
- self._default = None
129
+ # Check if we need a new default
130
+ if self._default not in self._store:
131
+ self._default = next(iter(self._store)) if self._store else None
121
132
 
122
- return True
133
+ return
123
134
 
124
135
  def check(self, value: str) -> str:
125
136
  """Check the key against the stored status names."""
@@ -129,93 +140,51 @@ class NWStatus:
129
140
  return self._default
130
141
  return ""
131
142
 
132
- def name(self, key: str | None) -> str:
133
- """Return the name associated with a given key."""
134
- if key and key in self._store:
135
- return self._store[key]["name"]
136
- elif self._default is not None:
137
- return self._store[self._default]["name"]
138
- return ""
139
-
140
- def cols(self, key: str | None) -> tuple[int, int, int]:
141
- """Return the colours associated with a given key."""
142
- if key and key in self._store:
143
- return self._store[key]["cols"]
144
- elif self._default is not None:
145
- return self._store[self._default]["cols"]
146
- return 100, 100, 100
147
-
148
- def count(self, key: str | None) -> int:
149
- """Return the count associated with a given key."""
150
- if key and key in self._store:
151
- return self._store[key]["count"]
152
- elif self._default is not None:
153
- return self._store[self._default]["count"]
154
- return 0
155
-
156
- def icon(self, key: str | None) -> QIcon:
157
- """Return the icon associated with a given key."""
158
- if key and key in self._store:
159
- return self._store[key]["icon"]
160
- elif self._default is not None:
161
- return self._store[self._default]["icon"]
162
- return self._defaultIcon
163
-
164
- def reorder(self, order: list[str]) -> bool:
165
- """Reorder the items according to list."""
166
- if len(order) != len(self._store):
167
- logger.error("Length mismatch between new and old order")
168
- return False
169
-
170
- if order == list(self._store.keys()):
171
- return False
172
-
173
- store = {}
174
- for key in order:
175
- if key in self._store:
176
- store[key] = self._store[key]
177
- else:
178
- logger.error("Unknown key '%s' in order", key)
179
- return False
180
-
181
- self._store = store
182
-
183
- return True
184
-
185
143
  def resetCounts(self) -> None:
186
144
  """Clear the counts of references to the status entries."""
187
- for key in self._store:
188
- self._store[key]["count"] = 0
145
+ for entry in self._store.values():
146
+ entry.count = 0
189
147
  return
190
148
 
191
149
  def increment(self, key: str | None) -> None:
192
150
  """Increment the counter for a given entry."""
193
151
  if key and key in self._store:
194
- self._store[key]["count"] += 1
152
+ self._store[key].count += 1
195
153
  return
196
154
 
197
155
  def pack(self) -> Iterable[tuple[str, dict]]:
198
156
  """Pack the status entries into a dictionary."""
199
- for key, data in self._store.items():
200
- yield (data["name"], {
157
+ for key, entry in self._store.items():
158
+ yield (entry.name, {
201
159
  "key": key,
202
- "count": str(data["count"]),
203
- "red": str(data["cols"][0]),
204
- "green": str(data["cols"][1]),
205
- "blue": str(data["cols"][2]),
160
+ "count": str(entry.count),
161
+ "red": str(entry.color.red()),
162
+ "green": str(entry.color.green()),
163
+ "blue": str(entry.color.blue()),
164
+ "shape": entry.shape.name,
206
165
  })
207
166
  return
208
167
 
209
- def unpack(self, data: dict) -> None:
210
- """Unpack a data dictionary and set the class values."""
211
- self._store = {}
212
- self._default = None
213
- for key, entry in data.items():
214
- label = entry.get("label", "")
215
- colour = entry.get("colour", (100, 100, 100))
216
- count = entry.get("count", 0)
217
- self.write(key, label, colour, count)
218
- return
168
+ def iterItems(self) -> Iterable[tuple[str, StatusEntry]]:
169
+ """Yield entries from the status icons."""
170
+ yield from self._store.items()
171
+
172
+ @staticmethod
173
+ def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
174
+ """Generate an icon for a status label."""
175
+ pixmap = QPixmap(48, 48)
176
+ pixmap.fill(QtTransparent)
177
+
178
+ painter = QPainter(pixmap)
179
+ painter.setRenderHint(QtPaintAnitAlias)
180
+ painter.fillPath(_SHAPES.getShape(shape), color)
181
+ painter.end()
182
+
183
+ return QIcon(pixmap.scaled(
184
+ height, height,
185
+ Qt.AspectRatioMode.IgnoreAspectRatio,
186
+ Qt.TransformationMode.SmoothTransformation
187
+ ))
219
188
 
220
189
  ##
221
190
  # Internal Functions
@@ -246,38 +215,116 @@ class NWStatus:
246
215
  return False
247
216
  return True
248
217
 
249
- def _createIcon(self, red: int, green: int, blue: int) -> QIcon:
250
- """Generate an icon for a status label."""
251
- pixmap = QPixmap(self._iPX, self._iPX)
252
- pixmap.fill(QtTransparent)
253
-
254
- painter = QPainter(pixmap)
255
- painter.setRenderHint(QtPaintAnitAlias)
256
- painter.fillPath(self._iconPath, QColor(red, green, blue))
257
- painter.end()
258
-
259
- return QIcon(pixmap)
218
+ def _checkKey(self, key: str | None) -> str:
219
+ """Check key is valid, and if not, generate one."""
220
+ return key if self._isKey(key) else self._newKey()
260
221
 
261
- ##
262
- # Iterator Bits
263
- ##
264
-
265
- def __len__(self) -> int:
266
- return len(self._store)
267
222
 
268
- def __getitem__(self, key: str) -> dict:
269
- return self._store[key]
223
+ class _ShapeCache:
270
224
 
271
- def __iter__(self) -> Iterator[dict]:
272
- return iter(self._store)
273
-
274
- def keys(self) -> KeysView[str]:
275
- return self._store.keys()
276
-
277
- def items(self) -> ItemsView[str, dict]:
278
- return self._store.items()
279
-
280
- def values(self) -> ValuesView[dict]:
281
- return self._store.values()
225
+ def __init__(self) -> None:
226
+ self._cache: dict[nwStatusShape, QPainterPath] = {}
227
+ return
282
228
 
283
- # END Class NWStatus
229
+ def getShape(self, shape: nwStatusShape) -> QPainterPath:
230
+ """Return a painter shape for an icon."""
231
+ if shape in self._cache:
232
+ return self._cache[shape]
233
+
234
+ path = QPainterPath()
235
+ if shape == nwStatusShape.SQUARE:
236
+ path.addRoundedRect(2.0, 2.0, 44.0, 44.0, 4.0, 4.0)
237
+ elif shape == nwStatusShape.TRIANGLE:
238
+ path.addPolygon(QPolygonF([
239
+ QPointF(24.00, 3.00),
240
+ QPointF(43.92, 37.50),
241
+ QPointF(4.08, 37.50),
242
+ ]))
243
+ elif shape == nwStatusShape.NABLA:
244
+ path.addPolygon(QPolygonF([
245
+ QPointF(24.00, 48.00),
246
+ QPointF(4.08, 14.50),
247
+ QPointF(43.92, 14.50),
248
+ ]))
249
+ elif shape == nwStatusShape.DIAMOND:
250
+ path.addPolygon(QPolygonF([
251
+ QPointF(24.00, 2.00),
252
+ QPointF(44.00, 24.00),
253
+ QPointF(24.00, 46.00),
254
+ QPointF(4.00, 24.00),
255
+ ]))
256
+ elif shape == nwStatusShape.PENTAGON:
257
+ path.addPolygon(QPolygonF([
258
+ QPointF(24.00, 1.50),
259
+ QPointF(45.87, 17.39),
260
+ QPointF(37.52, 43.11),
261
+ QPointF(10.48, 43.11),
262
+ QPointF(2.13, 17.39),
263
+ ]))
264
+ elif shape == nwStatusShape.HEXAGON:
265
+ path.addPolygon(QPolygonF([
266
+ QPointF(24.00, 1.50),
267
+ QPointF(43.92, 13.00),
268
+ QPointF(43.92, 36.00),
269
+ QPointF(24.00, 47.50),
270
+ QPointF(4.08, 36.00),
271
+ QPointF(4.08, 13.00),
272
+ ]))
273
+ elif shape == nwStatusShape.STAR:
274
+ path.addPolygon(QPolygonF([
275
+ QPointF(24.00, 0.50), QPointF(31.05, 14.79),
276
+ QPointF(46.83, 17.08), QPointF(35.41, 28.21),
277
+ QPointF(38.11, 43.92), QPointF(24.00, 36.50),
278
+ QPointF(9.89, 43.92), QPointF(12.59, 28.21),
279
+ QPointF(1.17, 17.08), QPointF(15.37, 16.16),
280
+ ]))
281
+ elif shape == nwStatusShape.PACMAN:
282
+ path.moveTo(24.0, 24.0)
283
+ path.arcTo(2.0, 2.0, 44.0, 44.0, 40.0, 280.0)
284
+ elif shape == nwStatusShape.CIRCLE_Q:
285
+ path.moveTo(24.0, 24.0)
286
+ path.arcTo(2.0, 2.0, 44.0, 44.0, 0.0, 90.0)
287
+ elif shape == nwStatusShape.CIRCLE_H:
288
+ path.moveTo(24.0, 24.0)
289
+ path.arcTo(2.0, 2.0, 44.0, 44.0, -90.0, 180.0)
290
+ elif shape == nwStatusShape.CIRCLE_T:
291
+ path.moveTo(24.0, 24.0)
292
+ path.arcTo(2.0, 2.0, 44.0, 44.0, -180.0, 270.0)
293
+ elif shape == nwStatusShape.CIRCLE:
294
+ path.addEllipse(2.0, 2.0, 44.0, 44.0)
295
+ elif shape == nwStatusShape.BARS_1:
296
+ path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
297
+ elif shape == nwStatusShape.BARS_2:
298
+ path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
299
+ path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
300
+ elif shape == nwStatusShape.BARS_3:
301
+ path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
302
+ path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
303
+ path.addRoundedRect(26.0, 2.0, 8.0, 44.0, 4.0, 4.0)
304
+ elif shape == nwStatusShape.BARS_4:
305
+ path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
306
+ path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
307
+ path.addRoundedRect(26.0, 2.0, 8.0, 44.0, 4.0, 4.0)
308
+ path.addRoundedRect(38.0, 2.0, 8.0, 44.0, 4.0, 4.0)
309
+ elif shape == nwStatusShape.BLOCK_1:
310
+ path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
311
+ elif shape == nwStatusShape.BLOCK_2:
312
+ path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
313
+ path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
314
+ elif shape == nwStatusShape.BLOCK_3:
315
+ path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
316
+ path.addRoundedRect(2.0, 24.0, 20.0, 20.0, 4.0, 4.0)
317
+ path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
318
+ elif shape == nwStatusShape.BLOCK_4:
319
+ path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
320
+ path.addRoundedRect(2.0, 24.0, 20.0, 20.0, 4.0, 4.0)
321
+ path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
322
+ path.addRoundedRect(24.0, 24.0, 20.0, 20.0, 4.0, 4.0)
323
+
324
+ self._cache[shape] = path
325
+
326
+ return path
327
+
328
+
329
+ # Create Singleton
330
+ _SHAPES = _ShapeCache()
@@ -54,8 +54,6 @@ class NWStorageOpen(Enum):
54
54
  FAILED = 3
55
55
  READY = 4
56
56
 
57
- # END Enum NWStorageOpen
58
-
59
57
 
60
58
  class NWStorageCreate(Enum):
61
59
 
@@ -63,8 +61,6 @@ class NWStorageCreate(Enum):
63
61
  OS_ERROR = 1
64
62
  READY = 2
65
63
 
66
- # END Enum NWStorageCreate
67
-
68
64
 
69
65
  class NWStorage:
70
66
  """Core: Project Storage Class
@@ -391,8 +387,6 @@ class NWStorage:
391
387
  self._lockedBy = None
392
388
  return True
393
389
 
394
- # END Class NWStorage
395
-
396
390
 
397
391
  class _LegacyStorage:
398
392
  """Core: Legacy Storage Converter Utils
@@ -590,5 +584,3 @@ class _LegacyStorage:
590
584
  logException()
591
585
 
592
586
  return
593
-
594
- # END Class _LegacyStorage