novelWriter 2.5b1__py3-none-any.whl → 2.5.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 (78) hide show
  1. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/RECORD +77 -75
  3. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +3 -3
  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_pl_PL.json +116 -0
  17. novelwriter/assets/i18n/project_pt_BR.json +74 -74
  18. novelwriter/assets/manual.pdf +0 -0
  19. novelwriter/assets/sample.zip +0 -0
  20. novelwriter/assets/text/credits_en.htm +52 -44
  21. novelwriter/assets/themes/cyberpunk_night.conf +1 -0
  22. novelwriter/assets/themes/default_dark.conf +1 -0
  23. novelwriter/assets/themes/default_light.conf +1 -0
  24. novelwriter/assets/themes/dracula.conf +1 -0
  25. novelwriter/assets/themes/solarized_dark.conf +1 -0
  26. novelwriter/assets/themes/solarized_light.conf +1 -0
  27. novelwriter/common.py +12 -3
  28. novelwriter/config.py +67 -15
  29. novelwriter/constants.py +8 -10
  30. novelwriter/core/buildsettings.py +5 -3
  31. novelwriter/core/coretools.py +3 -1
  32. novelwriter/core/docbuild.py +1 -0
  33. novelwriter/core/project.py +15 -4
  34. novelwriter/core/status.py +4 -1
  35. novelwriter/core/storage.py +6 -1
  36. novelwriter/core/tohtml.py +69 -29
  37. novelwriter/core/tokenizer.py +83 -14
  38. novelwriter/core/toodt.py +48 -21
  39. novelwriter/core/toqdoc.py +37 -21
  40. novelwriter/dialogs/about.py +10 -15
  41. novelwriter/dialogs/docmerge.py +16 -16
  42. novelwriter/dialogs/docsplit.py +16 -16
  43. novelwriter/dialogs/editlabel.py +6 -8
  44. novelwriter/dialogs/preferences.py +106 -93
  45. novelwriter/dialogs/projectsettings.py +16 -20
  46. novelwriter/dialogs/quotes.py +9 -5
  47. novelwriter/dialogs/wordlist.py +6 -6
  48. novelwriter/enum.py +4 -5
  49. novelwriter/extensions/configlayout.py +38 -4
  50. novelwriter/extensions/modified.py +22 -3
  51. novelwriter/extensions/{circularprogress.py → progressbars.py} +26 -3
  52. novelwriter/extensions/statusled.py +39 -23
  53. novelwriter/gui/doceditor.py +22 -13
  54. novelwriter/gui/dochighlight.py +30 -39
  55. novelwriter/gui/docviewer.py +24 -15
  56. novelwriter/gui/docviewerpanel.py +7 -0
  57. novelwriter/gui/mainmenu.py +11 -11
  58. novelwriter/gui/outline.py +4 -3
  59. novelwriter/gui/projtree.py +85 -77
  60. novelwriter/gui/search.py +10 -1
  61. novelwriter/gui/statusbar.py +25 -29
  62. novelwriter/gui/theme.py +3 -0
  63. novelwriter/guimain.py +139 -124
  64. novelwriter/shared.py +19 -8
  65. novelwriter/text/patterns.py +113 -0
  66. novelwriter/tools/dictionaries.py +2 -8
  67. novelwriter/tools/lipsum.py +8 -12
  68. novelwriter/tools/manusbuild.py +9 -9
  69. novelwriter/tools/manuscript.py +10 -5
  70. novelwriter/tools/manussettings.py +7 -3
  71. novelwriter/tools/noveldetails.py +10 -10
  72. novelwriter/tools/welcome.py +19 -10
  73. novelwriter/tools/writingstats.py +3 -3
  74. novelwriter/types.py +5 -2
  75. novelwriter/extensions/simpleprogress.py +0 -53
  76. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/LICENSE.md +0 -0
  77. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/entry_points.txt +0 -0
  78. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/top_level.txt +0 -0
