novelWriter 2.2rc1__py3-none-any.whl → 2.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/RECORD +113 -111
  3. {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +10 -5
  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 +3 -2
  22. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
  23. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
  24. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
  25. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
  26. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
  27. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
  28. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
  29. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
  30. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
  31. novelwriter/assets/icons/typicons_light/icons.conf +3 -2
  32. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
  33. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
  34. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
  35. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
  36. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
  37. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
  38. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
  39. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
  40. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
  41. novelwriter/assets/manual.pdf +0 -0
  42. novelwriter/assets/sample.zip +0 -0
  43. novelwriter/assets/text/release_notes.htm +50 -7
  44. novelwriter/common.py +16 -29
  45. novelwriter/config.py +3 -3
  46. novelwriter/constants.py +1 -1
  47. novelwriter/core/buildsettings.py +1 -1
  48. novelwriter/core/coretools.py +2 -1
  49. novelwriter/core/docbuild.py +1 -1
  50. novelwriter/core/document.py +1 -1
  51. novelwriter/core/index.py +2 -2
  52. novelwriter/core/item.py +2 -2
  53. novelwriter/core/options.py +3 -3
  54. novelwriter/core/project.py +3 -3
  55. novelwriter/core/projectdata.py +2 -2
  56. novelwriter/core/projectxml.py +1 -1
  57. novelwriter/core/sessions.py +2 -2
  58. novelwriter/core/spellcheck.py +4 -3
  59. novelwriter/core/status.py +3 -3
  60. novelwriter/core/storage.py +1 -1
  61. novelwriter/core/tohtml.py +2 -2
  62. novelwriter/core/tokenizer.py +1 -1
  63. novelwriter/core/tomd.py +2 -2
  64. novelwriter/core/toodt.py +1 -1
  65. novelwriter/core/tree.py +2 -2
  66. novelwriter/dialogs/about.py +30 -31
  67. novelwriter/dialogs/docmerge.py +24 -15
  68. novelwriter/dialogs/docsplit.py +27 -16
  69. novelwriter/dialogs/editlabel.py +19 -7
  70. novelwriter/dialogs/preferences.py +44 -57
  71. novelwriter/dialogs/projdetails.py +29 -36
  72. novelwriter/dialogs/projload.py +32 -36
  73. novelwriter/dialogs/projsettings.py +20 -15
  74. novelwriter/dialogs/quotes.py +32 -25
  75. novelwriter/dialogs/updates.py +4 -14
  76. novelwriter/dialogs/wordlist.py +34 -21
  77. novelwriter/enum.py +5 -4
  78. novelwriter/error.py +1 -1
  79. novelwriter/extensions/circularprogress.py +1 -1
  80. novelwriter/extensions/configlayout.py +3 -15
  81. novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
  82. novelwriter/extensions/novelselector.py +1 -1
  83. novelwriter/extensions/pageddialog.py +1 -1
  84. novelwriter/extensions/pagedsidebar.py +2 -5
  85. novelwriter/extensions/simpleprogress.py +8 -9
  86. novelwriter/extensions/statusled.py +1 -1
  87. novelwriter/extensions/switch.py +4 -4
  88. novelwriter/extensions/switchbox.py +1 -6
  89. novelwriter/gui/doceditor.py +72 -64
  90. novelwriter/gui/dochighlight.py +3 -2
  91. novelwriter/gui/docviewer.py +22 -47
  92. novelwriter/gui/docviewerpanel.py +68 -23
  93. novelwriter/gui/editordocument.py +3 -3
  94. novelwriter/gui/itemdetails.py +2 -2
  95. novelwriter/gui/mainmenu.py +35 -30
  96. novelwriter/gui/noveltree.py +44 -53
  97. novelwriter/gui/outline.py +2 -1
  98. novelwriter/gui/projtree.py +5 -6
  99. novelwriter/gui/sidebar.py +6 -4
  100. novelwriter/gui/statusbar.py +47 -4
  101. novelwriter/gui/theme.py +5 -6
  102. novelwriter/guimain.py +139 -189
  103. novelwriter/shared.py +53 -29
  104. novelwriter/tools/dictionaries.py +2 -2
  105. novelwriter/tools/lipsum.py +34 -28
  106. novelwriter/tools/manusbuild.py +3 -4
  107. novelwriter/tools/manuscript.py +19 -26
  108. novelwriter/tools/manussettings.py +2 -4
  109. novelwriter/tools/projwizard.py +3 -3
  110. novelwriter/tools/writingstats.py +17 -4
  111. novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
  112. novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
  113. novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
  114. novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
  115. {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/LICENSE.md +0 -0
  116. {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/entry_points.txt +0 -0
  117. {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/top_level.txt +0 -0
@@ -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
@@ -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
@@ -154,12 +157,12 @@ class GuiMainMenu(QMenuBar):
154
157
  # Project > Project Settings
155
158
  self.aProjectSettings = self.projMenu.addAction(self.tr("Project Settings"))
156
159
  self.aProjectSettings.setShortcut("Ctrl+Shift+,")
157
- self.aProjectSettings.triggered.connect(lambda: self.mainGui.showProjectSettingsDialog())
160
+ self.aProjectSettings.triggered.connect(self.mainGui.showProjectSettingsDialog)
158
161
 
159
162
  # Project > Project Details
160
163
  self.aProjectDetails = self.projMenu.addAction(self.tr("Project Details"))
161
164
  self.aProjectDetails.setShortcut("Shift+F6")
162
- self.aProjectDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
165
+ self.aProjectDetails.triggered.connect(self.mainGui.showProjectDetailsDialog)
163
166
 
164
167
  # Project > Separator
165
168
  self.projMenu.addSeparator()
@@ -566,7 +569,7 @@ class GuiMainMenu(QMenuBar):
566
569
 
567
570
  # Insert > Short Description Comment
568
571
  self.aInsShort = self.mInsComments.addAction(self.tr("Short Description Comment"))
569
- self.aInsShort.setShortcut("Ctrl+K, U")
572
+ self.aInsShort.setShortcut("Ctrl+K, H")
570
573
  self.aInsShort.triggered.connect(
571
574
  lambda: self.requestDocInsert.emit(nwDocInsert.SHORT)
572
575
  )
@@ -593,8 +596,10 @@ class GuiMainMenu(QMenuBar):
593
596
  )
594
597
 
595
598
  # Insert > Placeholder Text
596
- self.aLipsumText = self.mInsBreaks.addAction(self.tr("Placeholder Text"))
597
- self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog())
599
+ self.aLipsumText = self.insMenu.addAction(self.tr("Placeholder Text"))
600
+ self.aLipsumText.triggered.connect(
601
+ lambda: self.requestDocInsert.emit(nwDocInsert.LIPSUM)
602
+ )
598
603
 
599
604
  return
600
605
 
@@ -603,25 +608,25 @@ class GuiMainMenu(QMenuBar):
603
608
  # Format
604
609
  self.fmtMenu = self.addMenu(self.tr("&Format"))
605
610
 
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)
611
+ # Format > Bold
612
+ self.aFmtBold = self.fmtMenu.addAction(self.tr("Bold"))
613
+ self.aFmtBold.setShortcut("Ctrl+B")
614
+ self.aFmtBold.triggered.connect(
615
+ lambda: self.requestDocAction.emit(nwDocAction.MD_BOLD)
611
616
  )
612
617
 
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)
618
+ # Format > Italic
619
+ self.aFmtItalic = self.fmtMenu.addAction(self.tr("Italic"))
620
+ self.aFmtItalic.setShortcut("Ctrl+I")
621
+ self.aFmtItalic.triggered.connect(
622
+ lambda: self.requestDocAction.emit(nwDocAction.MD_ITALIC)
618
623
  )
