diff --git a/core/src/main/groovy/com/muwire/core/files/FileUnsharedEvent.groovy b/core/src/main/groovy/com/muwire/core/files/FileUnsharedEvent.groovy index b7e44925..1cdcda1d 100644 --- a/core/src/main/groovy/com/muwire/core/files/FileUnsharedEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/files/FileUnsharedEvent.groovy @@ -6,4 +6,8 @@ import com.muwire.core.SharedFile class FileUnsharedEvent extends Event { SharedFile[] unsharedFiles boolean deleted + /** + * true if the files are implicitly removed as part of unsharing a folder. + */ + boolean implicit } diff --git a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy index d3fa32ef..c176ba79 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy @@ -580,49 +580,75 @@ class MainFrameController { } void unshareSelectedFile() { - def sfs = view.selectedSharedFiles() + /* + * A selection may include files, folders or both. + */ Set folders = view.selectedFolders() - if (sfs == null && folders.isEmpty()) - return - if (sfs == null) - sfs = Collections.emptyList() + List leafFiles = view.selectedIndividualSharedFiles() + if (leafFiles == null) + leafFiles = Collections.emptyList() - List toUnshare = new ArrayList<>() - sfs.each { SharedFile sf -> - - if (view.settings.collectionWarning) { - Set collectionsInfoHashes = core.collectionManager.collectionsForFile(sf.rootInfoHash) - if (!collectionsInfoHashes.isEmpty()) { - String[] affected = collectionsInfoHashes.collect({core.collectionManager.getByInfoHash(it)}).collect{it.name}.toArray(new String[0]) + if (folders.isEmpty() && leafFiles.isEmpty()) + return - boolean [] answer = new boolean[1] - def props = [:] - props.collections = affected - props.answer = answer - props.fileName = sf.file.getName() - props.settings = view.settings - props.home = core.home - def mvc = mvcGroup.createMVCGroup("collection-warning", props) - mvc.destroy() - if (!answer[0]) { - File f = sf.getFile() - while(true) { - File parent = f.getParentFile() - if (parent == null) - break - folders.remove(parent) - f = parent - } - return - } - } + List implicitUnshared = new ArrayList<>() + for (File folder : folders) { + List contained = model.sharedTree.getFilesInFolder(folder) + for (SharedFile sf : contained) { + if (collectionMembershipCheck(sf)) + implicitUnshared.add sf + else + return } - toUnshare.add(sf) } - if (!folders.isEmpty()) + + List explicitUnshared = new ArrayList<>() + for (SharedFile sf : leafFiles) { + if (collectionMembershipCheck(sf)) + explicitUnshared << sf + else + return + } + + if (!folders.isEmpty()) { + for (File folder : folders) { + model.sharedTree.removeFromTree(folder) + model.allFilesSharedTree.removeFromTree(folder) + } core.eventBus.publish(new DirectoryUnsharedEvent(directories: folders.toArray(new File[0]), deleted: false)) - if (!toUnshare.isEmpty()) - core.eventBus.publish(new FileUnsharedEvent(unsharedFiles : toUnshare.toArray(new SharedFile[0]))) + core.eventBus.publish(new FileUnsharedEvent( + unsharedFiles: implicitUnshared.toArray(new SharedFile[0]), + deleted: false, + implicit: true + )) + } + if (!explicitUnshared.isEmpty()) + core.eventBus.publish(new FileUnsharedEvent(unsharedFiles : explicitUnshared.toArray(new SharedFile[0]))) + } + + /** + * @param sharedFile that may be in a collection + * @return true if the file should be unshared + */ + private boolean collectionMembershipCheck(SharedFile sf) { + if (!view.settings.collectionWarning) + return true + Set collectionsInfoHashes = core.collectionManager.collectionsForFile(sf.rootInfoHash) + if (!collectionsInfoHashes.isEmpty()) { + String[] affected = collectionsInfoHashes.collect({core.collectionManager.getByInfoHash(it)}).collect{it.name}.toArray(new String[0]) + + boolean [] answer = new boolean[1] + def props = [:] + props.collections = affected + props.answer = answer + props.fileName = sf.file.getName() + props.settings = view.settings + props.home = core.home + def mvc = mvcGroup.createMVCGroup("collection-warning", props) + mvc.destroy() + return answer[0] + } + return true } @ControllerAction diff --git a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy index 181aff5a..41728510 100644 --- a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy @@ -621,21 +621,19 @@ class MainFrameModel { loadedFiles = allSharedFiles.size() for (SharedFile sharedFile : e.unsharedFiles) { - removeUnsharedFromTree(sharedFile, e.deleted) + if (fileToNode.remove(sharedFile) == null) + continue + if (e.implicit) + continue + + allFilesSharedTree.removeFromTree(sharedFile, e.deleted) + sharedTree.removeFromTree(sharedFile, e.deleted) } + view.refreshSharedFiles() } } - private void removeUnsharedFromTree(SharedFile sharedFile, boolean deleted) { - SortedTreeNode dmtn = fileToNode.remove(sharedFile) - if (dmtn == null) - return - - allFilesSharedTree.removeFromTree(sharedFile, deleted) - sharedTree.removeFromTree(sharedFile, deleted) - } - void onUploadEvent(UploadEvent e) { runInsideUIAsync { int index = -1 diff --git a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy index d78a2ee8..204b00f7 100644 --- a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy @@ -1533,6 +1533,23 @@ class MainFrameView { return rv } } + + List selectedIndividualSharedFiles() { + if (!model.treeVisible) + return selectedSharedFiles() + else { + List rv = new ArrayList<>() + def sharedFilesTree = builder.getVariable("shared-files-tree") + TreePath[] selectedPaths = sharedFilesTree.getSelectionPaths() + for (TreePath path : selectedPaths) { + DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) path.getLastPathComponent() + Object obj = dmtn.getUserObject() + if (obj instanceof SharedFile) + rv << obj + } + return rv + } + } /** * @return if a single file is selected, return it. diff --git a/gui/src/main/groovy/com/muwire/gui/LibraryTreeModel.groovy b/gui/src/main/groovy/com/muwire/gui/LibraryTreeModel.groovy index 70fa0798..e2b7d20e 100644 --- a/gui/src/main/groovy/com/muwire/gui/LibraryTreeModel.groovy +++ b/gui/src/main/groovy/com/muwire/gui/LibraryTreeModel.groovy @@ -14,7 +14,7 @@ class LibraryTreeModel extends DefaultTreeModel { } TreeNode addToTree(SharedFile sharedFile) { - List parents = getParents(sharedFile) + List parents = getParents(sharedFile.getFile()) LibraryTreeNode node = root for (File path : parents) { def key = new InterimTreeNode(path) @@ -32,21 +32,33 @@ class LibraryTreeModel extends DefaultTreeModel { leaf } - void removeFromTree(SharedFile sharedFile, boolean deleted) { - List parents = getParents(sharedFile) - LibraryTreeNode node = root - - for (File path : parents) { - def key = new InterimTreeNode(path) - def child = node.getByKey(key) - if (child == null) { - if (deleted) - return - throw new IllegalStateException() - } - node = child + void removeFromTree(File folder) { + def node = findParentNode(folder, false) + def key = new InterimTreeNode(folder) + def child = node.getByKey(key) + while(true) { + def parent = child.getParent() + child.removeFromParent() + if (parent.getChildCount() == 0 && parent != root) + child = parent + else + break } + } + + List getFilesInFolder(File folder) { + def node = findParentNode(folder, false) + def key = new InterimTreeNode(folder) + def child = node.getByKey(key) + List rv = [] + TreeUtil.getLeafs(child, rv) + rv + } + + void removeFromTree(SharedFile sharedFile, boolean deleted) { + def node = findParentNode(sharedFile.getFile(), deleted) def leaf = node.getByKey(sharedFile) + while(true) { def parent = leaf.getParent() leaf.removeFromParent() @@ -57,9 +69,26 @@ class LibraryTreeModel extends DefaultTreeModel { } } - private List getParents(SharedFile sharedFile) { + private LibraryTreeNode findParentNode(File file, boolean deleted) { + List parents = getParents(file) + LibraryTreeNode node = root + + for (File path : parents) { + def key = new InterimTreeNode(path) + def child = node.getByKey(key) + if (child == null) { + if (deleted) + return null + throw new IllegalStateException() + } + node = child + } + node + } + + private static List getParents(File sharedFile) { List parents = new ArrayList<>() - File tmp = sharedFile.file.getParentFile() + File tmp = sharedFile.getParentFile() while(tmp.getParent() != null) { parents << tmp tmp = tmp.getParentFile()