novelWriter 2.4b1__py3-none-any.whl → 2.4.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 (89) hide show
  1. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/METADATA +5 -6
  2. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/RECORD +79 -83
  3. novelwriter/__init__.py +15 -8
  4. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  5. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  6. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  7. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  8. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  9. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  10. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  11. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  12. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  13. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  14. novelwriter/assets/icons/none.svg +4 -0
  15. novelwriter/assets/icons/typicons_dark/icons.conf +2 -2
  16. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +4 -0
  17. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +4 -0
  18. novelwriter/assets/icons/typicons_light/icons.conf +2 -2
  19. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +4 -0
  20. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +4 -0
  21. novelwriter/assets/manual.pdf +0 -0
  22. novelwriter/assets/sample.zip +0 -0
  23. novelwriter/common.py +11 -3
  24. novelwriter/config.py +12 -4
  25. novelwriter/core/buildsettings.py +7 -7
  26. novelwriter/core/coretools.py +21 -22
  27. novelwriter/core/docbuild.py +2 -2
  28. novelwriter/core/projectxml.py +1 -1
  29. novelwriter/core/spellcheck.py +3 -3
  30. novelwriter/core/status.py +3 -2
  31. novelwriter/core/tokenizer.py +3 -3
  32. novelwriter/core/toodt.py +333 -356
  33. novelwriter/dialogs/about.py +9 -11
  34. novelwriter/dialogs/docmerge.py +17 -14
  35. novelwriter/dialogs/docsplit.py +14 -12
  36. novelwriter/dialogs/editlabel.py +5 -4
  37. novelwriter/dialogs/preferences.py +29 -34
  38. novelwriter/dialogs/projectsettings.py +31 -28
  39. novelwriter/dialogs/quotes.py +10 -9
  40. novelwriter/dialogs/wordlist.py +17 -14
  41. novelwriter/error.py +14 -12
  42. novelwriter/extensions/circularprogress.py +12 -8
  43. novelwriter/extensions/configlayout.py +1 -3
  44. novelwriter/extensions/modified.py +33 -2
  45. novelwriter/extensions/pagedsidebar.py +16 -14
  46. novelwriter/extensions/simpleprogress.py +3 -1
  47. novelwriter/extensions/statusled.py +3 -1
  48. novelwriter/extensions/switch.py +10 -9
  49. novelwriter/extensions/switchbox.py +14 -13
  50. novelwriter/gui/doceditor.py +205 -246
  51. novelwriter/gui/dochighlight.py +26 -9
  52. novelwriter/gui/docviewer.py +55 -59
  53. novelwriter/gui/docviewerpanel.py +16 -13
  54. novelwriter/gui/editordocument.py +4 -4
  55. novelwriter/gui/itemdetails.py +45 -48
  56. novelwriter/gui/mainmenu.py +2 -2
  57. novelwriter/gui/noveltree.py +23 -21
  58. novelwriter/gui/outline.py +93 -94
  59. novelwriter/gui/projtree.py +32 -30
  60. novelwriter/gui/search.py +75 -29
  61. novelwriter/gui/sidebar.py +24 -28
  62. novelwriter/gui/statusbar.py +14 -14
  63. novelwriter/gui/theme.py +61 -39
  64. novelwriter/guimain.py +37 -33
  65. novelwriter/shared.py +21 -9
  66. novelwriter/text/counting.py +1 -0
  67. novelwriter/tools/dictionaries.py +15 -14
  68. novelwriter/tools/lipsum.py +20 -17
  69. novelwriter/tools/manusbuild.py +44 -35
  70. novelwriter/tools/manuscript.py +112 -112
  71. novelwriter/tools/manussettings.py +91 -98
  72. novelwriter/tools/noveldetails.py +20 -18
  73. novelwriter/tools/welcome.py +51 -48
  74. novelwriter/tools/writingstats.py +61 -55
  75. novelwriter/types.py +90 -0
  76. novelwriter/assets/icons/typicons_dark/typ_arrow-down.svg +0 -4
  77. novelwriter/assets/icons/typicons_dark/typ_arrow-right.svg +0 -4
  78. novelwriter/assets/icons/typicons_light/typ_arrow-down.svg +0 -4
  79. novelwriter/assets/icons/typicons_light/typ_arrow-right.svg +0 -4
  80. novelwriter/core/__init__.py +0 -3
  81. novelwriter/dialogs/__init__.py +0 -3
  82. novelwriter/extensions/__init__.py +0 -3
  83. novelwriter/gui/__init__.py +0 -3
  84. novelwriter/text/__init__.py +0 -3
  85. novelwriter/tools/__init__.py +0 -3
  86. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/LICENSE.md +0 -0
  87. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/WHEEL +0 -0
  88. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/entry_points.txt +0 -0
  89. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/top_level.txt +0 -0
