Simplify updating an item after it has been moved
This commit is contained in:
@@ -25,7 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
|
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):
|
def trConst(tString):
|
||||||
@@ -51,18 +51,9 @@ class nwConst():
|
|||||||
class nwLists():
|
class nwLists():
|
||||||
"""Lists used for grouping various other constants.
|
"""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
|
# Item classes which do not require items to have same class
|
||||||
FREE_CLASS = {nwItemClass.ARCHIVE, nwItemClass.TRASH}
|
FREE_CLASS = {nwItemClass.ARCHIVE, nwItemClass.TRASH}
|
||||||
|
|
||||||
# Deprecated nwItemLayout entries
|
|
||||||
DEP_LAYOUT = ("TITLE", "PAGE", "BOOK", "PARTITION", "UNNUMBERED", "CHAPTER", "SCENE")
|
|
||||||
|
|
||||||
# END Class nwLists
|
# END Class nwLists
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,13 @@ from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
|||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified
|
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified
|
||||||
)
|
)
|
||||||
from novelwriter.constants import nwLabels, nwLists, trConst
|
from novelwriter.constants import nwLabels, trConst
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Deprecated layout labels
|
||||||
|
DEP_LAYOUTS = ("TITLE", "PAGE", "BOOK", "PARTITION", "UNNUMBERED", "CHAPTER", "SCENE")
|
||||||
|
|
||||||
|
|
||||||
class NWItem():
|
class NWItem():
|
||||||
|
|
||||||
@@ -282,11 +285,27 @@ class NWItem():
|
|||||||
|
|
||||||
return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, ""))
|
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):
|
def getImportStatus(self):
|
||||||
"""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._class in nwLists.CLS_NOVEL:
|
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)
|
||||||
else:
|
else:
|
||||||
@@ -298,7 +317,7 @@ class NWItem():
|
|||||||
"""Update the importance or status value based on class. This is
|
"""Update the importance or status value based on class. This is
|
||||||
a wrapper setter for setStatus and setImport.
|
a wrapper setter for setStatus and setImport.
|
||||||
"""
|
"""
|
||||||
if self._class in nwLists.CLS_NOVEL:
|
if self.isNovelLike():
|
||||||
self.setStatus(value)
|
self.setStatus(value)
|
||||||
else:
|
else:
|
||||||
self.setImport(value)
|
self.setImport(value)
|
||||||
@@ -312,9 +331,14 @@ class NWItem():
|
|||||||
# Only update for child items
|
# Only update for child items
|
||||||
self.setClass(itemClass)
|
self.setClass(itemClass)
|
||||||
|
|
||||||
if self._class in nwLists.CLS_NOVEL:
|
if self._layout == nwItemLayout.NO_LAYOUT:
|
||||||
self._layout = nwItemLayout.DOCUMENT
|
# If no layout is set, pick one
|
||||||
else:
|
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
|
self._layout = nwItemLayout.NOTE
|
||||||
|
|
||||||
if self._status is None:
|
if self._status is None:
|
||||||
@@ -411,7 +435,7 @@ class NWItem():
|
|||||||
self._layout = itemLayout
|
self._layout = itemLayout
|
||||||
elif isItemLayout(itemLayout):
|
elif isItemLayout(itemLayout):
|
||||||
self._layout = nwItemLayout[itemLayout]
|
self._layout = nwItemLayout[itemLayout]
|
||||||
elif itemLayout in nwLists.DEP_LAYOUT:
|
elif itemLayout in DEP_LAYOUTS:
|
||||||
self._layout = nwItemLayout.DOCUMENT
|
self._layout = nwItemLayout.DOCUMENT
|
||||||
else:
|
else:
|
||||||
logger.error("Unrecognised item layout '%s'", itemLayout)
|
logger.error("Unrecognised item layout '%s'", itemLayout)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ from novelwriter.common import (
|
|||||||
checkString, checkBool, checkInt, isHandle, formatTimeStamp,
|
checkString, checkBool, checkInt, isHandle, formatTimeStamp,
|
||||||
makeFileNameSafe, hexToInt, simplified
|
makeFileNameSafe, hexToInt, simplified
|
||||||
)
|
)
|
||||||
from novelwriter.constants import nwLists, trConst, nwFiles, nwLabels
|
from novelwriter.constants import trConst, nwFiles, nwLabels
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -1197,7 +1197,7 @@ class NWProject():
|
|||||||
self.statusItems.resetCounts()
|
self.statusItems.resetCounts()
|
||||||
self.importItems.resetCounts()
|
self.importItems.resetCounts()
|
||||||
for nwItem in self.projTree:
|
for nwItem in self.projTree:
|
||||||
if nwItem.itemClass in nwLists.CLS_NOVEL:
|
if nwItem.isNovelLike():
|
||||||
self.statusItems.increment(nwItem.itemStatus)
|
self.statusItems.increment(nwItem.itemStatus)
|
||||||
else:
|
else:
|
||||||
self.importItems.increment(nwItem.itemImport)
|
self.importItems.increment(nwItem.itemImport)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.enum import nwItemLayout, nwItemType
|
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
|
from novelwriter.gui.custom import QSwitch
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -74,7 +74,7 @@ class GuiItemEditor(QDialog):
|
|||||||
# Item Status
|
# Item Status
|
||||||
self.editStatus = QComboBox()
|
self.editStatus = QComboBox()
|
||||||
self.editStatus.setMinimumWidth(mVd)
|
self.editStatus.setMinimumWidth(mVd)
|
||||||
if self.theItem.itemClass in nwLists.CLS_NOVEL:
|
if self.theItem.isNovelLike():
|
||||||
for key, entry in self.theProject.statusItems.items():
|
for key, entry in self.theProject.statusItems.items():
|
||||||
self.editStatus.addItem(entry["icon"], entry["name"], key)
|
self.editStatus.addItem(entry["icon"], entry["name"], key)
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ class GuiItemEditor(QDialog):
|
|||||||
self.editLayout.setMinimumWidth(mVd)
|
self.editLayout.setMinimumWidth(mVd)
|
||||||
validLayouts = []
|
validLayouts = []
|
||||||
if self.theItem.itemType == nwItemType.FILE:
|
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.DOCUMENT)
|
||||||
validLayouts.append(nwItemLayout.NOTE)
|
validLayouts.append(nwItemLayout.NOTE)
|
||||||
else:
|
else:
|
||||||
|
|||||||
+23
-50
@@ -206,7 +206,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
# Add the file or folder
|
# Add the file or folder
|
||||||
if itemType == nwItemType.FILE:
|
if itemType == nwItemType.FILE:
|
||||||
if pItem.itemClass in nwLists.CLS_NOVEL:
|
if pItem.isNovelLike():
|
||||||
tHandle = self.theProject.newFile(self.tr("New Document"), sHandle)
|
tHandle = self.theProject.newFile(self.tr("New Document"), sHandle)
|
||||||
else:
|
else:
|
||||||
tHandle = self.theProject.newFile(self.tr("New Note"), sHandle)
|
tHandle = self.theProject.newFile(self.tr("New Note"), sHandle)
|
||||||
@@ -495,10 +495,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
tIndex = trItemP.indexOfChild(trItemS)
|
tIndex = trItemP.indexOfChild(trItemS)
|
||||||
trItemC = trItemP.takeChild(tIndex)
|
trItemC = trItemP.takeChild(tIndex)
|
||||||
trItemT.addChild(trItemC)
|
trItemT.addChild(trItemC)
|
||||||
self._updateItemParent(tHandle)
|
self._postItemMove(tHandle, wCount)
|
||||||
self.propagateCount(tHandle, wCount)
|
|
||||||
|
|
||||||
self.theIndex.deleteHandle(tHandle)
|
|
||||||
self._recordLastMove(trItemS, trItemP, tIndex)
|
self._recordLastMove(trItemS, trItemP, tIndex)
|
||||||
self._setTreeChanged(True)
|
self._setTreeChanged(True)
|
||||||
|
|
||||||
@@ -661,9 +658,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
movItem = parItem.takeChild(srcIndex)
|
movItem = parItem.takeChild(srcIndex)
|
||||||
dstItem.insertChild(dstIndex, movItem)
|
dstItem.insertChild(dstIndex, movItem)
|
||||||
|
|
||||||
snItem = self.theProject.projTree[sHandle]
|
self._postItemMove(sHandle, wCount)
|
||||||
dnItem = self.theProject.projTree[dHandle]
|
|
||||||
self._postItemMove(sHandle, snItem, dnItem, wCount)
|
|
||||||
|
|
||||||
self.clearSelection()
|
self.clearSelection()
|
||||||
movItem.setSelected(True)
|
movItem.setSelected(True)
|
||||||
@@ -812,7 +807,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
logger.debug("Drag'n'drop of item '%s' accepted", sHandle)
|
logger.debug("Drag'n'drop of item '%s' accepted", sHandle)
|
||||||
self.propagateCount(sHandle, 0)
|
self.propagateCount(sHandle, 0)
|
||||||
QTreeWidget.dropEvent(self, theEvent)
|
QTreeWidget.dropEvent(self, theEvent)
|
||||||
self._postItemMove(sHandle, snItem, dnItem, wCount)
|
self._postItemMove(sHandle, wCount)
|
||||||
self._recordLastMove(sItem, pItem, pIndex)
|
self._recordLastMove(sItem, pItem, pIndex)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -828,40 +823,37 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _postItemMove(self, sHandle, snItem, dnItem, wCount):
|
def _postItemMove(self, tHandle, wCount):
|
||||||
"""Run various maintenance tasks for a moved item.
|
"""Run various maintenance tasks for a moved item.
|
||||||
"""
|
"""
|
||||||
isFile = snItem.itemType == nwItemType.FILE
|
trItemS = self._getTreeItem(tHandle)
|
||||||
isSame = snItem.itemClass == dnItem.itemClass
|
nwItemS = self.theProject.projTree[tHandle]
|
||||||
onFree = dnItem.itemClass in nwLists.FREE_CLASS and isFile
|
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,
|
logger.debug("The parent of item '%s' has been changed to '%s'", tHandle, pHandle)
|
||||||
# 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)
|
|
||||||
|
|
||||||
# The items dropped into archive or trash should be removed
|
# The items dropped into archive or trash should be removed
|
||||||
# from the project index, for all other items, we rescan the
|
# from the project index, for all other items, we rescan the
|
||||||
# file to ensure the index is up to date.
|
# file to ensure the index is up to date.
|
||||||
if onFree:
|
if nwItemS.isInactive():
|
||||||
self.theIndex.deleteHandle(sHandle)
|
self.theIndex.deleteHandle(tHandle)
|
||||||
else:
|
else:
|
||||||
self.theIndex.reIndexHandle(sHandle)
|
self.theIndex.reIndexHandle(tHandle)
|
||||||
|
|
||||||
# Trigger dependent updates
|
# Trigger dependent updates
|
||||||
self._setTreeChanged(True)
|
self._setTreeChanged(True)
|
||||||
self._emitItemChange(sHandle)
|
self._emitItemChange(tHandle)
|
||||||
|
|
||||||
return
|
return True
|
||||||
|
|
||||||
def _getTreeItem(self, tHandle):
|
def _getTreeItem(self, tHandle):
|
||||||
"""Returns the QTreeWidgetItem of a given item handle.
|
"""Returns the QTreeWidgetItem of a given item handle.
|
||||||
@@ -961,25 +953,6 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
return trItem
|
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):
|
def _setTreeChanged(self, theState):
|
||||||
"""Set the tree change flag, and propagate to the project.
|
"""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):
|
if self.theProject.projTree.checkType(tHandle, nwItemType.FILE):
|
||||||
nwItem = self.theProject.projTree[tHandle]
|
nwItem = self.theProject.projTree[tHandle]
|
||||||
if nwItem.itemClass in nwLists.CLS_NOVEL:
|
if nwItem.isNovelLike():
|
||||||
self.novelItemChanged.emit()
|
self.novelItemChanged.emit()
|
||||||
else:
|
else:
|
||||||
self.noteItemChanged.emit()
|
self.noteItemChanged.emit()
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ from novelwriter.enum import (
|
|||||||
nwItemType, nwItemClass, nwAlert, nwWidget, nwState
|
nwItemType, nwItemClass, nwAlert, nwWidget, nwState
|
||||||
)
|
)
|
||||||
from novelwriter.common import getGuiItem, hexToInt
|
from novelwriter.common import getGuiItem, hexToInt
|
||||||
from novelwriter.constants import nwLists
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -848,7 +847,7 @@ class GuiMain(QMainWindow):
|
|||||||
tItem = self.theProject.projTree[tHandle]
|
tItem = self.theProject.projTree[tHandle]
|
||||||
if tItem is None:
|
if tItem is None:
|
||||||
return False
|
return False
|
||||||
if tItem.itemType not in nwLists.REG_TYPES:
|
if tItem.itemType == nwItemType.NO_TYPE:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.verbose("Requesting change to item '%s'", tHandle)
|
logger.verbose("Requesting change to item '%s'", tHandle)
|
||||||
|
|||||||
@@ -1076,7 +1076,7 @@ def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
|
|||||||
assert oItem.itemParent == "b3643d0f92e32"
|
assert oItem.itemParent == "b3643d0f92e32"
|
||||||
assert oItem.itemClass == nwItemClass.NOVEL
|
assert oItem.itemClass == nwItemClass.NOVEL
|
||||||
assert oItem.itemType == nwItemType.FILE
|
assert oItem.itemType == nwItemType.FILE
|
||||||
assert oItem.itemLayout == nwItemLayout.DOCUMENT
|
assert oItem.itemLayout == nwItemLayout.NOTE
|
||||||
|
|
||||||
assert theProject.saveProject(nwLipsum)
|
assert theProject.saveProject(nwLipsum)
|
||||||
assert theProject.closeProject()
|
assert theProject.closeProject()
|
||||||
|
|||||||
Reference in New Issue
Block a user