619
624
 
620
625
  # Format > Strikethrough
621
626
  self.aFmtStrike = self.fmtMenu.addAction(self.tr("Strikethrough"))
622
627
  self.aFmtStrike.setShortcut("Ctrl+D")
623
628
  self.aFmtStrike.triggered.connect(
624
- lambda: self.requestDocAction.emit(nwDocAction.STRIKE)
629
+ lambda: self.requestDocAction.emit(nwDocAction.MD_STRIKE)
625
630
  )
626
631
 
627
632
  # Edit > Separator
@@ -647,18 +652,18 @@ class GuiMainMenu(QMenuBar):
647
652
  # Shortcodes
648
653
  self.mShortcodes = self.fmtMenu.addMenu(self.tr("More Formats ..."))
649
654
 
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
655
  # Shortcode Bold
657
656
  self.aScBold = self.mShortcodes.addAction(self.tr("Bold (Shortcode)"))
658
657
  self.aScBold.triggered.connect(
659
658
  lambda: self.requestDocAction.emit(nwDocAction.SC_BOLD)
660
659
  )
661
660
 
661
+ # Shortcode Italic
662
+ self.aScItalic = self.mShortcodes.addAction(self.tr("Italics (Shortcode)"))
663
+ self.aScItalic.triggered.connect(
664
+ lambda: self.requestDocAction.emit(nwDocAction.SC_ITALIC)
665
+ )
666
+
662
667
  # Shortcode Strikethrough
