mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-07 19:45:14 +00:00
Added: Optimized (faster) context menu for large SWF trees
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<SWF, HeaderItem> swfHeaders = new HashMap<>();
|
||||
|
||||
private final Map<ABCContainerTag, ClassesListTreeModel> abcTagsClassesTree = new WeakHashMap<>();
|
||||
private final Map<ABC, ClassesListTreeModel> abcClassesTree = new WeakHashMap<>();
|
||||
private final Map<ABC, ClassesListTreeModel> abcClassesTree = new WeakHashMap<>();
|
||||
|
||||
public TagListTreeModel(List<OpenableList> 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<? extends TreeItem> getAllChildren(Object parent) {
|
||||
List<? extends TreeItem> ret = getAllChildrenInternal(parent);
|
||||
for (TreeItem item : ret) {
|
||||
itemToParentCache.put(item, (TreeItem) parent);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<? extends TreeItem> getAllChildrenInternal(Object parent) {
|
||||
TreeItem parentNode = (TreeItem) parent;
|
||||
if (parentNode == root) {
|
||||
List<TreeItem> result = new ArrayList<>(swfs.size());
|
||||
@@ -378,6 +395,47 @@ public class TagListTreeModel extends AbstractTagTreeModel {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void searchTreeItemMulti(List<TreeItem> objs, TreeItem parent, List<TreeItem> path, Map<TreeItem, List<TreeItem>> result) {
|
||||
for (TreeItem n : getAllChildren(parent)) {
|
||||
List<TreeItem> 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<TreeItem> objs, TreeItem parent, Map<TreeItem, TreeItem> 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<TreeItem> searchTreeItem(TreeItem obj, TreeItem parent, List<TreeItem> path) {
|
||||
List<TreeItem> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<TreeItem, Integer> indices = new WeakHashMap<>();
|
||||
|
||||
protected Map<TreeItem, TreeItem> itemToParentCache = new WeakHashMap<>();
|
||||
|
||||
public final void calculateCollisions() {
|
||||
Map<TreeItem, Integer> indices = new WeakHashMap<>();
|
||||
@@ -144,6 +147,47 @@ public abstract class AbstractTagTreeModel implements TreeModel {
|
||||
|
||||
protected abstract List<TreeItem> searchTreeItem(TreeItem obj, TreeItem parent, List<TreeItem> path);
|
||||
|
||||
protected abstract void searchTreeItemMulti(List<TreeItem> objs, TreeItem parent, List<TreeItem> path, Map<TreeItem, List<TreeItem>> result);
|
||||
|
||||
protected abstract void searchTreeItemParentMulti(List<TreeItem> objs, TreeItem parent, Map<TreeItem, TreeItem> result);
|
||||
|
||||
public Map<TreeItem, TreeItem> getTreePathParentMulti(List<TreeItem> objs) {
|
||||
Map<TreeItem, TreeItem> 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<TreeItem, TreePath> getTreePathMulti(List<TreeItem> objs) {
|
||||
TreeItem root = getRoot();
|
||||
List<TreeItem> path = new ArrayList<>();
|
||||
path.add(root);
|
||||
Map<TreeItem, List<TreeItem>> paths = new IdentityHashMap<>();
|
||||
searchTreeItemMulti(objs, root, path, paths);
|
||||
|
||||
Map<TreeItem, TreePath> result = new IdentityHashMap<>();
|
||||
for (TreeItem item : paths.keySet()) {
|
||||
List<TreeItem> 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<TreeItem> path = new ArrayList<>();
|
||||
TreeItem root = getRoot();
|
||||
|
||||
@@ -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<TreeItem, TreeItem> paths = model.getTreePathParentMulti(items);
|
||||
Set<TreeItem> parentSet = new LinkedIdentityHashSet<>();
|
||||
parentSet.addAll(paths.values());
|
||||
if (parentSet.size() != 1) {
|
||||
allSelectedSameParent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<TreeItem> objs, TreeItem parent, List<TreeItem> path, Map<TreeItem, List<TreeItem>> result) {
|
||||
for (TreeItem n : getAllChildren(parent)) {
|
||||
List<TreeItem> 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<TreeItem> objs, TreeItem parent, Map<TreeItem, TreeItem> 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<TreeItem> searchTreeItem(TreeItem obj, TreeItem parent, List<TreeItem> path) {
|
||||
List<TreeItem> 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<? extends TreeItem> getAllChildren(Object parent) {
|
||||
List<? extends TreeItem> ret = getAllChildrenInternal(parent);
|
||||
for (TreeItem item : ret) {
|
||||
itemToParentCache.put(item, (TreeItem) parent);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<? extends TreeItem> getAllChildrenInternal(Object parent) {
|
||||
TreeItem parentNode = (TreeItem) parent;
|
||||
List<TreeItem> 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user