novelWriter 2.2.1__py3-none-any.whl → 2.3__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.2.1.dist-info → novelWriter-2.3.dist-info}/METADATA +1 -1
- {novelWriter-2.2.1.dist-info → novelWriter-2.3.dist-info}/RECORD +116 -101
- novelWriter-2.3.dist-info/entry_points.txt +2 -0
- novelwriter/__init__.py +4 -4
- 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_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/project_nl_NL.json +11 -0
- novelwriter/assets/i18n/project_pt_BR.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +8 -0
- novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -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_dark/typ_th-list.svg +9 -0
- novelwriter/assets/icons/typicons_light/icons.conf +8 -0
- novelwriter/assets/icons/typicons_light/mixed_document-new.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -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/icons/typicons_light/typ_th-list.svg +9 -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/cyberpunk_night.conf +26 -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/tango.conf +23 -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/cyberpunk_night.conf +29 -0
- novelwriter/assets/themes/default_dark.conf +2 -2
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +48 -37
- novelwriter/config.py +36 -41
- novelwriter/constants.py +38 -16
- novelwriter/core/buildsettings.py +7 -7
- novelwriter/core/coretools.py +196 -156
- novelwriter/core/docbuild.py +6 -3
- novelwriter/core/document.py +6 -6
- novelwriter/core/index.py +89 -56
- novelwriter/core/item.py +21 -3
- novelwriter/core/options.py +8 -7
- novelwriter/core/project.py +70 -44
- novelwriter/core/projectdata.py +1 -14
- novelwriter/core/projectxml.py +13 -41
- novelwriter/core/sessions.py +2 -1
- novelwriter/core/spellcheck.py +2 -1
- novelwriter/core/status.py +2 -1
- novelwriter/core/storage.py +182 -140
- novelwriter/core/tohtml.py +4 -2
- novelwriter/core/tokenizer.py +109 -82
- novelwriter/core/toodt.py +40 -30
- novelwriter/core/tree.py +3 -2
- novelwriter/dialogs/about.py +70 -160
- novelwriter/dialogs/docmerge.py +6 -5
- novelwriter/dialogs/docsplit.py +6 -6
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +553 -703
- novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
- novelwriter/dialogs/quotes.py +27 -23
- novelwriter/dialogs/wordlist.py +96 -40
- novelwriter/enum.py +20 -18
- novelwriter/error.py +1 -1
- novelwriter/extensions/circularprogress.py +11 -11
- novelwriter/extensions/configlayout.py +185 -134
- novelwriter/extensions/modified.py +81 -0
- novelwriter/extensions/novelselector.py +26 -12
- novelwriter/extensions/pagedsidebar.py +14 -16
- novelwriter/extensions/simpleprogress.py +5 -5
- novelwriter/extensions/statusled.py +8 -8
- novelwriter/extensions/switch.py +31 -63
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +153 -0
- novelwriter/gui/doceditor.py +178 -150
- novelwriter/gui/dochighlight.py +63 -92
- novelwriter/gui/docviewer.py +49 -51
- novelwriter/gui/docviewerpanel.py +72 -24
- novelwriter/gui/itemdetails.py +7 -7
- novelwriter/gui/mainmenu.py +14 -19
- novelwriter/gui/noveltree.py +9 -8
- novelwriter/gui/outline.py +98 -75
- novelwriter/gui/projtree.py +241 -106
- novelwriter/gui/sidebar.py +3 -4
- novelwriter/gui/statusbar.py +3 -4
- novelwriter/gui/theme.py +69 -70
- novelwriter/guimain.py +51 -156
- novelwriter/shared.py +15 -1
- novelwriter/tools/dictionaries.py +5 -6
- novelwriter/tools/manuscript.py +6 -6
- novelwriter/tools/manussettings.py +192 -221
- novelwriter/tools/noveldetails.py +525 -0
- novelwriter/tools/welcome.py +819 -0
- novelwriter/tools/writingstats.py +9 -9
- novelWriter-2.2.1.dist-info/entry_points.txt +0 -5
- novelwriter/assets/images/wizard-back.jpg +0 -0
- novelwriter/assets/text/gplv3_en.htm +0 -641
- novelwriter/assets/text/release_notes.htm +0 -60
- novelwriter/dialogs/projdetails.py +0 -518
- novelwriter/dialogs/projload.py +0 -294
- novelwriter/dialogs/updates.py +0 -172
- novelwriter/extensions/pageddialog.py +0 -130
- novelwriter/tools/projwizard.py +0 -478
- {novelWriter-2.2.1.dist-info → novelWriter-2.3.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3.dist-info}/WHEEL +0 -0
- {novelWriter-2.2.1.dist-info → novelWriter-2.3.dist-info}/top_level.txt +0 -0
novelwriter/guimain.py
CHANGED
@@ -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,23 +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.
|
56
|
+
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
57
|
+
from novelwriter.tools.welcome import GuiWelcome
|
59
58
|
from novelwriter.tools.manuscript import GuiManuscript
|
60
|
-
from novelwriter.tools.projwizard import GuiProjectWizard
|
61
59
|
from novelwriter.tools.dictionaries import GuiDictionaries
|
60
|
+
from novelwriter.tools.noveldetails import GuiNovelDetails
|
62
61
|
from novelwriter.tools.writingstats import GuiWritingStats
|
63
|
-
from novelwriter.core.coretools import ProjectBuilder
|
64
62
|
|
65
63
|
from novelwriter.enum import (
|
66
|
-
nwDocAction, nwDocInsert, nwDocMode, nwItemType,
|
64
|
+
nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwWidget, nwView
|
67
65
|
)
|
68
|
-
from novelwriter.common import hexToInt
|
69
|
-
from novelwriter.constants import nwFiles
|
66
|
+
from novelwriter.common import formatFileFilter, formatVersion, hexToInt
|
70
67
|
|
71
68
|
logger = logging.getLogger(__name__)
|
72
69
|
|
@@ -82,13 +79,6 @@ class GuiMain(QMainWindow):
|
|
82
79
|
function. Also, the project instance and theme instance are created
|
83
80
|
here. These should be passed around to all other objects who need
|
84
81
|
them and new instances of them should generally not be created.
|
85
|
-
|
86
|
-
* All other GUI classes that depend on any components from the
|
87
|
-
main GUI should be passed a reference to the instance of this
|
88
|
-
class.
|
89
|
-
* All non-GUI classes can be passed a reference to the NWProject
|
90
|
-
instance if the Main GUI is not needed (which it generally
|
91
|
-
shouldn't need).
|
92
82
|
"""
|
93
83
|
|
94
84
|
def __init__(self) -> None:
|
@@ -359,12 +349,18 @@ class GuiMain(QMainWindow):
|
|
359
349
|
self.openProject(cmdOpen)
|
360
350
|
|
361
351
|
if not SHARED.hasProject:
|
362
|
-
self.
|
352
|
+
self.showWelcomeDialog()
|
363
353
|
|
364
|
-
#
|
354
|
+
# If this is a new release, let the user know
|
365
355
|
if hexToInt(CONFIG.lastNotes) < hexToInt(__hexversion__):
|
366
356
|
CONFIG.lastNotes = __hexversion__
|
367
|
-
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}")
|
368
364
|
|
369
365
|
return
|
370
366
|
|
@@ -372,42 +368,6 @@ class GuiMain(QMainWindow):
|
|
372
368
|
# Project Actions
|
373
369
|
##
|
374
370
|
|
375
|
-
def newProject(self, projData: dict | None = None) -> bool:
|
376
|
-
"""Create a new project via the new project wizard."""
|
377
|
-
if SHARED.hasProject:
|
378
|
-
if not self.closeProject():
|
379
|
-
SHARED.error(self.tr(
|
380
|
-
"Cannot create a new project when another project is open."
|
381
|
-
))
|
382
|
-
return False
|
383
|
-
|
384
|
-
if projData is None:
|
385
|
-
projData = self.showNewProjectDialog()
|
386
|
-
|
387
|
-
if projData is None:
|
388
|
-
return False
|
389
|
-
|
390
|
-
projPath = projData.get("projPath", None)
|
391
|
-
if projPath is None or projData is None:
|
392
|
-
logger.error("No projData or projPath set")
|
393
|
-
return False
|
394
|
-
|
395
|
-
if (Path(projPath) / nwFiles.PROJ_FILE).is_file():
|
396
|
-
SHARED.error(self.tr(
|
397
|
-
"A project already exists in that location. "
|
398
|
-
"Please choose another folder."
|
399
|
-
))
|
400
|
-
return False
|
401
|
-
|
402
|
-
logger.info("Creating new project")
|
403
|
-
nwProject = ProjectBuilder()
|
404
|
-
if nwProject.buildProject(projData):
|
405
|
-
self.openProject(projPath)
|
406
|
-
else:
|
407
|
-
return False
|
408
|
-
|
409
|
-
return True
|
410
|
-
|
411
371
|
def closeProject(self, isYes: bool = False) -> bool:
|
412
372
|
"""Close the project if one is open. isYes is passed on from the
|
413
373
|
close application event so the user doesn't get prompted twice
|
@@ -446,7 +406,7 @@ class GuiMain(QMainWindow):
|
|
446
406
|
self.docViewerPanel.closeProjectTasks()
|
447
407
|
self.outlineView.closeProjectTasks()
|
448
408
|
self.novelView.closeProjectTasks()
|
449
|
-
self.projView.
|
409
|
+
self.projView.closeProjectTasks()
|
450
410
|
self.itemDetails.clearDetails()
|
451
411
|
self.mainStatus.clearStatus()
|
452
412
|
|
@@ -462,8 +422,7 @@ class GuiMain(QMainWindow):
|
|
462
422
|
if projFile is None:
|
463
423
|
return False
|
464
424
|
|
465
|
-
# Make sure any open project is cleared out first
|
466
|
-
# another one
|
425
|
+
# Make sure any open project is cleared out first
|
467
426
|
if not self.closeProject():
|
468
427
|
return False
|
469
428
|
|
@@ -711,14 +670,9 @@ class GuiMain(QMainWindow):
|
|
711
670
|
return False
|
712
671
|
|
713
672
|
lastPath = CONFIG.lastPath()
|
714
|
-
|
715
|
-
self.tr("Text files ({0})").format("*.txt"),
|
716
|
-
self.tr("Markdown files ({0})").format("*.md"),
|
717
|
-
self.tr("novelWriter files ({0})").format("*.nwd"),
|
718
|
-
self.tr("All files ({0})").format("*"),
|
719
|
-
]
|
673
|
+
ffilter = formatFileFilter(["*.txt", "*.md", "*.nwd", "*"])
|
720
674
|
loadFile, _ = QFileDialog.getOpenFileName(
|
721
|
-
self, self.tr("Import File"), str(lastPath), filter=
|
675
|
+
self, self.tr("Import File"), str(lastPath), filter=ffilter
|
722
676
|
)
|
723
677
|
if not loadFile:
|
724
678
|
return False
|
@@ -726,10 +680,10 @@ class GuiMain(QMainWindow):
|
|
726
680
|
if loadFile.strip() == "":
|
727
681
|
return False
|
728
682
|
|
729
|
-
|
683
|
+
text = None
|
730
684
|
try:
|
731
685
|
with open(loadFile, mode="rt", encoding="utf-8") as inFile:
|
732
|
-
|
686
|
+
text = inFile.read()
|
733
687
|
CONFIG.setLastPath(loadFile)
|
734
688
|
except Exception as exc:
|
735
689
|
SHARED.error(self.tr(
|
@@ -751,7 +705,7 @@ class GuiMain(QMainWindow):
|
|
751
705
|
if not msgYes:
|
752
706
|
return False
|
753
707
|
|
754
|
-
self.docEditor.replaceText(
|
708
|
+
self.docEditor.replaceText(text)
|
755
709
|
|
756
710
|
return True
|
757
711
|
|
@@ -838,33 +792,14 @@ class GuiMain(QMainWindow):
|
|
838
792
|
# Main Dialogs
|
839
793
|
##
|
840
794
|
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
dlgProj = GuiProjectLoad(self)
|
848
|
-
dlgProj.exec_()
|
849
|
-
|
850
|
-
if dlgProj.result() == QDialog.Accepted:
|
851
|
-
if dlgProj.openState == GuiProjectLoad.OPEN_STATE:
|
852
|
-
self.openProject(dlgProj.openPath)
|
853
|
-
elif dlgProj.openState == GuiProjectLoad.NEW_STATE:
|
854
|
-
self.newProject()
|
855
|
-
|
795
|
+
@pyqtSlot()
|
796
|
+
def showWelcomeDialog(self) -> None:
|
797
|
+
"""Open the welcome dialog."""
|
798
|
+
dialog = GuiWelcome(self)
|
799
|
+
dialog.openProjectRequest.connect(self._openProjectFromWelcome)
|
800
|
+
dialog.exec_()
|
856
801
|
return
|
857
802
|
|
858
|
-
def showNewProjectDialog(self) -> dict | None:
|
859
|
-
"""Open the wizard and assemble a project options dict."""
|
860
|
-
newProj = GuiProjectWizard(self)
|
861
|
-
newProj.exec_()
|
862
|
-
|
863
|
-
if newProj.result() == QDialog.Accepted:
|
864
|
-
return self._assembleProjectWizardData(newProj)
|
865
|
-
|
866
|
-
return None
|
867
|
-
|
868
803
|
@pyqtSlot()
|
869
804
|
def showPreferencesDialog(self) -> None:
|
870
805
|
"""Open the preferences dialog."""
|
@@ -875,19 +810,19 @@ class GuiMain(QMainWindow):
|
|
875
810
|
|
876
811
|
@pyqtSlot()
|
877
812
|
@pyqtSlot(int)
|
878
|
-
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.
|
813
|
+
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.PAGE_SETTINGS) -> None:
|
879
814
|
"""Open the project settings dialog."""
|
880
815
|
if SHARED.hasProject:
|
881
|
-
dialog = GuiProjectSettings(self,
|
816
|
+
dialog = GuiProjectSettings(self, gotoPage=focusTab)
|
882
817
|
dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
|
883
818
|
dialog.exec_()
|
884
819
|
return
|
885
820
|
|
886
821
|
@pyqtSlot()
|
887
|
-
def
|
888
|
-
"""Open the
|
822
|
+
def showNovelDetailsDialog(self) -> None:
|
823
|
+
"""Open the novel details dialog."""
|
889
824
|
if SHARED.hasProject:
|
890
|
-
dialog =
|
825
|
+
dialog = GuiNovelDetails(self)
|
891
826
|
dialog.setModal(True)
|
892
827
|
dialog.show()
|
893
828
|
dialog.raise_()
|
@@ -931,7 +866,7 @@ class GuiMain(QMainWindow):
|
|
931
866
|
return
|
932
867
|
|
933
868
|
@pyqtSlot()
|
934
|
-
def showAboutNWDialog(self
|
869
|
+
def showAboutNWDialog(self) -> None:
|
935
870
|
"""Show the novelWriter about dialog."""
|
936
871
|
dialog = GuiAbout(self)
|
937
872
|
dialog.setModal(True)
|
@@ -939,8 +874,6 @@ class GuiMain(QMainWindow):
|
|
939
874
|
dialog.raise_()
|
940
875
|
qApp.processEvents()
|
941
876
|
dialog.populateGUI()
|
942
|
-
if showNotes:
|
943
|
-
dialog.showReleaseNotes()
|
944
877
|
return
|
945
878
|
|
946
879
|
@pyqtSlot()
|
@@ -950,17 +883,6 @@ class GuiMain(QMainWindow):
|
|
950
883
|
msgBox.aboutQt(self, "About Qt")
|
951
884
|
return
|
952
885
|
|
953
|
-
@pyqtSlot()
|
954
|
-
def showUpdatesDialog(self) -> None:
|
955
|
-
"""Show the check for updates dialog."""
|
956
|
-
dialog = GuiUpdates(self)
|
957
|
-
dialog.setModal(True)
|
958
|
-
dialog.show()
|
959
|
-
dialog.raise_()
|
960
|
-
qApp.processEvents()
|
961
|
-
dialog.checkLatest()
|
962
|
-
return
|
963
|
-
|
964
886
|
@pyqtSlot()
|
965
887
|
def showDictionariesDialog(self) -> None:
|
966
888
|
"""Show the download dictionaries dialog."""
|
@@ -1179,13 +1101,15 @@ class GuiMain(QMainWindow):
|
|
1179
1101
|
|
1180
1102
|
return
|
1181
1103
|
|
1182
|
-
@pyqtSlot()
|
1183
|
-
def _processProjectSettingsChanges(self) -> None:
|
1104
|
+
@pyqtSlot(bool)
|
1105
|
+
def _processProjectSettingsChanges(self, rebuildTrees: bool) -> None:
|
1184
1106
|
"""Refresh data dependent on project settings."""
|
1185
1107
|
logger.debug("Applying new project settings")
|
1186
1108
|
SHARED.updateSpellCheckLanguage()
|
1187
1109
|
self.itemDetails.refreshDetails()
|
1188
1110
|
self._updateWindowTitle(SHARED.project.data.name)
|
1111
|
+
if rebuildTrees:
|
1112
|
+
self.rebuildTrees()
|
1189
1113
|
return
|
1190
1114
|
|
1191
1115
|
@pyqtSlot()
|
@@ -1207,6 +1131,15 @@ class GuiMain(QMainWindow):
|
|
1207
1131
|
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
|
1208
1132
|
return
|
1209
1133
|
|
1134
|
+
@pyqtSlot(Path)
|
1135
|
+
def _openProjectFromWelcome(self, path: Path) -> None:
|
1136
|
+
"""Handle an open project request from the welcome dialog."""
|
1137
|
+
qApp.processEvents()
|
1138
|
+
self.openProject(path)
|
1139
|
+
if not SHARED.hasProject:
|
1140
|
+
self.showWelcomeDialog()
|
1141
|
+
return
|
1142
|
+
|
1210
1143
|
@pyqtSlot(str, nwDocMode, str, bool)
|
1211
1144
|
def _openDocument(self, tHandle: str, mode: nwDocMode, sTitle: str, setFocus: bool) -> None:
|
1212
1145
|
"""Handle an open document request."""
|
@@ -1461,44 +1394,6 @@ class GuiMain(QMainWindow):
|
|
1461
1394
|
self.setWindowTitle(winTitle)
|
1462
1395
|
return
|
1463
1396
|
|
1464
|
-
def _assembleProjectWizardData(self, newProj: GuiProjectWizard) -> dict:
|
1465
|
-
"""Extract the user choices from the New Project Wizard and
|
1466
|
-
store them in a dictionary.
|
1467
|
-
"""
|
1468
|
-
projData = {
|
1469
|
-
"projName": newProj.field("projName"),
|
1470
|
-
"projTitle": newProj.field("projTitle"),
|
1471
|
-
"projAuthor": newProj.field("projAuthor"),
|
1472
|
-
"projPath": newProj.field("projPath"),
|
1473
|
-
"popSample": newProj.field("popSample"),
|
1474
|
-
"popMinimal": newProj.field("popMinimal"),
|
1475
|
-
"popCustom": newProj.field("popCustom"),
|
1476
|
-
"addRoots": [],
|
1477
|
-
"addNotes": False,
|
1478
|
-
"numChapters": 0,
|
1479
|
-
"numScenes": 0,
|
1480
|
-
}
|
1481
|
-
if newProj.field("popCustom"):
|
1482
|
-
addRoots = []
|
1483
|
-
if newProj.field("addPlot"):
|
1484
|
-
addRoots.append(nwItemClass.PLOT)
|
1485
|
-
if newProj.field("addChar"):
|
1486
|
-
addRoots.append(nwItemClass.CHARACTER)
|
1487
|
-
if newProj.field("addWorld"):
|
1488
|
-
addRoots.append(nwItemClass.WORLD)
|
1489
|
-
projData["addRoots"] = addRoots
|
1490
|
-
projData["addNotes"] = newProj.field("addNotes")
|
1491
|
-
projData["numChapters"] = newProj.field("numChapters")
|
1492
|
-
projData["numScenes"] = newProj.field("numScenes")
|
1493
|
-
|
1494
|
-
try:
|
1495
|
-
langIdx = newProj.field("projLang")
|
1496
|
-
projData["projLang"] = CONFIG.listLanguages(CONFIG.LANG_PROJ)[langIdx][0]
|
1497
|
-
except Exception:
|
1498
|
-
projData["projLang"] = "en_GB"
|
1499
|
-
|
1500
|
-
return projData
|
1501
|
-
|
1502
1397
|
def _getTagSource(self, tag: str) -> tuple[str | None, str | None]:
|
1503
1398
|
"""Handle the index lookup of a tag and display an alert if the
|
1504
1399
|
tag cannot be found.
|
novelwriter/shared.py
CHANGED
@@ -31,8 +31,10 @@ from typing import TYPE_CHECKING, TypeVar
|
|
31
31
|
from pathlib import Path
|
32
32
|
|
33
33
|
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
|
34
|
-
from PyQt5.QtWidgets import QMessageBox, QWidget
|
34
|
+
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget
|
35
|
+
from novelwriter.common import formatFileFilter
|
35
36
|
|
37
|
+
from novelwriter.constants import nwFiles
|
36
38
|
from novelwriter.core.spellcheck import NWSpellEnchant
|
37
39
|
|
38
40
|
if TYPE_CHECKING: # pragma: no cover
|
@@ -217,6 +219,18 @@ class SharedData(QObject):
|
|
217
219
|
QThreadPool.globalInstance().start(runnable, priority=priority)
|
218
220
|
return
|
219
221
|
|
222
|
+
def getProjectPath(self, parent: QWidget, path: str | Path | None = None,
|
223
|
+
allowZip: bool = False) -> Path | None:
|
224
|
+
"""Open the file dialog and select a novelWriter project file."""
|
225
|
+
label = (self.tr("novelWriter Project File or Zip File")
|
226
|
+
if allowZip else self.tr("novelWriter Project File"))
|
227
|
+
ext = f"{nwFiles.PROJ_FILE} *.zip" if allowZip else nwFiles.PROJ_FILE
|
228
|
+
ffilter = formatFileFilter([(label, ext), "*"])
|
229
|
+
selected, _ = QFileDialog.getOpenFileName(
|
230
|
+
parent, self.tr("Open Project"), str(path or ""), filter=ffilter
|
231
|
+
)
|
232
|
+
return Path(selected) if selected else None
|
233
|
+
|
220
234
|
def findTopLevelWidget(self, kind: type[NWWidget]) -> NWWidget | None:
|
221
235
|
"""Find a top level widget."""
|
222
236
|
for widget in self.mainGui.children():
|
@@ -37,7 +37,7 @@ from PyQt5.QtWidgets import (
|
|
37
37
|
|
38
38
|
from novelwriter import CONFIG, SHARED
|
39
39
|
from novelwriter.error import formatException
|
40
|
-
from novelwriter.common import openExternalPath, formatInt, getFileSize
|
40
|
+
from novelwriter.common import formatFileFilter, openExternalPath, formatInt, getFileSize
|
41
41
|
|
42
42
|
logger = logging.getLogger(__name__)
|
43
43
|
|
@@ -180,12 +180,11 @@ class GuiDictionaries(QDialog):
|
|
180
180
|
@pyqtSlot()
|
181
181
|
def _doBrowseHunspell(self):
|
182
182
|
"""Browse for a Free/Libre Office dictionary."""
|
183
|
-
|
184
|
-
self.tr("Free or Libre Office extension
|
185
|
-
|
186
|
-
]
|
183
|
+
ffilter = formatFileFilter([
|
184
|
+
(self.tr("Free or Libre Office extension"), "*.sox *.oxt"), "*"
|
185
|
+
])
|
187
186
|
soxFile, _ = QFileDialog.getOpenFileName(
|
188
|
-
self, self.tr("Browse Files"), "", filter=
|
187
|
+
self, self.tr("Browse Files"), "", filter=ffilter
|
189
188
|
)
|
190
189
|
if soxFile:
|
191
190
|
path = Path(soxFile).absolute()
|
novelwriter/tools/manuscript.py
CHANGED
@@ -375,9 +375,9 @@ class GuiManuscript(QDialog):
|
|
375
375
|
@pyqtSlot()
|
376
376
|
def _printDocument(self) -> None:
|
377
377
|
"""Open the print preview dialog."""
|
378
|
-
|
379
|
-
|
380
|
-
|
378
|
+
preview = QPrintPreviewDialog(self)
|
379
|
+
preview.paintRequested.connect(self.docPreview.printPreview)
|
380
|
+
preview.exec_()
|
381
381
|
return
|
382
382
|
|
383
383
|
##
|
@@ -604,7 +604,7 @@ class _DetailsWidget(QWidget):
|
|
604
604
|
for key in entries:
|
605
605
|
sub = QTreeWidgetItem()
|
606
606
|
sub.setText(0, build.getLabel(key))
|
607
|
-
sub.setText(1, hFmt.apply(build.getStr(key), title))
|
607
|
+
sub.setText(1, hFmt.apply(build.getStr(key), title, 0))
|
608
608
|
item.addChild(sub)
|
609
609
|
for key in ["headings.hideScene", "headings.hideSection"]:
|
610
610
|
sub = QTreeWidgetItem()
|
@@ -771,8 +771,8 @@ class _PreviewWidget(QTextBrowser):
|
|
771
771
|
self.setHtml(html)
|
772
772
|
qApp.processEvents()
|
773
773
|
while self.find("!!tab!!"):
|
774
|
-
|
775
|
-
|
774
|
+
cursor = self.textCursor()
|
775
|
+
cursor.insertText("\t")
|
776
776
|
|
777
777
|
self.verticalScrollBar().setValue(sPos)
|
778
778
|
self._docTime = checkInt(data.get("time"), 0)
|