diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java index efa2c213a..b0c47d8ec 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java @@ -25,7 +25,6 @@ import com.jpexs.decompiler.flash.gui.generictageditors.ChangeListener; import com.jpexs.decompiler.flash.gui.generictageditors.ColorEditor; import com.jpexs.decompiler.flash.gui.generictageditors.GenericTagEditor; import com.jpexs.decompiler.flash.gui.generictageditors.NumberEditor; -import com.jpexs.decompiler.flash.gui.generictageditors.ScrollPanedEditor; import com.jpexs.decompiler.flash.gui.generictageditors.StringEditor; import com.jpexs.decompiler.flash.gui.helpers.SpringUtilities; import com.jpexs.decompiler.flash.tags.Tag; @@ -413,7 +412,7 @@ public class GenericTagPanel extends JPanel implements ChangeListener { } else if (type.equals(ByteArrayRange.class)) { editor = new BinaryDataEditor(mainPanel, name, obj, field, index, type); } else if (type.equals(Amf3Value.class)) { - editor = new ScrollPanedEditor(new Amf3ValueEditor(name, obj, field, index, type)); + editor = new Amf3ValueEditor(name, obj, field, index, type); } else { if (value == null) { if (readonly) { @@ -501,10 +500,15 @@ public class GenericTagPanel extends JPanel implements ChangeListener { } } - public void save() { + public boolean save() { for (Object component : genericTagPropertiesEditPanel.getComponents()) { if (component instanceof GenericTagEditor) { - ((GenericTagEditor) component).save(); + try { + ((GenericTagEditor) component).validateValue(); + ((GenericTagEditor) component).save(); + } catch (IllegalArgumentException iex) { + return false; + } } } SWF swf = tag.getSwf(); @@ -512,6 +516,7 @@ public class GenericTagPanel extends JPanel implements ChangeListener { tag.setModified(true); tag.setSwf(swf); setTagText(tag); + return true; } public Tag getTag() { diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java index 96eb27d64..fbf0fbde2 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java @@ -24,7 +24,6 @@ import com.jpexs.decompiler.flash.gui.generictageditors.BooleanEditor; import com.jpexs.decompiler.flash.gui.generictageditors.ColorEditor; import com.jpexs.decompiler.flash.gui.generictageditors.GenericTagEditor; import com.jpexs.decompiler.flash.gui.generictageditors.NumberEditor; -import com.jpexs.decompiler.flash.gui.generictageditors.ScrollPanedEditor; import com.jpexs.decompiler.flash.gui.generictageditors.StringEditor; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; @@ -170,7 +169,7 @@ public class GenericTagTreePanel extends GenericTagPanel { } else if (type.equals(byte[].class) || type.equals(ByteArrayRange.class)) { editor = new BinaryDataEditor(mainPanel, field.getName(), obj, field, index, type); } else if (type.equals(Amf3Value.class)) { - editor = new ScrollPanedEditor(new Amf3ValueEditor(field.getName(), obj, field, index, type)); + editor = new Amf3ValueEditor(field.getName(), obj, field, index, type); } if (editor != null) { if (editors == null) { @@ -250,19 +249,28 @@ public class GenericTagTreePanel extends GenericTagPanel { } @Override - public boolean stopCellEditing() { - super.stopCellEditing(); - - /*List depends = ((MyTreeModel) tree.getModel()).getDependentFields(fnode); - boolean dep = false; - if (!depends.isEmpty()) { - dep = true; - } */ + public void cancelCellEditing() { if (editors != null) { for (GenericTagEditor editor : editors) { - editor.save(); + editor.reset(); } } + super.cancelCellEditing(); + } + + @Override + public boolean stopCellEditing() { + if (editors != null) { + for (GenericTagEditor editor : editors) { + try { + editor.validateValue(); + editor.save(); + } catch (IllegalArgumentException iex) { + return false; + } + } + } + super.stopCellEditing(); editors = null; @@ -885,20 +893,23 @@ public class GenericTagTreePanel extends GenericTagPanel { } catch (IOException ex) { logger.log(Level.SEVERE, null, ex); } - tree.setEditable(edit); if (!edit) { tree.stopEditing(); } + tree.setEditable(edit); refreshTree(); } @Override - public void save() { - tree.stopEditing(); - SWF swf = tag.getSwf(); - assignTag(tag, editedTag); - tag.setModified(true); - tag.setSwf(swf); + public boolean save() { + if (tree.stopEditing()) { + SWF swf = tag.getSwf(); + assignTag(tag, editedTag); + tag.setModified(true); + tag.setSwf(swf); + return true; + } + return false; } private void assignTag(Tag t, Tag assigned) { diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 2c9fc24ba..f8dcd9808 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -1,1266 +1,1267 @@ -/* - * Copyright (C) 2010-2016 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; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.parser.ActionParseException; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; -import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; -import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; -import com.jpexs.decompiler.flash.gui.player.MediaDisplay; -import com.jpexs.decompiler.flash.gui.player.PlayerControls; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.AloneTag; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.helpers.SerializableImage; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.SwingConstants; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -/** - * - * @author JPEXS - */ -public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel { - - private static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; - - private static final String DRAW_PREVIEW_CARD = "DRAWPREVIEW"; - - private static final String GENERIC_TAG_CARD = "GENERICTAG"; - - private static final String BINARY_TAG_CARD = "BINARYTAG"; - - private static final String METADATA_TAG_CARD = "METADATATAG"; - - private static final String EMPTY_CARD = "EMPTY"; - - private static final String CARDTEXTPANEL = "Text card"; - - private static final String CARDFONTPANEL = "Font card"; - - private final MainPanel mainPanel; - - private final JPanel viewerCards; - - private final FlashPlayerPanel flashPanel; - - private File tempFile; - - private ImagePanel imagePanel; - - private PlayerControls imagePlayControls; - - private MediaDisplay media; - - private BinaryPanel binaryPanel; - - private LineMarkedEditorPane metadataEditor; - - private GenericTagPanel genericTagPanel; - - private JPanel displayWithPreview; - - // Image tag buttons - private JButton replaceImageButton; - - private JButton replaceImageAlphaButton; - - private JButton prevFontsButton; - - private JButton nextFontsButton; - - // Binary tag buttons - private JButton replaceBinaryButton; - - // Metadata editor buttons - private JButton metadataEditButton; - - private JButton metadataSaveButton; - - private JButton metadataCancelButton; - - // Generic tag buttons - private JButton genericEditButton; - - private JButton genericSaveButton; - - private JButton genericCancelButton; - - private JPanel parametersPanel; - - private FontPanel fontPanel; - - private int fontPageNum; - - private TextPanel textPanel; - - private MetadataTag metadataTag; - - private boolean readOnly = false; - - private final int dividerSize; - - public void setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - setDividerSize(this.readOnly ? 0 : dividerSize); - if (readOnly) { - parametersPanel.setVisible(false); - } - } - - public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { - super(JSplitPane.HORIZONTAL_SPLIT, Configuration.guiPreviewSplitPaneDividerLocationPercent); - this.mainPanel = mainPanel; - this.flashPanel = flashPanel; - - viewerCards = new JPanel(); - viewerCards.setLayout(new CardLayout()); - - viewerCards.add(createFlashPlayerPanel(flashPanel), FLASH_VIEWER_CARD); - viewerCards.add(createImagesCard(), DRAW_PREVIEW_CARD); - viewerCards.add(createBinaryCard(), BINARY_TAG_CARD); - viewerCards.add(createMetadataCard(), METADATA_TAG_CARD); - viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD); - viewerCards.add(createEmptyCard(), EMPTY_CARD); - setLeftComponent(viewerCards); - - createParametersPanel(); - - showCardLeft(FLASH_VIEWER_CARD); - - dividerSize = getDividerSize(); - } - - private JPanel createEmptyCard() { - JPanel ret = new JPanel(); - ret.add(new JLabel("-")); - return ret; - } - - private void createParametersPanel() { - displayWithPreview = new JPanel(new CardLayout()); - - textPanel = new TextPanel(mainPanel); - displayWithPreview.add(textPanel, CARDTEXTPANEL); - - fontPanel = new FontPanel(mainPanel); - displayWithPreview.add(fontPanel, CARDFONTPANEL); - - JLabel paramsLabel = new HeaderLabel(mainPanel.translate("parameters")); - paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); - //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - parametersPanel = new JPanel(new BorderLayout()); - parametersPanel.add(paramsLabel, BorderLayout.NORTH); - parametersPanel.add(displayWithPreview, BorderLayout.CENTER); - setRightComponent(parametersPanel); - } - - private JPanel createImageButtonsPanel() { - replaceImageButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("replaceimage16")); - replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); - replaceImageButton.addActionListener(mainPanel::replaceButtonActionPerformed); - replaceImageButton.setVisible(false); - - replaceImageAlphaButton = new JButton(mainPanel.translate("button.replaceAlphaChannel"), View.getIcon("replacealpha16")); - replaceImageAlphaButton.setMargin(new Insets(3, 3, 3, 10)); - replaceImageAlphaButton.addActionListener(mainPanel::replaceAlphaButtonActionPerformed); - replaceImageAlphaButton.setVisible(false); - - prevFontsButton = new JButton(mainPanel.translate("button.prev"), View.getIcon("prev16")); - prevFontsButton.setMargin(new Insets(3, 3, 3, 10)); - prevFontsButton.addActionListener(this::prevFontsButtonActionPerformed); - prevFontsButton.setVisible(false); - - nextFontsButton = new JButton(mainPanel.translate("button.next"), View.getIcon("next16")); - nextFontsButton.setMargin(new Insets(3, 3, 3, 10)); - nextFontsButton.addActionListener(this::nextFontsButtonActionPerformed); - nextFontsButton.setVisible(false); - - ButtonsPanel imageButtonsPanel = new ButtonsPanel(); - imageButtonsPanel.add(replaceImageButton); - imageButtonsPanel.add(replaceImageAlphaButton); - imageButtonsPanel.add(prevFontsButton); - imageButtonsPanel.add(nextFontsButton); - return imageButtonsPanel; - } - - private JPanel createBinaryButtonsPanel() { - replaceBinaryButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); - replaceBinaryButton.setMargin(new Insets(3, 3, 3, 10)); - replaceBinaryButton.addActionListener(mainPanel::replaceButtonActionPerformed); - - ButtonsPanel binaryButtonsPanel = new ButtonsPanel(); - binaryButtonsPanel.add(replaceBinaryButton); - return binaryButtonsPanel; - } - - private JPanel createGenericTagButtonsPanel() { - genericEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); - genericEditButton.setMargin(new Insets(3, 3, 3, 10)); - genericEditButton.addActionListener(this::editGenericTagButtonActionPerformed); - genericSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); - genericSaveButton.setMargin(new Insets(3, 3, 3, 10)); - genericSaveButton.addActionListener(this::saveGenericTagButtonActionPerformed); - genericSaveButton.setVisible(false); - genericCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); - genericCancelButton.setMargin(new Insets(3, 3, 3, 10)); - genericCancelButton.addActionListener(this::cancelGenericTagButtonActionPerformed); - genericCancelButton.setVisible(false); - - ButtonsPanel genericTagButtonsPanel = new ButtonsPanel(); - genericTagButtonsPanel.add(genericEditButton); - genericTagButtonsPanel.add(genericSaveButton); - genericTagButtonsPanel.add(genericCancelButton); - return genericTagButtonsPanel; - } - - private JPanel createMetadataButtonsPanel() { - metadataEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); - metadataEditButton.setMargin(new Insets(3, 3, 3, 10)); - metadataEditButton.addActionListener(this::editMetadataButtonActionPerformed); - metadataSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); - metadataSaveButton.setMargin(new Insets(3, 3, 3, 10)); - metadataSaveButton.addActionListener(this::saveMetadataButtonActionPerformed); - metadataSaveButton.setVisible(false); - metadataCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); - metadataCancelButton.setMargin(new Insets(3, 3, 3, 10)); - metadataCancelButton.addActionListener(this::cancelMetadataButtonActionPerformed); - metadataCancelButton.setVisible(false); - - ButtonsPanel metadataTagButtonsPanel = new ButtonsPanel(); - metadataTagButtonsPanel.add(metadataEditButton); - metadataTagButtonsPanel.add(metadataSaveButton); - metadataTagButtonsPanel.add(metadataCancelButton); - return metadataTagButtonsPanel; - } - - private JPanel createFlashPlayerPanel(FlashPlayerPanel flashPanel) { - JPanel pan = new JPanel(new BorderLayout()); - JLabel prevLabel = new HeaderLabel(mainPanel.translate("swfpreview")); - prevLabel.setHorizontalAlignment(SwingConstants.CENTER); - //prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - pan.add(prevLabel, BorderLayout.NORTH); - - Component leftComponent; - if (flashPanel != null) { - JPanel flashPlayPanel = new JPanel(new BorderLayout()); - flashPlayPanel.add(flashPanel, BorderLayout.CENTER); - - /*JPanel bottomPanel = new JPanel(new BorderLayout()); - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton selectColorButton = new JButton(View.getIcon("color16")); - selectColorButton.addActionListener(mainPanel::selectBkColor); - selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); - buttonsPanel.add(selectColorButton); - bottomPanel.add(buttonsPanel, BorderLayout.EAST); - - flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH);*/ - JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); - flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); - flashPlayPanel2.add(new PlayerControls(mainPanel, flashPanel), BorderLayout.SOUTH); - leftComponent = flashPlayPanel2; - } else { - JPanel swtPanel = new JPanel(new BorderLayout()); - swtPanel.add(new JLabel("
" + mainPanel.translate("notavailonthisplatform") + "
", JLabel.CENTER), BorderLayout.CENTER); - swtPanel.setBackground(View.getDefaultBackgroundColor()); - leftComponent = swtPanel; - } - - pan.add(leftComponent, BorderLayout.CENTER); - return pan; - } - - private JPanel createImagesCard() { - JPanel shapesCard = new JPanel(new BorderLayout()); - JPanel previewPanel = new JPanel(new BorderLayout()); - - JPanel previewCnt = new JPanel(new BorderLayout()); - imagePanel = new ImagePanel(); - imagePanel.setLoop(Configuration.loopMedia.get()); - previewCnt.add(imagePanel, BorderLayout.CENTER); - previewCnt.add(imagePlayControls = new PlayerControls(mainPanel, imagePanel), BorderLayout.SOUTH); - imagePlayControls.setMedia(imagePanel); - previewPanel.add(previewCnt, BorderLayout.CENTER); - JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); - prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); - //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - previewPanel.add(prevIntLabel, BorderLayout.NORTH); - - shapesCard.add(previewPanel, BorderLayout.CENTER); - - shapesCard.add(createImageButtonsPanel(), BorderLayout.SOUTH); - return shapesCard; - } - - private JPanel createMetadataCard() { - JPanel metadataCard = new JPanel(new BorderLayout()); - metadataEditor = new LineMarkedEditorPane(); - metadataCard.add(new JScrollPane(metadataEditor), BorderLayout.CENTER); - //metadataEditor.setContentType("text/xml"); - metadataEditor.setEditable(false); - - metadataEditor.setFont(new Font("Monospaced", Font.PLAIN, metadataEditor.getFont().getSize())); - metadataEditor.changeContentType("text/xml"); - metadataEditor.addTextChangedListener(this::metadataTextChanged); - - metadataCard.add(createMetadataButtonsPanel(), BorderLayout.SOUTH); - return metadataCard; - } - - private boolean isMetadataModified() { - return metadataSaveButton.isVisible() && metadataSaveButton.isEnabled(); - } - - private void setMetadataModified(boolean value) { - metadataSaveButton.setEnabled(value); - metadataCancelButton.setEnabled(value); - } - - private void metadataTextChanged() { - setMetadataModified(true); - } - - private void updateMetadataButtonsVisibility() { - boolean edit = metadataEditor.isEditable(); - boolean editorMode = Configuration.editorMode.get(); - metadataEditButton.setVisible(!readOnly && !edit); - metadataSaveButton.setVisible(!readOnly && edit); - boolean metadataModified = isMetadataModified(); - metadataCancelButton.setVisible(!readOnly && edit); - metadataCancelButton.setEnabled(metadataModified || !editorMode); - } - - private JPanel createBinaryCard() { - JPanel binaryCard = new JPanel(new BorderLayout()); - binaryPanel = new BinaryPanel(mainPanel); - binaryCard.add(binaryPanel, BorderLayout.CENTER); - binaryCard.add(createBinaryButtonsPanel(), BorderLayout.SOUTH); - return binaryCard; - } - - private JPanel createGenericTagCard() { - JPanel genericTagCard = new JPanel(new BorderLayout()); - genericTagPanel = new GenericTagTreePanel(mainPanel); - genericTagCard.add(genericTagPanel, BorderLayout.CENTER); - genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); - return genericTagCard; - } - - private void showCardLeft(String card) { - CardLayout cl = (CardLayout) (viewerCards.getLayout()); - cl.show(viewerCards, card); - } - - private void showCardRight(String card) { - CardLayout cl = (CardLayout) (displayWithPreview.getLayout()); - cl.show(displayWithPreview, card); - } - - public TextPanel getTextPanel() { - return textPanel; - } - - public void setParametersPanelVisible(boolean show) { - parametersPanel.setVisible(show); - } - - public void showFlashViewerPanel() { - parametersPanel.setVisible(false); - showCardLeft(FLASH_VIEWER_CARD); - } - - public void showImagePanel(Timelined timelined, SWF swf, int frame) { - showCardLeft(DRAW_PREVIEW_CARD); - parametersPanel.setVisible(false); - imagePlayControls.setMedia(imagePanel); - imagePanel.setTimelined(timelined, swf, frame); - } - - public void showImagePanel(SerializableImage image) { - showCardLeft(DRAW_PREVIEW_CARD); - parametersPanel.setVisible(false); - 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; - imagePlayControls.setMedia(media); - } - - public void showFontPanel(FontTag fontTag) { - fontPageNum = 0; - showFontPage(fontTag); - - showCardRight(CARDFONTPANEL); - if (!readOnly) { - parametersPanel.setVisible(true); - } - fontPanel.showFontTag(fontTag); - - int pageCount = getFontPageCount(fontTag); - if (pageCount > 1) { - prevFontsButton.setVisible(true); - nextFontsButton.setVisible(true); - } - } - - private void showFontPage(FontTag fontTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); - } - } - - public static int getFontPageCount(FontTag fontTag) { - int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; - if (pageCount < 1) { - pageCount = 1; - } - return pageCount; - } - - public void showEmpty() { - showCardLeft(EMPTY_CARD); - } - - public void showTextPanel(TextTag textTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); - } - - showCardRight(CARDTEXTPANEL); - if (!readOnly) { - parametersPanel.setVisible(true); - } - textPanel.setText(textTag); - } - - public void focusTextPanel() { - textPanel.focusTextValue(); - } - - public void clear() { - imagePanel.clearAll(); - if (media != null) { - try { - media.close(); - } catch (IOException ex) { - // ignore - } - } - - binaryPanel.setBinaryData(null); - genericTagPanel.clear(); - fontPanel.clear(); - } - - public void closeTag() { - textPanel.closeTag(); - } - - public static String formatMetadata(String input, int indent) { - input = input.replace("> <", "><"); - try { - Source xmlInput = new StreamSource(new StringReader(input)); - StringWriter stringWriter = new StringWriter(); - StreamResult xmlOutput = new StreamResult(stringWriter); - StringWriter sw = new StringWriter(); - xmlOutput.setWriter(sw); - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - transformerFactory.setAttribute("indent-number", indent); - Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "" + indent); - transformer.transform(xmlInput, xmlOutput); - - return xmlOutput.getWriter().toString(); - } catch (IllegalArgumentException | TransformerException e) { - return input; - } - } - - public void showMetaDataPanel(MetadataTag metadataTag) { - showCardLeft(METADATA_TAG_CARD); - this.metadataTag = metadataTag; - metadataEditor.setEditable(!readOnly && !metadataTag.isReadOnly() && Configuration.editorMode.get()); - metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - parametersPanel.setVisible(false); - } - - public void showBinaryPanel(DefineBinaryDataTag binaryDataTag) { - showCardLeft(BINARY_TAG_CARD); - binaryPanel.setBinaryData(binaryDataTag); - parametersPanel.setVisible(false); - } - - public void showGenericTagPanel(Tag tag) { - showCardLeft(GENERIC_TAG_CARD); - genericEditButton.setVisible(!tag.isReadOnly()); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, tag); - parametersPanel.setVisible(false); - } - - public void setImageReplaceButtonVisible(boolean show, boolean showAlpha) { - if (readOnly) { - show = false; - showAlpha = false; - } - replaceImageButton.setVisible(show); - replaceImageAlphaButton.setVisible(showAlpha); - prevFontsButton.setVisible(false); - nextFontsButton.setVisible(false); - } - - private static Tag classicTag(Tag t) { - if (t instanceof DefineCompactedFont) { - return ((DefineCompactedFont) t).toClassicFont(); - } - return t; - } - - private static void writeTag(Tag t, SWFOutputStream sos) throws IOException { - t = classicTag(t); - - t.writeTag(sos); - if (t instanceof CharacterIdTag) { - List chIdTags = t.getSwf().getCharacterIdTags(((CharacterIdTag) t).getCharacterId()); - if (chIdTags != null) { - for (CharacterIdTag chIdTag : chIdTags) { - if (!(chIdTag instanceof PlaceObjectTypeTag || chIdTag instanceof RemoveTag)) { - ((Tag) chIdTag).writeTag(sos); - } - } - } - } - } - - public void createAndShowTempSwf(TreeItem treeItem) { - SWF swf = null; - try { - if (tempFile != null) { - tempFile.delete(); - } - - tempFile = File.createTempFile("ffdec_view_", ".swf"); - tempFile.deleteOnExit(); - - Color backgroundColor = View.getSwfBackgroundColor(); - - if (treeItem instanceof Tag) { - Tag tag = (Tag) treeItem; - swf = tag.getSwf(); - if (tag instanceof FontTag) { //Fonts are always black on white - backgroundColor = View.getDefaultBackgroundColor(); - } - } else if (treeItem instanceof Frame) { - Frame fn = (Frame) treeItem; - swf = fn.getSwf(); - if (fn.timeline.timelined == swf) { - SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); - if (setBgColorTag != null) { - backgroundColor = setBgColorTag.backgroundColor.toColor(); - } - } - } - - int frameCount = 1; - float frameRate = swf.frameRate; - HashMap videoFrames = new HashMap<>(); - if (treeItem instanceof DefineVideoStreamTag) { - DefineVideoStreamTag vs = (DefineVideoStreamTag) treeItem; - SWF.populateVideoFrames(vs.getCharacterId(), swf.getTags(), videoFrames); - frameCount = videoFrames.size(); - } - - List soundFrames = new ArrayList<>(); - if (treeItem instanceof SoundStreamHeadTypeTag) { - soundFrames = ((SoundStreamHeadTypeTag) treeItem).getBlocks(); - frameCount = soundFrames.size(); - } - - if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) { - frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; - frameCount = (int) (MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate); - } - - if (treeItem instanceof DefineSoundTag) { - frameCount = 1; - } - - if (treeItem instanceof DefineSpriteTag) { - frameCount = ((DefineSpriteTag) treeItem).frameCount; - } - - byte[] data; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); - RECT outrect = new RECT(swf.displayRect); - - RECT treeItemBounds = null; - if (treeItem instanceof FontTag) { - outrect.Xmin = 0; - outrect.Ymin = 0; - outrect.Xmax = FontTag.PREVIEWSIZE * 20; - outrect.Ymax = FontTag.PREVIEWSIZE * 20; - } else if (treeItem instanceof BoundedTag) { - treeItemBounds = ((BoundedTag) treeItem).getRect(); - } else if (treeItem instanceof Frame) { - treeItemBounds = ((Frame) treeItem).timeline.timelined.getRect(); - } - - if (treeItemBounds != null) { - if (outrect.getWidth() < treeItemBounds.getWidth()) { - outrect.Xmax += treeItemBounds.getWidth() - outrect.getWidth(); - } - - if (outrect.getHeight() < treeItemBounds.getHeight()) { - outrect.Ymax += treeItemBounds.getHeight() - outrect.getHeight(); - } - } - - int width = outrect.getWidth(); - int height = outrect.getHeight(); - - sos2.writeRECT(outrect); - sos2.writeFIXED8(frameRate); - sos2.writeUI16(frameCount); //framecnt - - FileAttributesTag fa = swf.getFileAttributes(); - if (fa != null) { - fa.writeTag(sos2); - } - - SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); - if (setBgColorTag == null) { - setBgColorTag = new SetBackgroundColorTag(swf, new RGB(backgroundColor)); - } - - setBgColorTag.writeTag(sos2); - - if (treeItem instanceof Frame) { - Frame fn = (Frame) treeItem; - Timelined parent = fn.timeline.timelined; - List doneCharacters = new ArrayList<>(); - for (Tag t : parent.getTags()) { - if (t instanceof FileAttributesTag || t instanceof SetBackgroundColorTag) { - continue; - } - - if (t instanceof DoActionTag || t instanceof DoInitActionTag) { - // todo: Maybe DoABC tags should be removed, too - continue; - } - - Set needed = new HashSet<>(); - t.getNeededCharactersDeep(needed); - for (int n : needed) { - if (!doneCharacters.contains(n)) { - writeTag(swf.getCharacter(n), sos2); - doneCharacters.add(n); - } - } - - if (t instanceof ShowFrameTag || t instanceof PlaceObjectTypeTag || t instanceof RemoveTag) { - continue; - } - - if (t instanceof CharacterTag) { - int characterId = ((CharacterTag) t).getCharacterId(); - if (!doneCharacters.contains(characterId)) { - doneCharacters.add(((CharacterTag) t).getCharacterId()); - } - } - - writeTag(t, sos2); - } - - RECT r = parent.getRect(); - for (Map.Entry value : fn.layers.entrySet()) { - PlaceObjectTypeTag pot = value.getValue().toPlaceObjectTag(value.getKey()); - MATRIX mat = new MATRIX(pot.getMatrix()); - mat.translateX += width / 2 - r.getWidth() / 2; - mat.translateY += height / 2 - r.getHeight() / 2; - pot.setMatrix(mat); - pot.writeTag(sos2); - } - - new ShowFrameTag(swf).writeTag(sos2); - } else { - boolean isSprite = false; - if (treeItem instanceof DefineSpriteTag) { - isSprite = true; - } - int chtId = 0; - if (treeItem instanceof CharacterTag) { - chtId = ((CharacterTag) treeItem).getCharacterId(); - } - - if (treeItem instanceof DefineBitsTag) { - JPEGTablesTag jtt = swf.getJtt(); - if (jtt != null) { - jtt.writeTag(sos2); - } - } else if (treeItem instanceof AloneTag) { - } else { - Set needed = new HashSet<>(); - ((Tag) treeItem).getNeededCharactersDeep(needed); - for (int n : needed) { - if (isSprite && chtId == n) { - continue; - } - - CharacterTag characterTag = swf.getCharacter(n); - if (characterTag instanceof DefineBitsTag) { - JPEGTablesTag jtt = swf.getJtt(); - if (jtt != null) { - jtt.writeTag(sos2); - } - } - - writeTag(characterTag, sos2); - } - } - - writeTag((Tag) treeItem, sos2); - - MATRIX mat = new MATRIX(); - mat.hasRotate = false; - mat.hasScale = false; - mat.translateX = 0; - mat.translateY = 0; - if (treeItem instanceof BoundedTag) { - RECT r = ((BoundedTag) treeItem).getRect(); - mat.translateX = -r.Xmin; - mat.translateY = -r.Ymin; - mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; - mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; - } else { - mat.translateX = width / 4; - mat.translateY = height / 4; - } - if (treeItem instanceof FontTag) { - FontTag ft = (FontTag) classicTag((Tag) treeItem); - - int countGlyphsTotal = ft.getGlyphShapeTable().size(); - int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); - int fontId = ft.getFontId(); - int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); - int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); - if (rows == 0) { - rows = 1; - cols = 1; - } - int x = 0; - int y = 0; - int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; - countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); - List shapes = ft.getGlyphShapeTable(); - int maxw = 0; - for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { - RECT b = shapes.get(f).getBounds(); - if (b.Xmin == Integer.MAX_VALUE) { - continue; - } - if (b.Ymin == Integer.MAX_VALUE) { - continue; - } - int w = (int) (b.getWidth() / ft.getDivider()); - if (w > maxw) { - maxw = w; - } - x++; - } - - x = 0; - - int BORDER = 3 * 20; - - int textHeight = height / rows; - - while (maxw * textHeight / 1024.0 > width / cols - 2 * BORDER) { - textHeight--; - } - - MATRIX tmat = new MATRIX(); - for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { - if (x >= cols) { - x = 0; - y++; - } - List rec = new ArrayList<>(); - TEXTRECORD tr = new TEXTRECORD(); - - RECT b = shapes.get(f).getBounds(); - int xmin = b.Xmin == Integer.MAX_VALUE ? 0 : (int) (b.Xmin / ft.getDivider()); - xmin *= textHeight / 1024.0; - int ymin = b.Ymin == Integer.MAX_VALUE ? 0 : (int) (b.Ymin / ft.getDivider()); - ymin *= textHeight / 1024.0; - int w = (int) (b.getWidth() / ft.getDivider()); - w *= textHeight / 1024.0; - int h = (int) (b.getHeight() / ft.getDivider()); - h *= textHeight / 1024.0; - - tr.fontId = fontId; - tr.styleFlagsHasFont = true; - tr.textHeight = textHeight; - tr.xOffset = -xmin; - tr.yOffset = 0; - tr.styleFlagsHasXOffset = true; - tr.styleFlagsHasYOffset = true; - tr.glyphEntries = new ArrayList<>(1); - tr.styleFlagsHasColor = true; - tr.textColor = new RGB(0, 0, 0); - GLYPHENTRY ge = new GLYPHENTRY(); - - double ga = ft.getGlyphAdvance(f); - int cw = ga == -1 ? w : (int) (ga / ft.getDivider() * textHeight / 1024.0); - - ge.glyphAdvance = 0; - ge.glyphIndex = f; - tr.glyphEntries.add(ge); - rec.add(tr); - - tmat.translateX = x * width / cols + width / cols / 2 - w / 2; - tmat.translateY = y * height / rows + height / rows / 2; - new DefineTextTag(swf, 999 + f, new RECT(0, cw, ymin, ymin + h), new MATRIX(), rec).writeTag(sos2); - new PlaceObject2Tag(swf, false, 1 + f, 999 + f, tmat, null, 0, null, -1, null).writeTag(sos2); - x++; - } - new ShowFrameTag(swf).writeTag(sos2); - } else if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) { - new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { - new PlaceObject2Tag(swf, true, 1, chtId, mat, null, ratio, null, -1, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (treeItem instanceof SoundStreamHeadTypeTag) { - for (SoundStreamBlockTag blk : soundFrames) { - blk.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (treeItem instanceof DefineSoundTag) { - ExportAssetsTag ea = new ExportAssetsTag(swf); - DefineSoundTag ds = (DefineSoundTag) treeItem; - ea.tags.add(ds.soundId); - ea.names.add("my_define_sound"); - ea.writeTag(sos2); - List actions; - DoActionTag doa; - - doa = new DoActionTag(swf, null); - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" - + "StopSounds\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Push 9999 0.0 2 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" - + "StopSounds\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"onSoundComplete\"\n" - + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" - + "Push 0.0 register1 \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "}\n" - + "SetMember\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"execParam\"\n" - + "GetMember\n" - + "Push 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "StopSounds\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - new ShowFrameTag(swf).writeTag(sos2); - } else if (treeItem instanceof DefineVideoStreamTag) { - - new PlaceObject2Tag(swf, false, 1, chtId, mat, null, -1, null, -1, null).writeTag(sos2); - List frs = new ArrayList<>(videoFrames.values()); - Collections.sort(frs, new Comparator() { - @Override - public int compare(VideoFrameTag o1, VideoFrameTag o2) { - return o1.frameNum - o2.frameNum; - } - }); - boolean first = true; - int ratio = 0; - for (VideoFrameTag f : frs) { - if (!first) { - ratio++; - new PlaceObject2Tag(swf, true, 1, 0, null, null, ratio, null, -1, null).writeTag(sos2); - } - f.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - first = false; - } - } else if (treeItem instanceof DefineSpriteTag) { - DefineSpriteTag s = (DefineSpriteTag) treeItem; - Tag lastTag = null; - for (Tag t : s.getTags()) { - if (t instanceof EndTag) { - break; - } else if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; - MATRIX m = pt.getMatrix(); - MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); - pt.writeTagWithMatrix(sos2, m2); - lastTag = t; - } else { - t.writeTag(sos2); - lastTag = t; - } - } - if (!s.getTags().isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { - new ShowFrameTag(swf).writeTag(sos2); - } - } else { - new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - - } // not showframe - - new EndTag(swf).writeTag(sos2); - data = baos.toByteArray(); - } - - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); - sos.write("FWS".getBytes()); - sos.write(swf.version); - sos.writeUI32(sos.getPos() + data.length + 4); - sos.write(data); - fos.flush(); - } - if (flashPanel != null) { - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); - } - showFlashViewerPanel(); - } catch (IOException | ActionParseException ex) { - Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public void showSwf(SWF swf) { - Color backgroundColor = View.getDefaultBackgroundColor(); - SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); - if (setBgColorTag != null) { - backgroundColor = setBgColorTag.backgroundColor.toColor(); - } - - if (tempFile != null) { - tempFile.delete(); - } - try { - tempFile = File.createTempFile("ffdec_view_", ".swf"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - swf.saveTo(fos); - } - //Inject Loader - if (swf.isAS3() && Configuration.autoOpenLoadedSWFs.get() && !Configuration.internalFlashViewer.get() && !DebuggerTools.hasDebugger(swf)) { - SWF instrSWF; - try (InputStream fis = new BufferedInputStream(new FileInputStream(tempFile))) { - instrSWF = new SWF(fis, false, false); - } - - DebuggerTools.switchDebugger(instrSWF); - DebuggerTools.injectDebugLoader(instrSWF); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - instrSWF.saveTo(fos); - } - } - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); - } catch (IOException iex) { - Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); - } catch (InterruptedException ex) { - - } - } - - private void editMetadataButtonActionPerformed(ActionEvent evt) { - TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof MetadataTag) { - metadataEditor.setEditable(true); - updateMetadataButtonsVisibility(); - } - } - - private void saveMetadataButtonActionPerformed(ActionEvent evt) { - metadataTag.xmlMetadata = metadataEditor.getText().replaceAll(">\r?\n<", "> <"); - metadataTag.setModified(true); - metadataEditor.setEditable(Configuration.editorMode.get()); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - mainPanel.repaintTree(); - } - - private void cancelMetadataButtonActionPerformed(ActionEvent evt) { - metadataEditor.setEditable(false); - metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); - metadataEditor.setEditable(Configuration.editorMode.get()); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - } - - private void editGenericTagButtonActionPerformed(ActionEvent evt) { - TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof TagScript) { - item = ((TagScript) item).getTag(); - } - - if (item instanceof Tag) { - genericEditButton.setVisible(false); - genericSaveButton.setVisible(true); - genericCancelButton.setVisible(true); - genericTagPanel.setEditMode(true, (Tag) item); - } - } - - private void saveGenericTagButtonActionPerformed(ActionEvent evt) { - genericTagPanel.save(); - Tag tag = genericTagPanel.getTag(); - SWF swf = tag.getSwf(); - swf.clearImageCache(); - swf.updateCharacters(); - tag.getTimelined().resetTimeline(); - swf.assignClassesToSymbols(); - swf.assignExportNamesToSymbols(); - mainPanel.refreshTree(swf); - mainPanel.setTagTreeSelectedNode(tag); - genericEditButton.setVisible(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); - } - - private void cancelGenericTagButtonActionPerformed(ActionEvent evt) { - genericEditButton.setVisible(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); - } - - private void prevFontsButtonActionPerformed(ActionEvent evt) { - FontTag fontTag = fontPanel.getFontTag(); - int pageCount = getFontPageCount(fontTag); - fontPageNum = (fontPageNum + pageCount - 1) % pageCount; - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); - } - } - - private void nextFontsButtonActionPerformed(ActionEvent evt) { - FontTag fontTag = fontPanel.getFontTag(); - int pageCount = getFontPageCount(fontTag); - fontPageNum = (fontPageNum + 1) % pageCount; - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); - } - } - - @Override - public boolean tryAutoSave() { - // todo: implement - return textPanel.tryAutoSave() && false; - } - - @Override - public boolean isEditing() { - return textPanel.isEditing() - || (genericSaveButton.isVisible() && genericSaveButton.isEnabled()) - || (metadataSaveButton.isVisible() && metadataSaveButton.isEnabled()); - } -} +/* + * Copyright (C) 2010-2016 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; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; +import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; +import com.jpexs.decompiler.flash.gui.player.MediaDisplay; +import com.jpexs.decompiler.flash.gui.player.PlayerControls; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.helpers.SerializableImage; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.SwingConstants; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * + * @author JPEXS + */ +public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel { + + private static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; + + private static final String DRAW_PREVIEW_CARD = "DRAWPREVIEW"; + + private static final String GENERIC_TAG_CARD = "GENERICTAG"; + + private static final String BINARY_TAG_CARD = "BINARYTAG"; + + private static final String METADATA_TAG_CARD = "METADATATAG"; + + private static final String EMPTY_CARD = "EMPTY"; + + private static final String CARDTEXTPANEL = "Text card"; + + private static final String CARDFONTPANEL = "Font card"; + + private final MainPanel mainPanel; + + private final JPanel viewerCards; + + private final FlashPlayerPanel flashPanel; + + private File tempFile; + + private ImagePanel imagePanel; + + private PlayerControls imagePlayControls; + + private MediaDisplay media; + + private BinaryPanel binaryPanel; + + private LineMarkedEditorPane metadataEditor; + + private GenericTagPanel genericTagPanel; + + private JPanel displayWithPreview; + + // Image tag buttons + private JButton replaceImageButton; + + private JButton replaceImageAlphaButton; + + private JButton prevFontsButton; + + private JButton nextFontsButton; + + // Binary tag buttons + private JButton replaceBinaryButton; + + // Metadata editor buttons + private JButton metadataEditButton; + + private JButton metadataSaveButton; + + private JButton metadataCancelButton; + + // Generic tag buttons + private JButton genericEditButton; + + private JButton genericSaveButton; + + private JButton genericCancelButton; + + private JPanel parametersPanel; + + private FontPanel fontPanel; + + private int fontPageNum; + + private TextPanel textPanel; + + private MetadataTag metadataTag; + + private boolean readOnly = false; + + private final int dividerSize; + + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + setDividerSize(this.readOnly ? 0 : dividerSize); + if (readOnly) { + parametersPanel.setVisible(false); + } + } + + public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { + super(JSplitPane.HORIZONTAL_SPLIT, Configuration.guiPreviewSplitPaneDividerLocationPercent); + this.mainPanel = mainPanel; + this.flashPanel = flashPanel; + + viewerCards = new JPanel(); + viewerCards.setLayout(new CardLayout()); + + viewerCards.add(createFlashPlayerPanel(flashPanel), FLASH_VIEWER_CARD); + viewerCards.add(createImagesCard(), DRAW_PREVIEW_CARD); + viewerCards.add(createBinaryCard(), BINARY_TAG_CARD); + viewerCards.add(createMetadataCard(), METADATA_TAG_CARD); + viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD); + viewerCards.add(createEmptyCard(), EMPTY_CARD); + setLeftComponent(viewerCards); + + createParametersPanel(); + + showCardLeft(FLASH_VIEWER_CARD); + + dividerSize = getDividerSize(); + } + + private JPanel createEmptyCard() { + JPanel ret = new JPanel(); + ret.add(new JLabel("-")); + return ret; + } + + private void createParametersPanel() { + displayWithPreview = new JPanel(new CardLayout()); + + textPanel = new TextPanel(mainPanel); + displayWithPreview.add(textPanel, CARDTEXTPANEL); + + fontPanel = new FontPanel(mainPanel); + displayWithPreview.add(fontPanel, CARDFONTPANEL); + + JLabel paramsLabel = new HeaderLabel(mainPanel.translate("parameters")); + paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); + //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + parametersPanel = new JPanel(new BorderLayout()); + parametersPanel.add(paramsLabel, BorderLayout.NORTH); + parametersPanel.add(displayWithPreview, BorderLayout.CENTER); + setRightComponent(parametersPanel); + } + + private JPanel createImageButtonsPanel() { + replaceImageButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("replaceimage16")); + replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); + replaceImageButton.addActionListener(mainPanel::replaceButtonActionPerformed); + replaceImageButton.setVisible(false); + + replaceImageAlphaButton = new JButton(mainPanel.translate("button.replaceAlphaChannel"), View.getIcon("replacealpha16")); + replaceImageAlphaButton.setMargin(new Insets(3, 3, 3, 10)); + replaceImageAlphaButton.addActionListener(mainPanel::replaceAlphaButtonActionPerformed); + replaceImageAlphaButton.setVisible(false); + + prevFontsButton = new JButton(mainPanel.translate("button.prev"), View.getIcon("prev16")); + prevFontsButton.setMargin(new Insets(3, 3, 3, 10)); + prevFontsButton.addActionListener(this::prevFontsButtonActionPerformed); + prevFontsButton.setVisible(false); + + nextFontsButton = new JButton(mainPanel.translate("button.next"), View.getIcon("next16")); + nextFontsButton.setMargin(new Insets(3, 3, 3, 10)); + nextFontsButton.addActionListener(this::nextFontsButtonActionPerformed); + nextFontsButton.setVisible(false); + + ButtonsPanel imageButtonsPanel = new ButtonsPanel(); + imageButtonsPanel.add(replaceImageButton); + imageButtonsPanel.add(replaceImageAlphaButton); + imageButtonsPanel.add(prevFontsButton); + imageButtonsPanel.add(nextFontsButton); + return imageButtonsPanel; + } + + private JPanel createBinaryButtonsPanel() { + replaceBinaryButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); + replaceBinaryButton.setMargin(new Insets(3, 3, 3, 10)); + replaceBinaryButton.addActionListener(mainPanel::replaceButtonActionPerformed); + + ButtonsPanel binaryButtonsPanel = new ButtonsPanel(); + binaryButtonsPanel.add(replaceBinaryButton); + return binaryButtonsPanel; + } + + private JPanel createGenericTagButtonsPanel() { + genericEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); + genericEditButton.setMargin(new Insets(3, 3, 3, 10)); + genericEditButton.addActionListener(this::editGenericTagButtonActionPerformed); + genericSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); + genericSaveButton.setMargin(new Insets(3, 3, 3, 10)); + genericSaveButton.addActionListener(this::saveGenericTagButtonActionPerformed); + genericSaveButton.setVisible(false); + genericCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); + genericCancelButton.setMargin(new Insets(3, 3, 3, 10)); + genericCancelButton.addActionListener(this::cancelGenericTagButtonActionPerformed); + genericCancelButton.setVisible(false); + + ButtonsPanel genericTagButtonsPanel = new ButtonsPanel(); + genericTagButtonsPanel.add(genericEditButton); + genericTagButtonsPanel.add(genericSaveButton); + genericTagButtonsPanel.add(genericCancelButton); + return genericTagButtonsPanel; + } + + private JPanel createMetadataButtonsPanel() { + metadataEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); + metadataEditButton.setMargin(new Insets(3, 3, 3, 10)); + metadataEditButton.addActionListener(this::editMetadataButtonActionPerformed); + metadataSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); + metadataSaveButton.setMargin(new Insets(3, 3, 3, 10)); + metadataSaveButton.addActionListener(this::saveMetadataButtonActionPerformed); + metadataSaveButton.setVisible(false); + metadataCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); + metadataCancelButton.setMargin(new Insets(3, 3, 3, 10)); + metadataCancelButton.addActionListener(this::cancelMetadataButtonActionPerformed); + metadataCancelButton.setVisible(false); + + ButtonsPanel metadataTagButtonsPanel = new ButtonsPanel(); + metadataTagButtonsPanel.add(metadataEditButton); + metadataTagButtonsPanel.add(metadataSaveButton); + metadataTagButtonsPanel.add(metadataCancelButton); + return metadataTagButtonsPanel; + } + + private JPanel createFlashPlayerPanel(FlashPlayerPanel flashPanel) { + JPanel pan = new JPanel(new BorderLayout()); + JLabel prevLabel = new HeaderLabel(mainPanel.translate("swfpreview")); + prevLabel.setHorizontalAlignment(SwingConstants.CENTER); + //prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + pan.add(prevLabel, BorderLayout.NORTH); + + Component leftComponent; + if (flashPanel != null) { + JPanel flashPlayPanel = new JPanel(new BorderLayout()); + flashPlayPanel.add(flashPanel, BorderLayout.CENTER); + + /*JPanel bottomPanel = new JPanel(new BorderLayout()); + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton selectColorButton = new JButton(View.getIcon("color16")); + selectColorButton.addActionListener(mainPanel::selectBkColor); + selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); + buttonsPanel.add(selectColorButton); + bottomPanel.add(buttonsPanel, BorderLayout.EAST); + + flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH);*/ + JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); + flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); + flashPlayPanel2.add(new PlayerControls(mainPanel, flashPanel), BorderLayout.SOUTH); + leftComponent = flashPlayPanel2; + } else { + JPanel swtPanel = new JPanel(new BorderLayout()); + swtPanel.add(new JLabel("
" + mainPanel.translate("notavailonthisplatform") + "
", JLabel.CENTER), BorderLayout.CENTER); + swtPanel.setBackground(View.getDefaultBackgroundColor()); + leftComponent = swtPanel; + } + + pan.add(leftComponent, BorderLayout.CENTER); + return pan; + } + + private JPanel createImagesCard() { + JPanel shapesCard = new JPanel(new BorderLayout()); + JPanel previewPanel = new JPanel(new BorderLayout()); + + JPanel previewCnt = new JPanel(new BorderLayout()); + imagePanel = new ImagePanel(); + imagePanel.setLoop(Configuration.loopMedia.get()); + previewCnt.add(imagePanel, BorderLayout.CENTER); + previewCnt.add(imagePlayControls = new PlayerControls(mainPanel, imagePanel), BorderLayout.SOUTH); + imagePlayControls.setMedia(imagePanel); + previewPanel.add(previewCnt, BorderLayout.CENTER); + JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); + prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); + //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + previewPanel.add(prevIntLabel, BorderLayout.NORTH); + + shapesCard.add(previewPanel, BorderLayout.CENTER); + + shapesCard.add(createImageButtonsPanel(), BorderLayout.SOUTH); + return shapesCard; + } + + private JPanel createMetadataCard() { + JPanel metadataCard = new JPanel(new BorderLayout()); + metadataEditor = new LineMarkedEditorPane(); + metadataCard.add(new JScrollPane(metadataEditor), BorderLayout.CENTER); + //metadataEditor.setContentType("text/xml"); + metadataEditor.setEditable(false); + + metadataEditor.setFont(new Font("Monospaced", Font.PLAIN, metadataEditor.getFont().getSize())); + metadataEditor.changeContentType("text/xml"); + metadataEditor.addTextChangedListener(this::metadataTextChanged); + + metadataCard.add(createMetadataButtonsPanel(), BorderLayout.SOUTH); + return metadataCard; + } + + private boolean isMetadataModified() { + return metadataSaveButton.isVisible() && metadataSaveButton.isEnabled(); + } + + private void setMetadataModified(boolean value) { + metadataSaveButton.setEnabled(value); + metadataCancelButton.setEnabled(value); + } + + private void metadataTextChanged() { + setMetadataModified(true); + } + + private void updateMetadataButtonsVisibility() { + boolean edit = metadataEditor.isEditable(); + boolean editorMode = Configuration.editorMode.get(); + metadataEditButton.setVisible(!readOnly && !edit); + metadataSaveButton.setVisible(!readOnly && edit); + boolean metadataModified = isMetadataModified(); + metadataCancelButton.setVisible(!readOnly && edit); + metadataCancelButton.setEnabled(metadataModified || !editorMode); + } + + private JPanel createBinaryCard() { + JPanel binaryCard = new JPanel(new BorderLayout()); + binaryPanel = new BinaryPanel(mainPanel); + binaryCard.add(binaryPanel, BorderLayout.CENTER); + binaryCard.add(createBinaryButtonsPanel(), BorderLayout.SOUTH); + return binaryCard; + } + + private JPanel createGenericTagCard() { + JPanel genericTagCard = new JPanel(new BorderLayout()); + genericTagPanel = new GenericTagTreePanel(mainPanel); + genericTagCard.add(genericTagPanel, BorderLayout.CENTER); + genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); + return genericTagCard; + } + + private void showCardLeft(String card) { + CardLayout cl = (CardLayout) (viewerCards.getLayout()); + cl.show(viewerCards, card); + } + + private void showCardRight(String card) { + CardLayout cl = (CardLayout) (displayWithPreview.getLayout()); + cl.show(displayWithPreview, card); + } + + public TextPanel getTextPanel() { + return textPanel; + } + + public void setParametersPanelVisible(boolean show) { + parametersPanel.setVisible(show); + } + + public void showFlashViewerPanel() { + parametersPanel.setVisible(false); + showCardLeft(FLASH_VIEWER_CARD); + } + + public void showImagePanel(Timelined timelined, SWF swf, int frame) { + showCardLeft(DRAW_PREVIEW_CARD); + parametersPanel.setVisible(false); + imagePlayControls.setMedia(imagePanel); + imagePanel.setTimelined(timelined, swf, frame); + } + + public void showImagePanel(SerializableImage image) { + showCardLeft(DRAW_PREVIEW_CARD); + parametersPanel.setVisible(false); + 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; + imagePlayControls.setMedia(media); + } + + public void showFontPanel(FontTag fontTag) { + fontPageNum = 0; + showFontPage(fontTag); + + showCardRight(CARDFONTPANEL); + if (!readOnly) { + parametersPanel.setVisible(true); + } + fontPanel.showFontTag(fontTag); + + int pageCount = getFontPageCount(fontTag); + if (pageCount > 1) { + prevFontsButton.setVisible(true); + nextFontsButton.setVisible(true); + } + } + + private void showFontPage(FontTag fontTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); + } + } + + public static int getFontPageCount(FontTag fontTag) { + int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; + if (pageCount < 1) { + pageCount = 1; + } + return pageCount; + } + + public void showEmpty() { + showCardLeft(EMPTY_CARD); + } + + public void showTextPanel(TextTag textTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); + } + + showCardRight(CARDTEXTPANEL); + if (!readOnly) { + parametersPanel.setVisible(true); + } + textPanel.setText(textTag); + } + + public void focusTextPanel() { + textPanel.focusTextValue(); + } + + public void clear() { + imagePanel.clearAll(); + if (media != null) { + try { + media.close(); + } catch (IOException ex) { + // ignore + } + } + + binaryPanel.setBinaryData(null); + genericTagPanel.clear(); + fontPanel.clear(); + } + + public void closeTag() { + textPanel.closeTag(); + } + + public static String formatMetadata(String input, int indent) { + input = input.replace("> <", "><"); + try { + Source xmlInput = new StreamSource(new StringReader(input)); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + StringWriter sw = new StringWriter(); + xmlOutput.setWriter(sw); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + transformerFactory.setAttribute("indent-number", indent); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "" + indent); + transformer.transform(xmlInput, xmlOutput); + + return xmlOutput.getWriter().toString(); + } catch (IllegalArgumentException | TransformerException e) { + return input; + } + } + + public void showMetaDataPanel(MetadataTag metadataTag) { + showCardLeft(METADATA_TAG_CARD); + this.metadataTag = metadataTag; + metadataEditor.setEditable(!readOnly && !metadataTag.isReadOnly() && Configuration.editorMode.get()); + metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + parametersPanel.setVisible(false); + } + + public void showBinaryPanel(DefineBinaryDataTag binaryDataTag) { + showCardLeft(BINARY_TAG_CARD); + binaryPanel.setBinaryData(binaryDataTag); + parametersPanel.setVisible(false); + } + + public void showGenericTagPanel(Tag tag) { + showCardLeft(GENERIC_TAG_CARD); + genericEditButton.setVisible(!tag.isReadOnly()); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, tag); + parametersPanel.setVisible(false); + } + + public void setImageReplaceButtonVisible(boolean show, boolean showAlpha) { + if (readOnly) { + show = false; + showAlpha = false; + } + replaceImageButton.setVisible(show); + replaceImageAlphaButton.setVisible(showAlpha); + prevFontsButton.setVisible(false); + nextFontsButton.setVisible(false); + } + + private static Tag classicTag(Tag t) { + if (t instanceof DefineCompactedFont) { + return ((DefineCompactedFont) t).toClassicFont(); + } + return t; + } + + private static void writeTag(Tag t, SWFOutputStream sos) throws IOException { + t = classicTag(t); + + t.writeTag(sos); + if (t instanceof CharacterIdTag) { + List chIdTags = t.getSwf().getCharacterIdTags(((CharacterIdTag) t).getCharacterId()); + if (chIdTags != null) { + for (CharacterIdTag chIdTag : chIdTags) { + if (!(chIdTag instanceof PlaceObjectTypeTag || chIdTag instanceof RemoveTag)) { + ((Tag) chIdTag).writeTag(sos); + } + } + } + } + } + + public void createAndShowTempSwf(TreeItem treeItem) { + SWF swf = null; + try { + if (tempFile != null) { + tempFile.delete(); + } + + tempFile = File.createTempFile("ffdec_view_", ".swf"); + tempFile.deleteOnExit(); + + Color backgroundColor = View.getSwfBackgroundColor(); + + if (treeItem instanceof Tag) { + Tag tag = (Tag) treeItem; + swf = tag.getSwf(); + if (tag instanceof FontTag) { //Fonts are always black on white + backgroundColor = View.getDefaultBackgroundColor(); + } + } else if (treeItem instanceof Frame) { + Frame fn = (Frame) treeItem; + swf = fn.getSwf(); + if (fn.timeline.timelined == swf) { + SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); + if (setBgColorTag != null) { + backgroundColor = setBgColorTag.backgroundColor.toColor(); + } + } + } + + int frameCount = 1; + float frameRate = swf.frameRate; + HashMap videoFrames = new HashMap<>(); + if (treeItem instanceof DefineVideoStreamTag) { + DefineVideoStreamTag vs = (DefineVideoStreamTag) treeItem; + SWF.populateVideoFrames(vs.getCharacterId(), swf.getTags(), videoFrames); + frameCount = videoFrames.size(); + } + + List soundFrames = new ArrayList<>(); + if (treeItem instanceof SoundStreamHeadTypeTag) { + soundFrames = ((SoundStreamHeadTypeTag) treeItem).getBlocks(); + frameCount = soundFrames.size(); + } + + if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) { + frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; + frameCount = (int) (MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate); + } + + if (treeItem instanceof DefineSoundTag) { + frameCount = 1; + } + + if (treeItem instanceof DefineSpriteTag) { + frameCount = ((DefineSpriteTag) treeItem).frameCount; + } + + byte[] data; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); + RECT outrect = new RECT(swf.displayRect); + + RECT treeItemBounds = null; + if (treeItem instanceof FontTag) { + outrect.Xmin = 0; + outrect.Ymin = 0; + outrect.Xmax = FontTag.PREVIEWSIZE * 20; + outrect.Ymax = FontTag.PREVIEWSIZE * 20; + } else if (treeItem instanceof BoundedTag) { + treeItemBounds = ((BoundedTag) treeItem).getRect(); + } else if (treeItem instanceof Frame) { + treeItemBounds = ((Frame) treeItem).timeline.timelined.getRect(); + } + + if (treeItemBounds != null) { + if (outrect.getWidth() < treeItemBounds.getWidth()) { + outrect.Xmax += treeItemBounds.getWidth() - outrect.getWidth(); + } + + if (outrect.getHeight() < treeItemBounds.getHeight()) { + outrect.Ymax += treeItemBounds.getHeight() - outrect.getHeight(); + } + } + + int width = outrect.getWidth(); + int height = outrect.getHeight(); + + sos2.writeRECT(outrect); + sos2.writeFIXED8(frameRate); + sos2.writeUI16(frameCount); //framecnt + + FileAttributesTag fa = swf.getFileAttributes(); + if (fa != null) { + fa.writeTag(sos2); + } + + SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); + if (setBgColorTag == null) { + setBgColorTag = new SetBackgroundColorTag(swf, new RGB(backgroundColor)); + } + + setBgColorTag.writeTag(sos2); + + if (treeItem instanceof Frame) { + Frame fn = (Frame) treeItem; + Timelined parent = fn.timeline.timelined; + List doneCharacters = new ArrayList<>(); + for (Tag t : parent.getTags()) { + if (t instanceof FileAttributesTag || t instanceof SetBackgroundColorTag) { + continue; + } + + if (t instanceof DoActionTag || t instanceof DoInitActionTag) { + // todo: Maybe DoABC tags should be removed, too + continue; + } + + Set needed = new HashSet<>(); + t.getNeededCharactersDeep(needed); + for (int n : needed) { + if (!doneCharacters.contains(n)) { + writeTag(swf.getCharacter(n), sos2); + doneCharacters.add(n); + } + } + + if (t instanceof ShowFrameTag || t instanceof PlaceObjectTypeTag || t instanceof RemoveTag) { + continue; + } + + if (t instanceof CharacterTag) { + int characterId = ((CharacterTag) t).getCharacterId(); + if (!doneCharacters.contains(characterId)) { + doneCharacters.add(((CharacterTag) t).getCharacterId()); + } + } + + writeTag(t, sos2); + } + + RECT r = parent.getRect(); + for (Map.Entry value : fn.layers.entrySet()) { + PlaceObjectTypeTag pot = value.getValue().toPlaceObjectTag(value.getKey()); + MATRIX mat = new MATRIX(pot.getMatrix()); + mat.translateX += width / 2 - r.getWidth() / 2; + mat.translateY += height / 2 - r.getHeight() / 2; + pot.setMatrix(mat); + pot.writeTag(sos2); + } + + new ShowFrameTag(swf).writeTag(sos2); + } else { + boolean isSprite = false; + if (treeItem instanceof DefineSpriteTag) { + isSprite = true; + } + int chtId = 0; + if (treeItem instanceof CharacterTag) { + chtId = ((CharacterTag) treeItem).getCharacterId(); + } + + if (treeItem instanceof DefineBitsTag) { + JPEGTablesTag jtt = swf.getJtt(); + if (jtt != null) { + jtt.writeTag(sos2); + } + } else if (treeItem instanceof AloneTag) { + } else { + Set needed = new HashSet<>(); + ((Tag) treeItem).getNeededCharactersDeep(needed); + for (int n : needed) { + if (isSprite && chtId == n) { + continue; + } + + CharacterTag characterTag = swf.getCharacter(n); + if (characterTag instanceof DefineBitsTag) { + JPEGTablesTag jtt = swf.getJtt(); + if (jtt != null) { + jtt.writeTag(sos2); + } + } + + writeTag(characterTag, sos2); + } + } + + writeTag((Tag) treeItem, sos2); + + MATRIX mat = new MATRIX(); + mat.hasRotate = false; + mat.hasScale = false; + mat.translateX = 0; + mat.translateY = 0; + if (treeItem instanceof BoundedTag) { + RECT r = ((BoundedTag) treeItem).getRect(); + mat.translateX = -r.Xmin; + mat.translateY = -r.Ymin; + mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; + mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; + } else { + mat.translateX = width / 4; + mat.translateY = height / 4; + } + if (treeItem instanceof FontTag) { + FontTag ft = (FontTag) classicTag((Tag) treeItem); + + int countGlyphsTotal = ft.getGlyphShapeTable().size(); + int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); + int fontId = ft.getFontId(); + int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); + int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); + if (rows == 0) { + rows = 1; + cols = 1; + } + int x = 0; + int y = 0; + int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; + countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); + List shapes = ft.getGlyphShapeTable(); + int maxw = 0; + for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { + RECT b = shapes.get(f).getBounds(); + if (b.Xmin == Integer.MAX_VALUE) { + continue; + } + if (b.Ymin == Integer.MAX_VALUE) { + continue; + } + int w = (int) (b.getWidth() / ft.getDivider()); + if (w > maxw) { + maxw = w; + } + x++; + } + + x = 0; + + int BORDER = 3 * 20; + + int textHeight = height / rows; + + while (maxw * textHeight / 1024.0 > width / cols - 2 * BORDER) { + textHeight--; + } + + MATRIX tmat = new MATRIX(); + for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { + if (x >= cols) { + x = 0; + y++; + } + List rec = new ArrayList<>(); + TEXTRECORD tr = new TEXTRECORD(); + + RECT b = shapes.get(f).getBounds(); + int xmin = b.Xmin == Integer.MAX_VALUE ? 0 : (int) (b.Xmin / ft.getDivider()); + xmin *= textHeight / 1024.0; + int ymin = b.Ymin == Integer.MAX_VALUE ? 0 : (int) (b.Ymin / ft.getDivider()); + ymin *= textHeight / 1024.0; + int w = (int) (b.getWidth() / ft.getDivider()); + w *= textHeight / 1024.0; + int h = (int) (b.getHeight() / ft.getDivider()); + h *= textHeight / 1024.0; + + tr.fontId = fontId; + tr.styleFlagsHasFont = true; + tr.textHeight = textHeight; + tr.xOffset = -xmin; + tr.yOffset = 0; + tr.styleFlagsHasXOffset = true; + tr.styleFlagsHasYOffset = true; + tr.glyphEntries = new ArrayList<>(1); + tr.styleFlagsHasColor = true; + tr.textColor = new RGB(0, 0, 0); + GLYPHENTRY ge = new GLYPHENTRY(); + + double ga = ft.getGlyphAdvance(f); + int cw = ga == -1 ? w : (int) (ga / ft.getDivider() * textHeight / 1024.0); + + ge.glyphAdvance = 0; + ge.glyphIndex = f; + tr.glyphEntries.add(ge); + rec.add(tr); + + tmat.translateX = x * width / cols + width / cols / 2 - w / 2; + tmat.translateY = y * height / rows + height / rows / 2; + new DefineTextTag(swf, 999 + f, new RECT(0, cw, ymin, ymin + h), new MATRIX(), rec).writeTag(sos2); + new PlaceObject2Tag(swf, false, 1 + f, 999 + f, tmat, null, 0, null, -1, null).writeTag(sos2); + x++; + } + new ShowFrameTag(swf).writeTag(sos2); + } else if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) { + new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { + new PlaceObject2Tag(swf, true, 1, chtId, mat, null, ratio, null, -1, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (treeItem instanceof SoundStreamHeadTypeTag) { + for (SoundStreamBlockTag blk : soundFrames) { + blk.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (treeItem instanceof DefineSoundTag) { + ExportAssetsTag ea = new ExportAssetsTag(swf); + DefineSoundTag ds = (DefineSoundTag) treeItem; + ea.tags.add(ds.soundId); + ea.names.add("my_define_sound"); + ea.writeTag(sos2); + List actions; + DoActionTag doa; + + doa = new DoActionTag(swf, null); + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" + + "StopSounds\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Push 9999 0.0 2 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" + + "StopSounds\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"onSoundComplete\"\n" + + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" + + "Push 0.0 register1 \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "}\n" + + "SetMember\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"execParam\"\n" + + "GetMember\n" + + "Push 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "StopSounds\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + new ShowFrameTag(swf).writeTag(sos2); + } else if (treeItem instanceof DefineVideoStreamTag) { + + new PlaceObject2Tag(swf, false, 1, chtId, mat, null, -1, null, -1, null).writeTag(sos2); + List frs = new ArrayList<>(videoFrames.values()); + Collections.sort(frs, new Comparator() { + @Override + public int compare(VideoFrameTag o1, VideoFrameTag o2) { + return o1.frameNum - o2.frameNum; + } + }); + boolean first = true; + int ratio = 0; + for (VideoFrameTag f : frs) { + if (!first) { + ratio++; + new PlaceObject2Tag(swf, true, 1, 0, null, null, ratio, null, -1, null).writeTag(sos2); + } + f.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + first = false; + } + } else if (treeItem instanceof DefineSpriteTag) { + DefineSpriteTag s = (DefineSpriteTag) treeItem; + Tag lastTag = null; + for (Tag t : s.getTags()) { + if (t instanceof EndTag) { + break; + } else if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; + MATRIX m = pt.getMatrix(); + MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); + pt.writeTagWithMatrix(sos2, m2); + lastTag = t; + } else { + t.writeTag(sos2); + lastTag = t; + } + } + if (!s.getTags().isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { + new ShowFrameTag(swf).writeTag(sos2); + } + } else { + new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + + } // not showframe + + new EndTag(swf).writeTag(sos2); + data = baos.toByteArray(); + } + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); + sos.write("FWS".getBytes()); + sos.write(swf.version); + sos.writeUI32(sos.getPos() + data.length + 4); + sos.write(data); + fos.flush(); + } + if (flashPanel != null) { + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); + } + showFlashViewerPanel(); + } catch (IOException | ActionParseException ex) { + Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void showSwf(SWF swf) { + Color backgroundColor = View.getDefaultBackgroundColor(); + SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); + if (setBgColorTag != null) { + backgroundColor = setBgColorTag.backgroundColor.toColor(); + } + + if (tempFile != null) { + tempFile.delete(); + } + try { + tempFile = File.createTempFile("ffdec_view_", ".swf"); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + swf.saveTo(fos); + } + //Inject Loader + if (swf.isAS3() && Configuration.autoOpenLoadedSWFs.get() && !Configuration.internalFlashViewer.get() && !DebuggerTools.hasDebugger(swf)) { + SWF instrSWF; + try (InputStream fis = new BufferedInputStream(new FileInputStream(tempFile))) { + instrSWF = new SWF(fis, false, false); + } + + DebuggerTools.switchDebugger(instrSWF); + DebuggerTools.injectDebugLoader(instrSWF); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + instrSWF.saveTo(fos); + } + } + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); + } catch (IOException iex) { + Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); + } catch (InterruptedException ex) { + + } + } + + private void editMetadataButtonActionPerformed(ActionEvent evt) { + TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof MetadataTag) { + metadataEditor.setEditable(true); + updateMetadataButtonsVisibility(); + } + } + + private void saveMetadataButtonActionPerformed(ActionEvent evt) { + metadataTag.xmlMetadata = metadataEditor.getText().replaceAll(">\r?\n<", "> <"); + metadataTag.setModified(true); + metadataEditor.setEditable(Configuration.editorMode.get()); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + mainPanel.repaintTree(); + } + + private void cancelMetadataButtonActionPerformed(ActionEvent evt) { + metadataEditor.setEditable(false); + metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); + metadataEditor.setEditable(Configuration.editorMode.get()); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + } + + private void editGenericTagButtonActionPerformed(ActionEvent evt) { + TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof TagScript) { + item = ((TagScript) item).getTag(); + } + + if (item instanceof Tag) { + genericEditButton.setVisible(false); + genericSaveButton.setVisible(true); + genericCancelButton.setVisible(true); + genericTagPanel.setEditMode(true, (Tag) item); + } + } + + private void saveGenericTagButtonActionPerformed(ActionEvent evt) { + if (genericTagPanel.save()) { + Tag tag = genericTagPanel.getTag(); + SWF swf = tag.getSwf(); + swf.clearImageCache(); + swf.updateCharacters(); + tag.getTimelined().resetTimeline(); + swf.assignClassesToSymbols(); + swf.assignExportNamesToSymbols(); + mainPanel.refreshTree(swf); + mainPanel.setTagTreeSelectedNode(tag); + genericEditButton.setVisible(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, null); + } + } + + private void cancelGenericTagButtonActionPerformed(ActionEvent evt) { + genericEditButton.setVisible(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, null); + } + + private void prevFontsButtonActionPerformed(ActionEvent evt) { + FontTag fontTag = fontPanel.getFontTag(); + int pageCount = getFontPageCount(fontTag); + fontPageNum = (fontPageNum + pageCount - 1) % pageCount; + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); + } + } + + private void nextFontsButtonActionPerformed(ActionEvent evt) { + FontTag fontTag = fontPanel.getFontTag(); + int pageCount = getFontPageCount(fontTag); + fontPageNum = (fontPageNum + 1) % pageCount; + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); + } + } + + @Override + public boolean tryAutoSave() { + // todo: implement + return textPanel.tryAutoSave() && false; + } + + @Override + public boolean isEditing() { + return textPanel.isEditing() + || (genericSaveButton.isVisible() && genericSaveButton.isEnabled()) + || (metadataSaveButton.isVisible() && metadataSaveButton.isEnabled()); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java index 222394eca..a29a9c72a 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java @@ -18,25 +18,55 @@ package com.jpexs.decompiler.flash.gui.generictageditors; import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; +import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer; import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException; import com.jpexs.helpers.Helper; import com.jpexs.helpers.ReflectionTools; +import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.io.IOException; import java.lang.reflect.Field; +import java.util.Timer; +import javax.swing.JComponent; import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; /** * * @author JPEXS */ -public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { +public class Amf3ValueEditor extends JPanel implements GenericTagEditor { + + private JEditorPane editor = new JEditorPane() { + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + @Override + public Component.BaselineResizeBehavior getBaselineResizeBehavior() { + return Component.BaselineResizeBehavior.CONSTANT_ASCENT; + } + + @Override + public int getBaseline(int width, int height) { + return 0; + } + }; private final Object obj; @@ -48,32 +78,16 @@ public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { private String fieldName; + private Amf3Value value; + private final JLabel errorLabel = new JLabel(); + private Timer hideErrorTimer; + private final int TIMEOUT = 5000; + @Override public void added() { - String s = getText(); - setContentType("text/javascript"); - setText(s); - } - - @Override - public boolean getScrollableTracksViewportWidth() { - return true; - } - - /* @Override - public Dimension getPreferredSize() { - Dimension ret = super.getPreferredSize(); - ret.width = 300; - return ret; - }*/ - @Override - public Component.BaselineResizeBehavior getBaselineResizeBehavior() { - return Component.BaselineResizeBehavior.CONSTANT_ASCENT; - } - - @Override - public int getBaseline(int width, int height) { - return 0; + String s = editor.getText(); + editor.setContentType("text/javascript"); + editor.setText(s); } public Amf3ValueEditor(String fieldName, Object obj, Field field, int index, Class type) { @@ -82,13 +96,31 @@ public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { this.index = index; this.type = type; this.fieldName = fieldName; + + setLayout(new BorderLayout()); + + Dimension d = new Dimension(500, 330); + setSize(d); + setPreferredSize(d); + + add(new JScrollPane(editor), BorderLayout.CENTER); + add(errorLabel, BorderLayout.SOUTH); + errorLabel.setBackground(Color.red); + errorLabel.setForeground(Color.white); + errorLabel.setOpaque(true); + + reset(); + } + + @Override + public void reset() { try { - Amf3Value val = (Amf3Value) ReflectionTools.getValue(obj, field, index); - if (val == null || val.getValue() == null) { - setText(""); + value = (Amf3Value) ReflectionTools.getValue(obj, field, index); + if (value == null || value.getValue() == null) { + editor.setText(""); } else { - String stringVal = Amf3Exporter.amfToString(val.getValue()); - setText(stringVal); + String stringVal = Amf3Exporter.amfToString(value.getValue()); + editor.setText(stringVal); } } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore @@ -98,8 +130,10 @@ public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { @Override public void save() { try { - ReflectionTools.setValue(obj, field, index, getChangedValue()); - } catch (IllegalArgumentException | IllegalAccessException ex) { + Object val = getChangedValue(); + ReflectionTools.setValue(obj, field, index, val); + value = (Amf3Value) val; + } catch (IllegalAccessException ex) { //ignore } } @@ -107,7 +141,7 @@ public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { @Override public void addChangeListener(final ChangeListener l) { final GenericTagEditor t = this; - addFocusListener(new FocusAdapter() { + editor.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { @@ -117,16 +151,39 @@ public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { }); } + @Override + public void validateValue() { + + Amf3Importer importer = new Amf3Importer(); + String textVal = editor.getText(); + try { + if (!textVal.trim().isEmpty()) { + importer.stringToAmf(textVal); + } + } catch (IOException | Amf3ParseException ex) { + final CaretListener cl = new CaretListener() { + @Override + public void caretUpdate(CaretEvent e) { + errorLabel.setVisible(false); + editor.removeCaretListener(this); + } + }; + editor.addCaretListener(cl); + errorLabel.setText("" + AppStrings.translate("error") + ":" + ex.getMessage() + ""); + errorLabel.setVisible(true); + throw new IllegalArgumentException("Invalid AMF value", ex); + } + } + @Override public Object getChangedValue() { Amf3Importer importer = new Amf3Importer(); - String textVal = getText(); + String textVal = editor.getText(); try { return textVal.trim().isEmpty() ? null : new Amf3Value(importer.stringToAmf(textVal)); } catch (IOException | Amf3ParseException ex) { - //ignore + return value; } - return null; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java index 57f999c53..6591db410 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java @@ -58,7 +58,16 @@ public class BinaryDataEditor extends JButton implements GenericTagEditor { this.fieldName = fieldName; setText(AppStrings.translate("button.replace")); addActionListener(this::buttonActionPerformed); + reset(); + } + + @Override + public void validateValue() { + } + + @Override + public void reset() { try { Object val = ReflectionTools.getValue(obj, field, index); if (val instanceof byte[]) { diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java index 5474f3a2a..9b593e0ac 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java @@ -50,6 +50,15 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { this.index = index; this.type = type; this.fieldName = fieldName; + reset(); + } + + @Override + public void validateValue() { + } + + @Override + public void reset() { try { setSelected((boolean) ReflectionTools.getValue(obj, field, index)); } catch (IllegalArgumentException | IllegalAccessException ex) { diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java index 8ac7fa53e..df4ea1a48 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java @@ -69,7 +69,7 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe private int colorType; - private JButton buttonChange; + private final JButton buttonChange; public int getColorType() { return colorType; @@ -86,6 +86,38 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe this.index = index; this.type = type; this.fieldName = fieldName; + + setLayout(new FlowLayout()); + + buttonChange = new JButton("") { + + @Override + protected void paintComponent(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + super.paintBorder(g); + } + + }; + buttonChange.setToolTipText(AppStrings.translate("button.selectcolor.hint")); + buttonChange.setCursor(new Cursor(Cursor.HAND_CURSOR)); + buttonChange.addActionListener(this); + + buttonChange.setBorderPainted(true); + buttonChange.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); + Dimension colorDim = new Dimension(16, 16); + buttonChange.setSize(colorDim); + buttonChange.setPreferredSize(colorDim); + add(buttonChange); + reset(); + } + + @Override + public void validateValue() { + } + + @Override + public void reset() { try { Object val = ReflectionTools.getValue(obj, field, index); if (val instanceof RGBA) { @@ -103,33 +135,11 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe if (val instanceof ARGB) { color = ((ARGB) val).toColor(); } - setLayout(new FlowLayout()); - //add(colorPanel); - buttonChange = new JButton("") { - - @Override - protected void paintComponent(Graphics g) { - g.setColor(getBackground()); - g.fillRect(0, 0, getWidth(), getHeight()); - super.paintBorder(g); - } - - }; - buttonChange.setToolTipText(AppStrings.translate("button.selectcolor.hint")); - buttonChange.setCursor(new Cursor(Cursor.HAND_CURSOR)); - buttonChange.addActionListener(this); buttonChange.setBackground(color); - buttonChange.setBorderPainted(true); - buttonChange.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); - Dimension colorDim = new Dimension(16, 16); - buttonChange.setSize(colorDim); - buttonChange.setPreferredSize(colorDim); - add(buttonChange); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } - } @Override @@ -137,7 +147,7 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe Object val = getChangedValue(); try { ReflectionTools.setValue(obj, field, index, val); - } catch (IllegalArgumentException | IllegalAccessException ex) { + } catch (IllegalAccessException ex) { // ignore } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java index ff55a7f5e..486bad8aa 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java @@ -26,6 +26,8 @@ public interface GenericTagEditor { public void added(); + public void reset(); + public void save(); public void addChangeListener(ChangeListener l); @@ -37,4 +39,6 @@ public interface GenericTagEditor { public Field getField(); public String getReadOnlyValue(); + + public void validateValue(); } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java index 16b44fe92..ccb9dd962 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java @@ -71,12 +71,19 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { this.type = type; this.swfType = swfType; this.fieldName = fieldName; + + JFormattedTextField jtf = ((JSpinner.NumberEditor) getEditor()).getTextField(); + DefaultFormatter formatter = (DefaultFormatter) jtf.getFormatter(); + formatter.setCommitsOnValidEdit(true); + reset(); + + } + + @Override + public void reset() { try { Object value = ReflectionTools.getValue(obj, field, index); setModel(getModel(swfType, value)); - JFormattedTextField jtf = ((JSpinner.NumberEditor) getEditor()).getTextField(); - DefaultFormatter formatter = (DefaultFormatter) jtf.getFormatter(); - formatter.setCommitsOnValidEdit(true); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } @@ -195,6 +202,10 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { }); } + @Override + public void validateValue() { + } + @Override public Object getChangedValue() { Object value = null; diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/ScrollPanedEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/ScrollPanedEditor.java deleted file mode 100644 index af151a97c..000000000 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/ScrollPanedEditor.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2016 Jindra - * - * 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.generictageditors; - -import java.awt.Component; -import java.awt.Dimension; -import java.lang.reflect.Field; -import javax.swing.JScrollPane; - -/** - * - * @author JPEXS - */ -public class ScrollPanedEditor extends JScrollPane implements GenericTagEditor { - - private final GenericTagEditor editor; - - public ScrollPanedEditor(GenericTagEditor editor) { - super((Component) editor); - this.editor = editor; - Dimension d = new Dimension(500, 300); - setSize(d); - setPreferredSize(d); - } - - @Override - public void save() { - editor.save(); - } - - @Override - public void addChangeListener(ChangeListener l) { - editor.addChangeListener(l); - } - - @Override - public Object getChangedValue() { - return editor.getChangedValue(); - } - - @Override - public String getFieldName() { - return editor.getFieldName(); - } - - @Override - public Field getField() { - return editor.getField(); - } - - @Override - public String getReadOnlyValue() { - return editor.getReadOnlyValue(); - } - - @Override - public void added() { - editor.added(); - } -} diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java index c08cfb0cd..5af38f02d 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java @@ -78,6 +78,11 @@ public class StringEditor extends JTextArea implements GenericTagEditor { setPreferredSize(d); setSize(d); } + reset(); + } + + @Override + public void reset() { try { setText((String) ReflectionTools.getValue(obj, field, index)); } catch (IllegalArgumentException | IllegalAccessException ex) { @@ -89,7 +94,7 @@ public class StringEditor extends JTextArea implements GenericTagEditor { public void save() { try { ReflectionTools.setValue(obj, field, index, getText()); - } catch (IllegalArgumentException | IllegalAccessException ex) { + } catch (IllegalAccessException ex) { // ignore } } @@ -131,4 +136,8 @@ public class StringEditor extends JTextArea implements GenericTagEditor { public void added() { } + + @Override + public void validateValue() { + } }