@@ -39,6 +39,7 @@ from novelwriter.common import checkInt
39
39
  from novelwriter.constants import nwHeaders, nwRegEx, nwUnicode
40
40
  from novelwriter.core.index import processComment
41
41
  from novelwriter.enum import nwComment
42
+ from novelwriter.text.patterns import REGEX_PATTERNS
42
43
  from novelwriter.types import QRegExUnicode
43
44
 
44
45
  logger = logging.getLogger(__name__)
@@ -59,8 +60,8 @@ BLOCK_TITLE = 4
59
60
  class GuiDocHighlighter(QSyntaxHighlighter):
60
61
 
61
62
  __slots__ = (
62
- "_tHandle", "_isInactive", "_spellCheck", "_spellErr", "_hStyles",
63
- "_txtRules", "_cmnRules",
63
+ "_tHandle", "_isNovel", "_isInactive", "_spellCheck", "_spellErr",
64
+ "_hStyles", "_minRules", "_txtRules", "_cmnRules",
64
65
  )
65
66
 
66
67
  def __init__(self, document: QTextDocument) -> None:
@@ -69,11 +70,13 @@ class GuiDocHighlighter(QSyntaxHighlighter):
69
70
  logger.debug("Create: GuiDocHighlighter")
70
71
 
71
72
  self._tHandle = None
73
+ self._isNovel = False
72
74
  self._isInactive = False
73
75
  self._spellCheck = False
74
76
  self._spellErr = QTextCharFormat()
75
77
 
76
78
  self._hStyles: dict[str, QTextCharFormat] = {}
79
+ self._minRules: list[tuple[QRegularExpression, dict[int, QTextCharFormat]]] = []
77
80
  self._txtRules: list[tuple[QRegularExpression, dict[int, QTextCharFormat]]] = []
78
81
  self._cmnRules: list[tuple[QRegularExpression, dict[int, QTextCharFormat]]] = []
79
82
 
@@ -137,6 +140,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
137
140
  hlRule = {
138
141
  0: self._hStyles["mspaces"],
139
142
  }
143
+ self._minRules.append((rxRule, hlRule))
140
144
  self._txtRules.append((rxRule, hlRule))
141
145
  self._cmnRules.append((rxRule, hlRule))
142
146
 
@@ -146,106 +150,89 @@ class GuiDocHighlighter(QSyntaxHighlighter):
146
150
  hlRule = {
147
151
  0: self._hStyles["nobreak"],
148
152
  }
153
+ self._minRules.append((rxRule, hlRule))
149
154
  self._txtRules.append((rxRule, hlRule))
150
155
  self._cmnRules.append((rxRule, hlRule))
151
156
 
152
157
  # Dialogue
153
158
  if CONFIG.dialogStyle > 0:
154
- symO = ""
155
- symC = ""
156
- if CONFIG.dialogStyle in (1, 3):
157
- symO += CONFIG.fmtSQuoteOpen
158
- symC += CONFIG.fmtSQuoteClose
159
- if CONFIG.dialogStyle in (2, 3):
160
- symO += CONFIG.fmtDQuoteOpen
161
- symC += CONFIG.fmtDQuoteClose
162
-
163
- rxEnd = "|$" if CONFIG.allowOpenDial else ""
164
- rxRule = QRegularExpression(f"\\B[{symO}].*?[{symC}]\\B{rxEnd}")
165
- rxRule.setPatternOptions(QRegExUnicode)
159
+ rxRule = REGEX_PATTERNS.dialogStyle
166
160
  hlRule = {
167
161
  0: self._hStyles["dialog"],
168
162
  }
169
163
  self._txtRules.append((rxRule, hlRule))
170
164
 
171
165
  if CONFIG.dialogLine:
