diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d49ba1bb..1a017bd02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ All notable changes to this project will be documented in this file. - [#873] Context menu items are organized with separators and the order is more intuitive - [#1644] Save all button - has priority over standard Save button - Exe export mode can be selected in in Save EXE dialog (select filetype) - wrapper or projectors +- Optimized (faster) context menu for large SWF trees ### Fixed - Debugger - getting children of top level variables diff --git a/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeModel.java b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeModel.java index 9a7f6b3bb..76da442f9 100644 --- a/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeModel.java +++ b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeModel.java @@ -39,9 +39,10 @@ import com.jpexs.decompiler.flash.treeitems.OpenableList; import com.jpexs.decompiler.flash.treeitems.TreeItem; import java.util.ArrayList; import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.WeakHashMap; import javax.swing.event.TreeModelEvent; import javax.swing.tree.TreePath; @@ -59,7 +60,7 @@ public class TagListTreeModel extends AbstractTagTreeModel { private Map swfHeaders = new HashMap<>(); private final Map abcTagsClassesTree = new WeakHashMap<>(); - private final Map abcClassesTree = new WeakHashMap<>(); + private final Map abcClassesTree = new WeakHashMap<>(); public TagListTreeModel(List swfs) { this.swfs = swfs; @@ -105,6 +106,14 @@ public class TagListTreeModel extends AbstractTagTreeModel { @Override public TreeItem getChild(Object parent, int index) { + TreeItem result = getChildInternal(parent, index); + if (result != null) { + itemToParentCache.put(result, (TreeItem) parent); + } + return result; + } + + private TreeItem getChildInternal(Object parent, int index) { if (getChildCount(parent) == 0) { return null; } @@ -323,6 +332,14 @@ public class TagListTreeModel extends AbstractTagTreeModel { @Override public List getAllChildren(Object parent) { + List ret = getAllChildrenInternal(parent); + for (TreeItem item : ret) { + itemToParentCache.put(item, (TreeItem) parent); + } + return ret; + } + + private List getAllChildrenInternal(Object parent) { TreeItem parentNode = (TreeItem) parent; if (parentNode == root) { List result = new ArrayList<>(swfs.size()); @@ -378,6 +395,47 @@ public class TagListTreeModel extends AbstractTagTreeModel { return ret; } + + + @Override + protected void searchTreeItemMulti(List objs, TreeItem parent, List path, Map> result) { + for (TreeItem n : getAllChildren(parent)) { + List newPath = new ArrayList<>(); + newPath.addAll(path); + newPath.add(n); + + for (TreeItem obj : objs) { + if (obj == n) { //Equals or == ??? + result.put(obj, newPath); + } + } + + searchTreeItemMulti(objs, n, newPath, result); + } + } + + @Override + protected void searchTreeItemParentMulti(List objs, TreeItem parent, Map result) { + for (TreeItem n : getAllChildren(parent)) { + + for (TreeItem obj : objs) { + if (obj == n) { //Equals or == ??? + result.put(obj, parent); + if (result.size() == objs.size()) { + return; + } + + } + } + + searchTreeItemParentMulti(objs, n, result); + if (result.size() == objs.size()) { + return; + } + } + } + + @Override protected List searchTreeItem(TreeItem obj, TreeItem parent, List path) { List ret = null; @@ -386,7 +444,7 @@ public class TagListTreeModel extends AbstractTagTreeModel { newPath.addAll(path); newPath.add(n); - if (Objects.equals(obj, n)) { + if (obj == n) { //Equals or == ??? return newPath; } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTreeModel.java b/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTreeModel.java index 40cffb883..31f608649 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTreeModel.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTreeModel.java @@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.treeitems.Openable; import com.jpexs.decompiler.flash.treeitems.TreeItem; import java.util.ArrayList; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.WeakHashMap; @@ -45,6 +46,8 @@ public abstract class AbstractTagTreeModel implements TreeModel { public abstract void updateSwfs(CollectionChangedEvent e); private Map indices = new WeakHashMap<>(); + + protected Map itemToParentCache = new WeakHashMap<>(); public final void calculateCollisions() { Map indices = new WeakHashMap<>(); @@ -144,6 +147,47 @@ public abstract class AbstractTagTreeModel implements TreeModel { protected abstract List searchTreeItem(TreeItem obj, TreeItem parent, List path); + protected abstract void searchTreeItemMulti(List objs, TreeItem parent, List path, Map> result); + + protected abstract void searchTreeItemParentMulti(List objs, TreeItem parent, Map result); + + public Map getTreePathParentMulti(List objs) { + Map result = new IdentityHashMap<>(); + for (TreeItem item : objs) { + if (itemToParentCache.containsKey(item)) { + result.put(item, itemToParentCache.get(item)); + } else { + break; + } + } + if (result.size() == objs.size()) { + return result; + } + + TreeItem root = getRoot(); + //SLOW way + searchTreeItemParentMulti(objs, root, result); + return result; + } + + + public Map getTreePathMulti(List objs) { + TreeItem root = getRoot(); + List path = new ArrayList<>(); + path.add(root); + Map> paths = new IdentityHashMap<>(); + searchTreeItemMulti(objs, root, path, paths); + + Map result = new IdentityHashMap<>(); + for (TreeItem item : paths.keySet()) { + List p = paths.get(item); + TreePath tp = new TreePath(p.toArray(new Object[p.size()])); + result.put(item, tp); + } + + return result; + } + public TreePath getTreePath(TreeItem obj) { List path = new ArrayList<>(); TreeItem root = getRoot(); diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 0e7cd62b0..350b7789c 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -1158,14 +1158,12 @@ public class TagTreeContextMenu extends JPopupMenu { if (parent == null) { allSelectedSameParent = false; } else { - for (TreeItem item : items) { - TreePath currentParent = model.getTreePath(item).getParentPath(); - - if (!currentParent.equals(parent)) { - allSelectedSameParent = false; - break; - } - } + Map paths = model.getTreePathParentMulti(items); + Set parentSet = new LinkedIdentityHashSet<>(); + parentSet.addAll(paths.values()); + if (parentSet.size() != 1) { + allSelectedSameParent = false; + } } } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java index 3ea104aa9..9464c171b 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java @@ -401,7 +401,91 @@ public class TagTreeModel extends AbstractTagTreeModel { public Frame getFrame(SWF swf, Timelined t, int frame) { return searchForFrame(swf, swf, t, frame); } + + @Override + protected void searchTreeItemMulti(List objs, TreeItem parent, List path, Map> result) { + for (TreeItem n : getAllChildren(parent)) { + List newPath = new ArrayList<>(); + newPath.addAll(path); + newPath.add(n); + for (TreeItem obj : objs) { + if (searchMatches(n, obj)) { + result.put(obj, newPath); + } + } + + searchTreeItemMulti(objs, n, newPath, result); + } + } + + + @Override + protected void searchTreeItemParentMulti(List objs, TreeItem parent, Map result) { + for (TreeItem n : getAllChildren(parent)) { + + for (TreeItem obj : objs) { + if (searchMatches(n, obj)) { //Equals or == ??? + result.put(obj, parent); + } + } + + searchTreeItemParentMulti(objs, n, result); + } + } + + private boolean searchMatches(TreeItem n, TreeItem obj) { + if (n instanceof AS3Package) { + AS3Package pkg = (AS3Package) n; + if (obj instanceof AS3Package) { + AS3Package opkg = (AS3Package) obj; + if (Objects.equals(pkg.packageName, opkg.packageName) && pkg.getAbc() == opkg.getAbc()) { + return true; + } + } + } + + if (n instanceof AS3ClassTreeItem) { + AS3ClassTreeItem te = (AS3ClassTreeItem) n; + if (obj == te) { + return true; + } + } + + if (obj instanceof SceneFrame && n instanceof SceneFrame) { + // SceneFrames are always recreated, so compare them by frame and swf + SceneFrame nds = (SceneFrame) n; + SceneFrame objs = (SceneFrame) obj; + if (objs.getFrame().frame == nds.getFrame().frame && objs.getOpenable() == nds.getOpenable()) { + return true; + } + } + + if (obj instanceof FolderItem && n instanceof FolderItem) { + // FolderItems are always recreated, so compare them by name and swf + FolderItem nds = (FolderItem) n; + FolderItem objs = (FolderItem) obj; + if (objs.getName().equals(nds.getName()) && objs.swf == nds.swf) { + return true; + } + } else { + + TreeItem objNoTs = obj; + if (obj instanceof TagScript) { + objNoTs = ((TagScript) obj).getTag(); + } + + TreeItem nNoTs = n; + if (n instanceof TagScript) { + nNoTs = ((TagScript) n).getTag(); + } + + if (objNoTs == nNoTs) { + return true; + } + } + return false; + } @Override protected List searchTreeItem(TreeItem obj, TreeItem parent, List path) { List ret = null; @@ -410,54 +494,8 @@ public class TagTreeModel extends AbstractTagTreeModel { newPath.addAll(path); newPath.add(n); - if (n instanceof AS3Package) { - AS3Package pkg = (AS3Package) n; - if (obj instanceof AS3Package) { - AS3Package opkg = (AS3Package) obj; - if (Objects.equals(pkg.packageName, opkg.packageName) && pkg.getAbc() == opkg.getAbc()) { - return newPath; - } - } - } - - if (n instanceof AS3ClassTreeItem) { - AS3ClassTreeItem te = (AS3ClassTreeItem) n; - if (obj == te) { - return newPath; - } - } - - if (obj instanceof SceneFrame && n instanceof SceneFrame) { - // SceneFrames are always recreated, so compare them by frame and swf - SceneFrame nds = (SceneFrame) n; - SceneFrame objs = (SceneFrame) obj; - if (objs.getFrame().frame == nds.getFrame().frame && objs.getOpenable() == nds.getOpenable()) { - return newPath; - } - } - - if (obj instanceof FolderItem && n instanceof FolderItem) { - // FolderItems are always recreated, so compare them by name and swf - FolderItem nds = (FolderItem) n; - FolderItem objs = (FolderItem) obj; - if (objs.getName().equals(nds.getName()) && objs.swf == nds.swf) { - return newPath; - } - } else { - - TreeItem objNoTs = obj; - if (obj instanceof TagScript) { - objNoTs = ((TagScript) obj).getTag(); - } - - TreeItem nNoTs = n; - if (n instanceof TagScript) { - nNoTs = ((TagScript) n).getTag(); - } - - if (objNoTs == nNoTs) { - return newPath; - } + if (searchMatches(n, obj)) { + return newPath; } ret = searchTreeItem(obj, n, newPath); @@ -520,6 +558,14 @@ public class TagTreeModel extends AbstractTagTreeModel { @Override public List getAllChildren(Object parent) { + List ret = getAllChildrenInternal(parent); + for (TreeItem item : ret) { + itemToParentCache.put(item, (TreeItem) parent); + } + return ret; + } + + private List getAllChildrenInternal(Object parent) { TreeItem parentNode = (TreeItem) parent; List result = new ArrayList<>(); if (parentNode instanceof CharacterTag) { @@ -616,6 +662,14 @@ public class TagTreeModel extends AbstractTagTreeModel { @Override public TreeItem getChild(Object parent, int index) { + TreeItem result = getChildInternal(parent, index); + if (result != null) { + itemToParentCache.put(result, (TreeItem) parent); + } + return result; + } + + private TreeItem getChildInternal(Object parent, int index) { if (getChildCount(parent) == 0) { return null; }