663
668
  self.aScStrike = self.mShortcodes.addAction(self.tr("Strikethrough (Shortcode)"))
664
669
  self.aScStrike.triggered.connect(
@@ -872,7 +877,7 @@ class GuiMainMenu(QMenuBar):
872
877
 
873
878
  # Tools > Project Word List
874
879
  self.aEditWordList = self.toolsMenu.addAction(self.tr("Project Word List"))
875
- self.aEditWordList.triggered.connect(lambda: self.mainGui.showProjectWordListDialog())
880
+ self.aEditWordList.triggered.connect(self.mainGui.showProjectWordListDialog)
876
881
 
877
882
  # Tools > Add Dictionaries
878
883
  if CONFIG.osWindows or CONFIG.isDebug:
@@ -902,13 +907,13 @@ class GuiMainMenu(QMenuBar):
902
907
  # Tools > Writing Statistics
903
908
  self.aWritingStats = self.toolsMenu.addAction(self.tr("Writing Statistics"))
904
909
  self.aWritingStats.setShortcut("F6")
905
- self.aWritingStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
910
+ self.aWritingStats.triggered.connect(self.mainGui.showWritingStatsDialog)
906
911
 
907
912
  # Tools > Preferences
908
913
  self.aPreferences = self.toolsMenu.addAction(self.tr("Preferences"))
909
914
  self.aPreferences.setShortcut("Ctrl+,")
910
915
  self.aPreferences.setMenuRole(QAction.PreferencesRole)
911
- self.aPreferences.triggered.connect(lambda: self.mainGui.showPreferencesDialog())
916
+ self.aPreferences.triggered.connect(self.mainGui.showPreferencesDialog)
912
917
 
913
918
  return
914
919
 
@@ -920,12 +925,12 @@ class GuiMainMenu(QMenuBar):
920
925
  # Help > About
921
926
  self.aAboutNW = self.helpMenu.addAction(self.tr("About novelWriter"))
922
927
  self.aAboutNW.setMenuRole(QAction.AboutRole)
923
- self.aAboutNW.triggered.connect(lambda: self.mainGui.showAboutNWDialog())
928
+ self.aAboutNW.triggered.connect(self.mainGui.showAboutNWDialog)
924
929
 
925
930
  # Help > About Qt5
926
931
  self.aAboutQt = self.helpMenu.addAction(self.tr("About Qt5"))
927
932
  self.aAboutQt.setMenuRole(QAction.AboutQtRole)
928
- self.aAboutQt.triggered.connect(lambda: self.mainGui.showAboutQtDialog())
933
+ self.aAboutQt.triggered.connect(self.mainGui.showAboutQtDialog)
929
934
 
930
935
  # Help > Separator
931
936
  self.helpMenu.addSeparator()
@@ -961,7 +966,7 @@ class GuiMainMenu(QMenuBar):
961
966
 
962
967
  # Document > Check for Updates
963
968
  self.aUpdates = self.helpMenu.addAction(self.tr("Check for New Release"))
964
- self.aUpdates.triggered.connect(lambda: self.mainGui.showUpdatesDialog())
969
+ self.aUpdates.triggered.connect(self.mainGui.showUpdatesDialog)
965
970
 
966
971
  return
967
972
 
@@ -8,7 +8,7 @@ Created: 2022-06-12 [2.0rc1] GuiNovelView
8
8
  Created: 2022-06-12 [2.0rc1] GuiNovelToolBar
9
9
 
10
10
  This file is a part of novelWriter
11
- Copyright 2018–2020, Veronica Berglyd Olsen
11
+ Copyright 2018–2024, Veronica Berglyd Olsen
12
12
 
13
13
  This program is free software: you can redistribute it and/or modify
14
14
  it under the terms of the GNU General Public License as published by
@@ -145,6 +145,7 @@ class GuiNovelView(QWidget):
145
145
  """Run closing project tasks."""
146
146
  lastColType = self.novelTree.lastColType
147
147
  lastColSize = self.novelTree.lastColSize
148
+ logger.debug("Saving State: GuiNovelView")
148
149
  pOptions = SHARED.project.options
149
150
  pOptions.setValue("GuiNovelView", "lastCol", lastColType)
150
151
  pOptions.setValue("GuiNovelView", "lastColSize", lastColSize)
@@ -205,18 +206,18 @@ class GuiNovelToolBar(QWidget):
205
206
 
206
207
  # Novel Selector
207
208
  selFont = self.font()
208
- selFont.setWeight(QFont.Bold)
209
+ selFont.setWeight(QFont.Weight.Bold)
209
210
  self.novelPrefix = self.tr("Outline of {0}")
210
211
  self.novelValue = NovelSelector(self)
211
212
  self.novelValue.setFont(selFont)
212
213
  self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
213
- self.novelValue.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
214
+ self.novelValue.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
214
215
  self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
215
216
 
216
217
  self.tbNovel = QToolButton(self)
217
218
  self.tbNovel.setToolTip(self.tr("Novel Root"))
218
219
  self.tbNovel.setIconSize(QSize(iPx, iPx))
219
- self.tbNovel.clicked.connect(self._openNovelSelector)
220
+ self.tbNovel.clicked.connect(self.novelValue.showPopup)
220
221
 
221
222
  # Refresh Button
222
223
  self.tbRefresh = QToolButton(self)
@@ -243,7 +244,7 @@ class GuiNovelToolBar(QWidget):
243
244
  self.tbMore.setToolTip(self.tr("More Options"))
244
245
  self.tbMore.setIconSize(QSize(iPx, iPx))
245
246
  self.tbMore.setMenu(self.mMore)
246
- self.tbMore.setPopupMode(QToolButton.InstantPopup)
247
+ self.tbMore.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
247
248
 
248
249
  # Assemble
249
250
  self.outerBox = QHBoxLayout()
@@ -274,7 +275,7 @@ class GuiNovelToolBar(QWidget):
274
275
  self.tbMore.setIcon(SHARED.theme.getIcon("menu"))
275
276
 
276
277
  qPalette = self.palette()
277
- qPalette.setBrush(QPalette.Window, qPalette.base())
278
+ qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
278
279
  self.setPalette(qPalette)
279
280
 
280
281
  # StyleSheets
@@ -326,12 +327,6 @@ class GuiNovelToolBar(QWidget):
326
327
  # Private Slots
327
328
  ##
328
329
 
329
- @pyqtSlot()
330
- def _openNovelSelector(self) -> None:
331
- """Trigger the dropdown list of the novel selector."""
332
- self.novelValue.showPopup()
333
- return
334
-
335
330
  @pyqtSlot()
336
331
  def _refreshNovelTree(self) -> None:
337
332
  """Rebuild the current tree."""
@@ -407,14 +402,14 @@ class GuiNovelTree(QTreeWidget):
407
402
  cMg = CONFIG.pxInt(6)
408
403
 
409
404
  self.setIconSize(QSize(iPx, iPx))
410
- self.setFrameStyle(QFrame.NoFrame)
405
+ self.setFrameStyle(QFrame.Shape.NoFrame)
411
406
  self.setUniformRowHeights(True)
412
407
  self.setAllColumnsShowFocus(True)
413
408
  self.setHeaderHidden(True)
414
409
  self.setIndentation(0)
415
410
  self.setColumnCount(4)
416
- self.setSelectionBehavior(QAbstractItemView.SelectRows)
417
- self.setSelectionMode(QAbstractItemView.SingleSelection)
411
+ self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
412
+ self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
418
413
  self.setExpandsOnDoubleClick(False)
419
414
  self.setDragEnabled(False)
420
415
 
@@ -422,10 +417,10 @@ class GuiNovelTree(QTreeWidget):
422
417
  treeHeader = self.header()
423
418
  treeHeader.setStretchLastSection(False)
424
419
  treeHeader.setMinimumSectionSize(iPx + cMg)
425
- treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.Stretch)
426
- treeHeader.setSectionResizeMode(self.C_WORDS, QHeaderView.ResizeToContents)
427
- treeHeader.setSectionResizeMode(self.C_EXTRA, QHeaderView.ResizeToContents)
428
- treeHeader.setSectionResizeMode(self.C_MORE, QHeaderView.ResizeToContents)
420
+ treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.ResizeMode.Stretch)
421
+ treeHeader.setSectionResizeMode(self.C_WORDS, QHeaderView.ResizeMode.ResizeToContents)
422
+ treeHeader.setSectionResizeMode(self.C_EXTRA, QHeaderView.ResizeMode.ResizeToContents)
423
+ treeHeader.setSectionResizeMode(self.C_MORE, QHeaderView.ResizeMode.ResizeToContents)
429
424
 
