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.
- {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/METADATA +1 -1
- {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/RECORD +77 -75
- {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +3 -3
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_pl_PL.json +116 -0
- novelwriter/assets/i18n/project_pt_BR.json +74 -74
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/text/credits_en.htm +52 -44
- 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 +12 -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/project.py +15 -4
- novelwriter/core/status.py +4 -1
- novelwriter/core/storage.py +6 -1
- novelwriter/core/tohtml.py +69 -29
- novelwriter/core/tokenizer.py +83 -14
- novelwriter/core/toodt.py +48 -21
- novelwriter/core/toqdoc.py +37 -21
- 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 +106 -93
- novelwriter/dialogs/projectsettings.py +16 -20
- novelwriter/dialogs/quotes.py +9 -5
- novelwriter/dialogs/wordlist.py +6 -6
- novelwriter/enum.py +4 -5
- novelwriter/extensions/configlayout.py +38 -4
- novelwriter/extensions/modified.py +22 -3
- novelwriter/extensions/{circularprogress.py → progressbars.py} +26 -3
- novelwriter/extensions/statusled.py +39 -23
- novelwriter/gui/doceditor.py +22 -13
- novelwriter/gui/dochighlight.py +30 -39
- novelwriter/gui/docviewer.py +24 -15
- novelwriter/gui/docviewerpanel.py +7 -0
- novelwriter/gui/mainmenu.py +11 -11
- novelwriter/gui/outline.py +4 -3
- novelwriter/gui/projtree.py +85 -77
- novelwriter/gui/search.py +10 -1
- novelwriter/gui/statusbar.py +25 -29
- novelwriter/gui/theme.py +3 -0
- novelwriter/guimain.py +139 -124
- novelwriter/shared.py +19 -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 +19 -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.5.1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5b1.dist-info → novelWriter-2.5.1.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
|
@@ -189,9 +189,6 @@ class GuiMain(QMainWindow):
|
|
189
189
|
self.splitView.setVisible(False)
|
190
190
|
self.docEditor.closeSearch()
|
191
191
|
|
192
|
-
# Initialise the Project Tree
|
193
|
-
self.rebuildTrees()
|
194
|
-
|
195
192
|
# Assemble Main Window Elements
|
196
193
|
self.mainBox = QHBoxLayout()
|
197
194
|
self.mainBox.addWidget(self.sideBar)
|
@@ -210,67 +207,70 @@ class GuiMain(QMainWindow):
|
|
210
207
|
# Connect Signals
|
211
208
|
# ===============
|
212
209
|
|
213
|
-
SHARED.projectStatusChanged.connect(self.mainStatus.updateProjectStatus)
|
214
|
-
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
|
215
|
-
SHARED.spellLanguageChanged.connect(self.mainStatus.setLanguage)
|
216
210
|
SHARED.focusModeChanged.connect(self._focusModeChanged)
|
211
|
+
SHARED.indexAvailable.connect(self.docViewerPanel.indexHasAppeared)
|
212
|
+
SHARED.indexChangedTags.connect(self.docEditor.updateChangedTags)
|
217
213
|
SHARED.indexChangedTags.connect(self.docViewerPanel.updateChangedTags)
|
214
|
+
SHARED.indexCleared.connect(self.docViewerPanel.indexWasCleared)
|
218
215
|
SHARED.indexScannedText.connect(self.docViewerPanel.projectItemChanged)
|
219
|
-
SHARED.indexScannedText.connect(self.projView.updateItemValues)
|
220
216
|
SHARED.indexScannedText.connect(self.itemDetails.updateViewBox)
|
221
|
-
SHARED.
|
222
|
-
SHARED.indexAvailable.connect(self.docViewerPanel.indexHasAppeared)
|
217
|
+
SHARED.indexScannedText.connect(self.projView.updateItemValues)
|
223
218
|
SHARED.mainClockTick.connect(self._timeTick)
|
219
|
+
SHARED.projectStatusChanged.connect(self.mainStatus.updateProjectStatus)
|
220
|
+
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
|
221
|
+
SHARED.spellLanguageChanged.connect(self.mainStatus.setLanguage)
|
222
|
+
SHARED.statusLabelsChanged.connect(self.docViewerPanel.updateStatusLabels)
|
223
|
+
SHARED.statusLabelsChanged.connect(self.projView.refreshUserLabels)
|
224
224
|
|
225
225
|
self.mainMenu.requestDocAction.connect(self._passDocumentAction)
|
226
226
|
self.mainMenu.requestDocInsert.connect(self._passDocumentInsert)
|
227
227
|
self.mainMenu.requestDocInsertText.connect(self._passDocumentInsert)
|
228
228
|
self.mainMenu.requestDocKeyWordInsert.connect(self.docEditor.insertKeyWord)
|
229
|
-
self.mainMenu.requestFocusChange.connect(self.
|
229
|
+
self.mainMenu.requestFocusChange.connect(self._switchFocus)
|
230
230
|
self.mainMenu.requestViewChange.connect(self._changeView)
|
231
231
|
|
232
232
|
self.sideBar.requestViewChange.connect(self._changeView)
|
233
233
|
|
234
|
-
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
235
234
|
self.projView.openDocumentRequest.connect(self._openDocument)
|
236
|
-
self.projView.
|
235
|
+
self.projView.projectSettingsRequest.connect(self.showProjectSettingsDialog)
|
236
|
+
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
237
|
+
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
238
|
+
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
239
|
+
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
237
240
|
self.projView.treeItemChanged.connect(self.docEditor.updateDocInfo)
|
238
241
|
self.projView.treeItemChanged.connect(self.docViewer.updateDocInfo)
|
239
|
-
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
|
240
242
|
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)
|
243
|
+
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
|
244
|
+
self.projView.wordCountsChanged.connect(self._updateStatusWordCount)
|
245
245
|
|
246
|
-
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
247
246
|
self.novelView.openDocumentRequest.connect(self._openDocument)
|
247
|
+
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
248
248
|
|
249
249
|
self.projSearch.openDocumentSelectRequest.connect(self._openDocumentSelection)
|
250
250
|
self.projSearch.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
251
251
|
|
252
|
-
self.docEditor.
|
252
|
+
self.docEditor.closeDocumentRequest.connect(self.closeDocEditor)
|
253
253
|
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
|
254
254
|
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
|
255
|
+
self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
|
256
|
+
self.docEditor.editedStatusChanged.connect(self.mainStatus.updateDocumentStatus)
|
255
257
|
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
256
|
-
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
|
257
258
|
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)
|
259
|
+
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
|
264
260
|
self.docEditor.requestNewNoteCreation.connect(self.projView.createNewNote)
|
265
|
-
self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
|
266
261
|
self.docEditor.requestNextDocument.connect(self.openNextDocument)
|
262
|
+
self.docEditor.requestProjectItemRenamed.connect(self.projView.renameTreeItem)
|
263
|
+
self.docEditor.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
264
|
+
self.docEditor.spellCheckStateChanged.connect(self.mainMenu.setSpellCheckState)
|
265
|
+
self.docEditor.statusMessage.connect(self.mainStatus.setStatusMessage)
|
266
|
+
self.docEditor.toggleFocusModeRequest.connect(self.toggleFocusMode)
|
267
267
|
|
268
|
+
self.docViewer.closeDocumentRequest.connect(self.closeDocViewer)
|
268
269
|
self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle)
|
269
270
|
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
|
270
|
-
self.docViewer.closeDocumentRequest.connect(self.closeDocViewer)
|
271
271
|
self.docViewer.reloadDocumentRequest.connect(self._reloadViewer)
|
272
|
-
self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility)
|
273
272
|
self.docViewer.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
273
|
+
self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility)
|
274
274
|
|
275
275
|
self.docViewerPanel.loadDocumentTagRequest.connect(self._followTag)
|
276
276
|
self.docViewerPanel.openDocumentRequest.connect(self._openDocument)
|
@@ -304,9 +304,6 @@ class GuiMain(QMainWindow):
|
|
304
304
|
self.keyEscape.setKey(Qt.Key.Key_Escape)
|
305
305
|
self.keyEscape.activated.connect(self._keyPressEscape)
|
306
306
|
|
307
|
-
# Check that config loaded fine
|
308
|
-
self.reportConfErr()
|
309
|
-
|
310
307
|
# Initialise Main GUI
|
311
308
|
self.initMain()
|
312
309
|
self.asProjTimer.start()
|
@@ -328,6 +325,15 @@ class GuiMain(QMainWindow):
|
|
328
325
|
|
329
326
|
def postLaunchTasks(self, cmdOpen: str | None) -> None:
|
330
327
|
"""Process tasks after the main window has been created."""
|
328
|
+
QApplication.processEvents()
|
329
|
+
app = QApplication.instance()
|
330
|
+
if isinstance(app, QApplication):
|
331
|
+
app.focusChanged.connect(self._appFocusChanged)
|
332
|
+
|
333
|
+
# Check that config loaded fine
|
334
|
+
if CONFIG.hasError:
|
335
|
+
SHARED.error(CONFIG.errorText())
|
336
|
+
|
331
337
|
if cmdOpen:
|
332
338
|
QApplication.processEvents()
|
333
339
|
logger.info("Command line path: %s", cmdOpen)
|
@@ -454,7 +460,6 @@ class GuiMain(QMainWindow):
|
|
454
460
|
|
455
461
|
# Update GUI
|
456
462
|
self._updateWindowTitle(SHARED.project.data.name)
|
457
|
-
self.rebuildTrees()
|
458
463
|
self.docEditor.toggleSpellCheck(SHARED.project.data.spellCheck)
|
459
464
|
self.mainStatus.setRefTime(SHARED.project.projOpened)
|
460
465
|
self.projView.openProjectTasks()
|
@@ -519,8 +524,14 @@ class GuiMain(QMainWindow):
|
|
519
524
|
self.novelView.setActiveHandle(None)
|
520
525
|
return
|
521
526
|
|
522
|
-
def openDocument(
|
523
|
-
|
527
|
+
def openDocument(
|
528
|
+
self,
|
529
|
+
tHandle: str | None,
|
530
|
+
tLine: int | None = None,
|
531
|
+
sTitle: str | None = None,
|
532
|
+
changeFocus: bool = True,
|
533
|
+
doScroll: bool = False
|
534
|
+
) -> bool:
|
524
535
|
"""Open a specific document, optionally at a given line."""
|
525
536
|
if not SHARED.hasProject:
|
526
537
|
logger.error("No project open")
|
@@ -530,9 +541,12 @@ class GuiMain(QMainWindow):
|
|
530
541
|
logger.debug("Requested item '%s' is not a document", tHandle)
|
531
542
|
return False
|
532
543
|
|
544
|
+
if sTitle and tLine is None:
|
545
|
+
if hItem := SHARED.project.index.getItemHeading(tHandle, sTitle):
|
546
|
+
tLine = hItem.line
|
547
|
+
|
533
548
|
self._changeView(nwView.EDITOR)
|
534
|
-
|
535
|
-
if cHandle == tHandle:
|
549
|
+
if tHandle == self.docEditor.docHandle:
|
536
550
|
self.docEditor.setCursorLine(tLine)
|
537
551
|
if changeFocus:
|
538
552
|
self.docEditor.setFocus()
|
@@ -616,6 +630,10 @@ class GuiMain(QMainWindow):
|
|
616
630
|
# Make sure main tab is in Editor view
|
617
631
|
self._changeView(nwView.EDITOR)
|
618
632
|
|
633
|
+
# If we're loading the document in the editor, it may need to be saved
|
634
|
+
if tHandle == self.docEditor.docHandle and self.docEditor.docChanged:
|
635
|
+
self.saveDocument()
|
636
|
+
|
619
637
|
logger.debug("Viewing document with handle '%s'", tHandle)
|
620
638
|
updateHistory = tHandle != self.docViewer.docHandle
|
621
639
|
if self.docViewer.loadText(tHandle, updateHistory=updateHistory):
|
@@ -647,7 +665,7 @@ class GuiMain(QMainWindow):
|
|
647
665
|
logger.error("No project open")
|
648
666
|
return False
|
649
667
|
|
650
|
-
lastPath = CONFIG.lastPath()
|
668
|
+
lastPath = CONFIG.lastPath("import")
|
651
669
|
ffilter = formatFileFilter(["*.txt", "*.md", "*.nwd", "*"])
|
652
670
|
loadFile, _ = QFileDialog.getOpenFileName(
|
653
671
|
self, self.tr("Import File"), str(lastPath), filter=ffilter
|
@@ -662,7 +680,7 @@ class GuiMain(QMainWindow):
|
|
662
680
|
try:
|
663
681
|
with open(loadFile, mode="rt", encoding="utf-8") as inFile:
|
664
682
|
text = inFile.read()
|
665
|
-
CONFIG.setLastPath(loadFile)
|
683
|
+
CONFIG.setLastPath("import", loadFile)
|
666
684
|
except Exception as exc:
|
667
685
|
SHARED.error(self.tr(
|
668
686
|
"Could not read file. The file must be an existing text file."
|
@@ -700,7 +718,6 @@ class GuiMain(QMainWindow):
|
|
700
718
|
if SHARED.hasProject:
|
701
719
|
tHandle = None
|
702
720
|
sTitle = None
|
703
|
-
tLine = None
|
704
721
|
if self.projView.treeHasFocus():
|
705
722
|
tHandle = self.projView.getSelectedHandle()
|
706
723
|
elif self.novelView.treeHasFocus():
|
@@ -711,25 +728,9 @@ class GuiMain(QMainWindow):
|
|
711
728
|
logger.warning("No item selected")
|
712
729
|
return
|
713
730
|
|
714
|
-
if tHandle and sTitle:
|
715
|
-
if hItem := SHARED.project.index.getItemHeading(tHandle, sTitle):
|
716
|
-
tLine = hItem.line
|
717
731
|
if tHandle:
|
718
|
-
self.openDocument(tHandle,
|
719
|
-
|
720
|
-
return
|
721
|
-
|
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
|
732
|
+
self.openDocument(tHandle, sTitle=sTitle, changeFocus=False, doScroll=False)
|
729
733
|
|
730
|
-
def rebuildTrees(self) -> None:
|
731
|
-
"""Rebuild the project tree."""
|
732
|
-
self.projView.populateTree()
|
733
734
|
return
|
734
735
|
|
735
736
|
def rebuildIndex(self, beQuiet: bool = False) -> None:
|
@@ -748,7 +749,6 @@ class GuiMain(QMainWindow):
|
|
748
749
|
self.mainStatus.setStatusMessage(
|
749
750
|
self.tr("Indexing completed in {0} ms").format(f"{(tEnd - tStart)*1000.0:.1f}")
|
750
751
|
)
|
751
|
-
self.docEditor.updateTagHighLighting()
|
752
752
|
self._updateStatusWordCount()
|
753
753
|
QApplication.restoreOverrideCursor()
|
754
754
|
|
@@ -800,7 +800,8 @@ class GuiMain(QMainWindow):
|
|
800
800
|
def showBuildManuscriptDialog(self) -> None:
|
801
801
|
"""Open the build manuscript dialog."""
|
802
802
|
if SHARED.hasProject:
|
803
|
-
dialog
|
803
|
+
if not (dialog := SHARED.findTopLevelWidget(GuiManuscript)):
|
804
|
+
dialog = GuiManuscript(self)
|
804
805
|
dialog.activateDialog()
|
805
806
|
dialog.loadContent()
|
806
807
|
return
|
@@ -818,7 +819,8 @@ class GuiMain(QMainWindow):
|
|
818
819
|
def showWritingStatsDialog(self) -> None:
|
819
820
|
"""Open the session stats dialog."""
|
820
821
|
if SHARED.hasProject:
|
821
|
-
dialog
|
822
|
+
if not (dialog := SHARED.findTopLevelWidget(GuiWritingStats)):
|
823
|
+
dialog = GuiWritingStats(self)
|
822
824
|
dialog.activateDialog()
|
823
825
|
dialog.populateGUI()
|
824
826
|
return
|
@@ -827,8 +829,7 @@ class GuiMain(QMainWindow):
|
|
827
829
|
def showAboutNWDialog(self) -> None:
|
828
830
|
"""Show the novelWriter about dialog."""
|
829
831
|
dialog = GuiAbout(self)
|
830
|
-
dialog.
|
831
|
-
dialog.populateGUI()
|
832
|
+
dialog.exec()
|
832
833
|
return
|
833
834
|
|
834
835
|
@pyqtSlot()
|
@@ -848,15 +849,6 @@ class GuiMain(QMainWindow):
|
|
848
849
|
SHARED.error(self.tr("Could not initialise the dialog."))
|
849
850
|
return
|
850
851
|
|
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
852
|
##
|
861
853
|
# Main Window Actions
|
862
854
|
##
|
@@ -887,9 +879,7 @@ class GuiMain(QMainWindow):
|
|
887
879
|
|
888
880
|
if SHARED.hasProject:
|
889
881
|
self.closeProject(True)
|
890
|
-
|
891
882
|
CONFIG.saveConfig()
|
892
|
-
self.reportConfErr()
|
893
883
|
|
894
884
|
QApplication.quit()
|
895
885
|
|
@@ -957,14 +947,37 @@ class GuiMain(QMainWindow):
|
|
957
947
|
SHARED.setFocusMode(not SHARED.focusMode)
|
958
948
|
return
|
959
949
|
|
950
|
+
##
|
951
|
+
# Private Slots
|
952
|
+
##
|
953
|
+
|
954
|
+
@pyqtSlot("QWidget*", "QWidget*")
|
955
|
+
def _appFocusChanged(self, old: QWidget, new: QWidget) -> None:
|
956
|
+
"""Alert main widgets that they have received or lost focus."""
|
957
|
+
if isinstance(new, QWidget):
|
958
|
+
docEditor = False
|
959
|
+
docViewer = False
|
960
|
+
if self.docEditor.isAncestorOf(new):
|
961
|
+
docEditor = True
|
962
|
+
elif self.docViewer.isAncestorOf(new):
|
963
|
+
docViewer = True
|
964
|
+
|
965
|
+
self.docEditor.changeFocusState(docEditor)
|
966
|
+
self.docViewer.changeFocusState(docViewer)
|
967
|
+
|
968
|
+
logger.debug("Main focus switched to: %s", type(new).__name__)
|
969
|
+
|
970
|
+
return
|
971
|
+
|
960
972
|
@pyqtSlot(bool)
|
961
973
|
def _focusModeChanged(self, focusMode: bool) -> None:
|
962
|
-
"""Handle change of focus mode. The Main GUI Focus Mode hides
|
963
|
-
view, statusbar and menu.
|
974
|
+
"""Handle change of focus mode. The Main GUI Focus Mode hides
|
975
|
+
tree, view, statusbar and menu.
|
964
976
|
"""
|
965
977
|
if focusMode:
|
966
978
|
logger.debug("Activating Focus Mode")
|
967
|
-
self.
|
979
|
+
self._changeView(nwView.EDITOR)
|
980
|
+
self.docEditor.setFocus()
|
968
981
|
else:
|
969
982
|
logger.debug("Deactivating Focus Mode")
|
970
983
|
|
@@ -987,39 +1000,53 @@ class GuiMain(QMainWindow):
|
|
987
1000
|
self.docEditor.ensureCursorVisibleNoCentre()
|
988
1001
|
return
|
989
1002
|
|
990
|
-
@pyqtSlot(
|
991
|
-
def
|
1003
|
+
@pyqtSlot(nwFocus)
|
1004
|
+
def _switchFocus(self, paneNo: nwFocus) -> None:
|
992
1005
|
"""Switch focus between main GUI views."""
|
993
|
-
if paneNo ==
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
+
if paneNo == nwFocus.TREE:
|
1007
|
+
# Decision Matrix
|
1008
|
+
# vM | vP | fP | vN | fN | Focus
|
1009
|
+
# ----|----|----|----|----|---------
|
1010
|
+
# T | T | T | F | F | Novel
|
1011
|
+
# T | T | F | F | F | Project
|
1012
|
+
# T | F | F | T | T | Project
|
1013
|
+
# T | F | F | T | F | Novel
|
1014
|
+
# T | F | F | F | F | Project
|
1015
|
+
# F | T | T | F | F | Project
|
1016
|
+
# F | T | F | F | F | Project
|
1017
|
+
# F | F | F | T | T | Novel
|
1018
|
+
# F | F | F | T | F | Novel
|
1019
|
+
# F | F | F | F | F | Project
|
1020
|
+
|
1021
|
+
vM = self.mainStack.currentWidget() is self.splitMain
|
1022
|
+
vP = self.projStack.currentWidget() is self.projView
|
1023
|
+
vN = self.projStack.currentWidget() is self.novelView
|
1024
|
+
fP = self.projView.treeHasFocus()
|
1025
|
+
fN = self.novelView.treeHasFocus()
|
1026
|
+
|
1027
|
+
self._changeView(nwView.EDITOR)
|
1028
|
+
if (vM and (vP and fP or vN and not fN)) or (not vM and vN):
|
1029
|
+
self._changeView(nwView.NOVEL)
|
1030
|
+
self.novelView.setTreeFocus()
|
1006
1031
|
else:
|
1007
1032
|
self._changeView(nwView.PROJECT)
|
1008
1033
|
self.projView.setTreeFocus()
|
1009
|
-
|
1010
|
-
|
1011
|
-
self.docEditor.setFocus()
|
1012
|
-
elif paneNo == nwWidget.VIEWER:
|
1034
|
+
|
1035
|
+
elif paneNo == nwFocus.DOCUMENT:
|
1013
1036
|
self._changeView(nwView.EDITOR)
|
1014
|
-
self.
|
1015
|
-
|
1037
|
+
hasViewer = self.splitView.isVisible()
|
1038
|
+
if hasViewer and self.docEditor.anyFocus():
|
1039
|
+
self.docViewer.setFocus()
|
1040
|
+
elif hasViewer and self.docViewer.anyFocus():
|
1041
|
+
self.docEditor.setFocus()
|
1042
|
+
else:
|
1043
|
+
self.docEditor.setFocus()
|
1044
|
+
|
1045
|
+
elif paneNo == nwFocus.OUTLINE:
|
1016
1046
|
self._changeView(nwView.OUTLINE)
|
1017
1047
|
self.outlineView.setTreeFocus()
|
1018
|
-
return
|
1019
1048
|
|
1020
|
-
|
1021
|
-
# Private Slots
|
1022
|
-
##
|
1049
|
+
return
|
1023
1050
|
|
1024
1051
|
@pyqtSlot(bool, bool, bool, bool)
|
1025
1052
|
def _processConfigChanges(self, restart: bool, tree: bool, theme: bool, syntax: bool) -> None:
|
@@ -1064,15 +1091,13 @@ class GuiMain(QMainWindow):
|
|
1064
1091
|
|
1065
1092
|
return
|
1066
1093
|
|
1067
|
-
@pyqtSlot(
|
1068
|
-
def _processProjectSettingsChanges(self
|
1094
|
+
@pyqtSlot()
|
1095
|
+
def _processProjectSettingsChanges(self) -> None:
|
1069
1096
|
"""Refresh data dependent on project settings."""
|
1070
1097
|
logger.debug("Applying new project settings")
|
1071
1098
|
SHARED.updateSpellCheckLanguage()
|
1072
1099
|
self.itemDetails.refreshDetails()
|
1073
1100
|
self._updateWindowTitle(SHARED.project.data.name)
|
1074
|
-
if rebuildTrees:
|
1075
|
-
self.rebuildTrees()
|
1076
1101
|
return
|
1077
1102
|
|
1078
1103
|
@pyqtSlot()
|
@@ -1089,7 +1114,7 @@ class GuiMain(QMainWindow):
|
|
1089
1114
|
tHandle, sTitle = self._getTagSource(tag)
|
1090
1115
|
if tHandle is not None:
|
1091
1116
|
if mode == nwDocMode.EDIT:
|
1092
|
-
self.openDocument(tHandle)
|
1117
|
+
self.openDocument(tHandle, sTitle=sTitle)
|
1093
1118
|
elif mode == nwDocMode.VIEW:
|
1094
1119
|
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
|
1095
1120
|
return
|
@@ -1108,11 +1133,7 @@ class GuiMain(QMainWindow):
|
|
1108
1133
|
"""Handle an open document request."""
|
1109
1134
|
if tHandle is not None:
|
1110
1135
|
if mode == nwDocMode.EDIT:
|
1111
|
-
|
1112
|
-
hItem = SHARED.project.index.getItemHeading(tHandle, sTitle)
|
1113
|
-
if hItem is not None:
|
1114
|
-
tLine = hItem.line
|
1115
|
-
self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus)
|
1136
|
+
self.openDocument(tHandle, sTitle=sTitle, changeFocus=setFocus)
|
1116
1137
|
elif mode == nwDocMode.VIEW:
|
1117
1138
|
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
|
1118
1139
|
return
|
@@ -1159,16 +1180,13 @@ class GuiMain(QMainWindow):
|
|
1159
1180
|
|
1160
1181
|
@pyqtSlot(nwDocAction)
|
1161
1182
|
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.
|
1183
|
+
"""Pass on a document action to the editor or viewer based on
|
1184
|
+
which one has focus, or if neither has focus, ignore it.
|
1165
1185
|
"""
|
1166
|
-
if self.
|
1167
|
-
self.docViewer.docAction(action)
|
1168
|
-
elif self.docEditor.hasFocus():
|
1186
|
+
if self.docEditor.hasFocus():
|
1169
1187
|
self.docEditor.docAction(action)
|
1170
|
-
|
1171
|
-
|
1188
|
+
elif self.docViewer.hasFocus():
|
1189
|
+
self.docViewer.docAction(action)
|
1172
1190
|
return
|
1173
1191
|
|
1174
1192
|
@pyqtSlot(str)
|
@@ -1376,10 +1394,7 @@ class GuiMain(QMainWindow):
|
|
1376
1394
|
|
1377
1395
|
def _updateWindowTitle(self, projName: str | None = None) -> None:
|
1378
1396
|
"""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)
|
1397
|
+
self.setWindowTitle(" - ".join(filter(None, [projName, CONFIG.appName])))
|
1383
1398
|
return
|
1384
1399
|
|
1385
1400
|
def _getTagSource(self, tag: str) -> tuple[str | None, str | None]:
|
novelwriter/shared.py
CHANGED
@@ -64,6 +64,7 @@ class SharedData(QObject):
|
|
64
64
|
indexCleared = pyqtSignal()
|
65
65
|
indexAvailable = pyqtSignal()
|
66
66
|
mainClockTick = pyqtSignal()
|
67
|
+
statusLabelsChanged = pyqtSignal(str)
|
67
68
|
|
68
69
|
def __init__(self) -> None:
|
69
70
|
super().__init__()
|
@@ -172,15 +173,21 @@ class SharedData(QObject):
|
|
172
173
|
logger.debug("Thread Pool Max Count: %d", QThreadPool.globalInstance().maxThreadCount())
|
173
174
|
return
|
174
175
|
|
175
|
-
def
|
176
|
+
def closeEditor(self, tHandle: str | None = None) -> None:
|
176
177
|
"""Close the document editor, optionally a specific document."""
|
177
178
|
if tHandle is None or tHandle == self.mainGui.docEditor.docHandle:
|
178
179
|
self.mainGui.closeDocument()
|
179
180
|
return
|
180
181
|
|
181
|
-
def
|
182
|
-
"""
|
183
|
-
self.mainGui.
|
182
|
+
def saveEditor(self, tHandle: str | None = None) -> None:
|
183
|
+
"""Save the editor content, optionally a specific document."""
|
184
|
+
docEditor = self.mainGui.docEditor
|
185
|
+
if (
|
186
|
+
self.hasProject and docEditor.docHandle
|
187
|
+
and (tHandle is None or tHandle == docEditor.docHandle)
|
188
|
+
):
|
189
|
+
logger.debug("Saving editor document before action")
|
190
|
+
docEditor.saveText()
|
184
191
|
return
|
185
192
|
|
186
193
|
def openProject(self, path: str | Path, clearLock: bool = False) -> bool:
|
@@ -303,6 +310,14 @@ class SharedData(QObject):
|
|
303
310
|
self.indexAvailable.emit()
|
304
311
|
return
|
305
312
|
|
313
|
+
def projectSingalProxy(self, data: dict) -> None:
|
314
|
+
"""Emit signals on project data change."""
|
315
|
+
event = data.get("event")
|
316
|
+
logger.debug("Received '%s' event from project data", event)
|
317
|
+
if event == "statusLabels":
|
318
|
+
self.statusLabelsChanged.emit(data.get("kind", ""))
|
319
|
+
return
|
320
|
+
|
306
321
|
##
|
307
322
|
# Alert Boxes
|
308
323
|
##
|
@@ -316,7 +331,6 @@ class SharedData(QObject):
|
|
316
331
|
if log:
|
317
332
|
logger.info(self._lastAlert, stacklevel=2)
|
318
333
|
alert.exec()
|
319
|
-
alert.deleteLater()
|
320
334
|
return
|
321
335
|
|
322
336
|
def warn(self, text: str, info: str = "", details: str = "", log: bool = True) -> None:
|
@@ -328,7 +342,6 @@ class SharedData(QObject):
|
|
328
342
|
if log:
|
329
343
|
logger.warning(self._lastAlert, stacklevel=2)
|
330
344
|
alert.exec()
|
331
|
-
alert.deleteLater()
|
332
345
|
return
|
333
346
|
|
334
347
|
def error(self, text: str, info: str = "", details: str = "", log: bool = True,
|
@@ -343,7 +356,6 @@ class SharedData(QObject):
|
|
343
356
|
if log:
|
344
357
|
logger.error(self._lastAlert, stacklevel=2)
|
345
358
|
alert.exec()
|
346
|
-
alert.deleteLater()
|
347
359
|
return
|
348
360
|
|
349
361
|
def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool:
|
@@ -354,7 +366,6 @@ class SharedData(QObject):
|
|
354
366
|
self._lastAlert = alert.logMessage
|
355
367
|
alert.exec()
|
356
368
|
isYes = alert.result() == QMessageBox.StandardButton.Yes
|
357
|
-
alert.deleteLater()
|
358
369
|
return isYes
|
359
370
|
|
360
371
|
##
|