novelWriter 2.2rc1__py3-none-any.whl → 2.2.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.2rc1.dist-info → novelWriter-2.2.1.dist-info}/METADATA +1 -1
- {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/RECORD +113 -111
- {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +10 -5
- 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_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_de_DE.json +1 -0
- novelwriter/assets/i18n/project_en_US.json +1 -0
- novelwriter/assets/i18n/project_es_419.json +11 -0
- novelwriter/assets/i18n/project_fr_FR.json +11 -0
- novelwriter/assets/i18n/project_it_IT.json +11 -0
- novelwriter/assets/i18n/project_ja_JP.json +2 -1
- novelwriter/assets/i18n/project_nb_NO.json +1 -0
- novelwriter/assets/i18n/project_zh_CN.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +3 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
- novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
- novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
- novelwriter/assets/icons/typicons_light/icons.conf +3 -2
- novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
- novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
- novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
- novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/text/release_notes.htm +50 -7
- novelwriter/common.py +16 -29
- novelwriter/config.py +3 -3
- novelwriter/constants.py +1 -1
- novelwriter/core/buildsettings.py +1 -1
- novelwriter/core/coretools.py +2 -1
- novelwriter/core/docbuild.py +1 -1
- novelwriter/core/document.py +1 -1
- novelwriter/core/index.py +2 -2
- novelwriter/core/item.py +2 -2
- novelwriter/core/options.py +3 -3
- novelwriter/core/project.py +3 -3
- novelwriter/core/projectdata.py +2 -2
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/sessions.py +2 -2
- novelwriter/core/spellcheck.py +4 -3
- novelwriter/core/status.py +3 -3
- novelwriter/core/storage.py +1 -1
- novelwriter/core/tohtml.py +2 -2
- novelwriter/core/tokenizer.py +1 -1
- novelwriter/core/tomd.py +2 -2
- novelwriter/core/toodt.py +1 -1
- novelwriter/core/tree.py +2 -2
- novelwriter/dialogs/about.py +30 -31
- novelwriter/dialogs/docmerge.py +24 -15
- novelwriter/dialogs/docsplit.py +27 -16
- novelwriter/dialogs/editlabel.py +19 -7
- novelwriter/dialogs/preferences.py +44 -57
- novelwriter/dialogs/projdetails.py +29 -36
- novelwriter/dialogs/projload.py +32 -36
- novelwriter/dialogs/projsettings.py +20 -15
- novelwriter/dialogs/quotes.py +32 -25
- novelwriter/dialogs/updates.py +4 -14
- novelwriter/dialogs/wordlist.py +34 -21
- novelwriter/enum.py +5 -4
- novelwriter/error.py +1 -1
- novelwriter/extensions/circularprogress.py +1 -1
- novelwriter/extensions/configlayout.py +3 -15
- novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
- novelwriter/extensions/novelselector.py +1 -1
- novelwriter/extensions/pageddialog.py +1 -1
- novelwriter/extensions/pagedsidebar.py +2 -5
- novelwriter/extensions/simpleprogress.py +8 -9
- novelwriter/extensions/statusled.py +1 -1
- novelwriter/extensions/switch.py +4 -4
- novelwriter/extensions/switchbox.py +1 -6
- novelwriter/gui/doceditor.py +72 -64
- novelwriter/gui/dochighlight.py +3 -2
- novelwriter/gui/docviewer.py +22 -47
- novelwriter/gui/docviewerpanel.py +68 -23
- novelwriter/gui/editordocument.py +3 -3
- novelwriter/gui/itemdetails.py +2 -2
- novelwriter/gui/mainmenu.py +35 -30
- novelwriter/gui/noveltree.py +44 -53
- novelwriter/gui/outline.py +2 -1
- novelwriter/gui/projtree.py +5 -6
- novelwriter/gui/sidebar.py +6 -4
- novelwriter/gui/statusbar.py +47 -4
- novelwriter/gui/theme.py +5 -6
- novelwriter/guimain.py +139 -189
- novelwriter/shared.py +53 -29
- novelwriter/tools/dictionaries.py +2 -2
- novelwriter/tools/lipsum.py +34 -28
- novelwriter/tools/manusbuild.py +3 -4
- novelwriter/tools/manuscript.py +19 -26
- novelwriter/tools/manussettings.py +2 -4
- novelwriter/tools/projwizard.py +3 -3
- novelwriter/tools/writingstats.py +17 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
- novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
- novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
- novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
- {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.2.1.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 2018–2024, Veronica Berglyd Olsen
|
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
|
@@ -56,7 +56,6 @@ from novelwriter.dialogs.wordlist import GuiWordList
|
|
56
56
|
from novelwriter.dialogs.preferences import GuiPreferences
|
57
57
|
from novelwriter.dialogs.projdetails import GuiProjectDetails
|
58
58
|
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
59
|
-
from novelwriter.tools.lipsum import GuiLipsum
|
60
59
|
from novelwriter.tools.manuscript import GuiManuscript
|
61
60
|
from novelwriter.tools.projwizard import GuiProjectWizard
|
62
61
|
from novelwriter.tools.dictionaries import GuiDictionaries
|
@@ -66,7 +65,7 @@ from novelwriter.core.coretools import ProjectBuilder
|
|
66
65
|
from novelwriter.enum import (
|
67
66
|
nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwItemClass, nwWidget, nwView
|
68
67
|
)
|
69
|
-
from novelwriter.common import
|
68
|
+
from novelwriter.common import hexToInt
|
70
69
|
from novelwriter.constants import nwFiles
|
71
70
|
|
72
71
|
logger = logging.getLogger(__name__)
|
@@ -338,19 +337,9 @@ class GuiMain(QMainWindow):
|
|
338
337
|
self.asProjTimer.start()
|
339
338
|
self.asDocTimer.start()
|
340
339
|
self.mainStatus.clearStatus()
|
341
|
-
|
342
|
-
# Handle Windows Mode
|
343
340
|
self.showNormal()
|
344
341
|
|
345
342
|
logger.debug("Ready: GUI")
|
346
|
-
|
347
|
-
if __hexversion__[-2] == "a" and not CONFIG.isDebug:
|
348
|
-
SHARED.warn(
|
349
|
-
"You are running an untested development version of novelWriter. "
|
350
|
-
"Please be careful when you are working on live projects "
|
351
|
-
"and make sure you take regular backups."
|
352
|
-
)
|
353
|
-
|
354
343
|
logger.info("novelWriter is ready ...")
|
355
344
|
self.mainStatus.setStatusMessage(self.tr("novelWriter is ready ..."))
|
356
345
|
|
@@ -617,7 +606,7 @@ class GuiMain(QMainWindow):
|
|
617
606
|
if self.docEditor.loadText(tHandle, tLine):
|
618
607
|
SHARED.project.data.setLastHandle(tHandle, "editor")
|
619
608
|
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
620
|
-
self.novelView.setActiveHandle(tHandle)
|
609
|
+
self.novelView.setActiveHandle(tHandle, doScroll=doScroll)
|
621
610
|
if changeFocus:
|
622
611
|
self.docEditor.setFocus()
|
623
612
|
else:
|
@@ -876,213 +865,113 @@ class GuiMain(QMainWindow):
|
|
876
865
|
|
877
866
|
return None
|
878
867
|
|
868
|
+
@pyqtSlot()
|
879
869
|
def showPreferencesDialog(self) -> None:
|
880
870
|
"""Open the preferences dialog."""
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
if dlgConf.result() == QDialog.Accepted:
|
885
|
-
logger.debug("Applying new preferences")
|
886
|
-
self.initMain()
|
887
|
-
self.saveDocument()
|
888
|
-
|
889
|
-
if dlgConf.needsRestart:
|
890
|
-
SHARED.info(self.tr(
|
891
|
-
"Some changes will not be applied until novelWriter has been restarted."
|
892
|
-
))
|
893
|
-
|
894
|
-
if dlgConf.refreshTree:
|
895
|
-
self.projView.populateTree()
|
896
|
-
|
897
|
-
if dlgConf.updateTheme:
|
898
|
-
# We are doing this manually instead of connecting to
|
899
|
-
# qApp.paletteChanged since the processing order matters
|
900
|
-
SHARED.theme.loadTheme()
|
901
|
-
self.docEditor.updateTheme()
|
902
|
-
self.docViewer.updateTheme()
|
903
|
-
self.docViewerPanel.updateTheme()
|
904
|
-
self.sideBar.updateTheme()
|
905
|
-
self.projView.updateTheme()
|
906
|
-
self.novelView.updateTheme()
|
907
|
-
self.outlineView.updateTheme()
|
908
|
-
self.itemDetails.updateTheme()
|
909
|
-
self.mainStatus.updateTheme()
|
910
|
-
|
911
|
-
if dlgConf.updateSyntax:
|
912
|
-
SHARED.theme.loadSyntax()
|
913
|
-
self.docEditor.updateSyntaxColours()
|
914
|
-
|
915
|
-
self.docEditor.initEditor()
|
916
|
-
self.docViewer.initViewer()
|
917
|
-
self.projView.initSettings()
|
918
|
-
self.novelView.initSettings()
|
919
|
-
self.outlineView.initSettings()
|
920
|
-
|
921
|
-
self._updateStatusWordCount()
|
922
|
-
|
871
|
+
dialog = GuiPreferences(self)
|
872
|
+
dialog.newPreferencesReady.connect(self._processConfigChanges)
|
873
|
+
dialog.exec_()
|
923
874
|
return
|
924
875
|
|
876
|
+
@pyqtSlot()
|
925
877
|
@pyqtSlot(int)
|
926
|
-
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.TAB_MAIN) ->
|
878
|
+
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.TAB_MAIN) -> None:
|
927
879
|
"""Open the project settings dialog."""
|
928
|
-
if
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
dlgProj.exec_()
|
934
|
-
|
935
|
-
if dlgProj.result() == QDialog.Accepted:
|
936
|
-
logger.debug("Applying new project settings")
|
937
|
-
SHARED.updateSpellCheckLanguage()
|
938
|
-
self.itemDetails.refreshDetails()
|
939
|
-
self._updateWindowTitle(SHARED.project.data.name)
|
940
|
-
|
941
|
-
return True
|
880
|
+
if SHARED.hasProject:
|
881
|
+
dialog = GuiProjectSettings(self, focusTab=focusTab)
|
882
|
+
dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
|
883
|
+
dialog.exec_()
|
884
|
+
return
|
942
885
|
|
943
|
-
|
886
|
+
@pyqtSlot()
|
887
|
+
def showProjectDetailsDialog(self) -> None:
|
944
888
|
"""Open the project details dialog."""
|
945
|
-
if
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
dlgDetails.setModal(True)
|
955
|
-
dlgDetails.show()
|
956
|
-
dlgDetails.raise_()
|
957
|
-
dlgDetails.updateValues()
|
958
|
-
|
959
|
-
return True
|
889
|
+
if SHARED.hasProject:
|
890
|
+
dialog = GuiProjectDetails(self)
|
891
|
+
dialog.setModal(True)
|
892
|
+
dialog.show()
|
893
|
+
dialog.raise_()
|
894
|
+
qApp.processEvents()
|
895
|
+
dialog.updateValues()
|
896
|
+
return
|
960
897
|
|
961
898
|
@pyqtSlot()
|
962
|
-
def showBuildManuscriptDialog(self) ->
|
899
|
+
def showBuildManuscriptDialog(self) -> None:
|
963
900
|
"""Open the build manuscript dialog."""
|
964
|
-
if
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
dlgBuild.setModal(False)
|
974
|
-
dlgBuild.show()
|
975
|
-
dlgBuild.raise_()
|
976
|
-
qApp.processEvents()
|
977
|
-
|
978
|
-
dlgBuild.loadContent()
|
979
|
-
|
980
|
-
return True
|
981
|
-
|
982
|
-
def showLoremIpsumDialog(self) -> bool:
|
983
|
-
"""Open the insert lorem ipsum text dialog."""
|
984
|
-
if not SHARED.hasProject:
|
985
|
-
logger.error("No project open")
|
986
|
-
return False
|
987
|
-
|
988
|
-
dlgLipsum = getGuiItem("GuiLipsum")
|
989
|
-
if dlgLipsum is None:
|
990
|
-
dlgLipsum = GuiLipsum(self)
|
991
|
-
assert isinstance(dlgLipsum, GuiLipsum)
|
992
|
-
|
993
|
-
dlgLipsum.setModal(False)
|
994
|
-
dlgLipsum.show()
|
995
|
-
dlgLipsum.raise_()
|
996
|
-
qApp.processEvents()
|
997
|
-
|
998
|
-
return True
|
901
|
+
if SHARED.hasProject:
|
902
|
+
if (dialog := SHARED.findTopLevelWidget(GuiManuscript)) is None:
|
903
|
+
dialog = GuiManuscript(self)
|
904
|
+
dialog.setModal(False)
|
905
|
+
dialog.show()
|
906
|
+
dialog.raise_()
|
907
|
+
qApp.processEvents()
|
908
|
+
dialog.loadContent()
|
909
|
+
return
|
999
910
|
|
1000
|
-
|
911
|
+
@pyqtSlot()
|
912
|
+
def showProjectWordListDialog(self) -> None:
|
1001
913
|
"""Open the project word list dialog."""
|
1002
|
-
if
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
dlgWords.exec_()
|
1008
|
-
|
1009
|
-
if dlgWords.result() == QDialog.Accepted:
|
1010
|
-
logger.debug("Reloading word list")
|
1011
|
-
SHARED.updateSpellCheckLanguage(reload=True)
|
1012
|
-
self.docEditor.spellCheckDocument()
|
1013
|
-
|
1014
|
-
return True
|
914
|
+
if SHARED.hasProject:
|
915
|
+
dialog = GuiWordList(self)
|
916
|
+
dialog.newWordListReady.connect(self._processWordListChanges)
|
917
|
+
dialog.exec_()
|
918
|
+
return
|
1015
919
|
|
1016
|
-
|
920
|
+
@pyqtSlot()
|
921
|
+
def showWritingStatsDialog(self) -> None:
|
1017
922
|
"""Open the session stats dialog."""
|
1018
|
-
if
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
dlgStats.setModal(False)
|
1028
|
-
dlgStats.show()
|
1029
|
-
dlgStats.raise_()
|
1030
|
-
qApp.processEvents()
|
1031
|
-
dlgStats.populateGUI()
|
1032
|
-
|
1033
|
-
return True
|
1034
|
-
|
1035
|
-
def showAboutNWDialog(self, showNotes: bool = False) -> bool:
|
1036
|
-
"""Show the about dialog for novelWriter."""
|
1037
|
-
dlgAbout = getGuiItem("GuiAbout")
|
1038
|
-
if dlgAbout is None:
|
1039
|
-
dlgAbout = GuiAbout(self)
|
1040
|
-
assert isinstance(dlgAbout, GuiAbout)
|
923
|
+
if SHARED.hasProject:
|
924
|
+
if (dialog := SHARED.findTopLevelWidget(GuiWritingStats)) is None:
|
925
|
+
dialog = GuiWritingStats(self)
|
926
|
+
dialog.setModal(False)
|
927
|
+
dialog.show()
|
928
|
+
dialog.raise_()
|
929
|
+
qApp.processEvents()
|
930
|
+
dialog.populateGUI()
|
931
|
+
return
|
1041
932
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
933
|
+
@pyqtSlot()
|
934
|
+
def showAboutNWDialog(self, showNotes: bool = False) -> None:
|
935
|
+
"""Show the novelWriter about dialog."""
|
936
|
+
dialog = GuiAbout(self)
|
937
|
+
dialog.setModal(True)
|
938
|
+
dialog.show()
|
939
|
+
dialog.raise_()
|
1045
940
|
qApp.processEvents()
|
1046
|
-
|
1047
|
-
|
941
|
+
dialog.populateGUI()
|
1048
942
|
if showNotes:
|
1049
|
-
|
1050
|
-
|
1051
|
-
return True
|
943
|
+
dialog.showReleaseNotes()
|
944
|
+
return
|
1052
945
|
|
946
|
+
@pyqtSlot()
|
1053
947
|
def showAboutQtDialog(self) -> None:
|
1054
|
-
"""Show the about dialog
|
948
|
+
"""Show the Qt about dialog."""
|
1055
949
|
msgBox = QMessageBox(self)
|
1056
950
|
msgBox.aboutQt(self, "About Qt")
|
1057
951
|
return
|
1058
952
|
|
953
|
+
@pyqtSlot()
|
1059
954
|
def showUpdatesDialog(self) -> None:
|
1060
955
|
"""Show the check for updates dialog."""
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
dlgUpdate.setModal(True)
|
1067
|
-
dlgUpdate.show()
|
1068
|
-
dlgUpdate.raise_()
|
956
|
+
dialog = GuiUpdates(self)
|
957
|
+
dialog.setModal(True)
|
958
|
+
dialog.show()
|
959
|
+
dialog.raise_()
|
1069
960
|
qApp.processEvents()
|
1070
|
-
|
1071
|
-
|
961
|
+
dialog.checkLatest()
|
1072
962
|
return
|
1073
963
|
|
1074
964
|
@pyqtSlot()
|
1075
965
|
def showDictionariesDialog(self) -> None:
|
1076
966
|
"""Show the download dictionaries dialog."""
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
967
|
+
dialog = GuiDictionaries(self)
|
968
|
+
dialog.setModal(True)
|
969
|
+
dialog.show()
|
970
|
+
dialog.raise_()
|
1081
971
|
qApp.processEvents()
|
1082
|
-
if not
|
1083
|
-
|
972
|
+
if not dialog.initDialog():
|
973
|
+
dialog.close()
|
1084
974
|
SHARED.error(self.tr("Could not initialise the dialog."))
|
1085
|
-
|
1086
975
|
return
|
1087
976
|
|
1088
977
|
def reportConfErr(self) -> bool:
|
@@ -1248,6 +1137,65 @@ class GuiMain(QMainWindow):
|
|
1248
1137
|
# Private Slots
|
1249
1138
|
##
|
1250
1139
|
|
1140
|
+
@pyqtSlot(bool, bool, bool, bool)
|
1141
|
+
def _processConfigChanges(self, restart: bool, tree: bool, theme: bool, syntax: bool) -> None:
|
1142
|
+
"""Refresh GUI based on flags from the Preferences dialog."""
|
1143
|
+
logger.debug("Applying new preferences")
|
1144
|
+
self.initMain()
|
1145
|
+
self.saveDocument()
|
1146
|
+
|
1147
|
+
if restart:
|
1148
|
+
SHARED.info(self.tr(
|
1149
|
+
"Some changes will not be applied until novelWriter has been restarted."
|
1150
|
+
))
|
1151
|
+
|
1152
|
+
if tree:
|
1153
|
+
self.projView.populateTree()
|
1154
|
+
|
1155
|
+
if theme:
|
1156
|
+
# We are doing this manually instead of connecting to
|
1157
|
+
# qApp.paletteChanged since the processing order matters
|
1158
|
+
SHARED.theme.loadTheme()
|
1159
|
+
self.docEditor.updateTheme()
|
1160
|
+
self.docViewer.updateTheme()
|
1161
|
+
self.docViewerPanel.updateTheme()
|
1162
|
+
self.sideBar.updateTheme()
|
1163
|
+
self.projView.updateTheme()
|
1164
|
+
self.novelView.updateTheme()
|
1165
|
+
self.outlineView.updateTheme()
|
1166
|
+
self.itemDetails.updateTheme()
|
1167
|
+
self.mainStatus.updateTheme()
|
1168
|
+
|
1169
|
+
if syntax:
|
1170
|
+
SHARED.theme.loadSyntax()
|
1171
|
+
self.docEditor.updateSyntaxColours()
|
1172
|
+
|
1173
|
+
self.docEditor.initEditor()
|
1174
|
+
self.docViewer.initViewer()
|
1175
|
+
self.projView.initSettings()
|
1176
|
+
self.novelView.initSettings()
|
1177
|
+
self.outlineView.initSettings()
|
1178
|
+
self._updateStatusWordCount()
|
1179
|
+
|
1180
|
+
return
|
1181
|
+
|
1182
|
+
@pyqtSlot()
|
1183
|
+
def _processProjectSettingsChanges(self) -> None:
|
1184
|
+
"""Refresh data dependent on project settings."""
|
1185
|
+
logger.debug("Applying new project settings")
|
1186
|
+
SHARED.updateSpellCheckLanguage()
|
1187
|
+
self.itemDetails.refreshDetails()
|
1188
|
+
self._updateWindowTitle(SHARED.project.data.name)
|
1189
|
+
return
|
1190
|
+
|
1191
|
+
@pyqtSlot()
|
1192
|
+
def _processWordListChanges(self) -> None:
|
1193
|
+
"""Reload project word list."""
|
1194
|
+
logger.debug("Reloading word list")
|
1195
|
+
SHARED.updateSpellCheckLanguage(reload=True)
|
1196
|
+
self.docEditor.spellCheckDocument()
|
1197
|
+
return
|
1198
|
+
|
1251
1199
|
@pyqtSlot(str, nwDocMode)
|
1252
1200
|
def _followTag(self, tag: str, mode: nwDocMode) -> None:
|
1253
1201
|
"""Follow a tag after user interaction with a link."""
|
@@ -1331,6 +1279,8 @@ class GuiMain(QMainWindow):
|
|
1331
1279
|
self.mainStatus.setUserIdle(editIdle or userIdle)
|
1332
1280
|
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
1333
1281
|
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
1282
|
+
if CONFIG.memInfo and int(currTime) % 5 == 0: # pragma: no cover
|
1283
|
+
self.mainStatus.memInfo()
|
1334
1284
|
return
|
1335
1285
|
|
1336
1286
|
@pyqtSlot()
|
@@ -1474,8 +1424,8 @@ class GuiMain(QMainWindow):
|
|
1474
1424
|
self.addAction(self.mainMenu.aReplaceNext)
|
1475
1425
|
|
1476
1426
|
# Format
|
1477
|
-
self.addAction(self.mainMenu.
|
1478
|
-
self.addAction(self.mainMenu.
|
1427
|
+
self.addAction(self.mainMenu.aFmtItalic)
|
1428
|
+
self.addAction(self.mainMenu.aFmtBold)
|
1479
1429
|
self.addAction(self.mainMenu.aFmtStrike)
|
1480
1430
|
self.addAction(self.mainMenu.aFmtDQuote)
|
1481
1431
|
self.addAction(self.mainMenu.aFmtSQuote)
|
novelwriter/shared.py
CHANGED
@@ -3,10 +3,11 @@ novelWriter – Shared Data Class
|
|
3
3
|
===============================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2023-08-10 [2.1rc1]
|
6
|
+
Created: 2023-08-10 [2.1rc1] SharedData
|
7
|
+
Created: 2023-08-14 [2.1rc1] _GuiAlert
|
7
8
|
|
8
9
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
10
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
11
|
|
11
12
|
This program is free software: you can redistribute it and/or modify
|
12
13
|
it under the terms of the GNU General Public License as published by
|
@@ -26,7 +27,7 @@ from __future__ import annotations
|
|
26
27
|
import logging
|
27
28
|
|
28
29
|
from time import time
|
29
|
-
from typing import TYPE_CHECKING
|
30
|
+
from typing import TYPE_CHECKING, TypeVar
|
30
31
|
from pathlib import Path
|
31
32
|
|
32
33
|
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
|
@@ -41,11 +42,13 @@ if TYPE_CHECKING: # pragma: no cover
|
|
41
42
|
|
42
43
|
logger = logging.getLogger(__name__)
|
43
44
|
|
45
|
+
NWWidget = TypeVar("NWWidget", bound=QWidget)
|
46
|
+
|
44
47
|
|
45
48
|
class SharedData(QObject):
|
46
49
|
|
47
50
|
__slots__ = (
|
48
|
-
"_gui", "_theme", "_project", "_spelling", "_lockedBy", "
|
51
|
+
"_gui", "_theme", "_project", "_spelling", "_lockedBy", "_lastAlert",
|
49
52
|
"_idleTime", "_idleRefTime",
|
50
53
|
)
|
51
54
|
|
@@ -68,7 +71,7 @@ class SharedData(QObject):
|
|
68
71
|
|
69
72
|
# Settings
|
70
73
|
self._lockedBy = None
|
71
|
-
self.
|
74
|
+
self._lastAlert = ""
|
72
75
|
self._idleTime = 0.0
|
73
76
|
self._idleRefTime = time()
|
74
77
|
|
@@ -122,9 +125,9 @@ class SharedData(QObject):
|
|
122
125
|
return self._idleTime
|
123
126
|
|
124
127
|
@property
|
125
|
-
def
|
126
|
-
"""Return
|
127
|
-
return self.
|
128
|
+
def lastAlert(self) -> str:
|
129
|
+
"""Return the last alert message."""
|
130
|
+
return self._lastAlert
|
128
131
|
|
129
132
|
##
|
130
133
|
# Methods
|
@@ -214,6 +217,13 @@ class SharedData(QObject):
|
|
214
217
|
QThreadPool.globalInstance().start(runnable, priority=priority)
|
215
218
|
return
|
216
219
|
|
220
|
+
def findTopLevelWidget(self, kind: type[NWWidget]) -> NWWidget | None:
|
221
|
+
"""Find a top level widget."""
|
222
|
+
for widget in self.mainGui.children():
|
223
|
+
if isinstance(widget, kind):
|
224
|
+
return widget
|
225
|
+
return None
|
226
|
+
|
217
227
|
##
|
218
228
|
# Signal Proxy
|
219
229
|
##
|
@@ -238,44 +248,53 @@ class SharedData(QObject):
|
|
238
248
|
|
239
249
|
def info(self, text: str, info: str = "", details: str = "", log: bool = True) -> None:
|
240
250
|
"""Open an information alert box."""
|
241
|
-
|
242
|
-
|
243
|
-
|
251
|
+
alert = _GuiAlert(self.mainGui, self.theme)
|
252
|
+
alert.setMessage(text, info, details)
|
253
|
+
alert.setAlertType(_GuiAlert.INFO, False)
|
254
|
+
self._lastAlert = alert.logMessage
|
244
255
|
if log:
|
245
|
-
logger.info(self.
|
246
|
-
|
256
|
+
logger.info(self._lastAlert, stacklevel=2)
|
257
|
+
alert.exec_()
|
258
|
+
alert.deleteLater()
|
247
259
|
return
|
248
260
|
|
249
261
|
def warn(self, text: str, info: str = "", details: str = "", log: bool = True) -> None:
|
250
262
|
"""Open a warning alert box."""
|
251
|
-
|
252
|
-
|
253
|
-
|
263
|
+
alert = _GuiAlert(self.mainGui, self.theme)
|
264
|
+
alert.setMessage(text, info, details)
|
265
|
+
alert.setAlertType(_GuiAlert.WARN, False)
|
266
|
+
self._lastAlert = alert.logMessage
|
254
267
|
if log:
|
255
|
-
logger.warning(self.
|
256
|
-
|
268
|
+
logger.warning(self._lastAlert, stacklevel=2)
|
269
|
+
alert.exec_()
|
270
|
+
alert.deleteLater()
|
257
271
|
return
|
258
272
|
|
259
273
|
def error(self, text: str, info: str = "", details: str = "", log: bool = True,
|
260
274
|
exc: Exception | None = None) -> None:
|
261
275
|
"""Open an error alert box."""
|
262
|
-
|
263
|
-
|
264
|
-
|
276
|
+
alert = _GuiAlert(self.mainGui, self.theme)
|
277
|
+
alert.setMessage(text, info, details)
|
278
|
+
alert.setAlertType(_GuiAlert.ERROR, False)
|
265
279
|
if exc:
|
266
|
-
|
280
|
+
alert.setException(exc)
|
281
|
+
self._lastAlert = alert.logMessage
|
267
282
|
if log:
|
268
|
-
logger.error(self.
|
269
|
-
|
283
|
+
logger.error(self._lastAlert, stacklevel=2)
|
284
|
+
alert.exec_()
|
285
|
+
alert.deleteLater()
|
270
286
|
return
|
271
287
|
|
272
288
|
def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool:
|
273
289
|
"""Open a question box."""
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
self.
|
278
|
-
|
290
|
+
alert = _GuiAlert(self.mainGui, self.theme)
|
291
|
+
alert.setMessage(text, info, details)
|
292
|
+
alert.setAlertType(_GuiAlert.WARN if warn else _GuiAlert.ASK, True)
|
293
|
+
self._lastAlert = alert.logMessage
|
294
|
+
alert.exec_()
|
295
|
+
isYes = alert.result() == QMessageBox.StandardButton.Yes
|
296
|
+
alert.deleteLater()
|
297
|
+
return isYes
|
279
298
|
|
280
299
|
##
|
281
300
|
# Internal Functions
|
@@ -312,6 +331,11 @@ class _GuiAlert(QMessageBox):
|
|
312
331
|
super().__init__(parent=parent)
|
313
332
|
self._theme = theme
|
314
333
|
self._message = ""
|
334
|
+
logger.debug("Ready: _GuiAlert")
|
335
|
+
return
|
336
|
+
|
337
|
+
def __del__(self) -> None: # pragma: no cover
|
338
|
+
logger.debug("Delete: _GuiAlert")
|
315
339
|
return
|
316
340
|
|
317
341
|
@property
|
@@ -3,10 +3,10 @@ novelWriter – GUI Dictionary Downloader
|
|
3
3
|
=======================================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created: 2023-11-19 [2.2rc1]
|
6
|
+
Created: 2023-11-19 [2.2rc1] GuiDictionaries
|
7
7
|
|
8
8
|
This file is a part of novelWriter
|
9
|
-
Copyright 2018–
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
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
|