From c9c75d0c00044446be2f780c8038f6e6a336900e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 25 May 2025 17:49:23 +0200 Subject: [PATCH] Added: #1682 AS2 - Adding class by context menu on packages --- CHANGELOG.md | 3 +- .../flash/gui/action/AddScriptDialog.java | 39 ++- .../flash/gui/graphics/scriptadd32.png | Bin 0 -> 1492 bytes .../locales/action/AddScriptDialog.properties | 2 + .../action/AddScriptDialog_cs.properties | 2 + .../flash/gui/tagtree/TagTreeContextMenu.java | 308 +++++++++++------- 6 files changed, 233 insertions(+), 121 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/scriptadd32.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 70673a2b4..487354129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable changes to this project will be documented in this file. - [#289] Support for Aero Snap on Windows - [#2412] Show coordinates on stage mouse move for up to 2 decimal places - "Show detail" context menu item for items in folder preview +- [#1682] AS2 - Adding class by context menu on packages ### Fixed - [#2456] FLA export - NullPointer exception while exporting to CS4 or lower via commandline @@ -3811,6 +3812,7 @@ Major version of SWF to XML export changed to 2. [#1418]: https://www.free-decompiler.com/flash/issues/1418 [#289]: https://www.free-decompiler.com/flash/issues/289 [#2412]: https://www.free-decompiler.com/flash/issues/2412 +[#1682]: https://www.free-decompiler.com/flash/issues/1682 [#2456]: https://www.free-decompiler.com/flash/issues/2456 [#2459]: https://www.free-decompiler.com/flash/issues/2459 [#2427]: https://www.free-decompiler.com/flash/issues/2427 @@ -4073,7 +4075,6 @@ Major version of SWF to XML export changed to 2. [#1888]: https://www.free-decompiler.com/flash/issues/1888 [#1892]: https://www.free-decompiler.com/flash/issues/1892 [#355]: https://www.free-decompiler.com/flash/issues/355 -[#1682]: https://www.free-decompiler.com/flash/issues/1682 [#1981]: https://www.free-decompiler.com/flash/issues/1981 [#1982]: https://www.free-decompiler.com/flash/issues/1982 [#1986]: https://www.free-decompiler.com/flash/issues/1986 diff --git a/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java b/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java index 3fe36535b..62a9b29dc 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java @@ -46,6 +46,8 @@ import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; @@ -122,6 +124,11 @@ public class AddScriptDialog extends AppDialog { private List existingClasses = new ArrayList<>(); private List spriteIdsWithDoInitAction; + private boolean createClassSet = false; + + private final JPanel classPanel; + private final JPanel topPanel; + public AddScriptDialog(Window owner, SWF swf) { super(owner); setDefaultCloseOperation(HIDE_ON_CLOSE); @@ -151,7 +158,7 @@ public class AddScriptDialog extends AppDialog { panButtons.add(okButton); panButtons.add(cancelButton); - JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JLabel typeLabel = new JLabel(translate("type")); typeComboBox = new JComboBox<>(new String[]{ @@ -196,7 +203,7 @@ public class AddScriptDialog extends AppDialog { centerPanel.add(createSpriteInitPanel(), "" + TYPE_SPRITE_INIT); centerPanel.add(createButtonPanel(), "" + TYPE_BUTTON_EVENT); centerPanel.add(createInstancePanel(), "" + TYPE_INSTANCE_EVENT); - centerPanel.add(createClassPanel(), "" + TYPE_CLASS); + centerPanel.add(classPanel = createClassPanel(), "" + TYPE_CLASS); cnt.add(centerPanel, BorderLayout.CENTER); cnt.add(panButtons, BorderLayout.SOUTH); @@ -204,10 +211,33 @@ public class AddScriptDialog extends AppDialog { setSize(900, 600); setModal(true); setResizable(true); - View.setWindowIcon(this); + View.setWindowIcon(this, "scriptadd"); View.centerScreen(this); checkEnabled(); + addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + if (createClassSet) { + classNameTextField.requestFocusInWindow(); + } + } + }); + } + + public void setCreateClassOnly(String prefillClassName) { + setTitle(translate("dialog.title.combined").replace("%title%", translate("dialog.title")).replace("%type%", translate("type.class"))); + typeComboBox.setSelectedIndex(5); + centerPanel.removeAll(); + centerPanel.add(classPanel, "" + TYPE_CLASS); + ((CardLayout) centerPanel.getLayout()).show(centerPanel, "" + TYPE_CLASS); + topPanel.setVisible(false); + pack(); + classNameTextField.setText(prefillClassName); + classNameTextField.setSelectionStart(prefillClassName.length()); + createClassSet = true; + checkEnabled(); + View.centerScreen(this); } private JPanel createFramePanel(DocumentListener checkEnabledDocumentListener) { @@ -973,6 +1003,9 @@ public class AddScriptDialog extends AppDialog { } if (type == TYPE_CLASS) { + if (classNameTextField.getText().trim().endsWith(".")) { + okButton.setEnabled(false); + } if (classNameTextField.getText().trim().isEmpty()) { okButton.setEnabled(false); } diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/scriptadd32.png b/src/com/jpexs/decompiler/flash/gui/graphics/scriptadd32.png new file mode 100644 index 0000000000000000000000000000000000000000..572a5e3f839e2e8daf66ea76c25ab6b7638ebafd GIT binary patch literal 1492 zcmV;_1uOcAP)!12~z+}K)Jd;>7-SN($6 zL;#p;&8PFDlp92UbbmI=2;laF!C*-IX|p%BmL>Bh0w8lHPhLJf%lcuOV+%hqCta1T z=N8XE^wd2Mgjpg0aAhxWr)v=h{2yW^krQ+c`l0a9&0xGZPZa-Lcg~+T#RV_je<;TN zM|%Y-Ib2)6mnb!d;t5Ye048UyOs7T;Xu?bqel7fDg!ntKhAz{~XwX*?7 zTguVY+Gv4LBw&e?DBX!~!Uihpcc3ZuhT;OCL%Zib=CV#N9FV25xvb;B$#>D%dI%-+ zOY!#NJxJ4?*4%*M$7N6RvigfZohhpJeN|VE4N-@T3jof{!mM;1LDRHEh_PgGE)xI; z_Sdb%_1jelYE(4^* zbBKZ4J_80_K`5aKsa0^L^EfVd9K)uPT{zudgC@^+==XWC?=^1OZQorqdJf^w>s46) z@>Wc8WvpFNh8F~VQrr-f)%?k%f&r0)>}!Z;VN2*W{C=Sls}`)rrJDz6vkvRtV_9Ri zFWmBaaG~pK6wg}?-HmFYQ_@2b@cR4HMA#R&9O(BO5r5%GXpC0BND)?$>qf`WRg0AX z>0K~J^Tf006Yo4jmyw@MzM)tQJX8-b8s}g!sZ_O-N&H+dL!%Wm8q(uGXX$LsOAm|I zzLNWWA021t_MMG7wwMkr|C73a*D$T3BS6#|BtGneBd|)Kq#;1l6*rbm1nvggEt*HH zaB0jijkT9$0zkUoFoOfzi?y+4yu~{x&-%LIpcN_`NOXt_KS|EsqR7eLt7C8|kqb|w zF?al(_k2XLZ1~pcgDsw=CC}u+XFFa>saBF{VHFE!EJbf$1E%ChoXrf2clXDMte&n$ zPOpgT>LrEsLMS5_00P6`@2o6$=bUorGG^sYu$WJ!1R?RY((UqRw_{&j10J73o6trg zedsacbm~);$mWcey+(CWXAAZU|711`xsyuE-jQdosHWU6hf}TjnD{DmS{I(sYcSpQ zBNjdoEVCqKhx>gkHQ7}d!j0+DAwkEO{=wqR4%G_0IC zgHDutzvvGUnC@=i?^e pkgParts = new ArrayList<>(); + while (it instanceof AS2Package) { + pkg = (AS2Package) it; + if (!pkg.isDefaultPackage()) { + pkgParts.add(0, pkg.getName()); + } + it = model.getParent(it); + } + pkgParts.remove(0); + String fullPkgName = String.join(".", pkgParts); + SWF swf = (SWF) pkg.getOpenable(); + AddScriptDialog addScriptDialog = new AddScriptDialog(Main.getDefaultDialogsOwner(), swf); + String prefix = fullPkgName; + if (!prefix.isEmpty()) { + prefix += "."; + } + addScriptDialog.setCreateClassOnly(prefix); + if (addScriptDialog.showDialog() != JOptionPane.OK_OPTION) { + return; + } + createClass(addScriptDialog.getClassName(), swf); + } + private void addAs3ClassActionPerformed(ActionEvent evt) { AbstractTagTree tree = getTree(); //using tagTree only here is safe since tagListTree does not have AS3 classes @@ -3729,6 +3782,130 @@ public class TagTreeContextMenu extends JPopupMenu { addSpriteInitScript(sprite.getSwf(), sprite); } + private void createClass(String className, SWF swf) { + ReadOnlyTagList tags = swf.getTags(); + List exportedIds = new ArrayList<>(); + for (int i = 0; i < tags.size(); i++) { + if (tags.get(i) instanceof ExportAssetsTag) { + ExportAssetsTag ea = (ExportAssetsTag) tags.get(i); + exportedIds.addAll(ea.tags); + } + } + + int insertPos = -1; + for (int i = 0; i < tags.size(); i++) { + if (tags.get(i) instanceof DoInitActionTag) { + DoInitActionTag doinit = (DoInitActionTag) tags.get(i); + if (!exportedIds.contains(doinit.spriteId)) { + //this is #initpragma, make sure class is inserted before it + insertPos = i; + break; + } + } + } + if (insertPos == -1) { + for (int i = 0; i < tags.size(); i++) { + if (tags.get(i) instanceof ShowFrameTag) { + insertPos = i; + break; + } + } + } + + if (insertPos > -1) { + int characterId = swf.getNextCharacterId(); + DefineSpriteTag sprite = new DefineSpriteTag(swf); + sprite.spriteId = characterId; + sprite.hasEndTag = true; + sprite.setTimelined(swf); + + String exportName = "__Packages." + className; + + ExportAssetsTag exportAssets = new ExportAssetsTag(swf); + exportAssets.names = new ArrayList<>(); + exportAssets.names.add(exportName); + exportAssets.tags = new ArrayList<>(); + exportAssets.tags.add(characterId); + exportAssets.setTimelined(swf); + + DoInitActionTag doInit = new DoInitActionTag(swf); + doInit.spriteId = characterId; + doInit.setTimelined(swf); + + ActionScript2Parser parser = new ActionScript2Parser(swf, doInit); + + String[] parts = className.contains(".") ? className.split("\\.") : new String[]{className}; + DottedChain dc = new DottedChain(parts); + + try { + List actions = parser.actionsFromString("class " + dc.toPrintableString(false) + "{}", swf.getCharset()); + doInit.setActions(actions); + } catch (ActionParseException | IOException | CompilationException + | InterruptedException ex) { + //ignore + } + + sprite.setExportName(exportName); + + swf.addTag(insertPos, sprite); + swf.addTag(insertPos + 1, exportAssets); + swf.addTag(insertPos + 2, doInit); + + swf.clearAllCache(); + swf.setModified(true); + mainPanel.refreshTree(swf); + + FolderItem scriptsNode = (FolderItem) mainPanel.tagTree.getFullModel().getScriptsNode(swf); + TreePath scriptsPath = mainPanel.tagTree.getFullModel().getTreePath(scriptsNode); + String[] classParts = className.contains(".") ? className.split("\\.") : new String[]{className}; + String pkgName = className.contains(".") ? className.substring(0, className.lastIndexOf(".")) : ""; + + for (TreeItem subItem : scriptsNode.subItems) { + if (subItem instanceof AS2Package) { + AS2Package pkg = (AS2Package) subItem; + if (pkg.getName().equals("__Packages")) { + TreePath classPath = scriptsPath.pathByAddingChild(pkg); + + if (Configuration.flattenASPackages.get()) { + List subs = pkg.getAllChildren(); + for (TreeItem s : subs) { + if (s instanceof AS2Package) { + AS2Package p = (AS2Package) s; + + if ((!p.isDefaultPackage() && pkgName.equals(p.getName())) + || (p.isDefaultPackage() && pkgName.isEmpty())) { + pkg = p; + classPath = classPath.pathByAddingChild(pkg); + break; + } + } + } + } else { + loopi: + for (int i = 0; i < classParts.length - 1; i++) { + List subs = pkg.getAllChildren(); + for (TreeItem s : subs) { + if (s instanceof AS2Package) { + AS2Package p = (AS2Package) s; + if (p.getName().equals(classParts[i])) { + pkg = (AS2Package) s; + classPath = classPath.pathByAddingChild(pkg); + break; + } + } + } + } + } + classPath = classPath.pathByAddingChild(doInit); + mainPanel.tagTree.setSelectionPath(classPath); + break; + } + } + } + + } + } + private void addAs12ScriptActionPerformed(ActionEvent evt) { List sel = getSelectedItems(); if (!sel.isEmpty()) { @@ -3761,110 +3938,7 @@ public class TagTreeContextMenu extends JPopupMenu { } addInstanceEventScript(swf, tim, placeType, frame); } else if (addScriptDialog.getScriptType() == AddScriptDialog.TYPE_CLASS) { - String className = addScriptDialog.getClassName(); - ReadOnlyTagList tags = swf.getTags(); - List exportedIds = new ArrayList<>(); - for (int i = 0; i < tags.size(); i++) { - if (tags.get(i) instanceof ExportAssetsTag) { - ExportAssetsTag ea = (ExportAssetsTag) tags.get(i); - exportedIds.addAll(ea.tags); - } - } - - int insertPos = -1; - for (int i = 0; i < tags.size(); i++) { - if (tags.get(i) instanceof DoInitActionTag) { - DoInitActionTag doinit = (DoInitActionTag) tags.get(i); - if (!exportedIds.contains(doinit.spriteId)) { - //this is #initpragma, make sure class is inserted before it - insertPos = i; - break; - } - } - } - if (insertPos == -1) { - for (int i = 0; i < tags.size(); i++) { - if (tags.get(i) instanceof ShowFrameTag) { - insertPos = i; - break; - } - } - } - - if (insertPos > -1) { - int characterId = swf.getNextCharacterId(); - DefineSpriteTag sprite = new DefineSpriteTag(swf); - sprite.spriteId = characterId; - sprite.hasEndTag = true; - sprite.setTimelined(swf); - - String exportName = "__Packages." + className; - - ExportAssetsTag exportAssets = new ExportAssetsTag(swf); - exportAssets.names = new ArrayList<>(); - exportAssets.names.add(exportName); - exportAssets.tags = new ArrayList<>(); - exportAssets.tags.add(characterId); - exportAssets.setTimelined(swf); - - DoInitActionTag doInit = new DoInitActionTag(swf); - doInit.spriteId = characterId; - doInit.setTimelined(swf); - - ActionScript2Parser parser = new ActionScript2Parser(swf, doInit); - - String[] parts = className.contains(".") ? className.split("\\.") : new String[]{className}; - DottedChain dc = new DottedChain(parts); - - try { - List actions = parser.actionsFromString("class " + dc.toPrintableString(false) + "{}", swf.getCharset()); - doInit.setActions(actions); - } catch (ActionParseException | IOException | CompilationException - | InterruptedException ex) { - //ignore - } - - sprite.setExportName(exportName); - - swf.addTag(insertPos, sprite); - swf.addTag(insertPos + 1, exportAssets); - swf.addTag(insertPos + 2, doInit); - - swf.clearAllCache(); - swf.setModified(true); - mainPanel.refreshTree(swf); - - TreePath selection = mainPanel.tagTree.getSelectionPath(); - TreePath swfPath = selection.getParentPath(); - FolderItem scriptsNode = (FolderItem) mainPanel.tagTree.getFullModel().getScriptsNode(swf); - TreePath scriptsPath = swfPath.pathByAddingChild(scriptsNode); - String[] classParts = className.contains(".") ? className.split("\\.") : new String[]{className}; - - for (TreeItem subItem : scriptsNode.subItems) { - if (subItem instanceof AS2Package) { - AS2Package pkg = (AS2Package) subItem; - if (pkg.getName().equals("__Packages")) { - TreePath classPath = scriptsPath.pathByAddingChild(pkg); - for (int i = 0; i < classParts.length - 1; i++) { - List subs = pkg.getAllChildren(); - for (TreeItem s : subs) { - if (s instanceof AS2Package) { - if (((AS2Package) s).getName().equals(classParts[i])) { - pkg = (AS2Package) s; - classPath = classPath.pathByAddingChild(pkg); - break; - } - } - } - } - classPath = classPath.pathByAddingChild(doInit); - mainPanel.tagTree.setSelectionPath(classPath); - break; - } - } - } - - } + createClass(addScriptDialog.getClassName(), swf); } else if (addScriptDialog.getScriptType() == AddScriptDialog.TYPE_SPRITE_INIT) { DefineSpriteTag sprite = addScriptDialog.getSprite(); addSpriteInitScript(swf, sprite); @@ -4411,7 +4485,7 @@ public class TagTreeContextMenu extends JPopupMenu { mainPanel.folderPreviewPanel.goToSelection(); mainPanel.folderListPanel.goToSelection(); } - + private void showInResourcesViewActionPerformed(ActionEvent evt) { TreeItem item = getCurrentItem(); mainPanel.showView(MainPanel.VIEW_RESOURCES); @@ -4434,7 +4508,7 @@ public class TagTreeContextMenu extends JPopupMenu { } mainPanel.setTagTreeSelectedNode(mainPanel.tagListTree, item); mainPanel.updateMenu(); - } + } private void showInHexDumpViewActionPerformed(ActionEvent evt) { if (mainPanel.isModified()) { @@ -4448,7 +4522,7 @@ public class TagTreeContextMenu extends JPopupMenu { mainPanel.dumpTree.setSelectedItem(item); mainPanel.updateMenu(); } - + private void showInEasyViewActionPerformed(ActionEvent evt) { TreeItem item = getCurrentItem(); if (item instanceof TagScript) {