novelWriter 2.2rc1__py3-none-any.whl → 2.3b1__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 (153) hide show
  1. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +141 -129
  3. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +11 -6
  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_zh_CN.qm +0 -0
  13. novelwriter/assets/i18n/project_de_DE.json +1 -0
  14. novelwriter/assets/i18n/project_en_US.json +1 -0
  15. novelwriter/assets/i18n/project_es_419.json +11 -0
  16. novelwriter/assets/i18n/project_fr_FR.json +11 -0
  17. novelwriter/assets/i18n/project_it_IT.json +11 -0
  18. novelwriter/assets/i18n/project_ja_JP.json +2 -1
  19. novelwriter/assets/i18n/project_nb_NO.json +1 -0
  20. novelwriter/assets/i18n/project_zh_CN.json +11 -0
  21. novelwriter/assets/icons/typicons_dark/icons.conf +9 -2
  22. novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
  23. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
  24. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
  25. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
  26. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
  27. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
  28. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
  29. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
  30. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
  31. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
  32. novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
  33. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
  34. novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
  35. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
  36. novelwriter/assets/icons/typicons_light/icons.conf +9 -2
  37. novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
  38. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
  39. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
  40. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
  41. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
  42. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
  43. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
  44. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
  45. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
  46. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
  47. novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
  48. novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
  49. novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
  50. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
  51. novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
  52. novelwriter/assets/images/novelwriter-text-light.svg +4 -0
  53. novelwriter/assets/images/welcome-dark.jpg +0 -0
  54. novelwriter/assets/images/welcome-light.jpg +0 -0
  55. novelwriter/assets/manual.pdf +0 -0
  56. novelwriter/assets/sample.zip +0 -0
  57. novelwriter/assets/syntax/default_dark.conf +1 -0
  58. novelwriter/assets/syntax/default_light.conf +1 -0
  59. novelwriter/assets/syntax/grey_dark.conf +1 -0
  60. novelwriter/assets/syntax/grey_light.conf +1 -0
  61. novelwriter/assets/syntax/light_owl.conf +1 -0
  62. novelwriter/assets/syntax/night_owl.conf +1 -0
  63. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  64. novelwriter/assets/syntax/solarized_light.conf +1 -0
  65. novelwriter/assets/syntax/tomorrow.conf +1 -0
  66. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  67. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  68. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  69. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  70. novelwriter/assets/text/credits_en.htm +4 -2
  71. novelwriter/assets/themes/default_dark.conf +2 -2
  72. novelwriter/assets/themes/default_light.conf +2 -2
  73. novelwriter/common.py +64 -66
  74. novelwriter/config.py +39 -44
  75. novelwriter/constants.py +39 -17
  76. novelwriter/core/buildsettings.py +8 -8
  77. novelwriter/core/coretools.py +194 -155
  78. novelwriter/core/docbuild.py +7 -4
  79. novelwriter/core/document.py +7 -7
  80. novelwriter/core/index.py +90 -57
  81. novelwriter/core/item.py +23 -5
  82. novelwriter/core/options.py +11 -10
  83. novelwriter/core/project.py +72 -47
  84. novelwriter/core/projectdata.py +3 -16
  85. novelwriter/core/projectxml.py +14 -42
  86. novelwriter/core/sessions.py +4 -3
  87. novelwriter/core/spellcheck.py +6 -4
  88. novelwriter/core/status.py +5 -4
  89. novelwriter/core/storage.py +179 -141
  90. novelwriter/core/tohtml.py +6 -4
  91. novelwriter/core/tokenizer.py +74 -46
  92. novelwriter/core/tomd.py +2 -2
  93. novelwriter/core/toodt.py +41 -31
  94. novelwriter/core/tree.py +5 -4
  95. novelwriter/dialogs/about.py +88 -179
  96. novelwriter/dialogs/docmerge.py +30 -20
  97. novelwriter/dialogs/docsplit.py +33 -22
  98. novelwriter/dialogs/editlabel.py +20 -8
  99. novelwriter/dialogs/preferences.py +562 -725
  100. novelwriter/dialogs/{projsettings.py → projectsettings.py} +301 -270
  101. novelwriter/dialogs/quotes.py +47 -36
  102. novelwriter/dialogs/wordlist.py +128 -59
  103. novelwriter/enum.py +25 -22
  104. novelwriter/error.py +2 -2
  105. novelwriter/extensions/circularprogress.py +12 -12
  106. novelwriter/extensions/configlayout.py +185 -146
  107. novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
  108. novelwriter/extensions/modified.py +81 -0
  109. novelwriter/extensions/novelselector.py +27 -13
  110. novelwriter/extensions/pagedsidebar.py +15 -20
  111. novelwriter/extensions/simpleprogress.py +8 -9
  112. novelwriter/extensions/statusled.py +9 -9
  113. novelwriter/extensions/switch.py +32 -64
  114. novelwriter/extensions/switchbox.py +2 -7
  115. novelwriter/extensions/versioninfo.py +153 -0
  116. novelwriter/gui/doceditor.py +250 -214
  117. novelwriter/gui/dochighlight.py +66 -94
  118. novelwriter/gui/docviewer.py +71 -98
  119. novelwriter/gui/docviewerpanel.py +140 -47
  120. novelwriter/gui/editordocument.py +3 -3
  121. novelwriter/gui/itemdetails.py +9 -9
  122. novelwriter/gui/mainmenu.py +47 -46
  123. novelwriter/gui/noveltree.py +53 -61
  124. novelwriter/gui/outline.py +100 -76
  125. novelwriter/gui/projtree.py +193 -67
  126. novelwriter/gui/sidebar.py +9 -8
  127. novelwriter/gui/statusbar.py +49 -7
  128. novelwriter/gui/theme.py +65 -74
  129. novelwriter/guimain.py +173 -330
  130. novelwriter/shared.py +68 -30
  131. novelwriter/tools/dictionaries.py +7 -8
  132. novelwriter/tools/lipsum.py +34 -28
  133. novelwriter/tools/manusbuild.py +3 -4
  134. novelwriter/tools/manuscript.py +25 -32
  135. novelwriter/tools/manussettings.py +194 -225
  136. novelwriter/tools/noveldetails.py +525 -0
  137. novelwriter/tools/welcome.py +802 -0
  138. novelwriter/tools/writingstats.py +26 -13
  139. novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
  140. novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
  141. novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
  142. novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
  143. novelwriter/assets/images/wizard-back.jpg +0 -0
  144. novelwriter/assets/text/gplv3_en.htm +0 -641
  145. novelwriter/assets/text/release_notes.htm +0 -17
  146. novelwriter/dialogs/projdetails.py +0 -525
  147. novelwriter/dialogs/projload.py +0 -298
  148. novelwriter/dialogs/updates.py +0 -182
  149. novelwriter/extensions/pageddialog.py +0 -130
  150. novelwriter/tools/projwizard.py +0 -478
  151. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
  152. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
  153. {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2023-11-14 [2.2rc1] GuiDocViewerPanel
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2023, Veronica Berglyd Olsen
9
+ Copyright 2018–2024, Veronica Berglyd Olsen
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,8 +29,8 @@ from enum import Enum
29
29
 
30
30
  from PyQt5.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot
31
31
  from PyQt5.QtWidgets import (
32
- QAbstractItemView, QFrame, QHeaderView, QTabWidget, QTreeWidget,
33
- QTreeWidgetItem, QVBoxLayout, QWidget
32
+ QAbstractItemView, QFrame, QHeaderView, QMenu, QTabWidget, QToolButton,
33
+ QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
34
34
  )
35
35
 
36
36
  from novelwriter import CONFIG, SHARED
@@ -54,10 +54,24 @@ class GuiDocViewerPanel(QWidget):
54
54
 
55
55
  self._lastHandle = None
56
56
 
57
+ iPx = int(1.0*SHARED.theme.baseIconSize)
58
+
57
59
  self.tabBackRefs = _ViewPanelBackRefs(self)
58
60
 
61
+ self.optsMenu = QMenu(self)
62
+
63
+ self.aInactive = self.optsMenu.addAction(self.tr("Hide Inactive Tags"))
64
+ self.aInactive.setCheckable(True)
65
+ self.aInactive.toggled.connect(self._toggleHideInactive)
66
+
67
+ self.optsButton = QToolButton(self)
68
+ self.optsButton.setIconSize(QSize(iPx, iPx))
69
+ self.optsButton.setMenu(self.optsMenu)
70
+ self.optsButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
71
+
59
72
  self.mainTabs = QTabWidget(self)
60
- self.mainTabs.addTab(self.tabBackRefs, self.tr("Backreferences"))
73
+ self.mainTabs.addTab(self.tabBackRefs, self.tr("References"))
74
+ self.mainTabs.setCornerWidget(self.optsButton, Qt.Corner.TopLeftCorner)
61
75
 
62
76
  self.kwTabs: dict[str, _ViewPanelKeyWords] = {}
63
77
  self.idTabs: dict[str, int] = {}
@@ -73,7 +87,7 @@ class GuiDocViewerPanel(QWidget):
73
87
  self.outerBox.setContentsMargins(0, 0, 0, 0)
74
88
 
75
89
  self.setLayout(self.outerBox)
76
- self.updateTheme()
90
+ self.updateTheme(updateTabs=False)
77
91
 
78
92
  logger.debug("Ready: GuiDocViewerPanel")
79
93
 
@@ -83,42 +97,57 @@ class GuiDocViewerPanel(QWidget):
83
97
  # Methods
84
98
  ##
85
99
 
86
- def updateTheme(self) -> None:
100
+ def updateTheme(self, updateTabs: bool = True) -> None:
87
101
  """Update theme elements."""
102
+ qPalette = self.palette()
103
+ mPx = CONFIG.pxInt(2)
88
104
  vPx = CONFIG.pxInt(4)
89
- lPx = CONFIG.pxInt(2)
90
- rPx = CONFIG.pxInt(14)
91
- hCol = self.palette().highlight().color()
105
+ hPx = CONFIG.pxInt(8)
106
+ hCol = qPalette.highlight().color()
107
+ fCol = qPalette.text().color()
108
+
109
+ buttonStyle = (
110
+ "QToolButton {{padding: {0}px; margin: 0 0 {1}px 0; border: none; "
111
+ "background: transparent;}} "
112
+ "QToolButton:hover {{border: none; background: rgba({2}, {3}, {4}, 0.2);}} "
113
+ "QToolButton::menu-indicator {{image: none;}} "
114
+ ).format(mPx, mPx, fCol.red(), fCol.green(), fCol.blue())
115
+ self.optsButton.setIcon(SHARED.theme.getIcon("menu"))
116
+ self.optsButton.setStyleSheet(buttonStyle)
92
117
 
93
118
  styleSheet = (
94
- "QTabWidget::pane {border: 0;} "
95
- "QTabWidget QTabBar::tab {"
96
- f"border: 0; padding: {vPx}px {rPx}px {vPx}px {lPx}px;"
97
- "} "
98
- "QTabWidget QTabBar::tab:selected {"
99
- f"color: rgb({hCol.red()}, {hCol.green()}, {hCol.blue()});"
100
- "} "
101
- )
119
+ "QTabWidget::pane {{border: 0;}} "
120
+ "QTabWidget QTabBar::tab {{border: 0; padding: {0}px {1}px;}} "
121
+ "QTabWidget QTabBar::tab:selected {{color: rgb({2}, {3}, {4});}} "
122
+ ).format(vPx, hPx, hCol.red(), hCol.green(), hCol.blue())
102
123
  self.mainTabs.setStyleSheet(styleSheet)
