/*
* Copyright (C) 2010-2016 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui.tagtree;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.gui.AppStrings;
import com.jpexs.decompiler.flash.gui.TreeNodeType;
import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel;
import com.jpexs.decompiler.flash.gui.helpers.CollectionChangedAction;
import com.jpexs.decompiler.flash.gui.helpers.CollectionChangedEvent;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer;
import com.jpexs.decompiler.flash.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.timeline.AS2Package;
import com.jpexs.decompiler.flash.timeline.AS3Package;
import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.FrameScript;
import com.jpexs.decompiler.flash.timeline.TagScript;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.treeitems.AS3ClassTreeItem;
import com.jpexs.decompiler.flash.treeitems.FolderItem;
import com.jpexs.decompiler.flash.treeitems.HeaderItem;
import com.jpexs.decompiler.flash.treeitems.SWFList;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
/**
*
* @author JPEXS
*/
public class TagTreeModel implements TreeModel {
public static final String FOLDER_SHAPES = "shapes";
public static final String FOLDER_MORPHSHAPES = "morphshapes";
public static final String FOLDER_SPRITES = "sprites";
public static final String FOLDER_TEXTS = "texts";
public static final String FOLDER_IMAGES = "images";
public static final String FOLDER_MOVIES = "movies";
public static final String FOLDER_SOUNDS = "sounds";
public static final String FOLDER_BUTTONS = "buttons";
public static final String FOLDER_FONTS = "fonts";
public static final String FOLDER_BINARY_DATA = "binaryData";
public static final String FOLDER_FRAMES = "frames";
public static final String FOLDER_OTHERS = "others";
public static final String FOLDER_SCRIPTS = "scripts";
private final List<TreeModelListener> listeners = new ArrayList<>();
private final TagTreeRoot root = new TagTreeRoot();
private final List<SWFList> swfs;
private final Map<SWF, TagTreeSwfInfo> swfInfos = new HashMap<>();
private final boolean addAllFolders;
public TagTreeModel(List<SWFList> swfs, boolean addAllFolders) {
this.swfs = swfs;
this.addAllFolders = addAllFolders;
//Main.startWork(AppStrings.translate("work.buildingscripttree") + "...");
//Main.stopWork();
}
private String translate(String key) {
return AppStrings.translate(key);
}
public void updateSwfs(CollectionChangedEvent e) {
if (e.getAction() != CollectionChangedAction.ADD) {
List<SWF> toRemove = new ArrayList<>();
for (SWF swf : swfInfos.keySet()) {
SWF swf2 = swf.getRootSwf();
if (swf2 != null && !swfs.contains(swf2.swfList)) {
toRemove.add(swf);
}
}
for (SWF swf : toRemove) {
swfInfos.remove(swf);
}
}
switch (e.getAction()) {
case ADD: {
TreePath rootPath = new TreePath(new Object[]{root});
fireTreeNodesInserted(new TreeModelEvent(this, rootPath, new int[]{e.getNewIndex()}, new Object[]{e.getNewItem()}));
break;
}
case REMOVE: {
TreePath rootPath = new TreePath(new Object[]{root});
fireTreeNodesRemoved(new TreeModelEvent(this, rootPath, new int[]{e.getOldIndex()}, new Object[]{e.getOldItem()}));
break;
}
default:
fireTreeStructureChanged(new TreeModelEvent(this, new TreePath(root)));
}
}
public void updateSwf(SWF swf) {
swfInfos.clear();
TreePath changedPath = getTreePath(swf == null ? root : swf);
fireTreeStructureChanged(new TreeModelEvent(this, changedPath));
}
public void updateNode(TreeItem treeItem) {
TreePath changedPath = getTreePath(treeItem);
fireTreeStructureChanged(new TreeModelEvent(this, changedPath));
}
public void updateNode(TreePath changedPath) {
fireTreeStructureChanged(new TreeModelEvent(this, changedPath.getParentPath()));
}
private void fireTreeNodesRemoved(TreeModelEvent e) {
for (TreeModelListener listener : listeners) {
listener.treeNodesRemoved(e);
}
}
private void fireTreeNodesInserted(TreeModelEvent e) {
for (TreeModelListener listener : listeners) {
listener.treeNodesInserted(e);
}
}
private void fireTreeStructureChanged(TreeModelEvent e) {
for (TreeModelListener listener : listeners) {
listener.treeStructureChanged(e);
}
}
private List<SoundStreamHeadTypeTag> getSoundStreams(DefineSpriteTag sprite) {
List<SoundStreamHeadTypeTag> ret = new ArrayList<>();
for (Tag t : sprite.getTags()) {
if (t instanceof SoundStreamHeadTypeTag) {
ret.add((SoundStreamHeadTypeTag) t);
}
}
return ret;
}
private void createTagList(SWF swf) {
List<TreeItem> nodeList = new ArrayList<>();
List<TreeItem> frames = new ArrayList<>();
List<TreeItem> shapes = new ArrayList<>();
List<TreeItem> morphShapes = new ArrayList<>();
List<TreeItem> sprites = new ArrayList<>();
List<TreeItem> buttons = new ArrayList<>();
List<TreeItem> images = new ArrayList<>();
List<TreeItem> fonts = new ArrayList<>();
List<TreeItem> texts = new ArrayList<>();
List<TreeItem> movies = new ArrayList<>();
List<TreeItem> sounds = new ArrayList<>();
List<TreeItem> binaryData = new ArrayList<>();
List<TreeItem> others = new ArrayList<>();
List<FolderItem> emptyFolders = new ArrayList<>();
Map<Integer, List<TreeItem>> mappedTags = new HashMap<>();
for (Tag t : swf.getTags()) {
TreeNodeType ttype = TagTree.getTreeNodeType(t);
switch (ttype) {
case SHAPE:
shapes.add(t);
break;
case MORPH_SHAPE:
morphShapes.add(t);
break;
case SPRITE:
sprites.add(t);
sounds.addAll(getSoundStreams((DefineSpriteTag) t));
break;
case BUTTON:
buttons.add(t);
break;
case IMAGE:
images.add(t);
break;
case FONT:
fonts.add(t);
break;
case TEXT:
texts.add(t);
break;
case MOVIE:
movies.add(t);
break;
case SOUND:
sounds.add(t);
break;
case BINARY_DATA:
binaryData.add(t);
break;
case AS:
break;
default:
if (t.getId() != ShowFrameTag.ID && !ShowFrameTag.isNestedTagType(t.getId())) {
boolean parentFound = false;
if ((t instanceof CharacterIdTag) && !(t instanceof CharacterTag)) {
CharacterIdTag chit = (CharacterIdTag) t;
if (swf.getCharacter(chit.getCharacterId()) != null) {
parentFound = true;
if (!mappedTags.containsKey(chit.getCharacterId())) {
mappedTags.put(chit.getCharacterId(), new ArrayList<>());
}
mappedTags.get(chit.getCharacterId()).add(t);
}
}
if (!parentFound) {
others.add(t);
}
}
break;
}
}
Timeline timeline = swf.getTimeline();
int frameCount = timeline.getFrameCount();
for (int i = 0; i < frameCount; i++) {
frames.add(timeline.getFrame(i));
}
for (int i = sounds.size() - 1; i >= 0; i--) {
TreeItem sound = sounds.get(i);
if (sound instanceof SoundStreamHeadTypeTag) {
List<SoundStreamBlockTag> blocks = ((SoundStreamHeadTypeTag) sound).getBlocks();
if (blocks.isEmpty()) {
sounds.remove(i);
}
}
}
nodeList.add(new HeaderItem(swf, translate("node.header")));
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.shapes"), FOLDER_SHAPES, swf, shapes);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.morphshapes"), FOLDER_MORPHSHAPES, swf, morphShapes);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.sprites"), FOLDER_SPRITES, swf, sprites);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.texts"), FOLDER_TEXTS, swf, texts);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.images"), FOLDER_IMAGES, swf, images);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.movies"), FOLDER_MOVIES, swf, movies);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.sounds"), FOLDER_SOUNDS, swf, sounds);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.buttons"), FOLDER_BUTTONS, swf, buttons);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.fonts"), FOLDER_FONTS, swf, fonts);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.binaryData"), FOLDER_BINARY_DATA, swf, binaryData);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.frames"), FOLDER_FRAMES, swf, frames);
addFolderItem(nodeList, emptyFolders, true /*always add*/, translate("node.others"), FOLDER_OTHERS, swf, others);
Map<Tag, TagScript> currentTagScriptCache = new HashMap<>();
if (swf.isAS3()) {
if (!swf.getAbcList().isEmpty()) {
nodeList.add(new ClassesListTreeModel(swf));
}
} else {
List<TreeItem> subNodes = swf.getFirstLevelASMNodes(currentTagScriptCache);
if (subNodes.size() > 0) {
TreeItem actionScriptNode = new FolderItem(translate("node.scripts"), FOLDER_SCRIPTS, swf, subNodes);
nodeList.add(actionScriptNode);
}
}
TagTreeSwfInfo swfInfo = new TagTreeSwfInfo();
swfInfo.folders = nodeList;
swfInfo.emptyFolders = emptyFolders;
swfInfo.mappedTags = mappedTags;
swfInfo.tagScriptCache = currentTagScriptCache;
swfInfos.put(swf, swfInfo);
}
private void addFolderItem(List<TreeItem> nodeList, List<FolderItem> emptyList, boolean addAllFolders, String title, String folderName, SWF swf, List<TreeItem> items) {
FolderItem node = new FolderItem(title, folderName, swf, items);
if (addAllFolders || !items.isEmpty()) {
nodeList.add(node);
}
if (items.isEmpty()) {
emptyList.add(node);
}
}
public TreeItem getScriptsNode(SWF swf) {
int childCount = getChildCount(swf);
for (int i = 0; i < childCount; i++) {
TreeItem child = getChild(swf, i);
if (child instanceof ClassesListTreeModel) {
return child;
} else if (child instanceof FolderItem) {
FolderItem folder = (FolderItem) child;
if (folder.getName().equals(FOLDER_SCRIPTS)) {
return folder;
}
}
}
return null;
}
public TreeItem getFolderNode(SWF swf, String folderType) {
int childCount = getChildCount(swf);
for (int i = 0; i < childCount; i++) {
TreeItem child = getChild(swf, i);
if (child instanceof FolderItem) {
FolderItem folder = (FolderItem) child;
if (folder.getName().equals(folderType)) {
return folder;
}
}
}
return null;
}
private Frame searchForFrame(Object parent, SWF swf, Timelined t, int frame) {
int childCount = getChildCount(parent);
Frame lastVisibleFrame = null;
for (int i = 0; i < childCount; i++) {
TreeItem child = getChild(parent, i);
if ((child instanceof DefineSpriteTag) && child == t) {
Frame si = searchForFrame(child, swf, t, frame);
if (si != null) {
return si;
}
}
if (child instanceof Frame) {
Frame f = (Frame) child;
if (f.frame <= frame) {
lastVisibleFrame = f;
}
}
if (child instanceof FolderItem) {
FolderItem folder = (FolderItem) child;
if (folder.getName().equals(FOLDER_FRAMES) && t == swf) {
Frame si = searchForFrame(folder, swf, t, frame);
if (si != null) {
return si;
}
}
if (folder.getName().equals(FOLDER_SPRITES)) {
Frame si = searchForFrame(folder, swf, t, frame);
if (si != null) {
return si;
}
}
}
}
return lastVisibleFrame;
}
public Frame getFrame(SWF swf, Timelined t, int frame) {
return searchForFrame(swf, swf, t, frame);
}
private List<TreeItem> searchTreeItem(TreeItem obj, TreeItem parent, List<TreeItem> path) {
List<TreeItem> ret = null;
for (TreeItem n : getAllChildren(parent)) {
List<TreeItem> newPath = new ArrayList<>();
newPath.addAll(path);
newPath.add(n);
if (n instanceof AS3ClassTreeItem) {
AS3ClassTreeItem te = (AS3ClassTreeItem) n;
if (obj.equals(te)) {
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.equals(nds.swf)) {
return newPath;
}
} else {
if (obj.equals(n)) {
return newPath;
}
if (n instanceof TagScript) {
if (obj.equals(((TagScript) n).getTag())) {
return newPath;
}
}
}
ret = searchTreeItem(obj, n, newPath);
if (ret != null) {
return ret;
}
}
return ret;
}
public TreePath getTreePath(TreeItem obj) {
List<TreeItem> path = new ArrayList<>();
path.add(root);
if (obj != root) {
path = searchTreeItem(obj, root, path);
}
if (path == null) {
return null;
}
TreePath tp = new TreePath(path.toArray(new Object[path.size()]));
return tp;
}
@Override
public TreeItem getRoot() {
return root;
}
private TagTreeSwfInfo getSwfInfo(SWF swf) {
TagTreeSwfInfo swfInfo = swfInfos.get(swf);
if (swfInfo == null) {
createTagList(swf);
swfInfo = swfInfos.get(swf);
}
return swfInfo;
}
public List<FolderItem> getEmptyFolders(SWF swf) {
TagTreeSwfInfo swfInfo = getSwfInfo(swf);
return swfInfo.emptyFolders;
}
private List<TreeItem> getSwfFolders(SWF swf) {
TagTreeSwfInfo swfInfo = getSwfInfo(swf);
return swfInfo.folders;
}
private List<TreeItem> getMappedCharacters(SWF swf, CharacterTag tag) {
TagTreeSwfInfo swfInfo = getSwfInfo(swf);
List<TreeItem> mapped = swfInfo.mappedTags.get(tag.getCharacterId());
if (mapped == null) {
mapped = new ArrayList<>();
}
return mapped;
}
private Map<Tag, TagScript> getTagScriptCache(SWF swf) {
TagTreeSwfInfo swfInfo = getSwfInfo(swf);
return swfInfo.tagScriptCache;
}
public List<? extends TreeItem> getAllChildren(Object parent) {
TreeItem parentNode = (TreeItem) parent;
if (parentNode == root) {
List<TreeItem> result = new ArrayList<>(swfs.size());
for (SWFList swfList : swfs) {
if (!swfList.isBundle()) {
result.add(swfList.get(0));
}
result.add(swfList);
}
return result;
} else if (parentNode instanceof SWFList) {
return ((SWFList) parentNode).swfs;
} else if (parentNode instanceof SWF) {
return getSwfFolders((SWF) parentNode);
} else if (parentNode instanceof FolderItem) {
return ((FolderItem) parentNode).subItems;
} else if (parentNode instanceof Frame) {
return ((Frame) parentNode).innerTags;
} else if (parentNode instanceof DefineSpriteTag) {
return ((DefineSpriteTag) parentNode).getTimeline().getFrames();
} else if (parentNode instanceof DefineBinaryDataTag) {
DefineBinaryDataTag binaryDataTag = (DefineBinaryDataTag) parentNode;
if (binaryDataTag.innerSwf != null) {
List<SWF> result = new ArrayList<>(1);
result.add(((DefineBinaryDataTag) parentNode).innerSwf);
return result;
} else {
return new ArrayList<>(0);
}
} else if (parentNode instanceof AS2Package) {
return ((AS2Package) parentNode).getAllChildren();
} else if (parentNode instanceof FrameScript) {
Frame parentFrame = ((FrameScript) parentNode).getFrame();
List<TreeItem> result = new ArrayList<>();
result.addAll(parentFrame.actionContainers);
result.addAll(parentFrame.actions);
for (int i = 0; i < result.size(); i++) {
TreeItem item = result.get(i);
if (item instanceof Tag) {
Tag resultTag = (Tag) item;
Map<Tag, TagScript> currentTagScriptCache = getTagScriptCache(item.getSwf());
TagScript tagScript = currentTagScriptCache.get(resultTag);
if (tagScript == null) {
List<TreeItem> subNodes = new ArrayList<>();
if (item instanceof ASMSourceContainer) {
for (ASMSource item2 : ((ASMSourceContainer) item).getSubItems()) {
subNodes.add(item2);
}
}
tagScript = new TagScript(item.getSwf(), resultTag, subNodes);
currentTagScriptCache.put(resultTag, tagScript);
}
result.set(i, tagScript);
}
}
return result;
} else if (parentNode instanceof TagScript) {
return ((TagScript) parentNode).getFrames();
} else if (parentNode instanceof ClassesListTreeModel) {
ClassesListTreeModel clt = (ClassesListTreeModel) parentNode;
return clt.getAllChildren(clt.getRoot());
} else if (parentNode instanceof AS3ClassTreeItem) {
if (parentNode instanceof AS3Package) {
return ((AS3Package) parentNode).getAllChildren();
} else {
return new ArrayList<>();
}
} else if (parentNode instanceof CharacterTag) {
return getMappedCharacters(((CharacterTag) parentNode).getSwf(), (CharacterTag) parentNode);
}
return new ArrayList<>();
}
@Override
public TreeItem getChild(Object parent, int index) {
TreeItem parentNode = (TreeItem) parent;
if (parentNode instanceof CharacterTag) {
List<TreeItem> mapped = getMappedCharacters(((CharacterTag) parentNode).getSwf(), (CharacterTag) parentNode);
if (index < mapped.size()) {
return mapped.get(index);
}
index -= mapped.size();
}
if (parentNode == root) {
SWFList swfList = swfs.get(index);
if (!swfList.isBundle()) {
return swfList.get(0);
}
return swfList;
} else if (parentNode instanceof SWFList) {
return ((SWFList) parentNode).swfs.get(index);
} else if (parentNode instanceof SWF) {
return getSwfFolders((SWF) parentNode).get(index);
} else if (parentNode instanceof FolderItem) {
return ((FolderItem) parentNode).subItems.get(index);
} else if (parentNode instanceof Frame) {
return ((Frame) parentNode).innerTags.get(index);
} else if (parentNode instanceof DefineSpriteTag) {
return ((DefineSpriteTag) parentNode).getTimeline().getFrame(index);
} else if (parentNode instanceof DefineBinaryDataTag) {
return ((DefineBinaryDataTag) parentNode).innerSwf;
} else if (parentNode instanceof AS2Package) {
return ((AS2Package) parentNode).getChild(index);
} else if (parentNode instanceof FrameScript) {
Frame parentFrame = ((FrameScript) parentNode).getFrame();
TreeItem result;
if (index < parentFrame.actionContainers.size()) {
result = parentFrame.actionContainers.get(index);
} else {
index -= parentFrame.actionContainers.size();
result = parentFrame.actions.get(index);
}
if (result instanceof Tag) {
Tag resultTag = (Tag) result;
Map<Tag, TagScript> currentTagScriptCache = getTagScriptCache(result.getSwf());
TagScript tagScript = currentTagScriptCache.get(resultTag);
if (tagScript == null) {
List<TreeItem> subNodes = new ArrayList<>();
if (result instanceof ASMSourceContainer) {
for (ASMSource item : ((ASMSourceContainer) result).getSubItems()) {
subNodes.add(item);
}
}
tagScript = new TagScript(result.getSwf(), resultTag, subNodes);
currentTagScriptCache.put(resultTag, tagScript);
}
result = tagScript;
}
return result;
} else if (parentNode instanceof TagScript) {
return ((TagScript) parentNode).getFrames().get(index);
} else if (parentNode instanceof ClassesListTreeModel) {
ClassesListTreeModel clt = (ClassesListTreeModel) parentNode;
return clt.getChild(clt.getRoot(), index);
} else if (parentNode instanceof AS3ClassTreeItem) {
return ((AS3Package) parentNode).getChild(index);
}
throw new Error("Unsupported parent type: " + parentNode.getClass().getName());
}
@Override
public int getChildCount(Object parent) {
TreeItem parentNode = (TreeItem) parent;
int mappedSize = 0;
if (parentNode instanceof CharacterTag) {
mappedSize = getMappedCharacters(((CharacterTag) parentNode).getSwf(), (CharacterTag) parentNode).size();
}
if (parentNode == root) {
return mappedSize + swfs.size();
} else if (parentNode instanceof SWFList) {
return mappedSize + ((SWFList) parentNode).swfs.size();
} else if (parentNode instanceof SWF) {
return mappedSize + getSwfFolders((SWF) parentNode).size();
} else if (parentNode instanceof HeaderItem) {
return mappedSize + 0;
} else if (parentNode instanceof FolderItem) {
return mappedSize + ((FolderItem) parentNode).subItems.size();
} else if (parentNode instanceof Frame) {
return mappedSize + ((Frame) parentNode).innerTags.size();
} else if (parentNode instanceof DefineSpriteTag) {
return mappedSize + ((DefineSpriteTag) parentNode).getTimeline().getFrameCount();
} else if (parentNode instanceof DefineBinaryDataTag) {
return mappedSize + (((DefineBinaryDataTag) parentNode).innerSwf == null ? 0 : 1);
} else if (parentNode instanceof AS2Package) {
return mappedSize + ((AS2Package) parentNode).getChildCount();
} else if (parentNode instanceof FrameScript) {
Frame parentFrame = ((FrameScript) parentNode).getFrame();
return mappedSize + parentFrame.actionContainers.size() + parentFrame.actions.size();
} else if (parentNode instanceof TagScript) {
return mappedSize + ((TagScript) parentNode).getFrames().size();
} else if (parentNode instanceof ClassesListTreeModel) {
ClassesListTreeModel clt = (ClassesListTreeModel) parentNode;
return mappedSize + clt.getChildCount(clt.getRoot());
} else if (parentNode instanceof AS3Package) {
return mappedSize + ((AS3Package) parentNode).getChildCount();
} else if (parentNode instanceof CharacterTag) {
return mappedSize;
}
return 0;
}
@Override
public boolean isLeaf(Object node) {
return (getChildCount(node) == 0);
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
}
private int indexOfAdd(int prevSize, int index) {
if (index == -1) {
return -1;
}
return prevSize + index;
}
@Override
public int getIndexOfChild(Object parent, Object child) {
TreeItem parentNode = (TreeItem) parent;
TreeItem childNode = (TreeItem) child;
int baseIndex = 0;
if (parentNode instanceof CharacterTag) {
List<TreeItem> mapped = getMappedCharacters(((CharacterTag) parentNode).getSwf(), (CharacterTag) parentNode);
int mindex = mapped.indexOf(child);
if (mindex > -1) {
return mindex;
}
baseIndex = mapped.size();
}
if (parentNode == root) {
SWFList swfList = child instanceof SWFList
? (SWFList) child
: ((SWF) child).swfList;
return indexOfAdd(baseIndex, swfs.indexOf(swfList));
} else if (parentNode instanceof SWFList) {
return indexOfAdd(baseIndex, ((SWFList) parentNode).swfs.indexOf(childNode));
} else if (parentNode instanceof SWF) {
return indexOfAdd(baseIndex, getSwfFolders((SWF) parentNode).indexOf(childNode));
} else if (parentNode instanceof FolderItem) {
return indexOfAdd(baseIndex, ((FolderItem) parentNode).subItems.indexOf(childNode));
} else if (parentNode instanceof Frame) {
return indexOfAdd(baseIndex, ((Frame) parentNode).innerTags.indexOf(childNode));
} else if (parentNode instanceof DefineSpriteTag) {
return indexOfAdd(baseIndex, ((Frame) childNode).frame);
} else if (parentNode instanceof DefineBinaryDataTag) {
return indexOfAdd(baseIndex, 0); // binary data tag can have only 1 child
} else if (parentNode instanceof AS2Package) {
return indexOfAdd(baseIndex, ((AS2Package) parentNode).getIndexOfChild(childNode));
} else if (parentNode instanceof FrameScript) {
Frame parentFrame = ((FrameScript) parentNode).getFrame();
if (childNode instanceof TagScript) {
childNode = ((TagScript) childNode).getTag();
}
if (childNode instanceof ASMSourceContainer) {
return indexOfAdd(baseIndex, parentFrame.actionContainers.indexOf(childNode));
} else {
return indexOfAdd(baseIndex, parentFrame.actionContainers.size() + parentFrame.actions.indexOf(childNode));
}
} else if (parentNode instanceof TagScript) {
return indexOfAdd(baseIndex, ((TagScript) parentNode).getFrames().indexOf(childNode));
} else if (parentNode instanceof ClassesListTreeModel) {
ClassesListTreeModel clt = (ClassesListTreeModel) parentNode;
return indexOfAdd(baseIndex, clt.getIndexOfChild(clt.getRoot(), childNode));
} else if (parentNode instanceof AS3ClassTreeItem) {
return indexOfAdd(baseIndex, ((AS3Package) parentNode).getIndexOfChild((AS3ClassTreeItem) childNode));
} else if (parentNode instanceof CharacterTag) {
return indexOfAdd(baseIndex, getMappedCharacters(((CharacterTag) parentNode).getSwf(), (CharacterTag) parentNode).indexOf(childNode));
}
throw new Error("Unsupported parent type: " + parentNode.getClass().getName());
}
public boolean treePathExists(TreePath treePath) {
TreeItem current = null;
for (Object o : treePath.getPath()) {
TreeItem item = (TreeItem) o;
if (current == null) {
if (item != getRoot()) {
return false;
}
current = item;
} else {
int idx = getIndexOfChild(current, item);
if (idx == -1) {
return false;
}
current = item;
}
}
return true;
}
@Override
public void addTreeModelListener(TreeModelListener l) {
listeners.add(l);
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
listeners.remove(l);
}
}