novelwriter/gui/theme.py CHANGED
@@ -29,17 +29,17 @@ import logging
29
29
  from math import ceil
30
30
  from pathlib import Path
31
31
 
32
- from PyQt5.QtCore import Qt
33
- from PyQt5.QtWidgets import qApp
32
+ from PyQt5.QtCore import QSize, Qt
34
33
  from PyQt5.QtGui import (
35
34
  QPalette, QColor, QIcon, QFont, QFontMetrics, QFontDatabase, QPixmap
36
35
  )
36
+ from PyQt5.QtWidgets import QApplication
37
37
 
38
38
  from novelwriter import CONFIG
39
+ from novelwriter.common import NWConfigParser, cssCol, minmax
40
+ from novelwriter.constants import nwLabels
39
41
  from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
40
42
  from novelwriter.error import logException
41
- from novelwriter.common import NWConfigParser, minmax
42
- from novelwriter.constants import nwLabels
43
43
 
44
44
  logger = logging.getLogger(__name__)
45
45
 
@@ -74,9 +74,9 @@ class GuiTheme:
74
74
  self.isLightTheme = True
75
75
 
76
76
  # GUI
77
- self.statNone = QColor(120, 120, 120)
78
- self.statUnsaved = QColor(200, 15, 39)
79
- self.statSaved = QColor(2, 133, 37)
77
+ self.statNone = QColor(0, 0, 0)
78
+ self.statUnsaved = QColor(0, 0, 0)
79
+ self.statSaved = QColor(0, 0, 0)
80
80
  self.helpText = QColor(0, 0, 0)
81
81
 
82
82
  # Loaded Syntax Settings
@@ -144,33 +144,42 @@ class GuiTheme:
144
144
  self.getHeaderDecorationNarrow = self.iconCache.getHeaderDecorationNarrow
145
145
 
146
146
  # Extract Other Info
147
- self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX()
148
- self.guiScale = qApp.primaryScreen().logicalDotsPerInchX()/96.0
147
+ self.guiDPI = QApplication.primaryScreen().logicalDotsPerInchX()
148
+ self.guiScale = QApplication.primaryScreen().logicalDotsPerInchX()/96.0
149
149
  CONFIG.guiScale = self.guiScale
150
150
  logger.debug("GUI DPI: %.1f", self.guiDPI)
151
151
  logger.debug("GUI Scale: %.2f", self.guiScale)
152
152
 
153
153
  # Fonts
154
- self.guiFont = qApp.font()
155
- self.guiFontB = qApp.font()
154
+ self.guiFont = QApplication.font()
155
+ self.guiFontB = QApplication.font()
156
156
  self.guiFontB.setBold(True)
157
157
 
158
158
  qMetric = QFontMetrics(self.guiFont)
159
+ fHeight = qMetric.height()
160
+ fAscent = qMetric.ascent()
159
161
  self.fontPointSize = self.guiFont.pointSizeF()
160
- self.fontPixelSize = int(round(qMetric.height()))
161
- self.baseIconSize = int(round(qMetric.ascent()))
162
+ self.fontPixelSize = int(round(fHeight))
163
+ self.baseIconHeight = int(round(fAscent))
164
+ self.baseButtonHeight = int(round(1.35*fAscent))
162
165
  self.textNHeight = qMetric.boundingRect("N").height()
163
166
  self.textNWidth = qMetric.boundingRect("N").width()
164
167
 
168
+ self.baseIconSize = QSize(self.baseIconHeight, self.baseIconHeight)
169
+ self.buttonIconSize = QSize(int(0.9*self.baseIconHeight), int(0.9*self.baseIconHeight))
170
+
165
171
  # Monospace Font
166
172
  self.guiFontFixed = QFont()
167
173
  self.guiFontFixed.setPointSizeF(0.95*self.fontPointSize)
168
- self.guiFontFixed.setFamily(QFontDatabase.systemFont(QFontDatabase.FixedFont).family())
174
+ self.guiFontFixed.setFamily(
175
+ QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont).family()
176
+ )
169
177
 
170
178
  logger.debug("GUI Font Family: %s", self.guiFont.family())
171
179
  logger.debug("GUI Font Point Size: %.2f", self.fontPointSize)
172
180
  logger.debug("GUI Font Pixel Size: %d", self.fontPixelSize)
