Remove max folder depth restriction, and simplify adding folders and files in the tree
This commit is contained in:
@@ -42,7 +42,6 @@ class nwConst():
|
||||
FMT_DSTAMP = "%Y-%m-%d" # Date only format
|
||||
|
||||
# Various Hard Limits
|
||||
MAX_DEPTH = 30 # Maximum folder depth of a project
|
||||
MAX_DOCSIZE = 5000000 # Maxium size of a single document
|
||||
MAX_BUILDSIZE = 10000000 # Maxium size of a project build
|
||||
|
||||
|
||||
@@ -308,7 +308,9 @@ class NWItem():
|
||||
"""Set the default values based on the item's class and the
|
||||
project settings.
|
||||
"""
|
||||
self.setClass(itemClass)
|
||||
if self._parent is not None:
|
||||
# Only update for child items
|
||||
self.setClass(itemClass)
|
||||
|
||||
if self._class in nwLists.CLS_NOVEL:
|
||||
self._layout = nwItemLayout.DOCUMENT
|
||||
|
||||
@@ -33,7 +33,7 @@ from hashlib import sha256
|
||||
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import checkHandle
|
||||
from novelwriter.constants import nwConst, nwFiles
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.core.item import NWItem
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -41,6 +41,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class NWTree():
|
||||
|
||||
MAX_DEPTH = 1000 # Cap of tree traversing for loops
|
||||
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.theProject = theProject
|
||||
@@ -219,7 +221,7 @@ class NWTree():
|
||||
return False
|
||||
|
||||
iItem = tItem
|
||||
for _ in range(nwConst.MAX_DEPTH + 1):
|
||||
for _ in range(self.MAX_DEPTH):
|
||||
if iItem.itemParent is None:
|
||||
tItem.setRoot(iItem.itemHandle)
|
||||
tItem.setClassDefaults(iItem.itemClass)
|
||||
@@ -228,8 +230,8 @@ class NWTree():
|
||||
iItem = self.__getitem__(iItem.itemParent)
|
||||
if iItem is None:
|
||||
return False
|
||||
|
||||
return False
|
||||
else:
|
||||
raise RecursionError("Critical internal error")
|
||||
|
||||
def checkType(self, tHandle, itemType):
|
||||
"""Return true of item exists and is of the specified item type.
|
||||
@@ -249,7 +251,7 @@ class NWTree():
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if tItem is not None:
|
||||
tTree.append(tHandle)
|
||||
for _ in range(nwConst.MAX_DEPTH + 1):
|
||||
for _ in range(self.MAX_DEPTH):
|
||||
if tItem.itemParent is None:
|
||||
return tTree
|
||||
else:
|
||||
@@ -259,6 +261,9 @@ class NWTree():
|
||||
return tTree
|
||||
else:
|
||||
tTree.append(tHandle)
|
||||
else:
|
||||
raise RecursionError("Critical internal error")
|
||||
|
||||
return tTree
|
||||
|
||||
##
|
||||
@@ -270,6 +275,23 @@ class NWTree():
|
||||
"""
|
||||
return tHandle in self._treeRoots
|
||||
|
||||
def isTrash(self, tHandle):
|
||||
"""Check if an item is in or is the trash folder.
|
||||
"""
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if tItem is None:
|
||||
return True
|
||||
if tItem.itemClass == nwItemClass.TRASH:
|
||||
return True
|
||||
if self._trashRoot is not None:
|
||||
if tHandle == self._trashRoot:
|
||||
return True
|
||||
elif tItem.itemParent == self._trashRoot:
|
||||
return True
|
||||
elif tItem.itemRoot == self._trashRoot:
|
||||
return True
|
||||
return False
|
||||
|
||||
def isTrashRoot(self, tHandle):
|
||||
"""Check if a handle is the trash folder.
|
||||
"""
|
||||
|
||||
@@ -34,7 +34,6 @@ from PyQt5.QtWidgets import (
|
||||
|
||||
from novelwriter.core import NWDoc
|
||||
from novelwriter.enum import nwAlert, nwItemType
|
||||
from novelwriter.constants import nwConst
|
||||
from novelwriter.gui.custom import QHelpLabel
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -160,16 +159,6 @@ class GuiDocSplit(QDialog):
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
# Check that another folder can be created
|
||||
parTree = self.theProject.projTree.getItemPath(srcItem.itemParent)
|
||||
if len(parTree) >= nwConst.MAX_DEPTH - 1:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Cannot add new folder for the document split. "
|
||||
"Maximum folder depth has been reached. "
|
||||
"Please move the file to another level in the project tree."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
msgYes = self.theParent.askQuestion(
|
||||
self.tr("Split Document"),
|
||||
"{0}<br><br>{1}".format(
|
||||
|
||||
@@ -197,7 +197,7 @@ class GuiMainMenu(QMenuBar):
|
||||
# Project > New Folder
|
||||
self.aCreateFolder = QAction(self.tr("Create Folder"), self)
|
||||
self.aCreateFolder.setShortcut("Ctrl+Shift+N")
|
||||
self.aCreateFolder.triggered.connect(lambda: self._newTreeItem(nwItemType.FOLDER, None))
|
||||
self.aCreateFolder.triggered.connect(lambda: self._newTreeItem(nwItemType.FOLDER))
|
||||
self.projMenu.addAction(self.aCreateFolder)
|
||||
|
||||
# Project > Separator
|
||||
@@ -259,7 +259,7 @@ class GuiMainMenu(QMenuBar):
|
||||
# Document > New
|
||||
self.aNewDoc = QAction(self.tr("New Document"), self)
|
||||
self.aNewDoc.setShortcut("Ctrl+N")
|
||||
self.aNewDoc.triggered.connect(lambda: self._newTreeItem(nwItemType.FILE, None))
|
||||
self.aNewDoc.triggered.connect(lambda: self._newTreeItem(nwItemType.FILE))
|
||||
self.docuMenu.addAction(self.aNewDoc)
|
||||
|
||||
# Document > Open
|
||||
|
||||
+37
-90
@@ -37,7 +37,7 @@ from PyQt5.QtWidgets import (
|
||||
|
||||
from novelwriter.core import NWDoc
|
||||
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||
from novelwriter.constants import nwConst, trConst, nwLists, nwLabels
|
||||
from novelwriter.constants import trConst, nwLists, nwLabels
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -161,114 +161,64 @@ class GuiProjectTree(QTreeWidget):
|
||||
self._timeChanged = 0
|
||||
return
|
||||
|
||||
def newTreeItem(self, itemType, itemClass):
|
||||
"""Add new item to the tree, with a given itemType and
|
||||
itemClass, and attach it to the selected handle. Also make sure
|
||||
the item is added in a place it can be added, and that other
|
||||
def newTreeItem(self, itemType, itemClass=None):
|
||||
"""Add new item to the tree, with a given itemType (and
|
||||
itemClass if Root), and attach it to the selected handle. Also make
|
||||
sure the item is added in a place it can be added, and that other
|
||||
meta data is set correctly to ensure a valid project tree.
|
||||
"""
|
||||
pHandle = self.getSelectedHandle()
|
||||
nHandle = None
|
||||
|
||||
if not self.theParent.hasProject:
|
||||
logger.error("No project open")
|
||||
return False
|
||||
|
||||
if not isinstance(itemType, nwItemType):
|
||||
# This would indicate an internal bug
|
||||
logger.error("No itemType provided")
|
||||
return False
|
||||
nHandle = None
|
||||
tHandle = None
|
||||
|
||||
# The item needs to be assigned an item class, so one must be
|
||||
# provided, or it must be possible to extract it from the parent
|
||||
# item of the new item.
|
||||
if itemClass is None and pHandle is not None:
|
||||
pItem = self.theProject.projTree[pHandle]
|
||||
if pItem is not None:
|
||||
itemClass = pItem.itemClass
|
||||
if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
|
||||
|
||||
# If class is still not set, alert the user and exit
|
||||
if itemClass is None:
|
||||
if itemType == nwItemType.FILE:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Please select a valid location in the tree to add the document."
|
||||
), nwAlert.ERROR)
|
||||
else:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Please select a valid location in the tree to add the folder."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
# Everything is fine, we have what we need, so we proceed
|
||||
logger.verbose(
|
||||
"Adding new item of type '%s' and class '%s' to handle '%s'",
|
||||
itemType.name, itemClass.name, str(pHandle)
|
||||
)
|
||||
|
||||
if itemType == nwItemType.ROOT:
|
||||
tHandle = self.theProject.newRoot(
|
||||
trConst(nwLabels.CLASS_NAME[itemClass]), itemClass
|
||||
)
|
||||
if tHandle is None:
|
||||
logger.error("No root item added")
|
||||
return False
|
||||
|
||||
else:
|
||||
# If no parent has been selected, make the new file under
|
||||
# the root NOVEL item.
|
||||
if pHandle is None:
|
||||
pHandle = self.theProject.projTree.findRoot(nwItemClass.NOVEL)
|
||||
elif itemType in (nwItemType.FILE, nwItemType.FOLDER):
|
||||
|
||||
# If still nothing, give up
|
||||
if pHandle is None:
|
||||
sHandle = self.getSelectedHandle()
|
||||
if sHandle is None or sHandle not in self.theProject.projTree:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Did not find anywhere to add the file or folder!"
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
# Now check if the selected item is a file, in which case
|
||||
# the new file will be a sibling
|
||||
pItem = self.theProject.projTree[pHandle]
|
||||
# If the selected item is a file, the new item will be a sibling
|
||||
pItem = self.theProject.projTree[sHandle]
|
||||
if pItem.itemType == nwItemType.FILE:
|
||||
nHandle = pHandle
|
||||
pHandle = pItem.itemParent
|
||||
nHandle = sHandle
|
||||
sHandle = pItem.itemParent
|
||||
if sHandle is None:
|
||||
logger.error("Internal error") # Bug
|
||||
return False
|
||||
|
||||
# If we again have no home, give up
|
||||
if pHandle is None:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Did not find anywhere to add the file or folder!"
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
if self.theProject.projTree.isTrashRoot(pHandle):
|
||||
if self.theProject.projTree.isTrash(sHandle):
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Cannot add new files or folders to the Trash folder."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
parTree = self.theProject.projTree.getItemPath(pHandle)
|
||||
|
||||
# If we're still here, add the file or folder
|
||||
# Add the file or folder
|
||||
if itemType == nwItemType.FILE:
|
||||
tHandle = self.theProject.newFile(self.tr("New File"), pHandle)
|
||||
|
||||
if pItem.itemClass in nwLists.CLS_NOVEL:
|
||||
tHandle = self.theProject.newFile(self.tr("New Document"), sHandle)
|
||||
else:
|
||||
tHandle = self.theProject.newFile(self.tr("New Note"), sHandle)
|
||||
elif itemType == nwItemType.FOLDER:
|
||||
if len(parTree) >= nwConst.MAX_DEPTH - 1:
|
||||
# Folders cannot be deeper than MAX_DEPTH - 1, leaving room
|
||||
# for one more level of files.
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Cannot add new folder to this item. "
|
||||
"Maximum folder depth has been reached."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
tHandle = self.theProject.newFolder(self.tr("New Folder"), pHandle)
|
||||
tHandle = self.theProject.newFolder(self.tr("New Folder"), sHandle)
|
||||
|
||||
else:
|
||||
logger.error("Failed to add new item")
|
||||
return False
|
||||
else:
|
||||
logger.error("Failed to add new item")
|
||||
return False
|
||||
|
||||
# If there is no handle set, return here
|
||||
if tHandle is None:
|
||||
# If there is no handle set, return here. This is a bug
|
||||
if tHandle is None: # pragma: no cover
|
||||
return True
|
||||
|
||||
# Add the new item to the tree
|
||||
@@ -282,11 +232,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
# This is a new file, so let's add some content
|
||||
newDoc = NWDoc(self.theProject, tHandle)
|
||||
curTxt = newDoc.readDocument()
|
||||
if curTxt is None:
|
||||
curTxt = ""
|
||||
|
||||
if curTxt == "":
|
||||
if not newDoc.readDocument():
|
||||
if nwItem.itemLayout == nwItemLayout.DOCUMENT:
|
||||
newText = f"### {nwItem.itemName}\n\n"
|
||||
else:
|
||||
@@ -633,7 +579,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
return
|
||||
|
||||
def propagateCount(self, tHandle, theCount, nDepth=0):
|
||||
def propagateCount(self, tHandle, theCount):
|
||||
"""Recursive function setting the word count for a given item,
|
||||
and propagating that count upwards in the tree until reaching a
|
||||
root item. This function is more efficient than recalculating
|
||||
@@ -653,12 +599,13 @@ class GuiProjectTree(QTreeWidget):
|
||||
return
|
||||
|
||||
pCount = 0
|
||||
pHandle = None
|
||||
for i in range(pItem.childCount()):
|
||||
pCount += int(pItem.child(i).data(self.C_COUNT, Qt.UserRole))
|
||||
pHandle = pItem.data(self.C_NAME, Qt.UserRole)
|
||||
|
||||
if not nDepth > nwConst.MAX_DEPTH + 1 and pHandle != "":
|
||||
self.propagateCount(pHandle, pCount, nDepth+1)
|
||||
if pHandle:
|
||||
self.propagateCount(pHandle, pCount)
|
||||
|
||||
return
|
||||
|
||||
@@ -1180,7 +1127,7 @@ class GuiProjectTreeMenu(QMenu):
|
||||
"""Forward the new file call to the project tree.
|
||||
"""
|
||||
if self.theItem is not None:
|
||||
self.theTree.newTreeItem(nwItemType.FILE, None)
|
||||
self.theTree.newTreeItem(nwItemType.FILE)
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
@@ -1188,7 +1135,7 @@ class GuiProjectTreeMenu(QMenu):
|
||||
"""Forward the new folder call to the project tree.
|
||||
"""
|
||||
if self.theItem is not None:
|
||||
self.theTree.newTreeItem(nwItemType.FOLDER, None)
|
||||
self.theTree.newTreeItem(nwItemType.FOLDER)
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
|
||||
Reference in New Issue
Block a user