Added Set class to character mapping tool (via SymbolClass) context menu on characters

This commit is contained in:
Jindra Petřík
2024-08-05 11:17:25 +02:00
parent 98317a9104
commit f66a0eb4ad
15 changed files with 1029 additions and 56 deletions
@@ -192,35 +192,46 @@ public class SelectTagPositionDialog extends AppDialog {
}
}
private void populateNodes(MyTreeNode root, Timelined tim) {
private void populateNodes(MyTreeNode root, Timelined tim, int minFrame) {
int f = 1;
MyTreeNode frameNode = new MyTreeNode();
List<String> labels = new ArrayList<>();
frameNode.setData(new MyFrame(1, labels));
frameNode.setParent(root);
root.addChild(frameNode);
if (minFrame <= 1) {
frameNode.setData(new MyFrame(1, labels));
frameNode.setParent(root);
root.addChild(frameNode);
}
boolean wasMinFrame = minFrame <= 1;
for (Tag t : tim.getTags()) {
MyTreeNode node = new MyTreeNode();
node.setData(t);
frameNode.addChild(node);
if (wasMinFrame) {
MyTreeNode node = new MyTreeNode();
node.setData(t);
frameNode.addChild(node);
if (t instanceof DefineSpriteTag) {
if (allowInsideSprites) {
populateNodes(node, (DefineSpriteTag) t);
if (t instanceof DefineSpriteTag) {
if (allowInsideSprites) {
populateNodes(node, (DefineSpriteTag) t, 1);
}
}
if (t instanceof FrameLabelTag) {
labels.add(((FrameLabelTag) t).name);
}
}
if (t instanceof FrameLabelTag) {
labels.add(((FrameLabelTag) t).name);
}
if (t instanceof ShowFrameTag) {
f++;
frameNode = new MyTreeNode();
labels = new ArrayList<>();
frameNode.setData(new MyFrame(f, labels));
frameNode.setParent(root);
root.addChild(frameNode);
if (f >= minFrame) {
wasMinFrame = true;
frameNode = new MyTreeNode();
labels = new ArrayList<>();
frameNode.setData(new MyFrame(f, labels));
frameNode.setParent(root);
root.addChild(frameNode);
}
}
}
if (frameNode.isLeaf()) {
@@ -279,7 +290,11 @@ public class SelectTagPositionDialog extends AppDialog {
}
public SelectTagPositionDialog(Window parent, SWF swf, boolean allowInsideSprites) {
this(parent, swf, null, null, allowInsideSprites, false);
this(parent, swf, allowInsideSprites, null, 1);
}
public SelectTagPositionDialog(Window parent, SWF swf, boolean allowInsideSprites, String newTypeName, int minFrame) {
this(parent, swf, null, null, allowInsideSprites, false, newTypeName, minFrame);
}
private static class PositionTreeCellRenderer extends DefaultTreeCellRenderer {
@@ -320,14 +335,18 @@ public class SelectTagPositionDialog extends AppDialog {
}
}
public SelectTagPositionDialog(Window parent, SWF swf, Tag selectedTag, Timelined selectedTimelined, boolean allowInsideSprites, boolean selectNext) {
public SelectTagPositionDialog(Window parent, SWF swf, Tag selectedTag, Timelined selectedTimelined, boolean allowInsideSprites, boolean selectNext, String newTypeName, int minFrame) {
super(parent);
this.swf = swf;
this.selectedTag = selectedTag;
this.selectedTimelined = selectedTimelined;
this.allowInsideSprites = allowInsideSprites;
this.selectNext = selectNext;
setTitle(translate("dialog.title").replace("%filetitle%", swf.getShortPathTitle()));
if (newTypeName != null) {
setTitle(translate("dialog.title.new.typed").replace("%type%", newTypeName).replace("%filetitle%", swf.getShortPathTitle()));
} else {
setTitle(translate("dialog.title").replace("%filetitle%", swf.getShortPathTitle()));
}
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
Container cnt = getContentPane();
cnt.setLayout(new BorderLayout());
@@ -344,7 +363,7 @@ public class SelectTagPositionDialog extends AppDialog {
MyTreeNode root = new MyTreeNode();
root.setData("root");
populateNodes(root, swf);
populateNodes(root, swf, minFrame);
positionTree = new JTree(root) {
@Override
@@ -227,8 +227,8 @@ public class AddClassDialog extends AppDialog {
selectedAbcContainer = preselectedAbcContainer;
} else {
if (existingAbcTagRadioButton.isSelected()) {
SelectDoABCDialog selectDoABCDialog = new SelectDoABCDialog(owner, (SWF) openable);
selectedAbcContainer = selectDoABCDialog.showDialog();
SelectTagOfTypeDialog selectDoABCDialog = new SelectTagOfTypeDialog(owner, (SWF) openable, ABCContainerTag.class, "DoABC", 1);
selectedAbcContainer = (ABCContainerTag) selectDoABCDialog.showDialog();
if (selectedAbcContainer == null) {
cancelButtonActionPerformed(evt);
return;
@@ -18,10 +18,9 @@ package com.jpexs.decompiler.flash.gui.abc;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.gui.AppDialog;
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.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.Tag;
import java.awt.Component;
import java.awt.Container;
@@ -40,30 +39,38 @@ import javax.swing.JPanel;
*
* @author JPEXS
*/
public class SelectDoABCDialog extends AppDialog {
public class SelectTagOfTypeDialog extends AppDialog {
private JComboBox<ComboItem> abcComboBox;
private ABCContainerTag result = null;
private JComboBox<ComboItem> tagComboBox;
private Tag result = null;
public SelectDoABCDialog(Window window, SWF swf) {
public SelectTagOfTypeDialog(Window window, SWF swf, Class<?> tagType, String tagTypeName, int minFrame) {
super(window);
setTitle(translate("dialog.title"));
setTitle(translate("dialog.title").replace("%type%", tagTypeName));
Container cnt = getContentPane();
abcComboBox = new JComboBox<>();
tagComboBox = new JComboBox<>();
int pos = 0;
int frame = 1;
for (Tag t : swf.getTags()) {
if (t instanceof ABCContainerTag) {
abcComboBox.addItem(new ComboItem(pos, (ABCContainerTag) t));
pos++;
if (t instanceof ShowFrameTag) {
frame++;
}
if (frame >= minFrame) {
if (tagType.isAssignableFrom(t.getClass())) {
tagComboBox.addItem(new ComboItem(pos, t, frame));
pos++;
}
}
}
abcComboBox.setRenderer(new DefaultListCellRenderer() {
tagComboBox.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
label.setIcon(TagTree.getIconForType(TreeNodeType.AS));
ComboItem item = (ComboItem) value;
label.setIcon(TagTree.getIconFor(item.tag));
label.setText(value.toString());
return label;
}
@@ -79,7 +86,7 @@ public class SelectDoABCDialog extends AppDialog {
buttonsPanel.add(cancelButton);
cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS));
cnt.add(abcComboBox);
cnt.add(tagComboBox);
cnt.add(buttonsPanel);
pack();
@@ -89,13 +96,13 @@ public class SelectDoABCDialog extends AppDialog {
View.centerScreen(this);
}
public ABCContainerTag getResult() {
public Tag getResult() {
return result;
}
@SuppressWarnings("unchecked")
private void okButtonActionPerformed(ActionEvent evt) {
result = ((ComboItem) abcComboBox.getSelectedItem()).abc;
result = ((ComboItem) tagComboBox.getSelectedItem()).tag;
setVisible(false);
}
@@ -104,13 +111,13 @@ public class SelectDoABCDialog extends AppDialog {
setVisible(false);
}
public ABCContainerTag showDialog() {
public Tag showDialog() {
result = null;
if (abcComboBox.getItemCount() == 0) {
if (tagComboBox.getItemCount() == 0) {
return null;
}
if (abcComboBox.getItemCount() == 1) {
result = abcComboBox.getItemAt(0).abc;
if (tagComboBox.getItemCount() == 1) {
result = tagComboBox.getItemAt(0).tag;
return result;
}
setVisible(true);
@@ -120,16 +127,18 @@ public class SelectDoABCDialog extends AppDialog {
class ComboItem {
public int index;
public ABCContainerTag abc;
public Tag tag;
public int frame;
public ComboItem(int index, ABCContainerTag abc) {
public ComboItem(int index, Tag tag, int frame) {
this.index = index;
this.abc = abc;
this.tag = tag;
this.frame = frame;
}
@Override
public String toString() {
return "" + (index + 1) + ". " + abc.toString();
return "" + (index + 1) + ". " + tag.toString() + " (frame " + frame + ")";
}
}
@@ -0,0 +1,616 @@
/*
* Copyright (C) 2024 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.abc;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.gui.AppDialog;
import static com.jpexs.decompiler.flash.gui.AppDialog.CANCEL_OPTION;
import static com.jpexs.decompiler.flash.gui.AppDialog.ERROR_OPTION;
import static com.jpexs.decompiler.flash.gui.AppDialog.OK_OPTION;
import com.jpexs.decompiler.flash.gui.SelectTagPositionDialog;
import com.jpexs.decompiler.flash.gui.View;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SymbolClassTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.helpers.LinkedIdentityHashSet;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
*
* @author JPEXS
*/
public class SetClassToCharacterMappingDialog extends AppDialog {
private final JButton proceedButton = new JButton(translate("button.proceed"));
private final JButton cancelButton = new JButton(translate("button.cancel"));
private final JTextField classNameTextField = new JTextField(30);
private final JTextField parentClassNameTextField = new JTextField(30);
private String selectedClass = null;
private String selectedParentClass = null;
private ABCContainerTag selectedAbcContainer;
private SymbolClassTag selectedSymbolClassTag;
private Tag selectedPosition;
private Timelined selectedTimelined;
private int result = ERROR_OPTION;
private final SWF swf;
private final int characterId;
private int characterFrame = -1;
private int abcCount = 0;
private int symbolClassCount = 0;
private ABCContainerTag foundInAbcContainer = null;
private final List<ABCContainerTag> abcContainers = new ArrayList<>();
private int abcFrame = -1;
private boolean createClass = false;
private final JPanel classFoundOrNotOrErrorPanel;
private final JLabel errorLabel = new JLabel();
private final JRadioButton existingAbcTagRadioButton = new JRadioButton(translate("class.notfound.create.abc.where.existing"));
private final JRadioButton newAbcTagRadioButton = new JRadioButton(translate("class.notfound.create.abc.where.new"));
private final JRadioButton createClassRadioButton = new JRadioButton(translate("class.notfound.create"));
private final JRadioButton onlySetClassNameRadioButton = new JRadioButton(translate("class.notfound.onlySetClassName"));
private final JRadioButton existingSymbolClassTagRadioButton = new JRadioButton(translate("class.notfound.onlySetClassName.symbolClass.where.existing"));
private final JRadioButton newSymbolClassTagRadioButton = new JRadioButton(translate("class.notfound.onlySetClassName.symbolClass.where.new"));
private final static String CLASS_NOT_FOUND_CARD = "Class not found panel";
private final static String CLASS_FOUND_CARD = "Class found panel";
private final static String ERROR_CARD = "Error panel";
private final static String CREATE_CLASS_CARD = "Create class panel";
private final static String DO_NOT_CREATE_CLASS_CARD = "Do not create class panel";
private final static Map<Class<?>, String> tagTypeToParentClass = new HashMap<>();
static {
tagTypeToParentClass.put(SoundTag.class, "flash.media.Sound");
tagTypeToParentClass.put(ImageTag.class, "flash.display.Bitmap");
tagTypeToParentClass.put(FontTag.class, "flash.text.Font");
tagTypeToParentClass.put(DefineFont4Tag.class, "flash.text.Font");
tagTypeToParentClass.put(DefineBinaryDataTag.class, "flash.utils.ByteArray");
tagTypeToParentClass.put(DefineSpriteTag.class, "flash.display.Sprite");
}
public static String getParentClassFromCharacter(CharacterTag ch) {
for (Class<?> cls : tagTypeToParentClass.keySet()) {
if (cls.isAssignableFrom(ch.getClass())) {
return tagTypeToParentClass.get(cls);
}
}
return null;
}
public SetClassToCharacterMappingDialog(Window owner, SWF swf, int characterId) {
super(owner);
this.swf = swf;
this.characterId = characterId;
CharacterTag ch = swf.getCharacter(characterId);
if (ch == null) {
throw new RuntimeException("Character " + characterId + " not found");
}
parentClassNameTextField.setText(getParentClassFromCharacter(ch));
int frame = 1;
for (Tag t : swf.getTags()) {
if (t == ch) {
characterFrame = frame;
}
if (t instanceof ShowFrameTag) {
frame++;
}
}
frame = 1;
for (Tag t : swf.getTags()) {
if (frame >= characterFrame) {
if (t instanceof ABCContainerTag) {
abcCount++;
abcContainers.add((ABCContainerTag) t);
}
if (t instanceof SymbolClassTag) {
symbolClassCount++;
}
}
if (t instanceof ShowFrameTag) {
frame++;
}
}
LinkedHashSet<String> classNames = ch.getClassNames();
if (classNames.size() == 1) {
classNameTextField.setText(classNames.iterator().next());
}
setDefaultCloseOperation(HIDE_ON_CLOSE);
setTitle(translate("dialog.title"));
Container cnt = getContentPane();
cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS));
JPanel classFoundPanel = new JPanel();
classFoundPanel.setLayout(new BoxLayout(classFoundPanel, BoxLayout.Y_AXIS));
JLabel classFoundLabel = new JLabel(translate("class.found"));
classFoundPanel.add(classFoundLabel);
JLabel symbolClassApropriateLabel = new JLabel(translate("symbolClassAppropriate"));
classFoundPanel.add(symbolClassApropriateLabel);
JPanel classNotFoundPanel = new JPanel();
classNotFoundPanel.setLayout(new BoxLayout(classNotFoundPanel, BoxLayout.Y_AXIS));
JLabel notFoundLabel = new JLabel(translate("class.notfound"));
classNotFoundPanel.add(notFoundLabel);
JLabel createAskLabel = new JLabel(translate("class.notfound.createAsk"));
classNotFoundPanel.add(createAskLabel);
ButtonGroup doCreateClassButtonGroup = new ButtonGroup();
doCreateClassButtonGroup.add(createClassRadioButton);
doCreateClassButtonGroup.add(onlySetClassNameRadioButton);
JPanel createNewClassAsk = new JPanel(new FlowLayout());
createNewClassAsk.add(createClassRadioButton);
createNewClassAsk.add(onlySetClassNameRadioButton);
createClassRadioButton.setSelected(true);
classNotFoundPanel.add(createNewClassAsk);
JPanel createNewClassPanel = new JPanel();
createNewClassPanel.setLayout(new BoxLayout(createNewClassPanel, BoxLayout.Y_AXIS));
JLabel parentClassNameLabel = new JLabel(translate("class.notfound.create.parentType"));
createNewClassPanel.add(parentClassNameLabel);
createNewClassPanel.add(parentClassNameTextField);
JLabel abcWhereLabel = new JLabel(translate("class.notfound.create.abc.where"));
createNewClassPanel.add(abcWhereLabel);
ButtonGroup abcTargetButtonGroup = new ButtonGroup();
abcTargetButtonGroup.add(existingAbcTagRadioButton);
abcTargetButtonGroup.add(newAbcTagRadioButton);
existingAbcTagRadioButton.setSelected(true);
JPanel abcTargetPanel = new JPanel(new FlowLayout());
abcTargetPanel.add(existingAbcTagRadioButton);
abcTargetPanel.add(newAbcTagRadioButton);
createNewClassPanel.add(abcTargetPanel);
JLabel symbolClassApropriate2Label = new JLabel(translate("symbolClassAppropriate"));
classFoundPanel.add(symbolClassApropriate2Label);
createNewClassPanel.add(symbolClassApropriate2Label);
JPanel doNotCreateNewClassPanel = new JPanel();
doNotCreateNewClassPanel.setLayout(new BoxLayout(doNotCreateNewClassPanel, BoxLayout.Y_AXIS));
ButtonGroup whereToStoreMappingButtonGroup = new ButtonGroup();
whereToStoreMappingButtonGroup.add(existingSymbolClassTagRadioButton);
whereToStoreMappingButtonGroup.add(newSymbolClassTagRadioButton);
doNotCreateNewClassPanel.add(new JLabel(translate("class.notfound.onlySetClassName.symbolClass.where")));
JPanel whereToStoreMappingPanel = new JPanel(new FlowLayout());
whereToStoreMappingPanel.add(existingSymbolClassTagRadioButton);
whereToStoreMappingPanel.add(newSymbolClassTagRadioButton);
doNotCreateNewClassPanel.add(whereToStoreMappingPanel);
existingSymbolClassTagRadioButton.setSelected(true);
setCentralAlignment(doNotCreateNewClassPanel);
setCentralAlignment(createNewClassPanel);
JPanel createNewClassAskCards = new JPanel(new CardLayout());
createNewClassAskCards.add(createNewClassPanel, CREATE_CLASS_CARD);
createNewClassAskCards.add(doNotCreateNewClassPanel, DO_NOT_CREATE_CLASS_CARD);
classNotFoundPanel.add(createNewClassAskCards);
ChangeListener createClassSwitched = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
CardLayout cl = (CardLayout) createNewClassAskCards.getLayout();
if (createClassRadioButton.isSelected()) {
cl.show(createNewClassAskCards, CREATE_CLASS_CARD);
} else {
cl.show(createNewClassAskCards, DO_NOT_CREATE_CLASS_CARD);
}
checkEnabled();
}
};
createClassRadioButton.addChangeListener(createClassSwitched);
onlySetClassNameRadioButton.addChangeListener(createClassSwitched);
JPanel buttonsPanel = new JPanel(new FlowLayout());
proceedButton.addActionListener(this::okButtonActionPerformed);
cancelButton.addActionListener(this::cancelButtonActionPerformed);
buttonsPanel.add(proceedButton);
buttonsPanel.add(cancelButton);
JLabel classNameLabel = new JLabel(translate("classname"));
cnt.add(classNameLabel);
cnt.add(classNameTextField);
JPanel errorPanel = new JPanel(new FlowLayout());
errorPanel.add(errorLabel);
setCentralAlignment(classFoundPanel);
setCentralAlignment(classNotFoundPanel);
setCentralAlignment(errorPanel);
classFoundOrNotOrErrorPanel = new JPanel(new CardLayout());
classFoundOrNotOrErrorPanel.add(classFoundPanel, CLASS_FOUND_CARD);
classFoundOrNotOrErrorPanel.add(classNotFoundPanel, CLASS_NOT_FOUND_CARD);
classFoundOrNotOrErrorPanel.add(errorPanel, ERROR_CARD);
cnt.add(classFoundOrNotOrErrorPanel);
cnt.add(buttonsPanel);
setCentralAlignment((JComponent)cnt);
if (abcCount == 0) {
newAbcTagRadioButton.setSelected(true);
//abcTargetPanel.setVisible(false);
newAbcTagRadioButton.setEnabled(false);
existingAbcTagRadioButton.setEnabled(false);
}
if (symbolClassCount == 0) {
newSymbolClassTagRadioButton.setSelected(true);
newSymbolClassTagRadioButton.setEnabled(false);
existingSymbolClassTagRadioButton.setEnabled(false);
}
existingSymbolClassTagRadioButton.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
checkEnabled();
}
});
newSymbolClassTagRadioButton.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
checkEnabled();
}
});
existingAbcTagRadioButton.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
checkEnabled();
}
});
newAbcTagRadioButton.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
checkEnabled();
}
});
classNameTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
checkEnabled();
}
@Override
public void removeUpdate(DocumentEvent e) {
checkEnabled();
}
@Override
public void changedUpdate(DocumentEvent e) {
checkEnabled();
}
});
classNameTextField.addActionListener(this::okButtonActionPerformed);
checkEnabled();
pack();
setModal(true);
setResizable(false);
View.setWindowIcon(this);
View.centerScreen(this);
}
private void setCentralAlignment(JComponent container) {
Component[] components = container.getComponents();
for (Component component : components) {
if (component instanceof JComponent) {
((JComponent) component).setAlignmentX(Component.CENTER_ALIGNMENT);
}
}
}
private void checkEnabled() {
boolean ok = true;
String newClassName = classNameTextField.getText();
if (newClassName.isEmpty()) {
ok = false;
errorLabel.setText("");
}
if (newClassName.endsWith(".")) {
ok = false;
errorLabel.setText("");
}
if (ok) {
CharacterTag ch = swf.getCharacter(characterId);
LinkedHashSet<String> oldClassNames = ch.getClassNames();
if (oldClassNames.size() > 1) {
ok = false;
errorLabel.setText(translate("error.multipleClasses"));
} else if (oldClassNames.size() == 1 && newClassName.equals(oldClassNames.iterator().next())) {
ok = false;
errorLabel.setText(translate("error.needToModify"));
} else if (swf.getCharacterByClass(newClassName) != null) {
ok = false;
errorLabel.setText(translate("error.alreadyAssignedClass"));
}
}
CardLayout cl = (CardLayout) classFoundOrNotOrErrorPanel.getLayout();
if (ok) {
List<String> classNames = new ArrayList<>();
classNames.add(newClassName);
foundInAbcContainer = null;
try {
List<ScriptPack> scriptPacks = swf.getScriptPacksByClassNames(classNames);
if (!scriptPacks.isEmpty()) {
ABC foundInAbc = scriptPacks.get(0).abc; //Assume there is only single pack with the class
boolean foundContainer = false;
for (ABCContainerTag cnt : abcContainers) {
if (cnt.getABC() == foundInAbc) {
foundInAbcContainer = cnt;
break;
}
}
}
} catch (Exception ex) {
//ignore
}
if (foundInAbcContainer != null) {
cl.show(classFoundOrNotOrErrorPanel, CLASS_FOUND_CARD);
} else {
cl.show(classFoundOrNotOrErrorPanel, CLASS_NOT_FOUND_CARD);
if (createClassRadioButton.isSelected()) {
if (existingAbcTagRadioButton.isSelected() && abcCount == 1) {
proceedButton.setText(translate("button.ok"));
} else {
proceedButton.setText(translate("button.proceed"));
}
} else {
if (existingSymbolClassTagRadioButton.isSelected() && symbolClassCount == 1) {
proceedButton.setText(translate("button.ok"));
} else {
proceedButton.setText(translate("button.proceed"));
}
}
}
} else {
cl.show(classFoundOrNotOrErrorPanel,ERROR_CARD);
proceedButton.setText(translate("button.ok"));
}
proceedButton.setEnabled(ok);
}
private void okButtonActionPerformed(ActionEvent evt) {
if (!proceedButton.isEnabled()) {
return;
}
setVisible(false);
if (foundInAbcContainer == null) {
if (createClassRadioButton.isSelected()) {
if (existingAbcTagRadioButton.isSelected()) {
SelectTagOfTypeDialog selectDoABCDialog = new SelectTagOfTypeDialog(owner, swf, ABCContainerTag.class, "DoABC", characterFrame);
selectedAbcContainer = (ABCContainerTag) selectDoABCDialog.showDialog();
if (selectedAbcContainer == null) {
cancelButtonActionPerformed(evt);
return;
}
}
if (newAbcTagRadioButton.isSelected()) {
SelectTagPositionDialog selectTagPositionDialog = new SelectTagPositionDialog(owner, swf, false, "DoABC", characterFrame);
if (selectTagPositionDialog.showDialog() != OK_OPTION) {
cancelButtonActionPerformed(evt);
return;
}
selectedPosition = selectTagPositionDialog.getSelectedTag();
selectedTimelined = selectTagPositionDialog.getSelectedTimelined();
}
} else {
if (existingSymbolClassTagRadioButton.isSelected()) {
SelectTagOfTypeDialog selectSymbolClassDialog = new SelectTagOfTypeDialog(owner, swf, SymbolClassTag.class, "SymbolClass", characterFrame);
selectedSymbolClassTag = (SymbolClassTag) selectSymbolClassDialog.showDialog();
if (selectedSymbolClassTag == null) {
cancelButtonActionPerformed(evt);
return;
}
}
if (newSymbolClassTagRadioButton.isSelected()) {
SelectTagPositionDialog selectTagPositionDialog = new SelectTagPositionDialog(owner, swf, false, "SymbolClass", characterFrame);
if (selectTagPositionDialog.showDialog() != OK_OPTION) {
cancelButtonActionPerformed(evt);
return;
}
selectedPosition = selectTagPositionDialog.getSelectedTag();
selectedTimelined = selectTagPositionDialog.getSelectedTimelined();
}
}
} else {
selectedAbcContainer = foundInAbcContainer;
}
if (selectedAbcContainer != null) {
int frame = 1;
for (Tag t : swf.getTags()) {
if (t == selectedAbcContainer) {
abcFrame = frame;
break;
}
if (t instanceof ShowFrameTag) {
frame++;
}
}
}
createClass = foundInAbcContainer == null && createClassRadioButton.isSelected();
result = OK_OPTION;
selectedClass = classNameTextField.getText();
selectedParentClass = parentClassNameTextField.getText();
setVisible(false);
}
private void cancelButtonActionPerformed(ActionEvent evt) {
selectedClass = null;
selectedParentClass = null;
selectedAbcContainer = null;
selectedPosition = null;
selectedTimelined = null;
selectedSymbolClassTag = null;
foundInAbcContainer = null;
createClass = false;
abcFrame = -1;
characterFrame = -1;
result = CANCEL_OPTION;
setVisible(false);
}
public int showDialog() {
selectedClass = null;
selectedParentClass = null;
selectedAbcContainer = null;
selectedPosition = null;
selectedTimelined = null;
selectedSymbolClassTag = null;
foundInAbcContainer = null;
createClass = false;
abcFrame = -1;
setVisible(true);
return result;
}
public Tag getSelectedPosition() {
return selectedPosition;
}
public String getSelectedClass() {
return selectedClass;
}
public String getSelectedParentClass() {
return selectedParentClass;
}
public SymbolClassTag getSelectedSymbolClassTag() {
return selectedSymbolClassTag;
}
public Timelined getSelectedTimelined() {
return selectedTimelined;
}
public ABCContainerTag getSelectedAbcContainer() {
return selectedAbcContainer;
}
public boolean isClassFound() {
return foundInAbcContainer != null;
}
public int getCharacterFrame() {
return characterFrame;
}
public int getAbcFrame() {
return abcFrame;
}
public boolean doCreateClass() {
return createClass;
}
}
@@ -1259,4 +1259,6 @@ contextmenu.showInFramesFolder = Show in frames folder
contextmenu.collapseAll = Collapse all
contextmenu.collectDepthAsSprites = Collect tags at same depth as sprites
preview.resample = Resample sound to 44kHz
preview.resample = Resample sound to 44kHz
contextmenu.setClassToCharacterMapping = Set class to character mapping
@@ -1234,4 +1234,6 @@ contextmenu.showInFramesFolder = Zobrazit ve slo\u017ece sn\u00edmk\u016f
contextmenu.collapseAll = Sbalit v\u0161e
contextmenu.collectDepthAsSprites = Posb\u00edrat tagy ve stejn\u00e9 hloubce do sprit\u016f
preview.resample = Resample sound to 44kHz
preview.resample = Resample sound to 44kHz
contextmenu.setClassToCharacterMapping = Nastavit mapov\u00e1n\u00ed t\u0159\u00eddy na charakter
@@ -17,3 +17,4 @@ dialog.title = Select tag position in %filetitle%
button.ok = OK
button.cancel = Cancel
timeline.end = end of timeline
dialog.title.new.typed = Select new %type% tag position in %filetitle%
@@ -17,3 +17,4 @@ dialog.title = Vyberte pozici tagu v %filetitle%
button.ok = OK
button.cancel = Storno
timeline.end = konec timeliny
dialog.title.new.typed = Vyberte pozici nov\u00e9ho tagu %type% v %filetitle%
@@ -0,0 +1,18 @@
# 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 <http://www.gnu.org/licenses/>.
dialog.title = Select %type% tag
button.ok = OK
button.cancel = Cancel
@@ -0,0 +1,18 @@
# 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 <http://www.gnu.org/licenses/>.
dialog.title = Vybrat %type% tag
button.ok = OK
button.cancel = Storno
@@ -0,0 +1,43 @@
# Copyright (C) 2024 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/>.
dialog.title = Set class to character mapping
button.ok = OK
button.proceed = Proceed
button.cancel = Cancel
classname = Fully qualified class name:
error.multipleClasses = Error: This character has already assigned more than single class, it cannot be renamed via this tool. However, you can still manually modify the SymbolClass tag.
error.alreadyAssignedClass = Error: This class is already assigned to different character
error.needToModify = Modify the classname to a new name.
class.found = Existing class with the name found.
class.notfound = Class with the name does not exist yet.
symbolClassAppropriate = SymbolClass tag in the nearest appropriate frame will be modified or created.
class.notfound.createAsk = Do you want the class to be created?
class.notfound.create = Yes, create class
class.notfound.create.parentType = Parent class name (fully qualified):
class.notfound.create.abc.where = Where to create byte code:
class.notfound.create.abc.where.existing = Existing DoABC tag
class.notfound.create.abc.where.new = New DoABC tag
class.notfound.onlySetClassName = No, just assign class name
class.notfound.onlySetClassName.symbolClass.where = Where to store the mapping:
class.notfound.onlySetClassName.symbolClass.where.existing = Existing SymbolClass tag
class.notfound.onlySetClassName.symbolClass.where.new = New SymbolClass tag
@@ -0,0 +1,43 @@
# Copyright (C) 2024 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/>.
dialog.title = Nastavit mapov\u00e1n\u00ed t\u0159\u00eddy na charakter
button.ok = OK
button.proceed = Pokra\u010dovat
button.cancel = Storno
classname = Pln\u011b kvalifikovan\u00fd n\u00e1zev t\u0159\u00eddy:
error.multipleClasses = Chyba: Tento charakter m\u00e1 ji\u017e p\u0159i\u0159azeno v\u00edce jak jednu t\u0159\u00eddu, nelze ho p\u0159ejmenovat t\u00edmto n\u00e1strojem. Ka\u017edopadn\u011b m\u016f\u017eete st\u00e1le upravit SymbolClass tag ru\u010dn\u011b.
error.alreadyAssignedClass = Chyba: Tato t\u0159\u00edda je ji\u017e p\u0159i\u0159azena jin\u00e9mu charakteru
error.needToModify = Upravte n\u00e1zev t\u0159\u00eddy na n\u011bjak\u00fd nov\u00fd n\u00e1zev.
class.found = Nalezena existuj\u00edc\u00ed t\u0159\u00edda s t\u00edmto n\u00e1zvem.
class.notfound = T\u0159\u00edda s t\u00edmto n\u00e1zvem zat\u00edm neexistuje.
symbolClassAppropriate = Bude vytvo\u0159en nebo upraven SymbolClass tag v nejbli\u017e\u0161\u00edm vhodn\u00e9m framu.
class.notfound.createAsk = Chcete tuto t\u0159\u00eddu vytvo\u0159it?
class.notfound.create = Ano, vytvo\u0159it t\u0159\u00eddu
class.notfound.create.parentType = N\u00e1zev rodi\u010dovsk\u00e9 t\u0159\u00eddy (pln\u011b kvalifikovan\u00fd):
class.notfound.create.abc.where = Kam um\u00edstit bajt k\u00f3d:
class.notfound.create.abc.where.existing = Existuj\u00edc\u00ed DoABC tag
class.notfound.create.abc.where.new = Nov\u00fd DoABC tag
class.notfound.onlySetClassName = Ne, jen p\u0159i\u0159adit n\u00e1zev t\u0159\u00eddy
class.notfound.onlySetClassName.symbolClass.where = Kam um\u00edstit mapov\u00e1n\u00ed:
class.notfound.onlySetClassName.symbolClass.where.existing = Existuj\u00edc\u00ed SymbolClass tag
class.notfound.onlySetClassName.symbolClass.where.new = Nov\u00fd SymbolClass tag
@@ -47,6 +47,7 @@ import com.jpexs.decompiler.flash.gui.View;
import com.jpexs.decompiler.flash.gui.ViewMessages;
import com.jpexs.decompiler.flash.gui.abc.ABCExplorerDialog;
import com.jpexs.decompiler.flash.gui.abc.AddClassDialog;
import com.jpexs.decompiler.flash.gui.abc.SetClassToCharacterMappingDialog;
import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel;
import com.jpexs.decompiler.flash.gui.action.AddScriptDialog;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
@@ -62,6 +63,7 @@ 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.ExportAssetsTag;
@@ -79,6 +81,7 @@ import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.StartSound2Tag;
import com.jpexs.decompiler.flash.tags.StartSoundTag;
import com.jpexs.decompiler.flash.tags.SymbolClassTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.TagTypeInfo;
import com.jpexs.decompiler.flash.tags.UnknownTag;
@@ -172,6 +175,8 @@ public class TagTreeContextMenu extends JPopupMenu {
private final MainPanel mainPanel;
private JMenuItem setClassToCharacterMappingMenuItem;
private JMenuItem expandRecursiveMenuItem;
private JMenuItem collapseRecursiveMenuItem;
@@ -336,8 +341,8 @@ public class TagTreeContextMenu extends JPopupMenu {
}
public TagTreeContextMenu(final List<AbstractTagTree> trees, MainPanel mainPanel) {
this.mainPanel = mainPanel;
this.mainPanel = mainPanel;
expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll"));
expandRecursiveMenuItem.addActionListener(this::expandRecursiveActionPerformed);
expandRecursiveMenuItem.setIcon(View.getIcon("expand16"));
@@ -438,6 +443,11 @@ public class TagTreeContextMenu extends JPopupMenu {
replaceRefsWithTagMenuItem.addActionListener(this::replaceRefsWithTagActionPerformed);
replaceRefsWithTagMenuItem.setIcon(View.getIcon("replacewithtag16"));
add(replaceRefsWithTagMenuItem);
setClassToCharacterMappingMenuItem = new JMenuItem(mainPanel.translate("contextmenu.setClassToCharacterMapping")); //FIXME
setClassToCharacterMappingMenuItem.addActionListener(this::setClassToCharacterMappingActionPerformed);
setClassToCharacterMappingMenuItem.setIcon(View.getIcon("asclass16"));
add(setClassToCharacterMappingMenuItem);
abcExplorerMenuItem = new JMenuItem(mainPanel.translate("contextmenu.abcexplorer"));
abcExplorerMenuItem.addActionListener(this::abcExplorerActionPerformed);
@@ -1104,6 +1114,7 @@ public class TagTreeContextMenu extends JPopupMenu {
boolean hasExportableNodes = tree.hasExportableNodes();
setClassToCharacterMappingMenuItem.setVisible(false);
expandRecursiveMenuItem.setVisible(false);
collapseRecursiveMenuItem.setVisible(false);
pinMenuItem.setVisible(false);
@@ -1342,6 +1353,12 @@ public class TagTreeContextMenu extends JPopupMenu {
addTagAfterMenu.add(othersMenu);*/
addTagAfterMenu.setVisible(addTagAfterMenu.getItemCount() > 0);
if ((firstItem instanceof CharacterTag)) {
if (SetClassToCharacterMappingDialog.getParentClassFromCharacter((CharacterTag) firstItem) != null) {
setClassToCharacterMappingMenuItem.setVisible(true);
}
}
if (tree.getModel().getChildCount(firstItem) > 0) {
expandRecursiveMenuItem.setVisible(true);
collapseRecursiveMenuItem.setVisible(true);
@@ -1945,7 +1962,7 @@ public class TagTreeContextMenu extends JPopupMenu {
selectedTimelined = (SWF) item;
}
SelectTagPositionDialog selectPositionDialog = new SelectTagPositionDialog(mainPanel.getMainFrame().getWindow(), swf, selectedTag, selectedTimelined, true, selectNext);
SelectTagPositionDialog selectPositionDialog = new SelectTagPositionDialog(mainPanel.getMainFrame().getWindow(), swf, selectedTag, selectedTimelined, true, selectNext, null, 1);
if (selectPositionDialog.showDialog() == AppDialog.OK_OPTION) {
selectedTimelined = selectPositionDialog.getSelectedTimelined();
selectedTag = selectPositionDialog.getSelectedTag();
@@ -2485,6 +2502,163 @@ public class TagTreeContextMenu extends JPopupMenu {
mainPanel.setTagTreeSelectedNode(mainPanel.getCurrentTree(), ((SWF) itemj.getOpenable()).getCharacter(hasCharacterId.getCharacterId()));
}
/*
How set class to character mapping work in AS3:
a) a character is selected
b) if the character already has assigned more than 1 class then exit with a message that user must do this manually
c) new class name is entered, must be different than previous
d) if new class name is already assigned to different character, exit
e) find the ABC, where class with that name is defined
if found
determine its frame
find nearest frame >= ABC frame, such that the character is defined in it (or is defined earlier in the file)
if the SymbolClass exists in that frame, use that one
otherwise create new SymbolClass in that frame
else (not found)
suggest creating new class
select parent class (extends) - the default is set according to type of the character(sound = flash.media.Sound, etc.)
choose whether to use existing ABC or add new ABC
AFTER PRESSING OK:
select existing ABC or add new ABC (select its position).
find nearest frame >= ABC frame, such that the character is defined in it (or is defined earlier in the file)
if the SymbolClass exists in that frame, use that one
otherwise create new SymbolClass in that frame
or suggest not creating class, set only its name
choose whether to use existing Symbolclass or add new
AFTER PRESSING OK:
select existing SymbolClass or add new SymbolClass (on selected position).
(Selected frame for target SymbolClass must have defined the character in it or earlier in the file)
AFTER PRESSING OK:
I. if there is previously assigned classname, find associated SymbolClass and remove the mapping from it
II. if the previous Symbolclass is empty, remove it
III. add new character mapping to new SymbolClass determined
*/
private void setClassToCharacterMappingActionPerformed(ActionEvent evt) {
CharacterTag ch = (CharacterTag) getCurrentItem();
SWF swf = ch.getSwf();
SetClassToCharacterMappingDialog d = new SetClassToCharacterMappingDialog(Main.getDefaultDialogsOwner(), swf, ch.getCharacterId());
if (d.showDialog() != AppDialog.OK_OPTION) {
return;
}
int abcFrame = d.getAbcFrame();
int characterFrame = d.getCharacterFrame();
SymbolClassTag selectedSymbolClass = d.getSelectedSymbolClassTag();
ABCContainerTag selectedAbcContainer = d.getSelectedAbcContainer();
String className = d.getSelectedClass();
String parentClassName = d.getSelectedParentClass();
if (!d.isClassFound() && !d.doCreateClass() && selectedSymbolClass == null) { //we selected position of new SymbolClass
selectedSymbolClass = new SymbolClassTag(swf);
selectedSymbolClass.setTimelined(swf);
Tag pos = d.getSelectedPosition();
if (pos == null) {
swf.addTag(selectedSymbolClass);
} else {
swf.addTag(swf.indexOfTag(pos), selectedSymbolClass);
}
}
if (!d.isClassFound() && d.doCreateClass()) {
if (d.getSelectedAbcContainer() == null) {
selectedAbcContainer = new DoABC2Tag(swf);
((Tag)selectedAbcContainer).setTimelined(swf);
Tag pos = d.getSelectedPosition();
if (pos == null) {
swf.addTag((Tag) selectedAbcContainer);
} else {
swf.addTag(swf.indexOfTag(pos), (Tag) selectedAbcContainer);
}
}
String pkg = className.contains(".") ? className.substring(0, className.lastIndexOf(".")) : "";
String classSimpleName = className.contains(".") ? className.substring(className.lastIndexOf(".") + 1) : className;
String fileName = className.replace(".", "/");
String[] pkgParts = new String[0];
if (!pkg.isEmpty()) {
if (pkg.contains(".")) {
pkgParts = pkg.split("\\.");
} else {
pkgParts = new String[]{pkg};
}
}
try {
AbcIndexing abcIndex = swf.getAbcIndex();
abcIndex.selectAbc(selectedAbcContainer.getABC());
ActionScript3Parser parser = new ActionScript3Parser(abcIndex);
DottedChain dc = new DottedChain(pkgParts);
String script = "package " + dc.toPrintableString(true) + " {"
+ (parentClassName.isEmpty() ? "" : "import " + parentClassName + ";")
+ "public class " + IdentifiersDeobfuscation.printIdentifier(true, classSimpleName) + (parentClassName.isEmpty() ? "" : " extends " + parentClassName) + " {"
+ " }"
+ "}";
parser.addScript(script, fileName, 0, 0, swf.getDocumentClass());
} catch (IOException | InterruptedException | AVM2ParseException | CompilationException ex) {
Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, "Error during script compilation", ex);
}
((Tag) selectedAbcContainer).setModified(true);
}
if (selectedSymbolClass == null) {
int symbolClassFrame = Math.max(abcFrame, characterFrame);
int frame = 1;
int pos = 0;
int symbolClassPos = -1;
for (Tag t : swf.getTags()) {
if ((t instanceof SymbolClassTag) && (symbolClassFrame == frame)) {
selectedSymbolClass = (SymbolClassTag) t;
break;
}
if (t instanceof ShowFrameTag) {
if (symbolClassFrame == frame) {
symbolClassPos = pos;
}
frame++;
}
pos++;
}
if (selectedSymbolClass == null) {
if (symbolClassPos == -1) {
symbolClassPos = pos;
}
selectedSymbolClass = new SymbolClassTag(swf);
selectedSymbolClass.setTimelined(swf);
swf.addTag(symbolClassPos, selectedSymbolClass);
}
}
//remove previously assigned name
ReadOnlyTagList tags = swf.getTags();
for (int j = 0; j < tags.size(); j++) {
Tag t = tags.get(j);
if (t instanceof SymbolClassTag) {
SymbolClassTag sct = (SymbolClassTag) t;
for (int i = sct.tags.size() - 1; i >= 0; i--) {
if (sct.tags.get(i) == ch.getCharacterId()) {
sct.names.remove(i);
sct.tags.remove(i);
}
}
if (sct.names.isEmpty() && sct != selectedSymbolClass) {
swf.removeTag(t);
}
}
}
selectedSymbolClass.tags.add(ch.getCharacterId());
selectedSymbolClass.names.add(className);
swf.clearAllCache();
swf.assignClassesToSymbols();
swf.setModified(true);
mainPanel.refreshTree(swf);
}
private void expandRecursiveActionPerformed(ActionEvent evt) {
AbstractTagTree tree = getTree();
TreePath path = tree.getFullModel().getTreePath(getCurrentItem());
@@ -3315,6 +3489,8 @@ public class TagTreeContextMenu extends JPopupMenu {
public void run() {
Map<SWF, List<Tag>> tagsToRemoveBySwf = new HashMap<>();
Set<SWF> swfsToClearCache = new HashSet<>();
Set<SWF> swfToAssignClassesToSymbols = new HashSet<>();
Set<SWF> swfToAssignExportNamesToSymbols = new HashSet<>();
for (int i = 0; i < itemsToRemove.size(); i++) {
Object item = itemsToRemove.get(i);
@@ -3422,7 +3598,7 @@ public class TagTreeContextMenu extends JPopupMenu {
}
}
}
}
}
if (item instanceof TreeItem) {
mainPanel.unpinItem((TreeItem) item);
@@ -3449,13 +3625,24 @@ public class TagTreeContextMenu extends JPopupMenu {
}
for (Tag tag : tagsToRemove) {
SWF swf = tag.getSwf();
if (tag instanceof SymbolClassTag) {
swfToAssignClassesToSymbols.add(swf);
}
if (tag instanceof ExportAssetsTag) {
swfToAssignExportNamesToSymbols.add(swf);
}
if (!tagsToRemoveBySwf.containsKey(swf)) {
tagsToRemoveBySwf.put(swf, new ArrayList<>());
}
tagsToRemoveBySwf.get(swf).add(tag);
mainPanel.unpinItem(tag);
mainPanel.unpinItem(tag);
}
for (SWF swf : tagsToRemoveBySwf.keySet()) {
@@ -3484,6 +3671,14 @@ public class TagTreeContextMenu extends JPopupMenu {
swf.clearAllCache();
}
for (SWF swf : swfToAssignClassesToSymbols) {
swf.assignClassesToSymbols();
}
for (SWF swf: swfToAssignExportNamesToSymbols) {
swf.assignExportNamesToSymbols();
}
if (!framesToRemove.isEmpty()) {
Timelined tim = framesToRemove.get(0).timeline.timelined;
SWF swf = tim.getSwf();
@@ -3713,7 +3908,7 @@ public class TagTreeContextMenu extends JPopupMenu {
return;
}
SelectTagPositionDialog dialog = new SelectTagPositionDialog(Main.getDefaultDialogsOwner(), t.getSwf(), t, timelined, true, false);
SelectTagPositionDialog dialog = new SelectTagPositionDialog(Main.getDefaultDialogsOwner(), t.getSwf(), t, timelined, true, false, null, 1);
if (dialog.showDialog() == AppDialog.OK_OPTION) {
Tag selectedTag = dialog.getSelectedTag();
Timelined selectedTimelined = dialog.getSelectedTimelined();