103
124
  self.updateHandle(self._lastHandle)
104
125
 
126
+ if updateTabs:
127
+ self.tabBackRefs.updateTheme()
128
+ for tab in self.kwTabs.values():
129
+ tab.updateTheme()
130
+
105
131
  return
106
132
 
107
133
  def openProjectTasks(self) -> None:
108
134
  """Run open project tasks."""
109
- widths = SHARED.project.options.getValue("GuiDocViewerPanel", "colWidths", {})
110
- if isinstance(widths, dict):
111
- for key, value in widths.items():
135
+ colWidths = SHARED.project.options.getValue("GuiDocViewerPanel", "colWidths", {})
136
+ hideInactive = SHARED.project.options.getBool("GuiDocViewerPanel", "hideInactive", False)
137
+ self.aInactive.setChecked(hideInactive)
138
+ if isinstance(colWidths, dict):
139
+ for key, value in colWidths.items():
112
140
  if key in self.kwTabs and isinstance(value, list):
113
141
  self.kwTabs[key].setColumnWidths(value)
114
142
  return
115
143
 
116
144
  def closeProjectTasks(self) -> None:
117
145
  """Run close project tasks."""
118
- widths = {}
119
- for key, tab in self.kwTabs.items():
120
- widths[key] = tab.getColumnWidths()
121
- SHARED.project.options.setValue("GuiDocViewerPanel", "colWidths", widths)
146
+ logger.debug("Saving State: GuiDocViewerPanel")
147
+ colWidths = {k: t.getColumnWidths() for k, t in self.kwTabs.items()}
148
+ hideInactive = self.aInactive.isChecked()
149
+ SHARED.project.options.setValue("GuiDocViewerPanel", "colWidths", colWidths)
150
+ SHARED.project.options.setValue("GuiDocViewerPanel", "hideInactive", hideInactive)
122
151
  return