173
- logger.debug("GUI Base Icon Size: %d", self.baseIconSize)
181
+ logger.debug("GUI Base Icon Height: %d", self.baseIconHeight)
182
+ logger.debug("GUI Base Button Height: %d", self.baseButtonHeight)
174
183
  logger.debug("Text 'N' Height: %d", self.textNHeight)
175
184
  logger.debug("Text 'N' Width: %d", self.textNWidth)
176
185
 
@@ -218,6 +227,10 @@ class GuiTheme:
218
227
  logException()
219
228
  return False
220
229
 
230
+ # Reset Palette
231
+ self._guiPalette = QApplication.style().standardPalette()
232
+ self._resetGuiColors()
233
+
221
234
  # Main
222
235
  sec = "Main"
223
236
  if parser.has_section(sec):
@@ -247,8 +260,6 @@ class GuiTheme:
247
260
  self._setPalette(parser, sec, "highlightedtext", QPalette.ColorRole.HighlightedText)
248
261
  self._setPalette(parser, sec, "link", QPalette.ColorRole.Link)
249
262
  self._setPalette(parser, sec, "linkvisited", QPalette.ColorRole.LinkVisited)
250
- else:
251
- self._guiPalette = qApp.style().standardPalette()
252
263
 
253
264
  # GUI
254
265
  sec = "GUI"
@@ -277,7 +288,7 @@ class GuiTheme:
277
288
  self.iconCache.loadTheme(self.themeIcons or defaultIcons)
278
289
 
279
290
  # Apply Styles
280
- qApp.setPalette(self._guiPalette)
291
+ QApplication.setPalette(self._guiPalette)
281
292
 
282
293
  # Reset stylesheets so that they are regenerated
283
294
  self._buildStyleSheets(self._guiPalette)
@@ -384,6 +395,14 @@ class GuiTheme:
384
395
  # Internal Functions
385
396
  ##
386
397
 
398
+ def _resetGuiColors(self) -> None:
399
+ """Reset GUI colours to default values."""
400
+ self.statNone = QColor(120, 120, 120)
401
+ self.statUnsaved = QColor(200, 15, 39)
402
+ self.statSaved = QColor(2, 133, 37)
403
+ self.helpText = QColor(0, 0, 0)
404
+ return
405
+
387
406
  def _setGuiFont(self) -> None:
388
407
  """Update the GUI's font style from settings."""
389
408
  font = QFont()
@@ -394,14 +413,14 @@ class GuiTheme:
394
413
  font.setFamily("Arial")
395
414
  font.setPointSize(10)
396
415
  else:
397
- font = fontDB.systemFont(QFontDatabase.GeneralFont)
416
+ font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
398
417
  CONFIG.guiFont = font.family()
399
418
  CONFIG.guiFontSize = font.pointSize()
400
419
  else:
401
420
  font.setFamily(CONFIG.guiFont)
402
421
  font.setPointSize(CONFIG.guiFontSize)
403
422
 
404
- qApp.setFont(font)
423
+ QApplication.setFont(font)
405
424
 
406
425
  return
407
426
 
@@ -440,24 +459,24 @@ class GuiTheme:
440
459
 
441
460
  # Flat Tab Widget and Tab Bar:
442
461
  self._styleSheets[STYLES_FLAT_TABS] = (
443
- "QTabWidget::pane {{border: 0;}} "
444
- "QTabWidget QTabBar::tab {{border: 0; padding: {0}px {1}px;}} "
445
- "QTabWidget QTabBar::tab:selected {{color: rgb({2}, {3}, {4});}} "
446
- ).format(bPx, dPx, hCol.red(), hCol.green(), hCol.blue())
462
+ "QTabWidget::pane {border: 0;} "
463
+ f"QTabWidget QTabBar::tab {{border: 0; padding: {bPx}px {dPx}px;}} "
464
+ f"QTabWidget QTabBar::tab:selected {{color: {cssCol(hCol)};}} "
465
+ )
447
466
 
448
467
  # Minimal Tool Button
449
468
  self._styleSheets[STYLES_MIN_TOOLBUTTON] = (
450
- "QToolButton {{padding: {0}px; margin: 0; border: none; background: transparent;}} "
451
- "QToolButton:hover {{border: none; background: rgba({1}, {2}, {3}, 0.2);}} "
452
- "QToolButton::menu-indicator {{image: none;}} "
453
- ).format(aPx, tCol.red(), tCol.green(), tCol.blue())
469
+ f"QToolButton {{padding: {aPx}px; margin: 0; border: none; background: transparent;}} "
470
+ f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} "
471
+ "QToolButton::menu-indicator {image: none;} "
472
+ )
454
473
 
