novelWriter 2.4.2__py3-none-any.whl → 2.5b1__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 (109) hide show
  1. {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/METADATA +4 -5
  2. {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/RECORD +109 -101
  3. novelwriter/__init__.py +33 -39
  4. novelwriter/assets/i18n/project_en_GB.json +1 -0
  5. novelwriter/assets/icons/typicons_dark/icons.conf +2 -0
  6. novelwriter/assets/icons/typicons_dark/nw_font.svg +4 -0
  7. novelwriter/assets/icons/typicons_dark/nw_quote.svg +4 -0
  8. novelwriter/assets/icons/typicons_light/icons.conf +2 -0
  9. novelwriter/assets/icons/typicons_light/nw_font.svg +4 -0
  10. novelwriter/assets/icons/typicons_light/nw_quote.svg +4 -0
  11. novelwriter/assets/manual.pdf +0 -0
  12. novelwriter/assets/sample.zip +0 -0
  13. novelwriter/assets/syntax/cyberpunk_night.conf +5 -3
  14. novelwriter/assets/syntax/default_dark.conf +32 -18
  15. novelwriter/assets/syntax/default_light.conf +24 -10
  16. novelwriter/assets/syntax/dracula.conf +44 -0
  17. novelwriter/assets/syntax/grey_dark.conf +5 -4
  18. novelwriter/assets/syntax/grey_light.conf +5 -4
  19. novelwriter/assets/syntax/light_owl.conf +7 -6
  20. novelwriter/assets/syntax/night_owl.conf +7 -6
  21. novelwriter/assets/syntax/snazzy.conf +42 -0
  22. novelwriter/assets/syntax/solarized_dark.conf +4 -3
  23. novelwriter/assets/syntax/solarized_light.conf +4 -3
  24. novelwriter/assets/syntax/tango.conf +27 -11
  25. novelwriter/assets/syntax/tomorrow.conf +6 -5
  26. novelwriter/assets/syntax/tomorrow_night.conf +7 -6
  27. novelwriter/assets/syntax/tomorrow_night_blue.conf +6 -5
  28. novelwriter/assets/syntax/tomorrow_night_bright.conf +6 -5
  29. novelwriter/assets/syntax/tomorrow_night_eighties.conf +6 -5
  30. novelwriter/assets/text/credits_en.htm +4 -1
  31. novelwriter/assets/themes/cyberpunk_night.conf +2 -0
  32. novelwriter/assets/themes/default_dark.conf +1 -0
  33. novelwriter/assets/themes/default_light.conf +1 -0
  34. novelwriter/assets/themes/dracula.conf +47 -0
  35. novelwriter/assets/themes/solarized_dark.conf +1 -0
  36. novelwriter/assets/themes/solarized_light.conf +1 -0
  37. novelwriter/common.py +31 -9
  38. novelwriter/config.py +118 -84
  39. novelwriter/constants.py +40 -26
  40. novelwriter/core/buildsettings.py +63 -66
  41. novelwriter/core/coretools.py +2 -22
  42. novelwriter/core/docbuild.py +51 -40
  43. novelwriter/core/document.py +3 -5
  44. novelwriter/core/index.py +115 -45
  45. novelwriter/core/item.py +8 -19
  46. novelwriter/core/options.py +2 -4
  47. novelwriter/core/project.py +23 -57
  48. novelwriter/core/projectdata.py +1 -3
  49. novelwriter/core/projectxml.py +12 -15
  50. novelwriter/core/sessions.py +3 -5
  51. novelwriter/core/spellcheck.py +4 -9
  52. novelwriter/core/status.py +211 -164
  53. novelwriter/core/storage.py +0 -8
  54. novelwriter/core/tohtml.py +94 -100
  55. novelwriter/core/tokenizer.py +199 -112
  56. novelwriter/core/{tomd.py → tomarkdown.py} +97 -78
  57. novelwriter/core/toodt.py +212 -148
  58. novelwriter/core/toqdoc.py +403 -0
  59. novelwriter/core/tree.py +5 -7
  60. novelwriter/dialogs/about.py +3 -5
  61. novelwriter/dialogs/docmerge.py +1 -3
  62. novelwriter/dialogs/docsplit.py +1 -3
  63. novelwriter/dialogs/editlabel.py +0 -2
  64. novelwriter/dialogs/preferences.py +111 -88
  65. novelwriter/dialogs/projectsettings.py +216 -180
  66. novelwriter/dialogs/quotes.py +3 -4
  67. novelwriter/dialogs/wordlist.py +3 -9
  68. novelwriter/enum.py +31 -25
  69. novelwriter/error.py +8 -15
  70. novelwriter/extensions/circularprogress.py +5 -6
  71. novelwriter/extensions/configlayout.py +18 -18
  72. novelwriter/extensions/eventfilters.py +1 -5
  73. novelwriter/extensions/modified.py +50 -13
  74. novelwriter/extensions/novelselector.py +1 -3
  75. novelwriter/extensions/pagedsidebar.py +9 -12
  76. novelwriter/extensions/simpleprogress.py +1 -3
  77. novelwriter/extensions/statusled.py +1 -3
  78. novelwriter/extensions/switch.py +4 -6
  79. novelwriter/extensions/switchbox.py +7 -6
  80. novelwriter/extensions/versioninfo.py +3 -9
  81. novelwriter/gui/doceditor.py +132 -133
  82. novelwriter/gui/dochighlight.py +237 -183
  83. novelwriter/gui/docviewer.py +61 -97
  84. novelwriter/gui/docviewerpanel.py +3 -10
  85. novelwriter/gui/editordocument.py +1 -3
  86. novelwriter/gui/itemdetails.py +7 -11
  87. novelwriter/gui/mainmenu.py +11 -7
  88. novelwriter/gui/noveltree.py +11 -24
  89. novelwriter/gui/outline.py +11 -23
  90. novelwriter/gui/projtree.py +26 -43
  91. novelwriter/gui/search.py +1 -3
  92. novelwriter/gui/sidebar.py +2 -6
  93. novelwriter/gui/statusbar.py +6 -10
  94. novelwriter/gui/theme.py +26 -51
  95. novelwriter/guimain.py +50 -71
  96. novelwriter/shared.py +30 -15
  97. novelwriter/tools/dictionaries.py +12 -15
  98. novelwriter/tools/lipsum.py +2 -4
  99. novelwriter/tools/manusbuild.py +1 -3
  100. novelwriter/tools/manuscript.py +71 -144
  101. novelwriter/tools/manussettings.py +67 -73
  102. novelwriter/tools/noveldetails.py +6 -11
  103. novelwriter/tools/welcome.py +2 -16
  104. novelwriter/tools/writingstats.py +6 -9
  105. novelwriter/types.py +45 -3
  106. {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/LICENSE.md +0 -0
  107. {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/WHEEL +0 -0
  108. {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/entry_points.txt +0 -0
  109. {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/top_level.txt +0 -0
@@ -31,27 +31,21 @@ 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 (
35
- QCursor, QFont, QMouseEvent, QPalette, QResizeEvent, QTextCursor,
36
- QTextOption
37
- )
34
+ from PyQt5.QtGui import QCursor, QMouseEvent, QPalette, QResizeEvent, QTextCursor
38
35
  from PyQt5.QtWidgets import (
39
36
  QAction, QApplication, QFrame, QHBoxLayout, QLabel, QMenu, QTextBrowser,
40
37
  QToolButton, QWidget
41
38
  )
42
39
 
43
40
  from novelwriter import CONFIG, SHARED
44
- from novelwriter.common import cssCol
45
41
  from novelwriter.constants import nwHeaders, nwUnicode
46
- from novelwriter.core.tohtml import ToHtml
42
+ from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
47
43
  from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
48
44
  from novelwriter.error import logException
49
45
  from novelwriter.extensions.eventfilters import WheelEventFilter
50
46
  from novelwriter.extensions.modified import NIconToolButton
51
47
  from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
52
- from novelwriter.types import (
53
- QtAlignCenterTop, QtAlignJustify, QtKeepAnchor, QtMouseLeft, QtMoveAnchor
54
- )
48
+ from novelwriter.types import QtAlignCenterTop, QtKeepAnchor, QtMouseLeft, QtMoveAnchor
55
49
 
56
50
  logger = logging.getLogger(__name__)
57
51
 
@@ -72,6 +66,7 @@ class GuiDocViewer(QTextBrowser):
72
66
 
73
67
  # Internal Variables
74
68
  self._docHandle = None
69
+ self._docTheme = TextDocumentTheme()
75
70
 
76
71
  # Settings
77
72
  self.setMinimumWidth(CONFIG.pxInt(300))
@@ -140,13 +135,10 @@ class GuiDocViewer(QTextBrowser):
140
135
 
141
136
  def initViewer(self) -> None:
142
137
  """Set editor settings from main config."""
143
- self._makeStyleSheet()
144
-
145
- # Set Font
146
- font = QFont()
147
- font.setFamily(CONFIG.textFont)
148
- font.setPointSize(CONFIG.textSize)
149
- self.document().setDefaultFont(font)
138
+ # Set the font. See issues #1862 and #1875.
139
+ self.setFont(CONFIG.textFont)
140
+ self.docHeader.updateFont()
141
+ self.docFooter.updateFont()
150
142
 
151
143
  # Set the widget colours to match syntax theme
152
144
  mainPalette = self.palette()
@@ -159,16 +151,23 @@ class GuiDocViewer(QTextBrowser):
159
151
  docPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
160
152
  docPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
161
153
  self.viewport().setPalette(docPalette)
162
-
163
154
  self.docHeader.matchColours()
164
155
  self.docFooter.matchColours()
165
156
 
157
+ # Update theme colours
158
+ self._docTheme.text = SHARED.theme.colText
159
+ self._docTheme.highlight = SHARED.theme.colMark
160
+ self._docTheme.head = SHARED.theme.colHead
161
+ self._docTheme.comment = SHARED.theme.colHidden
162
+ self._docTheme.note = SHARED.theme.colNote
163
+ self._docTheme.code = SHARED.theme.colCode
164
+ self._docTheme.modifier = SHARED.theme.colMod
165
+ self._docTheme.keyword = SHARED.theme.colKey
166
+ self._docTheme.tag = SHARED.theme.colTag
167
+ self._docTheme.optional = SHARED.theme.colOpt
168
+
166
169
  # Set default text margins
167
170
  self.document().setDocumentMargin(0)
168
- options = QTextOption()
169
- if CONFIG.doJustify:
170
- options.setAlignment(QtAlignJustify)
171
- self.document().setDefaultTextOption(options)
172
171
 
173
172
  # Scroll bars
174
173
  if CONFIG.hideVScroll:
@@ -200,21 +199,22 @@ class GuiDocViewer(QTextBrowser):
200
199
  QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
201
200
 
202
201
  sPos = self.verticalScrollBar().value()
203
- aDoc = ToHtml(SHARED.project)
204
- aDoc.setPreview(True)
205
- aDoc.setKeywords(True)
206
- aDoc.setComments(CONFIG.viewComments)
207
- aDoc.setSynopsis(CONFIG.viewSynopsis)
208
- aDoc.setLinkHeadings(True)
202
+ qDoc = ToQTextDocument(SHARED.project)
203
+ qDoc.setJustify(CONFIG.doJustify)
204
+ qDoc.initDocument(CONFIG.textFont, self._docTheme)
205
+ qDoc.setKeywords(True)
206
+ qDoc.setComments(CONFIG.viewComments)
207
+ qDoc.setSynopsis(CONFIG.viewSynopsis)
209
208
 
210
209
  # Be extra careful here to prevent crashes when first opening a
211
210
  # project as a crash here leaves no way of recovering.
212
211
  # See issue #298
213
212
  try:
214
- aDoc.setText(tHandle)
215
- aDoc.doPreProcessing()
216
- aDoc.tokenizeText()
217
- aDoc.doConvert()
213
+ qDoc.setText(tHandle)
214
+ qDoc.doPreProcessing()
215
+ qDoc.tokenizeText()
216
+ qDoc.doConvert()
217
+ qDoc.appendFootnotes()
218
218
  except Exception:
219
219
  logger.error("Failed to generate preview for document with handle '%s'", tHandle)
220
220
  logException()
@@ -230,11 +230,7 @@ class GuiDocViewer(QTextBrowser):
230
230
  self.docHistory.append(tHandle)
231
231
 
232
232
  self.setDocumentTitle(tHandle)
233
-
234
- # Replace tabs before setting the HTML, and then put them back in
235
- self.setHtml(aDoc.result.replace("\t", "!!tab!!"))
236
- while self.find("!!tab!!"):
237
- self.textCursor().insertText("\t")
233
+ self.setDocument(qDoc.document)
238
234
 
239
235
  if self._docHandle == tHandle:
240
236
  # This is a refresh, so we set the scrollbar back to where it was
@@ -372,12 +368,10 @@ class GuiDocViewer(QTextBrowser):
372
368
  @pyqtSlot("QUrl")
373
369
  def _linkClicked(self, url: QUrl) -> None:
374
370
  """Process a clicked link in the document."""
375
- link = url.url()
376
- logger.debug("Clicked link: '%s'", link)
377
- if len(link) > 0:
378
- bits = link.split("=")
379
- if len(bits) == 2:
380
- self.loadDocumentTagRequest.emit(bits[1], nwDocMode.VIEW)
371
+ if link := url.url():
372
+ logger.debug("Clicked link: '%s'", link)
373
+ if (bits := link.partition("_")) and bits[2]:
374
+ self.loadDocumentTagRequest.emit(bits[2], nwDocMode.VIEW)
381
375
  return
382
376
 
383
377
  @pyqtSlot("QPoint")
@@ -467,34 +461,6 @@ class GuiDocViewer(QTextBrowser):
467
461
  self._makeSelection(selType)
468
462
  return
469
463
 
470
- def _makeStyleSheet(self) -> None:
471
- """Generate an appropriate style sheet for the document viewer,
472
- based on the current syntax highlighter theme.
473
- """
474
- colText = cssCol(SHARED.theme.colText)
475
- colHead = cssCol(SHARED.theme.colHead)
476
- colVals = cssCol(SHARED.theme.colVal)
477
- colMark = cssCol(SHARED.theme.colMark)
478
- colKeys = cssCol(SHARED.theme.colKey)
479
- colOpts = cssCol(SHARED.theme.colOpt)
480
- colHide = cssCol(SHARED.theme.colHidden)
481
- colMods = cssCol(SHARED.theme.colMod)
482
- self.document().setDefaultStyleSheet(
483
- f"body {{color: {colText};}}\n"
484
- f"h1, h2, h3, h4 {{color: {colHead};}}\n"
485
- f"a {{color: {colVals};}}\n"
486
- f"mark {{background-color: {colMark};}}\n"
487
- f".tags {{color: {colKeys};}}\n"
488
- f".optional {{color: {colOpts};}}\n"
489
- f".comment {{color: {colHide};}}\n"
490
- f".synopsis {{color: {colMods};}}\n"
491
- ".title {text-align: center;}\n"
492
- )
493
-
494
- return
495
-
496
- # END Class GuiDocViewer
497
-
498
464
 
499
465
  class GuiDocViewHistory:
500
466
 
@@ -608,15 +574,13 @@ class GuiDocViewHistory:
608
574
  )
609
575
  return
610
576
 
611
- # END Class GuiDocViewHistory
612
-
613
-
614
- # =============================================================================================== #
615
- # The Embedded Document Header
616
- # Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport
617
- # =============================================================================================== #
618
577
 
619
578
  class GuiDocViewHeader(QWidget):
579
+ """The Embedded Document Header
580
+
581
+ Only used by DocViewer, and is at a fixed position in the
582
+ QTextBrowser's viewport.
583
+ """
620
584
 
621
585
  def __init__(self, docViewer: GuiDocViewer) -> None:
622
586
  super().__init__(parent=docViewer)
@@ -646,10 +610,6 @@ class GuiDocViewHeader(QWidget):
646
610
  self.itemTitle.setAlignment(QtAlignCenterTop)
647
611
  self.itemTitle.setFixedHeight(iPx)
648
612
 
649
- lblFont = self.itemTitle.font()
650
- lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
651
- self.itemTitle.setFont(lblFont)
652
-
653
613
  # Other Widgets
654
614
  self.outlineMenu = QMenu(self)
655
615
 
@@ -700,7 +660,7 @@ class GuiDocViewHeader(QWidget):
700
660
  self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
701
661
  self.setMinimumHeight(iPx + 2*mPx)
702
662
 
703
- # Fix the Colours
663
+ self.updateFont()
704
664
  self.updateTheme()
705
665
 
706
666
  logger.debug("Ready: GuiDocViewHeader")
@@ -745,6 +705,12 @@ class GuiDocViewHeader(QWidget):
745
705
  self._docOutline = data
746
706
  return
747
707
 
708
+ def updateFont(self) -> None:
709
+ """Update the font settings."""
710
+ self.setFont(SHARED.theme.guiFont)
711
+ self.itemTitle.setFont(SHARED.theme.guiFontSmall)
712
+ return
713
+
748
714
  def updateTheme(self) -> None:
749
715
  """Update theme elements."""
750
716
  self.outlineButton.setThemeIcon("list")
@@ -832,15 +798,13 @@ class GuiDocViewHeader(QWidget):
832
798
  self.docViewer.requestProjectItemSelected.emit(self._docHandle, True)
833
799
  return
834
800
 
835
- # END Class GuiDocViewHeader
836
-
837
-
838
- # =============================================================================================== #
839
- # The Embedded Document Footer
840
- # Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport
841
- # =============================================================================================== #
842
801
 
843
802
  class GuiDocViewFooter(QWidget):
803
+ """The Embedded Document Footer
804
+
805
+ Only used by DocViewer, and is at a fixed position in the
806
+ QTextBrowser's viewport.
807
+ """
844
808
 
845
809
  def __init__(self, docViewer: GuiDocViewer) -> None:
846
810
  super().__init__(parent=docViewer)
@@ -886,11 +850,6 @@ class GuiDocViewFooter(QWidget):
886
850
  self.showSynopsis.toggled.connect(self._doToggleSynopsis)
887
851
  self.showSynopsis.setToolTip(self.tr("Show Synopsis Comments"))
888
852
 
889
- lblFont = self.font()
890
- lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
891
- self.showComments.setFont(lblFont)
892
- self.showSynopsis.setFont(lblFont)
893
-
894
853
  # Assemble Layout
895
854
  self.outerBox = QHBoxLayout()
896
855
  self.outerBox.addWidget(self.showHide, 0)
@@ -906,7 +865,7 @@ class GuiDocViewFooter(QWidget):
906
865
  self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
907
866
  self.setMinimumHeight(iPx + 2*mPx)
908
867
 
909
- # Fix the Colours
868
+ self.updateFont()
910
869
  self.updateTheme()
911
870
 
912
871
  logger.debug("Ready: GuiDocViewFooter")
@@ -917,6 +876,13 @@ class GuiDocViewFooter(QWidget):
917
876
  # Methods
918
877
  ##
919
878
 
879
+ def updateFont(self) -> None:
880
+ """Update the font settings."""
881
+ self.setFont(SHARED.theme.guiFont)
882
+ self.showComments.setFont(SHARED.theme.guiFontSmall)
883
+ self.showSynopsis.setFont(SHARED.theme.guiFontSmall)
884
+ return
885
+
920
886
  def updateTheme(self) -> None:
921
887
  """Update theme elements."""
922
888
  # Icons
@@ -964,5 +930,3 @@ class GuiDocViewFooter(QWidget):
964
930
  CONFIG.viewSynopsis = state
965
931
  self.docViewer.reloadText()
966
932
  return
967
-
968
- # 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,16 +25,16 @@ 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
37
+ from novelwriter.constants import nwConst, nwKeyWords, nwLabels, nwUnicode, trConst
38
38
  from novelwriter.enum import nwDocAction, nwDocInsert, nwView, nwWidget
39
39
  from novelwriter.extensions.eventfilters import StatusTipFilter
40
40
 
@@ -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"))
@@ -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
@@ -30,30 +30,28 @@ from __future__ import annotations
30
30
  import csv
31
31
  import logging
32
32
 
33
- from time import time
34
33
  from enum import Enum
34
+ from time import time
35
35
 
36
- from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, QT_TRANSLATE_NOOP
36
+ from PyQt5.QtCore import QT_TRANSLATE_NOOP, Qt, pyqtSignal, pyqtSlot
37
37
  from PyQt5.QtWidgets import (
38
38
  QAbstractItemView, QAction, QFileDialog, QFrame, QGridLayout, QGroupBox,
39
- QHBoxLayout, QLabel, QMenu, QScrollArea, QSizePolicy, QSplitter, QToolBar,
40
- QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
39
+ QHBoxLayout, QLabel, QMenu, QScrollArea, QSplitter, QToolBar, QToolButton,
40
+ QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
41
41
  )
42
42
 
43
43
  from novelwriter import CONFIG, SHARED
44
- from novelwriter.enum import (
45
- nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
46
- )
47
- from novelwriter.error import logException
48
44
  from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe
49
- from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels
45
+ from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
46
+ from novelwriter.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
47
+ from novelwriter.error import logException
50
48
  from novelwriter.extensions.configlayout import NColourLabel
51
49
  from novelwriter.extensions.novelselector import NovelSelector
52
50
  from novelwriter.types import (
53
- QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration, QtUserRole
51
+ QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration,
52
+ QtSizeExpanding, QtUserRole
54
53
  )
55
54
 
56
-
57
55
  logger = logging.getLogger(__name__)
58
56
 
59
57
 
@@ -196,8 +194,6 @@ class GuiOutlineView(QWidget):
196
194
  self.outlineTree.refreshTree(rootHandle=(tHandle or None), overRide=True)
197
195
  return
198
196
 
199
- # END Class GuiOutlineView
200
-
201
197
 
202
198
  class GuiOutlineToolBar(QToolBar):
203
199
 
@@ -215,7 +211,7 @@ class GuiOutlineToolBar(QToolBar):
215
211
  self.setContentsMargins(0, 0, 0, 0)
216
212
 
217
213
  stretch = QWidget(self)
218
- stretch.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
214
+ stretch.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
219
215
 
220
216
  # Novel Selector
221
217
  self.novelLabel = NColourLabel(
@@ -311,8 +307,6 @@ class GuiOutlineToolBar(QToolBar):
311
307
  self.outlineExportRequest.emit()
312
308
  return
313
309
 
314
- # END Class GuiOutlineToolBar
315
-
316
310
 
317
311
  class GuiOutlineTree(QTreeWidget):
318
312
 
@@ -728,8 +722,6 @@ class GuiOutlineTree(QTreeWidget):
728
722
 
729
723
  return
730
724
 
731
- # END Class GuiOutlineTree
732
-
733
725
 
734
726
  class GuiOutlineHeaderMenu(QMenu):
735
727
 
@@ -772,8 +764,6 @@ class GuiOutlineHeaderMenu(QMenu):
772
764
 
773
765
  return
774
766
 
775
- # END Class GuiOutlineHeaderMenu
776
-
777
767
 
778
768
  class GuiOutlineDetails(QScrollArea):
779
769
 
@@ -1048,7 +1038,7 @@ class GuiOutlineDetails(QScrollArea):
1048
1038
  self.titleLabel.setText(self.tr(self.LVL_MAP.get(novIdx.level, "H1")))
1049
1039
  self.titleValue.setText(novIdx.title)
1050
1040
 
1051
- itemStatus, _ = nwItem.getImportStatus(incIcon=False)
1041
+ itemStatus, _ = nwItem.getImportStatus()
1052
1042
 
1053
1043
  self.fileValue.setText(nwItem.itemName)
1054
1044
  self.itemValue.setText(itemStatus)
@@ -1104,5 +1094,3 @@ class GuiOutlineDetails(QScrollArea):
1104
1094
  return ", ".join(
1105
1095
  [f"<a href='{tag}'>{tag}</a>" for tag in refs.get(key, [])]
1106
1096
  )
1107
-
1108
- # END Class GuiOutlineDetails