123
152
 
124
153
  ##
@@ -136,9 +165,7 @@ class GuiDocViewerPanel(QWidget):
136
165
  @pyqtSlot()
137
166
  def indexHasAppeared(self) -> None:
138
167
  """Handle event when the index has appeared."""
139
- for key, name, tClass, iItem, hItem in SHARED.project.index.getTagsData():
140
- if tClass in self.kwTabs and iItem and hItem:
141
- self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
168
+ self._loadAllTags()
142
169
  self._updateTabVisibility()
143
170
  self.updateHandle(self._lastHandle)
144
171
  return
@@ -147,10 +174,15 @@ class GuiDocViewerPanel(QWidget):
147
174
  def projectItemChanged(self, tHandle: str) -> None:
148
175
  """Update meta data for project item."""
149
176
  self.tabBackRefs.refreshDocument(tHandle)
177
+ activeOnly = self.aInactive.isChecked()
150
178
  for key in SHARED.project.index.getDocumentTags(tHandle):
151
179
  name, tClass, iItem, hItem = SHARED.project.index.getSingleTag(key)
152
180
  if tClass in self.kwTabs and iItem and hItem:
153
- self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
181
+ if not activeOnly or (iItem and iItem.item.isActive):
182
+ self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
183
+ else:
184
+ self.kwTabs[tClass].removeEntry(key)
185
+ self._updateTabVisibility()
154
186
  return
155
187
 
156
188
  @pyqtSlot(str)
@@ -176,6 +208,20 @@ class GuiDocViewerPanel(QWidget):
176
208
  self._updateTabVisibility()
177
209
  return
178
210
 
211
+ ##
212
+ # Private Slots
213
+ ##
214
+
215
+ @pyqtSlot(bool)
216
+ def _toggleHideInactive(self, state: bool) -> None:
217
+ """Process toggling of active/inactive visibility."""
218
+ logger.debug("Setting inactive items to %s", "hidden" if state else "visible")
219
+ for cTab in self.kwTabs.values():
220
+ cTab.clearContent()
221
+ self._loadAllTags()
222
+ self._updateTabVisibility()
223
+ return
224
+
179
225
  ##
180
226
  # Internal Functions
