novelWriter 2.6b2__py3-none-any.whl → 2.6.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.
Files changed (107) hide show
  1. {novelWriter-2.6b2.dist-info → novelWriter-2.6.1.dist-info}/METADATA +2 -2
  2. {novelWriter-2.6b2.dist-info → novelWriter-2.6.1.dist-info}/RECORD +107 -106
  3. {novelWriter-2.6b2.dist-info → novelWriter-2.6.1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +4 -4
  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_ru_RU.qm +0 -0
  16. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  17. novelwriter/assets/i18n/project_de_DE.json +2 -0
  18. novelwriter/assets/i18n/project_en_US.json +2 -0
  19. novelwriter/assets/i18n/project_es_419.json +2 -0
  20. novelwriter/assets/i18n/project_fr_FR.json +3 -1
  21. novelwriter/assets/i18n/project_it_IT.json +2 -0
  22. novelwriter/assets/i18n/project_ja_JP.json +2 -0
  23. novelwriter/assets/i18n/project_nb_NO.json +2 -0
  24. novelwriter/assets/i18n/project_nl_NL.json +2 -0
  25. novelwriter/assets/i18n/project_pl_PL.json +2 -0
  26. novelwriter/assets/i18n/project_pt_BR.json +2 -0
  27. novelwriter/assets/i18n/project_zh_CN.json +2 -0
  28. novelwriter/assets/manual.pdf +0 -0
  29. novelwriter/assets/manual_fr_FR.pdf +0 -0
  30. novelwriter/assets/sample.zip +0 -0
  31. novelwriter/common.py +1 -1
  32. novelwriter/config.py +45 -20
  33. novelwriter/constants.py +40 -36
  34. novelwriter/core/buildsettings.py +8 -1
  35. novelwriter/core/coretools.py +1 -1
  36. novelwriter/core/docbuild.py +1 -1
  37. novelwriter/core/document.py +1 -1
  38. novelwriter/core/index.py +1 -1
  39. novelwriter/core/item.py +1 -1
  40. novelwriter/core/itemmodel.py +1 -1
  41. novelwriter/core/options.py +1 -1
  42. novelwriter/core/project.py +3 -7
  43. novelwriter/core/projectdata.py +8 -2
  44. novelwriter/core/projectxml.py +1 -1
  45. novelwriter/core/sessions.py +1 -1
  46. novelwriter/core/spellcheck.py +1 -1
  47. novelwriter/core/status.py +17 -3
  48. novelwriter/core/storage.py +1 -1
  49. novelwriter/core/tree.py +1 -1
  50. novelwriter/dialogs/about.py +1 -1
  51. novelwriter/dialogs/docmerge.py +1 -1
  52. novelwriter/dialogs/docsplit.py +1 -1
  53. novelwriter/dialogs/editlabel.py +1 -1
  54. novelwriter/dialogs/preferences.py +16 -7
  55. novelwriter/dialogs/projectsettings.py +146 -96
  56. novelwriter/dialogs/quotes.py +1 -1
  57. novelwriter/dialogs/wordlist.py +11 -10
  58. novelwriter/enum.py +1 -8
  59. novelwriter/error.py +2 -2
  60. novelwriter/extensions/configlayout.py +1 -1
  61. novelwriter/extensions/eventfilters.py +1 -1
  62. novelwriter/extensions/modified.py +17 -5
  63. novelwriter/extensions/novelselector.py +4 -3
  64. novelwriter/extensions/pagedsidebar.py +4 -4
  65. novelwriter/extensions/progressbars.py +4 -4
  66. novelwriter/extensions/statusled.py +8 -9
  67. novelwriter/extensions/switch.py +3 -3
  68. novelwriter/extensions/switchbox.py +1 -1
  69. novelwriter/extensions/versioninfo.py +1 -1
  70. novelwriter/formats/shared.py +13 -12
  71. novelwriter/formats/todocx.py +2 -2
  72. novelwriter/formats/tohtml.py +31 -28
  73. novelwriter/formats/tokenizer.py +19 -13
  74. novelwriter/formats/tomarkdown.py +4 -3
  75. novelwriter/formats/toodt.py +2 -2
  76. novelwriter/formats/toqdoc.py +3 -1
  77. novelwriter/formats/toraw.py +1 -1
  78. novelwriter/gui/doceditor.py +39 -21
  79. novelwriter/gui/dochighlight.py +1 -1
  80. novelwriter/gui/docviewer.py +2 -2
  81. novelwriter/gui/docviewerpanel.py +1 -1
  82. novelwriter/gui/editordocument.py +1 -1
  83. novelwriter/gui/itemdetails.py +4 -3
  84. novelwriter/gui/mainmenu.py +1 -1
  85. novelwriter/gui/noveltree.py +1 -1
  86. novelwriter/gui/outline.py +8 -9
  87. novelwriter/gui/projtree.py +20 -25
  88. novelwriter/gui/search.py +2 -9
  89. novelwriter/gui/sidebar.py +1 -1
  90. novelwriter/gui/statusbar.py +26 -10
  91. novelwriter/gui/theme.py +1 -1
  92. novelwriter/guimain.py +2 -6
  93. novelwriter/shared.py +1 -1
  94. novelwriter/text/counting.py +1 -1
  95. novelwriter/text/patterns.py +1 -1
  96. novelwriter/tools/dictionaries.py +3 -3
  97. novelwriter/tools/lipsum.py +1 -1
  98. novelwriter/tools/manusbuild.py +1 -1
  99. novelwriter/tools/manuscript.py +7 -7
  100. novelwriter/tools/manussettings.py +9 -9
  101. novelwriter/tools/noveldetails.py +1 -1
  102. novelwriter/tools/welcome.py +2 -2
  103. novelwriter/tools/writingstats.py +1 -1
  104. novelwriter/types.py +2 -2
  105. {novelWriter-2.6b2.dist-info → novelWriter-2.6.1.dist-info}/LICENSE.md +0 -0
  106. {novelWriter-2.6b2.dist-info → novelWriter-2.6.1.dist-info}/entry_points.txt +0 -0
  107. {novelWriter-2.6b2.dist-info → novelWriter-2.6.1.dist-info}/top_level.txt +0 -0
novelwriter/config.py CHANGED
@@ -8,7 +8,7 @@ Created: 2022-11-09 [2.0rc2] RecentProjects
8
8
  Created: 2024-06-16 [2.5rc1] RecentPaths
9
9
 
10
10
  This file is a part of novelWriter
11
- Copyright 2018–2024, Veronica Berglyd Olsen
11
+ Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
12
12
 
13
13
  This program is free software: you can redistribute it and/or modify
14
14
  it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@ import sys
32
32
  from datetime import datetime
33
33
  from pathlib import Path
34
34
  from time import time
35
+ from typing import TYPE_CHECKING
35
36
 
36
37
  from PyQt5.QtCore import (
37
38
  PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
@@ -47,6 +48,9 @@ from novelwriter.common import (
47
48
  from novelwriter.constants import nwFiles, nwUnicode
48
49
  from novelwriter.error import formatException, logException
49
50
 
51
+ if TYPE_CHECKING: # pragma: no cover
52
+ from novelwriter.core.projectdata import NWProjectData
53
+
50
54
  logger = logging.getLogger(__name__)
51
55
 
52
56
 
@@ -101,8 +105,11 @@ class Config:
101
105
  self._qtTrans = {}
102
106
 
103
107
  # PDF Manual
104
- pdfDocs = self._appPath / "assets" / "manual.pdf"
105
- self.pdfDocs = pdfDocs if pdfDocs.is_file() else None
108
+ self._manuals: dict[str, Path] = {}
109
+ if (assets := self._appPath / "assets").is_dir():
110
+ for item in assets.iterdir():
111
+ if item.is_file() and item.stem.startswith("manual") and item.suffix == ".pdf":
112
+ self._manuals[item.stem] = item
106
113
 
107
114
  # User Settings
108
115
  # =============
@@ -112,7 +119,7 @@ class Config:
112
119
 
113
120
  # General GUI Settings
114
121
  self.guiLocale = self._qLocale.name()
115
- self.guiTheme = "default" # GUI theme
122
+ self.guiTheme = "default_light" # GUI theme
116
123
  self.guiSyntax = "default_light" # Syntax theme
117
124
  self.guiFont = QFont() # Main GUI font
118
125
  self.guiScale = 1.0 # Set automatically by Theme class
@@ -135,6 +142,7 @@ class Config:
135
142
  self.emphLabels = True # Add emphasis to H1 and H2 item labels
136
143
  self.backupOnClose = False # Flag for running automatic backups
137
144
  self.askBeforeBackup = True # Flag for asking before running automatic backup
145
+ self.askBeforeExit = True # Flag for asking before exiting the app
138
146
 
139
147
  # Text Editor Settings
140
148
  self.textFont = QFont() # Editor font
@@ -194,6 +202,7 @@ class Config:
194
202
  # State
195
203
  self.showViewerPanel = True # The panel for the viewer is visible
196
204
  self.showEditToolBar = False # The document editor toolbar visibility
205
+ self.showSessionTime = True # Show the session time in the status bar
197
206
  self.viewComments = True # Comments are shown in the viewer
198
207
  self.viewSynopsis = True # Synopsis is shown in the viewer
199
208
 
@@ -256,6 +265,10 @@ class Config:
256
265
  def hasError(self) -> bool:
257
266
  return self._hasError
258
267
 
268
+ @property
269
+ def pdfDocs(self) -> Path | None:
270
+ return self._manuals.get(f"manual_{self.locale.name()}", self._manuals.get("manual"))
271
+
259
272
  @property
260
273
  def locale(self) -> QLocale:
261
274
  return self._dLocale
@@ -627,6 +640,7 @@ class Config:
627
640
  self._backupPath = conf.rdPath(sec, "backuppath", self._backupPath)
628
641
  self.backupOnClose = conf.rdBool(sec, "backuponclose", self.backupOnClose)
629
642
  self.askBeforeBackup = conf.rdBool(sec, "askbeforebackup", self.askBeforeBackup)
643
+ self.askBeforeExit = conf.rdBool(sec, "askbeforeexit", self.askBeforeExit)
630
644
 
631
645
  # Editor
632
646
  sec = "Editor"
@@ -674,6 +688,7 @@ class Config:
674
688
  sec = "State"
675
689
  self.showViewerPanel = conf.rdBool(sec, "showviewerpanel", self.showViewerPanel)
676
690
  self.showEditToolBar = conf.rdBool(sec, "showedittoolbar", self.showEditToolBar)
691
+ self.showSessionTime = conf.rdBool(sec, "showsessiontime", self.showSessionTime)
677
692
  self.viewComments = conf.rdBool(sec, "viewcomments", self.viewComments)
678
693
  self.viewSynopsis = conf.rdBool(sec, "viewsynopsis", self.viewSynopsis)
679
694
  self.searchCase = conf.rdBool(sec, "searchcase", self.searchCase)
@@ -737,6 +752,7 @@ class Config:
737
752
  "backuppath": str(self._backupPath),
738
753
  "backuponclose": str(self.backupOnClose),
739
754
  "askbeforebackup": str(self.askBeforeBackup),
755
+ "askbeforeexit": str(self.askBeforeExit),
740
756
  }
741
757
 
742
758
  conf["Editor"] = {
@@ -784,6 +800,7 @@ class Config:
784
800
  conf["State"] = {
785
801
  "showviewerpanel": str(self.showViewerPanel),
786
802
  "showedittoolbar": str(self.showEditToolBar),
803
+ "showsessiontime": str(self.showSessionTime),
787
804
  "viewcomments": str(self.viewComments),
788
805
  "viewsynopsis": str(self.viewSynopsis),
789
806
  "searchcase": str(self.searchCase),
@@ -839,29 +856,30 @@ class RecentProjects:
839
856
 
840
857
  def __init__(self, config: Config) -> None:
841
858
  self._conf = config
842
- self._data = {}
859
+ self._data: dict[str, dict[str, str | int]] = {}
860
+ self._map: dict[str, str] = {}
843
861
  return
844
862
 
845
863
  def loadCache(self) -> bool:
846
864
  """Load the cache file for recent projects."""
847
865
  self._data = {}
848
-
866
+ self._map = {}
849
867
  cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
850
868
  if cacheFile.is_file():
851
869
  try:
852
870
  with open(cacheFile, mode="r", encoding="utf-8") as inFile:
853
871
  data = json.load(inFile)
854
872
  for path, entry in data.items():
855
- self._data[path] = {
856
- "title": entry.get("title", ""),
857
- "words": entry.get("words", 0),
858
- "time": entry.get("time", 0),
859
- }
873
+ puuid = str(entry.get("uuid", ""))
874
+ title = str(entry.get("title", ""))
875
+ words = checkInt(entry.get("words", 0), 0)
876
+ saved = checkInt(entry.get("time", 0), 0)
877
+ if path and title:
878
+ self._setEntry(puuid, path, title, words, saved)
860
879
  except Exception:
861
880
  logger.error("Could not load recent project cache")
862
881
  logException()
863
882
  return False
864
-
865
883
  return True
866
884
 
867
885
  def saveCache(self) -> bool:
@@ -876,7 +894,6 @@ class RecentProjects:
876
894
  logger.error("Could not save recent project cache")
877
895
  logException()
878
896
  return False
879
-
880
897
  return True
881
898
 
882
899
  def listEntries(self) -> list[tuple[str, str, int, int]]:
@@ -886,14 +903,15 @@ class RecentProjects:
886
903
  for k, e in self._data.items()
887
904
  ]
888
905
 
889
- def update(self, path: str | Path, title: str, words: int, saved: float | int) -> None:
906
+ def update(self, path: str | Path, data: NWProjectData, saved: float | int) -> None:
890
907
  """Add or update recent cache information on a given project."""
891
- self._data[str(path)] = {
892
- "title": title,
893
- "words": int(words),
894
- "time": int(saved),
895
- }
896
- self.saveCache()
908
+ try:
909
+ if (remove := self._map.get(data.uuid)) and (remove != str(path)):
910
+ self.remove(remove)
911
+ self._setEntry(data.uuid, str(path), data.name, sum(data.currCounts), int(saved))
912
+ self.saveCache()
913
+ except Exception:
914
+ pass
897
915
  return
898
916
 
899
917
  def remove(self, path: str | Path) -> None:
@@ -903,6 +921,13 @@ class RecentProjects:
903
921
  self.saveCache()
904
922
  return
905
923
 
924
+ def _setEntry(self, puuid: str, path: str, title: str, words: int, saved: int) -> None:
925
+ """Set an entry in the recent projects record."""
926
+ self._data[path] = {"uuid": puuid, "title": title, "words": words, "time": saved}
927
+ if puuid:
928
+ self._map[puuid] = path
929
+ return
930
+
906
931
 
907
932
  class RecentPaths:
908
933
 
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–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -173,6 +173,10 @@ class nwKeyWords:
173
173
  TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
174
174
  OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY,
175
175
  ]
176
+ CAN_CREATE = [
177
+ POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
178
+ OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY,
179
+ ]
176
180
 
177
181
  # Set of Valid Keys
178
182
  VALID_KEYS = set(ALL_KEYS)
@@ -207,7 +211,7 @@ class nwLists:
207
211
 
208
212
  class nwStats:
209
213
 
210
- CHARS_ALL = "allChars"
214
+ CHARS = "allChars"
211
215
  CHARS_TEXT = "textChars"
212
216
  CHARS_TITLE = "titleChars"
213
217
  PARAGRAPHS = "paragraphCount"
@@ -215,14 +219,14 @@ class nwStats:
215
219
  WCHARS_ALL = "allWordChars"
216
220
  WCHARS_TEXT = "textWordChars"
217
221
  WCHARS_TITLE = "titleWordChars"
218
- WORDS_ALL = "allWords"
222
+ WORDS = "allWords"
219
223
  WORDS_TEXT = "textWords"
220
224
  WORDS_TITLE = "titleWords"
221
225
 
222
226
  # Note: The order here affects the order of menu entries
223
227
  ALL_FIELDS = [
224
- WORDS_ALL, WORDS_TEXT, WORDS_TITLE,
225
- CHARS_ALL, CHARS_TEXT, CHARS_TITLE,
228
+ WORDS, WORDS_TEXT, WORDS_TITLE,
229
+ CHARS, CHARS_TEXT, CHARS_TITLE,
226
230
  WCHARS_ALL, WCHARS_TEXT, WCHARS_TITLE,
227
231
  PARAGRAPHS, TITLES,
228
232
  ]
@@ -329,17 +333,17 @@ class nwLabels:
329
333
  nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
330
334
  }
331
335
  STATS_NAME = {
332
- nwStats.CHARS_ALL: QT_TRANSLATE_NOOP("Constant", "Characters"),
333
- nwStats.CHARS_TEXT: QT_TRANSLATE_NOOP("Constant", "Characters in Text"),
334
- nwStats.CHARS_TITLE: QT_TRANSLATE_NOOP("Constant", "Characters in Headings"),
335
- nwStats.PARAGRAPHS: QT_TRANSLATE_NOOP("Constant", "Paragraphs"),
336
- nwStats.TITLES: QT_TRANSLATE_NOOP("Constant", "Headings"),
337
- nwStats.WCHARS_ALL: QT_TRANSLATE_NOOP("Constant", "Characters, No Spaces"),
338
- nwStats.WCHARS_TEXT: QT_TRANSLATE_NOOP("Constant", "Characters in Text, No Spaces"),
339
- nwStats.WCHARS_TITLE: QT_TRANSLATE_NOOP("Constant", "Characters in Headings, No Spaces"),
340
- nwStats.WORDS_ALL: QT_TRANSLATE_NOOP("Constant", "Words"),
341
- nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Constant", "Words in Text"),
342
- nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Constant", "Words in Headings"),
336
+ nwStats.CHARS: QT_TRANSLATE_NOOP("Stats", "Characters"),
337
+ nwStats.CHARS_TEXT: QT_TRANSLATE_NOOP("Stats", "Characters in Text"),
338
+ nwStats.CHARS_TITLE: QT_TRANSLATE_NOOP("Stats", "Characters in Headings"),
339
+ nwStats.PARAGRAPHS: QT_TRANSLATE_NOOP("Stats", "Paragraphs"),
340
+ nwStats.TITLES: QT_TRANSLATE_NOOP("Stats", "Headings"),
341
+ nwStats.WCHARS_ALL: QT_TRANSLATE_NOOP("Stats", "Characters, No Spaces"),
342
+ nwStats.WCHARS_TEXT: QT_TRANSLATE_NOOP("Stats", "Characters in Text, No Spaces"),
343
+ nwStats.WCHARS_TITLE: QT_TRANSLATE_NOOP("Stats", "Characters in Headings, No Spaces"),
344
+ nwStats.WORDS: QT_TRANSLATE_NOOP("Stats", "Words"),
345
+ nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
346
+ nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"),
343
347
  }
344
348
  BUILD_FMT = {
345
349
  nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
@@ -366,32 +370,32 @@ class nwLabels:
366
370
  nwBuildFmt.J_NWD: ".json",
367
371
  }
368
372
  SHAPES_PLAIN = {
369
- nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
370
- nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
371
- nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
372
- nwStatusShape.DIAMOND: QT_TRANSLATE_NOOP("Constant", "Diamond"),
373
- nwStatusShape.PENTAGON: QT_TRANSLATE_NOOP("Constant", "Pentagon"),
374
- nwStatusShape.HEXAGON: QT_TRANSLATE_NOOP("Constant", "Hexagon"),
375
- nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
376
- nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
373
+ nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Shape", "Square"),
374
+ nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Shape", "Triangle"),
375
+ nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Shape", "Nabla"),
376
+ nwStatusShape.DIAMOND: QT_TRANSLATE_NOOP("Shape", "Diamond"),
377
+ nwStatusShape.PENTAGON: QT_TRANSLATE_NOOP("Shape", "Pentagon"),
378
+ nwStatusShape.HEXAGON: QT_TRANSLATE_NOOP("Shape", "Hexagon"),
379
+ nwStatusShape.STAR: QT_TRANSLATE_NOOP("Shape", "Star"),
380
+ nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Shape", "Pacman"),
377
381
  }
378
382
  SHAPES_CIRCLE = {
379
- nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "1/4 Circle"),
380
- nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Half Circle"),
381
- nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "3/4 Circle"),
382
- nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Full Circle"),
383
+ nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Shape", "1/4 Circle"),
384
+ nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Shape", "Half Circle"),
385
+ nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Shape", "3/4 Circle"),
386
+ nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Shape", "Full Circle"),
383
387
  }