430
425
  # Pre-Generate Tree Formatting
431
426
  fH1 = self.font()
@@ -454,14 +449,14 @@ class GuiNovelTree(QTreeWidget):
454
449
  """Set or update tree widget settings."""
455
450
  # Scroll bars
456
451
  if CONFIG.hideVScroll:
457
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
452
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
458
453
  else:
459
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
454
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
460
455
 
461
456
  if CONFIG.hideHScroll:
462
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
457
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
463
458
  else:
464
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
459
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
465
460
 
466
461
  return
467
462
 
@@ -521,21 +516,16 @@ class GuiNovelTree(QTreeWidget):
521
516
 
522
517
  def refreshHandle(self, tHandle: str) -> None:
523
518
  """Refresh the data for a given handle."""
524
- idxData = SHARED.project.index.getItemData(tHandle)
525
- if idxData is None:
526
- return
527
-
528
- logger.debug("Refreshing meta data for item '%s'", tHandle)
529
- for sTitle, tHeading in idxData.items():
530
- sKey = f"{tHandle}:{sTitle}"
531
- trItem = self._treeMap.get(sKey, None)
532
- if trItem is None:
533
- logger.debug("Heading '%s' not in novel tree", sKey)
534
- self.refreshTree()
535
- return
536
-
537
- self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)
538
-
519
+ if idxData := SHARED.project.index.getItemData(tHandle):
520
+ logger.debug("Refreshing meta data for item '%s'", tHandle)
521
+ for sTitle, tHeading in idxData.items():
522
+ sKey = f"{tHandle}:{sTitle}"
523
+ if trItem := self._treeMap.get(sKey, None):
524
+ self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)
525
+ else:
526
+ logger.debug("Heading '%s' not in novel tree", sKey)
527
+ self.refreshTree()
528
+ return
539
529
  return