455
474
  # Big Tool Button
456
475
  self._styleSheets[STYLES_BIG_TOOLBUTTON] = (
457
- "QToolButton {{padding: {0}px; margin: 0; border: none; background: transparent;}} "
458
- "QToolButton:hover {{border: none; background: rgba({1}, {2}, {3}, 0.2);}} "
459
- "QToolButton::menu-indicator {{image: none;}} "
460
- ).format(cPx, tCol.red(), tCol.green(), tCol.blue())
476
+ f"QToolButton {{padding: {cPx}px; margin: 0; border: none; background: transparent;}} "
477
+ f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} "
478
+ "QToolButton::menu-indicator {image: none;} "
479
+ )
461
480
 
462
481
  return
463
482
 
@@ -544,6 +563,9 @@ class GuiIcons:
544
563
  self._confName = "icons.conf"
545
564
  self._iconPath = CONFIG.assetPath("icons")
546
565
 
566
+ # None Icon
567
+ self._noIcon = QIcon(str(self._iconPath / "none.svg"))
568
+
547
569
  # Icon Theme Meta
548
570
  self.themeName = ""
549
571
  self.themeDescription = ""
@@ -674,13 +696,13 @@ class GuiIcons:
674
696
  def getToggleIcon(self, name: str, size: tuple[int, int]) -> QIcon:
675
697
  """Return a toggle icon from the icon buffer. or load it."""
676
698
  if name in self.TOGGLE_ICON_KEYS:
677
- pOne = self.getPixmap(self.TOGGLE_ICON_KEYS[name][0], size)
699
+ pOne = self.getPixmap(self.TOGGLE_ICON_KEYS[name][0], size)
678
700
  pTwo = self.getPixmap(self.TOGGLE_ICON_KEYS[name][1], size)
679
701
  icon = QIcon()
680
702
  icon.addPixmap(pOne, QIcon.Mode.Normal, QIcon.State.On)
681
703
  icon.addPixmap(pTwo, QIcon.Mode.Normal, QIcon.State.Off)
682
704
  return icon
683
- return QIcon()
705
+ return self._noIcon
684
706
 
685
707
  def getPixmap(self, name: str, size: tuple[int, int]) -> QPixmap:
686
708
  """Return an icon from the icon buffer as a QPixmap. If it
@@ -712,14 +734,14 @@ class GuiIcons:
712
734
  elif tLayout == nwItemLayout.NOTE:
713
735
  iconName = "proj_note"
714
736
  if iconName is None:
715
- return QIcon()
737
+ return self._noIcon
716
738
 
717
739
  return self.getIcon(iconName)
718
740
 
719
741
  def getHeaderDecoration(self, hLevel: int) -> QPixmap:
720
742
  """Get the decoration for a specific heading level."""
721
743
  if not self._headerDec:
722
- iPx = self.mainTheme.baseIconSize
744
+ iPx = self.mainTheme.baseIconHeight
723
745
  self._headerDec = [
724
746
  self.loadDecoration("deco_doc_h0", h=iPx),
725
747
  self.loadDecoration("deco_doc_h1", h=iPx),
@@ -732,7 +754,7 @@ class GuiIcons:
732
754
  def getHeaderDecorationNarrow(self, hLevel: int) -> QPixmap:
733
755
  """Get the narrow decoration for a specific heading level."""
734
756
  if not self._headerDecNarrow:
735
- iPx = self.mainTheme.baseIconSize
757
+ iPx = self.mainTheme.baseIconHeight
736
758
  self._headerDecNarrow = [
737
759
  self.loadDecoration("deco_doc_h0_n", h=iPx),
738
760
  self.loadDecoration("deco_doc_h1_n", h=iPx),
@@ -753,7 +775,7 @@ class GuiIcons:
753
775
  """
754
776
  if name not in self.ICON_KEYS:
755
777
  logger.error("Requested unknown icon name '%s'", name)
756
- return QIcon()
778
+ return self._noIcon
757
779
 
758
780
  # If we just want the app icons, return right away
759
781
  if name == "novelwriter":
@@ -769,7 +791,7 @@ class GuiIcons:
769
791
  # If we didn't find one, give up and return an empty icon
770
792
  logger.warning("Did not load an icon for '%s'", name)
771
793
 
772
- return QIcon()
794
+ return self._noIcon
773
795
 
774
796
  # END Class GuiIcons
775
797
 
novelwriter/guimain.py CHANGED
@@ -30,11 +30,11 @@ from time import time
30
30
  from pathlib import Path
31
31
  from datetime import datetime
32
32
 
33
- from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
34
33
  from PyQt5.QtCore import Qt, QTimer, pyqtSlot