181
227
  ##
@@ -187,6 +233,14 @@ class GuiDocViewerPanel(QWidget):
187
233
  self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
188
234
  return
189
235
 
236
+ def _loadAllTags(self) -> None:
237
+ """Load all tags into the tabs."""
238
+ data = SHARED.project.index.getTagsData(activeOnly=self.aInactive.isChecked())
239
+ for key, name, tClass, iItem, hItem in data:
240
+ if tClass in self.kwTabs and iItem and hItem:
241
+ self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
242
+ return
243
+
190
244
  # END Class GuiDocViewerPanel
191
245
 
192
246
 
@@ -218,12 +272,14 @@ class _ViewPanelBackRefs(QTreeWidget):
218
272
  # Set Header Sizes
219
273
  treeHeader = self.header()
220
274
  treeHeader.setStretchLastSection(True)
275
+ treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1627
221
276
  treeHeader.setSectionResizeMode(self.C_DOC, QHeaderView.ResizeMode.ResizeToContents)
222
277
  treeHeader.setSectionResizeMode(self.C_EDIT, QHeaderView.ResizeMode.Fixed)
223
278
  treeHeader.setSectionResizeMode(self.C_VIEW, QHeaderView.ResizeMode.Fixed)
224
279
  treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.ResizeMode.ResizeToContents)
225
280
  treeHeader.resizeSection(self.C_EDIT, iPx + cMg)
226
281
  treeHeader.resizeSection(self.C_VIEW, iPx + cMg)
282
+ treeHeader.setSectionsMovable(False)
227
283
 
228
284
  # Cache Icons Locally
229
285
  self._editIcon = SHARED.theme.getIcon("edit")
@@ -235,6 +291,16 @@ class _ViewPanelBackRefs(QTreeWidget):
235
291
 
236
292
  return
237
293
 
294
+ def updateTheme(self) -> None:
295
+ """Update theme elements."""
296
+ self._editIcon = SHARED.theme.getIcon("edit")
297
+ self._viewIcon = SHARED.theme.getIcon("view")
298
+ for i in range(self.topLevelItemCount()):
299
+ if item := self.topLevelItem(i):
300
+ item.setIcon(self.C_EDIT, self._editIcon)
301
+ item.setIcon(self.C_VIEW, self._viewIcon)
302
+ return
303
+
238
304
  def clearContent(self) -> None:
239
305
  """Clear the widget."""
240
306
  self.clear()
@@ -276,7 +342,7 @@ class _ViewPanelBackRefs(QTreeWidget):
276
342
  def _treeItemDoubleClicked(self, index: QModelIndex) -> None:
277
343
  """Emit follow tag signal on user double click."""
278
344
  tHandle = index.siblingAtColumn(self.C_DATA).data(self.D_HANDLE)
279
- if index.column() == self.C_DOC:
345
+ if index.column() not in (self.C_EDIT, self.C_VIEW):
280
346
  self._parent.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, "", True)
281
347
  return
282
348
 
@@ -299,10 +365,12 @@ class _ViewPanelBackRefs(QTreeWidget):
299
365
 
300
366
  trItem.setIcon(self.C_DOC, docIcon)
301
367
  trItem.setText(self.C_DOC, nwItem.itemName)
368
+ trItem.setToolTip(self.C_DOC, nwItem.itemName)
302
369
  trItem.setIcon(self.C_EDIT, self._editIcon)
303
370
  trItem.setIcon(self.C_VIEW, self._viewIcon)
304
- trItem.setText(self.C_TITLE, hItem.title)
305
371
  trItem.setData(self.C_TITLE, Qt.ItemDataRole.DecorationRole, hDec)
372
+ trItem.setText(self.C_TITLE, hItem.title)
373
+ trItem.setToolTip(self.C_TITLE, hItem.title)
306
374
  trItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
307
375
 
308
376
  if tKey not in self._treeMap:
@@ -316,13 +384,14 @@ class _ViewPanelBackRefs(QTreeWidget):
316
384
 
317
385
  class _ViewPanelKeyWords(QTreeWidget):
318
386
 
319
- C_DATA = 0
320
- C_NAME = 0
321
- C_EDIT = 1
322
- C_VIEW = 2
323
- C_DOC = 3
324
- C_TITLE = 4
325
- C_SHORT = 5
387
+ C_DATA = 0
388
+ C_NAME = 0
389
+ C_EDIT = 1
390
+ C_VIEW = 2
391
+ C_IMPORT = 3
392
+ C_DOC = 4
393
+ C_TITLE = 5
394
+ C_SHORT = 6
326
395
 
327
396
  D_TAG = Qt.ItemDataRole.UserRole
328
397
 
@@ -330,13 +399,14 @@ class _ViewPanelKeyWords(QTreeWidget):
330
399
  super().__init__(parent=parent)
331
400
 
332
401
  self._parent = parent
402
+ self._class = itemClass
333
403
  self._treeMap: dict[str, QTreeWidgetItem] = {}
334
404
 
335
405
  iPx = SHARED.theme.baseIconSize
336
406
  cMg = CONFIG.pxInt(6)
337
407
 
338
408
  self.setHeaderLabels([
339
- self.tr("Tag"), "", "", self.tr("Document"),
409
+ self.tr("Tag"), "", "", self.tr("Importance"), self.tr("Document"),
340
410
  self.tr("Heading"), self.tr("Short Description")
341
411
  ])
