Implement nre doc merge functionality in tree

This commit is contained in:
Veronica Berglyd Olsen
2022-10-09 17:19:01 +02:00
parent e1c3a12d07
commit 0f3d598f8d
4 changed files with 156 additions and 70 deletions
+73 -56
View File
@@ -25,60 +25,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging import logging
from novelwriter.core.document import NWDoc
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# logger.verbose("GuiDocMerge merge button clicked")
# finalOrder = []
# for i in range(self.listBox.count()):
# finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
# if len(finalOrder) == 0:
# self.mainGui.makeAlert(self.tr(
# "No source documents found. Nothing to do."
# ), nwAlert.ERROR)
# return False
# theText = ""
# for tHandle in finalOrder:
# inDoc = NWDoc(self.theProject, tHandle)
# docText = inDoc.readDocument()
# docErr = inDoc.getError()
# if docText is None and docErr:
# self.mainGui.makeAlert([
# self.tr("Failed to open document file."), docErr
# ], nwAlert.ERROR)
# if docText:
# theText += docText.rstrip("\n")+"\n\n"
# if self.sourceItem is None:
# self.mainGui.makeAlert(self.tr(
# "No source folder selected. Nothing to do."
# ), nwAlert.ERROR)
# return False
# srcItem = self.theProject.tree[self.sourceItem]
# if srcItem is None:
# self.mainGui.makeAlert(self.tr("Internal error."), nwAlert.ERROR)
# return False
# nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemParent)
# newItem = self.theProject.tree[nHandle]
# newItem.setStatus(srcItem.itemStatus)
# newItem.setImport(srcItem.itemImport)
# outDoc = NWDoc(self.theProject, nHandle)
# if not outDoc.writeDocument(theText):
# self.mainGui.makeAlert([
# self.tr("Could not save document."), outDoc.getError()
# ], nwAlert.ERROR)
# return False
# self.mainGui.projView.revealNewTreeItem(nHandle)
# self.mainGui.openDocument(nHandle, doScroll=True)
# self._doClose()
class DocMerger: class DocMerger:
@@ -86,17 +36,84 @@ class DocMerger:
self.theProject = theProject self.theProject = theProject
self._error = ""
self._targetDoc = None self._targetDoc = None
self._targetText = []
return return
##
# Methods
##
def getError(self):
"""Return any collected errors.
"""
return self._error
def setTargetDoc(self, tHandle): def setTargetDoc(self, tHandle):
"""Set the target document for the merging. Calling this
function resets the class.
"""
self._targetDoc = tHandle
self._targetText = []
return return
def createNewDoc(self, docLabel, pHandle, itemLayout): def newTargetDoc(self, srcHandle, docLabel):
return """Create a barnd new target document based on a source handle
and a new doc label. Calling this function resets the class.
"""
srcItem = self.theProject.tree[srcHandle]
if srcItem is None:
return None
def appendDoc(self, tHandle): newHandle = self.theProject.newFile(docLabel, srcItem.itemParent)
return newItem = self.theProject.tree[newHandle]
newItem.setLayout(srcItem.itemLayout)
newItem.setStatus(srcItem.itemStatus)
newItem.setImport(srcItem.itemImport)
self._targetDoc = newHandle
self._targetText = []
return newHandle
def appendText(self, srcHandle, addComment, cmtPrefix):
"""Append text from an existing document to the text buffer.
"""
srcItem = self.theProject.tree[srcHandle]
if srcItem is None:
return False
inDoc = NWDoc(self.theProject, srcHandle)
docText = (inDoc.readDocument() or "").rstrip("\n")
if addComment:
docInfo = srcItem.describeMe("H0")
docSt, _ = srcItem.getImportStatus(incIcon=False)
cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n"
docText = cmtLine + docText
self._targetText.append(docText)
return True
def writeTargetDoc(self):
"""Write the accumulated text into the designated target
document, appending any existing text.
"""
if self._targetDoc is None:
return False
outDoc = NWDoc(self.theProject, self._targetDoc)
docText = (outDoc.readDocument() or "").rstrip("\n")
if docText:
self._targetText.insert(0, docText)
status = outDoc.writeDocument("\n\n".join(self._targetText))
if not status:
self._error = outDoc.getError()
return status
# END Class DocMerger # END Class DocMerger
+3 -3
View File
@@ -292,16 +292,16 @@ class NWItem():
return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, "")) return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, ""))
def getImportStatus(self): def getImportStatus(self, incIcon=True):
"""Return the relevant importance or status label and icon for """Return the relevant importance or status label and icon for
the current item based on its class. the current item based on its class.
""" """
if self.isNovelLike(): if self.isNovelLike():
stName = self.theProject.statusItems.name(self._status) stName = self.theProject.statusItems.name(self._status)
stIcon = self.theProject.statusItems.icon(self._status) stIcon = self.theProject.statusItems.icon(self._status) if incIcon else None
else: else:
stName = self.theProject.importItems.name(self._import) stName = self.theProject.importItems.name(self._import)
stIcon = self.theProject.importItems.icon(self._import) stIcon = self.theProject.importItems.icon(self._import) if incIcon else None
return stName, stIcon return stName, stIcon
## ##
+16
View File
@@ -88,6 +88,9 @@ class GuiDocMerge(QDialog):
self.buttonBox.accepted.connect(self.accept) self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject) self.buttonBox.rejected.connect(self.reject)
self.resetButton = self.buttonBox.addButton(QDialogButtonBox.Reset)
self.resetButton.clicked.connect(self._resetList)
# Assemble # Assemble
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(0) self.outerBox.setSpacing(0)
@@ -122,6 +125,19 @@ class GuiDocMerge(QDialog):
return self._data return self._data
##
# Slots
##
def _resetList(self):
"""Reset the content of the list box to its original state.
"""
logger.debug("Resetting list box content")
sHandle = self._data.get("sHandle", None)
itemList = self._data.get("origItems", [])
self._loadContent(sHandle, itemList)
return
## ##
# Internal Functions # Internal Functions
## ##
+64 -11
View File
@@ -39,7 +39,7 @@ from PyQt5.QtWidgets import (
QVBoxLayout, QWidget QVBoxLayout, QWidget
) )
from novelwriter.core import NWDoc from novelwriter.core import NWDoc, DocMerger
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert
from novelwriter.dialogs import GuiDocMerge, GuiEditLabel from novelwriter.dialogs import GuiDocMerge, GuiEditLabel
from novelwriter.constants import nwHeaders, trConst, nwLabels from novelwriter.constants import nwHeaders, trConst, nwLabels
@@ -770,10 +770,14 @@ class GuiProjectTree(QTreeWidget):
else: else:
# The item is not already in the trash folder, so we # The item is not already in the trash folder, so we
# move it there. # move it there.
msgYes = self.mainGui.askQuestion( if not alreadyAsked:
self.tr("Delete"), msgYes = self.mainGui.askQuestion(
self.tr("Move '{0}' to Trash?").format(nwItemS.itemName), self.tr("Delete"),
) self.tr("Move '{0}' to Trash?").format(nwItemS.itemName),
)
else:
msgYes = True
if msgYes: if msgYes:
logger.debug("Moving item '%s' to trash", tHandle) logger.debug("Moving item '%s' to trash", tHandle)
@@ -1114,13 +1118,17 @@ class GuiProjectTree(QTreeWidget):
if hasChild: if hasChild:
if isFile: if isFile:
mTrans.addAction( mTrans.addAction(
self.tr("Merge Child Documents"), self.tr("Merge Child Items into Self"),
lambda: self._mergeDocuments(tHandle, isFile) lambda: self._mergeDocuments(tHandle, False)
)
mTrans.addAction(
self.tr("Merge Child Items into New"),
lambda: self._mergeDocuments(tHandle, True)
) )
else: else:
mTrans.addAction( mTrans.addAction(
self.tr("Combine Documents in Folder"), self.tr("Combine Documents in Folder"),
lambda: self._mergeDocuments(tHandle, isFile) lambda: self._mergeDocuments(tHandle, True)
) )
if isFile: if isFile:
@@ -1377,18 +1385,63 @@ class GuiProjectTree(QTreeWidget):
logger.info("Folder conversion cancelled") logger.info("Folder conversion cancelled")
return return
def _mergeDocuments(self, tHandle, isFile): def _mergeDocuments(self, tHandle, newFile):
"""Merge an item's child documents into a single document. """Merge an item's child documents into a single document.
""" """
logger.info("Request to merge items under handle '%s'", tHandle) logger.info("Request to merge items under handle '%s'", tHandle)
itemList = self.getTreeFromHandle(tHandle) itemList = self.getTreeFromHandle(tHandle)
itemList.remove(tHandle)
tItem = self.theProject.tree[tHandle]
if tItem is None:
return
if not newFile:
itemList.remove(tHandle)
dlgMerge = GuiDocMerge(self.mainGui, tHandle, itemList) dlgMerge = GuiDocMerge(self.mainGui, tHandle, itemList)
dlgMerge.exec_() dlgMerge.exec_()
if dlgMerge.result() == QDialog.Accepted: if dlgMerge.result() == QDialog.Accepted:
print(dlgMerge.getData()) # Save the open document first, in case it's part of this
self.mainGui.saveDocument()
# Set up the merge job
mrgData = dlgMerge.getData()
docMerger = DocMerger(self.theProject)
mLabel = self.tr("Merged")
if newFile:
docLabel = f"[{mLabel}] {tItem.itemName}"
mHandle = docMerger.newTargetDoc(tHandle, docLabel)
elif tItem.isFileType():
docMerger.setTargetDoc(tHandle)
mHandle = tHandle
else:
return
for sHandle in mrgData.get("finalItems", []):
docMerger.appendText(sHandle, True, mLabel)
if not docMerger.writeTargetDoc():
self.mainGui.makeAlert([
self.tr("Could not save document."), docMerger.getError()
], nwAlert.ERROR)
return
if newFile:
self.mainGui.projView.revealNewTreeItem(mHandle, tHandle)
self.theProject.index.reIndexHandle(mHandle)
self.mainGui.openDocument(mHandle, doScroll=True)
if mrgData.get("moveToTrash", False):
for sHandle in reversed(mrgData.get("finalItems", [])):
trItem = self._getTreeItem(sHandle)
if isinstance(trItem, QTreeWidgetItem) and trItem.childCount() == 0:
self.deleteItem(sHandle, alreadyAsked=True, bulkAction=True)
self._alertTreeChange(sHandle, flush=False)
self._alertTreeChange(mHandle, flush=True)
return return