34
+ from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
35
35
  from PyQt5.QtWidgets import (
36
- QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, QSplitter,
37
- QStackedWidget, QVBoxLayout, QWidget, qApp
36
+ QApplication, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, QSplitter,
37
+ QStackedWidget, QVBoxLayout, QWidget
38
38
  )
39
39
 
40
40
  from novelwriter import CONFIG, SHARED, __hexversion__, __version__
@@ -109,7 +109,7 @@ class GuiMain(QMainWindow):
109
109
  nwIcon = CONFIG.assetPath("icons") / "novelwriter.svg"
110
110
  self.nwIcon = QIcon(str(nwIcon)) if nwIcon.is_file() else QIcon()
111
111
  self.setWindowIcon(self.nwIcon)
112
- qApp.setWindowIcon(self.nwIcon)
112
+ QApplication.setWindowIcon(self.nwIcon)
113
113
 
114
114
  # Build the GUI
115
115
  # =============
@@ -148,7 +148,7 @@ class GuiMain(QMainWindow):
148
148
  self.treePane.setLayout(self.treeBox)
149
149
 
150
150
  # Splitter : Document Viewer / Document Meta
151
- self.splitView = QSplitter(Qt.Vertical, self)
151
+ self.splitView = QSplitter(Qt.Orientation.Vertical, self)
152
152
  self.splitView.addWidget(self.docViewer)
153
153
  self.splitView.addWidget(self.docViewerPanel)
154
154
  self.splitView.setHandleWidth(hWd)
@@ -158,7 +158,7 @@ class GuiMain(QMainWindow):
158
158
  self.splitView.setCollapsible(1, False)
159
159
 
160
160
  # Splitter : Document Editor / Document Viewer
161
- self.splitDocs = QSplitter(Qt.Horizontal, self)
161
+ self.splitDocs = QSplitter(Qt.Orientation.Horizontal, self)
162
162
  self.splitDocs.addWidget(self.docEditor)
163
163
  self.splitDocs.addWidget(self.splitView)
164
164
  self.splitDocs.setOpaqueResize(False)
@@ -167,7 +167,7 @@ class GuiMain(QMainWindow):
167
167
  self.splitDocs.setCollapsible(1, False)
168
168
 
169
169
  # Splitter : Project Tree / Document Area
170
- self.splitMain = QSplitter(Qt.Horizontal)
170
+ self.splitMain = QSplitter(Qt.Orientation.Horizontal)
171
171
  self.splitMain.setContentsMargins(0, 0, 0, 0)
172
172
  self.splitMain.addWidget(self.treePane)
173
173
  self.splitMain.addWidget(self.splitDocs)
@@ -205,7 +205,7 @@ class GuiMain(QMainWindow):
205
205
  self.setMenuBar(self.mainMenu)
206
206
  self.setCentralWidget(self.mainWidget)
207
207
  self.setStatusBar(self.mainStatus)
208
- self.setContextMenuPolicy(Qt.NoContextMenu) # Issue #1147
208
+ self.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu) # Issue #1147
209
209
 
210
210
  # Connect Signals
211
211
  # ===============
@@ -262,6 +262,7 @@ class GuiMain(QMainWindow):
262
262
  self.docEditor.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
263
263
  self.docEditor.requestProjectItemRenamed.connect(self.projView.renameTreeItem)
264
264
  self.docEditor.requestNewNoteCreation.connect(self.projView.createNewNote)
265
+ self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
265
266
 
266
267
  self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle)
267
268
  self.docViewer.loadDocumentTagRequest.connect(self._followTag)
@@ -327,7 +328,7 @@ class GuiMain(QMainWindow):
327
328
  def postLaunchTasks(self, cmdOpen: str | None) -> None:
328
329
  """Process tasks after the main window has been created."""
329
330
  if cmdOpen:
330
- qApp.processEvents()
331
+ QApplication.processEvents()
331
332
  logger.info("Command line path: %s", cmdOpen)
332
333
  self.openProject(cmdOpen)
333
334
 
@@ -473,12 +474,12 @@ class GuiMain(QMainWindow):
473
474
  break
474
475
 
475
476
  if lastEdited is not None:
476
- qApp.processEvents()
477
+ QApplication.processEvents()
477
478
  self.openDocument(lastEdited, doScroll=True)
478
479
 
479
480
  lastViewed = SHARED.project.data.getLastHandle("viewer")
480
481
  if lastViewed is not None:
481
- qApp.processEvents()
482
+ QApplication.processEvents()
482
483
  self.viewDocument(lastViewed)
