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
@@ -0,0 +1,525 @@
|
|
1
|
+
"""
|
2
|
+
novelWriter – GUI Novel Info
|
3
|
+
============================
|
4
|
+
|
5
|
+
File History:
|
6
|
+
Created: 2024-01-18 [2.3b1] GuiNovelDetails
|
7
|
+
|
8
|
+
This file is a part of novelWriter
|
9
|
+
Copyright 2018–2024, Veronica Berglyd Olsen
|
10
|
+
|
11
|
+
This program is free software: you can redistribute it and/or modify
|
12
|
+
it under the terms of the GNU General Public License as published by
|
13
|
+
the Free Software Foundation, either version 3 of the License, or
|
14
|
+
(at your option) any later version.
|
15
|
+
|
16
|
+
This program is distributed in the hope that it will be useful, but
|
17
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
19
|
+
General Public License for more details.
|
20
|
+
|
21
|
+
You should have received a copy of the GNU General Public License
|
22
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
23
|
+
"""
|
24
|
+
from __future__ import annotations
|
25
|
+
|
26
|
+
import math
|
27
|
+
import logging
|
28
|
+
|
29
|
+
from PyQt5.QtGui import QCloseEvent
|
30
|
+
from PyQt5.QtCore import QSize, Qt, pyqtSlot
|
31
|
+
from PyQt5.QtWidgets import (
|
32
|
+
QAbstractItemView, QDialog, QDialogButtonBox, QFormLayout, QGridLayout,
|
33
|
+
QHBoxLayout, QLabel, QSpinBox, QStackedWidget, QTreeWidget,
|
34
|
+
QTreeWidgetItem, QVBoxLayout, QWidget
|
35
|
+
)
|
36
|
+
|
37
|
+
from novelwriter import CONFIG, SHARED
|
38
|
+
from novelwriter.common import formatTime, numberToRoman
|
39
|
+
from novelwriter.constants import nwUnicode
|
40
|
+
from novelwriter.extensions.switch import NSwitch
|
41
|
+
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollablePage
|
42
|
+
from novelwriter.extensions.pagedsidebar import NPagedSideBar
|
43
|
+
from novelwriter.extensions.novelselector import NovelSelector
|
44
|
+
|
45
|
+
logger = logging.getLogger(__name__)
|
46
|
+
|
47
|
+
|
48
|
+
class GuiNovelDetails(QDialog):
|
49
|
+
|
50
|
+
PAGE_OVERVIEW = 1
|
51
|
+
PAGE_CONTENTS = 2
|
52
|
+
|
53
|
+
def __init__(self, parent: QWidget) -> None:
|
54
|
+
super().__init__(parent=parent)
|
55
|
+
|
56
|
+
logger.debug("Create: GuiNovelDetails")
|
57
|
+
self.setObjectName("GuiNovelDetails")
|
58
|
+
self.setWindowTitle(self.tr("Novel Details"))
|
59
|
+
|
60
|
+
options = SHARED.project.options
|
61
|
+
self.setMinimumSize(CONFIG.pxInt(500), CONFIG.pxInt(400))
|
62
|
+
self.resize(
|
63
|
+
CONFIG.pxInt(options.getInt("GuiNovelDetails", "winWidth", CONFIG.pxInt(650))),
|
64
|
+
CONFIG.pxInt(options.getInt("GuiNovelDetails", "winHeight", CONFIG.pxInt(500)))
|
65
|
+
)
|
66
|
+
|
67
|
+
# Title
|
68
|
+
self.titleLabel = NColourLabel(
|
69
|
+
self.tr("Novel Details"), SHARED.theme.helpText,
|
70
|
+
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
71
|
+
)
|
72
|
+
|
73
|
+
# Novel Selector
|
74
|
+
self.novelSelector = NovelSelector(self)
|
75
|
+
self.novelSelector.refreshNovelList()
|
76
|
+
self.novelSelector.setHandle(
|
77
|
+
options.getString("GuiNovelDetails", "novelRoot", self.novelSelector.firstHandle or "")
|
78
|
+
)
|
79
|
+
|
80
|
+
# SideBar
|
81
|
+
self.sidebar = NPagedSideBar(self)
|
82
|
+
self.sidebar.setLabelColor(SHARED.theme.helpText)
|
83
|
+
self.sidebar.addButton(self.tr("Overview"), self.PAGE_OVERVIEW)
|
84
|
+
self.sidebar.addButton(self.tr("Contents"), self.PAGE_CONTENTS)
|
85
|
+
self.sidebar.setSelected(self.PAGE_OVERVIEW)
|
86
|
+
self.sidebar.buttonClicked.connect(self._sidebarClicked)
|
87
|
+
|
88
|
+
# Content
|
89
|
+
self.overviewPage = _OverviewPage(self)
|
90
|
+
self.contentsPage = _ContentsPage(self)
|
91
|
+
|
92
|
+
self.mainStack = QStackedWidget(self)
|
93
|
+
self.mainStack.addWidget(self.overviewPage)
|
94
|
+
self.mainStack.addWidget(self.contentsPage)
|
95
|
+
|
96
|
+
# Buttons
|
97
|
+
self.buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
|
98
|
+
self.buttonBox.rejected.connect(self.close)
|
99
|
+
|
100
|
+
# Assemble
|
101
|
+
self.topBox = QHBoxLayout()
|
102
|
+
self.topBox.addWidget(self.titleLabel)
|
103
|
+
self.topBox.addStretch(1)
|
104
|
+
self.topBox.addWidget(self.novelSelector, 1)
|
105
|
+
|
106
|
+
self.mainBox = QHBoxLayout()
|
107
|
+
self.mainBox.addWidget(self.sidebar)
|
108
|
+
self.mainBox.addWidget(self.mainStack)
|
109
|
+
self.mainBox.setContentsMargins(0, 0, 0, 0)
|
110
|
+
|
111
|
+
self.outerBox = QVBoxLayout()
|
112
|
+
self.outerBox.addLayout(self.topBox)
|
113
|
+
self.outerBox.addLayout(self.mainBox)
|
114
|
+
self.outerBox.addWidget(self.buttonBox)
|
115
|
+
self.outerBox.setSpacing(CONFIG.pxInt(8))
|
116
|
+
|
117
|
+
self.setLayout(self.outerBox)
|
118
|
+
self.setSizeGripEnabled(True)
|
119
|
+
|
120
|
+
# Connect Signals
|
121
|
+
self.novelSelector.novelSelectionChanged.connect(self.overviewPage.novelValueChanged)
|
122
|
+
self.novelSelector.novelSelectionChanged.connect(self.contentsPage.novelValueChanged)
|
123
|
+
|
124
|
+
logger.debug("Ready: GuiNovelDetails")
|
125
|
+
|
126
|
+
return
|
127
|
+
|
128
|
+
def __del__(self) -> None: # pragma: no cover
|
129
|
+
logger.debug("Delete: GuiNovelDetails")
|
130
|
+
return
|
131
|
+
|
132
|
+
##
|
133
|
+
# Methods
|
134
|
+
##
|
135
|
+
|
136
|
+
def updateValues(self) -> None:
|
137
|
+
"""Load the dialogs initial values."""
|
138
|
+
self.overviewPage.updateProjectData()
|
139
|
+
self.overviewPage.novelValueChanged(self.novelSelector.handle)
|
140
|
+
self.contentsPage.novelValueChanged(self.novelSelector.handle)
|
141
|
+
return
|
142
|
+
|
143
|
+
##
|
144
|
+
# Events
|
145
|
+
##
|
146
|
+
|
147
|
+
def closeEvent(self, event: QCloseEvent) -> None:
|
148
|
+
"""Capture the user closing the window and save settings."""
|
149
|
+
self._saveSettings()
|
150
|
+
event.accept()
|
151
|
+
self.deleteLater()
|
152
|
+
return
|
153
|
+
|
154
|
+
##
|
155
|
+
# Private Slots
|
156
|
+
##
|
157
|
+
|
158
|
+
@pyqtSlot(int)
|
159
|
+
def _sidebarClicked(self, pageId: int) -> None:
|
160
|
+
"""Process a user request to switch page."""
|
161
|
+
if pageId == self.PAGE_OVERVIEW:
|
162
|
+
self.mainStack.setCurrentWidget(self.overviewPage)
|
163
|
+
elif pageId == self.PAGE_CONTENTS:
|
164
|
+
self.mainStack.setCurrentWidget(self.contentsPage)
|
165
|
+
return
|
166
|
+
|
167
|
+
##
|
168
|
+
# Internal Functions
|
169
|
+
##
|
170
|
+
|
171
|
+
def _saveSettings(self) -> None:
|
172
|
+
"""Save the user GUI settings."""
|
173
|
+
winWidth = CONFIG.rpxInt(self.width())
|
174
|
+
winHeight = CONFIG.rpxInt(self.height())
|
175
|
+
novelRoot = self.novelSelector.handle
|
176
|
+
|
177
|
+
logger.debug("Saving State: GuiNovelDetails")
|
178
|
+
options = SHARED.project.options
|
179
|
+
options.setValue("GuiNovelDetails", "winWidth", winWidth)
|
180
|
+
options.setValue("GuiNovelDetails", "winHeight", winHeight)
|
181
|
+
options.setValue("GuiNovelDetails", "novelRoot", novelRoot)
|
182
|
+
|
183
|
+
self.contentsPage.saveSettings()
|
184
|
+
|
185
|
+
return
|
186
|
+
|
187
|
+
# END Class GuiNovelDetails
|
188
|
+
|
189
|
+
|
190
|
+
class _OverviewPage(NScrollablePage):
|
191
|
+
|
192
|
+
def __init__(self, parent: QWidget) -> None:
|
193
|
+
super().__init__(parent=parent)
|
194
|
+
|
195
|
+
mPx = CONFIG.pxInt(8)
|
196
|
+
sPx = CONFIG.pxInt(16)
|
197
|
+
hPx = CONFIG.pxInt(24)
|
198
|
+
vPx = CONFIG.pxInt(4)
|
199
|
+
|
200
|
+
# Project Info
|
201
|
+
self.projLabel = NColourLabel(
|
202
|
+
self.tr("Project"), SHARED.theme.helpText,
|
203
|
+
parent=self, scale=NColourLabel.HEADER_SCALE
|
204
|
+
)
|
205
|
+
|
206
|
+
self.projName = QLabel("", self)
|
207
|
+
self.projWords = QLabel("", self)
|
208
|
+
self.projNovels = QLabel("", self)
|
209
|
+
self.projNotes = QLabel("", self)
|
210
|
+
self.projRevisions = QLabel("", self)
|
211
|
+
self.projEditTime = QLabel("", self)
|
212
|
+
|
213
|
+
self.projForm = QFormLayout()
|
214
|
+
self.projForm.addRow("<b>{0}</b>".format(self.tr("Name")), self.projName)
|
215
|
+
self.projForm.addRow("<b>{0}</b>".format(self.tr("Revisions")), self.projRevisions)
|
216
|
+
self.projForm.addRow("<b>{0}</b>".format(self.tr("Editing Time")), self.projEditTime)
|
217
|
+
self.projForm.addRow("<b>{0}</b>".format(self.tr("Word Count")), self.projWords)
|
218
|
+
self.projForm.addRow("<b>\u2026 {0}</b>".format(self.tr("In Novels")), self.projNovels)
|
219
|
+
self.projForm.addRow("<b>\u2026 {0}</b>".format(self.tr("In Notes ")), self.projNotes)
|
220
|
+
self.projForm.setContentsMargins(mPx, 0, 0, 0)
|
221
|
+
self.projForm.setHorizontalSpacing(hPx)
|
222
|
+
self.projForm.setVerticalSpacing(vPx)
|
223
|
+
|
224
|
+
# Novel Info
|
225
|
+
self.novelLabel = NColourLabel(
|
226
|
+
self.tr("Selected Novel"), SHARED.theme.helpText,
|
227
|
+
parent=self, scale=NColourLabel.HEADER_SCALE
|
228
|
+
)
|
229
|
+
|
230
|
+
self.novelName = QLabel("", self)
|
231
|
+
self.novelWords = QLabel("", self)
|
232
|
+
self.novelChapters = QLabel("", self)
|
233
|
+
self.novelScenes = QLabel("", self)
|
234
|
+
|
235
|
+
self.novelForm = QFormLayout()
|
236
|
+
self.novelForm.addRow("<b>{0}</b>".format(self.tr("Name")), self.novelName)
|
237
|
+
self.novelForm.addRow("<b>{0}</b>".format(self.tr("Word Count")), self.novelWords)
|
238
|
+
self.novelForm.addRow("<b>{0}</b>".format(self.tr("Chapters")), self.novelChapters)
|
239
|
+
self.novelForm.addRow("<b>{0}</b>".format(self.tr("Scenes")), self.novelScenes)
|
240
|
+
self.novelForm.setContentsMargins(mPx, 0, 0, 0)
|
241
|
+
self.novelForm.setHorizontalSpacing(hPx)
|
242
|
+
self.novelForm.setVerticalSpacing(vPx)
|
243
|
+
|
244
|
+
# Assemble
|
245
|
+
self.outerBox = QVBoxLayout()
|
246
|
+
self.outerBox.addWidget(self.projLabel)
|
247
|
+
self.outerBox.addLayout(self.projForm)
|
248
|
+
self.outerBox.addWidget(self.novelLabel)
|
249
|
+
self.outerBox.addLayout(self.novelForm)
|
250
|
+
self.outerBox.setSpacing(sPx)
|
251
|
+
self.outerBox.addStretch(1)
|
252
|
+
|
253
|
+
self.setCentralLayout(self.outerBox)
|
254
|
+
|
255
|
+
return
|
256
|
+
|
257
|
+
##
|
258
|
+
# Methods
|
259
|
+
##
|
260
|
+
|
261
|
+
def updateProjectData(self) -> None:
|
262
|
+
"""Load information about the project."""
|
263
|
+
project = SHARED.project
|
264
|
+
project.updateWordCounts()
|
265
|
+
wcNovel, wcNotes = project.data.currCounts
|
266
|
+
|
267
|
+
self.projName.setText(project.data.name)
|
268
|
+
self.projRevisions.setText(f"{project.data.saveCount:n}")
|
269
|
+
self.projEditTime.setText(formatTime(project.currentEditTime))
|
270
|
+
self.projWords.setText(f"{wcNovel + wcNotes:n}")
|
271
|
+
self.projNovels.setText(f"{wcNovel:n}")
|
272
|
+
self.projNotes.setText(f"{wcNotes:n}")
|
273
|
+
return
|
274
|
+
|
275
|
+
##
|
276
|
+
# Public Slots
|
277
|
+
##
|
278
|
+
|
279
|
+
@pyqtSlot(str)
|
280
|
+
def novelValueChanged(self, tHandle: str) -> None:
|
281
|
+
"""Refresh the data for the selected novel."""
|
282
|
+
project = SHARED.project
|
283
|
+
if nwItem := project.tree[tHandle]:
|
284
|
+
self.novelName.setText(nwItem.itemName)
|
285
|
+
|
286
|
+
nwCount = project.index.getNovelWordCount(rootHandle=tHandle)
|
287
|
+
self.novelWords.setText(f"{nwCount:n}")
|
288
|
+
|
289
|
+
hCounts = project.index.getNovelTitleCounts(rootHandle=tHandle)
|
290
|
+
self.novelChapters.setText(f"{hCounts[2]:n}")
|
291
|
+
self.novelScenes.setText(f"{hCounts[3]:n}")
|
292
|
+
|
293
|
+
return
|
294
|
+
|
295
|
+
# END Class _OverviewPage
|
296
|
+
|
297
|
+
|
298
|
+
class _ContentsPage(NFixedPage):
|
299
|
+
|
300
|
+
C_TITLE = 0
|
301
|
+
C_WORDS = 1
|
302
|
+
C_PAGES = 2
|
303
|
+
C_PAGE = 3
|
304
|
+
C_PROG = 4
|
305
|
+
|
306
|
+
def __init__(self, parent: QWidget) -> None:
|
307
|
+
super().__init__(parent=parent)
|
308
|
+
|
309
|
+
self._data = []
|
310
|
+
self._currentRoot = None
|
311
|
+
|
312
|
+
iPx = SHARED.theme.baseIconSize
|
313
|
+
hPx = CONFIG.pxInt(12)
|
314
|
+
vPx = CONFIG.pxInt(4)
|
315
|
+
options = SHARED.project.options
|
316
|
+
|
317
|
+
# Title
|
318
|
+
self.contentLabel = NColourLabel(
|
319
|
+
self.tr("Table of Contents"), SHARED.theme.helpText,
|
320
|
+
parent=self, scale=NColourLabel.HEADER_SCALE
|
321
|
+
)
|
322
|
+
|
323
|
+
# Contents Tree
|
324
|
+
self.tocTree = QTreeWidget(self)
|
325
|
+
self.tocTree.setIconSize(QSize(iPx, iPx))
|
326
|
+
self.tocTree.setIndentation(0)
|
327
|
+
self.tocTree.setColumnCount(6)
|
328
|
+
self.tocTree.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
|
329
|
+
self.tocTree.setHeaderLabels([
|
330
|
+
self.tr("Title"),
|
331
|
+
self.tr("Words"),
|
332
|
+
self.tr("Pages"),
|
333
|
+
self.tr("Page"),
|
334
|
+
self.tr("Progress"),
|
335
|
+
"",
|
336
|
+
])
|
337
|
+
|
338
|
+
treeHeadItem = self.tocTree.headerItem()
|
339
|
+
if treeHeadItem:
|
340
|
+
treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignmentFlag.AlignRight)
|
341
|
+
treeHeadItem.setTextAlignment(self.C_PAGES, Qt.AlignmentFlag.AlignRight)
|
342
|
+
treeHeadItem.setTextAlignment(self.C_PAGE, Qt.AlignmentFlag.AlignRight)
|
343
|
+
treeHeadItem.setTextAlignment(self.C_PROG, Qt.AlignmentFlag.AlignRight)
|
344
|
+
|
345
|
+
treeHeader = self.tocTree.header()
|
346
|
+
treeHeader.setStretchLastSection(True)
|
347
|
+
treeHeader.setMinimumSectionSize(hPx)
|
348
|
+
|
349
|
+
wCol0 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol0", 200))
|
350
|
+
wCol1 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol1", 60))
|
351
|
+
wCol2 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol2", 60))
|
352
|
+
wCol3 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol3", 60))
|
353
|
+
wCol4 = CONFIG.pxInt(options.getInt("GuiNovelDetails", "widthCol4", 90))
|
354
|
+
|
355
|
+
self.tocTree.setColumnWidth(0, wCol0)
|
356
|
+
self.tocTree.setColumnWidth(1, wCol1)
|
357
|
+
self.tocTree.setColumnWidth(2, wCol2)
|
358
|
+
self.tocTree.setColumnWidth(3, wCol3)
|
359
|
+
self.tocTree.setColumnWidth(4, wCol4)
|
360
|
+
self.tocTree.setColumnWidth(5, hPx)
|
361
|
+
|
362
|
+
# Options
|
363
|
+
wordsPerPage = options.getInt("GuiNovelDetails", "wordsPerPage", 350)
|
364
|
+
countFrom = options.getInt("GuiNovelDetails", "countFrom", 1)
|
365
|
+
clearDouble = options.getBool("GuiNovelDetails", "clearDouble", True)
|
366
|
+
|
367
|
+
self.wpLabel = QLabel(self.tr("Words per page"))
|
368
|
+
|
369
|
+
self.wpValue = QSpinBox(self)
|
370
|
+
self.wpValue.setMinimum(10)
|
371
|
+
self.wpValue.setMaximum(1000)
|
372
|
+
self.wpValue.setSingleStep(10)
|
373
|
+
self.wpValue.setValue(wordsPerPage)
|
374
|
+
self.wpValue.valueChanged.connect(self._populateTree)
|
375
|
+
|
376
|
+
self.poLabel = QLabel(self.tr("First page offset"))
|
377
|
+
|
378
|
+
self.poValue = QSpinBox(self)
|
379
|
+
self.poValue.setMinimum(1)
|
380
|
+
self.poValue.setMaximum(9999)
|
381
|
+
self.poValue.setSingleStep(1)
|
382
|
+
self.poValue.setValue(countFrom)
|
383
|
+
self.poValue.valueChanged.connect(self._populateTree)
|
384
|
+
|
385
|
+
self.dblLabel = QLabel(self.tr("Chapters on odd pages"))
|
386
|
+
|
387
|
+
self.dblValue = NSwitch(self, height=iPx)
|
388
|
+
self.dblValue.setChecked(clearDouble)
|
389
|
+
self.dblValue.clicked.connect(self._populateTree)
|
390
|
+
|
391
|
+
self.optionsBox = QGridLayout()
|
392
|
+
self.optionsBox.addWidget(self.wpLabel, 0, 0)
|
393
|
+
self.optionsBox.addWidget(self.wpValue, 0, 1)
|
394
|
+
self.optionsBox.addWidget(self.dblLabel, 0, 3)
|
395
|
+
self.optionsBox.addWidget(self.dblValue, 0, 4)
|
396
|
+
self.optionsBox.addWidget(self.poLabel, 1, 0)
|
397
|
+
self.optionsBox.addWidget(self.poValue, 1, 1)
|
398
|
+
self.optionsBox.setHorizontalSpacing(hPx)
|
399
|
+
self.optionsBox.setVerticalSpacing(vPx)
|
400
|
+
self.optionsBox.setColumnStretch(2, 1)
|
401
|
+
|
402
|
+
# Assemble
|
403
|
+
self.outerBox = QVBoxLayout()
|
404
|
+
self.outerBox.addWidget(self.contentLabel)
|
405
|
+
self.outerBox.addWidget(self.tocTree)
|
406
|
+
self.outerBox.addLayout(self.optionsBox)
|
407
|
+
|
408
|
+
self.setCentralLayout(self.outerBox)
|
409
|
+
|
410
|
+
return
|
411
|
+
|
412
|
+
def saveSettings(self) -> None:
|
413
|
+
"""Save the user GUI settings."""
|
414
|
+
widthCol0 = CONFIG.rpxInt(self.tocTree.columnWidth(0))
|
415
|
+
widthCol1 = CONFIG.rpxInt(self.tocTree.columnWidth(1))
|
416
|
+
widthCol2 = CONFIG.rpxInt(self.tocTree.columnWidth(2))
|
417
|
+
widthCol3 = CONFIG.rpxInt(self.tocTree.columnWidth(3))
|
418
|
+
widthCol4 = CONFIG.rpxInt(self.tocTree.columnWidth(4))
|
419
|
+
|
420
|
+
options = SHARED.project.options
|
421
|
+
options.setValue("GuiNovelDetails", "widthCol0", widthCol0)
|
422
|
+
options.setValue("GuiNovelDetails", "widthCol1", widthCol1)
|
423
|
+
options.setValue("GuiNovelDetails", "widthCol2", widthCol2)
|
424
|
+
options.setValue("GuiNovelDetails", "widthCol3", widthCol3)
|
425
|
+
options.setValue("GuiNovelDetails", "widthCol4", widthCol4)
|
426
|
+
options.setValue("GuiNovelDetails", "wordsPerPage", self.wpValue.value())
|
427
|
+
options.setValue("GuiNovelDetails", "countFrom", self.poValue.value())
|
428
|
+
options.setValue("GuiNovelDetails", "clearDouble", self.dblValue.isChecked())
|
429
|
+
return
|
430
|
+
|
431
|
+
##
|
432
|
+
# Public Slots
|
433
|
+
##
|
434
|
+
|
435
|
+
@pyqtSlot(str)
|
436
|
+
def novelValueChanged(self, tHandle: str) -> None:
|
437
|
+
"""Refresh the tree with another root item."""
|
438
|
+
if tHandle != self._currentRoot:
|
439
|
+
self._prepareData(tHandle)
|
440
|
+
self._populateTree()
|
441
|
+
self._currentRoot = tHandle
|
442
|
+
return
|
443
|
+
|
444
|
+
##
|
445
|
+
# Private Slots
|
446
|
+
##
|
447
|
+
|
448
|
+
@pyqtSlot()
|
449
|
+
def _populateTree(self) -> None:
|
450
|
+
"""Set the content of the chapter/page tree."""
|
451
|
+
dblPages = self.dblValue.isChecked()
|
452
|
+
wpPage = self.wpValue.value()
|
453
|
+
fstPage = self.poValue.value() - 1
|
454
|
+
|
455
|
+
pTotal = 0
|
456
|
+
tPages = 1
|
457
|
+
|
458
|
+
entries = []
|
459
|
+
for _, tLevel, tTitle, wCount in self._data:
|
460
|
+
pCount = math.ceil(wCount/wpPage)
|
461
|
+
if dblPages:
|
462
|
+
pCount += pCount%2
|
463
|
+
|
464
|
+
pTotal += pCount
|
465
|
+
entries.append((tLevel, tTitle, wCount, pCount))
|
466
|
+
|
467
|
+
pMax = pTotal - fstPage
|
468
|
+
|
469
|
+
self.tocTree.clear()
|
470
|
+
for tLevel, tTitle, wCount, pCount in entries:
|
471
|
+
newItem = QTreeWidgetItem()
|
472
|
+
|
473
|
+
if tPages <= fstPage:
|
474
|
+
progPage = numberToRoman(tPages, True)
|
475
|
+
progText = ""
|
476
|
+
else:
|
477
|
+
cPage = tPages - fstPage
|
478
|
+
pgProg = 100.0*(cPage - 1)/pMax if pMax > 0 else 0.0
|
479
|
+
progPage = f"{cPage:n}"
|
480
|
+
progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"
|
481
|
+
|
482
|
+
hDec = SHARED.theme.getHeaderDecoration(tLevel)
|
483
|
+
if tTitle.strip() == "":
|
484
|
+
tTitle = self.tr("Untitled")
|
485
|
+
|
486
|
+
newItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
|
487
|
+
newItem.setText(self.C_TITLE, tTitle)
|
488
|
+
newItem.setText(self.C_WORDS, f"{wCount:n}")
|
489
|
+
newItem.setText(self.C_PAGES, f"{pCount:n}")
|
490
|
+
newItem.setText(self.C_PAGE, progPage)
|
491
|
+
newItem.setText(self.C_PROG, progText)
|
492
|
+
|
493
|
+
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
|
494
|
+
newItem.setTextAlignment(self.C_PAGES, Qt.AlignRight)
|
495
|
+
newItem.setTextAlignment(self.C_PAGE, Qt.AlignRight)
|
496
|
+
newItem.setTextAlignment(self.C_PROG, Qt.AlignRight)
|
497
|
+
|
498
|
+
# Make pages and titles/partitions stand out
|
499
|
+
if tLevel < 2:
|
500
|
+
bFont = newItem.font(self.C_TITLE)
|
501
|
+
if tLevel == 0:
|
502
|
+
bFont.setItalic(True)
|
503
|
+
else:
|
504
|
+
bFont.setBold(True)
|
505
|
+
bFont.setUnderline(True)
|
506
|
+
newItem.setFont(self.C_TITLE, bFont)
|
507
|
+
|
508
|
+
tPages += pCount
|
509
|
+
|
510
|
+
self.tocTree.addTopLevelItem(newItem)
|
511
|
+
|
512
|
+
return
|
513
|
+
|
514
|
+
##
|
515
|
+
# Internal Functions
|
516
|
+
##
|
517
|
+
|
518
|
+
def _prepareData(self, rootHandle: str | None) -> None:
|
519
|
+
"""Extract the information from the project index."""
|
520
|
+
logger.debug("Populating ToC from handle '%s'", rootHandle)
|
521
|
+
self._data = SHARED.project.index.getTableOfContents(rootHandle, 2)
|
522
|
+
self._data.append(("", 0, self.tr("END"), 0))
|
523
|
+
return
|
524
|
+
|
525
|
+
# END Class _ContentsPage
|