novelWriter 2.5b1__py3-none-any.whl → 2.5rc1__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.
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/METADATA +1 -1
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/RECORD +61 -61
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +3 -3
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/project_pt_BR.json +74 -74
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/themes/cyberpunk_night.conf +1 -0
- novelwriter/assets/themes/default_dark.conf +1 -0
- novelwriter/assets/themes/default_light.conf +1 -0
- novelwriter/assets/themes/dracula.conf +1 -0
- novelwriter/assets/themes/solarized_dark.conf +1 -0
- novelwriter/assets/themes/solarized_light.conf +1 -0
- novelwriter/common.py +2 -3
- novelwriter/config.py +67 -15
- novelwriter/constants.py +8 -10
- novelwriter/core/buildsettings.py +5 -3
- novelwriter/core/coretools.py +3 -1
- novelwriter/core/docbuild.py +1 -0
- novelwriter/core/tohtml.py +69 -29
- novelwriter/core/tokenizer.py +83 -14
- novelwriter/core/toodt.py +48 -21
- novelwriter/core/toqdoc.py +25 -9
- novelwriter/dialogs/about.py +10 -15
- novelwriter/dialogs/docmerge.py +16 -16
- novelwriter/dialogs/docsplit.py +16 -16
- novelwriter/dialogs/editlabel.py +6 -8
- novelwriter/dialogs/preferences.py +94 -68
- novelwriter/dialogs/projectsettings.py +10 -10
- novelwriter/dialogs/quotes.py +9 -5
- novelwriter/dialogs/wordlist.py +6 -6
- novelwriter/enum.py +4 -5
- novelwriter/extensions/configlayout.py +23 -4
- novelwriter/extensions/modified.py +22 -3
- novelwriter/extensions/{circularprogress.py → progressbars.py} +26 -3
- novelwriter/extensions/statusled.py +28 -22
- novelwriter/gui/doceditor.py +20 -11
- novelwriter/gui/dochighlight.py +30 -39
- novelwriter/gui/docviewer.py +21 -14
- novelwriter/gui/mainmenu.py +11 -11
- novelwriter/gui/outline.py +3 -3
- novelwriter/gui/projtree.py +19 -28
- novelwriter/gui/search.py +10 -1
- novelwriter/gui/statusbar.py +25 -29
- novelwriter/gui/theme.py +3 -0
- novelwriter/guimain.py +91 -84
- novelwriter/shared.py +10 -8
- novelwriter/text/patterns.py +113 -0
- novelwriter/tools/dictionaries.py +2 -8
- novelwriter/tools/lipsum.py +8 -12
- novelwriter/tools/manusbuild.py +9 -9
- novelwriter/tools/manuscript.py +10 -5
- novelwriter/tools/manussettings.py +7 -3
- novelwriter/tools/noveldetails.py +10 -10
- novelwriter/tools/welcome.py +10 -10
- novelwriter/tools/writingstats.py +3 -3
- novelwriter/types.py +5 -2
- novelwriter/extensions/simpleprogress.py +0 -53
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5b1.dist-info → novelWriter-2.5rc1.dist-info}/top_level.txt +0 -0
novelwriter/guimain.py
CHANGED
@@ -44,7 +44,7 @@ from novelwriter.dialogs.about import GuiAbout
|
|
44
44
|
from novelwriter.dialogs.preferences import GuiPreferences
|
45
45
|
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
46
46
|
from novelwriter.dialogs.wordlist import GuiWordList
|
47
|
-
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwView
|
47
|
+
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwFocus, nwItemType, nwView
|
48
48
|
from novelwriter.gui.doceditor import GuiDocEditor
|
49
49
|
from novelwriter.gui.docviewer import GuiDocViewer
|
50
50
|
from novelwriter.gui.docviewerpanel import GuiDocViewerPanel
|
@@ -210,67 +210,68 @@ class GuiMain(QMainWindow):
|
|
210
210
|
# Connect Signals
|
211
211
|
# ===============
|
212
212
|
|
213
|
-
SHARED.projectStatusChanged.connect(self.mainStatus.updateProjectStatus)
|
214
|
-
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
|
215
|
-
SHARED.spellLanguageChanged.connect(self.mainStatus.setLanguage)
|
216
213
|
SHARED.focusModeChanged.connect(self._focusModeChanged)
|
214
|
+
SHARED.indexAvailable.connect(self.docViewerPanel.indexHasAppeared)
|
215
|
+
SHARED.indexChangedTags.connect(self.docEditor.updateChangedTags)
|
217
216
|
SHARED.indexChangedTags.connect(self.docViewerPanel.updateChangedTags)
|
217
|
+
SHARED.indexCleared.connect(self.docViewerPanel.indexWasCleared)
|
218
218
|
SHARED.indexScannedText.connect(self.docViewerPanel.projectItemChanged)
|
219
|
-
SHARED.indexScannedText.connect(self.projView.updateItemValues)
|
220
219
|
SHARED.indexScannedText.connect(self.itemDetails.updateViewBox)
|
221
|
-
SHARED.
|
222
|
-
SHARED.indexAvailable.connect(self.docViewerPanel.indexHasAppeared)
|
220
|
+
SHARED.indexScannedText.connect(self.projView.updateItemValues)
|
223
221
|
SHARED.mainClockTick.connect(self._timeTick)
|
222
|
+
SHARED.projectStatusChanged.connect(self.mainStatus.updateProjectStatus)
|
223
|
+
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
|
224
|
+
SHARED.spellLanguageChanged.connect(self.mainStatus.setLanguage)
|
224
225
|
|
225
226
|
self.mainMenu.requestDocAction.connect(self._passDocumentAction)
|
226
227
|
self.mainMenu.requestDocInsert.connect(self._passDocumentInsert)
|
227
228
|
self.mainMenu.requestDocInsertText.connect(self._passDocumentInsert)
|
228
229
|
self.mainMenu.requestDocKeyWordInsert.connect(self.docEditor.insertKeyWord)
|
229
|
-
self.mainMenu.requestFocusChange.connect(self.
|
230
|
+
self.mainMenu.requestFocusChange.connect(self._switchFocus)
|
230
231
|
self.mainMenu.requestViewChange.connect(self._changeView)
|
231
232
|
|
232
233
|
self.sideBar.requestViewChange.connect(self._changeView)
|
233
234
|
|
234
|
-
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
235
235
|
self.projView.openDocumentRequest.connect(self._openDocument)
|
236
|
-
self.projView.
|
236
|
+
self.projView.projectSettingsRequest.connect(self.showProjectSettingsDialog)
|
237
|
+
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
238
|
+
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
239
|
+
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
240
|
+
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
237
241
|
self.projView.treeItemChanged.connect(self.docEditor.updateDocInfo)
|
238
242
|
self.projView.treeItemChanged.connect(self.docViewer.updateDocInfo)
|
239
|
-
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
|
240
243
|
self.projView.treeItemChanged.connect(self.docViewerPanel.projectItemChanged)
|
241
|
-
self.projView.
|
242
|
-
self.projView.
|
243
|
-
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
244
|
-
self.projView.projectSettingsRequest.connect(self.showProjectSettingsDialog)
|
244
|
+
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
|
245
|
+
self.projView.wordCountsChanged.connect(self._updateStatusWordCount)
|
245
246
|
|
246
|
-
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
247
247
|
self.novelView.openDocumentRequest.connect(self._openDocument)
|
248
|
+
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
248
249
|
|
249
250
|
self.projSearch.openDocumentSelectRequest.connect(self._openDocumentSelection)
|
250
251
|
self.projSearch.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
251
252
|
|
252
|
-
self.docEditor.
|
253
|
+
self.docEditor.closeDocumentRequest.connect(self.closeDocEditor)
|
253
254
|
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
|
254
255
|
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
|
256
|
+
self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
|
257
|
+
self.docEditor.editedStatusChanged.connect(self.mainStatus.updateDocumentStatus)
|
255
258
|
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
256
|
-
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
|
257
259
|
self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
|
258
|
-
self.docEditor.
|
259
|
-
self.docEditor.spellCheckStateChanged.connect(self.mainMenu.setSpellCheckState)
|
260
|
-
self.docEditor.closeDocumentRequest.connect(self.closeDocEditor)
|
261
|
-
self.docEditor.toggleFocusModeRequest.connect(self.toggleFocusMode)
|
262
|
-
self.docEditor.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
263
|
-
self.docEditor.requestProjectItemRenamed.connect(self.projView.renameTreeItem)
|
260
|
+
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
|
264
261
|
self.docEditor.requestNewNoteCreation.connect(self.projView.createNewNote)
|
265
|
-
self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
|
266
262
|
self.docEditor.requestNextDocument.connect(self.openNextDocument)
|
263
|
+
self.docEditor.requestProjectItemRenamed.connect(self.projView.renameTreeItem)
|
264
|
+
self.docEditor.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
265
|
+
self.docEditor.spellCheckStateChanged.connect(self.mainMenu.setSpellCheckState)
|
266
|
+
self.docEditor.statusMessage.connect(self.mainStatus.setStatusMessage)
|
267
|
+
self.docEditor.toggleFocusModeRequest.connect(self.toggleFocusMode)
|
267
268
|
|
269
|
+
self.docViewer.closeDocumentRequest.connect(self.closeDocViewer)
|
268
270
|
self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle)
|
269
271
|
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
|
270
|
-
self.docViewer.closeDocumentRequest.connect(self.closeDocViewer)
|
271
272
|
self.docViewer.reloadDocumentRequest.connect(self._reloadViewer)
|
272
|
-
self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility)
|
273
273
|
self.docViewer.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
274
|
+
self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility)
|
274
275
|
|
275
276
|
self.docViewerPanel.loadDocumentTagRequest.connect(self._followTag)
|
276
277
|
self.docViewerPanel.openDocumentRequest.connect(self._openDocument)
|
@@ -304,9 +305,6 @@ class GuiMain(QMainWindow):
|
|
304
305
|
self.keyEscape.setKey(Qt.Key.Key_Escape)
|
305
306
|
self.keyEscape.activated.connect(self._keyPressEscape)
|
306
307
|
|
307
|
-
# Check that config loaded fine
|
308
|
-
self.reportConfErr()
|
309
|
-
|
310
308
|
# Initialise Main GUI
|
311
309
|
self.initMain()
|
312
310
|
self.asProjTimer.start()
|
@@ -328,6 +326,15 @@ class GuiMain(QMainWindow):
|
|
328
326
|
|
329
327
|
def postLaunchTasks(self, cmdOpen: str | None) -> None:
|
330
328
|
"""Process tasks after the main window has been created."""
|
329
|
+
QApplication.processEvents()
|
330
|
+
app = QApplication.instance()
|
331
|
+
if isinstance(app, QApplication):
|
332
|
+
app.focusChanged.connect(self._appFocusChanged)
|
333
|
+
|
334
|
+
# Check that config loaded fine
|
335
|
+
if CONFIG.hasError:
|
336
|
+
SHARED.error(CONFIG.errorText())
|
337
|
+
|
331
338
|
if cmdOpen:
|
332
339
|
QApplication.processEvents()
|
333
340
|
logger.info("Command line path: %s", cmdOpen)
|
@@ -616,6 +623,10 @@ class GuiMain(QMainWindow):
|
|
616
623
|
# Make sure main tab is in Editor view
|
617
624
|
self._changeView(nwView.EDITOR)
|
618
625
|
|
626
|
+
# If we're loading the document in the editor, it may need to be saved
|
627
|
+
if tHandle == self.docEditor.docHandle and self.docEditor.docChanged:
|
628
|
+
self.saveDocument()
|
629
|
+
|
619
630
|
logger.debug("Viewing document with handle '%s'", tHandle)
|
620
631
|
updateHistory = tHandle != self.docViewer.docHandle
|
621
632
|
if self.docViewer.loadText(tHandle, updateHistory=updateHistory):
|
@@ -647,7 +658,7 @@ class GuiMain(QMainWindow):
|
|
647
658
|
logger.error("No project open")
|
648
659
|
return False
|
649
660
|
|
650
|
-
lastPath = CONFIG.lastPath()
|
661
|
+
lastPath = CONFIG.lastPath("import")
|
651
662
|
ffilter = formatFileFilter(["*.txt", "*.md", "*.nwd", "*"])
|
652
663
|
loadFile, _ = QFileDialog.getOpenFileName(
|
653
664
|
self, self.tr("Import File"), str(lastPath), filter=ffilter
|
@@ -662,7 +673,7 @@ class GuiMain(QMainWindow):
|
|
662
673
|
try:
|
663
674
|
with open(loadFile, mode="rt", encoding="utf-8") as inFile:
|
664
675
|
text = inFile.read()
|
665
|
-
CONFIG.setLastPath(loadFile)
|
676
|
+
CONFIG.setLastPath("import", loadFile)
|
666
677
|
except Exception as exc:
|
667
678
|
SHARED.error(self.tr(
|
668
679
|
"Could not read file. The file must be an existing text file."
|
@@ -719,14 +730,6 @@ class GuiMain(QMainWindow):
|
|
719
730
|
|
720
731
|
return
|
721
732
|
|
722
|
-
def editItemLabel(self, tHandle: str | None = None) -> None:
|
723
|
-
"""Open the edit item dialog."""
|
724
|
-
if SHARED.hasProject:
|
725
|
-
if tHandle is None and (self.docEditor.anyFocus() or SHARED.focusMode):
|
726
|
-
tHandle = self.docEditor.docHandle
|
727
|
-
self.projView.renameTreeItem(tHandle)
|
728
|
-
return
|
729
|
-
|
730
733
|
def rebuildTrees(self) -> None:
|
731
734
|
"""Rebuild the project tree."""
|
732
735
|
self.projView.populateTree()
|
@@ -748,7 +751,6 @@ class GuiMain(QMainWindow):
|
|
748
751
|
self.mainStatus.setStatusMessage(
|
749
752
|
self.tr("Indexing completed in {0} ms").format(f"{(tEnd - tStart)*1000.0:.1f}")
|
750
753
|
)
|
751
|
-
self.docEditor.updateTagHighLighting()
|
752
754
|
self._updateStatusWordCount()
|
753
755
|
QApplication.restoreOverrideCursor()
|
754
756
|
|
@@ -800,7 +802,8 @@ class GuiMain(QMainWindow):
|
|
800
802
|
def showBuildManuscriptDialog(self) -> None:
|
801
803
|
"""Open the build manuscript dialog."""
|
802
804
|
if SHARED.hasProject:
|
803
|
-
dialog
|
805
|
+
if not (dialog := SHARED.findTopLevelWidget(GuiManuscript)):
|
806
|
+
dialog = GuiManuscript(self)
|
804
807
|
dialog.activateDialog()
|
805
808
|
dialog.loadContent()
|
806
809
|
return
|
@@ -818,7 +821,8 @@ class GuiMain(QMainWindow):
|
|
818
821
|
def showWritingStatsDialog(self) -> None:
|
819
822
|
"""Open the session stats dialog."""
|
820
823
|
if SHARED.hasProject:
|
821
|
-
dialog
|
824
|
+
if not (dialog := SHARED.findTopLevelWidget(GuiWritingStats)):
|
825
|
+
dialog = GuiWritingStats(self)
|
822
826
|
dialog.activateDialog()
|
823
827
|
dialog.populateGUI()
|
824
828
|
return
|
@@ -827,8 +831,7 @@ class GuiMain(QMainWindow):
|
|
827
831
|
def showAboutNWDialog(self) -> None:
|
828
832
|
"""Show the novelWriter about dialog."""
|
829
833
|
dialog = GuiAbout(self)
|
830
|
-
dialog.
|
831
|
-
dialog.populateGUI()
|
834
|
+
dialog.exec()
|
832
835
|
return
|
833
836
|
|
834
837
|
@pyqtSlot()
|
@@ -848,15 +851,6 @@ class GuiMain(QMainWindow):
|
|
848
851
|
SHARED.error(self.tr("Could not initialise the dialog."))
|
849
852
|
return
|
850
853
|
|
851
|
-
def reportConfErr(self) -> None:
|
852
|
-
"""Checks if the Config module has any errors to report, and let
|
853
|
-
the user know if this is the case. The Config module caches
|
854
|
-
errors since it is initialised before the GUI itself.
|
855
|
-
"""
|
856
|
-
if CONFIG.hasError:
|
857
|
-
SHARED.error(CONFIG.errorText())
|
858
|
-
return
|
859
|
-
|
860
854
|
##
|
861
855
|
# Main Window Actions
|
862
856
|
##
|
@@ -887,9 +881,7 @@ class GuiMain(QMainWindow):
|
|
887
881
|
|
888
882
|
if SHARED.hasProject:
|
889
883
|
self.closeProject(True)
|
890
|
-
|
891
884
|
CONFIG.saveConfig()
|
892
|
-
self.reportConfErr()
|
893
885
|
|
894
886
|
QApplication.quit()
|
895
887
|
|
@@ -957,14 +949,37 @@ class GuiMain(QMainWindow):
|
|
957
949
|
SHARED.setFocusMode(not SHARED.focusMode)
|
958
950
|
return
|
959
951
|
|
952
|
+
##
|
953
|
+
# Private Slots
|
954
|
+
##
|
955
|
+
|
956
|
+
@pyqtSlot("QWidget*", "QWidget*")
|
957
|
+
def _appFocusChanged(self, old: QWidget, new: QWidget) -> None:
|
958
|
+
"""Alert main widgets that they have received or lost focus."""
|
959
|
+
if isinstance(new, QWidget):
|
960
|
+
docEditor = False
|
961
|
+
docViewer = False
|
962
|
+
if self.docEditor.isAncestorOf(new):
|
963
|
+
docEditor = True
|
964
|
+
elif self.docViewer.isAncestorOf(new):
|
965
|
+
docViewer = True
|
966
|
+
|
967
|
+
self.docEditor.changeFocusState(docEditor)
|
968
|
+
self.docViewer.changeFocusState(docViewer)
|
969
|
+
|
970
|
+
logger.debug("Main focus switched to: %s", type(new).__name__)
|
971
|
+
|
972
|
+
return
|
973
|
+
|
960
974
|
@pyqtSlot(bool)
|
961
975
|
def _focusModeChanged(self, focusMode: bool) -> None:
|
962
|
-
"""Handle change of focus mode. The Main GUI Focus Mode hides
|
963
|
-
view, statusbar and menu.
|
976
|
+
"""Handle change of focus mode. The Main GUI Focus Mode hides
|
977
|
+
tree, view, statusbar and menu.
|
964
978
|
"""
|
965
979
|
if focusMode:
|
966
980
|
logger.debug("Activating Focus Mode")
|
967
|
-
self.
|
981
|
+
self._changeView(nwView.EDITOR)
|
982
|
+
self.docEditor.setFocus()
|
968
983
|
else:
|
969
984
|
logger.debug("Deactivating Focus Mode")
|
970
985
|
|
@@ -987,10 +1002,10 @@ class GuiMain(QMainWindow):
|
|
987
1002
|
self.docEditor.ensureCursorVisibleNoCentre()
|
988
1003
|
return
|
989
1004
|
|
990
|
-
@pyqtSlot(
|
991
|
-
def
|
1005
|
+
@pyqtSlot(nwFocus)
|
1006
|
+
def _switchFocus(self, paneNo: nwFocus) -> None:
|
992
1007
|
"""Switch focus between main GUI views."""
|
993
|
-
if paneNo ==
|
1008
|
+
if paneNo == nwFocus.TREE:
|
994
1009
|
if self.projStack.currentWidget() is self.projView:
|
995
1010
|
if self.projView.treeHasFocus():
|
996
1011
|
self._changeView(nwView.NOVEL)
|
@@ -1006,21 +1021,19 @@ class GuiMain(QMainWindow):
|
|
1006
1021
|
else:
|
1007
1022
|
self._changeView(nwView.PROJECT)
|
1008
1023
|
self.projView.setTreeFocus()
|
1009
|
-
elif paneNo ==
|
1010
|
-
self._changeView(nwView.EDITOR)
|
1011
|
-
self.docEditor.setFocus()
|
1012
|
-
elif paneNo == nwWidget.VIEWER:
|
1024
|
+
elif paneNo == nwFocus.DOCUMENT:
|
1013
1025
|
self._changeView(nwView.EDITOR)
|
1014
|
-
self.
|
1015
|
-
|
1026
|
+
if self.docEditor.anyFocus():
|
1027
|
+
self.docViewer.setFocus()
|
1028
|
+
elif self.docViewer.anyFocus():
|
1029
|
+
self.docEditor.setFocus()
|
1030
|
+
else:
|
1031
|
+
self.docEditor.setFocus()
|
1032
|
+
elif paneNo == nwFocus.OUTLINE:
|
1016
1033
|
self._changeView(nwView.OUTLINE)
|
1017
1034
|
self.outlineView.setTreeFocus()
|
1018
1035
|
return
|
1019
1036
|
|
1020
|
-
##
|
1021
|
-
# Private Slots
|
1022
|
-
##
|
1023
|
-
|
1024
1037
|
@pyqtSlot(bool, bool, bool, bool)
|
1025
1038
|
def _processConfigChanges(self, restart: bool, tree: bool, theme: bool, syntax: bool) -> None:
|
1026
1039
|
"""Refresh GUI based on flags from the Preferences dialog."""
|
@@ -1159,16 +1172,13 @@ class GuiMain(QMainWindow):
|
|
1159
1172
|
|
1160
1173
|
@pyqtSlot(nwDocAction)
|
1161
1174
|
def _passDocumentAction(self, action: nwDocAction) -> None:
|
1162
|
-
"""Pass on a document action to the
|
1163
|
-
focus, or
|
1164
|
-
child widgets have focus. If neither has focus, ignore it.
|
1175
|
+
"""Pass on a document action to the editor or viewer based on
|
1176
|
+
which one has focus, or if neither has focus, ignore it.
|
1165
1177
|
"""
|
1166
|
-
if self.
|
1167
|
-
self.docViewer.docAction(action)
|
1168
|
-
elif self.docEditor.hasFocus():
|
1178
|
+
if self.docEditor.hasFocus():
|
1169
1179
|
self.docEditor.docAction(action)
|
1170
|
-
|
1171
|
-
|
1180
|
+
elif self.docViewer.hasFocus():
|
1181
|
+
self.docViewer.docAction(action)
|
1172
1182
|
return
|
1173
1183
|
|
1174
1184
|
@pyqtSlot(str)
|
@@ -1376,10 +1386,7 @@ class GuiMain(QMainWindow):
|
|
1376
1386
|
|
1377
1387
|
def _updateWindowTitle(self, projName: str | None = None) -> None:
|
1378
1388
|
"""Set the window title and add the project's name."""
|
1379
|
-
|
1380
|
-
if projName is not None:
|
1381
|
-
winTitle += " - %s" % projName
|
1382
|
-
self.setWindowTitle(winTitle)
|
1389
|
+
self.setWindowTitle(" - ".join(filter(None, [projName, CONFIG.appName])))
|
1383
1390
|
return
|
1384
1391
|
|
1385
1392
|
def _getTagSource(self, tag: str) -> tuple[str | None, str | None]:
|
novelwriter/shared.py
CHANGED
@@ -172,15 +172,21 @@ class SharedData(QObject):
|
|
172
172
|
logger.debug("Thread Pool Max Count: %d", QThreadPool.globalInstance().maxThreadCount())
|
173
173
|
return
|
174
174
|
|
175
|
-
def
|
175
|
+
def closeEditor(self, tHandle: str | None = None) -> None:
|
176
176
|
"""Close the document editor, optionally a specific document."""
|
177
177
|
if tHandle is None or tHandle == self.mainGui.docEditor.docHandle:
|
178
178
|
self.mainGui.closeDocument()
|
179
179
|
return
|
180
180
|
|
181
|
-
def
|
182
|
-
"""
|
183
|
-
self.mainGui.
|
181
|
+
def saveEditor(self, tHandle: str | None = None) -> None:
|
182
|
+
"""Save the editor content, optionally a specific document."""
|
183
|
+
docEditor = self.mainGui.docEditor
|
184
|
+
if (
|
185
|
+
self.hasProject and docEditor.docHandle
|
186
|
+
and (tHandle is None or tHandle == docEditor.docHandle)
|
187
|
+
):
|
188
|
+
logger.debug("Saving editor document before action")
|
189
|
+
docEditor.saveText()
|
184
190
|
return
|
185
191
|
|
186
192
|
def openProject(self, path: str | Path, clearLock: bool = False) -> bool:
|
@@ -316,7 +322,6 @@ class SharedData(QObject):
|
|
316
322
|
if log:
|
317
323
|
logger.info(self._lastAlert, stacklevel=2)
|
318
324
|
alert.exec()
|
319
|
-
alert.deleteLater()
|
320
325
|
return
|
321
326
|
|
322
327
|
def warn(self, text: str, info: str = "", details: str = "", log: bool = True) -> None:
|
@@ -328,7 +333,6 @@ class SharedData(QObject):
|
|
328
333
|
if log:
|
329
334
|
logger.warning(self._lastAlert, stacklevel=2)
|
330
335
|
alert.exec()
|
331
|
-
alert.deleteLater()
|
332
336
|
return
|
333
337
|
|
334
338
|
def error(self, text: str, info: str = "", details: str = "", log: bool = True,
|
@@ -343,7 +347,6 @@ class SharedData(QObject):
|
|
343
347
|
if log:
|
344
348
|
logger.error(self._lastAlert, stacklevel=2)
|
345
349
|
alert.exec()
|
346
|
-
alert.deleteLater()
|
347
350
|
return
|
348
351
|
|
349
352
|
def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool:
|
@@ -354,7 +357,6 @@ class SharedData(QObject):
|
|
354
357
|
self._lastAlert = alert.logMessage
|
355
358
|
alert.exec()
|
356
359
|
isYes = alert.result() == QMessageBox.StandardButton.Yes
|
357
|
-
alert.deleteLater()
|
358
360
|
return isYes
|
359
361
|
|
360
362
|
##
|
@@ -0,0 +1,113 @@
|
|
1
|
+
"""
|
2
|
+
novelWriter – Text Pattern Functions
|
3
|
+
====================================
|
4
|
+
|
5
|
+
File History:
|
6
|
+
Created: 2024-06-01 [2.5ec1]
|
7
|
+
|
8
|
+
This file is a part of novelWriter
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
|
+
|
11
|
+
This program is free software: you can redistribute it and/or modify
|
12
|
+
it under the terms of the GNU General Public License as published by
|
13
|
+
the Free Software Foundation, either version 3 of the License, or
|
14
|
+
(at your option) any later version.
|
15
|
+
|
16
|
+
This program is distributed in the hope that it will be useful, but
|
17
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
19
|
+
General Public License for more details.
|
20
|
+
|
21
|
+
You should have received a copy of the GNU General Public License
|
22
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
23
|
+
"""
|
24
|
+
from __future__ import annotations
|
25
|
+
|
26
|
+
from PyQt5.QtCore import QRegularExpression
|
27
|
+
|
28
|
+
from novelwriter import CONFIG
|
29
|
+
from novelwriter.constants import nwRegEx
|
30
|
+
from novelwriter.types import QRegExUnicode
|
31
|
+
|
32
|
+
|
33
|
+
class RegExPatterns:
|
34
|
+
|
35
|
+
@property
|
36
|
+
def markdownItalic(self) -> QRegularExpression:
|
37
|
+
"""Markdown italic style."""
|
38
|
+
rxRule = QRegularExpression(nwRegEx.FMT_EI)
|
39
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
40
|
+
return rxRule
|
41
|
+
|
42
|
+
@property
|
43
|
+
def markdownBold(self) -> QRegularExpression:
|
44
|
+
"""Markdown bold style."""
|
45
|
+
rxRule = QRegularExpression(nwRegEx.FMT_EB)
|
46
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
47
|
+
return rxRule
|
48
|
+
|
49
|
+
@property
|
50
|
+
def markdownStrike(self) -> QRegularExpression:
|
51
|
+
"""Markdown strikethrough style."""
|
52
|
+
rxRule = QRegularExpression(nwRegEx.FMT_ST)
|
53
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
54
|
+
return rxRule
|
55
|
+
|
56
|
+
@property
|
57
|
+
def shortcodePlain(self) -> QRegularExpression:
|
58
|
+
"""Plain shortcode style."""
|
59
|
+
rxRule = QRegularExpression(nwRegEx.FMT_SC)
|
60
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
61
|
+
return rxRule
|
62
|
+
|
63
|
+
@property
|
64
|
+
def shortcodeValue(self) -> QRegularExpression:
|
65
|
+
"""Plain shortcode style."""
|
66
|
+
rxRule = QRegularExpression(nwRegEx.FMT_SV)
|
67
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
68
|
+
return rxRule
|
69
|
+
|
70
|
+
@property
|
71
|
+
def dialogStyle(self) -> QRegularExpression:
|
72
|
+
"""Dialogue detection rule based on user settings."""
|
73
|
+
symO = ""
|
74
|
+
symC = ""
|
75
|
+
if CONFIG.dialogStyle in (1, 3):
|
76
|
+
symO += CONFIG.fmtSQuoteOpen
|
77
|
+
symC += CONFIG.fmtSQuoteClose
|
78
|
+
if CONFIG.dialogStyle in (2, 3):
|
79
|
+
symO += CONFIG.fmtDQuoteOpen
|
80
|
+
symC += CONFIG.fmtDQuoteClose
|
81
|
+
|
82
|
+
rxEnd = "|$" if CONFIG.allowOpenDial else ""
|
83
|
+
rxRule = QRegularExpression(f"\\B[{symO}].*?(?:[{symC}]\\B{rxEnd})")
|
84
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
85
|
+
return rxRule
|
86
|
+
|
87
|
+
@property
|
88
|
+
def dialogLine(self) -> QRegularExpression:
|
89
|
+
"""Dialogue line rule based on user settings."""
|
90
|
+
sym = QRegularExpression.escape(CONFIG.dialogLine)
|
91
|
+
rxRule = QRegularExpression(f"^{sym}.*?$")
|
92
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
93
|
+
return rxRule
|
94
|
+
|
95
|
+
@property
|
96
|
+
def narratorBreak(self) -> QRegularExpression:
|
97
|
+
"""Dialogue narrator break rule based on user settings."""
|
98
|
+
sym = QRegularExpression.escape(CONFIG.narratorBreak)
|
99
|
+
rxRule = QRegularExpression(f"\\B{sym}\\S.*?\\S{sym}\\B")
|
100
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
101
|
+
return rxRule
|
102
|
+
|
103
|
+
@property
|
104
|
+
def altDialogStyle(self) -> QRegularExpression:
|
105
|
+
"""Dialogue alternative rule based on user settings."""
|
106
|
+
symO = QRegularExpression.escape(CONFIG.altDialogOpen)
|
107
|
+
symC = QRegularExpression.escape(CONFIG.altDialogClose)
|
108
|
+
rxRule = QRegularExpression(f"\\B{symO}.*?{symC}\\B")
|
109
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
110
|
+
return rxRule
|
111
|
+
|
112
|
+
|
113
|
+
REGEX_PATTERNS = RegExPatterns()
|
@@ -109,7 +109,7 @@ class GuiDictionaries(NNonBlockingDialog):
|
|
109
109
|
|
110
110
|
# Buttons
|
111
111
|
self.buttonBox = QDialogButtonBox(QtDialogClose, self)
|
112
|
-
self.buttonBox.rejected.connect(self.
|
112
|
+
self.buttonBox.rejected.connect(self.reject)
|
113
113
|
|
114
114
|
# Assemble
|
115
115
|
self.innerBox = QVBoxLayout()
|
@@ -172,7 +172,7 @@ class GuiDictionaries(NNonBlockingDialog):
|
|
172
172
|
def closeEvent(self, event: QCloseEvent) -> None:
|
173
173
|
"""Capture the user closing the window."""
|
174
174
|
event.accept()
|
175
|
-
self.
|
175
|
+
self.softDelete()
|
176
176
|
return
|
177
177
|
|
178
178
|
##
|
@@ -220,12 +220,6 @@ class GuiDictionaries(NNonBlockingDialog):
|
|
220
220
|
SHARED.error("Path not found.")
|
221
221
|
return
|
222
222
|
|
223
|
-
@pyqtSlot()
|
224
|
-
def _doClose(self) -> None:
|
225
|
-
"""Close the dialog."""
|
226
|
-
self.close()
|
227
|
-
return
|
228
|
-
|
229
223
|
##
|
230
224
|
# Internal Functions
|
231
225
|
##
|
novelwriter/tools/lipsum.py
CHANGED
@@ -26,34 +26,32 @@ from __future__ import annotations
|
|
26
26
|
import logging
|
27
27
|
import random
|
28
28
|
|
29
|
-
from PyQt5.QtCore import
|
29
|
+
from PyQt5.QtCore import pyqtSlot
|
30
30
|
from PyQt5.QtWidgets import (
|
31
|
-
|
32
|
-
|
31
|
+
QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel, QSpinBox, QVBoxLayout,
|
32
|
+
QWidget
|
33
33
|
)
|
34
34
|
|
35
35
|
from novelwriter import CONFIG, SHARED
|
36
36
|
from novelwriter.common import readTextFile
|
37
|
+
from novelwriter.extensions.modified import NDialog
|
37
38
|
from novelwriter.extensions.switch import NSwitch
|
38
39
|
from novelwriter.types import QtAlignLeft, QtAlignRight, QtDialogClose, QtRoleAction
|
39
40
|
|
40
41
|
logger = logging.getLogger(__name__)
|
41
42
|
|
42
43
|
|
43
|
-
class GuiLipsum(
|
44
|
+
class GuiLipsum(NDialog):
|
44
45
|
|
45
46
|
def __init__(self, parent: QWidget) -> None:
|
46
47
|
super().__init__(parent=parent)
|
47
48
|
|
48
49
|
logger.debug("Create: GuiLipsum")
|
49
50
|
self.setObjectName("GuiLipsum")
|
50
|
-
|
51
|
-
self.setWindowFlag(Qt.WindowType.Tool)
|
51
|
+
self.setWindowTitle(self.tr("Insert Placeholder Text"))
|
52
52
|
|
53
53
|
self._lipsumText = ""
|
54
54
|
|
55
|
-
self.setWindowTitle(self.tr("Insert Placeholder Text"))
|
56
|
-
|
57
55
|
vSp = CONFIG.pxInt(4)
|
58
56
|
nPx = CONFIG.pxInt(64)
|
59
57
|
|
@@ -95,7 +93,7 @@ class GuiLipsum(QDialog):
|
|
95
93
|
|
96
94
|
# Buttons
|
97
95
|
self.buttonBox = QDialogButtonBox(self)
|
98
|
-
self.buttonBox.rejected.connect(self.
|
96
|
+
self.buttonBox.rejected.connect(self.reject)
|
99
97
|
|
100
98
|
self.btnClose = self.buttonBox.addButton(QtDialogClose)
|
101
99
|
self.btnClose.setAutoDefault(False)
|
@@ -104,8 +102,6 @@ class GuiLipsum(QDialog):
|
|
104
102
|
self.btnInsert.clicked.connect(self._doInsert)
|
105
103
|
self.btnInsert.setAutoDefault(False)
|
106
104
|
|
107
|
-
self.rejected.connect(self.close)
|
108
|
-
|
109
105
|
# Assemble
|
110
106
|
self.outerBox = QVBoxLayout()
|
111
107
|
self.outerBox.addLayout(self.innerBox)
|
@@ -132,7 +128,7 @@ class GuiLipsum(QDialog):
|
|
132
128
|
cls = GuiLipsum(parent)
|
133
129
|
cls.exec()
|
134
130
|
text = cls.lipsumText
|
135
|
-
cls.
|
131
|
+
cls.softDelete()
|
136
132
|
return text
|
137
133
|
|
138
134
|
##
|