483
484
 
484
485
  # Check if we need to rebuild the index
@@ -487,7 +488,7 @@ class GuiMain(QMainWindow):
487
488
  self.rebuildIndex()
488
489
 
489
490
  # Make sure the changed status is set to false on things opened
490
- qApp.processEvents()
491
+ QApplication.processEvents()
491
492
  self.docEditor.setDocumentChanged(False)
492
493
  SHARED.project.setProjectChanged(False)
493
494
 
@@ -737,7 +738,7 @@ class GuiMain(QMainWindow):
737
738
  """Rebuild the entire index."""
738
739
  if SHARED.hasProject:
739
740
  logger.info("Rebuilding index ...")
740
- qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
741
+ QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
741
742
  tStart = time()
742
743
 
743
744
  self.projView.saveProjectTasks()
@@ -751,7 +752,7 @@ class GuiMain(QMainWindow):
751
752
  )
752
753
  self.docEditor.updateTagHighLighting()
753
754
  self._updateStatusWordCount()
754
- qApp.restoreOverrideCursor()
755
+ QApplication.restoreOverrideCursor()
755
756
 
756
757
  if not beQuiet:
757
758
  SHARED.info(self.tr("The project index has been successfully rebuilt."))
@@ -767,7 +768,7 @@ class GuiMain(QMainWindow):
767
768
  """Open the welcome dialog."""
768
769
  dialog = GuiWelcome(self)
769
770
  dialog.openProjectRequest.connect(self._openProjectFromWelcome)
770
- dialog.exec_()
771
+ dialog.exec()
771
772
  return
772
773
 
773
774
  @pyqtSlot()
@@ -775,7 +776,7 @@ class GuiMain(QMainWindow):
775
776
  """Open the preferences dialog."""
776
777
  dialog = GuiPreferences(self)
777
778
  dialog.newPreferencesReady.connect(self._processConfigChanges)
778
- dialog.exec_()
779
+ dialog.exec()
779
780
  return
780
781
 
781
782
  @pyqtSlot()
@@ -785,7 +786,7 @@ class GuiMain(QMainWindow):
785
786
  if SHARED.hasProject:
786
787
  dialog = GuiProjectSettings(self, gotoPage=focusTab)
787
788
  dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
788
- dialog.exec_()
789
+ dialog.exec()
789
790
  return
790
791
 
791
792
  @pyqtSlot()
@@ -796,7 +797,7 @@ class GuiMain(QMainWindow):
796
797
  dialog.setModal(True)
797
798
  dialog.show()
798
799
  dialog.raise_()
799
- qApp.processEvents()
800
+ QApplication.processEvents()
800
801
  dialog.updateValues()
801
802
  return
802
803
 
@@ -809,7 +810,7 @@ class GuiMain(QMainWindow):
809
810
  dialog.setModal(False)
810
811
  dialog.show()
811
812
  dialog.raise_()
812
- qApp.processEvents()
813
+ QApplication.processEvents()
813
814
  dialog.loadContent()
814
815
  return
815
816
 
@@ -819,7 +820,7 @@ class GuiMain(QMainWindow):
819
820
  if SHARED.hasProject:
820
821
  dialog = GuiWordList(self)
821
822
  dialog.newWordListReady.connect(self._processWordListChanges)
822
- dialog.exec_()
823
+ dialog.exec()
823
824
  return
824
825
 
825
826
  @pyqtSlot()
@@ -831,7 +832,7 @@ class GuiMain(QMainWindow):
831
832
  dialog.setModal(False)
832
833
  dialog.show()
833
834
  dialog.raise_()
834
- qApp.processEvents()
835
+ QApplication.processEvents()
835
836
  dialog.populateGUI()
836
837
  return
837
838
 
@@ -842,7 +843,7 @@ class GuiMain(QMainWindow):
842
843
  dialog.setModal(True)
843
844
  dialog.show()
844
845
  dialog.raise_()
845
- qApp.processEvents()
846
+ QApplication.processEvents()
846
847
  dialog.populateGUI()
847
848
  return
848
849
 
@@ -860,7 +861,7 @@ class GuiMain(QMainWindow):
860
861
  dialog.setModal(True)
861
862
  dialog.show()
862
863
  dialog.raise_()
863
- qApp.processEvents()
864
+ QApplication.processEvents()
864
865
  if not dialog.initDialog():
865
866
  dialog.close()
866
867
  SHARED.error(self.tr("Could not initialise the dialog."))
@@ -898,7 +899,8 @@ class GuiMain(QMainWindow):
898
899
  CONFIG.setViewPanePos(self.splitView.sizes())
