novelWriter 2.5b1__py3-none-any.whl → 2.5.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 (78) hide show
  1. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/RECORD +77 -75
  3. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +3 -3
  5. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  6. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  7. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  8. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  9. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  10. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  11. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  12. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  13. novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
  14. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  15. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  16. novelwriter/assets/i18n/project_pl_PL.json +116 -0
  17. novelwriter/assets/i18n/project_pt_BR.json +74 -74
  18. novelwriter/assets/manual.pdf +0 -0
  19. novelwriter/assets/sample.zip +0 -0
  20. novelwriter/assets/text/credits_en.htm +52 -44
  21. novelwriter/assets/themes/cyberpunk_night.conf +1 -0
  22. novelwriter/assets/themes/default_dark.conf +1 -0
  23. novelwriter/assets/themes/default_light.conf +1 -0
  24. novelwriter/assets/themes/dracula.conf +1 -0
  25. novelwriter/assets/themes/solarized_dark.conf +1 -0
  26. novelwriter/assets/themes/solarized_light.conf +1 -0
  27. novelwriter/common.py +12 -3
  28. novelwriter/config.py +67 -15
  29. novelwriter/constants.py +8 -10
  30. novelwriter/core/buildsettings.py +5 -3
  31. novelwriter/core/coretools.py +3 -1
  32. novelwriter/core/docbuild.py +1 -0
  33. novelwriter/core/project.py +15 -4
  34. novelwriter/core/status.py +4 -1
  35. novelwriter/core/storage.py +6 -1
  36. novelwriter/core/tohtml.py +69 -29
  37. novelwriter/core/tokenizer.py +83 -14
  38. novelwriter/core/toodt.py +48 -21
  39. novelwriter/core/toqdoc.py +37 -21
  40. novelwriter/dialogs/about.py +10 -15
  41. novelwriter/dialogs/docmerge.py +16 -16
  42. novelwriter/dialogs/docsplit.py +16 -16
  43. novelwriter/dialogs/editlabel.py +6 -8
  44. novelwriter/dialogs/preferences.py +106 -93
  45. novelwriter/dialogs/projectsettings.py +16 -20
  46. novelwriter/dialogs/quotes.py +9 -5
  47. novelwriter/dialogs/wordlist.py +6 -6
  48. novelwriter/enum.py +4 -5
  49. novelwriter/extensions/configlayout.py +38 -4
  50. novelwriter/extensions/modified.py +22 -3
  51. novelwriter/extensions/{circularprogress.py → progressbars.py} +26 -3
  52. novelwriter/extensions/statusled.py +39 -23
  53. novelwriter/gui/doceditor.py +22 -13
  54. novelwriter/gui/dochighlight.py +30 -39
  55. novelwriter/gui/docviewer.py +24 -15
  56. novelwriter/gui/docviewerpanel.py +7 -0
  57. novelwriter/gui/mainmenu.py +11 -11
  58. novelwriter/gui/outline.py +4 -3
  59. novelwriter/gui/projtree.py +85 -77
  60. novelwriter/gui/search.py +10 -1
  61. novelwriter/gui/statusbar.py +25 -29
  62. novelwriter/gui/theme.py +3 -0
  63. novelwriter/guimain.py +139 -124
  64. novelwriter/shared.py +19 -8
  65. novelwriter/text/patterns.py +113 -0
  66. novelwriter/tools/dictionaries.py +2 -8
  67. novelwriter/tools/lipsum.py +8 -12
  68. novelwriter/tools/manusbuild.py +9 -9
  69. novelwriter/tools/manuscript.py +10 -5
  70. novelwriter/tools/manussettings.py +7 -3
  71. novelwriter/tools/noveldetails.py +10 -10
  72. novelwriter/tools/welcome.py +19 -10
  73. novelwriter/tools/writingstats.py +3 -3
  74. novelwriter/types.py +5 -2
  75. novelwriter/extensions/simpleprogress.py +0 -53
  76. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/LICENSE.md +0 -0
  77. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/entry_points.txt +0 -0
  78. {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/top_level.txt +0 -0
@@ -33,14 +33,14 @@ from PyQt5.QtWidgets import (
33
33
  from novelwriter import CONFIG, SHARED
34
34
  from novelwriter.common import cssCol, readTextFile
35
35
  from novelwriter.extensions.configlayout import NColourLabel
36
- from novelwriter.extensions.modified import NNonBlockingDialog
36
+ from novelwriter.extensions.modified import NDialog
37
37
  from novelwriter.extensions.versioninfo import VersionInfoWidget
38
38
  from novelwriter.types import QtAlignRightTop, QtDialogClose
39
39
 
40
40
  logger = logging.getLogger(__name__)
41
41
 
42
42
 
43
- class GuiAbout(NNonBlockingDialog):
43
+ class GuiAbout(NDialog):
44
44
 
45
45
  def __init__(self, parent: QWidget) -> None:
46
46
  super().__init__(parent=parent)
@@ -75,7 +75,7 @@ class GuiAbout(NNonBlockingDialog):
75
75
 
76
76
  # Credits
77
77
  self.lblCredits = NColourLabel(
78
- self.tr("Credits"), scale=1.6, parent=self, bold=True
78
+ self.tr("Credits"), self, scale=1.6, bold=True
79
79
  )
80
80
 
81
81
  self.txtCredits = QTextBrowser(self)
@@ -85,7 +85,7 @@ class GuiAbout(NNonBlockingDialog):
85
85
 
86
86
  # Buttons
87
87
  self.btnBox = QDialogButtonBox(QtDialogClose, self)
88
- self.btnBox.rejected.connect(self.close)
88
+ self.btnBox.rejected.connect(self.reject)
89
89
 
90
90
  # Assemble
91
91
  self.innerBox = QVBoxLayout()
@@ -106,7 +106,9 @@ class GuiAbout(NNonBlockingDialog):
106
106
 
107
107
  self.setLayout(self.outerBox)
108
108
  self.setSizeGripEnabled(True)
109
+
109
110
  self._setStyleSheet()
111
+ self._fillCreditsPage()
110
112
 
111
113
  logger.debug("Ready: GuiAbout")
112
114
 
@@ -116,11 +118,6 @@ class GuiAbout(NNonBlockingDialog):
116
118
  logger.debug("Delete: GuiAbout")
117
119
  return
118
120
 
119
- def populateGUI(self) -> None:
120
- """Populate tabs with text."""
121
- self._fillCreditsPage()
122
- return
123
-
124
121
  ##
125
122
  # Events
126
123
  ##
@@ -128,7 +125,7 @@ class GuiAbout(NNonBlockingDialog):
128
125
  def closeEvent(self, event: QCloseEvent) -> None:
129
126
  """Capture the close event and perform cleanup."""
130
127
  event.accept()
131
- self.deleteLater()
128
+ self.softDelete()
132
129
  return
133
130
 
134
131
  ##
@@ -137,16 +134,14 @@ class GuiAbout(NNonBlockingDialog):
137
134
 
138
135
  def _fillCreditsPage(self) -> None:
139
136
  """Load the content for the Credits page."""
140
- docPath = CONFIG.assetPath("text") / "credits_en.htm"
141
- docText = readTextFile(docPath)
142
- if docText:
143
- self.txtCredits.setHtml(docText)
137
+ if html := readTextFile(CONFIG.assetPath("text") / "credits_en.htm"):
138
+ self.txtCredits.setHtml(html)
144
139
  else:
145
140
  self.txtCredits.setHtml("Error loading credits text ...")
146
141
  return
147
142
 
148
143
  def _setStyleSheet(self) -> None:
149
- """Set stylesheet for all browser tabs."""
144
+ """Set stylesheet text document."""
150
145
  baseCol = cssCol(self.palette().window().color())
151
146
  self.txtCredits.setStyleSheet(
152
147
  f"QTextBrowser {{border: none; background: {baseCol};}} "
@@ -27,21 +27,21 @@ from __future__ import annotations
27
27
  import logging
28
28
 
29
29
  from PyQt5.QtCore import Qt, pyqtSlot
30
- from PyQt5.QtGui import QCloseEvent
31
30
  from PyQt5.QtWidgets import (
32
- QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel,
33
- QListWidget, QListWidgetItem, QVBoxLayout, QWidget
31
+ QAbstractItemView, QDialogButtonBox, QGridLayout, QLabel, QListWidget,
32
+ QListWidgetItem, QVBoxLayout, QWidget
34
33
  )
35
34
 
36
35
  from novelwriter import CONFIG, SHARED
37
36
  from novelwriter.extensions.configlayout import NColourLabel
37
+ from novelwriter.extensions.modified import NDialog
38
38
  from novelwriter.extensions.switch import NSwitch
39
- from novelwriter.types import QtDialogCancel, QtDialogOk, QtDialogReset, QtUserRole
39
+ from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk, QtDialogReset, QtUserRole
40
40
 
41
41
  logger = logging.getLogger(__name__)
42
42
 
43
43
 
44
- class GuiDocMerge(QDialog):
44
+ class GuiDocMerge(NDialog):
45
45
 
46
46
  D_HANDLE = QtUserRole
47
47
 
@@ -58,7 +58,7 @@ class GuiDocMerge(QDialog):
58
58
  self.headLabel.setFont(SHARED.theme.guiFontB)
59
59
  self.helpLabel = NColourLabel(
60
60
  self.tr("Drag and drop items to change the order, or uncheck to exclude."),
61
- SHARED.theme.helpText, parent=self, wrap=True
61
+ self, color=SHARED.theme.helpText, wrap=True
62
62
  )
63
63
 
64
64
  iPx = SHARED.theme.baseIconHeight
@@ -117,7 +117,7 @@ class GuiDocMerge(QDialog):
117
117
  logger.debug("Delete: GuiDocMerge")
118
118
  return
119
119
 
120
- def getData(self) -> dict:
120
+ def data(self) -> dict:
121
121
  """Return the user's choices."""
122
122
  finalItems = []
123
123
  for i in range(self.listBox.count()):
@@ -130,15 +130,15 @@ class GuiDocMerge(QDialog):
130
130
 
131
131
  return self._data
132
132
 
133
- ##
134
- # Events
135
- ##
136
-
137
- def closeEvent(self, event: QCloseEvent) -> None:
138
- """Capture the close event and perform cleanup."""
139
- event.accept()
140
- self.deleteLater()
141
- return
133
+ @classmethod
134
+ def getData(cls, parent: QWidget, handle: str, items: list[str]) -> tuple[dict, bool]:
135
+ """Pop the dialog and return the result."""
136
+ cls = GuiDocMerge(parent, handle, items)
137
+ cls.exec()
138
+ data = cls.data()
139
+ accepted = cls.result() == QtAccepted
140
+ cls.softDelete()
141
+ return data, accepted
142
142
 
143
143
  ##
144
144
  # Private Slots
@@ -27,21 +27,21 @@ from __future__ import annotations
27
27
  import logging
28
28
 
29
29
  from PyQt5.QtCore import pyqtSlot
30
- from PyQt5.QtGui import QCloseEvent
31
30
  from PyQt5.QtWidgets import (
32
- QAbstractItemView, QComboBox, QDialog, QDialogButtonBox, QGridLayout,
33
- QLabel, QListWidget, QListWidgetItem, QVBoxLayout, QWidget
31
+ QAbstractItemView, QComboBox, QDialogButtonBox, QGridLayout, QLabel,
32
+ QListWidget, QListWidgetItem, QVBoxLayout, QWidget
34
33
  )
35
34
 
36
35
  from novelwriter import CONFIG, SHARED
37
36
  from novelwriter.extensions.configlayout import NColourLabel
37
+ from novelwriter.extensions.modified import NDialog
38
38
  from novelwriter.extensions.switch import NSwitch
39
- from novelwriter.types import QtDialogCancel, QtDialogOk, QtUserRole
39
+ from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk, QtUserRole
40
40
 
41
41
  logger = logging.getLogger(__name__)
42
42
 
43
43
 
44
- class GuiDocSplit(QDialog):
44
+ class GuiDocSplit(NDialog):
45
45
 
46
46
  LINE_ROLE = QtUserRole
47
47
  LEVEL_ROLE = QtUserRole + 1
@@ -62,7 +62,7 @@ class GuiDocSplit(QDialog):
62
62
  self.headLabel.setFont(SHARED.theme.guiFontB)
63
63
  self.helpLabel = NColourLabel(
64
64
  self.tr("Select the maximum level to split into files."),
65
- SHARED.theme.helpText, parent=self, wrap=True
65
+ self, color=SHARED.theme.helpText, wrap=True
66
66
  )
67
67
 
68
68
  # Values
@@ -145,7 +145,7 @@ class GuiDocSplit(QDialog):
145
145
  logger.debug("Delete: GuiDocSplit")
146
146
  return
147
147
 
148
- def getData(self) -> tuple[dict, list]:
148
+ def data(self) -> tuple[dict, list[str]]:
149
149
  """Return the user's choices. Also save the users options for
150
150
  the next time the dialog is used.
151
151
  """
@@ -178,15 +178,15 @@ class GuiDocSplit(QDialog):
178
178
 
179
179
  return self._data, self._text
180
180
 
181
- ##
182
- # Events
183
- ##
184
-
185
- def closeEvent(self, event: QCloseEvent) -> None:
186
- """Capture the close event and perform cleanup."""
187
- event.accept()
188
- self.deleteLater()
189
- return
181
+ @classmethod
182
+ def getData(cls, parent: QWidget, handle: str) -> tuple[dict, list[str], bool]:
183
+ """Pop the dialog and return the result."""
184
+ cls = GuiDocSplit(parent, handle)
185
+ cls.exec()
186
+ data, text = cls.data()
187
+ accepted = cls.result() == QtAccepted
188
+ cls.softDelete()
189
+ return data, text, accepted
190
190
 
191
191
  ##
192
192
  # Private Slots
@@ -25,18 +25,16 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
- from PyQt5.QtWidgets import (
29
- QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QVBoxLayout,
30
- QWidget
31
- )
28
+ from PyQt5.QtWidgets import QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QVBoxLayout, QWidget
32
29
 
33
30
  from novelwriter import CONFIG
34
- from novelwriter.types import QtDialogCancel, QtDialogOk
31
+ from novelwriter.extensions.modified import NDialog
32
+ from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk
35
33
 
36
34
  logger = logging.getLogger(__name__)
37
35
 
38
36
 
39
- class GuiEditLabel(QDialog):
37
+ class GuiEditLabel(NDialog):
40
38
 
41
39
  def __init__(self, parent: QWidget, text: str = "") -> None:
42
40
  super().__init__(parent=parent)
@@ -91,6 +89,6 @@ class GuiEditLabel(QDialog):
91
89
  cls = GuiEditLabel(parent, text=text)
92
90
  cls.exec()
93
91
  label = cls.itemLabel
94
- accepted = cls.result() == QDialog.DialogCode.Accepted
95
- cls.deleteLater()
92
+ accepted = cls.result() == QtAccepted
93
+ cls.softDelete()
96
94
  return label, accepted
@@ -29,27 +29,26 @@ import logging
29
29
  from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
30
30
  from PyQt5.QtGui import QCloseEvent, QKeyEvent, QKeySequence
31
31
  from PyQt5.QtWidgets import (
32
- QAbstractButton, QApplication, QCompleter, QDialog, QDialogButtonBox,
33
- QFileDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget
32
+ QCompleter, QDialogButtonBox, QFileDialog, QHBoxLayout, QLineEdit,
33
+ QPushButton, QVBoxLayout, QWidget
34
34
  )
35
35
 
36
36
  from novelwriter import CONFIG, SHARED
37
- from novelwriter.common import describeFont
38
- from novelwriter.constants import nwConst, nwUnicode
37
+ from novelwriter.common import describeFont, uniqueCompact
38
+ from novelwriter.constants import nwUnicode
39
39
  from novelwriter.dialogs.quotes import GuiQuoteSelect
40
40
  from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
41
- from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NIconToolButton, NSpinBox
41
+ from novelwriter.extensions.modified import (
42
+ NComboBox, NDialog, NDoubleSpinBox, NIconToolButton, NSpinBox
43
+ )
42
44
  from novelwriter.extensions.pagedsidebar import NPagedSideBar
43
45
  from novelwriter.extensions.switch import NSwitch
44
- from novelwriter.types import (
45
- QtAlignCenter, QtDialogApply, QtDialogClose, QtDialogSave, QtRoleAccept,
46
- QtRoleApply, QtRoleReject
47
- )
46
+ from novelwriter.types import QtAlignCenter, QtDialogCancel, QtDialogSave
48
47
 
49
48
  logger = logging.getLogger(__name__)
50
49
 
51
50
 
52
- class GuiPreferences(QDialog):
51
+ class GuiPreferences(NDialog):
53
52
 
54
53
  newPreferencesReady = pyqtSignal(bool, bool, bool, bool)
55
54
 
@@ -64,8 +63,8 @@ class GuiPreferences(QDialog):
64
63
 
65
64
  # Title
66
65
  self.titleLabel = NColourLabel(
67
- self.tr("Preferences"), SHARED.theme.helpText,
68
- parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
66
+ self.tr("Preferences"), self, color=SHARED.theme.helpText,
67
+ scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
69
68
  )
70
69
 
71
70
  # Search Box
@@ -87,8 +86,9 @@ class GuiPreferences(QDialog):
87
86
  self.mainForm.setHelpTextStyle(SHARED.theme.helpText)
88
87
 
89
88
  # Buttons
90
- self.buttonBox = QDialogButtonBox(QtDialogApply | QtDialogSave | QtDialogClose, self)
91
- self.buttonBox.clicked.connect(self._dialogButtonClicked)
89
+ self.buttonBox = QDialogButtonBox(QtDialogSave | QtDialogCancel, self)
90
+ self.buttonBox.accepted.connect(self._doSave)
91
+ self.buttonBox.rejected.connect(self.reject)
92
92
 
93
93
  # Assemble
94
94
  self.searchBox = QHBoxLayout()
@@ -546,7 +546,7 @@ class GuiPreferences(QDialog):
546
546
  self.allowOpenDial = NSwitch(self)
547
547
  self.allowOpenDial.setChecked(CONFIG.allowOpenDial)
548
548
  self.mainForm.addRow(
549
- self.tr("Allow open-ended sialogue"), self.allowOpenDial,
549
+ self.tr("Allow open-ended dialogue"), self.allowOpenDial,
550
550
  self.tr("Highlight dialogue line with no closing quote.")
551
551
  )
552
552
 
@@ -583,8 +583,8 @@ class GuiPreferences(QDialog):
583
583
  self.altDialogClose.setText(CONFIG.altDialogClose)
584
584
 
585
585
  self.mainForm.addRow(
586
- self.tr("Alternative dialog symbols"), [self.altDialogOpen, self.altDialogClose],
587
- self.tr("Custom highlighting of dialog text.")
586
+ self.tr("Alternative dialogue symbols"), [self.altDialogOpen, self.altDialogClose],
587
+ self.tr("Custom highlighting of dialogue text.")
588
588
  )
589
589
 
590
590
  self.highlightEmph = NSwitch(self)
@@ -693,64 +693,62 @@ class GuiPreferences(QDialog):
693
693
  self.sidebar.addButton(title, section)
694
694
  self.mainForm.addGroupLabel(title, section)
695
695
 
696
- self.quoteSym = {}
697
-
698
696
  # Single Quote Style
699
- self.quoteSym["SO"] = QLineEdit(self)
700
- self.quoteSym["SO"].setMaxLength(1)
701
- self.quoteSym["SO"].setReadOnly(True)
702
- self.quoteSym["SO"].setFixedWidth(boxFixed)
703
- self.quoteSym["SO"].setAlignment(QtAlignCenter)
704
- self.quoteSym["SO"].setText(CONFIG.fmtSQuoteOpen)
705
- self.btnSingleStyleO = NIconToolButton(self, iSz, "quote")
706
- self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
707
- self.mainForm.addRow(
708
- self.tr("Single quote open style"), self.quoteSym["SO"],
697
+ self.fmtSQuoteOpen = QLineEdit(self)
698
+ self.fmtSQuoteOpen.setMaxLength(1)
699
+ self.fmtSQuoteOpen.setReadOnly(True)
700
+ self.fmtSQuoteOpen.setFixedWidth(boxFixed)
701
+ self.fmtSQuoteOpen.setAlignment(QtAlignCenter)
702
+ self.fmtSQuoteOpen.setText(CONFIG.fmtSQuoteOpen)
703
+ self.btnSQuoteOpen = NIconToolButton(self, iSz, "quote")
704
+ self.btnSQuoteOpen.clicked.connect(self._changeSingleQuoteOpen)
705
+ self.mainForm.addRow(
706
+ self.tr("Single quote open style"), self.fmtSQuoteOpen,
709
707
  self.tr("The symbol to use for a leading single quote."),
710
- button=self.btnSingleStyleO
708
+ button=self.btnSQuoteOpen
711
709
  )
712
710
 
713
- self.quoteSym["SC"] = QLineEdit(self)
714
- self.quoteSym["SC"].setMaxLength(1)
715
- self.quoteSym["SC"].setReadOnly(True)
716
- self.quoteSym["SC"].setFixedWidth(boxFixed)
717
- self.quoteSym["SC"].setAlignment(QtAlignCenter)
718
- self.quoteSym["SC"].setText(CONFIG.fmtSQuoteClose)
719
- self.btnSingleStyleC = NIconToolButton(self, iSz, "quote")
720
- self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
711
+ self.fmtSQuoteClose = QLineEdit(self)
712
+ self.fmtSQuoteClose.setMaxLength(1)
713
+ self.fmtSQuoteClose.setReadOnly(True)
714
+ self.fmtSQuoteClose.setFixedWidth(boxFixed)
715
+ self.fmtSQuoteClose.setAlignment(QtAlignCenter)
716
+ self.fmtSQuoteClose.setText(CONFIG.fmtSQuoteClose)
717
+ self.btnSQuoteClose = NIconToolButton(self, iSz, "quote")
718
+ self.btnSQuoteClose.clicked.connect(self._changeSingleQuoteClose)
721
719
  self.mainForm.addRow(
722
- self.tr("Single quote close style"), self.quoteSym["SC"],
720
+ self.tr("Single quote close style"), self.fmtSQuoteClose,
723
721
  self.tr("The symbol to use for a trailing single quote."),
724
- button=self.btnSingleStyleC
722
+ button=self.btnSQuoteClose
725
723
  )
726
724
 
727
725
  # Double Quote Style
728
- self.quoteSym["DO"] = QLineEdit(self)
729
- self.quoteSym["DO"].setMaxLength(1)
730
- self.quoteSym["DO"].setReadOnly(True)
731
- self.quoteSym["DO"].setFixedWidth(boxFixed)
732
- self.quoteSym["DO"].setAlignment(QtAlignCenter)
733
- self.quoteSym["DO"].setText(CONFIG.fmtDQuoteOpen)
734
- self.btnDoubleStyleO = NIconToolButton(self, iSz, "quote")
735
- self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
736
- self.mainForm.addRow(
737
- self.tr("Double quote open style"), self.quoteSym["DO"],
726
+ self.fmtDQuoteOpen = QLineEdit(self)
727
+ self.fmtDQuoteOpen.setMaxLength(1)
728
+ self.fmtDQuoteOpen.setReadOnly(True)
729
+ self.fmtDQuoteOpen.setFixedWidth(boxFixed)
730
+ self.fmtDQuoteOpen.setAlignment(QtAlignCenter)
731
+ self.fmtDQuoteOpen.setText(CONFIG.fmtDQuoteOpen)
732
+ self.btnDQuoteOpen = NIconToolButton(self, iSz, "quote")
733
+ self.btnDQuoteOpen.clicked.connect(self._changeDoubleQuoteOpen)
734
+ self.mainForm.addRow(
735
+ self.tr("Double quote open style"), self.fmtDQuoteOpen,
738
736
  self.tr("The symbol to use for a leading double quote."),
739
- button=self.btnDoubleStyleO
737
+ button=self.btnDQuoteOpen
740
738
  )
741
739
 
742
- self.quoteSym["DC"] = QLineEdit(self)
743
- self.quoteSym["DC"].setMaxLength(1)
744
- self.quoteSym["DC"].setReadOnly(True)
745
- self.quoteSym["DC"].setFixedWidth(boxFixed)
746
- self.quoteSym["DC"].setAlignment(QtAlignCenter)
747
- self.quoteSym["DC"].setText(CONFIG.fmtDQuoteClose)
748
- self.btnDoubleStyleC = NIconToolButton(self, iSz, "quote")
749
- self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
740
+ self.fmtDQuoteClose = QLineEdit(self)
741
+ self.fmtDQuoteClose.setMaxLength(1)
742
+ self.fmtDQuoteClose.setReadOnly(True)
743
+ self.fmtDQuoteClose.setFixedWidth(boxFixed)
744
+ self.fmtDQuoteClose.setAlignment(QtAlignCenter)
745
+ self.fmtDQuoteClose.setText(CONFIG.fmtDQuoteClose)
746
+ self.btnDQuoteClose = NIconToolButton(self, iSz, "quote")
747
+ self.btnDQuoteClose.clicked.connect(self._changeDoubleQuoteClose)
750
748
  self.mainForm.addRow(
751
- self.tr("Double quote close style"), self.quoteSym["DC"],
749
+ self.tr("Double quote close style"), self.fmtDQuoteClose,
752
750
  self.tr("The symbol to use for a trailing double quote."),
753
- button=self.btnDoubleStyleC
751
+ button=self.btnDQuoteClose
754
752
  )
755
753
 
756
754
  self.mainForm.finalise()
@@ -767,13 +765,14 @@ class GuiPreferences(QDialog):
767
765
  logger.debug("Close: GuiPreferences")
768
766
  self._saveWindowSize()
769
767
  event.accept()
770
- QApplication.processEvents()
771
- self.done(nwConst.DLG_FINISHED)
772
- self.deleteLater()
768
+ self.softDelete()
773
769
  return
774
770
 
775
771
  def keyPressEvent(self, event: QKeyEvent) -> None:
776
- """Overload keyPressEvent to block enter key to save."""
772
+ """Overload keyPressEvent and only accept escape. The main
773
+ purpose here is to prevent Enter/Return from closing the dialog
774
+ as it is used for the search box.
775
+ """
777
776
  if event.matches(QKeySequence.StandardKey.Cancel):
778
777
  self.close()
779
778
  event.ignore()
@@ -783,19 +782,6 @@ class GuiPreferences(QDialog):
783
782
  # Private Slots
784
783
  ##
785
784
 
786
- @pyqtSlot("QAbstractButton*")
787
- def _dialogButtonClicked(self, button: QAbstractButton) -> None:
788
- """Handle button clicks from the dialog button box."""
789
- role = self.buttonBox.buttonRole(button)
790
- if role == QtRoleApply:
791
- self._saveValues()
792
- elif role == QtRoleAccept:
793
- self._saveValues()
794
- self.close()
795
- elif role == QtRoleReject:
796
- self.close()
797
- return
798
-
799
785
  @pyqtSlot(int)
800
786
  def _sidebarClicked(self, section: int) -> None:
801
787
  """Process a user request to switch page."""
@@ -855,11 +841,36 @@ class GuiPreferences(QDialog):
855
841
  self.fmtPadThin.setEnabled(state)
856
842
  return
857
843
 
858
- def _getQuote(self, qType: str) -> None:
859
- """Dialog for single quote open."""
860
- quote, status = GuiQuoteSelect.getQuote(self, current=self.quoteSym[qType].text())
844
+ @pyqtSlot()
845
+ def _changeSingleQuoteOpen(self) -> None:
846
+ """Change single quote open style."""
847
+ quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtSQuoteOpen.text())
861
848
  if status:
862
- self.quoteSym[qType].setText(quote)
849
+ self.fmtSQuoteOpen.setText(quote)
850
+ return
851
+
852
+ @pyqtSlot()
853
+ def _changeSingleQuoteClose(self) -> None:
854
+ """Change single quote close style."""
855
+ quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtSQuoteClose.text())
856
+ if status:
857
+ self.fmtSQuoteClose.setText(quote)
858
+ return
859
+
860
+ @pyqtSlot()
861
+ def _changeDoubleQuoteOpen(self) -> None:
862
+ """Change double quote open style."""
863
+ quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtDQuoteOpen.text())
864
+ if status:
865
+ self.fmtDQuoteOpen.setText(quote)
866
+ return
867
+
868
+ @pyqtSlot()
869
+ def _changeDoubleQuoteClose(self) -> None:
870
+ """Change double quote close style."""
871
+ quote, status = GuiQuoteSelect.getQuote(self, current=self.fmtDQuoteClose.text())
872
+ if status:
873
+ self.fmtDQuoteClose.setText(quote)
863
874
  return
864
875
 
865
876
  ##
@@ -871,7 +882,7 @@ class GuiPreferences(QDialog):
871
882
  CONFIG.setPreferencesWinSize(self.width(), self.height())
872
883
  return
873
884
 
874
- def _saveValues(self) -> None:
885
+ def _doSave(self) -> None:
875
886
  """Save the values set in the form."""
876
887
  updateTheme = False
877
888
  needsRestart = False
@@ -879,8 +890,8 @@ class GuiPreferences(QDialog):
879
890
  refreshTree = False
880
891
 
881
892
  # Appearance
882
- guiLocale = self.guiLocale.currentData()
883
- guiTheme = self.guiTheme.currentData()
893
+ guiLocale = self.guiLocale.currentData()
894
+ guiTheme = self.guiTheme.currentData()
884
895
 
885
896
  updateTheme |= CONFIG.guiTheme != guiTheme
886
897
  needsRestart |= CONFIG.guiLocale != guiLocale
@@ -941,8 +952,8 @@ class GuiPreferences(QDialog):
941
952
  # Text Highlighting
942
953
  dialogueStyle = self.dialogStyle.currentData()
943
954
  allowOpenDial = self.allowOpenDial.isChecked()
944
- narratorBreak = self.narratorBreak.text()
945
- dialogueLine = self.dialogLine.text()
955
+ narratorBreak = self.narratorBreak.text().strip()
956
+ dialogueLine = self.dialogLine.text().strip()
946
957
  altDialogOpen = self.altDialogOpen.text()
947
958
  altDialogClose = self.altDialogClose.text()
948
959
  highlightEmph = self.highlightEmph.isChecked()
@@ -972,18 +983,20 @@ class GuiPreferences(QDialog):
972
983
  CONFIG.doReplaceDQuote = self.doReplaceDQuote.isChecked()
973
984
  CONFIG.doReplaceDash = self.doReplaceDash.isChecked()
974
985
  CONFIG.doReplaceDots = self.doReplaceDots.isChecked()
975
- CONFIG.fmtPadBefore = self.fmtPadBefore.text().strip()
976
- CONFIG.fmtPadAfter = self.fmtPadAfter.text().strip()
986
+ CONFIG.fmtPadBefore = uniqueCompact(self.fmtPadBefore.text())
987
+ CONFIG.fmtPadAfter = uniqueCompact(self.fmtPadAfter.text())
977
988
  CONFIG.fmtPadThin = self.fmtPadThin.isChecked()
978
989
 
979
990
  # Quotation Style
980
- CONFIG.fmtSQuoteOpen = self.quoteSym["SO"].text()
981
- CONFIG.fmtSQuoteClose = self.quoteSym["SC"].text()
982
- CONFIG.fmtDQuoteOpen = self.quoteSym["DO"].text()
983
- CONFIG.fmtDQuoteClose = self.quoteSym["DC"].text()
991
+ CONFIG.fmtSQuoteOpen = self.fmtSQuoteOpen.text()
992
+ CONFIG.fmtSQuoteClose = self.fmtSQuoteClose.text()
993
+ CONFIG.fmtDQuoteOpen = self.fmtDQuoteOpen.text()
994
+ CONFIG.fmtDQuoteClose = self.fmtDQuoteClose.text()
984
995
 
985
996
  # Finalise
986
997
  CONFIG.saveConfig()
987
998
  self.newPreferencesReady.emit(needsRestart, refreshTree, updateTheme, updateSyntax)
988
999
 
1000
+ self.close()
1001
+
989
1002
  return