Complete the build process in the build tool
This commit is contained in:
@@ -206,14 +206,14 @@ class nwLabels:
|
|||||||
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
|
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
|
||||||
}
|
}
|
||||||
BUILD_FORMATS = {
|
BUILD_FORMATS = {
|
||||||
"odt": ("odt", QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)")),
|
"odt": (".odt", QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)")),
|
||||||
"fodt": ("fodt", QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)")),
|
"fodt": (".fodt", QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)")),
|
||||||
"html": ("html", QT_TRANSLATE_NOOP("Constant", "novelWriter HTML (.html)")),
|
"html": (".html", QT_TRANSLATE_NOOP("Constant", "novelWriter HTML (.html)")),
|
||||||
"nwd": ("nwd", QT_TRANSLATE_NOOP("Constant", "novelWriter Markdown (.nwd)")),
|
"nwd": (".nwd", QT_TRANSLATE_NOOP("Constant", "novelWriter Markdown (.nwd)")),
|
||||||
"md": ("md", QT_TRANSLATE_NOOP("Constant", "Standard Markdown (.md)")),
|
"md": (".md", QT_TRANSLATE_NOOP("Constant", "Standard Markdown (.md)")),
|
||||||
"md+": ("md", QT_TRANSLATE_NOOP("Constant", "Extended Markdown (.md)")),
|
"md+": (".md", QT_TRANSLATE_NOOP("Constant", "Extended Markdown (.md)")),
|
||||||
"jhtml": ("json", QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter HTML (.json)")),
|
"jhtml": (".json", QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter HTML (.json)")),
|
||||||
"jnwd": ("json", QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markdown (.json)")),
|
"jnwd": (".json", QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markdown (.json)")),
|
||||||
}
|
}
|
||||||
|
|
||||||
# END Class nwLabels
|
# END Class nwLabels
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ class FilterMode(Enum):
|
|||||||
INCLUDED = 2
|
INCLUDED = 2
|
||||||
EXCLUDED = 3
|
EXCLUDED = 3
|
||||||
SKIPPED = 4
|
SKIPPED = 4
|
||||||
|
ROOT = 5
|
||||||
|
|
||||||
# END Enum FilterMode
|
# END Enum FilterMode
|
||||||
|
|
||||||
@@ -311,7 +312,9 @@ class BuildSettings:
|
|||||||
"""Check if a root handle is allowed in the build."""
|
"""Check if a root handle is allowed in the build."""
|
||||||
return tHandle not in self._skipRoot
|
return tHandle not in self._skipRoot
|
||||||
|
|
||||||
def buildItemFilter(self, project: NWProject) -> dict[str, tuple[bool, FilterMode]]:
|
def buildItemFilter(
|
||||||
|
self, project: NWProject, withRoots: bool = False
|
||||||
|
) -> dict[str, tuple[bool, FilterMode]]:
|
||||||
"""Return a dictionary of item handles with filter decissions
|
"""Return a dictionary of item handles with filter decissions
|
||||||
applied.
|
applied.
|
||||||
"""
|
"""
|
||||||
@@ -333,6 +336,9 @@ class BuildSettings:
|
|||||||
if item.isInactiveClass() or (item.itemRoot in self._skipRoot):
|
if item.isInactiveClass() or (item.itemRoot in self._skipRoot):
|
||||||
result[tHandle] = (False, FilterMode.SKIPPED)
|
result[tHandle] = (False, FilterMode.SKIPPED)
|
||||||
continue
|
continue
|
||||||
|
if withRoots and item.isRootType():
|
||||||
|
result[tHandle] = (True, FilterMode.ROOT)
|
||||||
|
continue
|
||||||
if not item.isFileType():
|
if not item.isFileType():
|
||||||
result[tHandle] = (False, FilterMode.SKIPPED)
|
result[tHandle] = (False, FilterMode.SKIPPED)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -106,25 +106,41 @@ class NWBuildDocument:
|
|||||||
self._queue.append(item.itemHandle)
|
self._queue.append(item.itemHandle)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def iterBuild(self, path: Path, bFormat: str) -> Iterable[tuple[int, bool]]:
|
||||||
|
"""Wrapper for builder based on format."""
|
||||||
|
if bFormat in ("odt", "fodt"):
|
||||||
|
yield from self.iterBuildOpenDocument(path, bFormat == "fodt")
|
||||||
|
elif bFormat in ("html", "jhtml"):
|
||||||
|
yield from self.iterBuildHTML(path if bFormat == "html" else None)
|
||||||
|
elif bFormat in ("md", "md+"):
|
||||||
|
yield from self.iterBuildMarkdown(path, bFormat == "md+")
|
||||||
|
elif bFormat in ("nwd", "jnwd"):
|
||||||
|
yield from self.iterBuildNovelWriter(path if bFormat == "nwd" else None)
|
||||||
|
return
|
||||||
|
|
||||||
def iterBuildOpenDocument(self, path: Path, isFlat: bool) -> Iterable[tuple[int, bool]]:
|
def iterBuildOpenDocument(self, path: Path, isFlat: bool) -> Iterable[tuple[int, bool]]:
|
||||||
"""Build an Open Document file."""
|
"""Build an Open Document file."""
|
||||||
makeOdt = ToOdt(self._project, isFlat=isFlat)
|
makeObj = ToOdt(self._project, isFlat=isFlat)
|
||||||
self._setupBuild(makeOdt)
|
filtered = self._setupBuild(makeObj)
|
||||||
makeOdt.initDocument()
|
makeObj.initDocument()
|
||||||
|
|
||||||
for i, tHandle in enumerate(self._queue):
|
for i, tHandle in enumerate(self._queue):
|
||||||
yield i, self._doBuild(makeOdt, tHandle)
|
self._error = None
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
yield i, self._doBuild(makeObj, tHandle)
|
||||||
|
else:
|
||||||
|
yield i, False
|
||||||
|
|
||||||
makeOdt.closeDocument()
|
makeObj.closeDocument()
|
||||||
|
|
||||||
self._error = None
|
self._error = None
|
||||||
self._cache = makeOdt
|
self._cache = makeObj
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if isFlat:
|
if isFlat:
|
||||||
makeOdt.saveFlatXML(path)
|
makeObj.saveFlatXML(path)
|
||||||
else:
|
else:
|
||||||
makeOdt.saveOpenDocText(path)
|
makeObj.saveOpenDocText(path)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self._error = formatException(exc)
|
self._error = formatException(exc)
|
||||||
|
|
||||||
@@ -134,21 +150,25 @@ class NWBuildDocument:
|
|||||||
"""Build an HTML file. If path is None, no file is saved. This
|
"""Build an HTML file. If path is None, no file is saved. This
|
||||||
is used for generating build previews.
|
is used for generating build previews.
|
||||||
"""
|
"""
|
||||||
makeHtml = ToHtml(self._project)
|
makeObj = ToHtml(self._project)
|
||||||
self._setupBuild(makeHtml)
|
filtered = self._setupBuild(makeObj)
|
||||||
|
|
||||||
if self._build.getBool("format.replaceTabs"):
|
if self._build.getBool("format.replaceTabs"):
|
||||||
makeHtml.replaceTabs()
|
makeObj.replaceTabs()
|
||||||
|
|
||||||
for i, tHandle in enumerate(self._queue):
|
for i, tHandle in enumerate(self._queue):
|
||||||
yield i, self._doBuild(makeHtml, tHandle)
|
self._error = None
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
yield i, self._doBuild(makeObj, tHandle)
|
||||||
|
else:
|
||||||
|
yield i, False
|
||||||
|
|
||||||
self._error = None
|
self._error = None
|
||||||
self._cache = makeHtml
|
self._cache = makeObj
|
||||||
|
|
||||||
if isinstance(path, Path):
|
if isinstance(path, Path):
|
||||||
try:
|
try:
|
||||||
makeHtml.saveHTML5(path)
|
makeObj.saveHTML5(path)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self._error = formatException(exc)
|
self._error = formatException(exc)
|
||||||
|
|
||||||
@@ -156,35 +176,66 @@ class NWBuildDocument:
|
|||||||
|
|
||||||
def iterBuildMarkdown(self, path: Path, extendedMd: bool) -> Iterable[tuple[int, bool]]:
|
def iterBuildMarkdown(self, path: Path, extendedMd: bool) -> Iterable[tuple[int, bool]]:
|
||||||
"""Build a Markdown file."""
|
"""Build a Markdown file."""
|
||||||
makeMd = ToMarkdown(self._project)
|
makeObj = ToMarkdown(self._project)
|
||||||
self._setupBuild(makeMd)
|
filtered = self._setupBuild(makeObj)
|
||||||
|
|
||||||
if extendedMd:
|
if extendedMd:
|
||||||
makeMd.setGitHubMarkdown()
|
makeObj.setGitHubMarkdown()
|
||||||
else:
|
else:
|
||||||
makeMd.setStandardMarkdown()
|
makeObj.setStandardMarkdown()
|
||||||
|
|
||||||
if self._build.getBool("format.replaceTabs"):
|
if self._build.getBool("format.replaceTabs"):
|
||||||
makeMd.replaceTabs(nSpaces=4, spaceChar=" ")
|
makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
|
||||||
|
|
||||||
for i, tHandle in enumerate(self._queue):
|
for i, tHandle in enumerate(self._queue):
|
||||||
yield i, self._doBuild(makeMd, tHandle)
|
self._error = None
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
yield i, self._doBuild(makeObj, tHandle)
|
||||||
|
else:
|
||||||
|
yield i, False
|
||||||
|
|
||||||
self._error = None
|
self._error = None
|
||||||
self._cache = makeMd
|
self._cache = makeObj
|
||||||
|
|
||||||
try:
|
try:
|
||||||
makeMd.saveMarkdown(path)
|
makeObj.saveMarkdown(path)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self._error = formatException(exc)
|
self._error = formatException(exc)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def iterBuildNovelWriter(self, path: Path | None) -> Iterable[tuple[int, bool]]:
|
||||||
|
"""Build a novelWriter Markdown file."""
|
||||||
|
makeObj = ToMarkdown(self._project)
|
||||||
|
filtered = self._setupBuild(makeObj)
|
||||||
|
|
||||||
|
makeObj.setKeepMarkdown(True)
|
||||||
|
if self._build.getBool("format.replaceTabs"):
|
||||||
|
makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
|
||||||
|
|
||||||
|
for i, tHandle in enumerate(self._queue):
|
||||||
|
self._error = None
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
yield i, self._doBuild(makeObj, tHandle, convert=False)
|
||||||
|
else:
|
||||||
|
yield i, False
|
||||||
|
|
||||||
|
self._error = None
|
||||||
|
self._cache = makeObj
|
||||||
|
|
||||||
|
if isinstance(path, Path):
|
||||||
|
try:
|
||||||
|
makeObj.saveRawMarkdown(path)
|
||||||
|
except Exception as exc:
|
||||||
|
self._error = formatException(exc)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _setupBuild(self, bldObj: Tokenizer):
|
def _setupBuild(self, bldObj: Tokenizer) -> dict:
|
||||||
"""Configure the build object."""
|
"""Configure the build object."""
|
||||||
# Get Settings
|
# Get Settings
|
||||||
fmtTitle = self._build.getStr("headings.fmtTitle")
|
fmtTitle = self._build.getStr("headings.fmtTitle")
|
||||||
@@ -199,6 +250,7 @@ class NWBuildDocument:
|
|||||||
incComments = self._build.getBool("text.includeComments")
|
incComments = self._build.getBool("text.includeComments")
|
||||||
incKeywords = self._build.getBool("text.includeKeywords")
|
incKeywords = self._build.getBool("text.includeKeywords")
|
||||||
incBodyText = self._build.getBool("text.includeBodyText")
|
incBodyText = self._build.getBool("text.includeBodyText")
|
||||||
|
noteHeadings = self._build.getBool("text.addNoteHeadings")
|
||||||
|
|
||||||
buildLang = self._build.getStr("format.buildLang")
|
buildLang = self._build.getStr("format.buildLang")
|
||||||
textFont = self._build.getStr("format.textFont")
|
textFont = self._build.getStr("format.textFont")
|
||||||
@@ -244,11 +296,12 @@ class NWBuildDocument:
|
|||||||
bldObj.setColourHeaders(odtAddColours)
|
bldObj.setColourHeaders(odtAddColours)
|
||||||
bldObj.setLanguage(buildLang)
|
bldObj.setLanguage(buildLang)
|
||||||
|
|
||||||
return
|
filtered = self._build.buildItemFilter(self._project, withRoots=noteHeadings)
|
||||||
|
|
||||||
def _doBuild(self, bldObj: Tokenizer, tHandle: str) -> bool:
|
return filtered
|
||||||
|
|
||||||
|
def _doBuild(self, bldObj: Tokenizer, tHandle: str, convert: bool = True) -> bool:
|
||||||
"""Build a single document and add it to the build object."""
|
"""Build a single document and add it to the build object."""
|
||||||
self._error = None
|
|
||||||
tItem = self._project.tree[tHandle]
|
tItem = self._project.tree[tHandle]
|
||||||
if tItem is None:
|
if tItem is None:
|
||||||
self._error = f"Build: Unknown item '{tHandle}'"
|
self._error = f"Build: Unknown item '{tHandle}'"
|
||||||
@@ -258,13 +311,15 @@ class NWBuildDocument:
|
|||||||
try:
|
try:
|
||||||
if tItem.isRootType() and not tItem.isNovelLike():
|
if tItem.isRootType() and not tItem.isNovelLike():
|
||||||
bldObj.addRootHeading(tItem.itemHandle)
|
bldObj.addRootHeading(tItem.itemHandle)
|
||||||
bldObj.doConvert()
|
if convert:
|
||||||
|
bldObj.doConvert()
|
||||||
elif tItem.isFileType():
|
elif tItem.isFileType():
|
||||||
bldObj.setText(tHandle)
|
bldObj.setText(tHandle)
|
||||||
bldObj.doPreProcessing()
|
bldObj.doPreProcessing()
|
||||||
bldObj.tokenizeText()
|
bldObj.tokenizeText()
|
||||||
bldObj.doHeaders()
|
bldObj.doHeaders()
|
||||||
bldObj.doConvert()
|
if convert:
|
||||||
|
bldObj.doConvert()
|
||||||
else:
|
else:
|
||||||
logger.info(f"Build: Skipping '{tHandle}'")
|
logger.info(f"Build: Skipping '{tHandle}'")
|
||||||
|
|
||||||
|
|||||||
@@ -786,8 +786,7 @@ class ItemIndex:
|
|||||||
"""Iterate over all item headers of an item.
|
"""Iterate over all item headers of an item.
|
||||||
"""
|
"""
|
||||||
if tHandle in self._items:
|
if tHandle in self._items:
|
||||||
for sTitle, hItem in self._items[tHandle].items():
|
yield from self._items[tHandle].items()
|
||||||
yield sTitle, hItem
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def iterAllHeaders(self):
|
def iterAllHeaders(self):
|
||||||
|
|||||||
@@ -24,19 +24,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt5.QtCore import QSize, Qt, pyqtSlot
|
from PyQt5.QtCore import QSize, Qt, pyqtSlot
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAbstractButton, QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout,
|
QAbstractButton, QAbstractItemView, QDialog, QDialogButtonBox, QFileDialog,
|
||||||
QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QProgressBar,
|
QLabel, QListWidget, QListWidgetItem, QProgressBar, QPushButton, QSplitter,
|
||||||
QPushButton, QSplitter, QVBoxLayout, QWidget
|
QVBoxLayout, QWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.common import makeFileNameSafe
|
from novelwriter.common import makeFileNameSafe
|
||||||
from novelwriter.constants import nwLabels
|
from novelwriter.constants import nwLabels
|
||||||
|
from novelwriter.core.docbuild import NWBuildDocument
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.core.buildsettings import BuildSettings
|
from novelwriter.core.buildsettings import BuildSettings
|
||||||
|
|
||||||
@@ -70,11 +72,11 @@ class GuiManuscriptBuild(QDialog):
|
|||||||
|
|
||||||
self.setWindowTitle(self.tr("Build Manuscript"))
|
self.setWindowTitle(self.tr("Build Manuscript"))
|
||||||
self.setMinimumWidth(CONFIG.pxInt(500))
|
self.setMinimumWidth(CONFIG.pxInt(500))
|
||||||
self.setMinimumHeight(CONFIG.pxInt(300))
|
self.setMinimumHeight(CONFIG.pxInt(250))
|
||||||
|
|
||||||
iPx = self.mainTheme.baseIconSize
|
iPx = self.mainTheme.baseIconSize
|
||||||
wWin = CONFIG.pxInt(660)
|
wWin = CONFIG.pxInt(660)
|
||||||
hWin = CONFIG.pxInt(390)
|
hWin = CONFIG.pxInt(360)
|
||||||
|
|
||||||
pOptions = self.theProject.options
|
pOptions = self.theProject.options
|
||||||
self.resize(
|
self.resize(
|
||||||
@@ -138,35 +140,18 @@ class GuiManuscriptBuild(QDialog):
|
|||||||
self.lblMain.setWordWrap(True)
|
self.lblMain.setWordWrap(True)
|
||||||
self.lblMain.setFont(font)
|
self.lblMain.setFont(font)
|
||||||
|
|
||||||
self.spaceWidget = QWidget()
|
|
||||||
self.spaceWidget.setFixedHeight(CONFIG.pxInt(8))
|
|
||||||
|
|
||||||
# Build Path
|
|
||||||
self.lblPath = QLabel(self.tr("Build Folder"))
|
|
||||||
self.buildPath = QLineEdit()
|
|
||||||
self.buildPath.setText(str(self._build.lastPath))
|
|
||||||
self.btnBrowse = QPushButton(self.mainTheme.getIcon("browse"), "")
|
|
||||||
|
|
||||||
self.pathBox = QHBoxLayout()
|
|
||||||
self.pathBox.addWidget(self.buildPath)
|
|
||||||
self.pathBox.addWidget(self.btnBrowse)
|
|
||||||
|
|
||||||
# Build Name
|
|
||||||
self.lblName = QLabel(self.tr("Build Name"))
|
|
||||||
self.buildName = QLineEdit()
|
|
||||||
self.btnReset = QPushButton(self.mainTheme.getIcon("revert"), "")
|
|
||||||
self.btnReset.setToolTip(self.tr("Reset Build Name to default"))
|
|
||||||
self.btnReset.clicked.connect(self._doResetBuildName)
|
|
||||||
|
|
||||||
self.nameBox = QHBoxLayout()
|
|
||||||
self.nameBox.addWidget(self.buildName)
|
|
||||||
self.nameBox.addWidget(self.btnReset)
|
|
||||||
|
|
||||||
# Build Progress
|
# Build Progress
|
||||||
self.lblProgress = QLabel(self.tr("Build Progress"))
|
self.lblProgress = QLabel(self.tr("Build Progress"))
|
||||||
|
|
||||||
self.buildProgress = QProgressBar()
|
self.buildProgress = QProgressBar()
|
||||||
|
self.buildProgress.setMinimum(0)
|
||||||
self.buildProgress.setValue(0)
|
self.buildProgress.setValue(0)
|
||||||
|
|
||||||
|
self.progressBox = QVBoxLayout()
|
||||||
|
self.progressBox.addWidget(self.lblProgress)
|
||||||
|
self.progressBox.addWidget(self.buildProgress)
|
||||||
|
self.progressBox.setSpacing(CONFIG.pxInt(4))
|
||||||
|
|
||||||
# Dialog Buttons
|
# Dialog Buttons
|
||||||
self.btnBuild = QPushButton(self.mainTheme.getIcon("export"), self.tr("&Build"))
|
self.btnBuild = QPushButton(self.mainTheme.getIcon("export"), self.tr("&Build"))
|
||||||
self.dlgButtons = QDialogButtonBox(QDialogButtonBox.Close)
|
self.dlgButtons = QDialogButtonBox(QDialogButtonBox.Close)
|
||||||
@@ -187,26 +172,15 @@ class GuiManuscriptBuild(QDialog):
|
|||||||
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "sumWidth", wWin//2)),
|
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "sumWidth", wWin//2)),
|
||||||
])
|
])
|
||||||
|
|
||||||
self.outerBox = QGridLayout()
|
self.outerBox = QVBoxLayout()
|
||||||
self.outerBox.addWidget(self.lblMain, 0, 0, 1, 2, Qt.AlignCenter)
|
self.outerBox.addWidget(self.lblMain, 0, Qt.AlignCenter)
|
||||||
self.outerBox.addWidget(self.spaceWidget, 1, 0, 1, 2)
|
self.outerBox.addWidget(self.mainSplit, 1)
|
||||||
self.outerBox.addWidget(self.mainSplit, 2, 0, 1, 2)
|
self.outerBox.addLayout(self.progressBox, 0)
|
||||||
self.outerBox.addWidget(self.lblPath, 3, 0, 1, 1)
|
self.outerBox.addWidget(self.dlgButtons, 0)
|
||||||
self.outerBox.addLayout(self.pathBox, 3, 1, 1, 1)
|
self.outerBox.setSpacing(CONFIG.pxInt(12))
|
||||||
self.outerBox.addWidget(self.lblName, 4, 0, 1, 1)
|
|
||||||
self.outerBox.addLayout(self.nameBox, 4, 1, 1, 1)
|
|
||||||
self.outerBox.addWidget(self.lblProgress, 5, 0, 1, 1)
|
|
||||||
self.outerBox.addWidget(self.buildProgress, 5, 1, 1, 1)
|
|
||||||
self.outerBox.addWidget(self.dlgButtons, 6, 0, 1, 2)
|
|
||||||
self.outerBox.setRowStretch(2, 1)
|
|
||||||
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
if self._build.lastBuildName:
|
|
||||||
self.buildName.setText(makeFileNameSafe(self._build.lastBuildName))
|
|
||||||
else:
|
|
||||||
self._doResetBuildName()
|
|
||||||
|
|
||||||
self.btnBuild.setFocus()
|
self.btnBuild.setFocus()
|
||||||
self._populateContentList()
|
self._populateContentList()
|
||||||
|
|
||||||
@@ -252,30 +226,43 @@ class GuiManuscriptBuild(QDialog):
|
|||||||
self.close()
|
self.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _doResetBuildName(self):
|
|
||||||
"""Generate a default build name."""
|
|
||||||
bName = makeFileNameSafe(f"{self.theProject.data.name} - {self._build.name}")
|
|
||||||
self.buildName.setText(bName)
|
|
||||||
self._build.setLastBuildName(bName)
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _runBuild(self) -> bool:
|
def _runBuild(self) -> bool:
|
||||||
"""Run the currently selected build."""
|
"""Run the currently selected build."""
|
||||||
bFormat = self._getSelectedFormat()
|
selFormat = self._getSelectedFormat()
|
||||||
if not bFormat:
|
if not selFormat or selFormat not in nwLabels.BUILD_FORMATS:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
bPath = self.buildPath.text()
|
lastName = self._build.lastBuildName
|
||||||
bName = self.buildName.text()
|
if not lastName:
|
||||||
|
lastName = f"{self.theProject.data.name} - {self._build.name}.ext"
|
||||||
|
|
||||||
self._build.setLastFormat(bFormat)
|
lastPath = self._build.lastPath
|
||||||
self._build.setLastPath(bPath)
|
selExt = nwLabels.BUILD_FORMATS[selFormat][0]
|
||||||
self._build.setLastBuildName(bName)
|
selName = Path(makeFileNameSafe(lastName)).with_suffix(selExt)
|
||||||
|
|
||||||
|
self.buildProgress.setValue(0)
|
||||||
|
savePath, _ = QFileDialog.getSaveFileName(
|
||||||
|
self, self.tr("Save Manuscript As"), str(lastPath / selName)
|
||||||
|
)
|
||||||
|
if not savePath:
|
||||||
|
return False
|
||||||
|
|
||||||
|
buildPath = Path(savePath)
|
||||||
|
|
||||||
|
docBuild = NWBuildDocument(self.theProject, self._build)
|
||||||
|
docBuild.queueAll()
|
||||||
|
|
||||||
|
self.buildProgress.setMaximum(len(docBuild))
|
||||||
|
for i, _ in docBuild.iterBuild(buildPath, selFormat):
|
||||||
|
self.buildProgress.setValue(i+1)
|
||||||
|
|
||||||
|
self._build.setLastFormat(selFormat)
|
||||||
|
self._build.setLastPath(buildPath.parent)
|
||||||
|
self._build.setLastBuildName(buildPath.name)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ BUILD_CONF = {
|
|||||||
"settings": {
|
"settings": {
|
||||||
"filter.includeNovel": True,
|
"filter.includeNovel": True,
|
||||||
"filter.includeNotes": True,
|
"filter.includeNotes": True,
|
||||||
"filter.includeInactive": False,
|
"filter.includeInactive": True,
|
||||||
"headings.fmtTitle": "Title: {Title}",
|
"headings.fmtTitle": "Title: {Title}",
|
||||||
"headings.fmtChapter": "Chapter: {Title}",
|
"headings.fmtChapter": "Chapter: {Title}",
|
||||||
"headings.fmtUnnumbered": "{Title}",
|
"headings.fmtUnnumbered": "{Title}",
|
||||||
@@ -96,7 +96,7 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
|
|||||||
if docBuild.error:
|
if docBuild.error:
|
||||||
error.append(docBuild.error)
|
error.append(docBuild.error)
|
||||||
|
|
||||||
assert count == 21
|
assert count == 19
|
||||||
assert error == []
|
assert error == []
|
||||||
|
|
||||||
copyfile(docFile, tstFile)
|
copyfile(docFile, tstFile)
|
||||||
@@ -114,7 +114,7 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
|
|||||||
if docBuild.error:
|
if docBuild.error:
|
||||||
error.append(docBuild.error)
|
error.append(docBuild.error)
|
||||||
|
|
||||||
assert count == 21
|
assert count == 19
|
||||||
assert error == []
|
assert error == []
|
||||||
|
|
||||||
assert docFile.is_file()
|
assert docFile.is_file()
|
||||||
@@ -137,19 +137,17 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
|
|||||||
|
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("novelwriter.core.toodt.ToOdt.doConvert", causeException)
|
mp.setattr("novelwriter.core.toodt.ToOdt.doConvert", causeException)
|
||||||
|
assert len(docBuild) == 21
|
||||||
docBuild.addDocument("0000000000000")
|
|
||||||
assert len(docBuild) == 22
|
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
error = []
|
error = []
|
||||||
docFile = fncPath / "Lorem Ipsum Err.fodt"
|
docFile = fncPath / "Lorem Ipsum Err.fodt"
|
||||||
for _, success in docBuild.iterBuildOpenDocument(docFile, True):
|
for _, success in docBuild.iterBuildOpenDocument(docFile, True):
|
||||||
count += 1 if success else 0
|
count += 1 if success else 0
|
||||||
if docBuild.error:
|
if not success and docBuild.error:
|
||||||
error.append(docBuild.error)
|
error.append(docBuild.error)
|
||||||
|
|
||||||
assert count == 3
|
assert count == 1
|
||||||
assert error == [
|
assert error == [
|
||||||
"Build: Failed to build '7a992350f3eb6'",
|
"Build: Failed to build '7a992350f3eb6'",
|
||||||
"Build: Failed to build '8c58a65414c23'",
|
"Build: Failed to build '8c58a65414c23'",
|
||||||
@@ -169,7 +167,6 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
|
|||||||
"Build: Failed to build '2426c6f0ca922'",
|
"Build: Failed to build '2426c6f0ca922'",
|
||||||
"Build: Failed to build '60bdf227455cc'",
|
"Build: Failed to build '60bdf227455cc'",
|
||||||
"Build: Failed to build '04468803b92e1'",
|
"Build: Failed to build '04468803b92e1'",
|
||||||
"Build: Unknown item '0000000000000'",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# END Test testCoreDocBuild_OpenDocument
|
# END Test testCoreDocBuild_OpenDocument
|
||||||
@@ -204,7 +201,7 @@ def testCoreDocBuild_HTML(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
|
|||||||
if docBuild.error:
|
if docBuild.error:
|
||||||
error.append(docBuild.error)
|
error.append(docBuild.error)
|
||||||
|
|
||||||
assert count == 21
|
assert count == 19
|
||||||
assert error == []
|
assert error == []
|
||||||
|
|
||||||
copyfile(docFile, tstFile)
|
copyfile(docFile, tstFile)
|
||||||
@@ -255,7 +252,7 @@ def testCoreDocBuild_Markdown(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths
|
|||||||
if docBuild.error:
|
if docBuild.error:
|
||||||
error.append(docBuild.error)
|
error.append(docBuild.error)
|
||||||
|
|
||||||
assert count == 21
|
assert count == 19
|
||||||
assert error == []
|
assert error == []
|
||||||
|
|
||||||
copyfile(docFile, tstFile)
|
copyfile(docFile, tstFile)
|
||||||
@@ -275,7 +272,7 @@ def testCoreDocBuild_Markdown(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths
|
|||||||
if docBuild.error:
|
if docBuild.error:
|
||||||
error.append(docBuild.error)
|
error.append(docBuild.error)
|
||||||
|
|
||||||
assert count == 21
|
assert count == 19
|
||||||
assert error == []
|
assert error == []
|
||||||
|
|
||||||
copyfile(docFile, tstFile)
|
copyfile(docFile, tstFile)
|
||||||
|
|||||||
Reference in New Issue
Block a user