novelWriter 2.2.1__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.
Files changed (110) hide show
  1. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +102 -92
  3. novelwriter/__init__.py +4 -4
  4. novelwriter/assets/icons/typicons_dark/icons.conf +6 -0
  5. novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
  6. novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
  7. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
  8. novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
  9. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
  10. novelwriter/assets/icons/typicons_light/icons.conf +6 -0
  11. novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
  12. novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
  13. novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
  14. novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
  15. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
  16. novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
  17. novelwriter/assets/images/novelwriter-text-light.svg +4 -0
  18. novelwriter/assets/images/welcome-dark.jpg +0 -0
  19. novelwriter/assets/images/welcome-light.jpg +0 -0
  20. novelwriter/assets/manual.pdf +0 -0
  21. novelwriter/assets/sample.zip +0 -0
  22. novelwriter/assets/syntax/default_dark.conf +1 -0
  23. novelwriter/assets/syntax/default_light.conf +1 -0
  24. novelwriter/assets/syntax/grey_dark.conf +1 -0
  25. novelwriter/assets/syntax/grey_light.conf +1 -0
  26. novelwriter/assets/syntax/light_owl.conf +1 -0
  27. novelwriter/assets/syntax/night_owl.conf +1 -0
  28. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  29. novelwriter/assets/syntax/solarized_light.conf +1 -0
  30. novelwriter/assets/syntax/tomorrow.conf +1 -0
  31. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  32. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  33. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  34. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  35. novelwriter/assets/text/credits_en.htm +4 -2
  36. novelwriter/assets/themes/default_dark.conf +2 -2
  37. novelwriter/assets/themes/default_light.conf +2 -2
  38. novelwriter/common.py +48 -37
  39. novelwriter/config.py +36 -41
  40. novelwriter/constants.py +38 -16
  41. novelwriter/core/buildsettings.py +7 -7
  42. novelwriter/core/coretools.py +192 -154
  43. novelwriter/core/docbuild.py +6 -3
  44. novelwriter/core/document.py +6 -6
  45. novelwriter/core/index.py +89 -56
  46. novelwriter/core/item.py +21 -3
  47. novelwriter/core/options.py +8 -7
  48. novelwriter/core/project.py +69 -44
  49. novelwriter/core/projectdata.py +1 -14
  50. novelwriter/core/projectxml.py +13 -41
  51. novelwriter/core/sessions.py +2 -1
  52. novelwriter/core/spellcheck.py +2 -1
  53. novelwriter/core/status.py +2 -1
  54. novelwriter/core/storage.py +178 -140
  55. novelwriter/core/tohtml.py +4 -2
  56. novelwriter/core/tokenizer.py +73 -45
  57. novelwriter/core/toodt.py +40 -30
  58. novelwriter/core/tree.py +3 -2
  59. novelwriter/dialogs/about.py +70 -160
  60. novelwriter/dialogs/docmerge.py +6 -5
  61. novelwriter/dialogs/docsplit.py +6 -6
  62. novelwriter/dialogs/editlabel.py +1 -1
  63. novelwriter/dialogs/preferences.py +553 -703
  64. novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
  65. novelwriter/dialogs/quotes.py +27 -23
  66. novelwriter/dialogs/wordlist.py +96 -40
  67. novelwriter/enum.py +20 -18
  68. novelwriter/error.py +1 -1
  69. novelwriter/extensions/circularprogress.py +11 -11
  70. novelwriter/extensions/configlayout.py +185 -134
  71. novelwriter/extensions/modified.py +81 -0
  72. novelwriter/extensions/novelselector.py +26 -12
  73. novelwriter/extensions/pagedsidebar.py +14 -16
  74. novelwriter/extensions/simpleprogress.py +5 -5
  75. novelwriter/extensions/statusled.py +8 -8
  76. novelwriter/extensions/switch.py +31 -63
  77. novelwriter/extensions/switchbox.py +1 -1
  78. novelwriter/extensions/versioninfo.py +153 -0
  79. novelwriter/gui/doceditor.py +178 -150
  80. novelwriter/gui/dochighlight.py +63 -92
  81. novelwriter/gui/docviewer.py +49 -51
  82. novelwriter/gui/docviewerpanel.py +72 -24
  83. novelwriter/gui/itemdetails.py +7 -7
  84. novelwriter/gui/mainmenu.py +14 -18
  85. novelwriter/gui/noveltree.py +9 -8
  86. novelwriter/gui/outline.py +98 -75
  87. novelwriter/gui/projtree.py +188 -61
  88. novelwriter/gui/sidebar.py +3 -4
  89. novelwriter/gui/statusbar.py +3 -4
  90. novelwriter/gui/theme.py +60 -68
  91. novelwriter/guimain.py +49 -156
  92. novelwriter/shared.py +15 -1
  93. novelwriter/tools/dictionaries.py +5 -6
  94. novelwriter/tools/manuscript.py +6 -6
  95. novelwriter/tools/manussettings.py +192 -221
  96. novelwriter/tools/noveldetails.py +525 -0
  97. novelwriter/tools/welcome.py +802 -0
  98. novelwriter/tools/writingstats.py +9 -9
  99. novelwriter/assets/images/wizard-back.jpg +0 -0
  100. novelwriter/assets/text/gplv3_en.htm +0 -641
  101. novelwriter/assets/text/release_notes.htm +0 -60
  102. novelwriter/dialogs/projdetails.py +0 -518
  103. novelwriter/dialogs/projload.py +0 -294
  104. novelwriter/dialogs/updates.py +0 -172
  105. novelwriter/extensions/pageddialog.py +0 -130
  106. novelwriter/tools/projwizard.py +0 -478
  107. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
  108. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +0 -0
  109. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
  110. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
