Skip to content

Commit

Permalink
Convert folder to document or note (#1128)
Browse files Browse the repository at this point in the history
  • Loading branch information
vkbo authored Oct 1, 2022
2 parents 08289d0 + 25dec2a commit bdc7e4d
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 18 deletions.
82 changes: 65 additions & 17 deletions novelwriter/gui/projtree.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ def revealNewTreeItem(self, tHandle, nHandle=None):
if pHandle is not None and pHandle in self._treeMap:
self._treeMap[pHandle].setExpanded(True)

self._alertTreeChange(tHandle=tHandle, flush=True)
self._alertTreeChange(tHandle, flush=True)
self.clearSelection()
trItem.setSelected(True)

Expand Down Expand Up @@ -577,7 +577,7 @@ def moveTreeItem(self, nStep):
pItem.insertChild(nIndex, cItem)
self._recordLastMove(cItem, pItem, tIndex)

self._alertTreeChange(tHandle=tHandle, flush=True)
self._alertTreeChange(tHandle, flush=True)
self.clearSelection()
trItem.setSelected(True)
trItem.setExpanded(isExp)
Expand All @@ -595,7 +595,7 @@ def renameTreeItem(self, tHandle):
if dlgOk:
tItem.setName(newLabel)
self.setTreeItemValues(tHandle)
self._alertTreeChange(tHandle=tHandle, flush=False)
self._alertTreeChange(tHandle, flush=False)

return

Expand Down Expand Up @@ -665,7 +665,7 @@ def emptyTrash(self):
self.deleteItem(tHandle, alreadyAsked=True, bulkAction=True)

if nTrash > 0:
self._alertTreeChange(tHandle=trashHandle, flush=True)
self._alertTreeChange(trashHandle, flush=True)

return True

Expand Down Expand Up @@ -707,7 +707,7 @@ def deleteItem(self, tHandle=None, alreadyAsked=False, bulkAction=False):
if trItemS.childCount() == 0:
self.takeTopLevelItem(tIndex)
self._deleteTreeItem(tHandle)
self._alertTreeChange(tHandle=tHandle, flush=True)
self._alertTreeChange(tHandle, flush=True)
else:
self.mainGui.makeAlert(self.tr(
"Cannot delete root folder. It is not empty. "
Expand All @@ -723,7 +723,7 @@ def deleteItem(self, tHandle=None, alreadyAsked=False, bulkAction=False):
tIndex = trItemP.indexOfChild(trItemS)
trItemP.takeChild(tIndex)
self._deleteTreeItem(tHandle)
self._alertTreeChange(tHandle=tHandle, flush=autoFlush)
self._alertTreeChange(tHandle, flush=autoFlush)

else:
# A populated FOLDER or a FILE requires confirmtation
Expand Down Expand Up @@ -759,7 +759,7 @@ def deleteItem(self, tHandle=None, alreadyAsked=False, bulkAction=False):
self.mainGui.closeDocument()
self._deleteTreeItem(dHandle)

self._alertTreeChange(tHandle=tHandle, flush=autoFlush)
self._alertTreeChange(tHandle, flush=autoFlush)
self.projView.wordCountsChanged.emit()

else:
Expand All @@ -778,7 +778,7 @@ def deleteItem(self, tHandle=None, alreadyAsked=False, bulkAction=False):
trItemT.addChild(trItemC)
self._postItemMove(tHandle, wCount)
self._recordLastMove(trItemS, trItemP, tIndex)
self._alertTreeChange(tHandle=tHandle, flush=autoFlush)
self._alertTreeChange(tHandle, flush=autoFlush)

return True

Expand Down Expand Up @@ -904,7 +904,7 @@ def undoLastMove(self):
dstItem.insertChild(dstIndex, movItem)

self._postItemMove(sHandle, wCount)
self._alertTreeChange(tHandle=sHandle, flush=True)
self._alertTreeChange(sHandle, flush=True)

self.clearSelection()
movItem.setSelected(True)
Expand Down Expand Up @@ -1077,6 +1077,20 @@ def _openContextMenu(self, clickPos):
),
lambda: self._changeItemLayout(tHandle, nwItemLayout.NOTE)
)
elif isFolder:
if tItem.documentAllowed():
ctxMenu.addAction(
self.tr("Convert to {0}").format(
trConst(nwLabels.LAYOUT_NAME[nwItemLayout.DOCUMENT])
),
lambda: self._covertFolderToFile(tHandle, nwItemLayout.DOCUMENT)
)
ctxMenu.addAction(
self.tr("Convert to {0}").format(
trConst(nwLabels.LAYOUT_NAME[nwItemLayout.NOTE])
),
lambda: self._covertFolderToFile(tHandle, nwItemLayout.NOTE)
)

ctxMenu.addSeparator()

Expand Down Expand Up @@ -1154,7 +1168,7 @@ def dropEvent(self, theEvent):
QTreeWidget.dropEvent(self, theEvent)
self._postItemMove(sHandle, wCount)
self._recordLastMove(sItem, pItem, pIndex)
self._alertTreeChange(tHandle=sHandle, flush=True)
self._alertTreeChange(sHandle, flush=True)
sItem.setExpanded(isExpanded)

return
Expand Down Expand Up @@ -1236,6 +1250,7 @@ def _toggleItemExported(self, tHandle):
if tItem is not None:
tItem.setExported(not tItem.isExported)
self.setTreeItemValues(tItem.itemHandle)
self._alertTreeChange(tHandle, flush=False)
return

def _changeItemStatus(self, tHandle, tStatus):
Expand All @@ -1245,6 +1260,7 @@ def _changeItemStatus(self, tHandle, tStatus):
if tItem is not None:
tItem.setStatus(tStatus)
self.setTreeItemValues(tItem.itemHandle)
self._alertTreeChange(tHandle, flush=False)
return

def _changeItemImport(self, tHandle, tImport):
Expand All @@ -1254,6 +1270,7 @@ def _changeItemImport(self, tHandle, tImport):
if tItem is not None:
tItem.setImport(tImport)
self.setTreeItemValues(tItem.itemHandle)
self._alertTreeChange(tHandle, flush=False)
return

def _changeItemLayout(self, tHandle, itemLayout):
Expand All @@ -1263,10 +1280,38 @@ def _changeItemLayout(self, tHandle, itemLayout):
if tItem is not None:
if itemLayout == nwItemLayout.DOCUMENT and tItem.documentAllowed():
tItem.setLayout(nwItemLayout.DOCUMENT)
self.setTreeItemValues(tItem.itemHandle)
self.setTreeItemValues(tHandle)
self._alertTreeChange(tHandle, flush=False)
elif itemLayout == nwItemLayout.NOTE:
tItem.setLayout(nwItemLayout.NOTE)
self.setTreeItemValues(tItem.itemHandle)
self.setTreeItemValues(tHandle)
self._alertTreeChange(tHandle, flush=False)
return

def _covertFolderToFile(self, tHandle, itemLayout):
"""Convert a folder to a note or document.
"""
tItem = self.theProject.tree[tHandle]
if tItem is not None and tItem.isFolderType():
msgYes = self.mainGui.askQuestion(
self.tr("Convert Folder"),
self.tr(
"Do you want to convert the folder to a {0}? "
"This action cannot be reversed."
).format(trConst(nwLabels.LAYOUT_NAME[itemLayout]))
)
if msgYes and itemLayout == nwItemLayout.DOCUMENT and tItem.documentAllowed():
tItem.setType(nwItemType.FILE)
tItem.setLayout(nwItemLayout.DOCUMENT)
self.setTreeItemValues(tHandle)
self._alertTreeChange(tHandle, flush=False)
elif msgYes and itemLayout == nwItemLayout.NOTE:
tItem.setType(nwItemType.FILE)
tItem.setLayout(nwItemLayout.NOTE)
self.setTreeItemValues(tHandle)
self._alertTreeChange(tHandle, flush=False)
else:
logger.info("Folder conversion cancelled")
return

def _scanChildren(self, theList, tItem, tIndex):
Expand Down Expand Up @@ -1351,25 +1396,28 @@ def _addTrashRoot(self):
trItem = self._addTreeItem(self.theProject.tree[trashHandle])
if trItem is not None:
trItem.setExpanded(True)
self._alertTreeChange(tHandle=trashHandle, flush=True)
self._alertTreeChange(trashHandle, flush=True)

return trItem

def _alertTreeChange(self, tHandle=None, flush=True):
def _alertTreeChange(self, tHandle, flush=False):
"""Update information on tree change state, and emit necessary
signals.
signals. A flush is only needed if an item is moved, created or
deleted.
"""
self._timeChanged = time()
self.theProject.setProjectChanged(True)
if flush:
self.saveTreeOrder()

if tHandle is None:
return

tItem = self.theProject.tree[tHandle]
if tItem is None:
return

itemType = tItem.itemType
if itemType == nwItemType.ROOT:
if tItem.isRootType():
self.projView.rootFolderChanged.emit(tHandle)

self.projView.treeItemChanged.emit(tHandle)
Expand Down
36 changes: 35 additions & 1 deletion tests/test_gui/test_gui_projtree.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR


@pytest.mark.gui
def testGuiProjTree_ContextMenu(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test the building of the project tree context menu. All this does
is test that the menu builds. It doesn't open the actual menu,
"""
Expand All @@ -470,6 +470,8 @@ def testGuiProjTree_ContextMenu(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)

nwTree = nwGUI.projView

# Create a project
prjDir = os.path.join(fncDir, "project")
buildTestProject(nwGUI, prjDir)
Expand Down Expand Up @@ -544,6 +546,38 @@ def itemPos(tHandle):
projTree._changeItemLayout(hNovelNote, nwItemLayout.NOTE)
assert nwItem.itemLayout == nwItemLayout.NOTE

# Convert Folders to Documents
# ============================

hNewFolderOne = "0000000000013"
hNewFolderTwo = "0000000000015"

nwTree.setSelectedHandle(hNovelNote)
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
nwTree.setSelectedHandle(hNewFolderOne)
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True

nwTree.setSelectedHandle(hNovelNote)
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
nwTree.setSelectedHandle(hNewFolderTwo)
assert nwTree.projTree.newTreeItem(nwItemType.FILE, isNote=True) is True

# Click no on the dialog
with monkeypatch.context() as mp:
mp.setattr(QMessageBox, "question", lambda *a: QMessageBox.No)
projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT)
assert nwGUI.theProject.tree[hNewFolderOne].isFolderType()

# Convert the first folder to a document
projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT)
assert nwGUI.theProject.tree[hNewFolderOne].isFileType()
assert nwGUI.theProject.tree[hNewFolderOne].isDocumentLayout()

# Convert the second folder to a note
projTree._covertFolderToFile(hNewFolderTwo, nwItemLayout.NOTE)
assert nwGUI.theProject.tree[hNewFolderTwo].isFileType()
assert nwGUI.theProject.tree[hNewFolderTwo].isNoteLayout()

# qtbot.stop()

# END Test testGuiProjTree_ContextMenu

0 comments on commit bdc7e4d

Please sign in to comment.