540
530
 
541
531
  def getSelectedHandle(self) -> tuple[str | None, str | None]:
@@ -566,27 +556,25 @@ class GuiNovelTree(QTreeWidget):
566
556
  self._lastColSize = minmax(colSize, 15, 75)/100.0
567
557
  return
568
558
 
569
- def setActiveHandle(self, tHandle: str | None) -> None:
559
+ def setActiveHandle(self, tHandle: str | None, doScroll: bool = False) -> None:
570
560
  """Highlight the rows associated with a given handle."""
571
- tStart = time()
572
-
561
+ didScroll = False
573
562
  self._actHandle = tHandle
574
563
  for i in range(self.topLevelItemCount()):
575
- tItem = self.topLevelItem(i)
576
- if tItem is not None:
564
+ if tItem := self.topLevelItem(i):
577
565
  if tItem.data(self.C_DATA, self.D_HANDLE) == tHandle:
578
566
  tItem.setBackground(self.C_TITLE, self.palette().alternateBase())
579
567
  tItem.setBackground(self.C_WORDS, self.palette().alternateBase())
580
568
  tItem.setBackground(self.C_EXTRA, self.palette().alternateBase())
581
569
  tItem.setBackground(self.C_MORE, self.palette().alternateBase())
570
+ if doScroll and not didScroll:
571
+ self.scrollToItem(tItem, QAbstractItemView.ScrollHint.PositionAtCenter)
572
+ didScroll = True
582
573
  else:
583
574
  tItem.setBackground(self.C_TITLE, self.palette().base())
584
575
  tItem.setBackground(self.C_WORDS, self.palette().base())
585
576
  tItem.setBackground(self.C_EXTRA, self.palette().base())
586
577
  tItem.setBackground(self.C_MORE, self.palette().base())
587
-
588
- logger.debug("Highlighted Novel Tree in %.3f ms", (time() - tStart)*1000)
589
-
590
578
  return
591
579
 
592
580
  ##