899
900
 
900
901
  CONFIG.showViewerPanel = self.docViewerPanel.isVisible()
901
- if self.windowState() & Qt.WindowFullScreen != Qt.WindowFullScreen:
902
+ wFull = Qt.WindowState.WindowFullScreen
903
+ if self.windowState() & wFull != wFull:
902
904
  # Ignore window size if in full screen mode
903
905
  CONFIG.setMainWinSize(self.width(), self.height())
904
906
 
@@ -908,7 +910,7 @@ class GuiMain(QMainWindow):
908
910
  CONFIG.saveConfig()
909
911
  self.reportConfErr()
910
912
 
911
- qApp.quit()
913
+ QApplication.quit()
912
914
 
913
915
  return True
914
916
 
@@ -935,14 +937,14 @@ class GuiMain(QMainWindow):
935
937
 
936
938
  def toggleFullScreenMode(self) -> None:
937
939
  """Toggle full screen mode"""
938
- self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
940
+ self.setWindowState(self.windowState() ^ Qt.WindowState.WindowFullScreen)
939
941
  return
940
942
 
941
943
  ##
942
944
  # Events
943
945
  ##
944
946
 
945
- def closeEvent(self, event: QCloseEvent):
947
+ def closeEvent(self, event: QCloseEvent) -> None:
946
948
  """Capture the closing event of the GUI and call the close
947
949
  function to handle all the close process steps.
948
950
  """
@@ -1055,7 +1057,7 @@ class GuiMain(QMainWindow):
1055
1057
 
1056
1058
  if theme:
1057
1059
  # We are doing this manually instead of connecting to
1058
- # qApp.paletteChanged since the processing order matters
1060
+ # paletteChanged since the processing order matters
1059
1061
  SHARED.theme.loadTheme()
1060
1062
  self.docEditor.updateTheme()
1061
1063
  self.docViewer.updateTheme()
@@ -1114,7 +1116,7 @@ class GuiMain(QMainWindow):
1114
1116
  @pyqtSlot(Path)
1115
1117
  def _openProjectFromWelcome(self, path: Path) -> None:
1116
1118
  """Handle an open project request from the welcome dialog."""
1117
- qApp.processEvents()
1119
+ QApplication.processEvents()
1118
1120
  self.openProject(path)
1119
1121
  if not SHARED.hasProject:
1120
1122
  self.showWelcomeDialog()
@@ -1167,7 +1169,9 @@ class GuiMain(QMainWindow):
1167
1169
  elif view == nwView.SEARCH:
1168
1170
  self.mainStack.setCurrentWidget(self.splitMain)
1169
1171
  self.projStack.setCurrentWidget(self.projSearch)
1170
- self.projSearch.beginSearch()
1172
+ self.projSearch.beginSearch(
1173
+ self.docEditor.getSelectedText() if self.docEditor.anyFocus() else ""
1174
+ )
1171
1175
  elif view == nwView.OUTLINE:
1172
1176
  self.mainStack.setCurrentWidget(self.outlineView)
1173
1177
  return
@@ -1197,7 +1201,7 @@ class GuiMain(QMainWindow):
1197
1201
  return
1198
1202
 
1199
1203
  @pyqtSlot()
1200
- def _toggleViewerPanelVisibility(self):
1204
+ def _toggleViewerPanelVisibility(self) -> None:
1201
1205
  """Toggle the visibility of the document viewer panel."""
1202
1206
  CONFIG.showViewerPanel = not CONFIG.showViewerPanel
1203
1207
  self.docViewerPanel.setVisible(CONFIG.showViewerPanel)
@@ -1209,7 +1213,7 @@ class GuiMain(QMainWindow):
1209
1213
  if SHARED.hasProject:
1210
1214
  currTime = time()
1211
1215
  editIdle = currTime - self.docEditor.lastActive > CONFIG.userIdleTime
1212
- userIdle = qApp.applicationState() != Qt.ApplicationActive
1216
+ userIdle = QApplication.applicationState() != Qt.ApplicationState.ApplicationActive
1213
1217
  self.mainStatus.setUserIdle(editIdle or userIdle)
1214
1218
  SHARED.updateIdleTime(currTime, editIdle or userIdle)
1215
1219
  self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
@@ -1266,7 +1270,7 @@ class GuiMain(QMainWindow):
1266
1270
  @pyqtSlot()
1267
1271
  def _keyPressEscape(self) -> None:
1268
1272
  """Process an escape keypress in the main window."""
1269
- if self.docEditor.docSearch.isVisible():
1273
+ if self.docEditor.searchVisible():
1270
1274
  self.docEditor.closeSearch()