342
412
  self.setIndentation(0)
@@ -351,7 +421,7 @@ class _ViewPanelKeyWords(QTreeWidget):
351
421
  # Set Header Sizes
352
422
  treeHeader = self.header()
353
423
  treeHeader.setStretchLastSection(True)
354
- treeHeader.setSectionResizeMode(self.C_NAME, QHeaderView.ResizeMode.ResizeToContents)
424
+ treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1627
355
425
  treeHeader.setSectionResizeMode(self.C_EDIT, QHeaderView.ResizeMode.Fixed)
356
426
  treeHeader.setSectionResizeMode(self.C_VIEW, QHeaderView.ResizeMode.Fixed)
357
427
  treeHeader.resizeSection(self.C_EDIT, iPx + cMg)
@@ -369,6 +439,17 @@ class _ViewPanelKeyWords(QTreeWidget):
369
439
 
370
440
  return
371
441
 
442
+ def updateTheme(self) -> None:
443
+ """Update theme elements."""
444
+ self._classIcon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[self._class])
445
+ self._editIcon = SHARED.theme.getIcon("edit")
446
+ self._viewIcon = SHARED.theme.getIcon("view")
447
+ for i in range(self.topLevelItemCount()):
448
+ if item := self.topLevelItem(i):
449
+ item.setIcon(self.C_EDIT, self._editIcon)
450
+ item.setIcon(self.C_VIEW, self._viewIcon)
451
+ return
452
+
372
453
  def countEntries(self) -> int:
373
454
  """Return the number of items in the list."""
374
455
  return self.topLevelItemCount()
@@ -386,22 +467,30 @@ class _ViewPanelKeyWords(QTreeWidget):
386
467
  nwItem.itemType, nwItem.itemClass,
387
468
  nwItem.itemLayout, nwItem.mainHeading
388
469
  )
470
+ impLabel, impIcon = nwItem.getImportStatus(incIcon=True)
389
471
  iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
390
472
  hDec = SHARED.theme.getHeaderDecorationNarrow(iLevel)
391
473
 
392
- # This can not use a get call to the dictionary as that creates
393
- # some weird issue with Qt, so we need to do this with an if
474
+ # This can not use a get call to the dictionary as that would create an
475
+ # instance of the QTreeWidgetItem, which has some weird side effects
394
476
  trItem = self._treeMap[tag] if tag in self._treeMap else QTreeWidgetItem()
395
477
 
396
- trItem.setText(self.C_NAME, name)
397
478
  trItem.setIcon(self.C_NAME, self._classIcon)
479
+ trItem.setText(self.C_NAME, name)
480
+ trItem.setToolTip(self.C_NAME, name)
398
481
  trItem.setIcon(self.C_EDIT, self._editIcon)
399
482
  trItem.setIcon(self.C_VIEW, self._viewIcon)
483
+ trItem.setIcon(self.C_IMPORT, impIcon)
484
+ trItem.setText(self.C_IMPORT, impLabel)
485
+ trItem.setToolTip(self.C_IMPORT, impLabel)
400
486
  trItem.setIcon(self.C_DOC, docIcon)
401
487
  trItem.setText(self.C_DOC, nwItem.itemName)
402
- trItem.setText(self.C_TITLE, hItem.title)
488
+ trItem.setToolTip(self.C_DOC, nwItem.itemName)
403
489
  trItem.setData(self.C_TITLE, Qt.ItemDataRole.DecorationRole, hDec)
490
+ trItem.setText(self.C_TITLE, hItem.title)
491
+ trItem.setToolTip(self.C_TITLE, hItem.title)
404
492
  trItem.setText(self.C_SHORT, hItem.synopsis)
493
+ trItem.setToolTip(self.C_SHORT, hItem.synopsis)
405
494
  trItem.setData(self.C_DATA, self.D_TAG, tag)
406
495
 
407
496
  if tag not in self._treeMap:
@@ -420,14 +509,18 @@ class _ViewPanelKeyWords(QTreeWidget):
420
509
 
421
510
  def setColumnWidths(self, widths: list[int]) -> None:
422
511
  """Set the column widths."""
423
- if isinstance(widths, list) and len(widths) >= 2:
424
- self.setColumnWidth(self.C_DOC, CONFIG.pxInt(checkInt(widths[0], 100)))
425
- self.setColumnWidth(self.C_TITLE, CONFIG.pxInt(checkInt(widths[1], 100)))
512
+ if isinstance(widths, list) and len(widths) >= 4:
513
+ self.setColumnWidth(self.C_NAME, CONFIG.pxInt(checkInt(widths[0], 100)))
514
+ self.setColumnWidth(self.C_IMPORT, CONFIG.pxInt(checkInt(widths[1], 100)))
515
+ self.setColumnWidth(self.C_DOC, CONFIG.pxInt(checkInt(widths[2], 100)))
516
+ self.setColumnWidth(self.C_TITLE, CONFIG.pxInt(checkInt(widths[3], 100)))
426
517
  return
427
518
 
428
519
  def getColumnWidths(self) -> list[int]:
429
520
  """Get the widths of the user-adjustable columns."""
