diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 21dea08c..f4280f30 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -178,6 +178,9 @@ class GuiProjectTree(QTreeWidget): if itemType == nwItemType.ROOT: tHandle = self.theProject.newRoot(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 @@ -236,8 +239,9 @@ class GuiProjectTree(QTreeWidget): return False # Add the new item to the tree - self.revealTreeItem(tHandle, nHandle) - self.theParent.editItem(tHandle) + if tHandle is not None: + self.revealTreeItem(tHandle, nHandle) + self.theParent.editItem(tHandle) return True @@ -257,7 +261,8 @@ class GuiProjectTree(QTreeWidget): """Move an item up or down in the tree, but only if the treeView has focus. This also applies when the menu is used. """ - if qApp.focusWidget() == self and self.theParent.hasProject: + hasFocus = qApp.focusWidget() == self or not self.mainConf.blockGUI + if hasFocus and self.theParent.hasProject: tHandle = self.getSelectedHandle() tItem = self._getTreeItem(tHandle) @@ -357,14 +362,15 @@ class GuiProjectTree(QTreeWidget): self.makeAlert("The Trash folder is already empty.", nwAlert.INFO) return False - msgBox = QMessageBox() - msgRes = msgBox.question( - self, "Empty Trash", "Permanently delete %d file%s from Trash?" % ( - nTrash, "s" if nTrash > 1 else "" + if self.mainConf.blockGUI: + msgBox = QMessageBox() + msgRes = msgBox.question( + self, "Empty Trash", "Permanently delete %d file%s from Trash?" % ( + nTrash, "s" if nTrash > 1 else "" + ) ) - ) - if msgRes != QMessageBox.Yes: - return False + if msgRes != QMessageBox.Yes: + return False logger.verbose("Deleting %d files from Trash" % nTrash) for tHandle in self.getTreeFromHandle(trashHandle): @@ -754,6 +760,7 @@ class GuiProjectTree(QTreeWidget): self.theIndex.reIndexHandle(sHandle) else: + theEvent.ignore() logger.debug("Drag'n'drop of item %s not accepted" % sHandle) self.makeAlert("The item cannot be moved to that location.", nwAlert.ERROR) @@ -920,14 +927,14 @@ class GuiProjectTree(QTreeWidget): nwItemS.setClass(nwItemD.itemClass) if trItemP is None: logger.error("Failed to find new parent item of %s" % tHandle) - return + return False pHandle = trItemP.data(self.C_NAME, Qt.UserRole) nwItemS.setParent(pHandle) self.setTreeItemValues(tHandle) self._setTreeChanged(True) - return + return True def _setTreeChanged(self, theState): """Set the tree change flag, and propagate to the project. diff --git a/tests/test_gui.py b/tests/test_gui.py index 92fe1a8d..ec24d5ec 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -10,11 +10,13 @@ from shutil import copyfile from nwtools import cmpFiles from os import path -from PyQt5.QtCore import Qt, QUrl, QPoint +from PyQt5.QtCore import Qt, QUrl, QPoint, QItemSelectionModel from PyQt5.QtGui import QTextCursor, QColor, QPixmap, QIcon from PyQt5.QtWidgets import qApp, QAction, QTreeWidgetItem, QStyle -from nw.constants import nwItemType, nwUnicode, nwOutline, nwDocAction, nwDocInsert +from nw.constants import ( + nwItemType, nwItemClass, nwUnicode, nwOutline, nwDocAction, nwDocInsert +) keyDelay = 2 stepDelay = 20 @@ -535,6 +537,140 @@ def testDocViewer(qtbot, nwLipsum, nwTemp): nwGUI.closeMain() nwGUI.close() +@pytest.mark.gui +def testProjectTree(qtbot, nwMinimal, nwTemp): + + nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % nwTemp, nwMinimal]) + qtbot.addWidget(nwGUI) + nwGUI.show() + qtbot.waitForWindowShown(nwGUI) + qtbot.wait(stepDelay) + + nwGUI.theProject.projTree.setSeed(42) + nwTree = nwGUI.treeView + + # No location selected for new item + assert not nwTree.newTreeItem(nwItemType.FILE, None) + assert not nwTree.newTreeItem(nwItemType.FOLDER, None) + + # Select a location + chItem = nwTree._getTreeItem("a6d311a93600a") + nwTree.setCurrentItem(chItem, QItemSelectionModel.Current) + chItem.setExpanded(True) + + # Create new item with no class set + assert nwTree.newTreeItem(nwItemType.FILE, None) + assert nwTree.newTreeItem(nwItemType.FOLDER, None) + + # Add roots + assert not nwTree.newTreeItem(nwItemType.ROOT, None) # Defaults to NOVEL + assert not nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) # Duplicate + assert nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.CUSTOM) # Valid + + # Check that we have the correct tree order + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "73475cb40a568", "44cb730c42048" + ] + + # Move second item up twice (should give same result) + nwTree.setSelectedHandle("8c659a11cd429") + nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "8c659a11cd429", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048" + ] + nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "8c659a11cd429", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048" + ] + + # Move it back down four times (last to should be the same) + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "73475cb40a568", "44cb730c42048" + ] + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "8c659a11cd429", "44cb730c42048" + ] + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048", "8c659a11cd429" + ] + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048", "8c659a11cd429" + ] + + # Move a root item (top level items are different) twice + nwTree.flushTreeOrder() + assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 9 + nwTree.setSelectedHandle("9d5247ab588e0") + + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + nwTree.flushTreeOrder() + assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 10 + + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + nwTree.flushTreeOrder() + assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 10 + + # Add some content to the new file + nwGUI.openDocument("73475cb40a568") + nwGUI.docEditor.setText("# Hello World\n") + nwGUI.saveDocument() + assert path.isfile(path.join(nwMinimal, "content", "73475cb40a568.nwd")) + + # Delete the items we added earlier + nwTree.clearSelection() + assert not nwTree.emptyTrash() # No folder yet + assert not nwTree.deleteItem(None) + assert not nwTree.deleteItem("1111111111111") + assert nwTree.deleteItem("73475cb40a568") # New File + assert nwTree.deleteItem("44cb730c42048") # New Folder + assert nwTree.deleteItem("71ee45a3c0db9") # Custom Root + assert "73475cb40a568" in nwGUI.theProject.projTree._treeOrder + assert "44cb730c42048" not in nwGUI.theProject.projTree._treeOrder + assert "71ee45a3c0db9" not in nwGUI.theProject.projTree._treeOrder + + # The file is in trash, empty it + assert path.isfile(path.join(nwMinimal, "content", "73475cb40a568.nwd")) + assert nwTree.emptyTrash() + assert not nwTree.emptyTrash() # Already empty + assert not path.isfile(path.join(nwMinimal, "content", "73475cb40a568.nwd")) + assert "73475cb40a568" not in nwGUI.theProject.projTree._treeOrder + + # Close the project + nwGUI.closeProject() + + # Add an orphaned file + orphFile = path.join(nwMinimal, "content", "1234567890abc.nwd") + with open(orphFile, mode="w+", encoding="utf8") as outFile: + outFile.write("# Hello World\n") + + # Open the project again + nwGUI.openProject(nwMinimal) + + # Check that the orphaned file was found and added to the tree + assert nwTree.orphRoot is not None + nwTree.flushTreeOrder() + assert "1234567890abc" not in nwGUI.theProject.projTree._treeOrder + orItem = nwTree._getTreeItem("1234567890abc") + assert orItem.text(nwTree.C_NAME) == "Orphaned File 1" + + # Move it to the Plot folder + # plItem = nwTree._getTreeItem("7695ce551d265") + # orRect = nwTree.visualItemRect(orItem) + # plRect = nwTree.visualItemRect(plItem) + + # qtbot.mouseMove(nwTree.viewport(), pos=orRect.center(), delay=1000) + # qtbot.mousePress(nwTree.viewport(), Qt.LeftButton, pos=orRect.center(), delay=1000) + # qtbot.mouseMove(nwTree.viewport(), pos=plRect.center(), delay=1000) + # qtbot.mouseRelease(nwTree.viewport(), Qt.LeftButton, pos=plRect.center(), delay=1000) + + # qtbot.stopForInteraction() + nwGUI.closeMain() + nwGUI.close() + @pytest.mark.gui def testEditFormatMenu(qtbot, nwLipsum, nwTemp):