novelWriter 2.5.3__py3-none-any.whl → 2.6__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.5.3.dist-info → novelWriter-2.6.dist-info}/METADATA +2 -2
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/RECORD +123 -103
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +50 -11
- 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_ru_RU.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_de_DE.json +2 -0
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/i18n/project_en_US.json +2 -0
- novelwriter/assets/i18n/project_it_IT.json +2 -0
- novelwriter/assets/i18n/project_ja_JP.json +2 -0
- novelwriter/assets/i18n/project_nb_NO.json +2 -0
- novelwriter/assets/i18n/project_nl_NL.json +2 -0
- novelwriter/assets/i18n/project_pl_PL.json +2 -0
- novelwriter/assets/i18n/project_pt_BR.json +2 -0
- novelwriter/assets/i18n/project_zh_CN.json +2 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +8 -0
- novelwriter/assets/icons/typicons_dark/mixed_copy.svg +4 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +5 -0
- novelwriter/assets/icons/typicons_light/icons.conf +8 -0
- novelwriter/assets/icons/typicons_light/mixed_copy.svg +4 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_light/nw_toolbar.svg +5 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/common.py +101 -3
- novelwriter/config.py +30 -17
- novelwriter/constants.py +189 -81
- novelwriter/core/buildsettings.py +74 -40
- novelwriter/core/coretools.py +146 -148
- novelwriter/core/docbuild.py +133 -171
- novelwriter/core/document.py +1 -1
- novelwriter/core/index.py +39 -38
- novelwriter/core/item.py +42 -9
- novelwriter/core/itemmodel.py +518 -0
- novelwriter/core/options.py +5 -2
- novelwriter/core/project.py +68 -90
- novelwriter/core/projectdata.py +8 -2
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/sessions.py +1 -1
- novelwriter/core/spellcheck.py +10 -15
- novelwriter/core/status.py +24 -8
- novelwriter/core/storage.py +1 -1
- novelwriter/core/tree.py +269 -288
- novelwriter/dialogs/about.py +1 -1
- novelwriter/dialogs/docmerge.py +8 -18
- novelwriter/dialogs/docsplit.py +1 -1
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +47 -34
- novelwriter/dialogs/projectsettings.py +149 -99
- novelwriter/dialogs/quotes.py +1 -1
- novelwriter/dialogs/wordlist.py +11 -10
- novelwriter/enum.py +37 -24
- novelwriter/error.py +2 -2
- novelwriter/extensions/configlayout.py +28 -13
- novelwriter/extensions/eventfilters.py +1 -1
- novelwriter/extensions/modified.py +30 -6
- novelwriter/extensions/novelselector.py +4 -3
- novelwriter/extensions/pagedsidebar.py +9 -9
- novelwriter/extensions/progressbars.py +4 -4
- novelwriter/extensions/statusled.py +3 -3
- novelwriter/extensions/switch.py +3 -3
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +1 -1
- novelwriter/formats/shared.py +156 -0
- novelwriter/formats/todocx.py +1191 -0
- novelwriter/formats/tohtml.py +454 -0
- novelwriter/{core → formats}/tokenizer.py +497 -495
- novelwriter/formats/tomarkdown.py +218 -0
- novelwriter/{core → formats}/toodt.py +312 -433
- novelwriter/formats/toqdoc.py +486 -0
- novelwriter/formats/toraw.py +91 -0
- novelwriter/gui/doceditor.py +347 -287
- novelwriter/gui/dochighlight.py +97 -85
- novelwriter/gui/docviewer.py +90 -33
- novelwriter/gui/docviewerpanel.py +18 -26
- novelwriter/gui/editordocument.py +18 -3
- novelwriter/gui/itemdetails.py +27 -29
- novelwriter/gui/mainmenu.py +130 -64
- novelwriter/gui/noveltree.py +46 -48
- novelwriter/gui/outline.py +202 -256
- novelwriter/gui/projtree.py +590 -1242
- novelwriter/gui/search.py +11 -19
- novelwriter/gui/sidebar.py +8 -7
- novelwriter/gui/statusbar.py +20 -3
- novelwriter/gui/theme.py +11 -6
- novelwriter/guimain.py +101 -201
- novelwriter/shared.py +67 -28
- novelwriter/text/counting.py +3 -1
- novelwriter/text/patterns.py +169 -61
- novelwriter/tools/dictionaries.py +3 -3
- novelwriter/tools/lipsum.py +1 -1
- novelwriter/tools/manusbuild.py +15 -13
- novelwriter/tools/manuscript.py +121 -79
- novelwriter/tools/manussettings.py +424 -291
- novelwriter/tools/noveldetails.py +1 -1
- novelwriter/tools/welcome.py +6 -6
- novelwriter/tools/writingstats.py +4 -4
- novelwriter/types.py +25 -9
- novelwriter/core/tohtml.py +0 -530
- novelwriter/core/tomarkdown.py +0 -252
- novelwriter/core/toqdoc.py +0 -419
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5.3.dist-info → novelWriter-2.6.dist-info}/top_level.txt +0 -0
novelwriter/guimain.py
CHANGED
@@ -6,7 +6,7 @@ File History:
|
|
6
6
|
Created: 2018-09-22 [0.0.1] GuiMain
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018
|
9
|
+
Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
|
10
10
|
|
11
11
|
This program is free software: you can redistribute it and/or modify
|
12
12
|
it under the terms of the GNU General Public License as published by
|
@@ -62,6 +62,7 @@ from novelwriter.tools.manuscript import GuiManuscript
|
|
62
62
|
from novelwriter.tools.noveldetails import GuiNovelDetails
|
63
63
|
from novelwriter.tools.welcome import GuiWelcome
|
64
64
|
from novelwriter.tools.writingstats import GuiWritingStats
|
65
|
+
from novelwriter.types import QtModShift
|
65
66
|
|
66
67
|
logger = logging.getLogger(__name__)
|
67
68
|
|
@@ -212,15 +213,19 @@ class GuiMain(QMainWindow):
|
|
212
213
|
SHARED.indexChangedTags.connect(self.docEditor.updateChangedTags)
|
213
214
|
SHARED.indexChangedTags.connect(self.docViewerPanel.updateChangedTags)
|
214
215
|
SHARED.indexCleared.connect(self.docViewerPanel.indexWasCleared)
|
215
|
-
SHARED.indexScannedText.connect(self.docViewerPanel.projectItemChanged)
|
216
|
-
SHARED.indexScannedText.connect(self.itemDetails.updateViewBox)
|
217
|
-
SHARED.indexScannedText.connect(self.projView.updateItemValues)
|
218
216
|
SHARED.mainClockTick.connect(self._timeTick)
|
217
|
+
SHARED.projectItemChanged.connect(self.docEditor.onProjectItemChanged)
|
218
|
+
SHARED.projectItemChanged.connect(self.docViewer.onProjectItemChanged)
|
219
|
+
SHARED.projectItemChanged.connect(self.docViewerPanel.onProjectItemChanged)
|
220
|
+
SHARED.projectItemChanged.connect(self.itemDetails.onProjectItemChanged)
|
221
|
+
SHARED.projectItemChanged.connect(self.projView.onProjectItemChanged)
|
219
222
|
SHARED.projectStatusChanged.connect(self.mainStatus.updateProjectStatus)
|
220
223
|
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
|
224
|
+
SHARED.rootFolderChanged.connect(self.novelView.updateRootItem)
|
225
|
+
SHARED.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
226
|
+
SHARED.rootFolderChanged.connect(self.projView.updateRootItem)
|
221
227
|
SHARED.spellLanguageChanged.connect(self.mainStatus.setLanguage)
|
222
228
|
SHARED.statusLabelsChanged.connect(self.docViewerPanel.updateStatusLabels)
|
223
|
-
SHARED.statusLabelsChanged.connect(self.projView.refreshUserLabels)
|
224
229
|
|
225
230
|
self.mainMenu.requestDocAction.connect(self._passDocumentAction)
|
226
231
|
self.mainMenu.requestDocInsert.connect(self._passDocumentInsert)
|
@@ -233,15 +238,7 @@ class GuiMain(QMainWindow):
|
|
233
238
|
|
234
239
|
self.projView.openDocumentRequest.connect(self._openDocument)
|
235
240
|
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
241
|
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
240
|
-
self.projView.treeItemChanged.connect(self.docEditor.updateDocInfo)
|
241
|
-
self.projView.treeItemChanged.connect(self.docViewer.updateDocInfo)
|
242
|
-
self.projView.treeItemChanged.connect(self.docViewerPanel.projectItemChanged)
|
243
|
-
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
|
244
|
-
self.projView.wordCountsChanged.connect(self._updateStatusWordCount)
|
245
242
|
|
246
243
|
self.novelView.openDocumentRequest.connect(self._openDocument)
|
247
244
|
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
@@ -249,25 +246,27 @@ class GuiMain(QMainWindow):
|
|
249
246
|
self.projSearch.openDocumentSelectRequest.connect(self._openDocumentSelection)
|
250
247
|
self.projSearch.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
251
248
|
|
252
|
-
self.docEditor.
|
253
|
-
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
|
254
|
-
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
|
249
|
+
self.docEditor.closeEditorRequest.connect(self.closeDocEditor)
|
255
250
|
self.docEditor.docTextChanged.connect(self.projSearch.textChanged)
|
256
251
|
self.docEditor.editedStatusChanged.connect(self.mainStatus.updateDocumentStatus)
|
252
|
+
self.docEditor.itemHandleChanged.connect(self.novelView.setActiveHandle)
|
253
|
+
self.docEditor.itemHandleChanged.connect(self.projView.setActiveHandle)
|
257
254
|
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
258
255
|
self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
|
259
256
|
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
|
260
|
-
self.docEditor.
|
257
|
+
self.docEditor.openDocumentRequest.connect(self._openDocument)
|
258
|
+
self.docEditor.requestNewNoteCreation.connect(SHARED.createNewNote)
|
261
259
|
self.docEditor.requestNextDocument.connect(self.openNextDocument)
|
262
260
|
self.docEditor.requestProjectItemRenamed.connect(self.projView.renameTreeItem)
|
263
261
|
self.docEditor.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
264
262
|
self.docEditor.spellCheckStateChanged.connect(self.mainMenu.setSpellCheckState)
|
265
|
-
self.docEditor.statusMessage.connect(self.mainStatus.setStatusMessage)
|
266
263
|
self.docEditor.toggleFocusModeRequest.connect(self.toggleFocusMode)
|
264
|
+
self.docEditor.updateStatusMessage.connect(self.mainStatus.setStatusMessage)
|
267
265
|
|
268
266
|
self.docViewer.closeDocumentRequest.connect(self.closeDocViewer)
|
269
267
|
self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle)
|
270
268
|
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
|
269
|
+
self.docViewer.openDocumentRequest.connect(self._openDocument)
|
271
270
|
self.docViewer.reloadDocumentRequest.connect(self._reloadViewer)
|
272
271
|
self.docViewer.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
|
273
272
|
self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility)
|
@@ -289,21 +288,30 @@ class GuiMain(QMainWindow):
|
|
289
288
|
self.asDocTimer = QTimer(self)
|
290
289
|
self.asDocTimer.timeout.connect(self._autoSaveDocument)
|
291
290
|
|
292
|
-
# Shortcuts
|
293
|
-
self._connectMenuActions()
|
294
|
-
|
291
|
+
# Shortcuts
|
295
292
|
self.keyReturn = QShortcut(self)
|
296
|
-
self.keyReturn.setKey(
|
293
|
+
self.keyReturn.setKey("Return")
|
297
294
|
self.keyReturn.activated.connect(self._keyPressReturn)
|
298
295
|
|
296
|
+
self.keyShiftReturn = QShortcut(self)
|
297
|
+
self.keyShiftReturn.setKey("Shift+Return")
|
298
|
+
self.keyShiftReturn.activated.connect(self._keyPressReturn)
|
299
|
+
|
299
300
|
self.keyEnter = QShortcut(self)
|
300
|
-
self.keyEnter.setKey(
|
301
|
+
self.keyEnter.setKey("Enter")
|
301
302
|
self.keyEnter.activated.connect(self._keyPressReturn)
|
302
303
|
|
304
|
+
self.keyShiftEnter = QShortcut(self)
|
305
|
+
self.keyShiftEnter.setKey("Shift+Enter")
|
306
|
+
self.keyShiftEnter.activated.connect(self._keyPressReturn)
|
307
|
+
|
303
308
|
self.keyEscape = QShortcut(self)
|
304
|
-
self.keyEscape.setKey(
|
309
|
+
self.keyEscape.setKey("Esc")
|
305
310
|
self.keyEscape.activated.connect(self._keyPressEscape)
|
306
311
|
|
312
|
+
# Internal Variables
|
313
|
+
self._lastTotalCount = 0
|
314
|
+
|
307
315
|
# Initialise Main GUI
|
308
316
|
self.initMain()
|
309
317
|
self.asProjTimer.start()
|
@@ -481,8 +489,7 @@ class GuiMain(QMainWindow):
|
|
481
489
|
QApplication.processEvents()
|
482
490
|
self.openDocument(lastEdited, doScroll=True)
|
483
491
|
|
484
|
-
lastViewed
|
485
|
-
if lastViewed is not None:
|
492
|
+
if lastViewed := SHARED.project.data.getLastHandle("viewer"):
|
486
493
|
QApplication.processEvents()
|
487
494
|
self.viewDocument(lastViewed)
|
488
495
|
|
@@ -502,17 +509,15 @@ class GuiMain(QMainWindow):
|
|
502
509
|
|
503
510
|
def saveProject(self, autoSave: bool = False) -> bool:
|
504
511
|
"""Save the current project."""
|
505
|
-
if
|
506
|
-
|
507
|
-
|
508
|
-
self.projView.saveProjectTasks()
|
509
|
-
return SHARED.saveProject(autoSave=autoSave)
|
512
|
+
if SHARED.hasProject:
|
513
|
+
return SHARED.saveProject(autoSave=autoSave)
|
514
|
+
return False
|
510
515
|
|
511
516
|
##
|
512
517
|
# Document Actions
|
513
518
|
##
|
514
519
|
|
515
|
-
def closeDocument(self
|
520
|
+
def closeDocument(self) -> None:
|
516
521
|
"""Close the document and clear the editor and title field."""
|
517
522
|
if SHARED.hasProject:
|
518
523
|
# Disable focus mode if it is active
|
@@ -520,8 +525,6 @@ class GuiMain(QMainWindow):
|
|
520
525
|
SHARED.setFocusMode(False)
|
521
526
|
self.saveDocument()
|
522
527
|
self.docEditor.clearEditor()
|
523
|
-
if not beforeOpen:
|
524
|
-
self.novelView.setActiveHandle(None)
|
525
528
|
return
|
526
529
|
|
527
530
|
def openDocument(
|
@@ -533,12 +536,8 @@ class GuiMain(QMainWindow):
|
|
533
536
|
doScroll: bool = False
|
534
537
|
) -> bool:
|
535
538
|
"""Open a specific document, optionally at a given line."""
|
536
|
-
if not SHARED.hasProject:
|
537
|
-
logger.error("
|
538
|
-
return False
|
539
|
-
|
540
|
-
if not tHandle or not SHARED.project.tree.checkType(tHandle, nwItemType.FILE):
|
541
|
-
logger.debug("Requested item '%s' is not a document", tHandle)
|
539
|
+
if not (SHARED.hasProject and tHandle):
|
540
|
+
logger.error("Nothing to open open")
|
542
541
|
return False
|
543
542
|
|
544
543
|
if sTitle and tLine is None:
|
@@ -548,19 +547,15 @@ class GuiMain(QMainWindow):
|
|
548
547
|
self._changeView(nwView.EDITOR)
|
549
548
|
if tHandle == self.docEditor.docHandle:
|
550
549
|
self.docEditor.setCursorLine(tLine)
|
551
|
-
if changeFocus:
|
552
|
-
self.docEditor.setFocus()
|
553
|
-
return True
|
554
|
-
|
555
|
-
self.closeDocument(beforeOpen=True)
|
556
|
-
if self.docEditor.loadText(tHandle, tLine):
|
557
|
-
SHARED.project.data.setLastHandle(tHandle, "editor")
|
558
|
-
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
559
|
-
self.novelView.setActiveHandle(tHandle, doScroll=doScroll)
|
560
|
-
if changeFocus:
|
561
|
-
self.docEditor.setFocus()
|
562
550
|
else:
|
563
|
-
|
551
|
+
self.closeDocument()
|
552
|
+
if self.docEditor.loadText(tHandle, tLine):
|
553
|
+
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
554
|
+
else:
|
555
|
+
return False
|
556
|
+
|
557
|
+
if changeFocus:
|
558
|
+
self.docEditor.setFocus()
|
564
559
|
|
565
560
|
return True
|
566
561
|
|
@@ -728,8 +723,11 @@ class GuiMain(QMainWindow):
|
|
728
723
|
logger.warning("No item selected")
|
729
724
|
return
|
730
725
|
|
731
|
-
if tHandle:
|
732
|
-
|
726
|
+
if tHandle and SHARED.project.tree.checkType(tHandle, nwItemType.FILE):
|
727
|
+
if QApplication.keyboardModifiers() == QtModShift:
|
728
|
+
self.viewDocument(tHandle)
|
729
|
+
else:
|
730
|
+
self.openDocument(tHandle, sTitle=sTitle, changeFocus=False, doScroll=False)
|
733
731
|
|
734
732
|
return
|
735
733
|
|
@@ -740,9 +738,8 @@ class GuiMain(QMainWindow):
|
|
740
738
|
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
|
741
739
|
tStart = time()
|
742
740
|
|
743
|
-
|
744
|
-
SHARED.project.
|
745
|
-
self.projView.populateTree()
|
741
|
+
SHARED.project.index.rebuild()
|
742
|
+
SHARED.project.tree.refreshAllItems()
|
746
743
|
self.novelView.refreshTree()
|
747
744
|
|
748
745
|
tEnd = time()
|
@@ -855,13 +852,11 @@ class GuiMain(QMainWindow):
|
|
855
852
|
|
856
853
|
def closeMain(self) -> bool:
|
857
854
|
"""Save everything, and close novelWriter."""
|
858
|
-
if SHARED.hasProject
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
if not msgYes:
|
864
|
-
return False
|
855
|
+
if SHARED.hasProject and not SHARED.question("%s<br>%s" % (
|
856
|
+
self.tr("Do you want to exit novelWriter?"),
|
857
|
+
self.tr("Changes are saved automatically.")
|
858
|
+
)):
|
859
|
+
return False
|
865
860
|
|
866
861
|
logger.info("Exiting novelWriter")
|
867
862
|
|
@@ -906,11 +901,6 @@ class GuiMain(QMainWindow):
|
|
906
901
|
|
907
902
|
return not self.splitView.isVisible()
|
908
903
|
|
909
|
-
def toggleFullScreenMode(self) -> None:
|
910
|
-
"""Toggle full screen mode"""
|
911
|
-
self.setWindowState(self.windowState() ^ Qt.WindowState.WindowFullScreen)
|
912
|
-
return
|
913
|
-
|
914
904
|
##
|
915
905
|
# Events
|
916
906
|
##
|
@@ -926,6 +916,12 @@ class GuiMain(QMainWindow):
|
|
926
916
|
# Public Slots
|
927
917
|
##
|
928
918
|
|
919
|
+
@pyqtSlot()
|
920
|
+
def toggleFullScreenMode(self) -> None:
|
921
|
+
"""Toggle full screen mode"""
|
922
|
+
self.setWindowState(self.windowState() ^ Qt.WindowState.WindowFullScreen)
|
923
|
+
return
|
924
|
+
|
929
925
|
@pyqtSlot()
|
930
926
|
def closeDocEditor(self) -> None:
|
931
927
|
"""Close the document editor. This does not hide the editor."""
|
@@ -961,12 +957,8 @@ class GuiMain(QMainWindow):
|
|
961
957
|
docEditor = True
|
962
958
|
elif self.docViewer.isAncestorOf(new):
|
963
959
|
docViewer = True
|
964
|
-
|
965
960
|
self.docEditor.changeFocusState(docEditor)
|
966
961
|
self.docViewer.changeFocusState(docViewer)
|
967
|
-
|
968
|
-
logger.debug("Main focus switched to: %s", type(new).__name__)
|
969
|
-
|
970
962
|
return
|
971
963
|
|
972
964
|
@pyqtSlot(bool)
|
@@ -1055,13 +1047,8 @@ class GuiMain(QMainWindow):
|
|
1055
1047
|
self.initMain()
|
1056
1048
|
self.saveDocument()
|
1057
1049
|
|
1058
|
-
if restart:
|
1059
|
-
SHARED.info(self.tr(
|
1060
|
-
"Some changes will not be applied until novelWriter has been restarted."
|
1061
|
-
))
|
1062
|
-
|
1063
1050
|
if tree:
|
1064
|
-
|
1051
|
+
SHARED.project.tree.refreshAllItems()
|
1065
1052
|
|
1066
1053
|
if theme:
|
1067
1054
|
# We are doing this manually instead of connecting to
|
@@ -1087,8 +1074,16 @@ class GuiMain(QMainWindow):
|
|
1087
1074
|
self.projView.initSettings()
|
1088
1075
|
self.novelView.initSettings()
|
1089
1076
|
self.outlineView.initSettings()
|
1077
|
+
|
1078
|
+
# Force update of word count
|
1079
|
+
self._lastTotalCount = 0
|
1090
1080
|
self._updateStatusWordCount()
|
1091
1081
|
|
1082
|
+
if restart:
|
1083
|
+
SHARED.info(self.tr(
|
1084
|
+
"Some changes will not be applied until novelWriter has been restarted."
|
1085
|
+
))
|
1086
|
+
|
1092
1087
|
return
|
1093
1088
|
|
1094
1089
|
@pyqtSlot()
|
@@ -1111,8 +1106,16 @@ class GuiMain(QMainWindow):
|
|
1111
1106
|
@pyqtSlot(str, nwDocMode)
|
1112
1107
|
def _followTag(self, tag: str, mode: nwDocMode) -> None:
|
1113
1108
|
"""Follow a tag after user interaction with a link."""
|
1114
|
-
tHandle, sTitle =
|
1115
|
-
if tHandle is
|
1109
|
+
tHandle, sTitle = SHARED.project.index.getTagSource(tag)
|
1110
|
+
if tHandle is None:
|
1111
|
+
SHARED.error(self.tr(
|
1112
|
+
"Could not find the reference for tag '{0}'. It either doesn't "
|
1113
|
+
"exist, or the index is out of date. The index can be updated "
|
1114
|
+
"from the Tools menu, or by pressing {1}."
|
1115
|
+
).format(
|
1116
|
+
tag, "F9"
|
1117
|
+
))
|
1118
|
+
else:
|
1116
1119
|
if mode == nwDocMode.EDIT:
|
1117
1120
|
self.openDocument(tHandle, sTitle=sTitle)
|
1118
1121
|
elif mode == nwDocMode.VIEW:
|
@@ -1216,8 +1219,10 @@ class GuiMain(QMainWindow):
|
|
1216
1219
|
self.mainStatus.setUserIdle(editIdle or userIdle)
|
1217
1220
|
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
1218
1221
|
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
1219
|
-
if
|
1220
|
-
self.
|
1222
|
+
if int(currTime) % 5 == 0:
|
1223
|
+
self._updateStatusWordCount()
|
1224
|
+
if CONFIG.memInfo: # pragma: no cover
|
1225
|
+
self.mainStatus.memInfo()
|
1221
1226
|
return
|
1222
1227
|
|
1223
1228
|
@pyqtSlot()
|
@@ -1245,15 +1250,19 @@ class GuiMain(QMainWindow):
|
|
1245
1250
|
if not SHARED.hasProject:
|
1246
1251
|
self.mainStatus.setProjectStats(0, 0)
|
1247
1252
|
|
1248
|
-
SHARED.project.
|
1249
|
-
if
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1253
|
+
currentTotalCount = SHARED.project.currentTotalCount
|
1254
|
+
if self._lastTotalCount != currentTotalCount:
|
1255
|
+
self._lastTotalCount = currentTotalCount
|
1256
|
+
|
1257
|
+
SHARED.project.updateWordCounts()
|
1258
|
+
if CONFIG.incNotesWCount:
|
1259
|
+
iTotal = sum(SHARED.project.data.initCounts)
|
1260
|
+
cTotal = sum(SHARED.project.data.currCounts)
|
1261
|
+
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
1262
|
+
else:
|
1263
|
+
iNovel, _ = SHARED.project.data.initCounts
|
1264
|
+
cNovel, _ = SHARED.project.data.currCounts
|
1265
|
+
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
1257
1266
|
|
1258
1267
|
return
|
1259
1268
|
|
@@ -1299,116 +1308,7 @@ class GuiMain(QMainWindow):
|
|
1299
1308
|
# Internal Functions
|
1300
1309
|
##
|
1301
1310
|
|
1302
|
-
def _connectMenuActions(self) -> None:
|
1303
|
-
"""Connect to the main window all menu actions that need to be
|
1304
|
-
available also when the main menu is hidden.
|
1305
|
-
"""
|
1306
|
-
# Project
|
1307
|
-
self.addAction(self.mainMenu.aSaveProject)
|
1308
|
-
self.addAction(self.mainMenu.aEditItem)
|
1309
|
-
self.addAction(self.mainMenu.aExitNW)
|
1310
|
-
|
1311
|
-
# Document
|
1312
|
-
self.addAction(self.mainMenu.aSaveDoc)
|
1313
|
-
self.addAction(self.mainMenu.aCloseDoc)
|
1314
|
-
|
1315
|
-
# Edit
|
1316
|
-
self.addAction(self.mainMenu.aEditUndo)
|
1317
|
-
self.addAction(self.mainMenu.aEditRedo)
|
1318
|
-
self.addAction(self.mainMenu.aEditCut)
|
1319
|
-
self.addAction(self.mainMenu.aEditCopy)
|
1320
|
-
self.addAction(self.mainMenu.aEditPaste)
|
1321
|
-
self.addAction(self.mainMenu.aSelectAll)
|
1322
|
-
self.addAction(self.mainMenu.aSelectPar)
|
1323
|
-
|
1324
|
-
# View
|
1325
|
-
self.addAction(self.mainMenu.aFocusMode)
|
1326
|
-
self.addAction(self.mainMenu.aFullScreen)
|
1327
|
-
|
1328
|
-
# Insert
|
1329
|
-
self.addAction(self.mainMenu.aInsENDash)
|
1330
|
-
self.addAction(self.mainMenu.aInsEMDash)
|
1331
|
-
self.addAction(self.mainMenu.aInsHorBar)
|
1332
|
-
self.addAction(self.mainMenu.aInsFigDash)
|
1333
|
-
self.addAction(self.mainMenu.aInsQuoteLS)
|
1334
|
-
self.addAction(self.mainMenu.aInsQuoteRS)
|
1335
|
-
self.addAction(self.mainMenu.aInsQuoteLD)
|
1336
|
-
self.addAction(self.mainMenu.aInsQuoteRD)
|
1337
|
-
self.addAction(self.mainMenu.aInsMSApos)
|
1338
|
-
self.addAction(self.mainMenu.aInsEllipsis)
|
1339
|
-
self.addAction(self.mainMenu.aInsPrime)
|
1340
|
-
self.addAction(self.mainMenu.aInsDPrime)
|
1341
|
-
self.addAction(self.mainMenu.aInsNBSpace)
|
1342
|
-
self.addAction(self.mainMenu.aInsThinSpace)
|
1343
|
-
self.addAction(self.mainMenu.aInsThinNBSpace)
|
1344
|
-
self.addAction(self.mainMenu.aInsBullet)
|
1345
|
-
self.addAction(self.mainMenu.aInsHyBull)
|
1346
|
-
self.addAction(self.mainMenu.aInsFlower)
|
1347
|
-
self.addAction(self.mainMenu.aInsPerMille)
|
1348
|
-
self.addAction(self.mainMenu.aInsDegree)
|
1349
|
-
self.addAction(self.mainMenu.aInsMinus)
|
1350
|
-
self.addAction(self.mainMenu.aInsTimes)
|
1351
|
-
self.addAction(self.mainMenu.aInsDivide)
|
1352
|
-
self.addAction(self.mainMenu.aInsSynopsis)
|
1353
|
-
self.addAction(self.mainMenu.aInsShort)
|
1354
|
-
|
1355
|
-
for mAction, _ in self.mainMenu.mInsKWItems.values():
|
1356
|
-
self.addAction(mAction)
|
1357
|
-
|
1358
|
-
# Search
|
1359
|
-
self.addAction(self.mainMenu.aFind)
|
1360
|
-
self.addAction(self.mainMenu.aReplace)
|
1361
|
-
self.addAction(self.mainMenu.aFindNext)
|
1362
|
-
self.addAction(self.mainMenu.aFindPrev)
|
1363
|
-
self.addAction(self.mainMenu.aReplaceNext)
|
1364
|
-
|
1365
|
-
# Format
|
1366
|
-
self.addAction(self.mainMenu.aFmtItalic)
|
1367
|
-
self.addAction(self.mainMenu.aFmtBold)
|
1368
|
-
self.addAction(self.mainMenu.aFmtStrike)
|
1369
|
-
self.addAction(self.mainMenu.aFmtDQuote)
|
1370
|
-
self.addAction(self.mainMenu.aFmtSQuote)
|
1371
|
-
self.addAction(self.mainMenu.aFmtHead1)
|
1372
|
-
self.addAction(self.mainMenu.aFmtHead2)
|
1373
|
-
self.addAction(self.mainMenu.aFmtHead3)
|
1374
|
-
self.addAction(self.mainMenu.aFmtHead4)
|
1375
|
-
self.addAction(self.mainMenu.aFmtAlignLeft)
|
1376
|
-
self.addAction(self.mainMenu.aFmtAlignCentre)
|
1377
|
-
self.addAction(self.mainMenu.aFmtAlignRight)
|
1378
|
-
self.addAction(self.mainMenu.aFmtIndentLeft)
|
1379
|
-
self.addAction(self.mainMenu.aFmtIndentRight)
|
1380
|
-
self.addAction(self.mainMenu.aFmtComment)
|
1381
|
-
self.addAction(self.mainMenu.aFmtNoFormat)
|
1382
|
-
|
1383
|
-
# Tools
|
1384
|
-
self.addAction(self.mainMenu.aSpellCheck)
|
1385
|
-
self.addAction(self.mainMenu.aReRunSpell)
|
1386
|
-
self.addAction(self.mainMenu.aPreferences)
|
1387
|
-
|
1388
|
-
# Help
|
1389
|
-
self.addAction(self.mainMenu.aHelpDocs)
|
1390
|
-
if isinstance(CONFIG.pdfDocs, Path):
|
1391
|
-
self.addAction(self.mainMenu.aPdfDocs)
|
1392
|
-
|
1393
|
-
return
|
1394
|
-
|
1395
1311
|
def _updateWindowTitle(self, projName: str | None = None) -> None:
|
1396
1312
|
"""Set the window title and add the project's name."""
|
1397
1313
|
self.setWindowTitle(" - ".join(filter(None, [projName, CONFIG.appName])))
|
1398
1314
|
return
|
1399
|
-
|
1400
|
-
def _getTagSource(self, tag: str) -> tuple[str | None, str | None]:
|
1401
|
-
"""Handle the index lookup of a tag and display an alert if the
|
1402
|
-
tag cannot be found.
|
1403
|
-
"""
|
1404
|
-
tHandle, sTitle = SHARED.project.index.getTagSource(tag)
|
1405
|
-
if tHandle is None:
|
1406
|
-
SHARED.error(self.tr(
|
1407
|
-
"Could not find the reference for tag '{0}'. It either doesn't "
|
1408
|
-
"exist, or the index is out of date. The index can be updated "
|
1409
|
-
"from the Tools menu, or by pressing {1}."
|
1410
|
-
).format(
|
1411
|
-
tag, "F9"
|
1412
|
-
))
|
1413
|
-
return None, None
|
1414
|
-
return tHandle, sTitle
|
novelwriter/shared.py
CHANGED
@@ -7,7 +7,7 @@ Created: 2023-08-10 [2.1rc1] SharedData
|
|
7
7
|
Created: 2023-08-14 [2.1rc1] _GuiAlert
|
8
8
|
|
9
9
|
This file is a part of novelWriter
|
10
|
-
Copyright
|
10
|
+
Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
|
11
11
|
|
12
12
|
This program is free software: you can redistribute it and/or modify
|
13
13
|
it under the terms of the GNU General Public License as published by
|
@@ -26,20 +26,23 @@ from __future__ import annotations
|
|
26
26
|
|
27
27
|
import logging
|
28
28
|
|
29
|
+
from enum import Enum
|
29
30
|
from pathlib import Path
|
30
31
|
from time import time
|
31
32
|
from typing import TYPE_CHECKING, TypeVar
|
32
33
|
|
33
|
-
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal
|
34
|
-
from PyQt5.QtGui import QFont
|
34
|
+
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, QUrl, pyqtSignal, pyqtSlot
|
35
|
+
from PyQt5.QtGui import QDesktopServices, QFont
|
35
36
|
from PyQt5.QtWidgets import QFileDialog, QFontDialog, QMessageBox, QWidget
|
36
37
|
|
37
38
|
from novelwriter.common import formatFileFilter
|
38
39
|
from novelwriter.constants import nwFiles
|
39
40
|
from novelwriter.core.spellcheck import NWSpellEnchant
|
41
|
+
from novelwriter.enum import nwChange, nwItemClass
|
40
42
|
|
41
43
|
if TYPE_CHECKING: # pragma: no cover
|
42
44
|
from novelwriter.core.project import NWProject
|
45
|
+
from novelwriter.core.status import T_StatusKind
|
43
46
|
from novelwriter.gui.theme import GuiTheme
|
44
47
|
from novelwriter.guimain import GuiMain
|
45
48
|
|
@@ -55,15 +58,16 @@ class SharedData(QObject):
|
|
55
58
|
"_idleTime", "_idleRefTime",
|
56
59
|
)
|
57
60
|
|
58
|
-
projectStatusChanged = pyqtSignal(bool)
|
59
|
-
projectStatusMessage = pyqtSignal(str)
|
60
|
-
spellLanguageChanged = pyqtSignal(str, str)
|
61
61
|
focusModeChanged = pyqtSignal(bool)
|
62
|
-
|
62
|
+
indexAvailable = pyqtSignal()
|
63
63
|
indexChangedTags = pyqtSignal(list, list)
|
64
64
|
indexCleared = pyqtSignal()
|
65
|
-
indexAvailable = pyqtSignal()
|
66
65
|
mainClockTick = pyqtSignal()
|
66
|
+
projectItemChanged = pyqtSignal(str, Enum)
|
67
|
+
rootFolderChanged = pyqtSignal(str, Enum)
|
68
|
+
projectStatusChanged = pyqtSignal(bool)
|
69
|
+
projectStatusMessage = pyqtSignal(str)
|
70
|
+
spellLanguageChanged = pyqtSignal(str, str)
|
67
71
|
statusLabelsChanged = pyqtSignal(str)
|
68
72
|
|
69
73
|
def __init__(self) -> None:
|
@@ -173,10 +177,12 @@ class SharedData(QObject):
|
|
173
177
|
logger.debug("Thread Pool Max Count: %d", QThreadPool.globalInstance().maxThreadCount())
|
174
178
|
return
|
175
179
|
|
176
|
-
def
|
180
|
+
def closeDocument(self, tHandle: str | None = None) -> None:
|
177
181
|
"""Close the document editor, optionally a specific document."""
|
178
182
|
if tHandle is None or tHandle == self.mainGui.docEditor.docHandle:
|
179
183
|
self.mainGui.closeDocument()
|
184
|
+
if tHandle is None or tHandle == self.mainGui.docViewer.docHandle:
|
185
|
+
self.mainGui.closeViewerPanel()
|
180
186
|
return
|
181
187
|
|
182
188
|
def saveEditor(self, tHandle: str | None = None) -> None:
|
@@ -293,29 +299,61 @@ class SharedData(QObject):
|
|
293
299
|
return None
|
294
300
|
|
295
301
|
##
|
296
|
-
#
|
302
|
+
# Public Slots
|
297
303
|
##
|
298
304
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
305
|
+
@pyqtSlot(str)
|
306
|
+
def openWebsite(self, url: str) -> None:
|
307
|
+
"""Open a URL in the system's default browser."""
|
308
|
+
QDesktopServices.openUrl(QUrl(url))
|
309
|
+
return
|
310
|
+
|
311
|
+
@pyqtSlot(str, nwItemClass)
|
312
|
+
def createNewNote(self, tag: str, itemClass: nwItemClass) -> None:
|
313
|
+
"""Process new note request."""
|
314
|
+
self.project.createNewNote(tag, itemClass)
|
315
|
+
return
|
316
|
+
|
317
|
+
##
|
318
|
+
# Signal Proxies
|
319
|
+
##
|
320
|
+
|
321
|
+
def emitIndexChangedTags(
|
322
|
+
self, project: NWProject, updated: list[str], deleted: list[str]
|
323
|
+
) -> None:
|
324
|
+
"""Emit the indexChangedTags signal."""
|
325
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
326
|
+
self.indexChangedTags.emit(updated, deleted)
|
327
|
+
return
|
328
|
+
|
329
|
+
def emitIndexCleared(self, project: NWProject) -> None:
|
330
|
+
"""Emit the indexCleared signal."""
|
331
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
308
332
|
self.indexCleared.emit()
|
309
|
-
|
333
|
+
return
|
334
|
+
|
335
|
+
def emitIndexAvailable(self, project: NWProject) -> None:
|
336
|
+
"""Emit the indexAvailable signal."""
|
337
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
310
338
|
self.indexAvailable.emit()
|
311
339
|
return
|
312
340
|
|
313
|
-
def
|
314
|
-
"""Emit
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
341
|
+
def emitStatusLabelsChanged(self, project: NWProject, kind: T_StatusKind) -> None:
|
342
|
+
"""Emit the statusLabelsChanged signal."""
|
343
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
344
|
+
self.statusLabelsChanged.emit(kind)
|
345
|
+
return
|
346
|
+
|
347
|
+
def emitProjectItemChanged(self, project: NWProject, handle: str, change: nwChange) -> None:
|
348
|
+
"""Emit the projectItemChanged signal."""
|
349
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
350
|
+
self.projectItemChanged.emit(handle, change)
|
351
|
+
return
|
352
|
+
|
353
|
+
def emitRootFolderChanged(self, project: NWProject, handle: str, change: nwChange) -> None:
|
354
|
+
"""Emit the rootFolderChanged signal."""
|
355
|
+
if self._project and self._project.data.uuid == project.data.uuid:
|
356
|
+
self.rootFolderChanged.emit(handle, change)
|
319
357
|
return
|
320
358
|
|
321
359
|
##
|
@@ -376,6 +414,7 @@ class SharedData(QObject):
|
|
376
414
|
"""Create a new project and spell checking instance."""
|
377
415
|
from novelwriter.core.project import NWProject
|
378
416
|
if isinstance(self._project, NWProject):
|
417
|
+
self._project.clear()
|
379
418
|
del self._project
|
380
419
|
del self._spelling
|
381
420
|
self._project = NWProject()
|
@@ -441,9 +480,9 @@ class _GuiAlert(QMessageBox):
|
|
441
480
|
Yes/No buttons or just an Ok button.
|
442
481
|
"""
|
443
482
|
if isYesNo:
|
444
|
-
self.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
483
|
+
self.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
445
484
|
else:
|
446
|
-
self.setStandardButtons(QMessageBox.Ok)
|
485
|
+
self.setStandardButtons(QMessageBox.StandardButton.Ok)
|
447
486
|
pSz = 2*self._theme.baseIconHeight
|
448
487
|
if level == self.INFO:
|
449
488
|
self.setIconPixmap(self._theme.getPixmap("alert_info", (pSz, pSz)))
|