172
- sym = QRegularExpression.escape(CONFIG.dialogLine)
173
- rxRule = QRegularExpression(f"^{sym}.*?$")
174
- rxRule.setPatternOptions(QRegExUnicode)
166
+ rxRule = REGEX_PATTERNS.dialogLine
175
167
  hlRule = {
176
168
  0: self._hStyles["dialog"],
177
169
  }
178
170
  self._txtRules.append((rxRule, hlRule))
179
171
 
180
172
  if CONFIG.narratorBreak:
181
- sym = QRegularExpression.escape(CONFIG.narratorBreak)
182
- rxRule = QRegularExpression(f"({sym}\\b)(.*?)(\\b{sym})")
183
- rxRule.setPatternOptions(QRegExUnicode)
173
+ rxRule = REGEX_PATTERNS.narratorBreak
184
174
  hlRule = {
185
175
  0: self._hStyles["text"],
186
176
  }
187
177
  self._txtRules.append((rxRule, hlRule))
188
178
 
189
179
  if CONFIG.altDialogOpen and CONFIG.altDialogClose:
190
- symO = QRegularExpression.escape(CONFIG.altDialogOpen)
191
- symC = QRegularExpression.escape(CONFIG.altDialogClose)
192
- rxRule = QRegularExpression(f"\\B{symO}.*?{symC}\\B")
193
- rxRule.setPatternOptions(QRegExUnicode)
180
+ rxRule = REGEX_PATTERNS.altDialogStyle
194
181
  hlRule = {
195
182
  0: self._hStyles["altdialog"],
196
183
  }
197
184
  self._txtRules.append((rxRule, hlRule))
198
185
 
199
186
  # Markdown Italic
200
- rxRule = QRegularExpression(nwRegEx.FMT_EI)
201
- rxRule.setPatternOptions(QRegExUnicode)
187
+ rxRule = REGEX_PATTERNS.markdownItalic
202
188
  hlRule = {
203
189
  1: self._hStyles["markup"],
204
190
  2: self._hStyles["italic"],
205
191
  3: self._hStyles["markup"],
206
192
  }
193
+ self._minRules.append((rxRule, hlRule))
207
194
  self._txtRules.append((rxRule, hlRule))
208
195
  self._cmnRules.append((rxRule, hlRule))
209
196
 
210
197
  # Markdown Bold
211
- rxRule = QRegularExpression(nwRegEx.FMT_EB)
212
- rxRule.setPatternOptions(QRegExUnicode)
198
+ rxRule = REGEX_PATTERNS.markdownBold
213
199
  hlRule = {
214
200
  1: self._hStyles["markup"],
215
201
  2: self._hStyles["bold"],
216
202
  3: self._hStyles["markup"],
217
203
  }
204
+ self._minRules.append((rxRule, hlRule))
218
205
  self._txtRules.append((rxRule, hlRule))
219
206
  self._cmnRules.append((rxRule, hlRule))
220
207
 
221
208
  # Markdown Strikethrough
222
- rxRule = QRegularExpression(nwRegEx.FMT_ST)
223
- rxRule.setPatternOptions(QRegExUnicode)
209
+ rxRule = REGEX_PATTERNS.markdownStrike
224
210
  hlRule = {
225
211
  1: self._hStyles["markup"],
226
212
  2: self._hStyles["strike"],
227
213
  3: self._hStyles["markup"],
228
214
  }
215
+ self._minRules.append((rxRule, hlRule))
229
216
  self._txtRules.append((rxRule, hlRule))
230
217
  self._cmnRules.append((rxRule, hlRule))
231
218
 
232
219
  # Shortcodes
233
- rxRule = QRegularExpression(nwRegEx.FMT_SC)
234
- rxRule.setPatternOptions(QRegExUnicode)
220
+ rxRule = REGEX_PATTERNS.shortcodePlain
235
221
  hlRule = {
236
222
  1: self._hStyles["code"],
237
223
  }
224
+ self._minRules.append((rxRule, hlRule))
238
225
  self._txtRules.append((rxRule, hlRule))
