From 8802e702268f63d535aa08db864542b0747ad7b4 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Fri, 24 Jun 2022 14:22:23 +0100 Subject: [PATCH] sort library tree alphabetically --- .../com/muwire/gui/MainFrameModel.groovy | 91 ++++--------------- .../com/muwire/gui/InterimTreeNode.groovy | 5 + .../com/muwire/gui/LibraryTreeModel.groovy | 90 ++++++++++++++++++ .../com/muwire/gui/ResultTreeModel.groovy | 42 ++------- .../com/muwire/gui/SortedTreeNode.groovy | 63 +++++++++++++ 5 files changed, 185 insertions(+), 106 deletions(-) create mode 100644 gui/src/main/groovy/com/muwire/gui/LibraryTreeModel.groovy create mode 100644 gui/src/main/groovy/com/muwire/gui/SortedTreeNode.groovy diff --git a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy index c7d08785..5d1d158a 100644 --- a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy @@ -130,8 +130,8 @@ class MainFrameModel { private boolean libraryDirty boolean libraryTabVisible private final javax.swing.Timer libraryTimer = new javax.swing.Timer(1000, {refreshLibrary()}) - TreeModel sharedTree - DefaultMutableTreeNode allFilesTreeRoot, treeRoot + LibraryTreeModel allFilesSharedTree, sharedTree + SortedTreeNode allFilesTreeRoot, treeRoot final Map fileToNode = new HashMap<>() def connectionList = [] @@ -247,9 +247,10 @@ class MainFrameModel { (Image) view.builder.imageIcon("/email.png").image) shared = [] - treeRoot = new DefaultMutableTreeNode() - sharedTree = new DefaultTreeModel(treeRoot) - allFilesTreeRoot = new DefaultMutableTreeNode() + treeRoot = new LibraryTreeModel.LibraryTreeNode() + sharedTree = new LibraryTreeModel(treeRoot) + allFilesTreeRoot = new LibraryTreeModel.LibraryTreeNode() + allFilesSharedTree = new LibraryTreeModel(allFilesTreeRoot) Timer timer = new Timer("download-pumper", true) timer.schedule({ @@ -497,10 +498,10 @@ class MainFrameModel { if (e.duplicate != null) allSharedFiles.remove(e.duplicate) allSharedFiles << e.sharedFile - insertIntoTree(e.sharedFile, allFilesTreeRoot, fileToNode) + insertIntoTree(e.sharedFile, allFilesSharedTree, fileToNode) if (filter(e.sharedFile)) { insertIntoTable(e.sharedFile) - insertIntoTree(e.sharedFile, treeRoot, null) + insertIntoTree(e.sharedFile, sharedTree, null) libraryDirty = true } } @@ -511,9 +512,9 @@ class MainFrameModel { return runInsideUIAsync { allSharedFiles << e.loadedFile - insertIntoTree(e.loadedFile, allFilesTreeRoot, fileToNode) + insertIntoTree(e.loadedFile, allFilesSharedTree, fileToNode) insertIntoTable(e.loadedFile) - insertIntoTree(e.loadedFile, treeRoot, null) + insertIntoTree(e.loadedFile, sharedTree, null) libraryDirty = true } } @@ -547,7 +548,7 @@ class MainFrameModel { insertIntoTable(sf) } shared.each { - insertIntoTree(it, treeRoot, null) + insertIntoTree(it, sharedTree, null) } view.refreshSharedFiles() view.magicTreeExpansion() @@ -577,7 +578,7 @@ class MainFrameModel { for (SharedFile sf : chunks) insertIntoTable(sf) chunks.each { - insertIntoTree(it, treeRoot, null) + insertIntoTree(it, sharedTree, null) } view.refreshSharedFiles() } @@ -616,36 +617,12 @@ class MainFrameModel { } private void removeUnsharedFromTree(SharedFile sharedFile, boolean deleted) { - DefaultMutableTreeNode dmtn = fileToNode.remove(sharedFile) + SortedTreeNode dmtn = fileToNode.remove(sharedFile) if (dmtn == null) return - Object[] path = dmtn.getUserObjectPath() - DefaultMutableTreeNode otherNode = treeRoot - for (int i = 1; i < path.length; i++) { - Object o = path[i] - DefaultMutableTreeNode next = null - for (int j = 0; j < otherNode.childCount; j++) { - if (otherNode.getChildAt(j).getUserObject() == o) { - next = otherNode.getChildAt(j) - break - } - } - if (next == null) { - if (deleted) - return - throw new IllegalStateException() - } - otherNode = next - } - while (true) { - def parent = otherNode.getParent() - otherNode.removeFromParent() - if (parent.getChildCount() == 0) { - otherNode = parent - } else - break - } + allFilesSharedTree.removeFromTree(sharedFile, deleted) + sharedTree.removeFromTree(sharedFile, deleted) } void onUploadEvent(UploadEvent e) { @@ -845,10 +822,10 @@ class MainFrameModel { return runInsideUIAsync { allSharedFiles << e.downloadedFile - insertIntoTree(e.downloadedFile, allFilesTreeRoot, fileToNode) + insertIntoTree(e.downloadedFile, allFilesSharedTree, fileToNode) if (filter(e.downloadedFile)) { insertIntoTable(e.downloadedFile) - insertIntoTree(e.downloadedFile,treeRoot, null) + insertIntoTree(e.downloadedFile,sharedTree, null) libraryDirty = true } } @@ -866,37 +843,9 @@ class MainFrameModel { } } - private void insertIntoTree(SharedFile file, TreeNode root, Map f2n) { - List parents = new ArrayList<>() - File tmp = file.file.getParentFile() - while(tmp.getParent() != null) { - parents << tmp - tmp = tmp.getParentFile() - } - Collections.reverse(parents) - TreeNode node = root - for(File path : parents) { - boolean exists = false - def children = node.children() - def child = null - while(children.hasMoreElements()) { - child = children.nextElement() - def userObject = child.getUserObject() - if (userObject != null && userObject.file == path) { - exists = true - break - } - } - if (!exists) { - child = new DefaultMutableTreeNode(new InterimTreeNode(path)) - node.add(child) - } - node = child - } - - def dmtn = new DefaultMutableTreeNode(file) - f2n?.put(file, dmtn) - node.add(dmtn) + private void insertIntoTree(SharedFile file, LibraryTreeModel libraryTreeModel, Map f2n) { + def leaf = libraryTreeModel.addToTree(file) + f2n?.put(file, leaf) } private void insertIntoTable(SharedFile sharedFile) { diff --git a/gui/src/main/groovy/com/muwire/gui/InterimTreeNode.groovy b/gui/src/main/groovy/com/muwire/gui/InterimTreeNode.groovy index 73cf484c..41ad9d67 100644 --- a/gui/src/main/groovy/com/muwire/gui/InterimTreeNode.groovy +++ b/gui/src/main/groovy/com/muwire/gui/InterimTreeNode.groovy @@ -12,6 +12,11 @@ class InterimTreeNode { return file; } + + public int hashCode() { + file.hashCode() + } + public boolean equals(Object o) { if (!(o instanceof InterimTreeNode)) return false diff --git a/gui/src/main/groovy/com/muwire/gui/LibraryTreeModel.groovy b/gui/src/main/groovy/com/muwire/gui/LibraryTreeModel.groovy new file mode 100644 index 00000000..70fa0798 --- /dev/null +++ b/gui/src/main/groovy/com/muwire/gui/LibraryTreeModel.groovy @@ -0,0 +1,90 @@ +package com.muwire.gui + +import com.muwire.core.SharedFile + +import javax.swing.tree.DefaultMutableTreeNode +import javax.swing.tree.DefaultTreeModel +import javax.swing.tree.TreeNode +import java.text.Collator + +class LibraryTreeModel extends DefaultTreeModel { + + LibraryTreeModel(TreeNode root) { + super(root) + } + + TreeNode addToTree(SharedFile sharedFile) { + List parents = getParents(sharedFile) + LibraryTreeNode node = root + for (File path : parents) { + def key = new InterimTreeNode(path) + def child = node.getByKey(key) + if (child == null) { + child = new LibraryTreeNode() + child.setUserObject(key) + node.addDescendant(child) + } + node = child + } + + def leaf = new LibraryTreeNode(sharedFile) + node.addDescendant(leaf) + 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 + } + def leaf = node.getByKey(sharedFile) + while(true) { + def parent = leaf.getParent() + leaf.removeFromParent() + if (parent.getChildCount() == 0 && parent != root) + leaf = parent + else + break + } + } + + private List getParents(SharedFile sharedFile) { + List parents = new ArrayList<>() + File tmp = sharedFile.file.getParentFile() + while(tmp.getParent() != null) { + parents << tmp + tmp = tmp.getParentFile() + } + Collections.reverse(parents) + parents + } + + static class LibraryTreeNode extends SortedTreeNode { + + LibraryTreeNode() { + super() + } + + LibraryTreeNode(SharedFile sharedFile) { + super(sharedFile) + } + + @Override + protected String getStringName() { + def object = getUserObject() + if (object instanceof SharedFile) + return object.getFile().getName() + else + return object.toString() + } + } +} diff --git a/gui/src/main/groovy/com/muwire/gui/ResultTreeModel.groovy b/gui/src/main/groovy/com/muwire/gui/ResultTreeModel.groovy index ffc59d11..c72106ce 100644 --- a/gui/src/main/groovy/com/muwire/gui/ResultTreeModel.groovy +++ b/gui/src/main/groovy/com/muwire/gui/ResultTreeModel.groovy @@ -18,7 +18,7 @@ class ResultTreeModel extends DefaultTreeModel { MutableResultNode node = root if (event.path == null || event.path.length == 0) { def child = new MutableResultNode(event) - node.addResult(child) + node.addDescendant(child) return } @@ -29,65 +29,37 @@ class ResultTreeModel extends DefaultTreeModel { String hiddenRoot = elements.remove(0) for (String element : elements) { def nodeData = new ResultTreeRenderer.ResultTreeNode(hiddenRoot, element) - def elementNode = node.childrenMap.get(nodeData) + def elementNode = node.getByKey(nodeData) if (elementNode == null) { elementNode = new MutableResultNode() elementNode.setUserObject(nodeData) - node.addResult(elementNode) + node.addDescendant(elementNode) } elementNode.getUserObject().addResult(event) node = elementNode } def fileNode = new MutableResultNode(event) - node.addResult(fileNode) + node.addDescendant(fileNode) } - static class MutableResultNode extends DefaultMutableTreeNode implements Comparable{ - private final Map childrenMap + static class MutableResultNode extends SortedTreeNode { MutableResultNode() { super() - childrenMap = new HashMap<>() } MutableResultNode(UIResultEvent event) { - super() - childrenMap = Collections.emptyMap() - setUserObject(event) + super(event) } @Override - void removeAllChildren() { - if (!childrenMap.isEmpty()) - childrenMap.clear() - super.removeAllChildren() - } - - void addResult(MutableResultNode newChild) { - childrenMap.put(newChild.getUserObject(), newChild) - if (children == null) - children = new Vector<>() - Object [] elementData = children.elementData - int idx = Arrays.binarySearch(elementData, 0, getChildCount(), newChild) - if (idx >= 0) - idx++ - else - idx = - idx - 1 - insert(newChild, idx) - } - - private String getStringName() { + protected String getStringName() { def object = getUserObject() if (object instanceof UIResultEvent) return object.getName() else return object.toString() } - - @Override - int compareTo(MutableResultNode other) { - Collator.getInstance().compare(getStringName(), other.getStringName()) - } } } diff --git a/gui/src/main/groovy/com/muwire/gui/SortedTreeNode.groovy b/gui/src/main/groovy/com/muwire/gui/SortedTreeNode.groovy new file mode 100644 index 00000000..1cab7f5b --- /dev/null +++ b/gui/src/main/groovy/com/muwire/gui/SortedTreeNode.groovy @@ -0,0 +1,63 @@ +package com.muwire.gui + +import javax.swing.tree.DefaultMutableTreeNode +import javax.swing.tree.MutableTreeNode +import javax.swing.tree.TreeNode +import java.text.Collator + +/** + * A tree node that keeps it's children sorted. + */ +abstract class SortedTreeNode extends DefaultMutableTreeNode implements Comparable { + private final Map childrenMap + + SortedTreeNode() { + super() + childrenMap = new HashMap<>() + } + + SortedTreeNode(T element) { + super() + childrenMap = Collections.emptyMap() + setUserObject(element) + } + + @Override + void removeAllChildren() { + if (!childrenMap.isEmpty()) + childrenMap.clear() + super.removeAllChildren() + } + + @Override + void remove(MutableTreeNode aChild) { + if (!(aChild instanceof SortedTreeNode)) + throw new IllegalStateException() + childrenMap.remove(aChild.getUserObject()) + super.remove(aChild) + } + + SortedTreeNode getByKey(Object key) { + childrenMap[key] + } + + void addDescendant(SortedTreeNode newChild) { + childrenMap.put(newChild.getUserObject(), newChild) + if (children == null) + children = new Vector<>() + Object [] elementData = children.elementData + int idx = Arrays.binarySearch(elementData, 0, getChildCount(), newChild) + if (idx >= 0) + idx++ + else + idx = - idx - 1 + insert(newChild, idx) + } + + protected abstract String getStringName() + + @Override + final int compareTo(SortedTreeNode other) { + Collator.getInstance().compare(getStringName(), other.getStringName()) + } +}