@@ -600,12 +588,12 @@ class GuiNovelTree(QTreeWidget):
600
588
  """
601
589
  super().mousePressEvent(event)
602
590
 
603
- if event.button() == Qt.LeftButton:
591
+ if event.button() == Qt.MouseButton.LeftButton:
604
592
  selItem = self.indexAt(event.pos())
605
593
  if not selItem.isValid():
606
594
  self.clearSelection()
607
595
 
608
- elif event.button() == Qt.MiddleButton:
596
+ elif event.button() == Qt.MouseButton.MiddleButton:
609
597
  selItem = self.itemAt(event.pos())
610
598
  if not isinstance(selItem, QTreeWidgetItem):
611
599
  return
@@ -636,7 +624,10 @@ class GuiNovelTree(QTreeWidget):
636
624
  trItem = self.topLevelItem(i)
637
625
  if isinstance(trItem, QTreeWidgetItem):
638
626
  lastText = trItem.data(self.C_DATA, self.D_EXTRA)
639
- trItem.setText(self.C_EXTRA, fMetric.elidedText(lastText, Qt.ElideRight, eliW))
627
+ trItem.setText(
628
+ self.C_EXTRA,
629
+ fMetric.elidedText(lastText, Qt.TextElideMode.ElideRight, eliW)
630
+ )
640
631
  return
641
632
 
642
633
  ##
@@ -692,7 +683,7 @@ class GuiNovelTree(QTreeWidget):
692
683
  newItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
693
684
  newItem.setData(self.C_DATA, self.D_TITLE, sTitle)
694
685
  newItem.setData(self.C_DATA, self.D_KEY, tKey)
695
- newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
686
+ newItem.setTextAlignment(self.C_WORDS, Qt.AlignmentFlag.AlignRight)
696
687
 
697
688
  self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle)
698
689
  self._treeMap[tKey] = newItem
@@ -711,16 +702,16 @@ class GuiNovelTree(QTreeWidget):
711
702
  iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0)
712
703
  hDec = SHARED.theme.getHeaderDecoration(iLevel)
713
704
 
714
- trItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
705
+ trItem.setData(self.C_TITLE, Qt.ItemDataRole.DecorationRole, hDec)
715
706
  trItem.setText(self.C_TITLE, idxItem.title)
716
707
  trItem.setFont(self.C_TITLE, self._hFonts[iLevel])
717
708
  trItem.setText(self.C_WORDS, f"{idxItem.wordCount:n}")
718
- trItem.setData(self.C_MORE, Qt.DecorationRole, self._pMore)
709
+ trItem.setData(self.C_MORE, Qt.ItemDataRole.DecorationRole, self._pMore)
719
710
 
720
711
  # Custom column
721
712
  mW = int(self._lastColSize * self.viewport().width())
722
713
  lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
723
- elideText = self.fontMetrics().elidedText(lastText, Qt.ElideRight, mW)
714
+ elideText = self.fontMetrics().elidedText(lastText, Qt.TextElideMode.ElideRight, mW)
724
715
  trItem.setText(self.C_EXTRA, elideText)
725
716
  trItem.setData(self.C_DATA, self.D_EXTRA, lastText)
726
717
  trItem.setToolTip(self.C_EXTRA, toolTip)
@@ -10,7 +10,7 @@ Created: 2019-11-16 [0.4.1] GuiOutlineHeaderMenu
10
10
  Created: 2020-06-02 [0.7] GuiOutlineDetails
11
11
 
12
12
  This file is a part of novelWriter
13
- Copyright 2018–2023, Veronica Berglyd Olsen
13
+ Copyright 2018–2024, Veronica Berglyd Olsen
14
14
 
15
15
  This program is free software: you can redistribute it and/or modify
16
16
  it under the terms of the GNU General Public License as published by
@@ -606,6 +606,7 @@ class GuiOutlineTree(QTreeWidget):
606
606
  logHidden, orgWidth if logHidden and logWidth == 0 else logWidth
607
607
  ]
608
608
 
609
+ logger.debug("Saving State: GuiOutline")
609
610
  pOptions = SHARED.project.options
610
611
  pOptions.setValue("GuiOutline", "columnState", colState)
611
612
  pOptions.saveSettings()
@@ -6,9 +6,10 @@ File History:
6
6
  Created: 2018-09-29 [0.0.1] GuiProjectTree
7
7
  Created: 2022-06-06 [2.0rc1] GuiProjectView
8
8
  Created: 2022-06-06 [2.0rc1] GuiProjectToolBar
9
+ Created: 2023-11-22 [2.2rc1] _TreeContextMenu
9
10
 
10
11
  This file is a part of novelWriter
11
- Copyright 2018–2023, Veronica Berglyd Olsen
12
+ Copyright 2018–2024, Veronica Berglyd Olsen
12
13
 
13
14
  This program is free software: you can redistribute it and/or modify
14
15
  it under the terms of the GNU General Public License as published by
@@ -40,6 +41,7 @@ from PyQt5.QtWidgets import (
40
41
  )
41
42
 
42
43
  from novelwriter import CONFIG, SHARED
44
+ from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout
43
45
  from novelwriter.common import minmax
44
46
  from novelwriter.constants import nwHeaders, nwUnicode, trConst, nwLabels
45
47
  from novelwriter.core.item import NWItem
@@ -48,9 +50,6 @@ from novelwriter.dialogs.docmerge import GuiDocMerge
48
50
  from novelwriter.dialogs.docsplit import GuiDocSplit
49
51
  from novelwriter.dialogs.editlabel import GuiEditLabel
50
52
  from novelwriter.dialogs.projsettings import GuiProjectSettings
51
- from novelwriter.enum import (
52
- nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwWidget
53
- )
54
53
 
55
54
  if TYPE_CHECKING: # pragma: no cover
56
55
  from novelwriter.guimain import GuiMain
@@ -681,7 +680,7 @@ class GuiProjectTree(QTreeWidget):
681
680
 
682
681
  # Add the new item to the project tree
683
682
  self.revealNewTreeItem(tHandle, nHandle=nHandle, wordCount=True)
684
- self.mainGui.switchFocus(nwWidget.TREE)
683
+ self.projView.setTreeFocus() # See issue #1376
685
684
 
686
685
  return True
687
686
 
@@ -1650,7 +1649,7 @@ class _TreeContextMenu(QMenu):
1650
1649
 
1651
1650
  return
1652
1651
 
1653
- def __del__(self): # pragma: no cover
1652
+ def __del__(self) -> None: # pragma: no cover
1654
1653
  logger.debug("Delete: _TreeContextMenu")
1655
1654
  return
1656
1655
 
@@ -3,10 +3,10 @@ novelWriter – GUI Main Window SideBar
3
3
  =====================================
4
4
 
5
5
  File History:
6
- Created: 2022-05-10 [2.0rc1]
6
+ Created: 2022-05-10 [2.0rc1] GuiSideBar
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
@@ -27,12 +27,13 @@ import logging
27
27
 
28
28
  from typing import TYPE_CHECKING
29
29
 
30
- from PyQt5.QtCore import QEvent, QPoint, Qt, QSize, pyqtSignal
31
30
  from PyQt5.QtGui import QPalette
31
+ from PyQt5.QtCore import QEvent, QPoint, Qt, QSize, pyqtSignal
32
32
  from PyQt5.QtWidgets import QMenu, QToolButton, QVBoxLayout, QWidget
33
33
 
34
34
  from novelwriter import CONFIG, SHARED
35
35
  from novelwriter.enum import nwView
36
+ from novelwriter.extensions.eventfilters import StatusTipFilter
36
37
 
37
38
  if TYPE_CHECKING: # pragma: no cover
38
39
  from novelwriter.guimain import GuiMain
@@ -54,6 +55,7 @@ class GuiSideBar(QWidget):
54
55
  iPx = CONFIG.pxInt(24)
55
56
  iconSize = QSize(iPx, iPx)
56
57
  self.setContentsMargins(0, 0, 0, 0)
58
+ self.installEventFilter(StatusTipFilter(mainGui))
57
59
 
58
60
  # Buttons
59
61
  self.tbProject = QToolButton(self)
@@ -162,7 +164,7 @@ class GuiSideBar(QWidget):
162
164
 
163
165
  class _PopRightMenu(QMenu):
164
166
 
165
- def event(self, event: QEvent):
167
+ def event(self, event: QEvent) -> bool:
166
168
  """Overload the show event and move the menu popup location."""
167
169
  if event.type() == QEvent.Show:
168
170
  parent = self.parent()
@@ -3,10 +3,10 @@ novelWriter – GUI Main Window Status Bar
3
3
  ========================================
4
4
 
5
5
  File History:
6
- Created: 2019-04-20 [0.0.1]
6
+ Created: 2019-04-20 [0.0.1] GuiMainStatus
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
@@ -27,9 +27,10 @@ import logging
27
27
 
28
28
  from time import time
29
29
  from typing import TYPE_CHECKING, Literal
30
+ from datetime import datetime
30
31
 
31
- from PyQt5.QtCore import pyqtSlot, QLocale
32
32
  from PyQt5.QtGui import QColor
33
+ from PyQt5.QtCore import pyqtSlot, QLocale
33
34
  from PyQt5.QtWidgets import qApp, QStatusBar, QLabel
34
35
 
35
36
  from novelwriter import CONFIG, SHARED
@@ -50,8 +51,9 @@ class GuiMainStatus(QStatusBar):
50
51
 
51
52
  logger.debug("Create: GuiMainStatus")
52
53
 
53
- self._refTime = -1.0
54
+ self._refTime = -1.0
54
55
  self._userIdle = False
56
+ self._debugInfo = False
55
57
 
56
58
  colNone = QColor(*SHARED.theme.statNone)
57
59
  colSaved = QColor(*SHARED.theme.statSaved)
@@ -223,4 +225,45 @@ class GuiMainStatus(QStatusBar):
223
225
  self.setDocumentStatus(StatusLED.S_BAD if status else StatusLED.S_GOOD)
224
226
  return
225
227
 
228
+ ##
229
+ # Debug
230
+ ##
231
+
232
+ def memInfo(self) -> None: # pragma: no cover
233
+ """Display memory info on the status bar. This is used to
234
+ investigate memory usage and Qt widgets that get left in memory.
235
+ Enabled by the --meminfo command line flag.
236
+
237
+ By default, this tracks memory usage diff after launch. To track
238
+ full memory usage, set environment variable PYTHONTRACEMALLOC=1
239
+ before starting novelWriter.
240
+ """
241
+ import tracemalloc
242
+ from collections import Counter
243
+
244
+ widgets = qApp.allWidgets()
245
+ if not self._debugInfo:
246
+ if tracemalloc.is_tracing():
247
+ self._traceMallocRef = "Total"
248
+ else:
249
+ self._traceMallocRef = "Relative"
250
+ tracemalloc.start()
251
+ self._debugInfo = True
252
+ self._wCounts = Counter([type(x).__name__ for x in widgets])
253
+
254
+ if hasattr(self, "_wCounts"):
255
+ diff = Counter([type(x).__name__ for x in widgets]) - self._wCounts
256
+ for name, count in diff.items():
257
+ logger.debug("Widget '%s': +%d", name, count)
258
+
259
+ mem = tracemalloc.get_traced_memory()
260
+ stamp = datetime.now().strftime("%H:%M:%S")
261
+ self.showMessage((
262
+ f"Debug [{stamp}]"
263
+ f" \u2013 Widgets: {len(widgets)}"
264
+ f" \u2013 {self._traceMallocRef} Memory: {mem[0]:n}"
265
+ f" \u2013 Peak: {mem[1]:n}"
266
+ ), 6000)
267
+ return
268
+
226
269
  # END Class GuiMainStatus
novelwriter/gui/theme.py CHANGED
@@ -7,7 +7,7 @@ Created: 2019-05-18 [0.1.3] GuiTheme
7
7
  Created: 2019-11-08 [0.4] GuiIcons
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2023, Veronica Berglyd Olsen
10
+ Copyright 2018–2024, Veronica Berglyd Olsen
11
11
 
12
12
  This program is free software: you can redistribute it and/or modify
13
13
  it under the terms of the GNU General Public License as published by
@@ -460,8 +460,8 @@ class GuiIcons:
460
460
  "search_regex", "search_word",
461
461
 
462
462
  # Format Icons
463
- "fmt_bold", "fmt_italic", "fmt_mode-md", "fmt_mode-sc", "fmt_strike", "fmt_subscript",
464
- "fmt_superscript", "fmt_underline",
463
+ "fmt_bold", "fmt_bold-md", "fmt_italic", "fmt_italic-md", "fmt_strike", "fmt_strike-md",
464
+ "fmt_subscript", "fmt_superscript", "fmt_underline",
465
465
 
466
466
  # General Button Icons
467
467
  "add", "backward", "bookmark", "browse", "checked", "close", "cross", "down", "edit",
@@ -479,9 +479,8 @@ class GuiIcons:
479
479
  }
480
480
 
481
481
  TOGGLE_ICON_KEYS = {
482
- "sticky": ("sticky-on", "sticky-off"),
483
- "bullet": ("bullet-on", "bullet-off"),
484
- "fmt_mode": ("fmt_mode-sc", "fmt_mode-md"),
482
+ "sticky": ("sticky-on", "sticky-off"),
483
+ "bullet": ("bullet-on", "bullet-off"),
485
484
  }
486
485
 
487
486
  IMAGE_MAP = {