From 7d1f6e684a92f276e91047a6ad17a7c447f666bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 29 Oct 2022 20:25:12 +0200 Subject: [PATCH] Added Tag list view - WIP - does not have context menu yet, and other things can be not working yet --- CHANGELOG.md | 1 + .../decompiler/flash/timeline/Frame.java | 11 + .../decompiler/flash/timeline/Timeline.java | 8 +- .../decompiler/flash/gui/MainFrameMenu.java | 12 +- .../jpexs/decompiler/flash/gui/MainPanel.java | 254 +++++++++++++++--- .../flash/gui/SelectTagPositionDialog.java | 27 +- .../decompiler/flash/gui/TreePanelMode.java | 2 +- .../flash/gui/graphics/taglist16.png | Bin 0 -> 344 bytes .../flash/gui/locales/MainFrame.properties | 4 +- .../flash/gui/taglistview/TagListTree.java | 184 +++++++++++++ .../taglistview/TagListTreeCellRenderer.java | 107 ++++++++ .../gui/taglistview/TagListTreeNode.java | 103 +++++++ 12 files changed, 645 insertions(+), 68 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/taglist16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/taglistview/TagListTree.java create mode 100644 src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeCellRenderer.java create mode 100644 src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeNode.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c1ff0cc2..bf6e32d54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - [#1731] Image viewer zoom support - Cloning of tags and frames - Changing tag position +- Tag list view ### Fixed - [#1834] PlaceObject4 tags appear as Unresolved inside of DefineSprite diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Frame.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Frame.java index 2d50e89bf..db852df19 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Frame.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Frame.java @@ -58,6 +58,8 @@ public class Frame implements TreeItem, Exportable { public List actionContainers = new ArrayList<>(); public List innerTags = new ArrayList<>(); + + public List allInnerTags = new ArrayList<>(); public ShowFrameTag showFrameTag = null; // can be null for the last frame @@ -113,6 +115,15 @@ public class Frame implements TreeItem, Exportable { return timeline.hashCode() ^ Integer.hashCode(frame); } + public boolean isAllInnerTagsModified() { + for (Tag t : allInnerTags) { + if (t.isModified()) { + return true; + } + } + return false; + } + @Override public boolean isModified() { for (Tag t : innerTags) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 59264b4c1..a98ca1876 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -74,6 +74,7 @@ import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -110,7 +111,7 @@ public class Timeline { private final Map actionFrames = new HashMap<>(); - private final Map> soundStramBlocks = new HashMap<>(); + private final Map> soundStramBlocks = new LinkedHashMap<>(); private AS2Package as2RootPackage; @@ -163,7 +164,7 @@ public class Timeline { public List getSoundStreamBlocks(SoundStreamHeadTypeTag head) { ensureInitialized(); - return soundStramBlocks.get(head); + return soundStramBlocks.get(head.getCharacterId()); } public Tag getParentTag() { @@ -274,6 +275,7 @@ public class Timeline { newFrameNeeded = true; frame.innerTags.add(t); } + frame.allInnerTags.add(t); if (t instanceof ASMSourceContainer) { ASMSourceContainer asmSourceContainer = (ASMSourceContainer) t; @@ -504,7 +506,7 @@ public class Timeline { SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) t; head.setVirtualCharacterId(containerId); blocks = new ArrayList<>(); - soundStramBlocks.put(head, blocks); + soundStramBlocks.put(containerId, blocks); continue; } diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java index 1c8c1dcc8..7add3f01a 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java @@ -863,6 +863,7 @@ public abstract class MainFrameMenu implements MenuBuilder { addMenuItem("/file/view", translate("menu.view"), null, null, 0, null, false, null, false); addToggleMenuItem("/file/view/viewResources", translate("menu.file.view.resources"), "view", "viewresources16", this::viewResourcesActionPerformed, PRIORITY_MEDIUM, null); + addToggleMenuItem("/file/view/viewTagList", translate("menu.file.view.tagList"), "view", "taglist16", this::viewTagListActionPerformed, PRIORITY_MEDIUM, null); addToggleMenuItem("/file/view/viewHex", translate("menu.file.view.hex"), "view", "viewhex16", this::viewHexActionPerformed, PRIORITY_MEDIUM, null); finishMenu("/file/view"); @@ -1141,6 +1142,14 @@ public abstract class MainFrameMenu implements MenuBuilder { setGroupSelection("view", "/file/view/viewHex"); setMenuChecked("/tools/timeline", false); } + + private void viewTagListActionPerformed(ActionEvent evt) { + Configuration.dumpView.set(false); + MainPanel mainPanel = mainFrame.getPanel(); + mainPanel.showView(MainPanel.VIEW_TAGLIST); + setGroupSelection("view", "/file/view/viewTagList"); + setMenuChecked("/tools/timeline", false); + } private void debuggerSwitchActionPerformed(ActionEvent evt) { boolean debuggerOn = isMenuChecked("/tools/debugger/debuggerSwitch"); @@ -1161,9 +1170,6 @@ public abstract class MainFrameMenu implements MenuBuilder { } else { setGroupSelection("view", null); } - } else if (Configuration.dumpView.get()) { - setGroupSelection("view", "/file/view/viewHex"); - mainFrame.getPanel().showView(MainPanel.VIEW_DUMP); } else { setGroupSelection("view", "/file/view/viewResources"); mainFrame.getPanel().showView(MainPanel.VIEW_RESOURCES); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 3b5442893..4a10d64df 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -91,6 +91,8 @@ import com.jpexs.decompiler.flash.gui.dumpview.DumpViewPanel; import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; import com.jpexs.decompiler.flash.gui.helpers.ObservableList; import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; +import com.jpexs.decompiler.flash.gui.taglistview.TagListTree; +import com.jpexs.decompiler.flash.gui.taglistview.TagListTreeNode; import com.jpexs.decompiler.flash.gui.tagtree.TagTree; import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; import com.jpexs.decompiler.flash.gui.timeline.TimelineViewPanel; @@ -280,6 +282,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public DumpTree dumpTree; + public TagListTree tagListTree; + private final FlashPlayerPanel flashPanel; private final FlashPlayerPanel flashPanel2; @@ -322,6 +326,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private static final String DUMP_VIEW = "DUMP"; + private static final String TAGLIST_VIEW = "TAGLIST"; + private static final String TIMELINE_VIEW = "TIMELINE"; private final JPersistentSplitPane splitPane1; @@ -354,6 +360,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public TreeItem oldItem; + private int currentView = VIEW_RESOURCES; + public List searchResultsDialogs = new ArrayList<>(); private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); @@ -590,6 +598,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } }); + tagListTree = new TagListTree(); + tagListTree.addTreeSelectionListener(this); + DragSource dragSource = DragSource.getDefaultDragSource(); dragSource.createDefaultDragGestureRecognizer(tagTree, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() { @Override @@ -675,6 +686,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se dumpTree.addTreeSelectionListener(this); dumpTree.createContextMenu(); + if (Configuration.dumpView.get()) { + currentView = VIEW_DUMP; + } + statusPanel = new MainFrameStatusPanel(this); add(statusPanel, BorderLayout.SOUTH); @@ -713,6 +728,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se LazyCardLayout treePanelLayout = new LazyCardLayout(); treePanelLayout.registerLayout(createResourcesViewCard(), RESOURCES_VIEW); treePanelLayout.registerLayout(createDumpViewCard(), DUMP_VIEW); + treePanelLayout.registerLayout(createTagListViewCard(), TAGLIST_VIEW); treePanel = new JPanel(treePanelLayout); //treePanel.add(searchPanel, BorderLayout.SOUTH); @@ -818,6 +834,12 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se dumpTree.expandFirstLevelNodes(); } + if (tagListTree.isInitialized()) { + tagListTree.updateSwfs(); + tagListTree.expandRoot(); + tagListTree.expandFirstLevelNodes(); + } + if (swfs.isEmpty()) { tagTree.setUI(new BasicTreeUI() { { @@ -829,6 +851,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se setHashColor(Color.gray); } }); + tagListTree.setUI(new BasicTreeUI() { + { + setHashColor(Color.gray); + } + }); } }); @@ -1444,7 +1471,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false); String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + try ( FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; if (swf.isAS3()) { ret.addAll(new AS3ScriptExporter().exportActionScript3(swf, handler, scriptsFolder, as3scripts, scriptExportSettings, parallel, evl)); @@ -1551,7 +1578,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false); String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + try ( FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); } @@ -1668,7 +1695,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, singleScriptFile, false); String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + try ( FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); } @@ -1709,6 +1736,13 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } return DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + } else if (treePanelMode == TreePanelMode.TAGLIST_TREE) { + TagListTreeNode node = (TagListTreeNode) tagListTree.getLastSelectedPathComponent(); + TreeItem treeNode = (TreeItem) node.getData(); + if (treeNode == null || treeNode instanceof SWFList) { + return null; + } + return treeNode.getSwf(); } return null; @@ -1824,7 +1858,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } String documentClass = swf.getDocumentClass(); - if (documentClass != null && !Configuration.dumpView.get()) { + if (documentClass != null && currentView != VIEW_DUMP) { List abcList = swf.getAbcList(); if (!abcList.isEmpty()) { ABCPanel abcPanel = getABCPanel(); @@ -2186,13 +2220,29 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } public void setTagTreeSelectedNode(TreeItem treeItem) { - TagTreeModel ttm = tagTree.getModel(); - TreePath tp = ttm.getTreePath(treeItem); - if (tp != null) { - tagTree.setSelectionPath(tp); - tagTree.scrollPathToVisible(tp); - } else { - showCard(CARDEMPTYPANEL); + if (currentView == VIEW_RESOURCES) { + TagTreeModel ttm = tagTree.getModel(); + TreePath tp = ttm.getTreePath(treeItem); + if (tp != null) { + tagTree.setSelectionPath(tp); + tagTree.scrollPathToVisible(tp); + } else { + showCard(CARDEMPTYPANEL); + } + } + if (currentView == VIEW_TAGLIST) { + TagListTreeNode node = (TagListTreeNode)tagTree.getLastSelectedPathComponent(); + if (node != null) { + TreePath tp = tagListTree.getPathForNode(node); + if (tp != null) { + tagListTree.setSelectionPath(tp); + tagListTree.scrollPathToVisible(tp); + } else { + showCard(CARDEMPTYPANEL); + } + } else { + showCard(CARDEMPTYPANEL); + } } } @@ -2932,9 +2982,18 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public void refreshTree(SWF[] swfs) { clear(); showCard(CARDEMPTYPANEL); - TreeItem treeItem = tagTree.getCurrentTreeItem(); + TreeItem treeItem = null; + if (currentView == VIEW_RESOURCES) { + treeItem = tagTree.getCurrentTreeItem(); + } else if (currentView == VIEW_TAGLIST) { + TagListTreeNode node = (TagListTreeNode) tagListTree.getLastSelectedPathComponent(); + if (node != null) { + treeItem = (TreeItem) node.getData(); + } + } tagTree.updateSwfs(swfs); + tagListTree.updateSwfs(); if (treeItem != null) { SWF swf = treeItem.getSwf(); @@ -3359,10 +3418,22 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se cl.show(displayPanel, card); } - @Override - public void valueChanged(TreeSelectionEvent e) { - Object source = e.getSource(); - TreeItem treeItem = (TreeItem) e.getPath().getLastPathComponent(); + private void valueChanged(Object source, TreePath selectedPath) { + TreeItem treeItem = null; + if (selectedPath != null) { + if (source == tagListTree) { + TagListTreeNode node = (TagListTreeNode) selectedPath.getLastPathComponent(); + if (node != null) { + treeItem = (TreeItem) node.getData(); + } + } else { + treeItem = (TreeItem) selectedPath.getLastPathComponent(); + } + } + + if (treeItem == null) { + return; + } if (!(treeItem instanceof SWFList)) { SWF swf = treeItem.getSwf(); @@ -3394,23 +3465,69 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se showPreview(t, dumpPreviewPanel, getFrameForTreeItem(t), getTimelinedForTreeItem(treeItem)); } } - + + @Override + public void valueChanged(TreeSelectionEvent e) { + Object source = e.getSource(); + valueChanged(source, e.getPath()); + } + + private TreePath convertViewPath(TreePath path) { + if (currentView == VIEW_RESOURCES) { + return path; + } + if (currentView == VIEW_TAGLIST) { + Object newPath[] = new Object[path.getPathCount()]; + for (int i = 0; i < path.getPathCount(); i++) { + TagListTreeNode node = (TagListTreeNode) path.getPathComponent(i); + newPath[i] = node.getData(); + } + return new TreePath(newPath); + } + return null; + } + private int getFrameForTreeItem(TreeItem treeItem) { - TreePath path = tagTree.getModel().getTreePath(treeItem); + if (treeItem == null) { + return -1; + } + TreePath path = null; + if (currentView == VIEW_RESOURCES) { + path = tagTree.getModel().getTreePath(treeItem); + } else if (currentView == VIEW_TAGLIST) { + path = tagListTree.getPathForData(treeItem); + } + if (path == null) { + return -1; + } + path = convertViewPath(path); for (int i = path.getPathCount() - 1; i >= 0; i--) { if (path.getPathComponent(i) instanceof Frame) { - Frame frame = (Frame)path.getPathComponent(i); + Frame frame = (Frame) path.getPathComponent(i); return frame.frame; } } return -1; } - + private Timelined getTimelinedForTreeItem(TreeItem treeItem) { - TreePath path = tagTree.getModel().getTreePath(treeItem); + if (treeItem == null) { + return null; + } + + TreePath path = null; + if (currentView == VIEW_RESOURCES) { + path = tagTree.getModel().getTreePath(treeItem); + } else if (currentView == VIEW_TAGLIST) { + path = tagListTree.getPathForData(treeItem); + } + if (path == null) { + return null; + } + path = convertViewPath(path); for (int i = path.getPathCount() - 1; i >= 0; i--) { if (path.getPathComponent(i) instanceof Timelined) { - return (Timelined)path.getPathComponent(i); + return (Timelined) path.getPathComponent(i); } } return null; @@ -3467,8 +3584,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public static final int VIEW_TIMELINE = 2; - private int getCurrentView() { - return Configuration.dumpView.get() ? VIEW_DUMP : VIEW_RESOURCES; + public static final int VIEW_TAGLIST = 3; + + public int getCurrentView() { + return currentView; } public void setTreeModel(int view) { @@ -3487,6 +3606,12 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se tagTree.expandFirstLevelNodes(); } break; + case VIEW_TAGLIST: + if (!tagListTree.isInitialized()) { + tagListTree.setSwfs(swfs); + tagListTree.expandFirstLevelNodes(); + } + break; } } @@ -3496,6 +3621,12 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return r; } + private JPanel createTagListViewCard() { + JPanel r = new JPanel(new BorderLayout()); + r.add(new FasterScrollPane(tagListTree), BorderLayout.CENTER); + return r; + } + private JPanel createResourcesViewCard() { JPanel r = new JPanel(new BorderLayout()); r.add(new FasterScrollPane(tagTree), BorderLayout.CENTER); @@ -3516,9 +3647,12 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public boolean showView(int view) { View.checkAccess(); + int prevView = currentView; + boolean okay = false; setTreeModel(view); switch (view) { case VIEW_DUMP: + currentView = view; if (!isWelcomeScreen) { showContentPanelCard(SPLIT_PANE1); } @@ -3526,8 +3660,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se treePanelMode = TreePanelMode.DUMP_TREE; showDetail(DETAILCARDEMPTYPANEL); reload(true); - return true; + okay = true; + break; case VIEW_RESOURCES: + currentView = view; if (!isWelcomeScreen) { showContentPanelCard(SPLIT_PANE1); } @@ -3543,8 +3679,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se }); reload(true); - return true; + okay = true; + break; case VIEW_TIMELINE: + currentView = view; final SWF swf = getCurrentSwf(); if (swf != null) { TreeItem item = tagTree.getCurrentTreeItem(); @@ -3559,12 +3697,36 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se timelineViewPanel.setTimelined(swf); } showContentPanelCard(TIMELINE_PANEL); - return true; + okay = true; + break; } - return false; + okay = false; + break; + case VIEW_TAGLIST: + currentView = view; + if (!isWelcomeScreen) { + showContentPanelCard(SPLIT_PANE1); + } + showTreePanelCard(TAGLIST_VIEW); + treePanelMode = TreePanelMode.TAGLIST_TREE; + reload(true); + okay = true; } - return false; + if (prevView != currentView) { + viewChanged(); + } + + return okay; + } + + private void viewChanged() { + if (currentView == VIEW_RESOURCES) { + valueChanged(tagTree, tagTree.getSelectionPath()); + } + if (currentView == VIEW_TAGLIST) { + valueChanged(tagListTree, tagListTree.getSelectionPath()); + } } private void dumpViewReload(boolean forceReload) { @@ -3690,9 +3852,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else if (treeItem instanceof ShowFrameTag) { SWF swf; if (timelinedContainer instanceof DefineSpriteTag) { - swf = ((DefineSpriteTag)timelinedContainer).getSwf(); + swf = ((DefineSpriteTag) timelinedContainer).getSwf(); } else { - swf = (SWF)timelinedContainer; + swf = (SWF) timelinedContainer; } previewPanel.showImagePanel(timelinedContainer, swf, frame, true); } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { @@ -3724,19 +3886,35 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } + private void tagListViewReload(boolean forceReload) { + showDetail(DETAILCARDEMPTYPANEL); + showCard(CARDEMPTYPANEL); + } + public void reload(boolean forceReload) { View.checkAccess(); tagTree.scrollPathToVisible(tagTree.getSelectionPath()); - if (Configuration.dumpView.get()) { + if (currentView == VIEW_DUMP) { dumpViewReload(forceReload); return; } + /*else if (currentView == VIEW_TAGLIST) { + tagListViewReload(forceReload); + return; + }*/ TreeItem treeItem = null; - TreePath treePath = tagTree.getSelectionPath(); - if (treePath != null && tagTree.getModel().treePathExists(treePath)) { - treeItem = (TreeItem) treePath.getLastPathComponent(); + if (currentView == VIEW_RESOURCES) { + TreePath treePath = tagTree.getSelectionPath(); + if (treePath != null && tagTree.getModel().treePathExists(treePath)) { + treeItem = (TreeItem) treePath.getLastPathComponent(); + } + } else if (currentView == VIEW_TAGLIST) { + TagListTreeNode node = (TagListTreeNode) tagListTree.getLastSelectedPathComponent(); + if (node != null) { + treeItem = (TreeItem) node.getData(); + } } // save last selected node to config @@ -3860,6 +4038,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else if (treeItem instanceof Frame && internalViewer) { showPreview(treeItem, previewPanel, -1, null); showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof ShowFrameTag && internalViewer) { + showPreview(treeItem, previewPanel, getFrameForTreeItem(treeItem), getTimelinedForTreeItem(treeItem)); + showCard(CARDPREVIEWPANEL); } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { showPreview(treeItem, previewPanel, -1, null); showCard(CARDPREVIEWPANEL); @@ -3879,6 +4060,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public void repaintTree() { tagTree.repaint(); + tagListTree.repaint(); reload(true); } @@ -4204,7 +4386,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public int indexOfTag(Tag tag) { - return -1; + return -1; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/SelectTagPositionDialog.java b/src/com/jpexs/decompiler/flash/gui/SelectTagPositionDialog.java index 70a97e96c..746a12524 100644 --- a/src/com/jpexs/decompiler/flash/gui/SelectTagPositionDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/SelectTagPositionDialog.java @@ -19,32 +19,12 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; import static com.jpexs.decompiler.flash.gui.AppDialog.CANCEL_OPTION; import com.jpexs.decompiler.flash.gui.tagtree.TagTree; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoABC2Tag; -import com.jpexs.decompiler.flash.tags.DoABCTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; 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.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.treeitems.TreeItem; -import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -203,7 +183,7 @@ public class SelectTagPositionDialog extends AppDialog { } } - private void populateNodes(MyTreeNode root, Timelined tim, int currentFrame) { + private void populateNodes(MyTreeNode root, Timelined tim) { int f = 1; MyTreeNode frameNode = new MyTreeNode(); @@ -218,7 +198,7 @@ public class SelectTagPositionDialog extends AppDialog { if (t instanceof DefineSpriteTag) { if (allowInsideSprites) { - populateNodes(node, (DefineSpriteTag) t, 1); + populateNodes(node, (DefineSpriteTag) t); } } if (t instanceof ShowFrameTag) { @@ -308,7 +288,6 @@ public class SelectTagPositionDialog extends AppDialog { } return renderer; } - } public SelectTagPositionDialog(Window parent, SWF swf, Tag selectedTag, Timelined selectedTimelined, boolean allowInsideSprites) { @@ -334,7 +313,7 @@ public class SelectTagPositionDialog extends AppDialog { MyTreeNode root = new MyTreeNode(); root.setData("root"); - populateNodes(root, swf, 1); + populateNodes(root, swf); positionTree = new JTree(root) { @Override diff --git a/src/com/jpexs/decompiler/flash/gui/TreePanelMode.java b/src/com/jpexs/decompiler/flash/gui/TreePanelMode.java index d0578522f..a05fdced9 100644 --- a/src/com/jpexs/decompiler/flash/gui/TreePanelMode.java +++ b/src/com/jpexs/decompiler/flash/gui/TreePanelMode.java @@ -22,5 +22,5 @@ package com.jpexs.decompiler.flash.gui; */ public enum TreePanelMode { - TAG_TREE, DUMP_TREE + TAG_TREE, DUMP_TREE, TAGLIST_TREE } diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/taglist16.png b/src/com/jpexs/decompiler/flash/gui/graphics/taglist16.png new file mode 100644 index 0000000000000000000000000000000000000000..4a8672bde48f806d3d4d37db192588a9aa3eac10 GIT binary patch literal 344 zcmV-e0jK_nP)PbXFR5;6H z`2YVu10|S&DhA}te_Swi*Xsu$nk)lAnzx?+_#Z@r_~qs0*+Bfiq@?73K|#U)?Ck9S zsi~>|6A}{sM@B~e4-O9gPhA%bd?2RGd{of6>E(lvp1b6Ep>6&12TPB<{a?EDDL4@0 z;^ML+A|n0=1_u83^78uc?CkvC#>VEqiHXU7U0vP(YHDhzff&$ntDt1@;|H#l*M@2! z+U8#>h@W=vfpy*`^1J}j+`sMRe-I7g8yOj8Yin!&S5Z;. + */ +package com.jpexs.decompiler.flash.gui.taglistview; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JTree; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; + +/** + * + * @author JPEXS + */ +public class TagListTree extends JTree { + + private List swfs; + + private TagListTreeNode root; + + private boolean initialized = false; + + public TagListTree() { + setCellRenderer(new TagListTreeCellRenderer()); + setRootVisible(false); + setShowsRootHandles(true); + setRowHeight(Math.max(getFont().getSize() + 5, 16)); + + if (View.isOceanic()) { + setBackground(Color.white); + setUI(new BasicTreeUI() { + { + setHashColor(Color.gray); + } + }); + } + } + + public boolean isInitialized() { + return initialized; + } + + public void setSwfs(List swfs) { + this.swfs = swfs; + updateSwfs(); + initialized = true; + } + + public void updateSwfs() { + root = new TagListTreeNode(); + root.setData("root"); + for (SWFList swfList : swfs) { + populateNodes(root, swfList); + } + + List> expandedNodes = View.getExpandedNodes(this); + setModel(new DefaultTreeModel(root)); + View.expandTreeNodes(this, expandedNodes); + } + + private void populateNodes(TagListTreeNode parent, Object obj) { + if (obj instanceof SWFList) { + SWFList list = (SWFList) obj; + TagListTreeNode node = new TagListTreeNode(); + node.setData(list); + node.setParent(parent); + parent.addChild(node); + + if (!list.isBundle()) { + node.setData(list.get(0)); + populateTimelineNodes(list.get(0), node, list.get(0)); + } else { + for (SWF swf : list) { + TagListTreeNode subNode = new TagListTreeNode(); + subNode.setData(swf); + subNode.setParent(node); + node.addChild(subNode); + parent.addChild(node); + populateTimelineNodes(swf, node, swf); + } + } + } + } + + private void populateTimelineNodes(SWF swf, TagListTreeNode root, Timelined timelined) { + int f = 0; + + Timeline timeline = timelined.getTimeline(); + + TagListTreeNode frameNode = new TagListTreeNode(); + frameNode.setData(timeline.getFrame(0)); + frameNode.setParent(root); + root.addChild(frameNode); + + for (Tag t : timelined.getTags()) { + TagListTreeNode node = new TagListTreeNode(); + node.setData(t); + node.setParent(frameNode); + frameNode.addChild(node); + + if (t instanceof DefineSpriteTag) { + populateTimelineNodes(swf, node, (DefineSpriteTag) t); + } + if (t instanceof ShowFrameTag) { + f++; + frameNode = new TagListTreeNode(); + frameNode.setData(timeline.getFrame(f)); + frameNode.setParent(root); + root.addChild(frameNode); + } + } + if (frameNode.isLeaf()) { + root.removeChild(root.getChildCount() - 1); + } + } + + public void expandRoot() { + expandPath(new TreePath(new Object[]{root})); + } + + public void expandFirstLevelNodes() { + int childCount = root.getChildCount(); + for (int i = 0; i < childCount; i++) { + expandPath(new TreePath(new Object[]{root, root.getChildAt(i)})); + } + } + + + public TreePath getPathForData(Object data) { + TagListTreeNode node = getNodeForData(root, data); + if (node == null) { + return new TreePath(new Object[0]); + } + return getPathForNode(node); + } + + public TreePath getPathForNode(TagListTreeNode node) { + List path = new ArrayList<>(); + while (node.getParent() != null) { + path.add(0, node); + node = (TagListTreeNode)node.getParent(); + } + path.add(0, node); + Object[] pathArr = path.toArray(new Object[path.size()]); + return new TreePath(pathArr); + } + + public TagListTreeNode getNodeForData(TagListTreeNode startNode, Object data) { + if (startNode.getData() == data) { + return startNode; + } + for (int i = 0; i < startNode.getChildCount(); i++) { + TagListTreeNode foundNode = getNodeForData((TagListTreeNode)startNode.getChildAt(i), data); + if (foundNode != null) { + return foundNode; + } + } + return null; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeCellRenderer.java b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeCellRenderer.java new file mode 100644 index 000000000..d20f5ddbc --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeCellRenderer.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 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 . + */ +package com.jpexs.decompiler.flash.gui.taglistview; + +import com.jpexs.decompiler.flash.gui.TreeNodeType; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.tagtree.TagTree; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import javax.swing.JLabel; +import javax.swing.JTree; +import javax.swing.plaf.basic.BasicLabelUI; +import javax.swing.tree.DefaultTreeCellRenderer; + +/** + * + * @author JPEXS + */ +public class TagListTreeCellRenderer extends DefaultTreeCellRenderer { + + private Font plainFont; + + private Font boldFont; + + public TagListTreeCellRenderer() { + if (View.isOceanic()) { + setUI(new BasicLabelUI()); + setOpaque(false); + setBackgroundNonSelectionColor(Color.white); + } + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Object subValue = value; + if (value instanceof TagListTreeNode) { + subValue = ((TagListTreeNode) value).getData(); + } + TreeItem val = null; + if (subValue instanceof TreeItem) { + val = (TreeItem) subValue; + } + + if (val != null && !(val instanceof SWFList) && val.getSwf() == null) { + // SWF was closed + value = null; + } + Component renderer = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + this.selected = sel; + if (renderer instanceof JLabel) { + JLabel lab = (JLabel) renderer; + + if (subValue instanceof TreeItem) { + lab.setIcon(TagTree.getIconForType(TagTree.getTreeNodeType((TreeItem) subValue))); + + boolean isReadOnly = false; + if (val instanceof Tag) { + isReadOnly = ((Tag) val).isReadOnly(); + } + + boolean isModified; + if (val instanceof Frame) { + isModified = ((Frame)val).isAllInnerTagsModified(); + } else { + isModified = val.isModified(); + } + if (isModified) { + if (boldFont == null) { + Font font = getFont(); + boldFont = font.deriveFont(Font.BOLD); + } + } else if (plainFont == null) { + Font font = getFont(); + plainFont = font.deriveFont(Font.PLAIN); + } + setFont(isModified ? boldFont : plainFont); + if (View.isOceanic()) { + if (isReadOnly) { + setForeground(new Color(0xcc, 0xcc, 0xcc)); + } else { + setForeground(Color.BLACK); + } + } + } + } + return renderer; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeNode.java b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeNode.java new file mode 100644 index 000000000..663ab30f8 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeNode.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 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 . + */ +package com.jpexs.decompiler.flash.gui.taglistview; + +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import javax.swing.tree.TreeNode; + +/** + * + * @author JPEXS + */ +public class TagListTreeNode implements TreeNode { + + private final List children = new ArrayList<>(); + private TreeNode parent; + private Object data; + + @Override + public String toString() { + //hack + if (data instanceof DoInitActionTag) { + DoInitActionTag doinit = (DoInitActionTag) data; + String exportName = doinit.getSwf().getExportName(doinit.spriteId); + if (exportName != null && !exportName.isEmpty()) { + return DoInitActionTag.NAME + " (" + doinit.spriteId + ") : " + exportName; + } + } + return data.toString(); + } + + public void setData(Object data) { + this.data = data; + } + + public Object getData() { + return data; + } + + public void addChild(TreeNode node) { + children.add(node); + } + + public void setParent(TreeNode parent) { + this.parent = parent; + } + + @Override + public TreeNode getChildAt(int childIndex) { + return children.get(childIndex); + } + + @Override + public int getChildCount() { + return children.size(); + } + + @Override + public TreeNode getParent() { + return parent; + } + + @Override + public int getIndex(TreeNode node) { + return children.indexOf(node); + } + + @Override + public boolean getAllowsChildren() { + return true; + } + + @Override + public boolean isLeaf() { + return children.isEmpty(); + } + + @Override + public Enumeration children() { + return Collections.enumeration(children); + } + + public void removeChild(int index) { + children.remove(index); + } +}