diff --git a/novelwriter/constants.py b/novelwriter/constants.py index 796d0145..dbdc3664 100644 --- a/novelwriter/constants.py +++ b/novelwriter/constants.py @@ -25,7 +25,7 @@ along with this program. If not, see . from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP -from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType, nwOutline +from novelwriter.enum import nwItemClass, nwItemLayout, nwOutline def trConst(tString): @@ -51,18 +51,9 @@ class nwConst(): class nwLists(): """Lists used for grouping various other constants. """ - # Regular user-accessible item types - REG_TYPES = {nwItemType.ROOT, nwItemType.FOLDER, nwItemType.FILE} - - # Item classes where the full list of novel layouts are allowed - CLS_NOVEL = {nwItemClass.NOVEL, nwItemClass.ARCHIVE} - # Item classes which do not require items to have same class FREE_CLASS = {nwItemClass.ARCHIVE, nwItemClass.TRASH} - # Deprecated nwItemLayout entries - DEP_LAYOUT = ("TITLE", "PAGE", "BOOK", "PARTITION", "UNNUMBERED", "CHAPTER", "SCENE") - # END Class nwLists diff --git a/novelwriter/core/item.py b/novelwriter/core/item.py index 893b3965..3a741f62 100644 --- a/novelwriter/core/item.py +++ b/novelwriter/core/item.py @@ -31,10 +31,13 @@ from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout from novelwriter.common import ( checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified ) -from novelwriter.constants import nwLabels, nwLists, trConst +from novelwriter.constants import nwLabels, trConst logger = logging.getLogger(__name__) +# Deprecated layout labels +DEP_LAYOUTS = ("TITLE", "PAGE", "BOOK", "PARTITION", "UNNUMBERED", "CHAPTER", "SCENE") + class NWItem(): @@ -282,11 +285,27 @@ class NWItem(): return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, "")) + def isNovelLike(self): + """Returns true if the item is of a novel-like class. + """ + return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE) + + def documentAllowed(self): + """Returns true if the item is allowed to be of document layout. + """ + return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE, nwItemClass.TRASH) + + def isInactive(self): + """Returns true if the item is in the inactive parts of the + project. + """ + return self._class in (nwItemClass.NO_CLASS, nwItemClass.ARCHIVE, nwItemClass.TRASH) + def getImportStatus(self): """Return the relevant importance or status label and icon for the current item based on its class. """ - if self._class in nwLists.CLS_NOVEL: + if self.isNovelLike(): stName = self.theProject.statusItems.name(self._status) stIcon = self.theProject.statusItems.icon(self._status) else: @@ -298,7 +317,7 @@ class NWItem(): """Update the importance or status value based on class. This is a wrapper setter for setStatus and setImport. """ - if self._class in nwLists.CLS_NOVEL: + if self.isNovelLike(): self.setStatus(value) else: self.setImport(value) @@ -312,9 +331,14 @@ class NWItem(): # Only update for child items self.setClass(itemClass) - if self._class in nwLists.CLS_NOVEL: - self._layout = nwItemLayout.DOCUMENT - else: + if self._layout == nwItemLayout.NO_LAYOUT: + # If no layout is set, pick one + if self.isNovelLike(): + self._layout = nwItemLayout.DOCUMENT + else: + self._layout = nwItemLayout.NOTE + elif not self.documentAllowed(): + # Change layout to note if it is not in an allowed folder self._layout = nwItemLayout.NOTE if self._status is None: @@ -411,7 +435,7 @@ class NWItem(): self._layout = itemLayout elif isItemLayout(itemLayout): self._layout = nwItemLayout[itemLayout] - elif itemLayout in nwLists.DEP_LAYOUT: + elif itemLayout in DEP_LAYOUTS: self._layout = nwItemLayout.DOCUMENT else: logger.error("Unrecognised item layout '%s'", itemLayout) diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index 28aba590..0d24d74c 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -46,7 +46,7 @@ from novelwriter.common import ( checkString, checkBool, checkInt, isHandle, formatTimeStamp, makeFileNameSafe, hexToInt, simplified ) -from novelwriter.constants import nwLists, trConst, nwFiles, nwLabels +from novelwriter.constants import trConst, nwFiles, nwLabels logger = logging.getLogger(__name__) @@ -1197,7 +1197,7 @@ class NWProject(): self.statusItems.resetCounts() self.importItems.resetCounts() for nwItem in self.projTree: - if nwItem.itemClass in nwLists.CLS_NOVEL: + if nwItem.isNovelLike(): self.statusItems.increment(nwItem.itemStatus) else: self.importItems.increment(nwItem.itemImport) diff --git a/novelwriter/dialogs/itemeditor.py b/novelwriter/dialogs/itemeditor.py index 84a0db14..b5faec0d 100644 --- a/novelwriter/dialogs/itemeditor.py +++ b/novelwriter/dialogs/itemeditor.py @@ -33,7 +33,7 @@ from PyQt5.QtWidgets import ( ) from novelwriter.enum import nwItemLayout, nwItemType -from novelwriter.constants import trConst, nwLists, nwLabels +from novelwriter.constants import trConst, nwLabels from novelwriter.gui.custom import QSwitch logger = logging.getLogger(__name__) @@ -74,7 +74,7 @@ class GuiItemEditor(QDialog): # Item Status self.editStatus = QComboBox() self.editStatus.setMinimumWidth(mVd) - if self.theItem.itemClass in nwLists.CLS_NOVEL: + if self.theItem.isNovelLike(): for key, entry in self.theProject.statusItems.items(): self.editStatus.addItem(entry["icon"], entry["name"], key) @@ -95,7 +95,7 @@ class GuiItemEditor(QDialog): self.editLayout.setMinimumWidth(mVd) validLayouts = [] if self.theItem.itemType == nwItemType.FILE: - if self.theItem.itemClass in nwLists.CLS_NOVEL: + if self.theItem.documentAllowed(): validLayouts.append(nwItemLayout.DOCUMENT) validLayouts.append(nwItemLayout.NOTE) else: diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index 5fdf7fcc..c75a0f58 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -206,7 +206,7 @@ class GuiProjectTree(QTreeWidget): # Add the file or folder if itemType == nwItemType.FILE: - if pItem.itemClass in nwLists.CLS_NOVEL: + if pItem.isNovelLike(): tHandle = self.theProject.newFile(self.tr("New Document"), sHandle) else: tHandle = self.theProject.newFile(self.tr("New Note"), sHandle) @@ -495,10 +495,7 @@ class GuiProjectTree(QTreeWidget): tIndex = trItemP.indexOfChild(trItemS) trItemC = trItemP.takeChild(tIndex) trItemT.addChild(trItemC) - self._updateItemParent(tHandle) - self.propagateCount(tHandle, wCount) - - self.theIndex.deleteHandle(tHandle) + self._postItemMove(tHandle, wCount) self._recordLastMove(trItemS, trItemP, tIndex) self._setTreeChanged(True) @@ -661,9 +658,7 @@ class GuiProjectTree(QTreeWidget): movItem = parItem.takeChild(srcIndex) dstItem.insertChild(dstIndex, movItem) - snItem = self.theProject.projTree[sHandle] - dnItem = self.theProject.projTree[dHandle] - self._postItemMove(sHandle, snItem, dnItem, wCount) + self._postItemMove(sHandle, wCount) self.clearSelection() movItem.setSelected(True) @@ -812,7 +807,7 @@ class GuiProjectTree(QTreeWidget): logger.debug("Drag'n'drop of item '%s' accepted", sHandle) self.propagateCount(sHandle, 0) QTreeWidget.dropEvent(self, theEvent) - self._postItemMove(sHandle, snItem, dnItem, wCount) + self._postItemMove(sHandle, wCount) self._recordLastMove(sItem, pItem, pIndex) else: @@ -828,40 +823,37 @@ class GuiProjectTree(QTreeWidget): # Internal Functions ## - def _postItemMove(self, sHandle, snItem, dnItem, wCount): + def _postItemMove(self, tHandle, wCount): """Run various maintenance tasks for a moved item. """ - isFile = snItem.itemType == nwItemType.FILE - isSame = snItem.itemClass == dnItem.itemClass - onFree = dnItem.itemClass in nwLists.FREE_CLASS and isFile + trItemS = self._getTreeItem(tHandle) + nwItemS = self.theProject.projTree[tHandle] + trItemP = trItemS.parent() + if trItemP is None: + logger.error("Failed to find new parent item of '%s'", tHandle) + return False - self._updateItemParent(sHandle) + pHandle = trItemP.data(self.C_NAME, Qt.UserRole) + nwItemS.setParent(pHandle) + self.theProject.projTree.updateItemData(tHandle) + self.setTreeItemValues(tHandle) + self.propagateCount(tHandle, wCount) - # If the item does not have the same class as the target, - # and the target is not a free root folder, update its class - if not (isSame or onFree): - logger.debug( - "Item '%s' class has been changed from '%s' to '%s'", - sHandle, snItem.itemClass.name, dnItem.itemClass.name - ) - snItem.setClass(dnItem.itemClass) - self.setTreeItemValues(sHandle) - - self.propagateCount(sHandle, wCount) + logger.debug("The parent of item '%s' has been changed to '%s'", tHandle, pHandle) # The items dropped into archive or trash should be removed # from the project index, for all other items, we rescan the # file to ensure the index is up to date. - if onFree: - self.theIndex.deleteHandle(sHandle) + if nwItemS.isInactive(): + self.theIndex.deleteHandle(tHandle) else: - self.theIndex.reIndexHandle(sHandle) + self.theIndex.reIndexHandle(tHandle) # Trigger dependent updates self._setTreeChanged(True) - self._emitItemChange(sHandle) + self._emitItemChange(tHandle) - return + return True def _getTreeItem(self, tHandle): """Returns the QTreeWidgetItem of a given item handle. @@ -961,25 +953,6 @@ class GuiProjectTree(QTreeWidget): return trItem - def _updateItemParent(self, tHandle): - """Update the parent handle of an item so that the information - in the project is consistent with the treeView. - """ - trItemS = self._getTreeItem(tHandle) - nwItemS = self.theProject.projTree[tHandle] - trItemP = trItemS.parent() - if trItemP is None: - logger.error("Failed to find new parent item of '%s'", tHandle) - return False - - pHandle = trItemP.data(self.C_NAME, Qt.UserRole) - nwItemS.setParent(pHandle) - self.setTreeItemValues(tHandle) - - logger.debug("The parent of item '%s' has been changed to '%s'", tHandle, pHandle) - - return True - def _setTreeChanged(self, theState): """Set the tree change flag, and propagate to the project. """ @@ -994,7 +967,7 @@ class GuiProjectTree(QTreeWidget): """ if self.theProject.projTree.checkType(tHandle, nwItemType.FILE): nwItem = self.theProject.projTree[tHandle] - if nwItem.itemClass in nwLists.CLS_NOVEL: + if nwItem.isNovelLike(): self.novelItemChanged.emit() else: self.noteItemChanged.emit() diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index c21fc091..fabd3867 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -55,7 +55,6 @@ from novelwriter.enum import ( nwItemType, nwItemClass, nwAlert, nwWidget, nwState ) from novelwriter.common import getGuiItem, hexToInt -from novelwriter.constants import nwLists logger = logging.getLogger(__name__) @@ -848,7 +847,7 @@ class GuiMain(QMainWindow): tItem = self.theProject.projTree[tHandle] if tItem is None: return False - if tItem.itemType not in nwLists.REG_TYPES: + if tItem.itemType == nwItemType.NO_TYPE: return False logger.verbose("Requesting change to item '%s'", tHandle) diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index b584679d..8a962268 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -1076,7 +1076,7 @@ def testCoreProject_OrphanedFiles(mockGUI, nwLipsum): assert oItem.itemParent == "b3643d0f92e32" assert oItem.itemClass == nwItemClass.NOVEL assert oItem.itemType == nwItemType.FILE - assert oItem.itemLayout == nwItemLayout.DOCUMENT + assert oItem.itemLayout == nwItemLayout.NOTE assert theProject.saveProject(nwLipsum) assert theProject.closeProject()