""" novelWriter – GUI Doc Split Dialog ================================== File History: Created: 2020-02-01 [0.4.3] GuiDocSplit Rewritten: 2022-10-12 [2.0rc1] GuiDocSplit This file is a part of novelWriter Copyright 2018–2024, 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 PyQt5.QtGui import QCloseEvent from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtWidgets import ( QAbstractItemView, QComboBox, QDialog, QDialogButtonBox, QGridLayout, QLabel, QListWidget, QListWidgetItem, QVBoxLayout, QWidget ) from novelwriter import CONFIG, SHARED from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.configlayout import NHelpLabel logger = logging.getLogger(__name__) class GuiDocSplit(QDialog): LINE_ROLE = Qt.ItemDataRole.UserRole LEVEL_ROLE = Qt.ItemDataRole.UserRole + 1 LABEL_ROLE = Qt.ItemDataRole.UserRole + 2 def __init__(self, parent: QWidget, sHandle: str) -> None: super().__init__(parent=parent) logger.debug("Create: GuiDocSplit") self.setObjectName("GuiDocSplit") self._data = {} self._text = [] self.setWindowTitle(self.tr("Split Document")) self.headLabel = QLabel("{0}".format(self.tr("Document Headers"))) self.helpLabel = NHelpLabel( self.tr("Select the maximum level to split into files."), SHARED.theme.helpText ) # Values iPx = SHARED.theme.baseIconSize hSp = CONFIG.pxInt(12) vSp = CONFIG.pxInt(8) bSp = CONFIG.pxInt(12) pOptions = SHARED.project.options spLevel = pOptions.getInt("GuiDocSplit", "spLevel", 3) intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True) docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True) # Header Selection self.listBox = QListWidget() self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop) self.listBox.setMinimumWidth(CONFIG.pxInt(400)) self.listBox.setMinimumHeight(CONFIG.pxInt(180)) self.splitLevel = QComboBox(self) self.splitLevel.addItem(self.tr("Split on Header Level 1 (Title)"), 1) self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2) self.splitLevel.addItem(self.tr("Split up to Header Level 3 (Scene)"), 3) self.splitLevel.addItem(self.tr("Split up to Header Level 4 (Section)"), 4) spIndex = self.splitLevel.findData(spLevel) if spIndex != -1: self.splitLevel.setCurrentIndex(spIndex) self.splitLevel.currentIndexChanged.connect(self._reloadList) # Split Options self.folderLabel = QLabel(self.tr("Split into a new folder")) self.folderSwitch = NSwitch(width=2*iPx, height=iPx) self.folderSwitch.setChecked(intoFolder) self.hierarchyLabel = QLabel(self.tr("Create document hierarchy")) self.hierarchySwitch = NSwitch(width=2*iPx, height=iPx) self.hierarchySwitch.setChecked(docHierarchy) self.trashLabel = QLabel(self.tr("Move split document to Trash")) self.trashSwitch = NSwitch(width=2*iPx, height=iPx) self.optBox = QGridLayout() self.optBox.addWidget(self.folderLabel, 0, 0) self.optBox.addWidget(self.folderSwitch, 0, 1) self.optBox.addWidget(self.hierarchyLabel, 1, 0) self.optBox.addWidget(self.hierarchySwitch, 1, 1) self.optBox.addWidget(self.trashLabel, 2, 0) self.optBox.addWidget(self.trashSwitch, 2, 1) self.optBox.setVerticalSpacing(vSp) self.optBox.setHorizontalSpacing(hSp) self.optBox.setColumnStretch(3, 1) # Buttons self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) # Assemble self.outerBox = QVBoxLayout() self.outerBox.setSpacing(0) self.outerBox.addWidget(self.headLabel) self.outerBox.addWidget(self.helpLabel) self.outerBox.addSpacing(vSp) self.outerBox.addWidget(self.listBox) self.outerBox.addWidget(self.splitLevel) self.outerBox.addSpacing(vSp) self.outerBox.addLayout(self.optBox) self.outerBox.addSpacing(bSp) self.outerBox.addWidget(self.buttonBox) self.setLayout(self.outerBox) # Load Content self._loadContent(sHandle) logger.debug("Ready: GuiDocSplit") return def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiDocSplit") return def getData(self) -> tuple[dict, list]: """Return the user's choices. Also save the users options for the next time the dialog is used. """ headerList = [] for i in range(self.listBox.count()): item = self.listBox.item(i) if item is not None: headerList.append(( item.data(self.LINE_ROLE), item.data(self.LEVEL_ROLE), item.data(self.LABEL_ROLE), )) spLevel = self.splitLevel.currentData() intoFolder = self.folderSwitch.isChecked() docHierarchy = self.hierarchySwitch.isChecked() moveToTrash = self.trashSwitch.isChecked() self._data["spLevel"] = spLevel self._data["headerList"] = headerList self._data["intoFolder"] = intoFolder self._data["docHierarchy"] = docHierarchy self._data["moveToTrash"] = moveToTrash logger.debug("Saving State: GuiDocSplit") pOptions = SHARED.project.options pOptions.setValue("GuiDocSplit", "spLevel", spLevel) pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder) pOptions.setValue("GuiDocSplit", "docHierarchy", docHierarchy) return self._data, self._text ## # Events ## def closeEvent(self, event: QCloseEvent) -> None: """Capture the close event and perform cleanup.""" event.accept() self.deleteLater() return ## # Private Slots ## @pyqtSlot() def _reloadList(self) -> None: """Reload the content of the list box.""" sHandle = self._data.get("sHandle", None) self._loadContent(sHandle) return ## # Internal Functions ## def _loadContent(self, sHandle: str) -> None: """Load content from a given source item.""" self._data = {} self._data["sHandle"] = sHandle self.listBox.clear() nwItem = SHARED.project.tree[sHandle] if nwItem is None or not nwItem.isFileType(): return spLevel = self.splitLevel.currentData() if not self._text: inDoc = SHARED.project.storage.getDocument(sHandle) self._text = (inDoc.readDocument() or "").splitlines() for lineNo, aLine in enumerate(self._text): onLine = -1 hLevel = 0 hLabel = aLine.strip() if aLine.startswith("# ") and spLevel >= 1: onLine = lineNo hLevel = 1 hLabel = aLine[2:].strip() elif aLine.startswith("## ") and spLevel >= 2: onLine = lineNo hLevel = 2 hLabel = aLine[3:].strip() elif aLine.startswith("### ") and spLevel >= 3: onLine = lineNo hLevel = 3 hLabel = aLine[4:].strip() elif aLine.startswith("#### ") and spLevel >= 4: onLine = lineNo hLevel = 4 hLabel = aLine[5:].strip() elif aLine.startswith("#! ") and spLevel >= 1: onLine = lineNo hLevel = 1 hLabel = aLine[3:].strip() elif aLine.startswith("##! ") and spLevel >= 2: onLine = lineNo hLevel = 2 hLabel = aLine[4:].strip() if onLine >= 0 and hLevel > 0: newItem = QListWidgetItem() newItem.setText(aLine.strip()) newItem.setData(self.LINE_ROLE, onLine) newItem.setData(self.LEVEL_ROLE, hLevel) newItem.setData(self.LABEL_ROLE, hLabel) self.listBox.addItem(newItem) return # END Class GuiDocSplit