diff --git a/CHANGELOG.md b/CHANGELOG.md index 7092ec91e..3887f6e2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added +- [#1870] AS3 Adding new class - Target DoABC tag or position can be selected to prevent Error 1014 + ### Fixed - [#1869] Replace references now replaces all references, not just PlaceObject - Handle StartSound tag as CharacterIdTag @@ -2581,6 +2584,7 @@ All notable changes to this project will be documented in this file. [alpha 9]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha8...alpha9 [alpha 8]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha7...alpha8 [alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 +[#1870]: https://www.free-decompiler.com/flash/issues/1870 [#1869]: https://www.free-decompiler.com/flash/issues/1869 [#1867]: https://www.free-decompiler.com/flash/issues/1867 [#1868]: https://www.free-decompiler.com/flash/issues/1868 diff --git a/src/com/jpexs/decompiler/flash/gui/AppDialog.java b/src/com/jpexs/decompiler/flash/gui/AppDialog.java index b981a1a5c..f996f80a3 100644 --- a/src/com/jpexs/decompiler/flash/gui/AppDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/AppDialog.java @@ -36,6 +36,8 @@ public abstract class AppDialog extends JDialog { private ResourceBundle resourceBundle = ResourceBundle.getBundle(AppStrings.getResourcePath(getClass())); + protected Window owner; + /*public AppDialog() { View.installEscapeCloseOperation(this); if (Configuration.useRibbonInterface.get()) { @@ -45,6 +47,7 @@ public abstract class AppDialog extends JDialog { public AppDialog(Window owner) { super(owner); + this.owner = owner; View.installEscapeCloseOperation(this); if (Configuration.useRibbonInterface.get()) { getRootPane().setWindowDecorationStyle(JRootPane.FRAME); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/AddClassDialog.java b/src/com/jpexs/decompiler/flash/gui/abc/AddClassDialog.java index fa381820f..645123372 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/AddClassDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/AddClassDialog.java @@ -20,7 +20,11 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.gui.AppDialog; import com.jpexs.decompiler.flash.gui.Main; +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.Tag; +import com.jpexs.decompiler.flash.timeline.Timelined; import java.awt.Component; import java.awt.Container; import java.awt.FlowLayout; @@ -29,12 +33,17 @@ import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; import javax.swing.JButton; 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; +import javax.swing.plaf.basic.BasicMenuUI; /** * @@ -42,32 +51,80 @@ import javax.swing.event.DocumentListener; */ public class AddClassDialog extends AppDialog { - private final JButton okButton = new JButton(translate("button.ok")); + 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 String result = null; + private String selectedClass = null; + private ABCContainerTag selectedAbcContainer; + private Tag selectedPosition; + private Timelined selectedTimelined; + private int result = ERROR_OPTION; + + private SWF swf; + + private int abcCount = 0; + + private JRadioButton existingAbcTagRadioButton = new JRadioButton(translate("abc.where.existing")); + private JRadioButton newAbcTagRadioButton = new JRadioButton(translate("abc.where.new")); - public AddClassDialog(Window owner) { + public AddClassDialog(Window owner, SWF swf) { super(owner); + this.swf = swf; + abcCount = 0; + for(Tag t : swf.getTags()) { + if (t instanceof ABCContainerTag) { + abcCount++; + } + } + setDefaultCloseOperation(HIDE_ON_CLOSE); setTitle(translate("dialog.title")); Container cnt = getContentPane(); cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS)); - JPanel panButtons = new JPanel(new FlowLayout()); - okButton.addActionListener(this::okButtonActionPerformed); + JPanel abcTargetPanel = new JPanel(); + abcTargetPanel.setLayout(new BoxLayout(abcTargetPanel, BoxLayout.Y_AXIS)); + abcTargetPanel.add(new JLabel(translate("abc.where"))); + existingAbcTagRadioButton.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + checkEnabled(); + } + }); + newAbcTagRadioButton.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + checkEnabled(); + } + }); + ButtonGroup abcTargetButtonGroup = new ButtonGroup(); + abcTargetButtonGroup.add(existingAbcTagRadioButton); + abcTargetButtonGroup.add(newAbcTagRadioButton); + existingAbcTagRadioButton.setSelected(true); + abcTargetPanel.add(existingAbcTagRadioButton); + abcTargetPanel.add(newAbcTagRadioButton); + + if (abcCount == 0) { + newAbcTagRadioButton.setSelected(true); + abcTargetPanel.setVisible(false); + } + + JPanel buttonsPanel = new JPanel(new FlowLayout()); + proceedButton.addActionListener(this::okButtonActionPerformed); cancelButton.addActionListener(this::cancelButtonActionPerformed); - panButtons.add(okButton); - panButtons.add(cancelButton); + buttonsPanel.add(proceedButton); + buttonsPanel.add(cancelButton); JLabel classNameLabel = new JLabel(translate("classname")); - classNameLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + classNameLabel.setAlignmentX(JLabel.CENTER); cnt.add(classNameLabel); cnt.add(classNameTextField); + + cnt.add(abcTargetPanel); - cnt.add(panButtons); + cnt.add(buttonsPanel); classNameTextField.getDocument().addDocumentListener(new DocumentListener() { @Override @@ -97,7 +154,14 @@ public class AddClassDialog extends AppDialog { } private void checkEnabled() { - + + if (existingAbcTagRadioButton.isSelected() && abcCount == 1) { + proceedButton.setText(translate("button.ok")); + } else { + proceedButton.setText(translate("button.proceed")); + } + + boolean ok = true; if (classNameTextField.getText().isEmpty()) { @@ -122,29 +186,72 @@ public class AddClassDialog extends AppDialog { } } - okButton.setEnabled(ok); + proceedButton.setEnabled(ok); } private void okButtonActionPerformed(ActionEvent evt) { - if (!okButton.isEnabled()) { + if (!proceedButton.isEnabled()) { return; } - result = classNameTextField.getText(); setVisible(false); + if (existingAbcTagRadioButton.isSelected()) { + SelectDoABCDialog selectDoABCDialog = new SelectDoABCDialog(owner, swf); + selectedAbcContainer = selectDoABCDialog.showDialog(); + if (selectedAbcContainer == null) { + cancelButtonActionPerformed(evt); + return; + } + } + if (newAbcTagRadioButton.isSelected()) { + SelectTagPositionDialog selectTagPositionDialog = new SelectTagPositionDialog(owner, swf, true); + if (selectTagPositionDialog.showDialog() != OK_OPTION) { + cancelButtonActionPerformed(evt); + return; + } + selectedPosition = selectTagPositionDialog.getSelectedTag(); + selectedTimelined = selectTagPositionDialog.getSelectedTimelined(); + } + + result = OK_OPTION; + selectedClass = classNameTextField.getText(); + setVisible(false); } private void cancelButtonActionPerformed(ActionEvent evt) { - result = null; + selectedClass = null; + selectedAbcContainer = null; + selectedPosition = null; + selectedTimelined = null; + result = CANCEL_OPTION; setVisible(false); } - public String showDialog() { + public int showDialog() { return showDialog(""); } - public String showDialog(String pkg) { + public int showDialog(String pkg) { classNameTextField.setText(pkg); - result = null; + selectedClass = null; + selectedAbcContainer = null; + selectedPosition = null; + selectedTimelined = null; setVisible(true); return result; } + + public Tag getSelectedPosition() { + return selectedPosition; + } + + public String getSelectedClass() { + return selectedClass; + } + + public Timelined getSelectedTimelined() { + return selectedTimelined; + } + + public ABCContainerTag getSelectedAbcContainer() { + return selectedAbcContainer; + } } diff --git a/src/com/jpexs/decompiler/flash/gui/abc/SelectDoABCDialog.java b/src/com/jpexs/decompiler/flash/gui/abc/SelectDoABCDialog.java new file mode 100644 index 000000000..463eeaf41 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/abc/SelectDoABCDialog.java @@ -0,0 +1,138 @@ +/* + * 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.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.Tag; +import java.awt.Component; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Window; +import java.awt.event.ActionEvent; +import javax.swing.BoxLayout; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; + +/** + * + * @author JPEXS + */ +public class SelectDoABCDialog extends AppDialog { + + private JComboBox abcComboBox; + private ABCContainerTag result = null; + + public SelectDoABCDialog(Window window, SWF swf) { + super(window); + setTitle(translate("dialog.title")); + Container cnt = getContentPane(); + + abcComboBox = new JComboBox<>(); + int pos = 0; + for(Tag t : swf.getTags()) { + if (t instanceof ABCContainerTag) { + abcComboBox.addItem(new ComboItem(pos, (ABCContainerTag) t)); + pos++; + } + } + + abcComboBox.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)); + label.setText(value.toString()); + return label; + } + }); + + JButton okButton = new JButton(translate("button.ok")); + okButton.addActionListener(this::okButtonActionPerformed); + JButton cancelButton = new JButton(translate("button.cancel")); + cancelButton.addActionListener(this::cancelButtonActionPerformed); + + JPanel buttonsPanel = new JPanel(new FlowLayout()); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS)); + cnt.add(abcComboBox); + cnt.add(buttonsPanel); + + pack(); + setModal(true); + setResizable(false); + View.setWindowIcon(this); + View.centerScreen(this); + } + + public ABCContainerTag getResult() { + return result; + } + + @SuppressWarnings("unchecked") + private void okButtonActionPerformed(ActionEvent evt) { + result = ((ComboItem) abcComboBox.getSelectedItem()).abc; + setVisible(false); + } + + private void cancelButtonActionPerformed(ActionEvent evt) { + result = null; + setVisible(false); + } + + public ABCContainerTag showDialog() { + result = null; + if (abcComboBox.getItemCount() == 0) { + return null; + } + if (abcComboBox.getItemCount() == 1) { + result = abcComboBox.getItemAt(0).abc; + return result; + } + setVisible(true); + return result; + } + +} + +class ComboItem { + public int index; + public ABCContainerTag abc; + + public ComboItem(int index, ABCContainerTag abc) { + this.index = index; + this.abc = abc; + } + + @Override + public String toString() { + return "" + (index + 1) +". " + abc.toString(); + } + + +} \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/abc/AddClassDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/abc/AddClassDialog.properties index 0b8a039d1..c42e56179 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/abc/AddClassDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/abc/AddClassDialog.properties @@ -17,4 +17,10 @@ dialog.title = Add script button.ok = OK button.cancel = Cancel -classname = Fully qualified class name: \ No newline at end of file +classname = Fully qualified class name: + +#after 16.3.1 +button.proceed = Proceed +abc.where = Where to create byte code: +abc.where.existing = Existing DoABC tag +abc.where.new = New DoABC tag diff --git a/src/com/jpexs/decompiler/flash/gui/locales/abc/AddClassDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/abc/AddClassDialog_cs.properties index f85ee5568..bfcdc2bdf 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/abc/AddClassDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/abc/AddClassDialog_cs.properties @@ -18,3 +18,9 @@ button.ok = OK button.cancel = Storno classname = Pln\u011b kvalifikovan\u00fd n\u00e1zev t\u0159\u00eddy: + +#after 16.3.1 +button.proceed = Pokra\u010dovat +abc.where = Kam um\u00edstit byte k\u00f3d: +abc.where.existing = Existuj\u00edc\u00ed DoABC tag +abc.where.new = Nov\u00fd DoABC tag \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/abc/SelectDoABCDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/abc/SelectDoABCDialog.properties new file mode 100644 index 000000000..96e66c9de --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/locales/abc/SelectDoABCDialog.properties @@ -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 . + +dialog.title = Select DoABC tag +button.ok = OK +button.cancel = Cancel \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/abc/SelectDoABCDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/abc/SelectDoABCDialog_cs.properties new file mode 100644 index 000000000..bd4a2f443 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/locales/abc/SelectDoABCDialog_cs.properties @@ -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 . + +dialog.title = Vyberte DoABC tag +button.ok = OK +button.cancel = Storno \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 2be28390f..3a51a1474 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -1570,35 +1570,35 @@ public class TagTreeContextMenu extends JPopupMenu { } { - AddClassDialog acd = new AddClassDialog(Main.getDefaultDialogsOwner()); - String className = acd.showDialog(preselected); - if (className == null) { + AddClassDialog acd = new AddClassDialog(Main.getDefaultDialogsOwner(), swf); + if (acd.showDialog(preselected) != AppDialog.OK_OPTION) { return; } + String className = acd.getSelectedClass(); String[] parts = className.contains(".") ? className.split("\\.") : new String[]{className}; - DoABC2Tag doAbc = new DoABC2Tag(swf); - doAbc.setTimelined(swf); - doAbc.name = className; + ABCContainerTag doAbc = acd.getSelectedAbcContainer(); + + if (doAbc == null) { + DoABC2Tag doAbc2 = new DoABC2Tag(swf); + + Timelined timelined = acd.getSelectedTimelined(); + Tag position = acd.getSelectedPosition(); + if (position == null) { + timelined.addTag(doAbc2); + } else { + timelined.addTag(timelined.indexOfTag(position), doAbc2); + } + doAbc2.setTimelined(acd.getSelectedTimelined()); + doAbc2.name = className; + doAbc = doAbc2; + } List abcs = new ArrayList<>(); for (ABCContainerTag ct : swf.getAbcList()) { abcs.add(ct.getABC()); } - - ReadOnlyTagList tags = swf.getTags(); - int insertPos = -1; - for (int i = 0; i < tags.size(); i++) { - if (tags.get(i) instanceof ShowFrameTag) { - insertPos = i; - break; - } - } - if (insertPos == -1) { - insertPos = tags.size(); - } - swf.addTag(insertPos, doAbc); - + String pkg = className.contains(".") ? className.substring(0, className.lastIndexOf(".")) : ""; String classSimpleName = className.contains(".") ? className.substring(className.lastIndexOf(".") + 1) : className; String fileName = className.replace(".", "/"); @@ -1623,7 +1623,7 @@ public class TagTreeContextMenu extends JPopupMenu { Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, "Error during script compilation", ex); } - doAbc.setModified(true); + ((Tag)doAbc).setModified(true); swf.clearAllCache(); swf.setModified(true); mainPanel.refreshTree(swf);