384
388
  SHAPES_BARS = {
385
- nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
386
- nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
387
- nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
388
- nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Constant", "4 Bars"),
389
+ nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Shape", "1 Bar"),
390
+ nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Shape", "2 Bars"),
391
+ nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Shape", "3 Bars"),
392
+ nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Shape", "4 Bars"),
389
393
  }
390
394
  SHAPES_BLOCKS = {
391
- nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Constant", "1 Block"),
392
- nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Constant", "2 Blocks"),
393
- nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Constant", "3 Blocks"),
394
- nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Constant", "4 Blocks"),
395
+ nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Shape", "1 Block"),
396
+ nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Shape", "2 Blocks"),
397
+ nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Shape", "3 Blocks"),
398
+ nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Shape", "4 Blocks"),
395
399
  }
396
400
  FILE_FILTERS = {
397
401
  "*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
@@ -7,7 +7,7 @@ Created: 2023-02-14 [2.1b1] BuildSettings
7
7
  Created: 2023-05-22 [2.1b1] BuildCollection
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -159,6 +159,13 @@ SETTINGS_LABELS = {
159
159
  "format.indentFirstPar": QT_TRANSLATE_NOOP("Builds", "Indent First Paragraph"),
160
160
 
161
161
  "format.grpMargins": QT_TRANSLATE_NOOP("Builds", "Text Margins"),
162
+ "format.titleMargin": QT_TRANSLATE_NOOP("Builds", "Title and Partition"),
163
+ "format.h1Margin": QT_TRANSLATE_NOOP("Builds", "Heading 1 and Chapter"),
164
+ "format.h2Margin": QT_TRANSLATE_NOOP("Builds", "Heading 2 and Scene"),
165
+ "format.h3Margin": QT_TRANSLATE_NOOP("Builds", "Heading 3 and Section"),
166
+ "format.h4Margin": QT_TRANSLATE_NOOP("Builds", "Heading 4"),
167
+ "format.textMargin": QT_TRANSLATE_NOOP("Builds", "Text Paragraph"),
168
+ "format.sepMargin": QT_TRANSLATE_NOOP("Builds", "Scene Separator"),
162
169
 
163
170
  "format.grpPage": QT_TRANSLATE_NOOP("Builds", "Page Layout"),
164
171
  "format.pageUnit": QT_TRANSLATE_NOOP("Builds", "Unit"),
@@ -9,7 +9,7 @@ Created: 2022-11-03 [2.0rc2] ProjectBuilder
9
9
  Created: 2023-07-20 [2.1b1] DocDuplicator
10
10
 
11
11
  This file is a part of novelWriter
12
- Copyright 2018–2024, Veronica Berglyd Olsen
12
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
13
13
 
14
14
  This program is free software: you can redistribute it and/or modify
15
15
  it under the terms of the GNU General Public License as published by
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2022-12-01 [2.1b1] NWBuildDocument
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2018-09-29 [0.0.1]
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
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
novelwriter/core/index.py CHANGED
@@ -10,7 +10,7 @@ Created: 2022-05-29 [2.0rc1] TagsIndex
10
10
  Created: 2022-05-29 [2.0rc1] ItemIndex
11
11
 
12
12
  This file is a part of novelWriter
13
- Copyright 2018–2024, Veronica Berglyd Olsen
13
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
14
14
 
15
15
  This program is free software: you can redistribute it and/or modify
16
16
  it under the terms of the GNU General Public License as published by
novelwriter/core/item.py CHANGED
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2018-10-27 [0.0.1] NWItem
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -7,7 +7,7 @@ Created: 2024-11-16 [2.6b2] ProjectNode
7
7
  Created: 2024-11-16 [2.6b2] ProjectModel
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2024 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -7,7 +7,7 @@ Created: 2019-10-21 [0.3.1] OptionState
7
7
  Rewritten: 2020-02-19 [0.4.5] OptionState
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2018-09-29 [0.0.1] NWProject
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -353,9 +353,7 @@ class NWProject:
353
353
 
354
354
  # Update recent projects
355
355
  if storePath := self._storage.storagePath:
356
- CONFIG.recentProjects.update(
357
- storePath, self._data.name, sum(self._data.initCounts), time()
358
- )
356
+ CONFIG.recentProjects.update(storePath, self._data, time())
359
357
 
360
358
  # Check the project tree consistency
361
359
  # This also handles any orphaned files found
@@ -421,9 +419,7 @@ class NWProject:
421
419
 
422
420
  # Update recent projects
423
421
  if storagePath := self._storage.storagePath:
424
- CONFIG.recentProjects.update(
425
- storagePath, self._data.name, sum(self._data.currCounts), saveTime
426
- )
422
+ CONFIG.recentProjects.update(storagePath, self._data, saveTime)
427
423
 
428
424
  SHARED.newStatusMessage(self.tr("Saved Project: {0}").format(self._data.name))
429
425
  self.setProjectChanged(False)
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2022-10-30 [2.0rc2] NWProjectData
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
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,8 @@ import uuid
29
29
  from typing import TYPE_CHECKING, Any
30
30
 
31
31
  from novelwriter.common import (
32
- checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
32
+ checkBool, checkInt, checkStringNone, checkUuid, isHandle,
33
+ makeFileNameSafe, simplified
33
34
  )
34
35
  from novelwriter.core.status import NWStatus
35
36
 
@@ -101,6 +102,11 @@ class NWProjectData:
101
102
  """Return the project name."""
102
103
  return self._name
103
104
 
105
+ @property
106
+ def fileSafeName(self) -> str:
107
+ """Return the project name in a file name safe format."""
108
+ return makeFileNameSafe(self._name)
109
+
104
110
  @property
105
111
  def author(self) -> str:
106
112
  """Return the project author."""
@@ -8,7 +8,7 @@ Created: 2022-09-28 [2.0rc2] ProjectXMLReader
8
8
  Created: 2022-10-31 [2.0rc2] ProjectXMLWriter
9
9
 
10
10
  This file is a part of novelWriter
11
- Copyright 2018–2024, Veronica Berglyd Olsen
11
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
12
12
 
13
13
  This program is free software: you can redistribute it and/or modify
14
14
  it under the terms of the GNU General Public License as published by
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2023-06-11 [2.1b1] NWSessionLog
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -7,7 +7,7 @@ Created: 2019-06-11 [0.1.5] NWSpellEnchant
7
7
  Created: 2023-06-13 [2.1b1] UserDictionary
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -7,7 +7,7 @@ Created: 2019-05-19 [0.1.3] NWStatus
7
7
  Rewritten: 2022-04-05 [2.0b1] NWStatus
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -37,7 +37,7 @@ from PyQt5.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygon
37
37
  from novelwriter import SHARED
38
38
  from novelwriter.common import simplified
39
39
  from novelwriter.enum import nwStatusShape
40
- from novelwriter.types import QtPaintAnitAlias, QtTransparent
40
+ from novelwriter.types import QtPaintAntiAlias, QtTransparent
41
41
 
42
42
  if TYPE_CHECKING: # pragma: no cover
43
43
  from typing import TypeGuard # Requires Python 3.10
@@ -174,6 +174,20 @@ class NWStatus:
174
174
  """Yield entries from the status icons."""
175
175
  yield from self._store.items()
176
176
 
177
+ def fromRaw(self, data: list[str]) -> StatusEntry | None:
178
+ """Create a StatusEntry from a list of three strings consisting
179
+ of shape, colour, and name. This entry is not automatically
180
+ added to the list of entries.
181
+ """
182
+ try:
183
+ shape = nwStatusShape[str(data[0])]
184
+ color = QColor(str(data[1]))
185
+ icon = NWStatus.createIcon(self._height, color, shape)
186
+ return StatusEntry(simplified(data[2]), color, shape, icon)
187
+ except Exception:
188
+ logger.error("Could not parse entry %s", str(data))
189
+ return None
190
+
177
191
  @staticmethod
178
192
  def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
179
193
  """Generate an icon for a status label."""
@@ -181,7 +195,7 @@ class NWStatus:
181
195
  pixmap.fill(QtTransparent)
182
196
 
183
197
  painter = QPainter(pixmap)
184
- painter.setRenderHint(QtPaintAnitAlias)
198
+ painter.setRenderHint(QtPaintAntiAlias)
185
199
  painter.fillPath(_SHAPES.getShape(shape), color)
186
200
  painter.end()
187
201
 
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2022-11-01 [2.0rc2] NWStorage
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
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
novelwriter/core/tree.py CHANGED
@@ -7,7 +7,7 @@ Created: 2020-05-07 [0.4.5] NWTree
7
7
  Rewritten: 2024-11-16 [2.6b2] NWTree
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2020-05-21 [0.5.2] GuiAbout
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -7,7 +7,7 @@ Created: 2020-01-23 [0.4.3] GuiDocMerge
7
7
  Rewritten: 2022-10-06 [2.0rc1] GuiDocMerge
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -7,7 +7,7 @@ Created: 2020-02-01 [0.4.3] GuiDocSplit
7
7
  Rewritten: 2022-10-12 [2.0rc1] GuiDocSplit
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
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
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2022-06-11 [2.0rc1] GuiEditLabel
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
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