1271
1275
  elif SHARED.focusMode:
1272
1276
  SHARED.setFocusMode(False)
novelwriter/shared.py CHANGED
@@ -26,21 +26,21 @@ from __future__ import annotations
26
26
 
27
27
  import logging
28
28
 
29
+ from pathlib import Path
29
30
  from time import time
30
31
  from typing import TYPE_CHECKING, TypeVar
31
- from pathlib import Path
32
32
 
33
33
  from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal
34
34
  from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget
35
- from novelwriter.common import formatFileFilter
36
35
 
36
+ from novelwriter.common import formatFileFilter
37
37
  from novelwriter.constants import nwFiles
38
38
  from novelwriter.core.spellcheck import NWSpellEnchant
39
39
 
40
40
  if TYPE_CHECKING: # pragma: no cover
41
- from novelwriter.guimain import GuiMain
42
- from novelwriter.gui.theme import GuiTheme
43
41
  from novelwriter.core.project import NWProject
42
+ from novelwriter.gui.theme import GuiTheme
43
+ from novelwriter.guimain import GuiMain
44
44
 
45
45
  logger = logging.getLogger(__name__)
46
46
 
@@ -198,6 +198,7 @@ class SharedData(QObject):
198
198
 
199
199
  def closeProject(self) -> None:
200
200
  """Close the current project."""
201
+ self._closeDialogs()
201
202
  self.project.closeProject(self._idleTime)
202
203
  self._resetProject()
203
204
  self._resetIdleTimer()
@@ -292,7 +293,7 @@ class SharedData(QObject):
292
293
  self._lastAlert = alert.logMessage
293
294
  if log:
294
295
  logger.info(self._lastAlert, stacklevel=2)
295
- alert.exec_()
296
+ alert.exec()
296
297
  alert.deleteLater()
297
298
  return
298
299
 
@@ -304,7 +305,7 @@ class SharedData(QObject):
304
305
  self._lastAlert = alert.logMessage
305
306
  if log:
306
307
  logger.warning(self._lastAlert, stacklevel=2)
307
- alert.exec_()
308
+ alert.exec()
308
309
  alert.deleteLater()
309
310
  return
310
311
 
@@ -319,7 +320,7 @@ class SharedData(QObject):
319
320
  self._lastAlert = alert.logMessage
320
321
  if log:
321
322
  logger.error(self._lastAlert, stacklevel=2)
322
- alert.exec_()
323
+ alert.exec()
323
324
  alert.deleteLater()
324
325
  return
325
326
 
@@ -329,7 +330,7 @@ class SharedData(QObject):
329
330
  alert.setMessage(text, info, details)
330
331
  alert.setAlertType(_GuiAlert.WARN if warn else _GuiAlert.ASK, True)
331
332
  self._lastAlert = alert.logMessage
332
- alert.exec_()
333
+ alert.exec()
333
334
  isYes = alert.result() == QMessageBox.StandardButton.Yes
334
335
  alert.deleteLater()
335
336
  return isYes
@@ -356,6 +357,17 @@ class SharedData(QObject):
356
357
  self._idleTime = 0.0
357
358
  return
358
359
 
360
+ def _closeDialogs(self) -> None:
361
+ """Close non-modal dialogs."""
362
+ from novelwriter.tools.manuscript import GuiManuscript
363
+ from novelwriter.tools.writingstats import GuiWritingStats
364
+
365
+ for widget in self.mainGui.children():
366
+ if isinstance(widget, (GuiManuscript, GuiWritingStats)):
367
+ widget.close()
368
+
369
+ return
370
+
359
371
  # END Class SharedData
360
372
 
361
373
 
@@ -404,7 +416,7 @@ class _GuiAlert(QMessageBox):
404
416
  self.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
405
417
  else:
406
418
  self.setStandardButtons(QMessageBox.Ok)
407
- pSz = 2*self._theme.baseIconSize
419
+ pSz = 2*self._theme.baseIconHeight
408
420
  if level == self.INFO:
409
421
  self.setIconPixmap(self._theme.getPixmap("alert_info", (pSz, pSz)))
410
422
  self.setWindowTitle(self.tr("Information"))
@@ -57,6 +57,7 @@ def preProcessText(text: str, keepHeaders: bool = True) -> list[str]:
57
57
  continue
58
58
  if line[0] == ">":
59
59
  line = line.lstrip(">").lstrip(" ")
60
+ if line: # Above block can return empty line (Issue #1816)
60
61
  if line[-1] == "<":
61
62
  line = line.rstrip("<").rstrip(" ")
62
63
  if "[" in line: