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
@@ -31,24 +31,22 @@ import logging
31
31
  from enum import Enum
32
32
 
33
33
  from PyQt5.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot
34
- from PyQt5.QtGui import QCursor, QMouseEvent, QPalette, QResizeEvent, QTextCursor, QTextOption
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
 
40
40
  from novelwriter import CONFIG, SHARED
41
- from novelwriter.common import cssCol
42
41
  from novelwriter.constants import nwHeaders, nwUnicode
43
- from novelwriter.core.tohtml import ToHtml
42
+ from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
44
43
  from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
45
44
  from novelwriter.error import logException
45
+ from novelwriter.extensions.configlayout import NColourLabel
46
46
  from novelwriter.extensions.eventfilters import WheelEventFilter
47
47
  from novelwriter.extensions.modified import NIconToolButton
48
48
  from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
49
- from novelwriter.types import (
50
- QtAlignCenterTop, QtAlignJustify, QtKeepAnchor, QtMouseLeft, QtMoveAnchor
51
- )
49
+ from novelwriter.types import QtAlignCenterTop, QtKeepAnchor, QtMouseLeft, QtMoveAnchor
52
50
 
53
51
  logger = logging.getLogger(__name__)
54
52
 
@@ -69,6 +67,7 @@ class GuiDocViewer(QTextBrowser):
69
67
 
70
68
  # Internal Variables
71
69
  self._docHandle = None
70
+ self._docTheme = TextDocumentTheme()
72
71
 
73
72
  # Settings
74
73
  self.setMinimumWidth(CONFIG.pxInt(300))
@@ -94,6 +93,9 @@ class GuiDocViewer(QTextBrowser):
94
93
  self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
95
94
  self.customContextMenuRequested.connect(self._openContextMenu)
96
95
 
96
+ # Function Mapping
97
+ self.changeFocusState = self.docHeader.changeFocusState
98
+
97
99
  self.initViewer()
98
100
 
99
101
  logger.debug("Ready: GuiDocViewer")
@@ -137,8 +139,10 @@ class GuiDocViewer(QTextBrowser):
137
139
 
138
140
  def initViewer(self) -> None:
139
141
  """Set editor settings from main config."""
140
- self._makeStyleSheet()
141
- self.initFont()
142
+ # Set the font. See issues #1862 and #1875.
143
+ self.setFont(CONFIG.textFont)
144
+ self.docHeader.updateFont()
145
+ self.docFooter.updateFont()
142
146
 
143
147
  # Set the widget colours to match syntax theme
144
148
  mainPalette = self.palette()
@@ -151,16 +155,25 @@ class GuiDocViewer(QTextBrowser):
151
155
  docPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
152
156
  docPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
153
157
  self.viewport().setPalette(docPalette)
154
-
155
158
  self.docHeader.matchColours()
156
159
  self.docFooter.matchColours()
157
160
 
161
+ # Update theme colours
162
+ self._docTheme.text = SHARED.theme.colText
163
+ self._docTheme.highlight = SHARED.theme.colMark
164
+ self._docTheme.head = SHARED.theme.colHead
165
+ self._docTheme.comment = SHARED.theme.colHidden
166
+ self._docTheme.note = SHARED.theme.colNote
167
+ self._docTheme.code = SHARED.theme.colCode
168
+ self._docTheme.modifier = SHARED.theme.colMod
169
+ self._docTheme.keyword = SHARED.theme.colKey
170
+ self._docTheme.tag = SHARED.theme.colTag
171
+ self._docTheme.optional = SHARED.theme.colOpt
172
+ self._docTheme.dialog = SHARED.theme.colDialN
173
+ self._docTheme.altdialog = SHARED.theme.colDialA
174
+
158
175
  # Set default text margins
159
176
  self.document().setDocumentMargin(0)
160
- options = QTextOption()
161
- if CONFIG.doJustify:
162
- options.setAlignment(QtAlignJustify)
163
- self.document().setDefaultTextOption(options)
164
177
 
165
178
  # Scroll bars
166
179
  if CONFIG.hideVScroll:
@@ -181,22 +194,6 @@ class GuiDocViewer(QTextBrowser):
181
194
 
182
195
  return
183
196
 
184
- def initFont(self) -> None:
185
- """Set the font of the main widget and sub-widgets. This needs
186
- special attention since there appears to be a bug in Qt 5.15.3.
187
- See issues #1862 and #1875.
188
- """
189
- font = self.font()
190
- font.setFamily(CONFIG.textFont)
191
- font.setPointSize(CONFIG.textSize)
192
- self.setFont(font)
193
-
194
- # Reset sub-widget font to GUI font
195
- self.docHeader.updateFont()
196
- self.docFooter.updateFont()
197
-
198
- return
199
-
200
197
  def loadText(self, tHandle: str, updateHistory: bool = True) -> bool:
201
198
  """Load text into the viewer from an item handle."""
202
199
  if not SHARED.project.tree.checkType(tHandle, nwItemType.FILE):
@@ -208,21 +205,23 @@ class GuiDocViewer(QTextBrowser):
208
205
  QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
209
206
 
210
207
  sPos = self.verticalScrollBar().value()
211
- aDoc = ToHtml(SHARED.project)
212
- aDoc.setPreview(True)
213
- aDoc.setKeywords(True)
214
- aDoc.setComments(CONFIG.viewComments)
215
- aDoc.setSynopsis(CONFIG.viewSynopsis)
216
- aDoc.setLinkHeadings(True)
208
+ qDoc = ToQTextDocument(SHARED.project)
209
+ qDoc.setJustify(CONFIG.doJustify)
210
+ qDoc.setDialogueHighlight(True)
211
+ qDoc.initDocument(CONFIG.textFont, self._docTheme)
212
+ qDoc.setKeywords(True)
213
+ qDoc.setComments(CONFIG.viewComments)
214
+ qDoc.setSynopsis(CONFIG.viewSynopsis)
217
215
 
218
216
  # Be extra careful here to prevent crashes when first opening a
219
217
  # project as a crash here leaves no way of recovering.
220
218
  # See issue #298
221
219
  try:
222
- aDoc.setText(tHandle)
223
- aDoc.doPreProcessing()
224
- aDoc.tokenizeText()
225
- aDoc.doConvert()
220
+ qDoc.setText(tHandle)
221
+ qDoc.doPreProcessing()
222
+ qDoc.tokenizeText()
223
+ qDoc.doConvert()
224
+ qDoc.appendFootnotes()
226
225
  except Exception:
227
226
  logger.error("Failed to generate preview for document with handle '%s'", tHandle)
228
227
  logException()
@@ -238,11 +237,7 @@ class GuiDocViewer(QTextBrowser):
238
237
  self.docHistory.append(tHandle)
239
238
 
240
239
  self.setDocumentTitle(tHandle)
241
-
242
- # Replace tabs before setting the HTML, and then put them back in
243
- self.setHtml(aDoc.result.replace("\t", "!!tab!!"))
244
- while self.find("!!tab!!"):
245
- self.textCursor().insertText("\t")
240
+ self.setDocument(qDoc.document)
246
241
 
247
242
  if self._docHandle == tHandle:
248
243
  # This is a refresh, so we set the scrollbar back to where it was
@@ -257,9 +252,6 @@ class GuiDocViewer(QTextBrowser):
257
252
  })
258
253
  self.updateDocMargins()
259
254
 
260
- # Since we change the content while it may still be rendering, we mark
261
- # the document dirty again to make sure it's re-rendered properly.
262
- self.redrawText()
263
255
  QApplication.restoreOverrideCursor()
264
256
  self.documentLoaded.emit(tHandle)
265
257
 
@@ -271,12 +263,6 @@ class GuiDocViewer(QTextBrowser):
271
263
  self.loadText(self._docHandle, updateHistory=False)
272
264
  return
273
265
 
274
- def redrawText(self) -> None:
275
- """Redraw the text by marking the content as "dirty"."""
276
- self.document().markContentsDirty(0, self.document().characterCount())
277
- self.updateDocMargins()
278
- return
279
-
280
266
  def docAction(self, action: nwDocAction) -> bool:
281
267
  """Process document actions on the current document."""
282
268
  logger.debug("Requesting action: '%s'", action.name)
@@ -296,6 +282,10 @@ class GuiDocViewer(QTextBrowser):
296
282
  return False
297
283
  return True
298
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
+
299
289
  def clearNavHistory(self) -> None:
300
290
  """Clear the navigation history."""
301
291
  self.docHistory.clear()
@@ -380,12 +370,12 @@ class GuiDocViewer(QTextBrowser):
380
370
  @pyqtSlot("QUrl")
381
371
  def _linkClicked(self, url: QUrl) -> None:
382
372
  """Process a clicked link in the document."""
383
- link = url.url()
384
- logger.debug("Clicked link: '%s'", link)
385
- if len(link) > 0:
386
- bits = link.split("=")
387
- if len(bits) == 2:
388
- self.loadDocumentTagRequest.emit(bits[1], nwDocMode.VIEW)
373
+ if link := url.url():
374
+ logger.debug("Clicked link: '%s'", link)
375
+ if (bits := link.partition("_")) and bits[0] == "#tag" and bits[2]:
376
+ self.loadDocumentTagRequest.emit(bits[2], nwDocMode.VIEW)
377
+ else:
378
+ self.navigateTo(link)
389
379
  return
390
380
 
391
381
  @pyqtSlot("QPoint")
@@ -475,34 +465,6 @@ class GuiDocViewer(QTextBrowser):
475
465
  self._makeSelection(selType)
476
466
  return
477
467
 
478
- def _makeStyleSheet(self) -> None:
479
- """Generate an appropriate style sheet for the document viewer,
480
- based on the current syntax highlighter theme.
481
- """
482
- colText = cssCol(SHARED.theme.colText)
483
- colHead = cssCol(SHARED.theme.colHead)
484
- colVals = cssCol(SHARED.theme.colVal)
485
- colMark = cssCol(SHARED.theme.colMark)
486
- colKeys = cssCol(SHARED.theme.colKey)
487
- colOpts = cssCol(SHARED.theme.colOpt)
488
- colHide = cssCol(SHARED.theme.colHidden)
489
- colMods = cssCol(SHARED.theme.colMod)
490
- self.document().setDefaultStyleSheet(
491
- f"body {{color: {colText};}}\n"
492
- f"h1, h2, h3, h4 {{color: {colHead};}}\n"
493
- f"a {{color: {colVals};}}\n"
494
- f"mark {{background-color: {colMark};}}\n"
495
- f".tags {{color: {colKeys};}}\n"
496
- f".optional {{color: {colOpts};}}\n"
497
- f".comment {{color: {colHide};}}\n"
498
- f".synopsis {{color: {colMods};}}\n"
499
- ".title {text-align: center;}\n"
500
- )
501
-
502
- return
503
-
504
- # END Class GuiDocViewer
505
-
506
468
 
507
469
  class GuiDocViewHistory:
508
470
 
@@ -616,15 +578,13 @@ class GuiDocViewHistory:
616
578
  )
617
579
  return
618
580
 
619
- # END Class GuiDocViewHistory
620
-
621
-
622
- # =============================================================================================== #
623
- # The Embedded Document Header
624
- # Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport
625
- # =============================================================================================== #
626
581
 
627
582
  class GuiDocViewHeader(QWidget):
583
+ """The Embedded Document Header
584
+
585
+ Only used by DocViewer, and is at a fixed position in the
586
+ QTextBrowser's viewport.
587
+ """
628
588
 
629
589
  def __init__(self, docViewer: GuiDocViewer) -> None:
630
590
  super().__init__(parent=docViewer)
@@ -645,9 +605,7 @@ class GuiDocViewHeader(QWidget):
645
605
  self.setAutoFillBackground(True)
646
606
 
647
607
  # Title Label
648
- self.itemTitle = QLabel(self)
649
- self.itemTitle.setText("")
650
- self.itemTitle.setIndent(0)
608
+ self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
651
609
  self.itemTitle.setMargin(0)
652
610
  self.itemTitle.setContentsMargins(0, 0, 0, 0)
653
611
  self.itemTitle.setAutoFillBackground(True)
@@ -783,7 +741,14 @@ class GuiDocViewHeader(QWidget):
783
741
  palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
784
742
  palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
785
743
  self.setPalette(palette)
786
- 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)
787
752
  return
788
753
 
789
754
  def setHandle(self, tHandle: str) -> None:
@@ -842,15 +807,13 @@ class GuiDocViewHeader(QWidget):
842
807
  self.docViewer.requestProjectItemSelected.emit(self._docHandle, True)
843
808
  return
844
809
 
845
- # END Class GuiDocViewHeader
846
-
847
-
848
- # =============================================================================================== #
849
- # The Embedded Document Footer
850
- # Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport
851
- # =============================================================================================== #
852
810
 
853
811
  class GuiDocViewFooter(QWidget):
812
+ """The Embedded Document Footer
813
+
814
+ Only used by DocViewer, and is at a fixed position in the
815
+ QTextBrowser's viewport.
816
+ """
854
817
 
855
818
  def __init__(self, docViewer: GuiDocViewer) -> None:
856
819
  super().__init__(parent=docViewer)
@@ -976,5 +939,3 @@ class GuiDocViewFooter(QWidget):
976
939
  CONFIG.viewSynopsis = state
977
940
  self.docViewer.reloadText()
978
941
  return
979
-
980
- # END Class GuiDocViewFooter
@@ -209,9 +209,8 @@ class GuiDocViewerPanel(QWidget):
209
209
 
210
210
  def _updateTabVisibility(self) -> None:
211
211
  """Hide class tabs with no content."""
212
- if CONFIG.verQtValue >= 0x050f00:
213
- for tClass, cTab in self.kwTabs.items():
214
- self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
212
+ for tClass, cTab in self.kwTabs.items():
213
+ self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
215
214
  return
216
215
 
217
216
  def _loadAllTags(self) -> None:
@@ -222,8 +221,6 @@ class GuiDocViewerPanel(QWidget):
222
221
  self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
223
222
  return
224
223
 
225
- # END Class GuiDocViewerPanel
226
-
227
224
 
228
225
  class _ViewPanelBackRefs(QTreeWidget):
229
226
 
@@ -361,8 +358,6 @@ class _ViewPanelBackRefs(QTreeWidget):
361
358
 
362
359
  return
363
360
 
364
- # END Class _ViewPanelBackRefs
365
-
366
361
 
367
362
  class _ViewPanelKeyWords(QTreeWidget):
368
363
 
@@ -450,7 +445,7 @@ class _ViewPanelKeyWords(QTreeWidget):
450
445
  nwItem.itemType, nwItem.itemClass,
451
446
  nwItem.itemLayout, nwItem.mainHeading
452
447
  )
453
- impLabel, impIcon = nwItem.getImportStatus(incIcon=True)
448
+ impLabel, impIcon = nwItem.getImportStatus()
454
449
  iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
455
450
  hDec = SHARED.theme.getHeaderDecorationNarrow(iLevel)
456
451
 
@@ -529,5 +524,3 @@ class _ViewPanelKeyWords(QTreeWidget):
529
524
  if index.column() not in (self.C_EDIT, self.C_VIEW):
530
525
  self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.VIEW)
531
526
  return
532
-
533
- # END Class _ViewPanelKeyWords
@@ -28,8 +28,8 @@ import logging
28
28
  from collections.abc import Iterable
29
29
  from time import time
30
30
 
31
- from PyQt5.QtGui import QTextBlock, QTextCursor, QTextDocument
32
31
  from PyQt5.QtCore import QObject, pyqtSlot
32
+ from PyQt5.QtGui import QTextBlock, QTextCursor, QTextDocument
33
33
  from PyQt5.QtWidgets import QApplication, QPlainTextDocumentLayout
34
34
 
35
35
  from novelwriter import SHARED
@@ -133,5 +133,3 @@ class GuiTextDocument(QTextDocument):
133
133
  """Set the spell check state of the syntax highlighter."""
134
134
  self._syntax.setSpellCheck(state)
135
135
  return
136
-
137
- # END Class GuiTextDocument
@@ -26,12 +26,14 @@ from __future__ import annotations
26
26
  import logging
27
27
 
28
28
  from PyQt5.QtCore import pyqtSlot
29
- from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
29
+ from PyQt5.QtWidgets import QGridLayout, QLabel, QWidget
30
30
 
31
31
  from novelwriter import CONFIG, SHARED
32
- from novelwriter.constants import trConst, nwLabels
32
+ from novelwriter.common import elide
33
+ from novelwriter.constants import nwLabels, trConst
33
34
  from novelwriter.types import (
34
- QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase, QtAlignRightMiddle
35
+ QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase,
36
+ QtAlignRightMiddle
35
37
  )
36
38
 
37
39
  logger = logging.getLogger(__name__)
@@ -236,10 +238,6 @@ class GuiItemDetails(QWidget):
236
238
  # Label
237
239
  # =====
238
240
 
239
- label = nwItem.itemName
240
- if len(label) > 100:
241
- label = label[:96].rstrip()+" ..."
242
-
243
241
  if nwItem.isFileType():
244
242
  if nwItem.isActive:
245
243
  self.labelIcon.setPixmap(SHARED.theme.getPixmap("checked", (iPx, iPx)))
@@ -248,12 +246,12 @@ class GuiItemDetails(QWidget):
248
246
  else:
249
247
  self.labelIcon.setPixmap(SHARED.theme.getPixmap("noncheckable", (iPx, iPx)))
250
248
 
251
- self.labelData.setText(label)
249
+ self.labelData.setText(elide(nwItem.itemName, 100))
252
250
 
253
251
  # Status
254
252
  # ======
255
253
 
256
- status, icon = nwItem.getImportStatus(incIcon=True)
254
+ status, icon = nwItem.getImportStatus()
257
255
  self.statusIcon.setPixmap(icon.pixmap(iPx, iPx))
258
256
  self.statusData.setText(status)
259
257
 
@@ -297,5 +295,3 @@ class GuiItemDetails(QWidget):
297
295
  self.wCountData.setText(f"{wC:n}")
298
296
  self.pCountData.setText(f"{pC:n}")
299
297
  return
300
-
301
- # END Class GuiItemDetails
@@ -25,17 +25,17 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
- from typing import TYPE_CHECKING
29
28
  from pathlib import Path
29
+ from typing import TYPE_CHECKING
30
30
 
31
- from PyQt5.QtGui import QDesktopServices
32
31
  from PyQt5.QtCore import QUrl, pyqtSignal, pyqtSlot
33
- from PyQt5.QtWidgets import QMenuBar, QAction
32
+ from PyQt5.QtGui import QDesktopServices
33
+ from PyQt5.QtWidgets import QAction, QMenuBar
34
34
 
35
35
  from novelwriter import CONFIG, SHARED
36
36
  from novelwriter.common import openExternalPath
37
- from novelwriter.constants import nwConst, trConst, nwKeyWords, nwLabels, nwUnicode
38
- from novelwriter.enum import nwDocAction, nwDocInsert, nwView, nwWidget
37
+ from novelwriter.constants import nwConst, nwKeyWords, nwLabels, nwUnicode, trConst
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"))
@@ -202,7 +202,7 @@ class GuiMainMenu(QMenuBar):
202
202
  # Document > Save
203
203
  self.aSaveDoc = self.docuMenu.addAction(self.tr("Save Document"))
204
204
  self.aSaveDoc.setShortcut("Ctrl+S")
205
- self.aSaveDoc.triggered.connect(self.mainGui.saveDocument)
205
+ self.aSaveDoc.triggered.connect(self.mainGui.forceSaveDocument)
206
206
 
207
207
  # Document > Close
208
208
  self.aCloseDoc = self.docuMenu.addAction(self.tr("Close Document"))
@@ -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
@@ -597,6 +597,12 @@ class GuiMainMenu(QMenuBar):
597
597
  lambda: self.requestDocInsert.emit(nwDocInsert.LIPSUM)
598
598
  )
599
599
 
600
+ # Insert > Footnote
601
+ self.aFootnote = self.insMenu.addAction(self.tr("Footnote"))
602
+ self.aFootnote.triggered.connect(
603
+ lambda: self.requestDocInsert.emit(nwDocInsert.FOOTNOTE)
604
+ )
605
+
600
606
  return
601
607
 
602
608
  def _buildFormatMenu(self) -> None:
@@ -985,5 +991,3 @@ class GuiMainMenu(QMenuBar):
985
991
  self.aWebsite.triggered.connect(lambda: self._openWebsite(nwConst.URL_WEB))
986
992
 
987
993
  return
988
-
989
- # END Class GuiMainMenu
@@ -29,14 +29,13 @@ import logging
29
29
 
30
30
  from enum import Enum
31
31
  from time import time
32
- from typing import TYPE_CHECKING
33
32
 
34
- from PyQt5.QtCore import QModelIndex, QPoint, Qt, pyqtSlot, pyqtSignal
33
+ from PyQt5.QtCore import QModelIndex, QPoint, Qt, pyqtSignal, pyqtSlot
35
34
  from PyQt5.QtGui import QFocusEvent, QFont, QMouseEvent, QPalette, QResizeEvent
36
35
  from PyQt5.QtWidgets import (
37
36
  QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView,
38
- QInputDialog, QMenu, QSizePolicy, QToolTip, QTreeWidget, QTreeWidgetItem,
39
- QVBoxLayout, QWidget
37
+ QInputDialog, QMenu, QToolTip, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
38
+ QWidget
40
39
  )
41
40
 
42
41
  from novelwriter import CONFIG, SHARED
@@ -47,10 +46,10 @@ from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
47
46
  from novelwriter.extensions.modified import NIconToolButton
48
47
  from novelwriter.extensions.novelselector import NovelSelector
49
48
  from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
50
- from novelwriter.types import QtAlignRight, QtDecoration, QtMouseLeft, QtMouseMiddle, QtUserRole
51
-
52
- if TYPE_CHECKING: # pragma: no cover
53
- from novelwriter.guimain import GuiMain
49
+ from novelwriter.types import (
50
+ QtAlignRight, QtDecoration, QtMouseLeft, QtMouseMiddle, QtSizeExpanding,
51
+ QtUserRole
52
+ )
54
53
 
55
54
  logger = logging.getLogger(__name__)
56
55
 
@@ -62,8 +61,6 @@ class NovelTreeColumn(Enum):
62
61
  FOCUS = 2
63
62
  PLOT = 3
64
63
 
65
- # END Enum NovelTreeColumn
66
-
67
64
 
68
65
  class GuiNovelView(QWidget):
69
66
 
@@ -71,10 +68,8 @@ class GuiNovelView(QWidget):
71
68
  selectedItemChanged = pyqtSignal(str)
72
69
  openDocumentRequest = pyqtSignal(str, Enum, str, bool)
73
70
 
74
- def __init__(self, mainGui: GuiMain) -> None:
75
- super().__init__(parent=mainGui)
76
-
77
- self.mainGui = mainGui
71
+ def __init__(self, parent: QWidget) -> None:
72
+ super().__init__(parent=parent)
78
73
 
79
74
  # Build GUI
80
75
  self.novelTree = GuiNovelTree(self)
@@ -188,8 +183,6 @@ class GuiNovelView(QWidget):
188
183
  self.novelTree.refreshHandle(tHandle)
189
184
  return
190
185
 
191
- # END Class GuiNovelView
192
-
193
186
 
194
187
  class GuiNovelToolBar(QWidget):
195
188
 
@@ -199,7 +192,6 @@ class GuiNovelToolBar(QWidget):
199
192
  logger.debug("Create: GuiNovelToolBar")
200
193
 
201
194
  self.novelView = novelView
202
- self.mainGui = novelView.mainGui
203
195
 
204
196
  iSz = SHARED.theme.baseIconSize
205
197
  mPx = CONFIG.pxInt(2)
@@ -215,7 +207,7 @@ class GuiNovelToolBar(QWidget):
215
207
  self.novelValue.setFont(selFont)
216
208
  self.novelValue.setListFormat(self.tr("Outline of {0}"))
217
209
  self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
218
- self.novelValue.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
210
+ self.novelValue.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
219
211
  self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
220
212
 
221
213
  self.tbNovel = NIconToolButton(self, iSz)
@@ -353,8 +345,6 @@ class GuiNovelToolBar(QWidget):
353
345
  self.aLastCol[colType] = aLast
354
346
  return
355
347
 
356
- # END Class GuiNovelToolBar
357
-
358
348
 
359
349
  class GuiNovelTree(QTreeWidget):
360
350
 
@@ -375,7 +365,6 @@ class GuiNovelTree(QTreeWidget):
375
365
  logger.debug("Create: GuiNovelTree")
376
366
 
377
367
  self.novelView = novelView
378
- self.mainGui = novelView.mainGui
379
368
 
380
369
  # Internal Variables
381
370
  self._treeMap = {}
@@ -490,7 +479,7 @@ class GuiNovelTree(QTreeWidget):
490
479
  if rootHandle is None:
491
480
  rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
492
481
 
493
- treeChanged = self.mainGui.projView.changedSince(self._lastBuild)
482
+ treeChanged = SHARED.mainGui.projView.changedSince(self._lastBuild)
494
483
  indexChanged = SHARED.project.index.rootChangedSince(rootHandle, self._lastBuild)
495
484
  if not (treeChanged or indexChanged or overRide):
496
485
  logger.debug("No changes have been made to the novel index")
@@ -783,5 +772,3 @@ class GuiNovelTree(QTreeWidget):
783
772
  if tags:
784
773
  lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}</b>: {tags}")
785
774
  return lines
786
-
787
- # END Class GuiNovelTree