diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java index 0aa10bea8..3f06f5682 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.tags.base.Exportable; import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; @@ -336,6 +337,14 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { sos.write(originalRange.getArray(), originalRange.getPos(), originalRange.getLength()); } } + + public Tag cloneTag() throws InterruptedException, IOException { + byte[] data = getData(); + SWFInputStream tagDataStream = new SWFInputStream(swf, data, getDataPos(), data.length); + TagStub copy = new TagStub(swf, getId(), "Unresolved", getOriginalRange(), tagDataStream); + copy.forceWriteAsLong = forceWriteAsLong; + return SWFInputStream.resolveTag(copy, 0, false, true, false); + } /** * Returns string representation of the object diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/RecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/RecompileTest.java index 25838d4fa..4b3824e71 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/RecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/RecompileTest.java @@ -156,11 +156,7 @@ public class RecompileTest { SWF swf = new SWF(new BufferedInputStream(new FileInputStream(TESTDATADIR + File.separator + filename)), false); for (Tag tag : swf.tags) { if (!(tag instanceof TagStub)) { - byte[] data = tag.getData(); - SWFInputStream tagDataStream = new SWFInputStream(swf, data, tag.getDataPos(), data.length); - TagStub copy = new TagStub(swf, tag.getId(), "Unresolved", tag.getOriginalRange(), tagDataStream); - copy.forceWriteAsLong = tag.forceWriteAsLong; - Tag tag2 = SWFInputStream.resolveTag(copy, 0, false, true, false); + Tag tag2 = tag.cloneTag(); if (tag2 instanceof TagStub) { fail("Recompile failed. Tag: " + tag.getId() + " " + tag.getName()); } diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java index 663195085..9eee22297 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java @@ -662,11 +662,7 @@ public class GenericTagTreePanel extends GenericTagPanel { this.tag = tag; SWF swf = tag.getSwf(); try { - byte[] data = tag.getData(); - SWFInputStream tagDataStream = new SWFInputStream(swf, data, tag.getDataPos(), data.length); - TagStub copy = new TagStub(swf, tag.getId(), "Unresolved", tag.getOriginalRange(), tagDataStream); - copy.forceWriteAsLong = tag.forceWriteAsLong; - editedTag = SWFInputStream.resolveTag(copy, 0, false, true, false); + editedTag = tag.cloneTag(); } catch (InterruptedException ex) { } catch (IOException ex) { Logger.getLogger(GenericTagTreePanel.class.getName()).log(Level.SEVERE, null, ex); diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 816cff5cc..f3f610886 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.tags.base.ButtonTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.DrawableTag; import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.timeline.DepthState; import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.timeline.Timelined; @@ -74,6 +75,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis private boolean stillFrame = false; private Timer timer; private int frame = -1; + private boolean zoomAvailable = false; private int counter = 0; private AtomicBoolean shouldDraw = new AtomicBoolean(); private SWF swf; @@ -90,6 +92,8 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis private final Object delayObject = new Object(); private boolean drawReady; private final int drawWaitLimit = 50; // ms + private TextTag textTag; + private TextTag newTextTag; public synchronized void selectDepth(int depth) { if (depth != selectedDepth) { @@ -417,6 +421,9 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis public synchronized void zoom(Zoom zoom) { this.zoom = zoom; shouldDraw.set(true); + if (textTag != null) { + setText(textTag, newTextTag); + } } @Override @@ -468,7 +475,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public synchronized boolean zoomAvailable() { - return timelined != null; + return zoomAvailable; } public void setTimelined(final Timelined drawable, final SWF swf, int frame) { @@ -480,6 +487,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis this.timelined = drawable; this.swf = swf; + zoomAvailable = true; counter++; if (frame > -1) { this.frame = frame; @@ -520,14 +528,63 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis public synchronized void setImage(SerializableImage image) { setBackground(View.swfBackgroundColor); - if (timer != null) { - timer.cancel(); - timer = null; - } + clear(); timelined = null; loaded = true; stillFrame = true; + zoomAvailable = false; + iconPanel.setImg(image); + iconPanel.setOutlines(new ArrayList(), new ArrayList()); + drawReady = true; + } + + public synchronized void setText(TextTag textTag, TextTag newTextTag) { + setBackground(View.swfBackgroundColor); + clear(); + + timelined = null; + loaded = true; + stillFrame = true; + zoomAvailable = true; + + this.textTag = textTag; + this.newTextTag = newTextTag; + + double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; + + RECT rect = textTag.getRect(new HashSet()); + int width = (int) (rect.getWidth() * zoomDouble); + int height = (int) (rect.getHeight() * zoomDouble); + SerializableImage image = new SerializableImage((int) (width / SWF.unitDivisor) + 1, + (int) (height / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); + image.fillTransparent(); + Matrix m = new Matrix(); + m.translate(-rect.Xmin * zoomDouble, -rect.Ymin * zoomDouble); + m.scale(zoomDouble); + textTag.toImage(0, 0, 0, null, 0, image, m, new ColorTransform() { + + @Override + public int getBlueAdd() { + return 192; + } + + @Override + public int getGreenAdd() { + return 192; + } + + @Override + public int getRedAdd() { + return 192; + } + + }); + + if (newTextTag != null) { + newTextTag.toImage(0, 0, 0, null, 0, image, m, new ColorTransform()); + } + iconPanel.setImg(image); iconPanel.setOutlines(new ArrayList(), new ArrayList()); drawReady = true; @@ -566,6 +623,13 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } soundPlayers.clear(); } + + private void clear() { + if (timer != null) { + timer.cancel(); + timer = null; + } + } private void nextFrame(int counter) { drawFrame(counter); @@ -771,11 +835,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } public synchronized void stop() { - if (timer != null) { - timer.cancel(); - timer = null; - } - + clear(); stopAllSounds(); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 7240311c4..53fb25071 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -2330,6 +2330,11 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec previewPanel.showTextPanel(textTag); } + public void showTextTagWithNewValue(TextTag textTag, TextTag newTextTag) { + + previewPanel.showTextComparePanel(textTag, newTextTag); + } + private void showFolderPreview(TreeItem treeNode) { List folderPreviewItems = new ArrayList<>(); FolderItem item = (FolderItem) treeNode; diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 0be47aede..672abe7f1 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -388,6 +388,10 @@ public class PreviewPanel extends JSplitPane implements ActionListener { imagePlayControls.setMedia(imagePanel); imagePanel.setImage(image); } + + public void showTextComparePanel(TextTag textTag, TextTag newTextTag) { + imagePanel.setText(textTag, newTextTag); + } public void setMedia(MediaDisplay media) { this.media = media; diff --git a/src/com/jpexs/decompiler/flash/gui/TextPanel.java b/src/com/jpexs/decompiler/flash/gui/TextPanel.java index 7217824ff..55b3fdb92 100644 --- a/src/com/jpexs/decompiler/flash/gui/TextPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/TextPanel.java @@ -17,7 +17,10 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.gui.abc.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.text.TextParseException; import com.jpexs.decompiler.flash.treeitems.TreeItem; import java.awt.BorderLayout; import java.awt.FlowLayout; @@ -25,9 +28,12 @@ import java.awt.Font; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.IOException; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import jsyntaxpane.DefaultSyntaxKit; /** @@ -47,7 +53,7 @@ public class TextPanel extends JPanel implements ActionListener { private final JButton textEditButton; private final JButton textCancelButton; - public TextPanel(MainPanel mainPanel) { + public TextPanel(final MainPanel mainPanel) { super(new BorderLayout()); DefaultSyntaxKit.initKit(); @@ -59,6 +65,23 @@ public class TextPanel extends JPanel implements ActionListener { textValue.setEditable(false); textValue.setFont(new Font("Monospaced", Font.PLAIN, textValue.getFont().getSize())); textValue.setContentType("text/swftext"); + textValue.getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + textChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + textChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + textChanged(); + } + }); JPanel textButtonsPanel = new JPanel(); textButtonsPanel.setLayout(new FlowLayout()); @@ -120,6 +143,7 @@ public class TextPanel extends JPanel implements ActionListener { switch (e.getActionCommand()) { case ACTION_EDIT_TEXT: setEditText(true); + textChanged(); break; case ACTION_CANCEL_TEXT: setEditText(false); @@ -138,4 +162,33 @@ public class TextPanel extends JPanel implements ActionListener { break; } } + + private void textChanged() { + if (textValue.isEditable()) { + TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); + if (item instanceof TextTag) { + TextTag textTag = (TextTag) item; + boolean ok = false; + try { + TextTag copyTextTag = (TextTag) textTag.cloneTag(); + if (copyTextTag.setFormattedText(new MissingCharacterHandler() { + + @Override + public boolean handle(TextTag textTag, FontTag font, char character) { + return false; + } + + }, textValue.getText(), null)) { + ok = true; + mainPanel.showTextTagWithNewValue(textTag, copyTextTag); + } + } catch (TextParseException | InterruptedException | IOException ex) { + } + + if (!ok) { + mainPanel.showTextTagWithNewValue(textTag, null); + } + } + } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 566a244da..069e97606 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -319,12 +319,7 @@ public class TagTreeContextMenu extends JPopupMenu implements ActionListener { @Override public void actionPerformed(ActionEvent ae) { try { - SWF sourceSwf = tag.getSwf(); - byte[] data = tag.getData(); - SWFInputStream tagDataStream = new SWFInputStream(sourceSwf, data, tag.getDataPos(), data.length); - TagStub copy = new TagStub(sourceSwf, tag.getId(), "Unresolved", tag.getOriginalRange(), tagDataStream); - copy.forceWriteAsLong = tag.forceWriteAsLong; - Tag copyTag = SWFInputStream.resolveTag(copy, 0, false, true, false); + Tag copyTag = tag.cloneTag(); copyTag.setSwf(targetSwf); targetSwf.tags.add(copyTag); copyTag.setModified(true);