novelWriter 2.5.3__py3-none-any.whl → 2.6b2__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.5.3.dist-info → novelWriter-2.6b2.dist-info}/METADATA +1 -1
- {novelWriter-2.5.3.dist-info → novelWriter-2.6b2.dist-info}/RECORD +80 -60
- novelwriter/__init__.py +49 -10
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +8 -0
- novelwriter/assets/icons/typicons_dark/mixed_copy.svg +4 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +5 -0
- novelwriter/assets/icons/typicons_light/icons.conf +8 -0
- novelwriter/assets/icons/typicons_light/mixed_copy.svg +4 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_light/nw_toolbar.svg +5 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/common.py +100 -2
- novelwriter/config.py +25 -15
- novelwriter/constants.py +168 -60
- novelwriter/core/buildsettings.py +66 -39
- novelwriter/core/coretools.py +145 -147
- novelwriter/core/docbuild.py +132 -170
- novelwriter/core/index.py +38 -37
- novelwriter/core/item.py +41 -8
- novelwriter/core/itemmodel.py +518 -0
- novelwriter/core/options.py +4 -1
- novelwriter/core/project.py +67 -89
- novelwriter/core/spellcheck.py +9 -14
- novelwriter/core/status.py +7 -5
- novelwriter/core/tree.py +268 -287
- novelwriter/dialogs/docmerge.py +7 -17
- novelwriter/dialogs/preferences.py +46 -33
- novelwriter/dialogs/projectsettings.py +5 -5
- novelwriter/enum.py +36 -23
- novelwriter/extensions/configlayout.py +27 -12
- novelwriter/extensions/modified.py +13 -1
- novelwriter/extensions/pagedsidebar.py +5 -5
- novelwriter/formats/shared.py +155 -0
- novelwriter/formats/todocx.py +1191 -0
- novelwriter/formats/tohtml.py +451 -0
- novelwriter/{core → formats}/tokenizer.py +487 -491
- novelwriter/formats/tomarkdown.py +217 -0
- novelwriter/{core → formats}/toodt.py +311 -432
- novelwriter/formats/toqdoc.py +484 -0
- novelwriter/formats/toraw.py +91 -0
- novelwriter/gui/doceditor.py +342 -284
- novelwriter/gui/dochighlight.py +96 -84
- novelwriter/gui/docviewer.py +88 -31
- novelwriter/gui/docviewerpanel.py +17 -25
- novelwriter/gui/editordocument.py +17 -2
- novelwriter/gui/itemdetails.py +25 -28
- novelwriter/gui/mainmenu.py +129 -63
- novelwriter/gui/noveltree.py +45 -47
- novelwriter/gui/outline.py +196 -249
- novelwriter/gui/projtree.py +594 -1241
- novelwriter/gui/search.py +9 -10
- novelwriter/gui/sidebar.py +7 -6
- novelwriter/gui/theme.py +10 -5
- novelwriter/guimain.py +100 -196
- novelwriter/shared.py +66 -27
- novelwriter/text/counting.py +2 -0
- novelwriter/text/patterns.py +168 -60
- novelwriter/tools/manusbuild.py +14 -12
- novelwriter/tools/manuscript.py +120 -78
- novelwriter/tools/manussettings.py +424 -291
- novelwriter/tools/welcome.py +4 -4
- novelwriter/tools/writingstats.py +3 -3
- novelwriter/types.py +23 -7
- novelwriter/core/tohtml.py +0 -530
- novelwriter/core/tomarkdown.py +0 -252
- novelwriter/core/toqdoc.py +0 -419
- {novelWriter-2.5.3.dist-info → novelWriter-2.6b2.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5.3.dist-info → novelWriter-2.6b2.dist-info}/WHEEL +0 -0
- {novelWriter-2.5.3.dist-info → novelWriter-2.6b2.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5.3.dist-info → novelWriter-2.6b2.dist-info}/top_level.txt +0 -0
@@ -29,18 +29,18 @@ from enum import Enum
|
|
29
29
|
|
30
30
|
from PyQt5.QtCore import QModelIndex, Qt, pyqtSignal, pyqtSlot
|
31
31
|
from PyQt5.QtWidgets import (
|
32
|
-
QAbstractItemView, QFrame,
|
33
|
-
|
32
|
+
QAbstractItemView, QFrame, QMenu, QTabWidget, QToolButton, QTreeWidget,
|
33
|
+
QTreeWidgetItem, QVBoxLayout, QWidget
|
34
34
|
)
|
35
35
|
|
36
36
|
from novelwriter import CONFIG, SHARED
|
37
37
|
from novelwriter.common import checkInt
|
38
|
-
from novelwriter.constants import
|
38
|
+
from novelwriter.constants import nwLabels, nwLists, nwStyles, trConst
|
39
39
|
from novelwriter.core.index import IndexHeading, IndexItem
|
40
|
-
from novelwriter.enum import nwDocMode, nwItemClass
|
40
|
+
from novelwriter.enum import nwChange, nwDocMode, nwItemClass
|
41
41
|
from novelwriter.extensions.modified import NIconToolButton
|
42
42
|
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
|
43
|
-
from novelwriter.types import QtDecoration, QtUserRole
|
43
|
+
from novelwriter.types import QtDecoration, QtHeaderFixed, QtHeaderToContents, QtUserRole
|
44
44
|
|
45
45
|
logger = logging.getLogger(__name__)
|
46
46
|
|
@@ -151,8 +151,8 @@ class GuiDocViewerPanel(QWidget):
|
|
151
151
|
self.updateHandle(self._lastHandle)
|
152
152
|
return
|
153
153
|
|
154
|
-
@pyqtSlot(str)
|
155
|
-
def
|
154
|
+
@pyqtSlot(str, Enum)
|
155
|
+
def onProjectItemChanged(self, tHandle: str, change: nwChange) -> None:
|
156
156
|
"""Update meta data for project item."""
|
157
157
|
self.tabBackRefs.refreshDocument(tHandle)
|
158
158
|
activeOnly = self.aInactive.isChecked()
|
@@ -259,10 +259,10 @@ class _ViewPanelBackRefs(QTreeWidget):
|
|
259
259
|
treeHeader = self.header()
|
260
260
|
treeHeader.setStretchLastSection(True)
|
261
261
|
treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1627
|
262
|
-
treeHeader.setSectionResizeMode(self.C_DOC,
|
263
|
-
treeHeader.setSectionResizeMode(self.C_EDIT,
|
264
|
-
treeHeader.setSectionResizeMode(self.C_VIEW,
|
265
|
-
treeHeader.setSectionResizeMode(self.C_TITLE,
|
262
|
+
treeHeader.setSectionResizeMode(self.C_DOC, QtHeaderToContents)
|
263
|
+
treeHeader.setSectionResizeMode(self.C_EDIT, QtHeaderFixed)
|
264
|
+
treeHeader.setSectionResizeMode(self.C_VIEW, QtHeaderFixed)
|
265
|
+
treeHeader.setSectionResizeMode(self.C_TITLE, QtHeaderToContents)
|
266
266
|
treeHeader.resizeSection(self.C_EDIT, iPx + cMg)
|
267
267
|
treeHeader.resizeSection(self.C_VIEW, iPx + cMg)
|
268
268
|
treeHeader.setSectionsMovable(False)
|
@@ -339,17 +339,13 @@ class _ViewPanelBackRefs(QTreeWidget):
|
|
339
339
|
def _setTreeItemValues(self, tHandle: str, sTitle: str, hItem: IndexHeading) -> None:
|
340
340
|
"""Add or update a tree item."""
|
341
341
|
if nwItem := SHARED.project.tree[tHandle]:
|
342
|
-
|
343
|
-
nwItem.itemType, nwItem.itemClass,
|
344
|
-
nwItem.itemLayout, nwItem.mainHeading
|
345
|
-
)
|
346
|
-
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
|
342
|
+
iLevel = nwStyles.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
|
347
343
|
hDec = SHARED.theme.getHeaderDecorationNarrow(iLevel)
|
348
344
|
|
349
345
|
tKey = f"{tHandle}:{sTitle}"
|
350
346
|
trItem = self._treeMap[tKey] if tKey in self._treeMap else QTreeWidgetItem()
|
351
347
|
|
352
|
-
trItem.setIcon(self.C_DOC,
|
348
|
+
trItem.setIcon(self.C_DOC, nwItem.getMainIcon())
|
353
349
|
trItem.setText(self.C_DOC, nwItem.itemName)
|
354
350
|
trItem.setToolTip(self.C_DOC, nwItem.itemName)
|
355
351
|
trItem.setIcon(self.C_EDIT, self._editIcon)
|
@@ -407,8 +403,8 @@ class _ViewPanelKeyWords(QTreeWidget):
|
|
407
403
|
treeHeader = self.header()
|
408
404
|
treeHeader.setStretchLastSection(True)
|
409
405
|
treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1627
|
410
|
-
treeHeader.setSectionResizeMode(self.C_EDIT,
|
411
|
-
treeHeader.setSectionResizeMode(self.C_VIEW,
|
406
|
+
treeHeader.setSectionResizeMode(self.C_EDIT, QtHeaderFixed)
|
407
|
+
treeHeader.setSectionResizeMode(self.C_VIEW, QtHeaderFixed)
|
412
408
|
treeHeader.resizeSection(self.C_EDIT, iPx + cMg)
|
413
409
|
treeHeader.resizeSection(self.C_VIEW, iPx + cMg)
|
414
410
|
treeHeader.setSectionsMovable(False)
|
@@ -448,12 +444,8 @@ class _ViewPanelKeyWords(QTreeWidget):
|
|
448
444
|
def addUpdateEntry(self, tag: str, name: str, iItem: IndexItem, hItem: IndexHeading) -> None:
|
449
445
|
"""Add a new entry, or update an existing one."""
|
450
446
|
nwItem = iItem.item
|
451
|
-
docIcon = SHARED.theme.getItemIcon(
|
452
|
-
nwItem.itemType, nwItem.itemClass,
|
453
|
-
nwItem.itemLayout, nwItem.mainHeading
|
454
|
-
)
|
455
447
|
impLabel, impIcon = nwItem.getImportStatus()
|
456
|
-
iLevel =
|
448
|
+
iLevel = nwStyles.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
|
457
449
|
hDec = SHARED.theme.getHeaderDecorationNarrow(iLevel)
|
458
450
|
|
459
451
|
# This can not use a get call to the dictionary as that would create an
|
@@ -468,7 +460,7 @@ class _ViewPanelKeyWords(QTreeWidget):
|
|
468
460
|
trItem.setIcon(self.C_IMPORT, impIcon)
|
469
461
|
trItem.setText(self.C_IMPORT, impLabel)
|
470
462
|
trItem.setToolTip(self.C_IMPORT, impLabel)
|
471
|
-
trItem.setIcon(self.C_DOC,
|
463
|
+
trItem.setIcon(self.C_DOC, nwItem.getMainIcon())
|
472
464
|
trItem.setText(self.C_DOC, nwItem.itemName)
|
473
465
|
trItem.setToolTip(self.C_DOC, nwItem.itemName)
|
474
466
|
trItem.setData(self.C_TITLE, QtDecoration, hDec)
|
@@ -95,6 +95,21 @@ class GuiTextDocument(QTextDocument):
|
|
95
95
|
|
96
96
|
return
|
97
97
|
|
98
|
+
def metaDataAtPos(self, pos: int) -> tuple[str, str]:
|
99
|
+
"""Check if there is meta data available at a given position in
|
100
|
+
the document, and if so, return it.
|
101
|
+
"""
|
102
|
+
cursor = QTextCursor(self)
|
103
|
+
cursor.setPosition(pos)
|
104
|
+
block = cursor.block()
|
105
|
+
data = block.userData()
|
106
|
+
if block.isValid() and isinstance(data, TextBlockData):
|
107
|
+
if (check := pos - block.position()) >= 0:
|
108
|
+
for cPos, cEnd, cData, cType in data.metaData:
|
109
|
+
if cPos <= check <= cEnd:
|
110
|
+
return cData, cType
|
111
|
+
return "", ""
|
112
|
+
|
98
113
|
def spellErrorAtPos(self, pos: int) -> tuple[str, int, int, list[str]]:
|
99
114
|
"""Check if there is a misspelled word at a given position in
|
100
115
|
the document, and if so, return it.
|
@@ -107,8 +122,8 @@ class GuiTextDocument(QTextDocument):
|
|
107
122
|
text = block.text()
|
108
123
|
check = pos - block.position()
|
109
124
|
if check >= 0:
|
110
|
-
for cPos,
|
111
|
-
|
125
|
+
for cPos, cEnd in data.spellErrors:
|
126
|
+
cLen = cEnd - cPos
|
112
127
|
if cPos <= check <= cEnd:
|
113
128
|
word = text[cPos:cEnd]
|
114
129
|
return word, cPos, cLen, SHARED.spelling.suggestWords(word)
|
novelwriter/gui/itemdetails.py
CHANGED
@@ -25,12 +25,15 @@ from __future__ import annotations
|
|
25
25
|
|
26
26
|
import logging
|
27
27
|
|
28
|
+
from enum import Enum
|
29
|
+
|
28
30
|
from PyQt5.QtCore import pyqtSlot
|
29
31
|
from PyQt5.QtWidgets import QGridLayout, QLabel, QWidget
|
30
32
|
|
31
33
|
from novelwriter import CONFIG, SHARED
|
32
34
|
from novelwriter.common import elide
|
33
|
-
from novelwriter.constants import nwLabels, trConst
|
35
|
+
from novelwriter.constants import nwLabels, nwStats, trConst
|
36
|
+
from novelwriter.enum import nwChange
|
34
37
|
from novelwriter.types import (
|
35
38
|
QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase,
|
36
39
|
QtAlignRightMiddle
|
@@ -62,6 +65,10 @@ class GuiItemDetails(QWidget):
|
|
62
65
|
fntValue = self.font()
|
63
66
|
fntValue.setPointSizeF(0.9*fPt)
|
64
67
|
|
68
|
+
trStats1 = trConst(nwLabels.STATS_NAME[nwStats.CHARS_ALL])
|
69
|
+
trStats2 = trConst(nwLabels.STATS_NAME[nwStats.WORDS_ALL])
|
70
|
+
trStats3 = trConst(nwLabels.STATS_NAME[nwStats.PARAGRAPHS])
|
71
|
+
|
65
72
|
# Label
|
66
73
|
self.labelName = QLabel(self.tr("Label"), self)
|
67
74
|
self.labelName.setFont(fntLabel)
|
@@ -113,7 +120,7 @@ class GuiItemDetails(QWidget):
|
|
113
120
|
self.usageData.setWordWrap(True)
|
114
121
|
|
115
122
|
# Character Count
|
116
|
-
self.cCountName = QLabel(
|
123
|
+
self.cCountName = QLabel(trStats1, self)
|
117
124
|
self.cCountName.setFont(fntLabel)
|
118
125
|
self.cCountName.setAlignment(QtAlignRight)
|
119
126
|
|
@@ -122,7 +129,7 @@ class GuiItemDetails(QWidget):
|
|
122
129
|
self.cCountData.setAlignment(QtAlignRight)
|
123
130
|
|
124
131
|
# Word Count
|
125
|
-
self.wCountName = QLabel(
|
132
|
+
self.wCountName = QLabel(trStats2, self)
|
126
133
|
self.wCountName.setFont(fntLabel)
|
127
134
|
self.wCountName.setAlignment(QtAlignRight)
|
128
135
|
|
@@ -131,7 +138,7 @@ class GuiItemDetails(QWidget):
|
|
131
138
|
self.wCountData.setAlignment(QtAlignRight)
|
132
139
|
|
133
140
|
# Paragraph Count
|
134
|
-
self.pCountName = QLabel(
|
141
|
+
self.pCountName = QLabel(trStats3, self)
|
135
142
|
self.pCountName.setFont(fntLabel)
|
136
143
|
self.pCountName.setAlignment(QtAlignRight)
|
137
144
|
|
@@ -216,19 +223,9 @@ class GuiItemDetails(QWidget):
|
|
216
223
|
self.updateViewBox(self._handle)
|
217
224
|
return
|
218
225
|
|
219
|
-
|
220
|
-
# Public Slots
|
221
|
-
##
|
222
|
-
|
223
|
-
@pyqtSlot(str)
|
224
|
-
def updateViewBox(self, tHandle: str) -> None:
|
226
|
+
def updateViewBox(self, tHandle: str | None) -> None:
|
225
227
|
"""Populate the details box from a given handle."""
|
226
|
-
if tHandle
|
227
|
-
self.clearDetails()
|
228
|
-
return
|
229
|
-
|
230
|
-
nwItem = SHARED.project.tree[tHandle]
|
231
|
-
if nwItem is None:
|
228
|
+
if not (tHandle and (nwItem := SHARED.project.tree[tHandle])):
|
232
229
|
self.clearDetails()
|
233
230
|
return
|
234
231
|
|
@@ -265,10 +262,7 @@ class GuiItemDetails(QWidget):
|
|
265
262
|
# Layout
|
266
263
|
# ======
|
267
264
|
|
268
|
-
usageIcon
|
269
|
-
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
|
270
|
-
)
|
271
|
-
self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx))
|
265
|
+
self.usageIcon.setPixmap(nwItem.getMainIcon().pixmap(iPx, iPx))
|
272
266
|
self.usageData.setText(nwItem.describeMe())
|
273
267
|
|
274
268
|
# Counts
|
@@ -285,13 +279,16 @@ class GuiItemDetails(QWidget):
|
|
285
279
|
|
286
280
|
return
|
287
281
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
282
|
+
##
|
283
|
+
# Public Slots
|
284
|
+
##
|
285
|
+
|
286
|
+
@pyqtSlot(str, Enum)
|
287
|
+
def onProjectItemChanged(self, tHandle: str, change: nwChange) -> None:
|
288
|
+
"""Process project item change."""
|
293
289
|
if tHandle == self._handle:
|
294
|
-
|
295
|
-
|
296
|
-
|
290
|
+
if change == nwChange.UPDATE:
|
291
|
+
self.updateViewBox(tHandle)
|
292
|
+
elif change == nwChange.DELETE:
|
293
|
+
self.updateViewBox(None)
|
297
294
|
return
|