430
521
  return [
522
+ CONFIG.rpxInt(self.columnWidth(self.C_NAME)),
523
+ CONFIG.rpxInt(self.columnWidth(self.C_IMPORT)),
431
524
  CONFIG.rpxInt(self.columnWidth(self.C_DOC)),
432
525
  CONFIG.rpxInt(self.columnWidth(self.C_TITLE)),
433
526
  ]
@@ -450,7 +543,7 @@ class _ViewPanelKeyWords(QTreeWidget):
450
543
  def _treeItemDoubleClicked(self, index: QModelIndex) -> None:
451
544
  """Emit follow tag signal on user double click."""
452
545
  tag = index.siblingAtColumn(self.C_DATA).data(self.D_TAG)
453
- if index.column() == self.C_NAME:
546
+ if index.column() not in (self.C_EDIT, self.C_VIEW):
454
547
  self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.VIEW)
455
548
  return
456
549
 
@@ -3,10 +3,10 @@ novelWriter – GUI Text Document
3
3
  ===============================
4
4
 
5
5
  File History:
6
- Created: 2023-09-07 [2.2b1]
6
+ Created: 2023-09-07 [2.2b1] GuiTextDocument
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2023, Veronica Berglyd Olsen
9
+ Copyright 2018–2024, Veronica Berglyd Olsen
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
@@ -50,7 +50,7 @@ class GuiTextDocument(QTextDocument):
50
50
 
51
51
  return
52
52
 
53
- def __del__(self): # pragma: no cover
53
+ def __del__(self) -> None: # pragma: no cover
54
54
  logger.debug("Delete: GuiTextDocument")
55
55
  return
56
56
 
@@ -3,10 +3,10 @@ novelWriter – GUI Item Details Panel
3
3
  ====================================
4
4
 
5
5
  File History:
6
- Created: 2019-04-24 [0.0.1]
6
+ Created: 2019-04-24 [0.0.1] GuiItemDetails
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2023, Veronica Berglyd Olsen
9
+ Copyright 2018–2024, Veronica Berglyd Olsen
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
@@ -239,9 +239,9 @@ class GuiItemDetails(QWidget):
239
239
  # Label
240
240
  # =====
241
241
 
242
- theLabel = nwItem.itemName
243
- if len(theLabel) > 100:
244
- theLabel = theLabel[:96].rstrip()+" ..."
242
+ label = nwItem.itemName
243
+ if len(label) > 100:
244
+ label = label[:96].rstrip()+" ..."
245
245
 
246
246
  if nwItem.isFileType():
247
247
  if nwItem.isActive:
@@ -251,14 +251,14 @@ class GuiItemDetails(QWidget):
251
251
  else:
252
252
  self.labelIcon.setPixmap(SHARED.theme.getPixmap("noncheckable", (iPx, iPx)))
253
253
 
254
- self.labelData.setText(theLabel)
254
+ self.labelData.setText(label)
255
255
 
256
256
  # Status
257
257
  # ======
258
258
 
259
- theStatus, theIcon = nwItem.getImportStatus(incIcon=True)
260
- self.statusIcon.setPixmap(theIcon.pixmap(iPx, iPx))
261
- self.statusData.setText(theStatus)
259
+ status, icon = nwItem.getImportStatus(incIcon=True)
260
+ self.statusIcon.setPixmap(icon.pixmap(iPx, iPx))
261
+ self.statusData.setText(status)
262
262
 
263
263
  # Class
264
264
  # =====
@@ -3,10 +3,10 @@ novelWriter – GUI Main Menu
3
3
  ===========================
4
4
 
5
5
  File History:
6
- Created: 2019-04-27 [0.0.1]
6
+ Created: 2019-04-27 [0.0.1] GuiMainMenu
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2023, Veronica Berglyd Olsen
9
+ Copyright 2018–2024, Veronica Berglyd Olsen
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
@@ -36,6 +36,7 @@ from novelwriter import CONFIG, SHARED
36
36
  from novelwriter.enum import nwDocAction, nwDocInsert, nwWidget
37
37
  from novelwriter.common import openExternalPath
38
38
  from novelwriter.constants import nwConst, trConst, nwKeyWords, nwLabels, nwUnicode
39
+ from novelwriter.extensions.eventfilters import StatusTipFilter
39
40
 
40
41
  if TYPE_CHECKING: # pragma: no cover
41
42
  from novelwriter.guimain import GuiMain
@@ -73,6 +74,8 @@ class GuiMainMenu(QMenuBar):
73
74
  self._buildToolsMenu()
74
75
  self._buildHelpMenu()
75
76
 
77
+ self.installEventFilter(StatusTipFilter(mainGui))
78
+
76
79
  logger.debug("Ready: GuiMainMenu")
77
80
 
78
81
  return
@@ -129,14 +132,10 @@ class GuiMainMenu(QMenuBar):
129
132
  # Project
130
133
  self.projMenu = self.addMenu(self.tr("&Project"))
131
134
 
132
- # Project > New Project
133
- self.aNewProject = self.projMenu.addAction(self.tr("New Project"))
134
- self.aNewProject.triggered.connect(lambda: self.mainGui.newProject(None))
135
-
136
- # Project > Open Project
137
- self.aOpenProject = self.projMenu.addAction(self.tr("Open Project"))
135
+ # Project > Create or Open Project
136
+ self.aOpenProject = self.projMenu.addAction(self.tr("Create or Open Project"))
138
137
  self.aOpenProject.setShortcut("Ctrl+Shift+O")