@@ -1,60 +0,0 @@
1
- <!DOCTYPE html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2
- <html>
3
- <body>
4
-
5
- <h2>Release Notes for 2.2</h2>
6
- <p><i>Released on 17 December 2023</i></p>
7
-
8
- <p>Scroll down for <a href="#patch">Patch Notes</a></p>
9
-
10
- <p>This release comes with a number of new features. These are some highlights.</p>
11
- <p>In addition to the common Markdown style formatting for bold, italic and strike through, a set
12
- of new shortcodes have been added. The shortcodes are far more flexible than the Markdown style
13
- syntax, and can be used for more complex formatting cases. Like when you need to add multiple,
14
- overlapping formats, or add emphasis to just a part of a word. The shortcodes also allow for
15
- underline, subscript and superscript, which the Markdown syntax does not. The new formats are
16
- available in the "Format" menu, and in a new toolbar in the editor that can be enabled by clicking
17
- the three dots in the top–left corner. The shortcode format was chosen because it can later be
18
- extended to include other requested features as well. Please have a look at the documentation for
19
- more details about the new shortcodes.</p>
20
- <p>The Tags and References system has been improved. The tags themselves are no longer case
21
- sensitive when you use them in references, but they are still displayed as you typed them in the
22
- tag definition when they are displayed in the user interface. Starting to type the '@' symbol in
23
- the text editor, on a new line, will now open an auto-completer menu which will display available
24
- options. It may not display all of your tags if you have a lot of them, but starting to type more
25
- characters will filter the list down further.</p>
26
- <p>You can now automatically create a note file for a new tag that you have added to a reference
27
- list in a document, but is not yet defined in a project note. So, for instance, if you come up with
28
- a new character while writing, and add a new tag to your '@char' references, you can right-click
29
- the new tag and create a new note for that entry directly. In addition, it is now also possible to
30
- right-click a heading in an open document and set the item label in the project tree to match the
31
- heading.</p>
32
- <p>In addition to the changes in the editor, the "References" panel below the document viewer has
33
- also been completely redesigned. It now shows all the references to the document you are viewing as
34
- a list, with a lot more details than before. In addition, tabs in the panel will appear to show all
35
- the tags you have defined in your notes, sorted as one tab per category. Like for instance
36
- Characters, Locations, Objects, etc. You can also give each note a short description comment on the
37
- same format as the summary comments for chapters and scenes. The short description comment can be
38
- added from the "Insert" menu under "Special Comments".</p>
39
- <p>The last major change in this release is the new multi-select feature in the project tree. You
40
- can now select multiple documents and folders using the mouse while pressing 'Ctrl' or 'Shift'. By
41
- right-clicking the selected items, you can perform a limited set of operations on all of them, like
42
- changing active status, and the status or importance labels. You can also drag and drop multiple
43
- items under the condition that all the selected items are in the same folder, at the same level.
44
- This restriction is in place due to limitations in the framework novelWriter is based on. But this
45
- should help in cases where multiple documents need to be moved in and out of folders or between
46
- folders. Note that adding the multi-select feature meant that the undo feature of the project tree
47
- had to be removed. It may be added back later.</p>
48
-
49
- <p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
50
-
51
- <a name="patch"></a><h2>Patch Notes</h2>
52
-
53
- <h3>Patch 2.2.1 &ndash; 27 January 2024</h3>
54
-
55
- <p>This is a patch release that fixes an issue where the Project View would sometimes switch to the
56
- Novel View when a new item was created. This patch also includes updated translations for German
57
- and Chinese.</p>
58
-
59
- </body>
60
- </html>
@@ -1,518 +0,0 @@
1
- """
2
- novelWriter – GUI Project Details
3
- =================================
4
-
5
- File History:
6
- Created: 2021-01-03 [1.1rc1] GuiProjectDetails
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, QFont
30
- from PyQt5.QtCore import Qt, QSize, pyqtSlot
31
- from PyQt5.QtWidgets import (
32
- QAbstractItemView, QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel,
33
- QLineEdit, QSpinBox, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
34
- )
35
-
36
- from novelwriter import CONFIG, SHARED
37
- from novelwriter.common import formatTime, numberToRoman
38
- from novelwriter.constants import nwUnicode
39
- from novelwriter.extensions.switch import NSwitch
40
- from novelwriter.extensions.pageddialog import NPagedDialog
41
- from novelwriter.extensions.novelselector import NovelSelector
42
-
43
- logger = logging.getLogger(__name__)
44
-
45
-
46
- class GuiProjectDetails(NPagedDialog):
47
-
48
- def __init__(self, parent: QWidget) -> None:
49
- super().__init__(parent=parent)
50
-
51
- logger.debug("Create: GuiProjectDetails")
52
- self.setObjectName("GuiProjectDetails")
53
-
54
- self.setWindowTitle(self.tr("Project Details"))
55
-
56
- wW = CONFIG.pxInt(600)
57
- wH = CONFIG.pxInt(400)
58
- pOptions = SHARED.project.options
59
-
60
- self.setMinimumWidth(wW)
61
- self.setMinimumHeight(wH)
62
- self.resize(
63
- CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "winWidth", wW)),
64
- CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "winHeight", wH))
65
- )
66
-
67
- self.tabMain = GuiProjectDetailsMain(self)
68
- self.tabContents = GuiProjectDetailsContents(self)
69
-
70
- self.addTab(self.tabMain, self.tr("Overview"))
71
- self.addTab(self.tabContents, self.tr("Contents"))
72
-
73
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
74
- self.buttonBox.rejected.connect(self.close)
75
- self.rejected.connect(self.close)
76
- self.addControls(self.buttonBox)
77
-
78
- logger.debug("Ready: GuiProjectDetails")
79
-
80
- return
81
-
82
- def __del__(self) -> None: # pragma: no cover
83
- logger.debug("Delete: GuiProjectDetails")
84
- return
85
-
86
- def updateValues(self) -> None:
87
- """Set all the values of the pages."""
88
- self.tabMain.updateValues()
89
- self.tabContents.updateValues()
90
- return
91
-
92
- ##
93
- # Events
94
- ##
95
-
96
- def closeEvent(self, event: QCloseEvent) -> None:
97
- """Capture the close event and perform cleanup."""
98
- self._saveGuiSettings()
99
- event.accept()
100
- self.deleteLater()
101
- return
102
-
103
- ##
104
- # Internal Functions
105
- ##
106
-
107
- def _saveGuiSettings(self) -> None:
108
- """Save GUI settings."""
109
- winWidth = CONFIG.rpxInt(self.width())
110
- winHeight = CONFIG.rpxInt(self.height())
111
-
112
- cColWidth = self.tabContents.getColumnSizes()
113
- widthCol0 = CONFIG.rpxInt(cColWidth[0])
114
- widthCol1 = CONFIG.rpxInt(cColWidth[1])
115
- widthCol2 = CONFIG.rpxInt(cColWidth[2])
116
- widthCol3 = CONFIG.rpxInt(cColWidth[3])
117
- widthCol4 = CONFIG.rpxInt(cColWidth[4])
118
-
119
- wordsPerPage = self.tabContents.wpValue.value()
120
- countFrom = self.tabContents.poValue.value()
121
- clearDouble = self.tabContents.dblValue.isChecked()
122
-
123
- logger.debug("Saving State: GuiProjectDetails")
124
- pOptions = SHARED.project.options
125
- pOptions.setValue("GuiProjectDetails", "winWidth", winWidth)
126
- pOptions.setValue("GuiProjectDetails", "winHeight", winHeight)
127
- pOptions.setValue("GuiProjectDetails", "widthCol0", widthCol0)
128
- pOptions.setValue("GuiProjectDetails", "widthCol1", widthCol1)
129
- pOptions.setValue("GuiProjectDetails", "widthCol2", widthCol2)
130
- pOptions.setValue("GuiProjectDetails", "widthCol3", widthCol3)
131
- pOptions.setValue("GuiProjectDetails", "widthCol4", widthCol4)
132
- pOptions.setValue("GuiProjectDetails", "wordsPerPage", wordsPerPage)
133
- pOptions.setValue("GuiProjectDetails", "countFrom", countFrom)
134
- pOptions.setValue("GuiProjectDetails", "clearDouble", clearDouble)
135
-
136
- return
137
-
138
- # END Class GuiProjectDetails
139
-
140
-
141
- class GuiProjectDetailsMain(QWidget):
142
-
143
- def __init__(self, parent: QWidget) -> None:
144
- super().__init__(parent=parent)
145
-
146
- fPx = SHARED.theme.fontPixelSize
147
- fPt = SHARED.theme.fontPointSize
148
- vPx = CONFIG.pxInt(4)
149
- hPx = CONFIG.pxInt(12)
150
-
151
- # Header
152
- # ======
153
-
154
- self.bookTitle = QLabel("")
155
- bookFont = self.bookTitle.font()
156
- bookFont.setPointSizeF(2.2*fPt)
157
- bookFont.setWeight(QFont.Bold)
158
- self.bookTitle.setFont(bookFont)
159
- self.bookTitle.setAlignment(Qt.AlignHCenter)
160
- self.bookTitle.setWordWrap(True)
161
-
162
- self.projName = QLabel("")
163
- workFont = self.projName.font()
164
- workFont.setPointSizeF(0.8*fPt)
165
- workFont.setItalic(True)
166
- self.projName.setFont(workFont)
167
- self.projName.setAlignment(Qt.AlignHCenter)
168
- self.projName.setWordWrap(True)
169
-
170
- self.bookAuthors = QLabel("")
171
- authFont = self.bookAuthors.font()
172
- authFont.setPointSizeF(1.2*fPt)
173
- self.bookAuthors.setFont(authFont)
174
- self.bookAuthors.setAlignment(Qt.AlignHCenter)
175
- self.bookAuthors.setWordWrap(True)
176
-
177
- # Stats
178
- # =====
179
-
180
- self.wordCountLbl = QLabel("<b>%s:</b>" % self.tr("Words"))
181
- self.wordCountVal = QLabel("")
182
-
183
- self.chapCountLbl = QLabel("<b>%s:</b>" % self.tr("Chapters"))
184
- self.chapCountVal = QLabel("")
185
-
186
- self.sceneCountLbl = QLabel("<b>%s:</b>" % self.tr("Scenes"))
187
- self.sceneCountVal = QLabel("")
188
-
189
- self.revCountLbl = QLabel("<b>%s:</b>" % self.tr("Revisions"))
190
- self.revCountVal = QLabel("")
191
-
192
- self.editTimeLbl = QLabel("<b>%s:</b>" % self.tr("Editing Time"))
193
- self.editTimeVal = QLabel("")
194
-
195
- self.statsGrid = QGridLayout()
196
- self.statsGrid.addWidget(self.wordCountLbl, 0, 0, 1, 1, Qt.AlignRight)
197
- self.statsGrid.addWidget(self.wordCountVal, 0, 1, 1, 1, Qt.AlignLeft)
198
- self.statsGrid.addWidget(self.chapCountLbl, 1, 0, 1, 1, Qt.AlignRight)
199
- self.statsGrid.addWidget(self.chapCountVal, 1, 1, 1, 1, Qt.AlignLeft)
200
- self.statsGrid.addWidget(self.sceneCountLbl, 2, 0, 1, 1, Qt.AlignRight)
201
- self.statsGrid.addWidget(self.sceneCountVal, 2, 1, 1, 1, Qt.AlignLeft)
202
- self.statsGrid.addWidget(self.revCountLbl, 3, 0, 1, 1, Qt.AlignRight)
203
- self.statsGrid.addWidget(self.revCountVal, 3, 1, 1, 1, Qt.AlignLeft)
204
- self.statsGrid.addWidget(self.editTimeLbl, 4, 0, 1, 1, Qt.AlignRight)
205
- self.statsGrid.addWidget(self.editTimeVal, 4, 1, 1, 1, Qt.AlignLeft)
206
- self.statsGrid.setHorizontalSpacing(hPx)
207
- self.statsGrid.setVerticalSpacing(vPx)
208
-
209
- # Meta
210
- # ====
211
-
212
- self.projPathLbl = QLabel("<b>%s:</b>" % self.tr("Path"))
213
- self.projPathVal = QLineEdit()
214
- self.projPathVal.setReadOnly(True)
215
-
216
- self.projPathBox = QHBoxLayout()
217
- self.projPathBox.addWidget(self.projPathLbl)
218
- self.projPathBox.addWidget(self.projPathVal)
219
- self.projPathBox.setSpacing(hPx)
220
-
221
- # Assemble
222
- # ========
223
-
224
- self.outerBox = QVBoxLayout()
225
- self.outerBox.addSpacing(fPx)
226
- self.outerBox.addWidget(self.bookTitle)
227
- self.outerBox.addWidget(self.projName)
228
- self.outerBox.addWidget(self.bookAuthors)
229
- self.outerBox.addSpacing(2*fPx)
230
- self.outerBox.addLayout(self.statsGrid)
231
- self.outerBox.addSpacing(fPx)
232
- self.outerBox.addStretch(1)
233
- self.outerBox.addLayout(self.projPathBox)
234
-
235
- self.setLayout(self.outerBox)
236
-
237
- return
238
-
239
- def updateValues(self) -> None:
240
- """Set all the values."""
241
- project = SHARED.project
242
- pIndex = project.index
243
- hCounts = pIndex.getNovelTitleCounts()
244
- nwCount = pIndex.getNovelWordCount()
245
- edTime = project.currentEditTime
246
-
247
- self.bookTitle.setText(project.data.title or project.data.name)
248
- self.projName.setText(self.tr("Project: {0}").format(project.data.name))
249
- self.bookAuthors.setText(self.tr("By {0}").format(project.data.author))
250
-
251
- self.wordCountVal.setText(f"{nwCount:n}")
252
- self.chapCountVal.setText(f"{hCounts[2]:n}")
253
- self.sceneCountVal.setText(f"{hCounts[3]:n}")
254
- self.revCountVal.setText(f"{project.data.saveCount:n}")
255
- self.editTimeVal.setText(formatTime(edTime))
256
-
257
- self.projPathVal.setText(str(project.storage.storagePath))
258
-
259
- return
260
-
261
- # END Class GuiProjectDetailsMain
262
-
263
-
264
- class GuiProjectDetailsContents(QWidget):
265
-
266
- C_TITLE = 0
267
- C_WORDS = 1
268
- C_PAGES = 2
269
- C_PAGE = 3
270
- C_PROG = 4
271
-
272
- def __init__(self, parent: QWidget) -> None:
273
- super().__init__(parent=parent)
274
-
275
- # Internal
276
- self._theToC = []
277
- self._currentRoot = None
278
-
279
- iPx = SHARED.theme.baseIconSize
280
- hPx = CONFIG.pxInt(12)
281
- vPx = CONFIG.pxInt(4)
282
- pOptions = SHARED.project.options
283
-
284
- # Header
285
- # ======
286
-
287
- self.tocLabel = QLabel("<b>%s</b>" % self.tr("Table of Contents"))
288
-
289
- self.novelValue = NovelSelector(self)
290
- self.novelValue.setMinimumWidth(CONFIG.pxInt(200))
291
- self.novelValue.novelSelectionChanged.connect(self._novelValueChanged)
292
-
293
- self.headBox = QHBoxLayout()
294
- self.headBox.addWidget(self.tocLabel)
295
- self.headBox.addWidget(self.novelValue)
296
-
297
- # Contents Tree
298
- # =============
299
-
300
- self.tocTree = QTreeWidget()
301
- self.tocTree.setIconSize(QSize(iPx, iPx))
302
- self.tocTree.setIndentation(0)
303
- self.tocTree.setColumnCount(6)
304
- self.tocTree.setSelectionMode(QAbstractItemView.NoSelection)
305
- self.tocTree.setHeaderLabels([
306
- self.tr("Title"),
307
- self.tr("Words"),
308
- self.tr("Pages"),
309
- self.tr("Page"),
310
- self.tr("Progress"),
311
- ""
312
- ])
313
-
314
- treeHeadItem = self.tocTree.headerItem()
315
- if treeHeadItem:
316
- treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
317
- treeHeadItem.setTextAlignment(self.C_PAGES, Qt.AlignRight)
318
- treeHeadItem.setTextAlignment(self.C_PAGE, Qt.AlignRight)
319
- treeHeadItem.setTextAlignment(self.C_PROG, Qt.AlignRight)
320
-
321
- treeHeader = self.tocTree.header()
322
- treeHeader.setStretchLastSection(True)
323
- treeHeader.setMinimumSectionSize(hPx)
324
-
325
- wCol0 = CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "widthCol0", 200))
326
- wCol1 = CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "widthCol1", 60))
327
- wCol2 = CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "widthCol2", 60))
328
- wCol3 = CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "widthCol3", 60))
329
- wCol4 = CONFIG.pxInt(pOptions.getInt("GuiProjectDetails", "widthCol4", 90))
330
-
331
- self.tocTree.setColumnWidth(0, wCol0)
332
- self.tocTree.setColumnWidth(1, wCol1)
333
- self.tocTree.setColumnWidth(2, wCol2)
334
- self.tocTree.setColumnWidth(3, wCol3)
335
- self.tocTree.setColumnWidth(4, wCol4)
336
- self.tocTree.setColumnWidth(5, hPx)
337
-
338
- # Options
339
- # =======
340
-
341
- wordsPerPage = pOptions.getInt("GuiProjectDetails", "wordsPerPage", 350)
342
- countFrom = pOptions.getInt("GuiProjectDetails", "countFrom", 1)
343
- clearDouble = pOptions.getBool("GuiProjectDetails", "clearDouble", True)
344
-
345
- wordsHelp = (
346
- self.tr("Typical word count for a 5 by 8 inch book page with 11 pt font is 350.")
347
- )
348
- offsetHelp = (
349
- self.tr("Start counting page numbers from this page.")
350
- )
351
- dblHelp = (
352
- self.tr("Assume a new chapter or partition always start on an odd numbered page.")
353
- )
354
-
355
- self.wpLabel = QLabel(self.tr("Words per page"))
356
- self.wpLabel.setToolTip(wordsHelp)
357
-
358
- self.wpValue = QSpinBox()
359
- self.wpValue.setMinimum(10)
360
- self.wpValue.setMaximum(1000)
361
- self.wpValue.setSingleStep(10)
362
- self.wpValue.setValue(wordsPerPage)
363
- self.wpValue.setToolTip(wordsHelp)
364
- self.wpValue.valueChanged.connect(self._populateTree)
365
-
366
- self.poLabel = QLabel(self.tr("Count pages from"))
367
- self.poLabel.setToolTip(offsetHelp)
368
-
369
- self.poValue = QSpinBox()
370
- self.poValue.setMinimum(1)
371
- self.poValue.setMaximum(9999)
372
- self.poValue.setSingleStep(1)
373
- self.poValue.setValue(countFrom)
374
- self.poValue.setToolTip(offsetHelp)
375
- self.poValue.valueChanged.connect(self._populateTree)
376
-
377
- self.dblLabel = QLabel(self.tr("Clear double pages"))
378
- self.dblLabel.setToolTip(dblHelp)
379
-
380
- self.dblValue = NSwitch(self, 2*iPx, iPx)
381
- self.dblValue.setChecked(clearDouble)
382
- self.dblValue.setToolTip(dblHelp)
383
- self.dblValue.clicked.connect(self._populateTree)
384
-
385
- self.optionsBox = QGridLayout()
386
- self.optionsBox.addWidget(self.wpLabel, 0, 0)
387
- self.optionsBox.addWidget(self.wpValue, 0, 1)
388
- self.optionsBox.addWidget(self.dblLabel, 0, 3)
389
- self.optionsBox.addWidget(self.dblValue, 0, 4)
390
- self.optionsBox.addWidget(self.poLabel, 1, 0)
391
- self.optionsBox.addWidget(self.poValue, 1, 1)
392
- self.optionsBox.setHorizontalSpacing(hPx)
393
- self.optionsBox.setVerticalSpacing(vPx)
394
- self.optionsBox.setColumnStretch(2, 1)
395
-
396
- # Assemble
397
- # ========
398
-
399
- self.outerBox = QVBoxLayout()
400
- self.outerBox.addLayout(self.headBox)
401
- self.outerBox.addWidget(self.tocTree)
402
- self.outerBox.addLayout(self.optionsBox)
403
-
404
- self.setLayout(self.outerBox)
405
-
406
- return
407
-
408
- def getColumnSizes(self) -> list[int]:
409
- """Return the column widths for the tree columns."""
410
- retVals = [
411
- self.tocTree.columnWidth(0),
412
- self.tocTree.columnWidth(1),
413
- self.tocTree.columnWidth(2),
414
- self.tocTree.columnWidth(3),
415
- self.tocTree.columnWidth(4),
416
- ]
417
- return retVals
418
-
419
- def updateValues(self) -> None:
420
- """Populate the tree."""
421
- self._currentRoot = None
422
- self.novelValue.updateList()
423
- self.novelValue.setHandle(self.novelValue.firstHandle)
424
- self._prepareData(self.novelValue.firstHandle)
425
- self._populateTree()
426
- return
427
-
428
- ##
429
- # Internal Functions
430
- ##
431
-
432
- def _prepareData(self, rootHandle: str | None) -> None:
433
- """Extract the information from the project index."""
434
- logger.debug("Populating ToC from handle '%s'", rootHandle)
435
- self._theToC = SHARED.project.index.getTableOfContents(rootHandle, 2)
436
- self._theToC.append(("", 0, self.tr("END"), 0))
437
- return
438
-
439
- ##
440
- # Slots
441
- ##
442
-
443
- @pyqtSlot(str)
444
- def _novelValueChanged(self, tHandle: str) -> None:
445
- """Refresh the tree with another root item."""
446
- if tHandle != self._currentRoot:
447
- self._prepareData(tHandle)
448
- self._populateTree()
449
- self._currentRoot = self.novelValue.handle
450
- return
451
-
452
- @pyqtSlot()
453
- def _populateTree(self) -> None:
454
- """Set the content of the chapter/page tree."""
455
- dblPages = self.dblValue.isChecked()
456
- wpPage = self.wpValue.value()
457
- fstPage = self.poValue.value() - 1
458
-
459
- pTotal = 0
460
- tPages = 1
461
-
462
- theList = []
463
- for _, tLevel, tTitle, wCount in self._theToC:
464
- pCount = math.ceil(wCount/wpPage)
465
- if dblPages:
466
- pCount += pCount%2
467
-
468
- pTotal += pCount
469
- theList.append((tLevel, tTitle, wCount, pCount))
470
-
471
- pMax = pTotal - fstPage
472
-
473
- self.tocTree.clear()
474
- for tLevel, tTitle, wCount, pCount in theList:
475
- newItem = QTreeWidgetItem()
476
-
477
- if tPages <= fstPage:
478
- progPage = numberToRoman(tPages, True)
479
- progText = ""
480
- else:
481
- cPage = tPages - fstPage
482
- pgProg = 100.0*(cPage - 1)/pMax if pMax > 0 else 0.0
483
- progPage = f"{cPage:n}"
484
- progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"
485
-
486
- hDec = SHARED.theme.getHeaderDecoration(tLevel)
487
- if tTitle.strip() == "":
488
- tTitle = self.tr("Untitled")
489
-
490
- newItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
491
- newItem.setText(self.C_TITLE, tTitle)
492
- newItem.setText(self.C_WORDS, f"{wCount:n}")
493
- newItem.setText(self.C_PAGES, f"{pCount:n}")
494
- newItem.setText(self.C_PAGE, progPage)
495
- newItem.setText(self.C_PROG, progText)
496
-
497
- newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
498
- newItem.setTextAlignment(self.C_PAGES, Qt.AlignRight)
499
- newItem.setTextAlignment(self.C_PAGE, Qt.AlignRight)
500
- newItem.setTextAlignment(self.C_PROG, Qt.AlignRight)
501
-
502
- # Make pages and titles/partitions stand out
503
- if tLevel < 2:
504
- bFont = newItem.font(self.C_TITLE)
505
- if tLevel == 0:
506
- bFont.setItalic(True)
507
- else:
508
- bFont.setBold(True)
509
- bFont.setUnderline(True)
510
- newItem.setFont(self.C_TITLE, bFont)
511
-
512
- tPages += pCount
513
-
514
- self.tocTree.addTopLevelItem(newItem)
515
-
516
- return
517
-
518
- # END Class GuiProjectDetailsContents