239
226
  self._cmnRules.append((rxRule, hlRule))
240
227
 
241
228
  # Shortcodes w/Value
242
- rxRule = QRegularExpression(nwRegEx.FMT_SV)
243
- rxRule.setPatternOptions(QRegExUnicode)
229
+ rxRule = REGEX_PATTERNS.shortcodeValue
244
230
  hlRule = {
245
231
  1: self._hStyles["code"],
246
232
  2: self._hStyles["value"],
247
233
  3: self._hStyles["code"],
248
234
  }
235
+ self._minRules.append((rxRule, hlRule))
249
236
  self._txtRules.append((rxRule, hlRule))
250
237
  self._cmnRules.append((rxRule, hlRule))
251
238
 
@@ -255,6 +242,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
255
242
  hlRule = {
256
243
  1: self._hStyles["markup"],
257
244
  }
245
+ self._minRules.append((rxRule, hlRule))
258
246
  self._txtRules.append((rxRule, hlRule))
259
247
 
260
248
  # Auto-Replace Tags
@@ -263,6 +251,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
263
251
  hlRule = {
264
252
  0: self._hStyles["replace"],
265
253
  }
254
+ self._minRules.append((rxRule, hlRule))
266
255
  self._txtRules.append((rxRule, hlRule))
267
256
  self._cmnRules.append((rxRule, hlRule))
268
257
 
@@ -280,9 +269,11 @@ class GuiDocHighlighter(QSyntaxHighlighter):
280
269
  def setHandle(self, tHandle: str) -> None:
281
270
  """Set the handle of the currently highlighted document."""
282
271
  self._tHandle = tHandle
283
- self._isInactive = (
284
- item.isInactiveClass() if (item := SHARED.project.tree[tHandle]) else False
285
- )
272
+ self._isNovel = False
273
+ self._isInactive = False
274
+ if item := SHARED.project.tree[tHandle]:
275
+ self._isNovel = item.isDocumentLayout()
276
+ self._isInactive = item.isInactiveClass()
286
277
  logger.debug("Syntax highlighter enabled for item '%s'", tHandle)
287
278
  return
288
279
 
@@ -397,7 +388,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
397
388
 
398
389
  elif text.startswith("["): # Special Command
399
390
  self.setCurrentBlockState(BLOCK_TEXT)
400
- hRules = self._txtRules
391
+ hRules = self._txtRules if self._isNovel else self._minRules
401
392
 
402
393
  sText = text.rstrip().lower()
403
394
  if sText in ("[newpage]", "[new page]", "[vspace]"):
@@ -414,7 +405,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
414
405
 
415
406
  else: # Text Paragraph
416
407
  self.setCurrentBlockState(BLOCK_TEXT)
417
- hRules = self._txtRules
408
+ hRules = self._txtRules if self._isNovel else self._minRules
418
409
 
419
410
  if hRules:
420
411
  for rX, hRule in hRules:
@@ -33,7 +33,7 @@ from enum import Enum
33
33
  from PyQt5.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot
34
34
  from PyQt5.QtGui import QCursor, QMouseEvent, QPalette, QResizeEvent, QTextCursor
35
35
  from PyQt5.QtWidgets import (
36
- QAction, QApplication, QFrame, QHBoxLayout, QLabel, QMenu, QTextBrowser,
36
+ QAction, QApplication, QFrame, QHBoxLayout, QMenu, QTextBrowser,
37
37
  QToolButton, QWidget
38
38
  )
39
39
 
@@ -42,6 +42,7 @@ from novelwriter.constants import nwHeaders, nwUnicode
42
42
  from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
43
43
  from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
44
44
  from novelwriter.error import logException
45
+ from novelwriter.extensions.configlayout import NColourLabel
45
46
  from novelwriter.extensions.eventfilters import WheelEventFilter