139
- self.aOpenProject.triggered.connect(lambda: self.mainGui.showProjectLoadDialog())
138
+ self.aOpenProject.triggered.connect(self.mainGui.showWelcomeDialog)
140
139
 
141
140
  # Project > Save Project
142
141
  self.aSaveProject = self.projMenu.addAction(self.tr("Save Project"))
@@ -154,12 +153,12 @@ class GuiMainMenu(QMenuBar):
154
153
  # Project > Project Settings
155
154
  self.aProjectSettings = self.projMenu.addAction(self.tr("Project Settings"))
156
155
  self.aProjectSettings.setShortcut("Ctrl+Shift+,")
157
- self.aProjectSettings.triggered.connect(lambda: self.mainGui.showProjectSettingsDialog())
156
+ self.aProjectSettings.triggered.connect(self.mainGui.showProjectSettingsDialog)
158
157
 
159
- # Project > Project Details
160
- self.aProjectDetails = self.projMenu.addAction(self.tr("Project Details"))
161
- self.aProjectDetails.setShortcut("Shift+F6")
162
- self.aProjectDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
158
+ # Project > Novel Details
159
+ self.aNovelDetails = self.projMenu.addAction(self.tr("Novel Details"))
160
+ self.aNovelDetails.setShortcut("Shift+F6")
161
+ self.aNovelDetails.triggered.connect(self.mainGui.showNovelDetailsDialog)
163
162
 
164
163
  # Project > Separator
165
164
  self.projMenu.addSeparator()
@@ -566,7 +565,7 @@ class GuiMainMenu(QMenuBar):
566
565
 
567
566
  # Insert > Short Description Comment
568
567
  self.aInsShort = self.mInsComments.addAction(self.tr("Short Description Comment"))
569
- self.aInsShort.setShortcut("Ctrl+K, U")
568
+ self.aInsShort.setShortcut("Ctrl+K, H")
570
569
  self.aInsShort.triggered.connect(
571
570
  lambda: self.requestDocInsert.emit(nwDocInsert.SHORT)
572
571
  )
@@ -593,8 +592,10 @@ class GuiMainMenu(QMenuBar):
593
592
  )
594
593
 
595
594
  # Insert > Placeholder Text
596
- self.aLipsumText = self.mInsBreaks.addAction(self.tr("Placeholder Text"))
597
- self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog())
595
+ self.aLipsumText = self.insMenu.addAction(self.tr("Placeholder Text"))
596
+ self.aLipsumText.triggered.connect(
597
+ lambda: self.requestDocInsert.emit(nwDocInsert.LIPSUM)
598
+ )
598
599
 
599
600
  return
600
601
 
@@ -603,25 +604,25 @@ class GuiMainMenu(QMenuBar):
603
604
  # Format
604
605
  self.fmtMenu = self.addMenu(self.tr("&Format"))
605
606
 
