novelWriter 2.2rc1__py3-none-any.whl → 2.3b1__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.3b1.dist-info}/METADATA +1 -1
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +141 -129
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +11 -6
- 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 +9 -2
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
- 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_dark/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +9 -2
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
- 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/icons/typicons_light/typ_document-add-col.svg +8 -0
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
- novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
- novelwriter/assets/images/novelwriter-text-light.svg +4 -0
- novelwriter/assets/images/welcome-dark.jpg +0 -0
- novelwriter/assets/images/welcome-light.jpg +0 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/default_dark.conf +1 -0
- novelwriter/assets/syntax/default_light.conf +1 -0
- novelwriter/assets/syntax/grey_dark.conf +1 -0
- novelwriter/assets/syntax/grey_light.conf +1 -0
- novelwriter/assets/syntax/light_owl.conf +1 -0
- novelwriter/assets/syntax/night_owl.conf +1 -0
- novelwriter/assets/syntax/solarized_dark.conf +1 -0
- novelwriter/assets/syntax/solarized_light.conf +1 -0
- novelwriter/assets/syntax/tomorrow.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
- novelwriter/assets/text/credits_en.htm +4 -2
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +64 -66
- novelwriter/config.py +39 -44
- novelwriter/constants.py +39 -17
- novelwriter/core/buildsettings.py +8 -8
- novelwriter/core/coretools.py +194 -155
- novelwriter/core/docbuild.py +7 -4
- novelwriter/core/document.py +7 -7
- novelwriter/core/index.py +90 -57
- novelwriter/core/item.py +23 -5
- novelwriter/core/options.py +11 -10
- novelwriter/core/project.py +72 -47
- novelwriter/core/projectdata.py +3 -16
- novelwriter/core/projectxml.py +14 -42
- novelwriter/core/sessions.py +4 -3
- novelwriter/core/spellcheck.py +6 -4
- novelwriter/core/status.py +5 -4
- novelwriter/core/storage.py +179 -141
- novelwriter/core/tohtml.py +6 -4
- novelwriter/core/tokenizer.py +74 -46
- novelwriter/core/tomd.py +2 -2
- novelwriter/core/toodt.py +41 -31
- novelwriter/core/tree.py +5 -4
- novelwriter/dialogs/about.py +88 -179
- novelwriter/dialogs/docmerge.py +30 -20
- novelwriter/dialogs/docsplit.py +33 -22
- novelwriter/dialogs/editlabel.py +20 -8
- novelwriter/dialogs/preferences.py +562 -725
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +301 -270
- novelwriter/dialogs/quotes.py +47 -36
- novelwriter/dialogs/wordlist.py +128 -59
- novelwriter/enum.py +25 -22
- novelwriter/error.py +2 -2
- novelwriter/extensions/circularprogress.py +12 -12
- novelwriter/extensions/configlayout.py +185 -146
- novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +27 -13
- novelwriter/extensions/pagedsidebar.py +15 -20
- novelwriter/extensions/simpleprogress.py +8 -9
- novelwriter/extensions/statusled.py +9 -9
- novelwriter/extensions/switch.py +32 -64
- novelwriter/extensions/switchbox.py +2 -7
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +250 -214
- novelwriter/gui/dochighlight.py +66 -94
- novelwriter/gui/docviewer.py +71 -98
- novelwriter/gui/docviewerpanel.py +140 -47
- novelwriter/gui/editordocument.py +3 -3
- novelwriter/gui/itemdetails.py +9 -9
- novelwriter/gui/mainmenu.py +47 -46
- novelwriter/gui/noveltree.py +53 -61
- novelwriter/gui/outline.py +100 -76
- novelwriter/gui/projtree.py +193 -67
- novelwriter/gui/sidebar.py +9 -8
- novelwriter/gui/statusbar.py +49 -7
- novelwriter/gui/theme.py +65 -74
- novelwriter/guimain.py +173 -330
- novelwriter/shared.py +68 -30
- novelwriter/tools/dictionaries.py +7 -8
- novelwriter/tools/lipsum.py +34 -28
- novelwriter/tools/manusbuild.py +3 -4
- novelwriter/tools/manuscript.py +25 -32
- novelwriter/tools/manussettings.py +194 -225
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +802 -0
- novelwriter/tools/writingstats.py +26 -13
- 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/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -17
- novelwriter/dialogs/projdetails.py +0 -525
- novelwriter/dialogs/projload.py +0 -298
- novelwriter/dialogs/updates.py +0 -182
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.2rc1.dist-info → novelWriter-2.3b1.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
|
@@ -30,14 +30,15 @@ from time import time
|
|
30
30
|
from pathlib import Path
|
31
31
|
from datetime import datetime
|
32
32
|
|
33
|
-
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
|
34
33
|
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
|
34
|
+
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
|
35
35
|
from PyQt5.QtWidgets import (
|
36
|
-
|
37
|
-
|
36
|
+
QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, QSplitter,
|
37
|
+
QStackedWidget, QVBoxLayout, QWidget, qApp
|
38
38
|
)
|
39
39
|
|
40
|
-
from novelwriter import CONFIG, SHARED, __hexversion__
|
40
|
+
from novelwriter import CONFIG, SHARED, __hexversion__, __version__
|
41
|
+
from novelwriter.constants import nwConst
|
41
42
|
from novelwriter.gui.theme import GuiTheme
|
42
43
|
from novelwriter.gui.sidebar import GuiSideBar
|
43
44
|
from novelwriter.gui.outline import GuiOutlineView
|
@@ -50,24 +51,19 @@ from novelwriter.gui.statusbar import GuiMainStatus
|
|
50
51
|
from novelwriter.gui.itemdetails import GuiItemDetails
|
51
52
|
from novelwriter.gui.docviewerpanel import GuiDocViewerPanel
|
52
53
|
from novelwriter.dialogs.about import GuiAbout
|
53
|
-
from novelwriter.dialogs.updates import GuiUpdates
|
54
|
-
from novelwriter.dialogs.projload import GuiProjectLoad
|
55
54
|
from novelwriter.dialogs.wordlist import GuiWordList
|
56
55
|
from novelwriter.dialogs.preferences import GuiPreferences
|
57
|
-
from novelwriter.dialogs.
|
58
|
-
from novelwriter.
|
59
|
-
from novelwriter.tools.lipsum import GuiLipsum
|
56
|
+
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
57
|
+
from novelwriter.tools.welcome import GuiWelcome
|
60
58
|
from novelwriter.tools.manuscript import GuiManuscript
|
61
|
-
from novelwriter.tools.projwizard import GuiProjectWizard
|
62
59
|
from novelwriter.tools.dictionaries import GuiDictionaries
|
60
|
+
from novelwriter.tools.noveldetails import GuiNovelDetails
|
63
61
|
from novelwriter.tools.writingstats import GuiWritingStats
|
64
|
-
from novelwriter.core.coretools import ProjectBuilder
|
65
62
|
|
66
63
|
from novelwriter.enum import (
|
67
|
-
nwDocAction, nwDocInsert, nwDocMode, nwItemType,
|
64
|
+
nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwWidget, nwView
|
68
65
|
)
|
69
|
-
from novelwriter.common import
|
70
|
-
from novelwriter.constants import nwFiles
|
66
|
+
from novelwriter.common import formatFileFilter, formatVersion, hexToInt
|
71
67
|
|
72
68
|
logger = logging.getLogger(__name__)
|
73
69
|
|
@@ -83,13 +79,6 @@ class GuiMain(QMainWindow):
|
|
83
79
|
function. Also, the project instance and theme instance are created
|
84
80
|
here. These should be passed around to all other objects who need
|
85
81
|
them and new instances of them should generally not be created.
|
86
|
-
|
87
|
-
* All other GUI classes that depend on any components from the
|
88
|
-
main GUI should be passed a reference to the instance of this
|
89
|
-
class.
|
90
|
-
* All non-GUI classes can be passed a reference to the NWProject
|
91
|
-
instance if the Main GUI is not needed (which it generally
|
92
|
-
shouldn't need).
|
93
82
|
"""
|
94
83
|
|
95
84
|
def __init__(self) -> None:
|
@@ -338,19 +327,9 @@ class GuiMain(QMainWindow):
|
|
338
327
|
self.asProjTimer.start()
|
339
328
|
self.asDocTimer.start()
|
340
329
|
self.mainStatus.clearStatus()
|
341
|
-
|
342
|
-
# Handle Windows Mode
|
343
330
|
self.showNormal()
|
344
331
|
|
345
332
|
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
333
|
logger.info("novelWriter is ready ...")
|
355
334
|
self.mainStatus.setStatusMessage(self.tr("novelWriter is ready ..."))
|
356
335
|
|
@@ -370,12 +349,18 @@ class GuiMain(QMainWindow):
|
|
370
349
|
self.openProject(cmdOpen)
|
371
350
|
|
372
351
|
if not SHARED.hasProject:
|
373
|
-
self.
|
352
|
+
self.showWelcomeDialog()
|
374
353
|
|
375
|
-
#
|
354
|
+
# If this is a new release, let the user know
|
376
355
|
if hexToInt(CONFIG.lastNotes) < hexToInt(__hexversion__):
|
377
356
|
CONFIG.lastNotes = __hexversion__
|
378
|
-
self.
|
357
|
+
trVersion = self.tr(
|
358
|
+
"You are now running novelWriter version {0}.".format(formatVersion(__version__))
|
359
|
+
)
|
360
|
+
trRelease = self.tr(
|
361
|
+
"Please check the {0}release notes{1} for further details."
|
362
|
+
).format(f"<a href='{nwConst.URL_RELEASES}'>", "</a>")
|
363
|
+
SHARED.info(f"{trVersion}<br>{trRelease}")
|
379
364
|
|
380
365
|
return
|
381
366
|
|
@@ -383,42 +368,6 @@ class GuiMain(QMainWindow):
|
|
383
368
|
# Project Actions
|
384
369
|
##
|
385
370
|
|
386
|
-
def newProject(self, projData: dict | None = None) -> bool:
|
387
|
-
"""Create a new project via the new project wizard."""
|
388
|
-
if SHARED.hasProject:
|
389
|
-
if not self.closeProject():
|
390
|
-
SHARED.error(self.tr(
|
391
|
-
"Cannot create a new project when another project is open."
|
392
|
-
))
|
393
|
-
return False
|
394
|
-
|
395
|
-
if projData is None:
|
396
|
-
projData = self.showNewProjectDialog()
|
397
|
-
|
398
|
-
if projData is None:
|
399
|
-
return False
|
400
|
-
|
401
|
-
projPath = projData.get("projPath", None)
|
402
|
-
if projPath is None or projData is None:
|
403
|
-
logger.error("No projData or projPath set")
|
404
|
-
return False
|
405
|
-
|
406
|
-
if (Path(projPath) / nwFiles.PROJ_FILE).is_file():
|
407
|
-
SHARED.error(self.tr(
|
408
|
-
"A project already exists in that location. "
|
409
|
-
"Please choose another folder."
|
410
|
-
))
|
411
|
-
return False
|
412
|
-
|
413
|
-
logger.info("Creating new project")
|
414
|
-
nwProject = ProjectBuilder()
|
415
|
-
if nwProject.buildProject(projData):
|
416
|
-
self.openProject(projPath)
|
417
|
-
else:
|
418
|
-
return False
|
419
|
-
|
420
|
-
return True
|
421
|
-
|
422
371
|
def closeProject(self, isYes: bool = False) -> bool:
|
423
372
|
"""Close the project if one is open. isYes is passed on from the
|
424
373
|
close application event so the user doesn't get prompted twice
|
@@ -457,7 +406,7 @@ class GuiMain(QMainWindow):
|
|
457
406
|
self.docViewerPanel.closeProjectTasks()
|
458
407
|
self.outlineView.closeProjectTasks()
|
459
408
|
self.novelView.closeProjectTasks()
|
460
|
-
self.projView.
|
409
|
+
self.projView.closeProjectTasks()
|
461
410
|
self.itemDetails.clearDetails()
|
462
411
|
self.mainStatus.clearStatus()
|
463
412
|
|
@@ -473,8 +422,7 @@ class GuiMain(QMainWindow):
|
|
473
422
|
if projFile is None:
|
474
423
|
return False
|
475
424
|
|
476
|
-
# Make sure any open project is cleared out first
|
477
|
-
# another one
|
425
|
+
# Make sure any open project is cleared out first
|
478
426
|
if not self.closeProject():
|
479
427
|
return False
|
480
428
|
|
@@ -617,7 +565,7 @@ class GuiMain(QMainWindow):
|
|
617
565
|
if self.docEditor.loadText(tHandle, tLine):
|
618
566
|
SHARED.project.data.setLastHandle(tHandle, "editor")
|
619
567
|
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
620
|
-
self.novelView.setActiveHandle(tHandle)
|
568
|
+
self.novelView.setActiveHandle(tHandle, doScroll=doScroll)
|
621
569
|
if changeFocus:
|
622
570
|
self.docEditor.setFocus()
|
623
571
|
else:
|
@@ -722,14 +670,9 @@ class GuiMain(QMainWindow):
|
|
722
670
|
return False
|
723
671
|
|
724
672
|
lastPath = CONFIG.lastPath()
|
725
|
-
|
726
|
-
self.tr("Text files ({0})").format("*.txt"),
|
727
|
-
self.tr("Markdown files ({0})").format("*.md"),
|
728
|
-
self.tr("novelWriter files ({0})").format("*.nwd"),
|
729
|
-
self.tr("All files ({0})").format("*"),
|
730
|
-
]
|
673
|
+
ffilter = formatFileFilter(["*.txt", "*.md", "*.nwd", "*"])
|
731
674
|
loadFile, _ = QFileDialog.getOpenFileName(
|
732
|
-
self, self.tr("Import File"), str(lastPath), filter=
|
675
|
+
self, self.tr("Import File"), str(lastPath), filter=ffilter
|
733
676
|
)
|
734
677
|
if not loadFile:
|
735
678
|
return False
|
@@ -737,10 +680,10 @@ class GuiMain(QMainWindow):
|
|
737
680
|
if loadFile.strip() == "":
|
738
681
|
return False
|
739
682
|
|
740
|
-
|
683
|
+
text = None
|
741
684
|
try:
|
742
685
|
with open(loadFile, mode="rt", encoding="utf-8") as inFile:
|
743
|
-
|
686
|
+
text = inFile.read()
|
744
687
|
CONFIG.setLastPath(loadFile)
|
745
688
|
except Exception as exc:
|
746
689
|
SHARED.error(self.tr(
|
@@ -762,7 +705,7 @@ class GuiMain(QMainWindow):
|
|
762
705
|
if not msgYes:
|
763
706
|
return False
|
764
707
|
|
765
|
-
self.docEditor.replaceText(
|
708
|
+
self.docEditor.replaceText(text)
|
766
709
|
|
767
710
|
return True
|
768
711
|
|
@@ -849,240 +792,108 @@ class GuiMain(QMainWindow):
|
|
849
792
|
# Main Dialogs
|
850
793
|
##
|
851
794
|
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
dlgProj = GuiProjectLoad(self)
|
859
|
-
dlgProj.exec_()
|
860
|
-
|
861
|
-
if dlgProj.result() == QDialog.Accepted:
|
862
|
-
if dlgProj.openState == GuiProjectLoad.OPEN_STATE:
|
863
|
-
self.openProject(dlgProj.openPath)
|
864
|
-
elif dlgProj.openState == GuiProjectLoad.NEW_STATE:
|
865
|
-
self.newProject()
|
866
|
-
|
795
|
+
@pyqtSlot()
|
796
|
+
def showWelcomeDialog(self) -> None:
|
797
|
+
"""Open the welcome dialog."""
|
798
|
+
dialog = GuiWelcome(self)
|
799
|
+
dialog.openProjectRequest.connect(self._openProject)
|
800
|
+
dialog.exec_()
|
867
801
|
return
|
868
802
|
|
869
|
-
|
870
|
-
"""Open the wizard and assemble a project options dict."""
|
871
|
-
newProj = GuiProjectWizard(self)
|
872
|
-
newProj.exec_()
|
873
|
-
|
874
|
-
if newProj.result() == QDialog.Accepted:
|
875
|
-
return self._assembleProjectWizardData(newProj)
|
876
|
-
|
877
|
-
return None
|
878
|
-
|
803
|
+
@pyqtSlot()
|
879
804
|
def showPreferencesDialog(self) -> None:
|
880
805
|
"""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
|
-
|
806
|
+
dialog = GuiPreferences(self)
|
807
|
+
dialog.newPreferencesReady.connect(self._processConfigChanges)
|
808
|
+
dialog.exec_()
|
923
809
|
return
|
924
810
|
|
811
|
+
@pyqtSlot()
|
925
812
|
@pyqtSlot(int)
|
926
|
-
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.
|
813
|
+
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.PAGE_SETTINGS) -> None:
|
927
814
|
"""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
|
942
|
-
|
943
|
-
def showProjectDetailsDialog(self) -> bool:
|
944
|
-
"""Open the project details dialog."""
|
945
|
-
if not SHARED.hasProject:
|
946
|
-
logger.error("No project open")
|
947
|
-
return False
|
948
|
-
|
949
|
-
dlgDetails = getGuiItem("GuiProjectDetails")
|
950
|
-
if dlgDetails is None:
|
951
|
-
dlgDetails = GuiProjectDetails(self)
|
952
|
-
assert isinstance(dlgDetails, GuiProjectDetails)
|
953
|
-
|
954
|
-
dlgDetails.setModal(True)
|
955
|
-
dlgDetails.show()
|
956
|
-
dlgDetails.raise_()
|
957
|
-
dlgDetails.updateValues()
|
815
|
+
if SHARED.hasProject:
|
816
|
+
dialog = GuiProjectSettings(self, gotoPage=focusTab)
|
817
|
+
dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
|
818
|
+
dialog.exec_()
|
819
|
+
return
|
958
820
|
|
959
|
-
|
821
|
+
@pyqtSlot()
|
822
|
+
def showNovelDetailsDialog(self) -> None:
|
823
|
+
"""Open the novel details dialog."""
|
824
|
+
if SHARED.hasProject:
|
825
|
+
dialog = GuiNovelDetails(self)
|
826
|
+
dialog.setModal(True)
|
827
|
+
dialog.show()
|
828
|
+
dialog.raise_()
|
829
|
+
qApp.processEvents()
|
830
|
+
dialog.updateValues()
|
831
|
+
return
|
960
832
|
|
961
833
|
@pyqtSlot()
|
962
|
-
def showBuildManuscriptDialog(self) ->
|
834
|
+
def showBuildManuscriptDialog(self) -> None:
|
963
835
|
"""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
|
836
|
+
if SHARED.hasProject:
|
837
|
+
if (dialog := SHARED.findTopLevelWidget(GuiManuscript)) is None:
|
838
|
+
dialog = GuiManuscript(self)
|
839
|
+
dialog.setModal(False)
|
840
|
+
dialog.show()
|
841
|
+
dialog.raise_()
|
842
|
+
qApp.processEvents()
|
843
|
+
dialog.loadContent()
|
844
|
+
return
|
999
845
|
|
1000
|
-
|
846
|
+
@pyqtSlot()
|
847
|
+
def showProjectWordListDialog(self) -> None:
|
1001
848
|
"""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
|
849
|
+
if SHARED.hasProject:
|
850
|
+
dialog = GuiWordList(self)
|
851
|
+
dialog.newWordListReady.connect(self._processWordListChanges)
|
852
|
+
dialog.exec_()
|
853
|
+
return
|
1015
854
|
|
1016
|
-
|
855
|
+
@pyqtSlot()
|
856
|
+
def showWritingStatsDialog(self) -> None:
|
1017
857
|
"""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)
|
858
|
+
if SHARED.hasProject:
|
859
|
+
if (dialog := SHARED.findTopLevelWidget(GuiWritingStats)) is None:
|
860
|
+
dialog = GuiWritingStats(self)
|
861
|
+
dialog.setModal(False)
|
862
|
+
dialog.show()
|
863
|
+
dialog.raise_()
|
864
|
+
qApp.processEvents()
|
865
|
+
dialog.populateGUI()
|
866
|
+
return
|
1041
867
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
868
|
+
@pyqtSlot()
|
869
|
+
def showAboutNWDialog(self) -> None:
|
870
|
+
"""Show the novelWriter about dialog."""
|
871
|
+
dialog = GuiAbout(self)
|
872
|
+
dialog.setModal(True)
|
873
|
+
dialog.show()
|
874
|
+
dialog.raise_()
|
1045
875
|
qApp.processEvents()
|
1046
|
-
|
1047
|
-
|
1048
|
-
if showNotes:
|
1049
|
-
dlgAbout.showReleaseNotes()
|
1050
|
-
|
1051
|
-
return True
|
876
|
+
dialog.populateGUI()
|
877
|
+
return
|
1052
878
|
|
879
|
+
@pyqtSlot()
|
1053
880
|
def showAboutQtDialog(self) -> None:
|
1054
|
-
"""Show the about dialog
|
881
|
+
"""Show the Qt about dialog."""
|
1055
882
|
msgBox = QMessageBox(self)
|
1056
883
|
msgBox.aboutQt(self, "About Qt")
|
1057
884
|
return
|
1058
885
|
|
1059
|
-
def showUpdatesDialog(self) -> None:
|
1060
|
-
"""Show the check for updates dialog."""
|
1061
|
-
dlgUpdate = getGuiItem("GuiUpdates")
|
1062
|
-
if dlgUpdate is None:
|
1063
|
-
dlgUpdate = GuiUpdates(self)
|
1064
|
-
assert isinstance(dlgUpdate, GuiUpdates)
|
1065
|
-
|
1066
|
-
dlgUpdate.setModal(True)
|
1067
|
-
dlgUpdate.show()
|
1068
|
-
dlgUpdate.raise_()
|
1069
|
-
qApp.processEvents()
|
1070
|
-
dlgUpdate.checkLatest()
|
1071
|
-
|
1072
|
-
return
|
1073
|
-
|
1074
886
|
@pyqtSlot()
|
1075
887
|
def showDictionariesDialog(self) -> None:
|
1076
888
|
"""Show the download dictionaries dialog."""
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
889
|
+
dialog = GuiDictionaries(self)
|
890
|
+
dialog.setModal(True)
|
891
|
+
dialog.show()
|
892
|
+
dialog.raise_()
|
1081
893
|
qApp.processEvents()
|
1082
|
-
if not
|
1083
|
-
|
894
|
+
if not dialog.initDialog():
|
895
|
+
dialog.close()
|
1084
896
|
SHARED.error(self.tr("Could not initialise the dialog."))
|
1085
|
-
|
1086
897
|
return
|
1087
898
|
|
1088
899
|
def reportConfErr(self) -> bool:
|
@@ -1248,6 +1059,67 @@ class GuiMain(QMainWindow):
|
|
1248
1059
|
# Private Slots
|
1249
1060
|
##
|
1250
1061
|
|
1062
|
+
@pyqtSlot(bool, bool, bool, bool)
|
1063
|
+
def _processConfigChanges(self, restart: bool, tree: bool, theme: bool, syntax: bool) -> None:
|
1064
|
+
"""Refresh GUI based on flags from the Preferences dialog."""
|
1065
|
+
logger.debug("Applying new preferences")
|
1066
|
+
self.initMain()
|
1067
|
+
self.saveDocument()
|
1068
|
+
|
1069
|
+
if restart:
|
1070
|
+
SHARED.info(self.tr(
|
1071
|
+
"Some changes will not be applied until novelWriter has been restarted."
|
1072
|
+
))
|
1073
|
+
|
1074
|
+
if tree:
|
1075
|
+
self.projView.populateTree()
|
1076
|
+
|
1077
|
+
if theme:
|
1078
|
+
# We are doing this manually instead of connecting to
|
1079
|
+
# qApp.paletteChanged since the processing order matters
|
1080
|
+
SHARED.theme.loadTheme()
|
1081
|
+
self.docEditor.updateTheme()
|
1082
|
+
self.docViewer.updateTheme()
|
1083
|
+
self.docViewerPanel.updateTheme()
|
1084
|
+
self.sideBar.updateTheme()
|
1085
|
+
self.projView.updateTheme()
|
1086
|
+
self.novelView.updateTheme()
|
1087
|
+
self.outlineView.updateTheme()
|
1088
|
+
self.itemDetails.updateTheme()
|
1089
|
+
self.mainStatus.updateTheme()
|
1090
|
+
|
1091
|
+
if syntax:
|
1092
|
+
SHARED.theme.loadSyntax()
|
1093
|
+
self.docEditor.updateSyntaxColours()
|
1094
|
+
|
1095
|
+
self.docEditor.initEditor()
|
1096
|
+
self.docViewer.initViewer()
|
1097
|
+
self.projView.initSettings()
|
1098
|
+
self.novelView.initSettings()
|
1099
|
+
self.outlineView.initSettings()
|
1100
|
+
self._updateStatusWordCount()
|
1101
|
+
|
1102
|
+
return
|
1103
|
+
|
1104
|
+
@pyqtSlot(bool)
|
1105
|
+
def _processProjectSettingsChanges(self, rebuildTrees: bool) -> None:
|
1106
|
+
"""Refresh data dependent on project settings."""
|
1107
|
+
logger.debug("Applying new project settings")
|
1108
|
+
SHARED.updateSpellCheckLanguage()
|
1109
|
+
self.itemDetails.refreshDetails()
|
1110
|
+
self._updateWindowTitle(SHARED.project.data.name)
|
1111
|
+
if rebuildTrees:
|
1112
|
+
self.rebuildTrees()
|
1113
|
+
return
|
1114
|
+
|
1115
|
+
@pyqtSlot()
|
1116
|
+
def _processWordListChanges(self) -> None:
|
1117
|
+
"""Reload project word list."""
|
1118
|
+
logger.debug("Reloading word list")
|
1119
|
+
SHARED.updateSpellCheckLanguage(reload=True)
|
1120
|
+
self.docEditor.spellCheckDocument()
|
1121
|
+
return
|
1122
|
+
|
1251
1123
|
@pyqtSlot(str, nwDocMode)
|
1252
1124
|
def _followTag(self, tag: str, mode: nwDocMode) -> None:
|
1253
1125
|
"""Follow a tag after user interaction with a link."""
|
@@ -1259,6 +1131,13 @@ class GuiMain(QMainWindow):
|
|
1259
1131
|
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
|
1260
1132
|
return
|
1261
1133
|
|
1134
|
+
@pyqtSlot(Path)
|
1135
|
+
def _openProject(self, path: Path) -> None:
|
1136
|
+
"""Handle an open project request."""
|
1137
|
+
qApp.processEvents()
|
1138
|
+
self.openProject(path)
|
1139
|
+
return
|
1140
|
+
|
1262
1141
|
@pyqtSlot(str, nwDocMode, str, bool)
|
1263
1142
|
def _openDocument(self, tHandle: str, mode: nwDocMode, sTitle: str, setFocus: bool) -> None:
|
1264
1143
|
"""Handle an open document request."""
|
@@ -1331,6 +1210,8 @@ class GuiMain(QMainWindow):
|
|
1331
1210
|
self.mainStatus.setUserIdle(editIdle or userIdle)
|
1332
1211
|
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
1333
1212
|
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
1213
|
+
if CONFIG.memInfo and int(currTime) % 5 == 0: # pragma: no cover
|
1214
|
+
self.mainStatus.memInfo()
|
1334
1215
|
return
|
1335
1216
|
|
1336
1217
|
@pyqtSlot()
|
@@ -1474,8 +1355,8 @@ class GuiMain(QMainWindow):
|
|
1474
1355
|
self.addAction(self.mainMenu.aReplaceNext)
|
1475
1356
|
|
1476
1357
|
# Format
|
1477
|
-
self.addAction(self.mainMenu.
|
1478
|
-
self.addAction(self.mainMenu.
|
1358
|
+
self.addAction(self.mainMenu.aFmtItalic)
|
1359
|
+
self.addAction(self.mainMenu.aFmtBold)
|
1479
1360
|
self.addAction(self.mainMenu.aFmtStrike)
|
1480
1361
|
self.addAction(self.mainMenu.aFmtDQuote)
|
1481
1362
|
self.addAction(self.mainMenu.aFmtSQuote)
|
@@ -1511,44 +1392,6 @@ class GuiMain(QMainWindow):
|
|
1511
1392
|
self.setWindowTitle(winTitle)
|
1512
1393
|
return
|
1513
1394
|
|
1514
|
-
def _assembleProjectWizardData(self, newProj: GuiProjectWizard) -> dict:
|
1515
|
-
"""Extract the user choices from the New Project Wizard and
|
1516
|
-
store them in a dictionary.
|
1517
|
-
"""
|
1518
|
-
projData = {
|
1519
|
-
"projName": newProj.field("projName"),
|
1520
|
-
"projTitle": newProj.field("projTitle"),
|
1521
|
-
"projAuthor": newProj.field("projAuthor"),
|
1522
|
-
"projPath": newProj.field("projPath"),
|
1523
|
-
"popSample": newProj.field("popSample"),
|
1524
|
-
"popMinimal": newProj.field("popMinimal"),
|
1525
|
-
"popCustom": newProj.field("popCustom"),
|
1526
|
-
"addRoots": [],
|
1527
|
-
"addNotes": False,
|
1528
|
-
"numChapters": 0,
|
1529
|
-
"numScenes": 0,
|
1530
|
-
}
|
1531
|
-
if newProj.field("popCustom"):
|
1532
|
-
addRoots = []
|
1533
|
-
if newProj.field("addPlot"):
|
1534
|
-
addRoots.append(nwItemClass.PLOT)
|
1535
|
-
if newProj.field("addChar"):
|
1536
|
-
addRoots.append(nwItemClass.CHARACTER)
|
1537
|
-
if newProj.field("addWorld"):
|
1538
|
-
addRoots.append(nwItemClass.WORLD)
|
1539
|
-
projData["addRoots"] = addRoots
|
1540
|
-
projData["addNotes"] = newProj.field("addNotes")
|
1541
|
-
projData["numChapters"] = newProj.field("numChapters")
|
1542
|
-
projData["numScenes"] = newProj.field("numScenes")
|
1543
|
-
|
1544
|
-
try:
|
1545
|
-
langIdx = newProj.field("projLang")
|
1546
|
-
projData["projLang"] = CONFIG.listLanguages(CONFIG.LANG_PROJ)[langIdx][0]
|
1547
|
-
except Exception:
|
1548
|
-
projData["projLang"] = "en_GB"
|
1549
|
-
|
1550
|
-
return projData
|
1551
|
-
|
1552
1395
|
def _getTagSource(self, tag: str) -> tuple[str | None, str | None]:
|
1553
1396
|
"""Handle the index lookup of a tag and display an alert if the
|
1554
1397
|
tag cannot be found.
|