46
47
  from novelwriter.extensions.modified import NIconToolButton
47
48
  from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
@@ -92,6 +93,9 @@ class GuiDocViewer(QTextBrowser):
92
93
  self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
93
94
  self.customContextMenuRequested.connect(self._openContextMenu)
94
95
 
96
+ # Function Mapping
97
+ self.changeFocusState = self.docHeader.changeFocusState
98
+
95
99
  self.initViewer()
96
100
 
97
101
  logger.debug("Ready: GuiDocViewer")
@@ -165,6 +169,8 @@ class GuiDocViewer(QTextBrowser):
165
169
  self._docTheme.keyword = SHARED.theme.colKey
166
170
  self._docTheme.tag = SHARED.theme.colTag
167
171
  self._docTheme.optional = SHARED.theme.colOpt
172
+ self._docTheme.dialog = SHARED.theme.colDialN
173
+ self._docTheme.altdialog = SHARED.theme.colDialA
168
174
 
169
175
  # Set default text margins
170
176
  self.document().setDocumentMargin(0)
@@ -201,6 +207,7 @@ class GuiDocViewer(QTextBrowser):
201
207
  sPos = self.verticalScrollBar().value()
202
208
  qDoc = ToQTextDocument(SHARED.project)
203
209
  qDoc.setJustify(CONFIG.doJustify)
210
+ qDoc.setDialogueHighlight(True)
204
211
  qDoc.initDocument(CONFIG.textFont, self._docTheme)
205
212
  qDoc.setKeywords(True)
206
213
  qDoc.setComments(CONFIG.viewComments)
@@ -245,9 +252,6 @@ class GuiDocViewer(QTextBrowser):
245
252
  })
246
253
  self.updateDocMargins()
247
254
 
248
- # Since we change the content while it may still be rendering, we mark
249
- # the document dirty again to make sure it's re-rendered properly.
250
- self.redrawText()
251
255
  QApplication.restoreOverrideCursor()
252
256
  self.documentLoaded.emit(tHandle)
253
257
 
@@ -259,12 +263,6 @@ class GuiDocViewer(QTextBrowser):
259
263
  self.loadText(self._docHandle, updateHistory=False)
260
264
  return
261
265
 
262
- def redrawText(self) -> None:
263
- """Redraw the text by marking the content as "dirty"."""
264
- self.document().markContentsDirty(0, self.document().characterCount())
265
- self.updateDocMargins()
266
- return
267
-
268
266
  def docAction(self, action: nwDocAction) -> bool:
269
267
  """Process document actions on the current document."""
270
268
  logger.debug("Requesting action: '%s'", action.name)
@@ -284,6 +282,10 @@ class GuiDocViewer(QTextBrowser):
284
282
  return False
285
283
  return True
286
284
 
285
+ def anyFocus(self) -> bool:
286
+ """Check if any widget or child widget has focus."""
287
+ return self.hasFocus() or self.isAncestorOf(QApplication.focusWidget())
288
+
287
289
  def clearNavHistory(self) -> None:
288
290
  """Clear the navigation history."""
289
291
  self.docHistory.clear()
@@ -370,8 +372,10 @@ class GuiDocViewer(QTextBrowser):
370
372
  """Process a clicked link in the document."""
371
373
  if link := url.url():
372
374
  logger.debug("Clicked link: '%s'", link)
373
- if (bits := link.partition("_")) and bits[2]:
375
+ if (bits := link.partition("_")) and bits[0] == "#tag" and bits[2]:
374
376
  self.loadDocumentTagRequest.emit(bits[2], nwDocMode.VIEW)
377
+ else:
378
+ self.navigateTo(link)
375
379
  return
376
380
 
377
381
  @pyqtSlot("QPoint")
@@ -601,9 +605,7 @@ class GuiDocViewHeader(QWidget):
601
605
  self.setAutoFillBackground(True)
602
606
 
603
607
  # Title Label
604
- self.itemTitle = QLabel(self)
605
- self.itemTitle.setText("")
606
- self.itemTitle.setIndent(0)
608
+ self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
607
609
  self.itemTitle.setMargin(0)
608
610
  self.itemTitle.setContentsMargins(0, 0, 0, 0)
609
611
  self.itemTitle.setAutoFillBackground(True)
@@ -739,7 +741,14 @@ class GuiDocViewHeader(QWidget):
739
741
  palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
740
742
  palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
741
743
  self.setPalette(palette)
742
- self.itemTitle.setPalette(palette)
744
+ self.itemTitle.setTextColors(
745
+ color=palette.windowText().color(), faded=SHARED.theme.fadedText
746
+ )
747
+ return
748
+
749
+ def changeFocusState(self, state: bool) -> None:
750
+ """Toggle focus state."""
751
+ self.itemTitle.setColorState(state)
743
752
  return
744
753
 
745
754
  def setHandle(self, tHandle: str) -> None:
@@ -189,6 +189,13 @@ class GuiDocViewerPanel(QWidget):
189
189
  self._updateTabVisibility()
190
190
  return
191
191
 
192
+ @pyqtSlot(str)
193
+ def updateStatusLabels(self, kind: str) -> None:
194
+ """Update the importance labels."""
195
+ if kind == "i":
196
+ self._loadAllTags()
197
+ return
198
+
192
199
  ##
193
200
  # Private Slots
194
201
  ##
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import QAction, QMenuBar
35
35
  from novelwriter import CONFIG, SHARED
36
36
  from novelwriter.common import openExternalPath
37
37
  from novelwriter.constants import nwConst, nwKeyWords, nwLabels, nwUnicode, trConst
38
- from novelwriter.enum import nwDocAction, nwDocInsert, nwView, nwWidget
38
+ from novelwriter.enum import nwDocAction, nwDocInsert, nwFocus, nwView
39
39
  from novelwriter.extensions.eventfilters import StatusTipFilter
40
40
 
41
41
  if TYPE_CHECKING: # pragma: no cover
@@ -54,7 +54,7 @@ class GuiMainMenu(QMenuBar):
54
54
  requestDocInsert = pyqtSignal(nwDocInsert)
55
55
  requestDocInsertText = pyqtSignal(str)
56
56
  requestDocKeyWordInsert = pyqtSignal(str)
57
- requestFocusChange = pyqtSignal(nwWidget)
57
+ requestFocusChange = pyqtSignal(nwFocus)
58
58
  requestViewChange = pyqtSignal(nwView)
59
59
 
60
60
  def __init__(self, mainGui: GuiMain) -> None:
@@ -167,7 +167,7 @@ class GuiMainMenu(QMenuBar):
167
167
  # Project > Edit
168
168
  self.aEditItem = self.projMenu.addAction(self.tr("Rename Item"))
169
169
  self.aEditItem.setShortcut("F2")
170
- self.aEditItem.triggered.connect(lambda: self.mainGui.editItemLabel(None))
170
+ self.aEditItem.triggered.connect(lambda: self.mainGui.projView.renameTreeItem(None))
171
171
 
172
172
  # Project > Delete
173
173
  self.aDeleteItem = self.projMenu.addAction(self.tr("Delete Item"))
@@ -303,24 +303,24 @@ class GuiMainMenu(QMenuBar):
303
303
  self.viewMenu = self.addMenu(self.tr("&View"))
304
304
 
305
305
  # View > TreeView
306
- self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Project Tree"))
306
+ self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Tree View"))
307
307
  self.aFocusTree.setShortcut("Ctrl+T")
308
308
  self.aFocusTree.triggered.connect(
309
- lambda: self.requestFocusChange.emit(nwWidget.TREE)
309
+ lambda: self.requestFocusChange.emit(nwFocus.TREE)
310
310
  )
311
311
 