606
- # Format > Emphasis
607
- self.aFmtEmph = self.fmtMenu.addAction(self.tr("Emphasis"))
608
- self.aFmtEmph.setShortcut("Ctrl+I")
609
- self.aFmtEmph.triggered.connect(
610
- lambda: self.requestDocAction.emit(nwDocAction.EMPH)
607
+ # Format > Bold
608
+ self.aFmtBold = self.fmtMenu.addAction(self.tr("Bold"))
609
+ self.aFmtBold.setShortcut("Ctrl+B")
610
+ self.aFmtBold.triggered.connect(
611
+ lambda: self.requestDocAction.emit(nwDocAction.MD_BOLD)
611
612
  )
612
613
 
613
- # Format > Strong Emphasis
614
- self.aFmtStrong = self.fmtMenu.addAction(self.tr("Strong Emphasis"))
615
- self.aFmtStrong.setShortcut("Ctrl+B")
616
- self.aFmtStrong.triggered.connect(
617
- lambda: self.requestDocAction.emit(nwDocAction.STRONG)
614
+ # Format > Italic
615
+ self.aFmtItalic = self.fmtMenu.addAction(self.tr("Italic"))
616
+ self.aFmtItalic.setShortcut("Ctrl+I")
617
+ self.aFmtItalic.triggered.connect(
618
+ lambda: self.requestDocAction.emit(nwDocAction.MD_ITALIC)
618
619
  )
619
620
 
620
621
  # Format > Strikethrough
621
622
  self.aFmtStrike = self.fmtMenu.addAction(self.tr("Strikethrough"))
622
623
  self.aFmtStrike.setShortcut("Ctrl+D")
623
624
  self.aFmtStrike.triggered.connect(
624
- lambda: self.requestDocAction.emit(nwDocAction.STRIKE)
625
+ lambda: self.requestDocAction.emit(nwDocAction.MD_STRIKE)
625
626
  )
626
627
 
627
628
  # Edit > Separator
@@ -647,18 +648,18 @@ class GuiMainMenu(QMenuBar):
647
648
  # Shortcodes
648
649
  self.mShortcodes = self.fmtMenu.addMenu(self.tr("More Formats ..."))
649
650
 
650
- # Shortcode Italic
651
- self.aScItalic = self.mShortcodes.addAction(self.tr("Italics (Shortcode)"))
652
- self.aScItalic.triggered.connect(
653
- lambda: self.requestDocAction.emit(nwDocAction.SC_ITALIC)
654
- )
655
-
656
651
  # Shortcode Bold
657
652
  self.aScBold = self.mShortcodes.addAction(self.tr("Bold (Shortcode)"))
658
653
  self.aScBold.triggered.connect(
659
654
  lambda: self.requestDocAction.emit(nwDocAction.SC_BOLD)
660
655
  )
661
656
 
657
+ # Shortcode Italic
658
+ self.aScItalic = self.mShortcodes.addAction(self.tr("Italics (Shortcode)"))
659
+ self.aScItalic.triggered.connect(
660
+ lambda: self.requestDocAction.emit(nwDocAction.SC_ITALIC)
661
+ )
662
+
662
663
  # Shortcode Strikethrough
663
664
  self.aScStrike = self.mShortcodes.addAction(self.tr("Strikethrough (Shortcode)"))
664
665
  self.aScStrike.triggered.connect(
@@ -780,6 +781,13 @@ class GuiMainMenu(QMenuBar):
780
781
  lambda: self.requestDocAction.emit(nwDocAction.BLOCK_COM)
781
782
  )
782
783
 
784
+ # Format > Ignore Text
785
+ self.aFmtIgnore = self.fmtMenu.addAction(self.tr("Toggle Ignore Text"))
786
+ self.aFmtIgnore.setShortcut("Ctrl+Shift+D")
787
+ self.aFmtIgnore.triggered.connect(
788
+ lambda: self.requestDocAction.emit(nwDocAction.BLOCK_IGN)
789
+ )
790
+
783
791
  # Format > Remove Block Format
784
792
  self.aFmtNoFormat = self.fmtMenu.addAction(self.tr("Remove Block Format"))
785
793
  self.aFmtNoFormat.setShortcuts(["Ctrl+0", "Ctrl+Shift+/"])
@@ -872,7 +880,7 @@ class GuiMainMenu(QMenuBar):
872
880
 
873
881
  # Tools > Project Word List
874
882
  self.aEditWordList = self.toolsMenu.addAction(self.tr("Project Word List"))
875
- self.aEditWordList.triggered.connect(lambda: self.mainGui.showProjectWordListDialog())
883
+ self.aEditWordList.triggered.connect(self.mainGui.showProjectWordListDialog)
876
884
 
877
885
  # Tools > Add Dictionaries
878
886
  if CONFIG.osWindows or CONFIG.isDebug:
@@ -902,13 +910,13 @@ class GuiMainMenu(QMenuBar):
902
910
  # Tools > Writing Statistics
903
911
  self.aWritingStats = self.toolsMenu.addAction(self.tr("Writing Statistics"))
904
912
  self.aWritingStats.setShortcut("F6")
905
- self.aWritingStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
913
+ self.aWritingStats.triggered.connect(self.mainGui.showWritingStatsDialog)
906
914
 
907
915
  # Tools > Preferences
908
916
  self.aPreferences = self.toolsMenu.addAction(self.tr("Preferences"))
909
917
  self.aPreferences.setShortcut("Ctrl+,")
910
918
  self.aPreferences.setMenuRole(QAction.PreferencesRole)
911
- self.aPreferences.triggered.connect(lambda: self.mainGui.showPreferencesDialog())
919
+ self.aPreferences.triggered.connect(self.mainGui.showPreferencesDialog)
912
920
 
913
921
  return
914
922
 
@@ -920,12 +928,12 @@ class GuiMainMenu(QMenuBar):
920
928
  # Help > About
921
929
  self.aAboutNW = self.helpMenu.addAction(self.tr("About novelWriter"))
922
930
  self.aAboutNW.setMenuRole(QAction.AboutRole)
923
- self.aAboutNW.triggered.connect(lambda: self.mainGui.showAboutNWDialog())
931
+ self.aAboutNW.triggered.connect(self.mainGui.showAboutNWDialog)
924
932
 
925
933
  # Help > About Qt5
926
934
  self.aAboutQt = self.helpMenu.addAction(self.tr("About Qt5"))
927
935
  self.aAboutQt.setMenuRole(QAction.AboutQtRole)
928
- self.aAboutQt.triggered.connect(lambda: self.mainGui.showAboutQtDialog())
936
+ self.aAboutQt.triggered.connect(self.mainGui.showAboutQtDialog)
929
937
 
930
938
  # Help > Separator
931
939
  self.helpMenu.addSeparator()
@@ -956,13 +964,6 @@ class GuiMainMenu(QMenuBar):
956
964
  self.aWebsite = self.helpMenu.addAction(self.tr("The novelWriter Website"))
957
965
  self.aWebsite.triggered.connect(lambda: self._openWebsite(nwConst.URL_WEB))
958
966
 
959
- # Help > Separator
960
- self.helpMenu.addSeparator()
961
-
962
- # Document > Check for Updates
963
- self.aUpdates = self.helpMenu.addAction(self.tr("Check for New Release"))
964
- self.aUpdates.triggered.connect(lambda: self.mainGui.showUpdatesDialog())
965
-
966
967
  return
967
968
 
968
969
  # END Class GuiMainMenu