Added handling of orphaned documents.
This commit is contained in:
+13
-10
@@ -64,17 +64,20 @@ class GuiDocDetails(QFrame):
|
|||||||
|
|
||||||
def buildViewBox(self, tHandle):
|
def buildViewBox(self, tHandle):
|
||||||
|
|
||||||
nwItem = self.theProject.getItem(tHandle)
|
nwItem = self.theProject.getItem(tHandle)
|
||||||
itemStatus = nwItem.itemStatus
|
|
||||||
if itemStatus < 0 or itemStatus >= len(self.theProject.statusLabels):
|
|
||||||
itemStatus = 0
|
|
||||||
|
|
||||||
colTwo = [
|
if nwItem is None:
|
||||||
nwItem.itemName,
|
colTwo = [""]*4
|
||||||
self.theProject.statusLabels[itemStatus],
|
else:
|
||||||
nwLabels.CLASS_NAME[nwItem.itemClass],
|
itemStatus = nwItem.itemStatus
|
||||||
nwLabels.LAYOUT_NAME[nwItem.itemLayout],
|
if itemStatus < 0 or itemStatus >= len(self.theProject.statusLabels):
|
||||||
]
|
itemStatus = 0
|
||||||
|
colTwo = [
|
||||||
|
nwItem.itemName,
|
||||||
|
self.theProject.statusLabels[itemStatus],
|
||||||
|
nwLabels.CLASS_NAME[nwItem.itemClass],
|
||||||
|
nwLabels.LAYOUT_NAME[nwItem.itemLayout],
|
||||||
|
]
|
||||||
|
|
||||||
for nRow in range(4):
|
for nRow in range(4):
|
||||||
lblTwo = QLabel(colTwo[nRow])
|
lblTwo = QLabel(colTwo[nRow])
|
||||||
|
|||||||
+26
-6
@@ -40,6 +40,7 @@ class GuiDocTree(QTreeWidget):
|
|||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
self.theMap = {}
|
self.theMap = {}
|
||||||
|
self.orphRoot = None
|
||||||
|
|
||||||
self.setIconSize(QSize(13,13))
|
self.setIconSize(QSize(13,13))
|
||||||
self.setExpandsOnDoubleClick(True)
|
self.setExpandsOnDoubleClick(True)
|
||||||
@@ -152,6 +153,8 @@ class GuiDocTree(QTreeWidget):
|
|||||||
def saveTreeOrder(self):
|
def saveTreeOrder(self):
|
||||||
theList = []
|
theList = []
|
||||||
for i in range(self.topLevelItemCount()):
|
for i in range(self.topLevelItemCount()):
|
||||||
|
if self.topLevelItem(i) == self.orphRoot:
|
||||||
|
continue
|
||||||
theList = self._scanChildren(theList, self.topLevelItem(i), i)
|
theList = self._scanChildren(theList, self.topLevelItem(i), i)
|
||||||
self.theProject.setTreeOrder(theList)
|
self.theProject.setTreeOrder(theList)
|
||||||
return True
|
return True
|
||||||
@@ -200,7 +203,12 @@ class GuiDocTree(QTreeWidget):
|
|||||||
|
|
||||||
self.theMap[tHandle] = newItem
|
self.theMap[tHandle] = newItem
|
||||||
if pHandle is None:
|
if pHandle is None:
|
||||||
self.addTopLevelItem(newItem)
|
if nwItem.itemType == nwItemType.ROOT:
|
||||||
|
self.addTopLevelItem(newItem)
|
||||||
|
self.theParent.mainMenu.setAvailableRoot()
|
||||||
|
else:
|
||||||
|
self._addOrphanedRoot()
|
||||||
|
self.orphRoot.addChild(newItem)
|
||||||
else:
|
else:
|
||||||
self.theMap[pHandle].addChild(newItem)
|
self.theMap[pHandle].addChild(newItem)
|
||||||
self.propagateCount(tHandle, nwItem.wordCount)
|
self.propagateCount(tHandle, nwItem.wordCount)
|
||||||
@@ -210,7 +218,6 @@ class GuiDocTree(QTreeWidget):
|
|||||||
|
|
||||||
if nwItem.itemType == nwItemType.ROOT:
|
if nwItem.itemType == nwItemType.ROOT:
|
||||||
newItem.setIcon(self.C_NAME, QIcon.fromTheme("drive-harddisk"))
|
newItem.setIcon(self.C_NAME, QIcon.fromTheme("drive-harddisk"))
|
||||||
self.theParent.mainMenu.setAvailableRoot()
|
|
||||||
elif nwItem.itemType == nwItemType.FOLDER:
|
elif nwItem.itemType == nwItemType.FOLDER:
|
||||||
newItem.setIcon(self.C_NAME, QIcon.fromTheme("folder"))
|
newItem.setIcon(self.C_NAME, QIcon.fromTheme("folder"))
|
||||||
elif nwItem.itemType == nwItemType.FILE:
|
elif nwItem.itemType == nwItemType.FILE:
|
||||||
@@ -218,6 +225,18 @@ class GuiDocTree(QTreeWidget):
|
|||||||
|
|
||||||
return newItem
|
return newItem
|
||||||
|
|
||||||
|
def _addOrphanedRoot(self):
|
||||||
|
if self.orphRoot is None:
|
||||||
|
newItem = QTreeWidgetItem([""]*4)
|
||||||
|
newItem.setText(self.C_NAME, "Orphaned Files")
|
||||||
|
newItem.setText(self.C_COUNT, "")
|
||||||
|
newItem.setText(self.C_FLAGS, "")
|
||||||
|
newItem.setText(self.C_HANDLE, "")
|
||||||
|
self.addTopLevelItem(newItem)
|
||||||
|
self.orphRoot = newItem
|
||||||
|
newItem.setExpanded(True)
|
||||||
|
return
|
||||||
|
|
||||||
def setTreeItemValues(self, tHandle):
|
def setTreeItemValues(self, tHandle):
|
||||||
|
|
||||||
trItem = self.theMap[tHandle]
|
trItem = self.theMap[tHandle]
|
||||||
@@ -248,7 +267,7 @@ class GuiDocTree(QTreeWidget):
|
|||||||
for i in range(pItem.childCount()):
|
for i in range(pItem.childCount()):
|
||||||
pCount += int(pItem.child(i).text(self.C_COUNT))
|
pCount += int(pItem.child(i).text(self.C_COUNT))
|
||||||
pHandle = pItem.text(self.C_HANDLE)
|
pHandle = pItem.text(self.C_HANDLE)
|
||||||
if not nDepth > NWItem.MAX_DEPTH:
|
if not nDepth > NWItem.MAX_DEPTH and pHandle != "":
|
||||||
self.propagateCount(pHandle, pCount, nDepth+1)
|
self.propagateCount(pHandle, pCount, nDepth+1)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -298,14 +317,15 @@ class GuiDocTree(QTreeWidget):
|
|||||||
snItem = self.theProject.getItem(sHandle)
|
snItem = self.theProject.getItem(sHandle)
|
||||||
dnItem = self.theProject.getItem(dHandle)
|
dnItem = self.theProject.getItem(dHandle)
|
||||||
isSame = snItem.itemClass == dnItem.itemClass and dnItem.itemType
|
isSame = snItem.itemClass == dnItem.itemClass and dnItem.itemType
|
||||||
|
isNone = snItem.itemClass == nwItemClass.NO_CLASS
|
||||||
isFile = dnItem.itemType == nwItemType.FILE
|
isFile = dnItem.itemType == nwItemType.FILE
|
||||||
isRoot = snItem.itemType == nwItemType.ROOT
|
isRoot = snItem.itemType == nwItemType.ROOT
|
||||||
isOnTop = self.dropIndicatorPosition() == QAbstractItemView.OnItem
|
isOnTop = self.dropIndicatorPosition() == QAbstractItemView.OnItem
|
||||||
if isSame and not (isFile and isOnTop) and not isRoot:
|
if (isSame or isNone) and not (isFile and isOnTop) and not isRoot:
|
||||||
logger.verbose("Drag'n'drop on item %s allowed" % sHandle)
|
logger.verbose("Drag'n'drop of item %s accepted" % sHandle)
|
||||||
QTreeWidget.dropEvent(self, theEvent)
|
QTreeWidget.dropEvent(self, theEvent)
|
||||||
else:
|
else:
|
||||||
logger.verbose("Drag'n'drop on item %s not allowed" % sHandle)
|
logger.verbose("Drag'n'drop of item %s not accepted" % sHandle)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
+53
-1
@@ -13,7 +13,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import nw
|
import nw
|
||||||
|
|
||||||
from os import path, mkdir
|
from os import path, mkdir, listdir
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -167,6 +167,8 @@ class NWProject():
|
|||||||
self._makeStatusIcons()
|
self._makeStatusIcons()
|
||||||
self.mainConf.setRecent(self.projPath)
|
self.mainConf.setRecent(self.projPath)
|
||||||
|
|
||||||
|
self._scanProjectFolder()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveProject(self):
|
def saveProject(self):
|
||||||
@@ -287,6 +289,56 @@ class NWProject():
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def _scanProjectFolder(self):
|
||||||
|
|
||||||
|
if self.projPath is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# First, scan the project data folders
|
||||||
|
itemList = []
|
||||||
|
for subItem in listdir(self.projPath):
|
||||||
|
if subItem[:5] != "data_":
|
||||||
|
continue
|
||||||
|
dataDir = path.join(self.projPath,subItem)
|
||||||
|
for subFile in listdir(dataDir):
|
||||||
|
if subFile[-4:] == ".nwd":
|
||||||
|
newItem = path.join(subItem,subFile)
|
||||||
|
itemList.append(newItem)
|
||||||
|
|
||||||
|
# Then check the valid files
|
||||||
|
orphanFiles = []
|
||||||
|
for fileItem in itemList:
|
||||||
|
if len(fileItem) != 28:
|
||||||
|
# Just to be safe, shouldn't happen
|
||||||
|
logger.warning("Skipping file %s" % fileItem)
|
||||||
|
continue
|
||||||
|
fHandle = fileItem[5]+fileItem[7:19]
|
||||||
|
if fHandle in self.treeOrder:
|
||||||
|
logger.verbose("Checking file %s, handle %s: OK" % (fileItem,fHandle))
|
||||||
|
else:
|
||||||
|
logger.verbose("Checking file %s, handle %s: Orphaned" % (fileItem,fHandle))
|
||||||
|
orphanFiles.append(fHandle)
|
||||||
|
|
||||||
|
# Report status
|
||||||
|
if len(orphanFiles) > 0:
|
||||||
|
logger.warning("Found %d orphaned file(s) in project folder!" % len(orphanFiles))
|
||||||
|
else:
|
||||||
|
logger.verbose("File check OK")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Handle orphans
|
||||||
|
nOrph = 0
|
||||||
|
for oHandle in orphanFiles:
|
||||||
|
nOrph += 1
|
||||||
|
orItem = NWItem()
|
||||||
|
orItem.setName("Orphaned File %d" % nOrph)
|
||||||
|
orItem.setType(nwItemType.FILE)
|
||||||
|
orItem.setClass(nwItemClass.NO_CLASS)
|
||||||
|
orItem.setLayout(nwItemLayout.NO_LAYOUT)
|
||||||
|
self._appendItem(oHandle,None,orItem)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def _countItemDepth(self, tHandle):
|
def _countItemDepth(self, tHandle):
|
||||||
theDepth = 0
|
theDepth = 0
|
||||||
nwItem = self.getItem(tHandle)
|
nwItem = self.getItem(tHandle)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
# John Smith
|
# John Smith
|
||||||
|
|
||||||
He’s pretty cool. Nor Brad Pitt though.
|
He’s pretty cool. Not Brad Pitt though.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# My Novel
|
||||||
|
|
||||||
|
**By Jane Doh**
|
||||||
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
# Earth
|
# Earth
|
||||||
|
|
||||||
Third planet from the sun, fairly dense and with lots of people on it.
|
Third planet from the sun, fairly dense, and with lots of people on it.
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="0.0.1" fileVersion="1.0" timeStamp="2019-04-28 13:15:28">
|
<novelWriterXML appVersion="0.0.1" fileVersion="1.0" timeStamp="2019-04-28 14:03:05">
|
||||||
<project>
|
<project>
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
</project>
|
</project>
|
||||||
<content count="9">
|
<content count="10">
|
||||||
<item handle="7031beac91f75" order="0" parent="None">
|
<item handle="7031beac91f75" order="0" parent="None">
|
||||||
<name>Novel</name>
|
<name>Novel</name>
|
||||||
<type>ROOT</type>
|
<type>ROOT</type>
|
||||||
|
|||||||
Reference in New Issue
Block a user