312
312
  # View > Document Editor
313
- self.aFocusEditor = self.viewMenu.addAction(self.tr("Go to Document Editor"))
314
- self.aFocusEditor.setShortcut("Ctrl+E")
315
- self.aFocusEditor.triggered.connect(
316
- lambda: self.requestFocusChange.emit(nwWidget.EDITOR)
313
+ self.aFocusDocument = self.viewMenu.addAction(self.tr("Go to Document"))
314
+ self.aFocusDocument.setShortcut("Ctrl+E")
315
+ self.aFocusDocument.triggered.connect(
316
+ lambda: self.requestFocusChange.emit(nwFocus.DOCUMENT)
317
317
  )
318
318
 
319
319
  # View > Outline
320
320
  self.aFocusOutline = self.viewMenu.addAction(self.tr("Go to Outline"))
321
321
  self.aFocusOutline.setShortcut("Ctrl+Shift+T")
322
322
  self.aFocusOutline.triggered.connect(
323
- lambda: self.requestFocusChange.emit(nwWidget.OUTLINE)
323
+ lambda: self.requestFocusChange.emit(nwFocus.OUTLINE)
324
324
  )
325
325
 
326
326
  # View > Separator
@@ -422,7 +422,7 @@ class GuiMainMenu(QMenuBar):
422
422
  self.aInsMSApos = self.mInsQuotes.addAction(self.tr("Alternative Apostrophe"))
423
423
  self.aInsMSApos.setShortcut("Ctrl+K, '")
424
424
  self.aInsMSApos.triggered.connect(
425
- lambda: self.requestDocInsertText.emit(nwUnicode.U_MAPOSS)
425
+ lambda: self.requestDocInsertText.emit(nwUnicode.U_MAPOS)
426
426
  )
427
427
 
428
428
  # Insert > Symbols
@@ -215,7 +215,7 @@ class GuiOutlineToolBar(QToolBar):
215
215
 
216
216
  # Novel Selector
217
217
  self.novelLabel = NColourLabel(
218
- self.tr("Outline of"), parent=self, scale=NColourLabel.HEADER_SCALE, bold=True
218
+ self.tr("Outline of"), self, scale=NColourLabel.HEADER_SCALE, bold=True
219
219
  )
220
220
  self.novelLabel.setContentsMargins(0, 0, CONFIG.pxInt(12), 0)
221
221
 
@@ -268,6 +268,7 @@ class GuiOutlineToolBar(QToolBar):
268
268
  self.aExport.setIcon(SHARED.theme.getIcon("export"))
269
269
  self.tbColumns.setIcon(SHARED.theme.getIcon("menu"))
270
270
  self.tbColumns.setStyleSheet("QToolButton::menu-indicator {image: none;}")
271
+ self.novelLabel.setTextColors(color=self.palette().windowText().color())
271
272
  return
272
273
 
273
274
  def populateNovelList(self) -> None:
@@ -523,12 +524,12 @@ class GuiOutlineTree(QTreeWidget):
523
524
  @pyqtSlot()
524
525
  def exportOutline(self) -> None:
525
526
  """Export the outline as a CSV file."""
526
- path = CONFIG.lastPath() / f"{makeFileNameSafe(SHARED.project.data.name)}.csv"
527
+ path = CONFIG.lastPath("outline") / f"{makeFileNameSafe(SHARED.project.data.name)}.csv"
527
528
  path, _ = QFileDialog.getSaveFileName(
528
529
  self, self.tr("Save Outline As"), str(path), formatFileFilter(["*.csv", "*"])
529
530
  )
530
531
  if path:
531
- CONFIG.setLastPath(path)
532
+ CONFIG.setLastPath("outline", path)
532
533
  logger.info("Writing CSV file: %s", path)
533
534
  cols = [col for col in self._treeOrder if not self._colHidden[col]]
534
535
  order = [self._colIdx[col] for col in cols]