diff --git a/novelwriter/core/__init__.py b/novelwriter/core/__init__.py index 90b103f8..f1b5dfd3 100644 --- a/novelwriter/core/__init__.py +++ b/novelwriter/core/__init__.py @@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from novelwriter.core.doctools import DocMerger +from novelwriter.core.doctools import DocMerger, DocSplitter from novelwriter.core.document import NWDoc from novelwriter.core.index import countWords from novelwriter.core.project import NWProject @@ -30,6 +30,7 @@ from novelwriter.core.tomd import ToMarkdown __all__ = [ "DocMerger", + "DocSplitter", "countWords", "NWDoc", "NWProject", diff --git a/novelwriter/core/doctools.py b/novelwriter/core/doctools.py index f6716590..f4fc9cb5 100644 --- a/novelwriter/core/doctools.py +++ b/novelwriter/core/doctools.py @@ -4,7 +4,8 @@ novelWriter – Project Document Tools A collection of tools to create and manipulate documents File History: -Created: 2022-10-02 [2.0b1] +Created: 2022-10-02 [2.0b1] DocMerger +Created: 2022-10-11 [2.0b1] DocSplitter This file is a part of novelWriter Copyright 2018–2022, Veronica Berglyd Olsen @@ -117,3 +118,90 @@ class DocMerger: return status # END Class DocMerger + + +class DocSplitter: + + def __init__(self, theProject, sHandle): + + self.theProject = theProject + + self._error = "" + self._parHandle = None + self._srcHandle = None + self._srcItem = None + + self._rawData = [] + + srcItem = self.theProject.tree[sHandle] + if srcItem is not None and srcItem.isFileType(): + self._srcHandle = sHandle + self._srcItem = srcItem + + return + + ## + # Methods + ## + + def getError(self): + """Return any collected errors. + """ + return self._error + + def setParentItem(self, pHandle): + """Set the item that will be the top level parent item for the + new documents. + """ + self._parHandle = pHandle + return + + def newParentFolder(self, pHandle, folderLabel): + """Create a new folder that will be the top level parent item + for the new documents. + """ + if self._srcItem is None: + return None + + newHandle = self.theProject.newFolder(folderLabel, pHandle) + newItem = self.theProject.tree[self._parHandle] + newItem.setStatus(self._srcItem.itemStatus) + newItem.setImport(self._srcItem.itemImport) + + self._parHandle = newHandle + + return newHandle + + def splitDocument(self, splitData, splitText): + """Loop through the split data record and perform the split job. + """ + self._rawData = [] + buffer = splitText.copy() + for lineNo, hLevel, hLabel in reversed(splitData): + chunk = buffer[lineNo:] + buffer = buffer[:lineNo] + self._rawData.insert(0, (chunk, hLevel, hLabel)) + + return True + + def writeDocuments(self): + """An iterator that will write each document in the buffer, and + return its new handle, parent handle, and sibling handle. + """ + nearHandle = self._parHandle + for docText, hLevel, docLabel in self._rawData: + + newHandle = self.theProject.newFile(docLabel, self._parHandle) + + outDoc = NWDoc(self.theProject, newHandle) + status = outDoc.writeDocument("\n".join(docText)) + if not status: + self._error = outDoc.getError() + + yield newHandle, self._parHandle, nearHandle + + nearHandle = newHandle + + return + +# END Class DocSplitter diff --git a/novelwriter/dialogs/docsplit.py b/novelwriter/dialogs/docsplit.py index 0cd567c9..7ff206ee 100644 --- a/novelwriter/dialogs/docsplit.py +++ b/novelwriter/dialogs/docsplit.py @@ -41,6 +41,10 @@ logger = logging.getLogger(__name__) class GuiDocSplit(QDialog): + LINE_ROLE = Qt.UserRole + LEVEL_ROLE = Qt.UserRole + 1 + LABEL_ROLE = Qt.UserRole + 2 + def __init__(self, mainGui, sHandle): super().__init__(parent=mainGui) @@ -141,7 +145,9 @@ class GuiDocSplit(QDialog): headerList = [] for i in range(self.listBox.count()): item = self.listBox.item(i) - headerList.append((item.text(), item.data(Qt.UserRole))) + 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() @@ -157,7 +163,7 @@ class GuiDocSplit(QDialog): pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder) pOptions.setValue("GuiDocSplit", "docHierarchy", docHierarchy) - return self._data + return self._data, self._text ## # Slots @@ -193,19 +199,38 @@ class GuiDocSplit(QDialog): for lineNo, aLine in enumerate(self._text): onLine = -1 - if aLine.startswith(("# ", "#! ")) and spLevel >= 1: + hLevel = 0 + if aLine.startswith("# ") and spLevel >= 1: onLine = lineNo - elif aLine.startswith(("## ", "##! ")) and spLevel >= 2: + 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: + if onLine >= 0 and hLevel > 0: newItem = QListWidgetItem() newItem.setText(aLine.strip()) - newItem.setData(Qt.UserRole, onLine) + newItem.setData(self.LINE_ROLE, onLine) + newItem.setData(self.LEVEL_ROLE, hLevel) + newItem.setData(self.LABEL_ROLE, hLabel) self.listBox.addItem(newItem) return True diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index 540b3712..923410eb 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -40,6 +40,7 @@ from PyQt5.QtWidgets import ( ) from novelwriter.core import DocMerger +from novelwriter.core.doctools import DocSplitter from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert from novelwriter.dialogs import GuiDocMerge, GuiDocSplit, GuiEditLabel from novelwriter.constants import nwHeaders, trConst, nwLabels @@ -1475,7 +1476,18 @@ class GuiProjectTree(QTreeWidget): if dlgSplit.result() == QDialog.Accepted: - print(dlgSplit.getData()) + splitData, splitText = dlgSplit.getData() + print(splitData) + + headerList = splitData.get("headerList", []) + + docSplit = DocSplitter(self.theProject, tHandle) + docSplit.setParentItem(tItem.itemParent) + docSplit.splitDocument(headerList, splitText) + + for dHandle, _, nHandle in docSplit.writeDocuments(): + self.mainGui.projView.revealNewTreeItem(dHandle, nHandle) + self._alertTreeChange(dHandle, flush=False) else: logger.info("Action cancelled by user")