novelWriter 2.5.1__py3-none-any.whl → 2.6b1__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.1.dist-info → novelWriter-2.6b1.dist-info}/METADATA +2 -1
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/RECORD +61 -56
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +3 -3
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +1 -0
- novelwriter/assets/icons/typicons_dark/mixed_copy.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +1 -0
- novelwriter/assets/icons/typicons_light/mixed_copy.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/themes/default_light.conf +2 -2
- novelwriter/common.py +63 -0
- novelwriter/config.py +10 -3
- novelwriter/constants.py +153 -60
- novelwriter/core/buildsettings.py +66 -39
- novelwriter/core/coretools.py +34 -22
- novelwriter/core/docbuild.py +130 -169
- novelwriter/core/index.py +29 -18
- novelwriter/core/item.py +2 -2
- novelwriter/core/options.py +4 -1
- novelwriter/core/spellcheck.py +9 -14
- novelwriter/dialogs/preferences.py +45 -32
- novelwriter/dialogs/projectsettings.py +3 -3
- novelwriter/enum.py +29 -23
- novelwriter/extensions/configlayout.py +24 -11
- novelwriter/extensions/modified.py +13 -1
- novelwriter/extensions/pagedsidebar.py +5 -5
- novelwriter/formats/shared.py +155 -0
- novelwriter/formats/todocx.py +1195 -0
- novelwriter/formats/tohtml.py +452 -0
- novelwriter/{core → formats}/tokenizer.py +483 -485
- novelwriter/formats/tomarkdown.py +217 -0
- novelwriter/{core → formats}/toodt.py +270 -320
- novelwriter/formats/toqdoc.py +436 -0
- novelwriter/formats/toraw.py +91 -0
- novelwriter/gui/doceditor.py +240 -193
- novelwriter/gui/dochighlight.py +96 -84
- novelwriter/gui/docviewer.py +56 -30
- novelwriter/gui/docviewerpanel.py +3 -3
- novelwriter/gui/editordocument.py +17 -2
- novelwriter/gui/itemdetails.py +8 -4
- novelwriter/gui/mainmenu.py +121 -60
- novelwriter/gui/noveltree.py +35 -37
- novelwriter/gui/outline.py +186 -238
- novelwriter/gui/projtree.py +142 -131
- novelwriter/gui/sidebar.py +7 -6
- novelwriter/gui/theme.py +5 -4
- novelwriter/guimain.py +43 -155
- novelwriter/shared.py +14 -4
- novelwriter/text/counting.py +2 -0
- novelwriter/text/patterns.py +155 -59
- novelwriter/tools/manusbuild.py +1 -1
- novelwriter/tools/manuscript.py +121 -78
- novelwriter/tools/manussettings.py +403 -260
- novelwriter/tools/welcome.py +4 -4
- novelwriter/tools/writingstats.py +3 -3
- novelwriter/types.py +16 -6
- novelwriter/core/tohtml.py +0 -530
- novelwriter/core/tomarkdown.py +0 -252
- novelwriter/core/toqdoc.py +0 -419
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/top_level.txt +0 -0
novelwriter/core/tomarkdown.py
DELETED
@@ -1,252 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
novelWriter – Markdown Text Converter
|
3
|
-
=====================================
|
4
|
-
|
5
|
-
File History:
|
6
|
-
Created: 2021-02-06 [1.2b1] ToMarkdown
|
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 logging
|
27
|
-
|
28
|
-
from pathlib import Path
|
29
|
-
|
30
|
-
from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode
|
31
|
-
from novelwriter.core.project import NWProject
|
32
|
-
from novelwriter.core.tokenizer import T_Formats, Tokenizer
|
33
|
-
|
34
|
-
logger = logging.getLogger(__name__)
|
35
|
-
|
36
|
-
|
37
|
-
# Standard Markdown
|
38
|
-
STD_MD = {
|
39
|
-
Tokenizer.FMT_B_B: "**",
|
40
|
-
Tokenizer.FMT_B_E: "**",
|
41
|
-
Tokenizer.FMT_I_B: "_",
|
42
|
-
Tokenizer.FMT_I_E: "_",
|
43
|
-
Tokenizer.FMT_D_B: "",
|
44
|
-
Tokenizer.FMT_D_E: "",
|
45
|
-
Tokenizer.FMT_U_B: "",
|
46
|
-
Tokenizer.FMT_U_E: "",
|
47
|
-
Tokenizer.FMT_M_B: "",
|
48
|
-
Tokenizer.FMT_M_E: "",
|
49
|
-
Tokenizer.FMT_SUP_B: "",
|
50
|
-
Tokenizer.FMT_SUP_E: "",
|
51
|
-
Tokenizer.FMT_SUB_B: "",
|
52
|
-
Tokenizer.FMT_SUB_E: "",
|
53
|
-
Tokenizer.FMT_STRIP: "",
|
54
|
-
}
|
55
|
-
|
56
|
-
# Extended Markdown
|
57
|
-
EXT_MD = {
|
58
|
-
Tokenizer.FMT_B_B: "**",
|
59
|
-
Tokenizer.FMT_B_E: "**",
|
60
|
-
Tokenizer.FMT_I_B: "_",
|
61
|
-
Tokenizer.FMT_I_E: "_",
|
62
|
-
Tokenizer.FMT_D_B: "~~",
|
63
|
-
Tokenizer.FMT_D_E: "~~",
|
64
|
-
Tokenizer.FMT_U_B: "",
|
65
|
-
Tokenizer.FMT_U_E: "",
|
66
|
-
Tokenizer.FMT_M_B: "==",
|
67
|
-
Tokenizer.FMT_M_E: "==",
|
68
|
-
Tokenizer.FMT_SUP_B: "^",
|
69
|
-
Tokenizer.FMT_SUP_E: "^",
|
70
|
-
Tokenizer.FMT_SUB_B: "~",
|
71
|
-
Tokenizer.FMT_SUB_E: "~",
|
72
|
-
Tokenizer.FMT_STRIP: "",
|
73
|
-
}
|
74
|
-
|
75
|
-
|
76
|
-
class ToMarkdown(Tokenizer):
|
77
|
-
"""Core: Markdown Document Writer
|
78
|
-
|
79
|
-
Extend the Tokenizer class to writer Markdown output. It supports
|
80
|
-
both Standard Markdown and Extended Markdown. The class also
|
81
|
-
supports concatenating novelWriter markup files.
|
82
|
-
"""
|
83
|
-
|
84
|
-
def __init__(self, project: NWProject) -> None:
|
85
|
-
super().__init__(project)
|
86
|
-
self._fullMD: list[str] = []
|
87
|
-
self._usedNotes: dict[str, int] = {}
|
88
|
-
self._extended = True
|
89
|
-
return
|
90
|
-
|
91
|
-
##
|
92
|
-
# Properties
|
93
|
-
##
|
94
|
-
|
95
|
-
@property
|
96
|
-
def fullMD(self) -> list[str]:
|
97
|
-
"""Return the markdown as a list."""
|
98
|
-
return self._fullMD
|
99
|
-
|
100
|
-
##
|
101
|
-
# Setters
|
102
|
-
##
|
103
|
-
|
104
|
-
def setExtendedMarkdown(self, state: bool) -> None:
|
105
|
-
"""Set the converter to use Extended Markdown formatting."""
|
106
|
-
self._extended = state
|
107
|
-
return
|
108
|
-
|
109
|
-
##
|
110
|
-
# Class Methods
|
111
|
-
##
|
112
|
-
|
113
|
-
def getFullResultSize(self) -> int:
|
114
|
-
"""Return the size of the full Markdown result."""
|
115
|
-
return sum(len(x) for x in self._fullMD)
|
116
|
-
|
117
|
-
def doConvert(self) -> None:
|
118
|
-
"""Convert the list of text tokens into a Markdown document."""
|
119
|
-
self._result = ""
|
120
|
-
|
121
|
-
if self._extended:
|
122
|
-
mTags = EXT_MD
|
123
|
-
cSkip = nwUnicode.U_MMSP
|
124
|
-
else:
|
125
|
-
mTags = STD_MD
|
126
|
-
cSkip = ""
|
127
|
-
|
128
|
-
lines = []
|
129
|
-
for tType, _, tText, tFormat, tStyle in self._tokens:
|
130
|
-
|
131
|
-
if tType == self.T_TEXT:
|
132
|
-
tTemp = self._formatText(tText, tFormat, mTags).replace("\n", " \n")
|
133
|
-
lines.append(f"{tTemp}\n\n")
|
134
|
-
|
135
|
-
elif tType == self.T_TITLE:
|
136
|
-
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
137
|
-
lines.append(f"# {tHead}\n\n")
|
138
|
-
|
139
|
-
elif tType == self.T_HEAD1:
|
140
|
-
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
141
|
-
lines.append(f"# {tHead}\n\n")
|
142
|
-
|
143
|
-
elif tType == self.T_HEAD2:
|
144
|
-
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
145
|
-
lines.append(f"## {tHead}\n\n")
|
146
|
-
|
147
|
-
elif tType == self.T_HEAD3:
|
148
|
-
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
149
|
-
lines.append(f"### {tHead}\n\n")
|
150
|
-
|
151
|
-
elif tType == self.T_HEAD4:
|
152
|
-
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
153
|
-
lines.append(f"#### {tHead}\n\n")
|
154
|
-
|
155
|
-
elif tType == self.T_SEP:
|
156
|
-
lines.append(f"{tText}\n\n")
|
157
|
-
|
158
|
-
elif tType == self.T_SKIP:
|
159
|
-
lines.append(f"{cSkip}\n\n")
|
160
|
-
|
161
|
-
elif tType == self.T_SYNOPSIS and self._doSynopsis:
|
162
|
-
label = self._localLookup("Synopsis")
|
163
|
-
lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
|
164
|
-
|
165
|
-
elif tType == self.T_SHORT and self._doSynopsis:
|
166
|
-
label = self._localLookup("Short Description")
|
167
|
-
lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
|
168
|
-
|
169
|
-
elif tType == self.T_COMMENT and self._doComments:
|
170
|
-
label = self._localLookup("Comment")
|
171
|
-
lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
|
172
|
-
|
173
|
-
elif tType == self.T_KEYWORD and self._doKeywords:
|
174
|
-
lines.append(self._formatKeywords(tText, tStyle))
|
175
|
-
|
176
|
-
self._result = "".join(lines)
|
177
|
-
self._fullMD.append(self._result)
|
178
|
-
|
179
|
-
return
|
180
|
-
|
181
|
-
def appendFootnotes(self) -> None:
|
182
|
-
"""Append the footnotes in the buffer."""
|
183
|
-
if self._usedNotes:
|
184
|
-
tags = EXT_MD if self._extended else STD_MD
|
185
|
-
footnotes = self._localLookup("Footnotes")
|
186
|
-
|
187
|
-
lines = []
|
188
|
-
lines.append(f"### {footnotes}\n\n")
|
189
|
-
for key, index in self._usedNotes.items():
|
190
|
-
if content := self._footnotes.get(key):
|
191
|
-
marker = f"{index}. "
|
192
|
-
text = self._formatText(*content, tags)
|
193
|
-
lines.append(f"{marker}{text}\n")
|
194
|
-
lines.append("\n")
|
195
|
-
|
196
|
-
result = "".join(lines)
|
197
|
-
self._result += result
|
198
|
-
self._fullMD.append(result)
|
199
|
-
|
200
|
-
return
|
201
|
-
|
202
|
-
def saveMarkdown(self, path: str | Path) -> None:
|
203
|
-
"""Save the data to a plain text file."""
|
204
|
-
with open(path, mode="w", encoding="utf-8") as outFile:
|
205
|
-
outFile.write("".join(self._fullMD))
|
206
|
-
logger.info("Wrote file: %s", path)
|
207
|
-
return
|
208
|
-
|
209
|
-
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
|
210
|
-
"""Replace tabs with spaces."""
|
211
|
-
spaces = spaceChar*nSpaces
|
212
|
-
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
|
213
|
-
if self._keepMD:
|
214
|
-
self._markdown = [p.replace("\t", spaces) for p in self._markdown]
|
215
|
-
return
|
216
|
-
|
217
|
-
##
|
218
|
-
# Internal Functions
|
219
|
-
##
|
220
|
-
|
221
|
-
def _formatText(self, text: str, tFmt: T_Formats, tags: dict[int, str]) -> str:
|
222
|
-
"""Apply formatting tags to text."""
|
223
|
-
temp = text
|
224
|
-
for pos, fmt, data in reversed(tFmt):
|
225
|
-
md = ""
|
226
|
-
if fmt == self.FMT_FNOTE:
|
227
|
-
if data in self._footnotes:
|
228
|
-
index = len(self._usedNotes) + 1
|
229
|
-
self._usedNotes[data] = index
|
230
|
-
md = f"[{index}]"
|
231
|
-
else:
|
232
|
-
md = "[ERR]"
|
233
|
-
else:
|
234
|
-
md = tags.get(fmt, "")
|
235
|
-
temp = f"{temp[:pos]}{md}{temp[pos:]}"
|
236
|
-
return temp
|
237
|
-
|
238
|
-
def _formatKeywords(self, text: str, style: int) -> str:
|
239
|
-
"""Apply Markdown formatting to keywords."""
|
240
|
-
valid, bits, _ = self._project.index.scanThis("@"+text)
|
241
|
-
if not valid or not bits:
|
242
|
-
return ""
|
243
|
-
|
244
|
-
result = ""
|
245
|
-
if bits[0] in nwLabels.KEY_NAME:
|
246
|
-
result += f"**{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:** "
|
247
|
-
if len(bits) > 1:
|
248
|
-
result += ", ".join(bits[1:])
|
249
|
-
|
250
|
-
result += " \n" if style & self.A_Z_BTMMRG else "\n\n"
|
251
|
-
|
252
|
-
return result
|
novelwriter/core/toqdoc.py
DELETED
@@ -1,419 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
novelWriter – QTextDocument Converter
|
3
|
-
=====================================
|
4
|
-
|
5
|
-
File History:
|
6
|
-
Created: 2024-05-21 [2.5b1] ToQTextDocument
|
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 logging
|
27
|
-
|
28
|
-
from PyQt5.QtGui import (
|
29
|
-
QColor, QFont, QFontMetricsF, QTextBlockFormat, QTextCharFormat,
|
30
|
-
QTextCursor, QTextDocument
|
31
|
-
)
|
32
|
-
|
33
|
-
from novelwriter.constants import nwHeaders, nwHeadFmt, nwKeyWords, nwLabels, nwUnicode
|
34
|
-
from novelwriter.core.project import NWProject
|
35
|
-
from novelwriter.core.tokenizer import T_Formats, Tokenizer
|
36
|
-
from novelwriter.types import (
|
37
|
-
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
|
38
|
-
QtBlack, QtPageBreakAfter, QtPageBreakBefore, QtTransparent,
|
39
|
-
QtVAlignNormal, QtVAlignSub, QtVAlignSuper
|
40
|
-
)
|
41
|
-
|
42
|
-
logger = logging.getLogger(__name__)
|
43
|
-
|
44
|
-
T_TextStyle = tuple[QTextBlockFormat, QTextCharFormat]
|
45
|
-
|
46
|
-
|
47
|
-
class TextDocumentTheme:
|
48
|
-
text: QColor = QtBlack
|
49
|
-
highlight: QColor = QtTransparent
|
50
|
-
head: QColor = QtBlack
|
51
|
-
comment: QColor = QtBlack
|
52
|
-
note: QColor = QtBlack
|
53
|
-
code: QColor = QtBlack
|
54
|
-
modifier: QColor = QtBlack
|
55
|
-
keyword: QColor = QtBlack
|
56
|
-
tag: QColor = QtBlack
|
57
|
-
optional: QColor = QtBlack
|
58
|
-
dialog: QColor = QtBlack
|
59
|
-
altdialog: QColor = QtBlack
|
60
|
-
|
61
|
-
|
62
|
-
def newBlock(cursor: QTextCursor, bFmt: QTextBlockFormat) -> None:
|
63
|
-
if cursor.position() > 0:
|
64
|
-
cursor.insertBlock(bFmt)
|
65
|
-
else:
|
66
|
-
cursor.setBlockFormat(bFmt)
|
67
|
-
|
68
|
-
|
69
|
-
class ToQTextDocument(Tokenizer):
|
70
|
-
"""Core: QTextDocument Writer
|
71
|
-
|
72
|
-
Extend the Tokenizer class to generate a QTextDocument output. This
|
73
|
-
is intended for usage in the document viewer and build tool preview.
|
74
|
-
"""
|
75
|
-
|
76
|
-
def __init__(self, project: NWProject) -> None:
|
77
|
-
super().__init__(project)
|
78
|
-
self._document = QTextDocument()
|
79
|
-
self._document.setUndoRedoEnabled(False)
|
80
|
-
self._document.setDocumentMargin(0)
|
81
|
-
|
82
|
-
self._theme = TextDocumentTheme()
|
83
|
-
self._styles: dict[int, T_TextStyle] = {}
|
84
|
-
self._usedNotes: dict[str, int] = {}
|
85
|
-
|
86
|
-
self._init = False
|
87
|
-
self._bold = QFont.Weight.Bold
|
88
|
-
self._normal = QFont.Weight.Normal
|
89
|
-
|
90
|
-
return
|
91
|
-
|
92
|
-
def initDocument(self, font: QFont, theme: TextDocumentTheme) -> None:
|
93
|
-
"""Initialise all computed values of the document."""
|
94
|
-
self._textFont = font
|
95
|
-
self._theme = theme
|
96
|
-
|
97
|
-
self._document.setUndoRedoEnabled(False)
|
98
|
-
self._document.blockSignals(True)
|
99
|
-
self._document.clear()
|
100
|
-
self._document.setDefaultFont(self._textFont)
|
101
|
-
|
102
|
-
qMetric = QFontMetricsF(self._textFont)
|
103
|
-
mPx = qMetric.ascent() # 1 em in pixels
|
104
|
-
fPt = self._textFont.pointSizeF()
|
105
|
-
|
106
|
-
# Scaled Sizes
|
107
|
-
# ============
|
108
|
-
|
109
|
-
self._mHead = {
|
110
|
-
self.T_TITLE: (mPx * self._marginTitle[0], mPx * self._marginTitle[1]),
|
111
|
-
self.T_HEAD1: (mPx * self._marginHead1[0], mPx * self._marginHead1[1]),
|
112
|
-
self.T_HEAD2: (mPx * self._marginHead2[0], mPx * self._marginHead2[1]),
|
113
|
-
self.T_HEAD3: (mPx * self._marginHead3[0], mPx * self._marginHead3[1]),
|
114
|
-
self.T_HEAD4: (mPx * self._marginHead4[0], mPx * self._marginHead4[1]),
|
115
|
-
}
|
116
|
-
|
117
|
-
self._sHead = {
|
118
|
-
self.T_TITLE: nwHeaders.H_SIZES.get(0, 1.0) * fPt,
|
119
|
-
self.T_HEAD1: nwHeaders.H_SIZES.get(1, 1.0) * fPt,
|
120
|
-
self.T_HEAD2: nwHeaders.H_SIZES.get(2, 1.0) * fPt,
|
121
|
-
self.T_HEAD3: nwHeaders.H_SIZES.get(3, 1.0) * fPt,
|
122
|
-
self.T_HEAD4: nwHeaders.H_SIZES.get(4, 1.0) * fPt,
|
123
|
-
}
|
124
|
-
|
125
|
-
self._mText = (mPx * self._marginText[0], mPx * self._marginText[1])
|
126
|
-
self._mMeta = (mPx * self._marginMeta[0], mPx * self._marginMeta[1])
|
127
|
-
self._mSep = (mPx * self._marginSep[0], mPx * self._marginSep[1])
|
128
|
-
|
129
|
-
self._mIndent = mPx * 2.0
|
130
|
-
self._tIndent = mPx * self._firstWidth
|
131
|
-
|
132
|
-
# Block Format
|
133
|
-
# ============
|
134
|
-
|
135
|
-
self._blockFmt = QTextBlockFormat()
|
136
|
-
self._blockFmt.setTopMargin(self._mText[0])
|
137
|
-
self._blockFmt.setBottomMargin(self._mText[1])
|
138
|
-
self._blockFmt.setAlignment(QtAlignJustify if self._doJustify else QtAlignAbsolute)
|
139
|
-
self._blockFmt.setLineHeight(
|
140
|
-
100*self._lineHeight, QTextBlockFormat.LineHeightTypes.ProportionalHeight
|
141
|
-
)
|
142
|
-
|
143
|
-
# Character Formats
|
144
|
-
# =================
|
145
|
-
|
146
|
-
self._cText = QTextCharFormat()
|
147
|
-
self._cText.setBackground(QtTransparent)
|
148
|
-
self._cText.setForeground(self._theme.text)
|
149
|
-
|
150
|
-
self._cHead = QTextCharFormat(self._cText)
|
151
|
-
self._cHead.setForeground(self._theme.head)
|
152
|
-
|
153
|
-
self._cComment = QTextCharFormat(self._cText)
|
154
|
-
self._cComment.setForeground(self._theme.comment)
|
155
|
-
|
156
|
-
self._cCommentMod = QTextCharFormat(self._cText)
|
157
|
-
self._cCommentMod.setForeground(self._theme.comment)
|
158
|
-
self._cCommentMod.setFontWeight(self._bold)
|
159
|
-
|
160
|
-
self._cNote = QTextCharFormat(self._cText)
|
161
|
-
self._cNote.setForeground(self._theme.note)
|
162
|
-
|
163
|
-
self._cCode = QTextCharFormat(self._cText)
|
164
|
-
self._cCode.setForeground(self._theme.code)
|
165
|
-
|
166
|
-
self._cModifier = QTextCharFormat(self._cText)
|
167
|
-
self._cModifier.setForeground(self._theme.modifier)
|
168
|
-
self._cModifier.setFontWeight(self._bold)
|
169
|
-
|
170
|
-
self._cKeyword = QTextCharFormat(self._cText)
|
171
|
-
self._cKeyword.setForeground(self._theme.keyword)
|
172
|
-
|
173
|
-
self._cTag = QTextCharFormat(self._cText)
|
174
|
-
self._cTag.setForeground(self._theme.tag)
|
175
|
-
|
176
|
-
self._cOptional = QTextCharFormat(self._cText)
|
177
|
-
self._cOptional.setForeground(self._theme.optional)
|
178
|
-
|
179
|
-
self._init = True
|
180
|
-
|
181
|
-
return
|
182
|
-
|
183
|
-
##
|
184
|
-
# Properties
|
185
|
-
##
|
186
|
-
|
187
|
-
@property
|
188
|
-
def document(self) -> QTextDocument:
|
189
|
-
"""Return the document."""
|
190
|
-
return self._document
|
191
|
-
|
192
|
-
##
|
193
|
-
# Class Methods
|
194
|
-
##
|
195
|
-
|
196
|
-
def doConvert(self) -> None:
|
197
|
-
"""Write text tokens into the document."""
|
198
|
-
if not self._init:
|
199
|
-
return
|
200
|
-
|
201
|
-
self._document.blockSignals(True)
|
202
|
-
cursor = QTextCursor(self._document)
|
203
|
-
cursor.movePosition(QTextCursor.MoveOperation.End)
|
204
|
-
|
205
|
-
for tType, nHead, tText, tFormat, tStyle in self._tokens:
|
206
|
-
|
207
|
-
# Styles
|
208
|
-
bFmt = QTextBlockFormat(self._blockFmt)
|
209
|
-
if tStyle is not None:
|
210
|
-
if tStyle & self.A_LEFT:
|
211
|
-
bFmt.setAlignment(QtAlignLeft)
|
212
|
-
elif tStyle & self.A_RIGHT:
|
213
|
-
bFmt.setAlignment(QtAlignRight)
|
214
|
-
elif tStyle & self.A_CENTRE:
|
215
|
-
bFmt.setAlignment(QtAlignCenter)
|
216
|
-
elif tStyle & self.A_JUSTIFY:
|
217
|
-
bFmt.setAlignment(QtAlignJustify)
|
218
|
-
|
219
|
-
if tStyle & self.A_PBB:
|
220
|
-
bFmt.setPageBreakPolicy(QtPageBreakBefore)
|
221
|
-
if tStyle & self.A_PBA:
|
222
|
-
bFmt.setPageBreakPolicy(QtPageBreakAfter)
|
223
|
-
|
224
|
-
if tStyle & self.A_Z_BTMMRG:
|
225
|
-
bFmt.setBottomMargin(0.0)
|
226
|
-
if tStyle & self.A_Z_TOPMRG:
|
227
|
-
bFmt.setTopMargin(0.0)
|
228
|
-
|
229
|
-
if tStyle & self.A_IND_L:
|
230
|
-
bFmt.setLeftMargin(self._mIndent)
|
231
|
-
if tStyle & self.A_IND_R:
|
232
|
-
bFmt.setRightMargin(self._mIndent)
|
233
|
-
if tStyle & self.A_IND_T:
|
234
|
-
bFmt.setTextIndent(self._tIndent)
|
235
|
-
|
236
|
-
if tType == self.T_TEXT:
|
237
|
-
newBlock(cursor, bFmt)
|
238
|
-
self._insertFragments(tText, tFormat, cursor, self._cText)
|
239
|
-
|
240
|
-
elif tType in self.L_HEADINGS:
|
241
|
-
bFmt, cFmt = self._genHeadStyle(tType, nHead, bFmt)
|
242
|
-
newBlock(cursor, bFmt)
|
243
|
-
cursor.insertText(tText.replace(nwHeadFmt.BR, "\n"), cFmt)
|
244
|
-
|
245
|
-
elif tType == self.T_SEP:
|
246
|
-
sFmt = QTextBlockFormat(bFmt)
|
247
|
-
sFmt.setTopMargin(self._mSep[0])
|
248
|
-
sFmt.setBottomMargin(self._mSep[1])
|
249
|
-
newBlock(cursor, sFmt)
|
250
|
-
cursor.insertText(tText, self._cText)
|
251
|
-
|
252
|
-
elif tType == self.T_SKIP:
|
253
|
-
newBlock(cursor, bFmt)
|
254
|
-
cursor.insertText(nwUnicode.U_NBSP, self._cText)
|
255
|
-
|
256
|
-
elif tType in self.L_SUMMARY and self._doSynopsis:
|
257
|
-
newBlock(cursor, bFmt)
|
258
|
-
modifier = self._localLookup(
|
259
|
-
"Short Description" if tType == self.T_SHORT else "Synopsis"
|
260
|
-
)
|
261
|
-
cursor.insertText(f"{modifier}: ", self._cModifier)
|
262
|
-
self._insertFragments(tText, tFormat, cursor, self._cNote)
|
263
|
-
|
264
|
-
elif tType == self.T_COMMENT and self._doComments:
|
265
|
-
newBlock(cursor, bFmt)
|
266
|
-
modifier = self._localLookup("Comment")
|
267
|
-
cursor.insertText(f"{modifier}: ", self._cCommentMod)
|
268
|
-
self._insertFragments(tText, tFormat, cursor, self._cComment)
|
269
|
-
|
270
|
-
elif tType == self.T_KEYWORD and self._doKeywords:
|
271
|
-
newBlock(cursor, bFmt)
|
272
|
-
self._insertKeywords(tText, cursor)
|
273
|
-
|
274
|
-
self._document.blockSignals(False)
|
275
|
-
|
276
|
-
return
|
277
|
-
|
278
|
-
def appendFootnotes(self) -> None:
|
279
|
-
"""Append the footnotes in the buffer."""
|
280
|
-
if self._usedNotes:
|
281
|
-
self._document.blockSignals(True)
|
282
|
-
|
283
|
-
cursor = QTextCursor(self._document)
|
284
|
-
cursor.movePosition(QTextCursor.MoveOperation.End)
|
285
|
-
|
286
|
-
bFmt, cFmt = self._genHeadStyle(self.T_HEAD4, -1, self._blockFmt)
|
287
|
-
newBlock(cursor, bFmt)
|
288
|
-
cursor.insertText(self._localLookup("Footnotes"), cFmt)
|
289
|
-
|
290
|
-
for key, index in self._usedNotes.items():
|
291
|
-
if content := self._footnotes.get(key):
|
292
|
-
cFmt = QTextCharFormat(self._cCode)
|
293
|
-
cFmt.setAnchor(True)
|
294
|
-
cFmt.setAnchorNames([f"footnote_{index}"])
|
295
|
-
newBlock(cursor, self._blockFmt)
|
296
|
-
cursor.insertText(f"{index}. ", cFmt)
|
297
|
-
self._insertFragments(*content, cursor, self._cText)
|
298
|
-
|
299
|
-
self._document.blockSignals(False)
|
300
|
-
|
301
|
-
return
|
302
|
-
|
303
|
-
##
|
304
|
-
# Internal Functions
|
305
|
-
##
|
306
|
-
|
307
|
-
def _insertFragments(
|
308
|
-
self, text: str, tFmt: T_Formats, cursor: QTextCursor, dFmt: QTextCharFormat
|
309
|
-
) -> None:
|
310
|
-
"""Apply formatting tags to text."""
|
311
|
-
cFmt = QTextCharFormat(dFmt)
|
312
|
-
start = 0
|
313
|
-
temp = text.replace("\n", nwUnicode.U_LSEP)
|
314
|
-
for pos, fmt, data in tFmt:
|
315
|
-
|
316
|
-
# Insert buffer with previous format
|
317
|
-
cursor.insertText(temp[start:pos], cFmt)
|
318
|
-
|
319
|
-
# Construct next format
|
320
|
-
if fmt == self.FMT_B_B:
|
321
|
-
cFmt.setFontWeight(self._bold)
|
322
|
-
elif fmt == self.FMT_B_E:
|
323
|
-
cFmt.setFontWeight(self._normal)
|
324
|
-
elif fmt == self.FMT_I_B:
|
325
|
-
cFmt.setFontItalic(True)
|
326
|
-
elif fmt == self.FMT_I_E:
|
327
|
-
cFmt.setFontItalic(False)
|
328
|
-
elif fmt == self.FMT_D_B:
|
329
|
-
cFmt.setFontStrikeOut(True)
|
330
|
-
elif fmt == self.FMT_D_E:
|
331
|
-
cFmt.setFontStrikeOut(False)
|
332
|
-
elif fmt == self.FMT_U_B:
|
333
|
-
cFmt.setFontUnderline(True)
|
334
|
-
elif fmt == self.FMT_U_E:
|
335
|
-
cFmt.setFontUnderline(False)
|
336
|
-
elif fmt == self.FMT_M_B:
|
337
|
-
cFmt.setBackground(self._theme.highlight)
|
338
|
-
elif fmt == self.FMT_M_E:
|
339
|
-
cFmt.setBackground(QtTransparent)
|
340
|
-
elif fmt == self.FMT_SUP_B:
|
341
|
-
cFmt.setVerticalAlignment(QtVAlignSuper)
|
342
|
-
elif fmt == self.FMT_SUP_E:
|
343
|
-
cFmt.setVerticalAlignment(QtVAlignNormal)
|
344
|
-
elif fmt == self.FMT_SUB_B:
|
345
|
-
cFmt.setVerticalAlignment(QtVAlignSub)
|
346
|
-
elif fmt == self.FMT_SUB_E:
|
347
|
-
cFmt.setVerticalAlignment(QtVAlignNormal)
|
348
|
-
elif fmt == self.FMT_DL_B:
|
349
|
-
cFmt.setForeground(self._theme.dialog)
|
350
|
-
elif fmt == self.FMT_DL_E:
|
351
|
-
cFmt.setForeground(self._theme.text)
|
352
|
-
elif fmt == self.FMT_ADL_B:
|
353
|
-
cFmt.setForeground(self._theme.altdialog)
|
354
|
-
elif fmt == self.FMT_ADL_E:
|
355
|
-
cFmt.setForeground(self._theme.text)
|
356
|
-
elif fmt == self.FMT_FNOTE:
|
357
|
-
xFmt = QTextCharFormat(self._cCode)
|
358
|
-
xFmt.setVerticalAlignment(QtVAlignSuper)
|
359
|
-
if data in self._footnotes:
|
360
|
-
index = len(self._usedNotes) + 1
|
361
|
-
self._usedNotes[data] = index
|
362
|
-
xFmt.setAnchor(True)
|
363
|
-
xFmt.setAnchorHref(f"#footnote_{index}")
|
364
|
-
xFmt.setFontUnderline(True)
|
365
|
-
cursor.insertText(f"[{index}]", xFmt)
|
366
|
-
else:
|
367
|
-
cursor.insertText("[ERR]", cFmt)
|
368
|
-
|
369
|
-
# Move pos for next pass
|
370
|
-
start = pos
|
371
|
-
|
372
|
-
# Insert whatever is left in the buffer
|
373
|
-
cursor.insertText(temp[start:], cFmt)
|
374
|
-
|
375
|
-
return
|
376
|
-
|
377
|
-
def _insertKeywords(self, text: str, cursor: QTextCursor) -> None:
|
378
|
-
"""Apply Markdown formatting to keywords."""
|
379
|
-
valid, bits, _ = self._project.index.scanThis("@"+text)
|
380
|
-
if valid and bits:
|
381
|
-
key = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
|
382
|
-
cursor.insertText(key, self._cKeyword)
|
383
|
-
if (num := len(bits)) > 1:
|
384
|
-
if bits[0] == nwKeyWords.TAG_KEY:
|
385
|
-
one, two = self._project.index.parseValue(bits[1])
|
386
|
-
cFmt = QTextCharFormat(self._cTag)
|
387
|
-
cFmt.setAnchor(True)
|
388
|
-
cFmt.setAnchorNames([f"tag_{one}".lower()])
|
389
|
-
cursor.insertText(one, cFmt)
|
390
|
-
if two:
|
391
|
-
cursor.insertText(" | ", self._cText)
|
392
|
-
cursor.insertText(two, self._cOptional)
|
393
|
-
else:
|
394
|
-
for n, bit in enumerate(bits[1:], 2):
|
395
|
-
cFmt = QTextCharFormat(self._cTag)
|
396
|
-
cFmt.setFontUnderline(True)
|
397
|
-
cFmt.setAnchor(True)
|
398
|
-
cFmt.setAnchorHref(f"#tag_{bit}".lower())
|
399
|
-
cursor.insertText(bit, cFmt)
|
400
|
-
if n < num:
|
401
|
-
cursor.insertText(", ", self._cText)
|
402
|
-
return
|
403
|
-
|
404
|
-
def _genHeadStyle(self, hType: int, nHead: int, rFmt: QTextBlockFormat) -> T_TextStyle:
|
405
|
-
"""Generate a heading style set."""
|
406
|
-
mTop, mBottom = self._mHead.get(hType, (0.0, 0.0))
|
407
|
-
|
408
|
-
bFmt = QTextBlockFormat(rFmt)
|
409
|
-
bFmt.setTopMargin(mTop)
|
410
|
-
bFmt.setBottomMargin(mBottom)
|
411
|
-
|
412
|
-
cFmt = QTextCharFormat(self._cText if hType == self.T_TITLE else self._cHead)
|
413
|
-
cFmt.setFontWeight(self._bold)
|
414
|
-
cFmt.setFontPointSize(self._sHead.get(hType, 1.0))
|
415
|
-
if nHead >= 0:
|
416
|
-
cFmt.setAnchorNames([f"{self._handle}:T{nHead:04d}"])
|
417
|
-
cFmt.setAnchor(True)
|
418
|
-
|
419
|
-
return bFmt, cFmt
|
File without changes
|
File without changes
|
File without changes
|