diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py index 63a998dd..30a369e1 100644 --- a/nw/gui/elements/doctree.py +++ b/nw/gui/elements/doctree.py @@ -16,10 +16,10 @@ import nw from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QFont, QColor from PyQt5.QtWidgets import ( - QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication + QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication, QMessageBox ) -from nw.project import NWItem +from nw.project import NWItem, NWDoc from nw.constants import ( nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert ) @@ -263,21 +263,65 @@ class GuiDocTree(QTreeWidget): trItemS = self._getTreeItem(tHandle) nwItemS = self.theProject.getItem(tHandle) + if nwItemS is None: + return False + if nwItemS.itemType == nwItemType.FILE: logger.debug("User requested file %s moved to trash" % tHandle) trItemP = trItemS.parent() trItemT = self._addTrashRoot() if trItemP is None or trItemT is None: - logger.error("Could not move item to trash") + logger.error("Could not delete item") return False - tIndex = trItemP.indexOfChild(trItemS) - trItemC = trItemP.takeChild(tIndex) - trItemT.addChild(trItemC) - nwItemS.setParent(self.theProject.trashRoot) - self.clearSelection() - trItemP.setSelected(True) - self.theProject.setProjectChanged(True) - self.theParent.theIndex.deleteHandle(tHandle) + + pHandle = nwItemS.parHandle + if pHandle is not None and pHandle == self.theProject.trashRoot: + # If the file is in the trash folder already, as the + # user if they want to permanently delete the file. + + doPermanent = False + if self.mainConf.showGUI: + msgBox = QMessageBox() + msgRes = msgBox.question( + self, "Delete File", "Permanently delete file '%s'?" % nwItemS.itemName + ) + if msgRes == QMessageBox.Yes: + doPermanent = True + else: + doPermanent = True + + if doPermanent: + logger.debug("Permanently deleting file with handle %s" % tHandle) + + tIndex = trItemP.indexOfChild(trItemS) + trItemC = trItemP.takeChild(tIndex) + self.clearSelection() + trItemP.setSelected(True) + + if self.theParent.docEditor.theHandle == tHandle: + self.theParent.closeDocument() + + theDoc = NWDoc(self.theProject, self.theParent) + theDoc.deleteDocument(tHandle) + self.theProject.deleteItem(tHandle) + self.theParent.theIndex.deleteHandle(tHandle) + + else: + # The file is not already in the trash folder, so we + # move it there. + + if pHandle is None: + logger.warning("File has no parent item") + + tIndex = trItemP.indexOfChild(trItemS) + trItemC = trItemP.takeChild(tIndex) + trItemT.addChild(trItemC) + nwItemS.setParent(self.theProject.trashRoot) + self.clearSelection() + trItemP.setSelected(True) + + self.theProject.setProjectChanged(True) + self.theParent.theIndex.deleteHandle(tHandle) elif nwItemS.itemType == nwItemType.FOLDER: logger.debug("User requested folder %s deleted" % tHandle) @@ -445,6 +489,9 @@ class GuiDocTree(QTreeWidget): return newItem def _addTrashRoot(self): + """Adds the trash root folder if it doesn't already exist in the + project tree. + """ if self.theProject.trashRoot is None: self.theProject.addTrash() trItem = self._addTreeItem( diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 061b5e2d..580aacaf 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -370,8 +370,8 @@ class GuiMainMenu(QMenuBar): self.docuMenu.addAction(self.aImportFile) # Document > Merge Documents - self.aMergeDocs = QAction("Merge Documents", self) - self.aMergeDocs.setStatusTip("Merge multiple documents") + self.aMergeDocs = QAction("Merge Folder to Document", self) + self.aMergeDocs.setStatusTip("Merge a folder of documents to a single document") # self.aMergeDocs.setShortcut("Ctrl+Shift+I") self.aMergeDocs.triggered.connect(self.theParent.mergeDocuments) self.docuMenu.addAction(self.aMergeDocs) diff --git a/nw/project/document.py b/nw/project/document.py index 0cc0fa66..e2a46d65 100644 --- a/nw/project/document.py +++ b/nw/project/document.py @@ -59,7 +59,7 @@ class NWDoc(): if self.theItem.parHandle == self.theProject.trashRoot: self.docEditable = False - docDir, docFile = self._assemblePath(self.FILE_MN) + docDir, docFile = self.assemblePath(self.docHandle, self.FILE_MN) self.fileLoc = path.join(docDir,docFile) logger.debug("Opening document %s" % self.fileLoc) dataDir = path.join(self.theProject.projPath, docDir) @@ -92,7 +92,7 @@ class NWDoc(): if self.docHandle is None or not self.docEditable: return False - docDir, docFile = self._assemblePath(self.FILE_MN) + docDir, docFile = self.assemblePath(self.docHandle, self.FILE_MN) logger.debug("Saving document %s" % path.join(docDir,docFile)) dataPath = path.join(self.theProject.projPath, docDir) docPath = path.join(dataPath, docFile) @@ -124,15 +124,32 @@ class NWDoc(): return True - ## - # Internal Functions - ## + def deleteDocument(self, tHandle): + """Permanently delete a document source file and its backups + from the project data folder. + """ + docDir, docFile = self.assemblePath(tHandle, self.FILE_MN) + dataPath = path.join(self.theProject.projPath, docDir) + chkList = [] + chkList.append(path.join(dataPath, docFile)) + chkList.append(path.join(dataPath,docFile[:-3]+"tmp")) + chkList.append(path.join(dataPath,docFile[:-3]+"bak")) + for chkFile in chkList: + if path.isfile(chkFile): + try: + unlink(chkFile) + logger.debug("Deleted: %s" % chkFile) + except Exception as e: + self.makeAlert(["Could not delete document file.",str(e)], nwAlert.ERROR) + return False + return True - def _assemblePath(self, docExt): - if self.docHandle is None: + @staticmethod + def assemblePath(tHandle, docExt): + if tHandle is None: return None - docDir = "data_"+self.docHandle[0] - docFile = self.docHandle[1:13]+"_"+docExt + docDir = "data_"+tHandle[0] + docFile = tHandle[1:13]+"_"+docExt return docDir, docFile # END Class NWDoc diff --git a/nw/project/project.py b/nw/project/project.py index 2a88333b..a52a83f3 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -13,7 +13,7 @@ import logging import nw -from os import path, mkdir, listdir +from os import path, mkdir, listdir, unlink from shutil import copyfile from lxml import etree from hashlib import sha256 @@ -369,39 +369,6 @@ class NWProject(): self.clearProject() return True - ## - # Document Methods - ## - - def splitDocument(self, tHandle, headerLevel, folderLevel): - """Split a document into multiple documents under the same - header. Header level threshold determines at what level the - splitting should occur, and folders can also be created at a - certain structure level. - """ - - return True - - def mergeDocuments(self, handleList): - """Merge a list of document handles into a single document. - """ - - fileList = [] - for tHandle in handleList: - tItem = self.getItem(tHandle) - if tItem is None: - continue - if tItem.itemType == nwItemType.FILE: - fileList.append(tHandle) - else: - pass - - mergeText = "" - for tHandle in fileList: - pass - - return True - ## # Set Functions ## @@ -485,9 +452,6 @@ class NWProject(): self.setProjectChanged(True) return True - def getSessionWordCount(self): - return self.currWCount - self.lastWCount - def setStatusColours(self, newCols): replaceMap = self.statusItems.setNewEntries(newCols) if self.projTree is not None: @@ -530,6 +494,9 @@ class NWProject(): logger.error("No tree item with handle %s" % str(tHandle)) return None + def getSessionWordCount(self): + return self.currWCount - self.lastWCount + def getRootItem(self, tHandle): """Iterate upwards in the tree until we find the item with parent None, the root item. We do this with a for loop with a @@ -538,10 +505,13 @@ class NWProject(): tItem = self.getItem(tHandle) if tItem is not None: for i in range(200): - tHandle = tItem.parHandle - tItem = self.getItem(tHandle) - if tItem is None: + if tItem.parHandle is None: return tHandle + else: + tHandle = tItem.parHandle + tItem = self.getItem(tHandle) + if tItem is None: + return tHandle return None def getProjectItems(self):