"""
novelWriter – GUI Build Manuscript
==================================
File History:
Created: 2023-05-24 [2.1b1] GuiManuscriptBuild
This file is a part of novelWriter
Copyright 2018–2023, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtWidgets import (
QAbstractButton, QDialog, QDialogButtonBox, QFrame, QGridLayout,
QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QProgressBar,
QPushButton, QSplitter, QVBoxLayout, QWidget
)
from novelwriter import CONFIG
from novelwriter.common import makeFileNameSafe
from novelwriter.constants import nwLabels
from novelwriter.core.buildsettings import BuildSettings
from novelwriter.extensions.switchbox import NSwitchBox
if TYPE_CHECKING:
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
class GuiManuscriptBuild(QDialog):
"""GUI Tools: Manucript Builder Dialog
This is the tool for running the build itself. It can be accessed
independently of the Manuscript Build Tool.
"""
def __init__(self, parent: QWidget, mainGui: GuiMain, build: BuildSettings):
super().__init__(parent=parent)
logger.debug("Create: GuiManuscriptBuild")
self.setObjectName("GuiManuscriptBuild")
self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
self.theProject = mainGui.theProject
self._parent = parent
self._build = build
self.setWindowTitle(self.tr("Build Manuscript"))
self.setMinimumWidth(CONFIG.pxInt(500))
self.setMinimumHeight(CONFIG.pxInt(250))
wWin = CONFIG.pxInt(660)
hWin = CONFIG.pxInt(350)
pOptions = self.theProject.options
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winHeight", hWin))
)
# Formats
# =======
self.lblFormat = QLabel("{0}".format(self.tr("Build Format")))
self.listFormats = QListWidget()
current = None
for key, (_, label) in nwLabels.BUILD_FORMATS.items():
item = QListWidgetItem()
item.setText(label)
item.setData(Qt.UserRole, key)
self.listFormats.addItem(item)
if key == self._build.lastFormat:
current = item
if current:
self.listFormats.setCurrentItem(current)
self.formatBox = QVBoxLayout()
self.formatBox.addWidget(self.lblFormat, 0)
self.formatBox.addWidget(self.listFormats, 1)
self.formatBox.setContentsMargins(0, 0, 0, 0)
self.formatWidget = QWidget()
self.formatWidget.setLayout(self.formatBox)
self.formatWidget.setContentsMargins(0, 0, 0, 0)
# Build Controls
# ==============
# Build Options
self.swtOptions = NSwitchBox(self, self.mainTheme.baseIconSize)
self.swtOptions.switchToggled.connect(self._applyBuildOptions)
self.swtOptions.setFrameStyle(QFrame.NoFrame)
self.swtOptions.setInnerContentsMargins(0, 0, 0, 0)
self.swtOptions.addLabel(self._build.getLabel("build"))
self.swtOptions.addItem(
self.mainTheme.getIcon("cls_novel"), self._build.getLabel("build.splitNovel"),
"build.splitNovel", default=self._build.getBool("build.splitNovel")
)
self.swtOptions.addItem(
self.mainTheme.getIcon("cls_custom"), self._build.getLabel("build.splitNotes"),
"build.splitNotes", default=self._build.getBool("build.splitNotes")
)
self.swtOptions.addItem(
self.mainTheme.getIcon("proj_chapter"), self._build.getLabel("build.splitChapters"),
"build.splitChapters", default=self._build.getBool("build.splitChapters")
)
# Dialog Controls
# ===============
# 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
self.lblProgress = QLabel(self.tr("Build Progress"))
self.buildProgress = QProgressBar()
# Dialog Buttons
self.dlgButtons = QDialogButtonBox(QDialogButtonBox.Close)
self.dlgButtons.addButton(
QPushButton(self.mainTheme.getIcon("export"), self.tr("&Build")),
QDialogButtonBox.ActionRole
)
self.dlgButtons.clicked.connect(self._dialogButtonClicked)
# Assemble GUI
# ============
self.mainSplit = QSplitter()
self.mainSplit.addWidget(self.formatWidget)
self.mainSplit.addWidget(self.swtOptions)
self.mainSplit.setHandleWidth(CONFIG.pxInt(16))
self.mainSplit.setCollapsible(0, False)
self.mainSplit.setCollapsible(1, False)
self.mainSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "fmtWidth", int(0.45*wWin))),
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "optsWidth", int(0.55*wWin))),
])
self.outerBox = QGridLayout()
self.outerBox.addWidget(self.mainSplit, 0, 0, 1, 2)
self.outerBox.addWidget(self.lblPath, 1, 0, 1, 1)
self.outerBox.addLayout(self.pathBox, 1, 1, 1, 1)
self.outerBox.addWidget(self.lblName, 2, 0, 1, 1)
self.outerBox.addLayout(self.nameBox, 2, 1, 1, 1)
self.outerBox.addWidget(self.lblProgress, 3, 0, 1, 1)
self.outerBox.addWidget(self.buildProgress, 3, 1, 1, 1)
self.outerBox.addWidget(self.dlgButtons, 4, 0, 1, 2)
self.setLayout(self.outerBox)
if self._build.lastBuildName:
self.buildName.setText(makeFileNameSafe(self._build.lastBuildName))
else:
self._doResetBuildName()
logger.debug("Ready: GuiManuscriptBuild")
return
def __del__(self):
"""For debug use only."""
logger.debug("Delete: GuiManuscriptBuild")
return
##
# Events
##
def closeEvent(self, event):
"""Capture the user closing the window so we can save GUI
settings.
"""
self._saveSettings()
event.accept()
self.deleteLater()
return
##
# Private Slots
##
@pyqtSlot(str, bool)
def _applyBuildOptions(self, key: str, state: bool):
"""Set the build options for the build."""
self._build.setValue(key, state)
return
@pyqtSlot("QAbstractButton*")
def _dialogButtonClicked(self, button: QAbstractButton):
"""Handle button clicks from the dialog button box."""
role = self.dlgButtons.buttonRole(button)
if role == QDialogButtonBox.ActionRole:
self._runBuild()
elif role == QDialogButtonBox.RejectRole:
self.close()
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
##
def _runBuild(self) -> bool:
"""Run the currently selected build."""
bFormat = self._getSelectedFormat()
if not bFormat:
return False
bPath = self.buildPath.text()
bName = self.buildName.text()
self._build.setLastFormat(bFormat)
self._build.setLastPath(bPath)
self._build.setLastBuildName(bName)
return True
def _getSelectedFormat(self) -> str | None:
"""Get the currently selected format."""
items = self.listFormats.selectedItems()
if items and isinstance(items[0], QListWidgetItem):
return str(items[0].data(Qt.UserRole))
return None
def _saveSettings(self):
"""Save the user GUI settings."""
logger.debug("Saving GuiManuscriptBuild settings")
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
mainSplit = self.mainSplit.sizes()
fmtWidth = CONFIG.rpxInt(mainSplit[0])
optsWidth = CONFIG.rpxInt(mainSplit[1])
pOptions = self.theProject.options
pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth)
pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight)
pOptions.setValue("GuiManuscriptBuild", "fmtWidth", fmtWidth)
pOptions.setValue("GuiManuscriptBuild", "optsWidth", optsWidth)
pOptions.saveSettings()
return
# END Class GuiManuscriptBuild