diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index eed9fd4cb..328d1ce2c 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -1,1195 +1,1176 @@ -/* - * Copyright (C) 2010-2015 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.abc.LineMarkedEditorPane; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -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.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.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -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.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.Helper; -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.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -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.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.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -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 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 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; - - public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { - super(JSplitPane.HORIZONTAL_SPLIT, Configuration.guiPreviewSplitPaneDividerLocationPercent); - this.mainPanel = mainPanel; - this.flashPanel = flashPanel; - - Runtime.getRuntime().addShutdownHook(new Thread() { - - @Override - public void run() { - if (tempFile != null) { - try { - tempFile.delete(); - } catch (Exception ex) { - } - } - } - - }); - - 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); - setLeftComponent(viewerCards); - - createParametersPanel(); - - showCardLeft(FLASH_VIEWER_CARD); - } - - 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("edit16")); - replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); - replaceImageButton.addActionListener(mainPanel::replaceButtonActionPerformed); - replaceImageButton.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(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.setContentType("text/xml"); - metadataEditor.getDocument().addDocumentListener(new DocumentListener() { - - @Override - public void insertUpdate(DocumentEvent e) { - metadataTextChanged(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - metadataTextChanged(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - metadataTextChanged(); - } - }); - - metadataCard.add(createMetadataButtonsPanel(), BorderLayout.SOUTH); - return metadataCard; - } - - private boolean isMetadataModified() { - return metadataSaveButton.isVisible() && metadataSaveButton.isEnabled(); - } - - private void setMetadataModified(boolean value) { - metadataSaveButton.setEnabled(value); - } - - private void metadataTextChanged() { - setMetadataModified(true); - updateMetadataButtonsVisibility(); - } - - private void updateMetadataButtonsVisibility() { - boolean edit = metadataEditor.isEditable(); - boolean editorMode = Configuration.editorMode.get(); - metadataEditButton.setVisible(!edit); - metadataSaveButton.setVisible(edit); - boolean metadataModified = isMetadataModified(); - metadataCancelButton.setVisible(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(); - 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); - 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 showTextPanel(TextTag textTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); - } - - showCardRight(CARDTEXTPANEL); - parametersPanel.setVisible(true); - textPanel.setText(textTag); - } - - public void focusTextPanel() { - textPanel.focusTextValue(); - } - - public void clear() { - imagePanel.clearAll(); - if (media != null) { - media.pause(); - } - - 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(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(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, tag); - parametersPanel.setVisible(false); - } - - public void setImageReplaceButtonVisible(boolean show) { - replaceImageButton.setVisible(show); - prevFontsButton.setVisible(false); - nextFontsButton.setVisible(false); - } - - private static Tag classicTag(Tag t) { - if (t instanceof DefineCompactedFont) { - return ((DefineCompactedFont) t).toClassicFont(); - } - return t; - } - - public void createAndShowTempSwf(TreeItem tagObj) { - SWF swf; - try { - if (tempFile != null) { - tempFile.delete(); - } - tempFile = File.createTempFile("ffdec_view_", ".swf"); - tempFile.deleteOnExit(); - - Color backgroundColor = View.getSwfBackgroundColor(); - - if (tagObj instanceof FontTag) { //Fonts are always black on white - backgroundColor = View.getDefaultBackgroundColor(); - } - - if (tagObj instanceof Frame) { - Frame fn = (Frame) tagObj; - swf = fn.getSwf(); - if (fn.timeline.timelined == swf) { - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); - break; - } - } - } - } else { - Tag tag = (Tag) tagObj; - swf = tag.getSwf(); - } - - int frameCount = 1; - int frameRate = swf.frameRate; - HashMap videoFrames = new HashMap<>(); - if (tagObj instanceof DefineVideoStreamTag) { - DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; - SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); - frameCount = videoFrames.size(); - } - - List soundFrames = new ArrayList<>(); - if (tagObj instanceof SoundStreamHeadTypeTag) { - soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); - frameCount = soundFrames.size(); - } - - if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { - frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; - frameCount = MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate; - } - - if (tagObj instanceof DefineSoundTag) { - frameCount = 1; - } - - if (tagObj instanceof DefineSpriteTag) { - frameCount = ((DefineSpriteTag) tagObj).frameCount; - } - - byte[] data; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); - RECT outrect = new RECT(swf.displayRect); - - if (tagObj instanceof FontTag) { - outrect.Xmin = 0; - outrect.Ymin = 0; - outrect.Xmax = FontTag.PREVIEWSIZE * 20; - outrect.Ymax = FontTag.PREVIEWSIZE * 20; - } - int width = outrect.getWidth(); - int height = outrect.getHeight(); - - sos2.writeRECT(outrect); - sos2.writeUI8(0); - sos2.writeUI8(frameRate); - sos2.writeUI16(frameCount); //framecnt - - /*FileAttributesTag fa = new FileAttributesTag(); - sos2.writeTag(fa); - */ - new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); - - if (tagObj instanceof Frame) { - Frame fn = (Frame) tagObj; - Timelined parent = fn.timeline.timelined; - List subs = fn.timeline.tags; - List doneCharacters = new ArrayList<>(); - int frameCnt = 0; - for (Tag t : subs) { - if (t instanceof ShowFrameTag) { - frameCnt++; - continue; - } - if (frameCnt > fn.frame) { - break; - } - - 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)) { - classicTag(swf.getCharacter(n)).writeTag(sos2); - doneCharacters.add(n); - } - } - if (t instanceof CharacterTag) { - int characterId = ((CharacterTag) t).getCharacterId(); - if (!doneCharacters.contains(characterId)) { - doneCharacters.add(((CharacterTag) t).getCharacterId()); - } - } - classicTag(t).writeTag(sos2); - - if (parent != null) { - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; - int chid = pot.getCharacterId(); - int depth = pot.getDepth(); - MATRIX mat = pot.getMatrix(); - if (mat == null) { - mat = new MATRIX(); - } - mat = Helper.deepCopy(mat); - if (parent instanceof BoundedTag) { - RECT r = ((BoundedTag) parent).getRect(); - mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; - mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; - } else { - mat.translateX += width / 2; - mat.translateY += height / 2; - } - new PlaceObject2Tag(swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null).writeTag(sos2); - - } - } - } - new ShowFrameTag(swf).writeTag(sos2); - } else { - - boolean isSprite = false; - if (tagObj instanceof DefineSpriteTag) { - isSprite = true; - } - int chtId = 0; - if (tagObj instanceof CharacterTag) { - chtId = ((CharacterTag) tagObj).getCharacterId(); - } - - if (tagObj instanceof DefineBitsTag) { - JPEGTablesTag jtt = swf.getJtt(); - if (jtt != null) { - jtt.writeTag(sos2); - } - } else if (tagObj instanceof AloneTag) { - } else { - Set needed = new HashSet<>(); - ((Tag) tagObj).getNeededCharactersDeep(needed); - for (int n : needed) { - if (isSprite && chtId == n) { - continue; - } - classicTag(swf.getCharacter(n)).writeTag(sos2); - } - } - - classicTag((Tag) tagObj).writeTag(sos2); - - MATRIX mat = new MATRIX(); - mat.hasRotate = false; - mat.hasScale = false; - mat.translateX = 0; - mat.translateY = 0; - if (tagObj instanceof BoundedTag) { - RECT r = ((BoundedTag) tagObj).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 (tagObj instanceof FontTag) { - - FontTag ft = (FontTag) classicTag((Tag) tagObj); - - 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)); - 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, false, false, true, false, true, true, false, 1 + f, 999 + f, tmat, null, 0, null, 0, null).writeTag(sos2); - x++; - } - new ShowFrameTag(swf).writeTag(sos2); - } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { - new PlaceObject2Tag(swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (tagObj instanceof SoundStreamHeadTypeTag) { - for (SoundStreamBlockTag blk : soundFrames) { - blk.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (tagObj instanceof DefineSoundTag) { - ExportAssetsTag ea = new ExportAssetsTag(swf); - DefineSoundTag ds = (DefineSoundTag) tagObj; - 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); - //if (flashPanel != null) { - // flashPanel.specialPlayback = true; - //} - } else if (tagObj instanceof DefineVideoStreamTag) { - - new PlaceObject2Tag(swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, 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, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null).writeTag(sos2); - } - f.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - first = false; - } - } else if (tagObj instanceof DefineSpriteTag) { - DefineSpriteTag s = (DefineSpriteTag) tagObj; - Tag lastTag = null; - for (Tag t : s.subTags) { - 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.subTags.isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { - new ShowFrameTag(swf).writeTag(sos2); - } - } else { - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, 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(); - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); - break; - } - } - - if (tempFile != null) { - tempFile.delete(); - } - try { - tempFile = File.createTempFile("ffdec_view_", ".swf"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - swf.saveTo(fos); - } - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); - } catch (IOException iex) { - Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); - } - } - - 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(); - mainPanel.repaintTree(); - 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-2015 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.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.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.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +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.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.Helper; +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.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +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.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 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 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; + + public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { + super(JSplitPane.HORIZONTAL_SPLIT, Configuration.guiPreviewSplitPaneDividerLocationPercent); + this.mainPanel = mainPanel; + this.flashPanel = flashPanel; + + Runtime.getRuntime().addShutdownHook(new Thread() { + + @Override + public void run() { + if (tempFile != null) { + try { + tempFile.delete(); + } catch (Exception ex) { + } + } + } + + }); + + 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); + setLeftComponent(viewerCards); + + createParametersPanel(); + + showCardLeft(FLASH_VIEWER_CARD); + } + + 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("edit16")); + replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); + replaceImageButton.addActionListener(mainPanel::replaceButtonActionPerformed); + replaceImageButton.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(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); + } + + private void metadataTextChanged() { + setMetadataModified(true); + } + + private void updateMetadataButtonsVisibility() { + boolean edit = metadataEditor.isEditable(); + boolean editorMode = Configuration.editorMode.get(); + metadataEditButton.setVisible(!edit); + metadataSaveButton.setVisible(edit); + boolean metadataModified = isMetadataModified(); + metadataCancelButton.setVisible(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(); + 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); + 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 showTextPanel(TextTag textTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); + } + + showCardRight(CARDTEXTPANEL); + parametersPanel.setVisible(true); + textPanel.setText(textTag); + } + + public void focusTextPanel() { + textPanel.focusTextValue(); + } + + public void clear() { + imagePanel.clearAll(); + if (media != null) { + media.pause(); + } + + 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(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(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, tag); + parametersPanel.setVisible(false); + } + + public void setImageReplaceButtonVisible(boolean show) { + replaceImageButton.setVisible(show); + prevFontsButton.setVisible(false); + nextFontsButton.setVisible(false); + } + + private static Tag classicTag(Tag t) { + if (t instanceof DefineCompactedFont) { + return ((DefineCompactedFont) t).toClassicFont(); + } + return t; + } + + public void createAndShowTempSwf(TreeItem tagObj) { + SWF swf; + try { + if (tempFile != null) { + tempFile.delete(); + } + tempFile = File.createTempFile("ffdec_view_", ".swf"); + tempFile.deleteOnExit(); + + Color backgroundColor = View.getSwfBackgroundColor(); + + if (tagObj instanceof FontTag) { //Fonts are always black on white + backgroundColor = View.getDefaultBackgroundColor(); + } + + if (tagObj instanceof Frame) { + Frame fn = (Frame) tagObj; + swf = fn.getSwf(); + if (fn.timeline.timelined == swf) { + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); + break; + } + } + } + } else { + Tag tag = (Tag) tagObj; + swf = tag.getSwf(); + } + + int frameCount = 1; + int frameRate = swf.frameRate; + HashMap videoFrames = new HashMap<>(); + if (tagObj instanceof DefineVideoStreamTag) { + DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; + SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); + frameCount = videoFrames.size(); + } + + List soundFrames = new ArrayList<>(); + if (tagObj instanceof SoundStreamHeadTypeTag) { + soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); + frameCount = soundFrames.size(); + } + + if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { + frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; + frameCount = MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate; + } + + if (tagObj instanceof DefineSoundTag) { + frameCount = 1; + } + + if (tagObj instanceof DefineSpriteTag) { + frameCount = ((DefineSpriteTag) tagObj).frameCount; + } + + byte[] data; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); + RECT outrect = new RECT(swf.displayRect); + + if (tagObj instanceof FontTag) { + outrect.Xmin = 0; + outrect.Ymin = 0; + outrect.Xmax = FontTag.PREVIEWSIZE * 20; + outrect.Ymax = FontTag.PREVIEWSIZE * 20; + } + int width = outrect.getWidth(); + int height = outrect.getHeight(); + + sos2.writeRECT(outrect); + sos2.writeUI8(0); + sos2.writeUI8(frameRate); + sos2.writeUI16(frameCount); //framecnt + + /*FileAttributesTag fa = new FileAttributesTag(); + sos2.writeTag(fa); + */ + new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); + + if (tagObj instanceof Frame) { + Frame fn = (Frame) tagObj; + Timelined parent = fn.timeline.timelined; + List subs = fn.timeline.tags; + List doneCharacters = new ArrayList<>(); + int frameCnt = 0; + for (Tag t : subs) { + if (t instanceof ShowFrameTag) { + frameCnt++; + continue; + } + if (frameCnt > fn.frame) { + break; + } + + 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)) { + classicTag(swf.getCharacter(n)).writeTag(sos2); + doneCharacters.add(n); + } + } + if (t instanceof CharacterTag) { + int characterId = ((CharacterTag) t).getCharacterId(); + if (!doneCharacters.contains(characterId)) { + doneCharacters.add(((CharacterTag) t).getCharacterId()); + } + } + classicTag(t).writeTag(sos2); + + if (parent != null) { + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; + int chid = pot.getCharacterId(); + int depth = pot.getDepth(); + MATRIX mat = pot.getMatrix(); + if (mat == null) { + mat = new MATRIX(); + } + mat = Helper.deepCopy(mat); + if (parent instanceof BoundedTag) { + RECT r = ((BoundedTag) parent).getRect(); + mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; + mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; + } else { + mat.translateX += width / 2; + mat.translateY += height / 2; + } + new PlaceObject2Tag(swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null).writeTag(sos2); + + } + } + } + new ShowFrameTag(swf).writeTag(sos2); + } else { + + boolean isSprite = false; + if (tagObj instanceof DefineSpriteTag) { + isSprite = true; + } + int chtId = 0; + if (tagObj instanceof CharacterTag) { + chtId = ((CharacterTag) tagObj).getCharacterId(); + } + + if (tagObj instanceof DefineBitsTag) { + JPEGTablesTag jtt = swf.getJtt(); + if (jtt != null) { + jtt.writeTag(sos2); + } + } else if (tagObj instanceof AloneTag) { + } else { + Set needed = new HashSet<>(); + ((Tag) tagObj).getNeededCharactersDeep(needed); + for (int n : needed) { + if (isSprite && chtId == n) { + continue; + } + classicTag(swf.getCharacter(n)).writeTag(sos2); + } + } + + classicTag((Tag) tagObj).writeTag(sos2); + + MATRIX mat = new MATRIX(); + mat.hasRotate = false; + mat.hasScale = false; + mat.translateX = 0; + mat.translateY = 0; + if (tagObj instanceof BoundedTag) { + RECT r = ((BoundedTag) tagObj).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 (tagObj instanceof FontTag) { + + FontTag ft = (FontTag) classicTag((Tag) tagObj); + + 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)); + 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, false, false, true, false, true, true, false, 1 + f, 999 + f, tmat, null, 0, null, 0, null).writeTag(sos2); + x++; + } + new ShowFrameTag(swf).writeTag(sos2); + } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { + new PlaceObject2Tag(swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (tagObj instanceof SoundStreamHeadTypeTag) { + for (SoundStreamBlockTag blk : soundFrames) { + blk.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (tagObj instanceof DefineSoundTag) { + ExportAssetsTag ea = new ExportAssetsTag(swf); + DefineSoundTag ds = (DefineSoundTag) tagObj; + 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); + //if (flashPanel != null) { + // flashPanel.specialPlayback = true; + //} + } else if (tagObj instanceof DefineVideoStreamTag) { + + new PlaceObject2Tag(swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, 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, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null).writeTag(sos2); + } + f.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + first = false; + } + } else if (tagObj instanceof DefineSpriteTag) { + DefineSpriteTag s = (DefineSpriteTag) tagObj; + Tag lastTag = null; + for (Tag t : s.subTags) { + 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.subTags.isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { + new ShowFrameTag(swf).writeTag(sos2); + } + } else { + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, 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(); + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); + break; + } + } + + if (tempFile != null) { + tempFile.delete(); + } + try { + tempFile = File.createTempFile("ffdec_view_", ".swf"); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + swf.saveTo(fos); + } + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); + } catch (IOException iex) { + Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); + } + } + + 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(); + mainPanel.repaintTree(); + 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/TextPanel.java b/src/com/jpexs/decompiler/flash/gui/TextPanel.java index 5b0c4459a..52838d47b 100644 --- a/src/com/jpexs/decompiler/flash/gui/TextPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/TextPanel.java @@ -18,8 +18,8 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.abc.LineMarkedEditorPane; import com.jpexs.decompiler.flash.gui.controls.JRepeatButton; +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; import com.jpexs.decompiler.flash.helpers.HighlightedText; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; @@ -43,8 +43,6 @@ import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingConstants; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; /** @@ -99,24 +97,8 @@ public class TextPanel extends JPanel implements TagEditorPanel { textValue = new LineMarkedEditorPane(); add(new JScrollPane(textValue), BorderLayout.CENTER); textValue.setFont(new Font("Monospaced", Font.PLAIN, textValue.getFont().getSize())); - textValue.setContentType("text/swftext"); - textValue.getDocument().addDocumentListener(new DocumentListener() { - - @Override - public void insertUpdate(DocumentEvent e) { - textChanged(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - textChanged(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - textChanged(); - } - }); + textValue.changeContentType("text/swftext"); + textValue.addTextChangedListener(this::textChanged); JPanel textButtonsPanel = new JPanel(); textButtonsPanel.setLayout(new FlowLayout(SwingConstants.WEST)); @@ -370,7 +352,6 @@ public class TextPanel extends JPanel implements TagEditorPanel { private void textChanged() { setModified(true); - updateButtonsVisibility(); showTextComparingPreview(); } diff --git a/src/com/jpexs/decompiler/flash/gui/View.java b/src/com/jpexs/decompiler/flash/gui/View.java index 5d2a8b0eb..2d4733726 100644 --- a/src/com/jpexs/decompiler/flash/gui/View.java +++ b/src/com/jpexs/decompiler/flash/gui/View.java @@ -1,654 +1,654 @@ -/* - * Copyright (C) 2010-2015 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.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Desktop; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.TexturePaint; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowEvent; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.ImageIO; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.ActionMap; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.InputMap; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JRootPane; -import javax.swing.JTable; -import javax.swing.JTree; -import javax.swing.KeyStroke; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.basic.BasicColorChooserUI; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableModel; -import javax.swing.text.JTextComponent; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; -import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon; -import org.pushingpixels.substance.api.ColorSchemeAssociationKind; -import org.pushingpixels.substance.api.ComponentState; -import org.pushingpixels.substance.api.DecorationAreaType; -import org.pushingpixels.substance.api.SubstanceColorScheme; -import org.pushingpixels.substance.api.SubstanceConstants; -import org.pushingpixels.substance.api.SubstanceLookAndFeel; -import org.pushingpixels.substance.api.fonts.FontPolicy; -import org.pushingpixels.substance.api.fonts.FontSet; -import org.pushingpixels.substance.api.skin.SubstanceOfficeBlue2007LookAndFeel; -import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; - -/** - * Contains methods for GUI - * - * @author JPEXS - */ -public class View { - - public static Color getDefaultBackgroundColor() { - return SubstanceLookAndFeel.getCurrentSkin().getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); - } - - private static Color swfBackgroundColor = null; - - public static void setSwfBackgroundColor(Color swfBackgroundColor) { - View.swfBackgroundColor = swfBackgroundColor; - } - - public static Color getSwfBackgroundColor() { - if (swfBackgroundColor == null) { - return getDefaultBackgroundColor(); - } - return swfBackgroundColor; - } - - private static final BufferedImage transparentTexture; - - public static final TexturePaint transparentPaint; - - private static final Color transparentColor1 = new Color(0x99, 0x99, 0x99); - - private static final Color transparentColor2 = new Color(0x66, 0x66, 0x66); - - static { - transparentTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); - Graphics g = transparentTexture.getGraphics(); - g.setColor(transparentColor1); - g.fillRect(0, 0, 16, 16); - g.setColor(transparentColor2); - g.fillRect(0, 0, 8, 8); - g.fillRect(8, 8, 8, 8); - transparentPaint = new TexturePaint(View.transparentTexture, new Rectangle(0, 0, transparentTexture.getWidth(), transparentTexture.getHeight())); - } - - /** - * Sets windows Look and Feel - */ - public static void setLookAndFeel() { - - //Save default font for Chinese characters - final Font defaultFont = (new JLabel()).getFont(); - try { - - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - - } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { - } - - execInEventDispatch(() -> { - try { - UIManager.setLookAndFeel(new SubstanceOfficeBlue2007LookAndFeel()); - SubstanceLookAndFeel.setSkin(Configuration.guiSkin.get()); - UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, 0.999);//This works for not changing labels color and not changing Dialogs title - UIManager.put("Tree.expandedIcon", getIcon("expand16")); - UIManager.put("Tree.collapsedIcon", getIcon("collapse16")); - UIManager.put("ColorChooserUI", BasicColorChooserUI.class.getName()); - UIManager.put("ColorChooser.swatchesRecentSwatchSize", new Dimension(20, 20)); - UIManager.put("ColorChooser.swatchesSwatchSize", new Dimension(20, 20)); - UIManager.put("RibbonApplicationMenuPopupPanelUI", MyRibbonApplicationMenuPopupPanelUI.class.getName()); - UIManager.put("RibbonApplicationMenuButtonUI", MyRibbonApplicationMenuButtonUI.class.getName()); - UIManager.put("ProgressBarUI", MyProgressBarUI.class.getName()); - UIManager.put("TextField.background", Color.white); - UIManager.put("FormattedTextField.background", Color.white); - UIManager.put("CommandButtonUI", MyCommandButtonUI.class.getName()); - - FontPolicy pol = SubstanceLookAndFeel.getFontPolicy(); - final FontSet fs = pol.getFontSet("Substance", null); - - //Restore default font for chinese characters - SubstanceLookAndFeel.setFontPolicy(new FontPolicy() { - - private final FontSet fontSet = new FontSet() { - - private FontUIResource controlFont; - - private FontUIResource menuFont; - - private FontUIResource titleFont; - - private FontUIResource windowTitleFont; - - private FontUIResource smallFont; - - private FontUIResource messageFont; - - @Override - public FontUIResource getControlFont() { - if (controlFont == null) { - FontUIResource f = fs.getControlFont(); - controlFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return controlFont; - } - - @Override - public FontUIResource getMenuFont() { - if (menuFont == null) { - FontUIResource f = fs.getMenuFont(); - menuFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return menuFont; - } - - @Override - public FontUIResource getTitleFont() { - if (titleFont == null) { - FontUIResource f = fs.getTitleFont(); - titleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return titleFont; - } - - @Override - public FontUIResource getWindowTitleFont() { - if (windowTitleFont == null) { - FontUIResource f = fs.getWindowTitleFont(); - windowTitleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return windowTitleFont; - } - - @Override - public FontUIResource getSmallFont() { - if (smallFont == null) { - FontUIResource f = fs.getSmallFont(); - smallFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return smallFont; - } - - @Override - public FontUIResource getMessageFont() { - if (messageFont == null) { - FontUIResource f = fs.getMessageFont(); - messageFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return messageFont; - } - }; - - @Override - public FontSet getFontSet(String string, UIDefaults uid) { - return fontSet; - } - }); - } catch (UnsupportedLookAndFeelException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - }); - - UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, SubstanceConstants.TabContentPaneBorderKind.SINGLE_FULL); - - JFrame.setDefaultLookAndFeelDecorated(true); - JDialog.setDefaultLookAndFeelDecorated(true); - } - - /** - * Loads image from resources - * - * @param name Name of the image - * @return loaded Image - */ - public static BufferedImage loadImage(String name) { - URL imageURL = View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + name + ".png"); - try { - return ImageIO.read(imageURL); - } catch (IOException ex) { - return null; - } - } - - /** - * Sets icon of specified frame to ASDec icon - * - * @param f Frame to set icon in - */ - public static void setWindowIcon(Window f) { - List images = new ArrayList<>(); - MyResizableIcon[] icons = MyRibbonApplicationMenuButtonUI.getIcons(); - MyResizableIcon icon = icons[1]; - int sizes[] = new int[]{16, 32, 48, 256}; - for (int size : sizes) { - icon.setIconSize(size, size); - BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR); - icon.paintIcon(f, bi.getGraphics(), 0, 0); - images.add(bi); - } - f.setIconImages(images); - } - - /** - * Centers specified frame on the screen - * - * @param f Frame to center on the screen - */ - public static void centerScreen(Window f) { - centerScreen(f, 0); // todo, set screen to the currently active screen instead of the first screen in a multi screen setup, (maybe by using the screen where the main window is now classic or ribbon?) - } - - public static void centerScreen(Window f, int screen) { - - GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice[] allDevices = env.getScreenDevices(); - int topLeftX, topLeftY, screenX, screenY, windowPosX, windowPosY; - - if (screen < allDevices.length && screen > -1) { - topLeftX = allDevices[screen].getDefaultConfiguration().getBounds().x; - topLeftY = allDevices[screen].getDefaultConfiguration().getBounds().y; - - screenX = allDevices[screen].getDefaultConfiguration().getBounds().width; - screenY = allDevices[screen].getDefaultConfiguration().getBounds().height; - } else { - topLeftX = allDevices[0].getDefaultConfiguration().getBounds().x; - topLeftY = allDevices[0].getDefaultConfiguration().getBounds().y; - - screenX = allDevices[0].getDefaultConfiguration().getBounds().width; - screenY = allDevices[0].getDefaultConfiguration().getBounds().height; - } - - windowPosX = ((screenX - f.getWidth()) / 2) + topLeftX; - windowPosY = ((screenY - f.getHeight()) / 2) + topLeftY; - - f.setLocation(windowPosX, windowPosY); - } - - public static ImageIcon getIcon(String name) { - return new ImageIcon(View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); - } - - private static final KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - - private static final String dispatchWindowClosingActionMapKey = "com.jpexs.dispatch:WINDOW_CLOSING"; - - public static void installEscapeCloseOperation(final JDialog dialog) { - Action dispatchClosing = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent event) { - dialog.dispatchEvent(new WindowEvent( - dialog, WindowEvent.WINDOW_CLOSING)); - } - }; - JRootPane root = dialog.getRootPane(); - root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( - escapeStroke, dispatchWindowClosingActionMapKey); - root.getActionMap().put(dispatchWindowClosingActionMapKey, dispatchClosing); - } - - public static ImageWrapperResizableIcon getResizableIcon(String resource) { - return ImageWrapperResizableIcon.getIcon(View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"), new Dimension(256, 256)); - } - - public static MyResizableIcon getMyResizableIcon(String resource) { - try { - return new MyResizableIcon(ImageIO.read(View.class.getResourceAsStream("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"))); - } catch (IOException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - return null; - } - } - - public static void execInEventDispatch(Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - try { - SwingUtilities.invokeAndWait(r); - } catch (InterruptedException ex) { - } catch (InvocationTargetException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - public static void execInEventDispatchLater(Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - SwingUtilities.invokeLater(r); - } - } - - public static int showOptionDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageType, final Icon icon, final Object[] options, final Object initialValue) { - final int[] ret = new int[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue); - }); - return ret[0]; - } - - public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType) { - return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE); - } - - public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageTyp) { - final int ret[] = new int[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageTyp); - }); - return ret[0]; - } - - public static int showConfirmDialog(Component parentComponent, String message, String title, int optionType, ConfigurationItem showAgainConfig, int defaultOption) { - return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE, showAgainConfig, defaultOption); - } - - public static int showConfirmDialog(final Component parentComponent, String message, final String title, final int optionType, final int messageTyp, ConfigurationItem showAgainConfig, int defaultOption) { - - JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); - final JPanel warPanel = new JPanel(new BorderLayout()); - warPanel.add(warLabel, BorderLayout.CENTER); - JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); - donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); - warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); - - if (donotShowAgainCheckBox.isSelected()) { - return defaultOption; - } - - final int ret[] = new int[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showConfirmDialog(parentComponent, warPanel, title, optionType, messageTyp); - }); - showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); - return ret[0]; - } - - public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType) { - showMessageDialog(parentComponent, message, title, messageType, null); - } - - public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType, ConfigurationItem showAgainConfig) { - - execInEventDispatch(() -> { - Object msg = message; - JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); - if (showAgainConfig != null) { - JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); - final JPanel warPanel = new JPanel(new BorderLayout()); - warPanel.add(warLabel, BorderLayout.CENTER); - donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); - warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); - msg = warPanel; - if (donotShowAgainCheckBox.isSelected()) { - return; - } - } - final Object fmsg = msg; - - JOptionPane.showMessageDialog(parentComponent, fmsg, title, messageType); - if (showAgainConfig != null) { - showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); - } - }); - } - - public static void showMessageDialog(final Component parentComponent, final Object message) { - execInEventDispatch(() -> { - JOptionPane.showMessageDialog(parentComponent, message); - }); - } - - public static String showInputDialog(final Object message, final Object initialSelection) { - final String[] ret = new String[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showInputDialog(message, initialSelection); - }); - return ret[0]; - } - - public static SubstanceColorScheme getColorScheme() { - return SubstanceColorSchemeUtilities.getActiveColorScheme(new JButton(), ComponentState.ENABLED); - } - - public static void refreshTree(JTree tree, TreeModel model) { - List> expandedNodes = getExpandedNodes(tree); - tree.setModel(model); - expandTreeNodes(tree, expandedNodes); - } - - public static List> getExpandedNodes(JTree tree) { - List> expandedNodes = new ArrayList<>(); - int rowCount = tree.getRowCount(); - for (int i = 0; i < rowCount; i++) { - try { - TreePath path = tree.getPathForRow(i); - if (tree.isExpanded(path)) { - List pathAsStringList = new ArrayList<>(); - for (Object pathCompnent : path.getPath()) { - pathAsStringList.add(pathCompnent.toString()); - } - expandedNodes.add(pathAsStringList); - } - } catch (IndexOutOfBoundsException | NullPointerException ex) { - // TreeNode was removed, ignore - } - } - return expandedNodes; - } - - public static void expandTreeNodes(JTree tree, List> pathsToExpand) { - for (List pathAsStringList : pathsToExpand) { - expandTreeNode(tree, pathAsStringList); - } - } - - private static TreePath expandTreeNode(JTree tree, List pathAsStringList) { - TreePath tp = getTreePathByPathStrings(tree, pathAsStringList); - tree.expandPath(tp); - return tp; - } - - public static TreePath getTreePathByPathStrings(JTree tree, List pathAsStringList) { - TreeModel model = tree.getModel(); - if (model == null) { - return null; - } - - Object node = model.getRoot(); - - if (pathAsStringList.isEmpty()) { - return null; - } - if (!pathAsStringList.get(0).equals(node.toString())) { - return null; - } - - List path = new ArrayList<>(); - path.add(node); - - for (int i = 1; i < pathAsStringList.size(); i++) { - String name = pathAsStringList.get(i); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - if (child.toString().equals(name)) { - node = child; - path.add(node); - break; - } - } - } - - TreePath tp = new TreePath(path.toArray(new Object[path.size()])); - return tp; - } - - public static void expandTreeNodes(JTree tree, TreePath parent, boolean expand) { - expandTreeNodesRecursive(tree, parent, expand); - } - - private static void expandTreeNodesRecursive(JTree tree, TreePath parent, boolean expand) { - TreeModel model = tree.getModel(); - - Object node = parent.getLastPathComponent(); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - TreePath path = parent.pathByAddingChild(child); - expandTreeNodesRecursive(tree, path, expand); - } - - if (expand) { - tree.expandPath(parent); - } else { - tree.collapsePath(parent); - } - } - - public static void addEditorAction(JEditorPane editor, AbstractAction a, String key, String name, String keyStroke) { - KeyStroke ks = KeyStroke.getKeyStroke(keyStroke); - a.putValue(Action.ACCELERATOR_KEY, ks); - a.putValue(Action.NAME, name); - - String actionName = key; - ActionMap amap = editor.getActionMap(); - InputMap imap = editor.getInputMap(JTextComponent.WHEN_FOCUSED); - imap.put(ks, actionName); - amap.put(actionName, a); - - JPopupMenu pmenu = editor.getComponentPopupMenu(); - JMenuItem findUsagesMenu = new JMenuItem(a); - pmenu.add(findUsagesMenu); - } - - public static boolean navigateUrl(String url) { - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - if (desktop.isSupported(Desktop.Action.BROWSE)) { - try { - URI uri = new URI(url); - desktop.browse(uri); - return true; - } catch (URISyntaxException | IOException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - return false; - } - - public static JTable autoResizeColWidth(final JTable table, final TableModel model) { - View.execInEventDispatch(() -> { - table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - table.setModel(model); - - int margin = 5; - - for (int i = 0; i < table.getColumnCount(); i++) { - int vColIndex = i; - DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); - TableColumn col = colModel.getColumn(vColIndex); - int width; - - // Get width of column header - TableCellRenderer renderer = col.getHeaderRenderer(); - - if (renderer == null) { - renderer = table.getTableHeader().getDefaultRenderer(); - } - - Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); - - width = comp.getPreferredSize().width; - - // Get maximum width of column data - for (int r = 0; r < table.getRowCount(); r++) { - renderer = table.getCellRenderer(r, vColIndex); - comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, - r, vColIndex); - width = Math.max(width, comp.getPreferredSize().width); - } - - // Add margin - width += 2 * margin; - - // Set the width - col.setPreferredWidth(width); - } - - ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( - SwingConstants.LEFT); - - // table.setAutoCreateRowSorter(true); - table.getTableHeader().setReorderingAllowed(false); - }); - - return table; - } -} +/* + * Copyright (C) 2010-2015 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.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.TexturePaint; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JRootPane; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.basic.BasicColorChooserUI; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; +import javax.swing.text.JTextComponent; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon; +import org.pushingpixels.substance.api.ColorSchemeAssociationKind; +import org.pushingpixels.substance.api.ComponentState; +import org.pushingpixels.substance.api.DecorationAreaType; +import org.pushingpixels.substance.api.SubstanceColorScheme; +import org.pushingpixels.substance.api.SubstanceConstants; +import org.pushingpixels.substance.api.SubstanceLookAndFeel; +import org.pushingpixels.substance.api.fonts.FontPolicy; +import org.pushingpixels.substance.api.fonts.FontSet; +import org.pushingpixels.substance.api.skin.SubstanceOfficeBlue2007LookAndFeel; +import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; + +/** + * Contains methods for GUI + * + * @author JPEXS + */ +public class View { + + public static Color getDefaultBackgroundColor() { + return SubstanceLookAndFeel.getCurrentSkin().getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); + } + + private static Color swfBackgroundColor = null; + + public static void setSwfBackgroundColor(Color swfBackgroundColor) { + View.swfBackgroundColor = swfBackgroundColor; + } + + public static Color getSwfBackgroundColor() { + if (swfBackgroundColor == null) { + return getDefaultBackgroundColor(); + } + return swfBackgroundColor; + } + + private static final BufferedImage transparentTexture; + + public static final TexturePaint transparentPaint; + + private static final Color transparentColor1 = new Color(0x99, 0x99, 0x99); + + private static final Color transparentColor2 = new Color(0x66, 0x66, 0x66); + + static { + transparentTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + Graphics g = transparentTexture.getGraphics(); + g.setColor(transparentColor1); + g.fillRect(0, 0, 16, 16); + g.setColor(transparentColor2); + g.fillRect(0, 0, 8, 8); + g.fillRect(8, 8, 8, 8); + transparentPaint = new TexturePaint(View.transparentTexture, new Rectangle(0, 0, transparentTexture.getWidth(), transparentTexture.getHeight())); + } + + /** + * Sets windows Look and Feel + */ + public static void setLookAndFeel() { + + //Save default font for Chinese characters + final Font defaultFont = (new JLabel()).getFont(); + try { + + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { + } + + execInEventDispatch(() -> { + try { + UIManager.setLookAndFeel(new SubstanceOfficeBlue2007LookAndFeel()); + SubstanceLookAndFeel.setSkin(Configuration.guiSkin.get()); + UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, 0.999);//This works for not changing labels color and not changing Dialogs title + UIManager.put("Tree.expandedIcon", getIcon("expand16")); + UIManager.put("Tree.collapsedIcon", getIcon("collapse16")); + UIManager.put("ColorChooserUI", BasicColorChooserUI.class.getName()); + UIManager.put("ColorChooser.swatchesRecentSwatchSize", new Dimension(20, 20)); + UIManager.put("ColorChooser.swatchesSwatchSize", new Dimension(20, 20)); + UIManager.put("RibbonApplicationMenuPopupPanelUI", MyRibbonApplicationMenuPopupPanelUI.class.getName()); + UIManager.put("RibbonApplicationMenuButtonUI", MyRibbonApplicationMenuButtonUI.class.getName()); + UIManager.put("ProgressBarUI", MyProgressBarUI.class.getName()); + UIManager.put("TextField.background", Color.white); + UIManager.put("FormattedTextField.background", Color.white); + UIManager.put("CommandButtonUI", MyCommandButtonUI.class.getName()); + + FontPolicy pol = SubstanceLookAndFeel.getFontPolicy(); + final FontSet fs = pol.getFontSet("Substance", null); + + //Restore default font for chinese characters + SubstanceLookAndFeel.setFontPolicy(new FontPolicy() { + + private final FontSet fontSet = new FontSet() { + + private FontUIResource controlFont; + + private FontUIResource menuFont; + + private FontUIResource titleFont; + + private FontUIResource windowTitleFont; + + private FontUIResource smallFont; + + private FontUIResource messageFont; + + @Override + public FontUIResource getControlFont() { + if (controlFont == null) { + FontUIResource f = fs.getControlFont(); + controlFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return controlFont; + } + + @Override + public FontUIResource getMenuFont() { + if (menuFont == null) { + FontUIResource f = fs.getMenuFont(); + menuFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return menuFont; + } + + @Override + public FontUIResource getTitleFont() { + if (titleFont == null) { + FontUIResource f = fs.getTitleFont(); + titleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return titleFont; + } + + @Override + public FontUIResource getWindowTitleFont() { + if (windowTitleFont == null) { + FontUIResource f = fs.getWindowTitleFont(); + windowTitleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return windowTitleFont; + } + + @Override + public FontUIResource getSmallFont() { + if (smallFont == null) { + FontUIResource f = fs.getSmallFont(); + smallFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return smallFont; + } + + @Override + public FontUIResource getMessageFont() { + if (messageFont == null) { + FontUIResource f = fs.getMessageFont(); + messageFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return messageFont; + } + }; + + @Override + public FontSet getFontSet(String string, UIDefaults uid) { + return fontSet; + } + }); + } catch (UnsupportedLookAndFeelException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + }); + + UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, SubstanceConstants.TabContentPaneBorderKind.SINGLE_FULL); + + JFrame.setDefaultLookAndFeelDecorated(true); + JDialog.setDefaultLookAndFeelDecorated(true); + } + + /** + * Loads image from resources + * + * @param name Name of the image + * @return loaded Image + */ + public static BufferedImage loadImage(String name) { + URL imageURL = View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + name + ".png"); + try { + return ImageIO.read(imageURL); + } catch (IOException ex) { + return null; + } + } + + /** + * Sets icon of specified frame to ASDec icon + * + * @param f Frame to set icon in + */ + public static void setWindowIcon(Window f) { + List images = new ArrayList<>(); + MyResizableIcon[] icons = MyRibbonApplicationMenuButtonUI.getIcons(); + MyResizableIcon icon = icons[1]; + int sizes[] = new int[]{16, 32, 48, 256}; + for (int size : sizes) { + icon.setIconSize(size, size); + BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR); + icon.paintIcon(f, bi.getGraphics(), 0, 0); + images.add(bi); + } + f.setIconImages(images); + } + + /** + * Centers specified frame on the screen + * + * @param f Frame to center on the screen + */ + public static void centerScreen(Window f) { + centerScreen(f, 0); // todo, set screen to the currently active screen instead of the first screen in a multi screen setup, (maybe by using the screen where the main window is now classic or ribbon?) + } + + public static void centerScreen(Window f, int screen) { + + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] allDevices = env.getScreenDevices(); + int topLeftX, topLeftY, screenX, screenY, windowPosX, windowPosY; + + if (screen < allDevices.length && screen > -1) { + topLeftX = allDevices[screen].getDefaultConfiguration().getBounds().x; + topLeftY = allDevices[screen].getDefaultConfiguration().getBounds().y; + + screenX = allDevices[screen].getDefaultConfiguration().getBounds().width; + screenY = allDevices[screen].getDefaultConfiguration().getBounds().height; + } else { + topLeftX = allDevices[0].getDefaultConfiguration().getBounds().x; + topLeftY = allDevices[0].getDefaultConfiguration().getBounds().y; + + screenX = allDevices[0].getDefaultConfiguration().getBounds().width; + screenY = allDevices[0].getDefaultConfiguration().getBounds().height; + } + + windowPosX = ((screenX - f.getWidth()) / 2) + topLeftX; + windowPosY = ((screenY - f.getHeight()) / 2) + topLeftY; + + f.setLocation(windowPosX, windowPosY); + } + + public static ImageIcon getIcon(String name) { + return new ImageIcon(View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); + } + + private static final KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + + private static final String dispatchWindowClosingActionMapKey = "com.jpexs.dispatch:WINDOW_CLOSING"; + + public static void installEscapeCloseOperation(final JDialog dialog) { + Action dispatchClosing = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent event) { + dialog.dispatchEvent(new WindowEvent( + dialog, WindowEvent.WINDOW_CLOSING)); + } + }; + JRootPane root = dialog.getRootPane(); + root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( + escapeStroke, dispatchWindowClosingActionMapKey); + root.getActionMap().put(dispatchWindowClosingActionMapKey, dispatchClosing); + } + + public static ImageWrapperResizableIcon getResizableIcon(String resource) { + return ImageWrapperResizableIcon.getIcon(View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"), new Dimension(256, 256)); + } + + public static MyResizableIcon getMyResizableIcon(String resource) { + try { + return new MyResizableIcon(ImageIO.read(View.class.getResourceAsStream("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"))); + } catch (IOException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + } + + public static void execInEventDispatch(Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + try { + SwingUtilities.invokeAndWait(r); + } catch (InterruptedException ex) { + } catch (InvocationTargetException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + public static void execInEventDispatchLater(Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + SwingUtilities.invokeLater(r); + } + } + + public static int showOptionDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageType, final Icon icon, final Object[] options, final Object initialValue) { + final int[] ret = new int[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue); + }); + return ret[0]; + } + + public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType) { + return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE); + } + + public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageTyp) { + final int ret[] = new int[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageTyp); + }); + return ret[0]; + } + + public static int showConfirmDialog(Component parentComponent, String message, String title, int optionType, ConfigurationItem showAgainConfig, int defaultOption) { + return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE, showAgainConfig, defaultOption); + } + + public static int showConfirmDialog(final Component parentComponent, String message, final String title, final int optionType, final int messageTyp, ConfigurationItem showAgainConfig, int defaultOption) { + + JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); + final JPanel warPanel = new JPanel(new BorderLayout()); + warPanel.add(warLabel, BorderLayout.CENTER); + JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); + donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); + warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); + + if (donotShowAgainCheckBox.isSelected()) { + return defaultOption; + } + + final int ret[] = new int[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showConfirmDialog(parentComponent, warPanel, title, optionType, messageTyp); + }); + showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); + return ret[0]; + } + + public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType) { + showMessageDialog(parentComponent, message, title, messageType, null); + } + + public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType, ConfigurationItem showAgainConfig) { + + execInEventDispatch(() -> { + Object msg = message; + JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); + if (showAgainConfig != null) { + JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); + final JPanel warPanel = new JPanel(new BorderLayout()); + warPanel.add(warLabel, BorderLayout.CENTER); + donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); + warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); + msg = warPanel; + if (donotShowAgainCheckBox.isSelected()) { + return; + } + } + final Object fmsg = msg; + + JOptionPane.showMessageDialog(parentComponent, fmsg, title, messageType); + if (showAgainConfig != null) { + showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); + } + }); + } + + public static void showMessageDialog(final Component parentComponent, final Object message) { + execInEventDispatch(() -> { + JOptionPane.showMessageDialog(parentComponent, message); + }); + } + + public static String showInputDialog(final Object message, final Object initialSelection) { + final String[] ret = new String[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showInputDialog(message, initialSelection); + }); + return ret[0]; + } + + public static SubstanceColorScheme getColorScheme() { + return SubstanceColorSchemeUtilities.getActiveColorScheme(new JButton(), ComponentState.ENABLED); + } + + public static void refreshTree(JTree tree, TreeModel model) { + List> expandedNodes = getExpandedNodes(tree); + tree.setModel(model); + expandTreeNodes(tree, expandedNodes); + } + + public static List> getExpandedNodes(JTree tree) { + List> expandedNodes = new ArrayList<>(); + int rowCount = tree.getRowCount(); + for (int i = 0; i < rowCount; i++) { + try { + TreePath path = tree.getPathForRow(i); + if (tree.isExpanded(path)) { + List pathAsStringList = new ArrayList<>(); + for (Object pathCompnent : path.getPath()) { + pathAsStringList.add(pathCompnent.toString()); + } + expandedNodes.add(pathAsStringList); + } + } catch (IndexOutOfBoundsException | NullPointerException ex) { + // TreeNode was removed, ignore + } + } + return expandedNodes; + } + + public static void expandTreeNodes(JTree tree, List> pathsToExpand) { + for (List pathAsStringList : pathsToExpand) { + expandTreeNode(tree, pathAsStringList); + } + } + + private static TreePath expandTreeNode(JTree tree, List pathAsStringList) { + TreePath tp = getTreePathByPathStrings(tree, pathAsStringList); + tree.expandPath(tp); + return tp; + } + + public static TreePath getTreePathByPathStrings(JTree tree, List pathAsStringList) { + TreeModel model = tree.getModel(); + if (model == null) { + return null; + } + + Object node = model.getRoot(); + + if (pathAsStringList.isEmpty()) { + return null; + } + if (!pathAsStringList.get(0).equals(node.toString())) { + return null; + } + + List path = new ArrayList<>(); + path.add(node); + + for (int i = 1; i < pathAsStringList.size(); i++) { + String name = pathAsStringList.get(i); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + if (child.toString().equals(name)) { + node = child; + path.add(node); + break; + } + } + } + + TreePath tp = new TreePath(path.toArray(new Object[path.size()])); + return tp; + } + + public static void expandTreeNodes(JTree tree, TreePath parent, boolean expand) { + expandTreeNodesRecursive(tree, parent, expand); + } + + private static void expandTreeNodesRecursive(JTree tree, TreePath parent, boolean expand) { + TreeModel model = tree.getModel(); + + Object node = parent.getLastPathComponent(); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + TreePath path = parent.pathByAddingChild(child); + expandTreeNodesRecursive(tree, path, expand); + } + + if (expand) { + tree.expandPath(parent); + } else { + tree.collapsePath(parent); + } + } + + public static void addEditorAction(JEditorPane editor, AbstractAction a, String key, String name, String keyStroke) { + KeyStroke ks = KeyStroke.getKeyStroke(keyStroke); + a.putValue(Action.ACCELERATOR_KEY, ks); + a.putValue(Action.NAME, name); + + String actionName = key; + ActionMap amap = editor.getActionMap(); + InputMap imap = editor.getInputMap(JTextComponent.WHEN_FOCUSED); + imap.put(ks, actionName); + amap.put(actionName, a); + + JPopupMenu pmenu = editor.getComponentPopupMenu(); + JMenuItem findUsagesMenu = new JMenuItem(a); + pmenu.add(findUsagesMenu); + } + + public static boolean navigateUrl(String url) { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.BROWSE)) { + try { + URI uri = new URI(url); + desktop.browse(uri); + return true; + } catch (URISyntaxException | IOException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + return false; + } + + public static JTable autoResizeColWidth(final JTable table, final TableModel model) { + View.execInEventDispatch(() -> { + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + table.setModel(model); + + int margin = 5; + + for (int i = 0; i < table.getColumnCount(); i++) { + int vColIndex = i; + DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); + TableColumn col = colModel.getColumn(vColIndex); + int width; + + // Get width of column header + TableCellRenderer renderer = col.getHeaderRenderer(); + + if (renderer == null) { + renderer = table.getTableHeader().getDefaultRenderer(); + } + + Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); + + width = comp.getPreferredSize().width; + + // Get maximum width of column data + for (int r = 0; r < table.getRowCount(); r++) { + renderer = table.getCellRenderer(r, vColIndex); + comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, + r, vColIndex); + width = Math.max(width, comp.getPreferredSize().width); + } + + // Add margin + width += 2 * margin; + + // Set the width + col.setPreferredWidth(width); + } + + ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( + SwingConstants.LEFT); + + // table.setAutoCreateRowSorter(true); + table.getTableHeader().setReorderingAllowed(false); + }); + + return table; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index e8de4e8fe..da24a8a63 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -1,886 +1,893 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.TraitMultinameUsage; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.AppDialog; -import com.jpexs.decompiler.flash.gui.AppStrings; -import com.jpexs.decompiler.flash.gui.HeaderLabel; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.MainPanel; -import com.jpexs.decompiler.flash.gui.SearchListener; -import com.jpexs.decompiler.flash.gui.SearchPanel; -import com.jpexs.decompiler.flash.gui.SearchResultsDialog; -import com.jpexs.decompiler.flash.gui.TagEditorPanel; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.DecimalTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.DoubleTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.IntTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.MultinameTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceSetTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.StringTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.helpers.Freed; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import java.awt.BorderLayout; -import java.awt.Cursor; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import javax.swing.AbstractAction; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTable; -import javax.swing.JToggleButton; -import javax.swing.SwingConstants; -import javax.swing.border.BevelBorder; -import javax.swing.table.DefaultTableModel; -import javax.swing.text.Highlighter; -import javax.swing.tree.TreePath; -import jsyntaxpane.SyntaxDocument; -import jsyntaxpane.Token; -import jsyntaxpane.TokenType; - -public class ABCPanel extends JPanel implements ItemListener, SearchListener, Freed, TagEditorPanel { - - private MainPanel mainPanel; - - public TraitsList navigator; - - public ABC abc; - - public JComboBox abcComboBox; - - public DecompiledEditorPane decompiledTextArea; - - public JScrollPane decompiledScrollPane; - - public JPersistentSplitPane splitPane; - - private JTable constantTable; - - public JComboBox constantTypeList; - - public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); - - public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); - - public DetailPanel detailPanel; - - public JPanel navPanel; - - public JTabbedPane tabbedPane; - - public SearchPanel searchPanel; - - private NewTraitDialog newTraitDialog; - - public JLabel scriptNameLabel; - - public JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); - - public JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); - - public JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); - - public JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); - - public String lastDecompiled = null; - - public MainPanel getMainPanel() { - return mainPanel; - } - - public boolean search(final String txt, boolean ignoreCase, boolean regexp) { - if ((txt != null) && (!txt.isEmpty())) { - searchPanel.setOptions(ignoreCase, regexp); - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreeItem scriptsNode = ttm.getScriptsNode(mainPanel.getCurrentSwf()); - final List found = new ArrayList<>(); - if (scriptsNode instanceof ClassesListTreeModel) { - ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; - List allpacks = clModel.getList(); - final Pattern pat = regexp - ? Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0) - : Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - int pos = 0; - for (final ScriptPack pack : allpacks) { - pos++; - String workText = AppStrings.translate("work.searching"); - String decAdd = ""; - if (!SWF.isCached(pack)) { - decAdd = ", " + AppStrings.translate("work.decompiling"); - } - - try { - CancellableWorker worker = new CancellableWorker() { - - @Override - public Void doInBackground() throws Exception { - if (pat.matcher(SWF.getCached(pack).text).find()) { - ABCPanelSearchResult searchResult = new ABCPanelSearchResult(); - searchResult.scriptPack = pack; - found.add(searchResult); - } - return null; - } - }; - worker.execute(); - Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + pack.getClassPath().toString() + "... ", worker); - worker.get(); - } catch (InterruptedException ex) { - break; - } catch (ExecutionException ex) { - Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - Main.stopWork(); - - searchPanel.setSearchText(txt); - - View.execInEventDispatch(() -> { - SearchResultsDialog sr = new SearchResultsDialog<>(ABCPanel.this.mainPanel.getMainFrame().getWindow(), txt, ABCPanel.this); - sr.setResults(found); - sr.setVisible(true); - }); - - return true; - - //return searchPanel.setResults(found); - } - return false; - } - - public void setAbc(ABC abc) { - this.abc = abc; - navigator.setAbc(abc); - updateConstList(); - } - - public void updateConstList() { - switch (constantTypeList.getSelectedIndex()) { - case 0: - View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); - break; - case 1: - View.autoResizeColWidth(constantTable, new IntTableModel(abc)); - break; - case 2: - View.autoResizeColWidth(constantTable, new DoubleTableModel(abc)); - break; - case 3: - View.autoResizeColWidth(constantTable, new DecimalTableModel(abc)); - break; - case 4: - View.autoResizeColWidth(constantTable, new StringTableModel(abc)); - break; - case 5: - View.autoResizeColWidth(constantTable, new NamespaceTableModel(abc)); - break; - case 6: - View.autoResizeColWidth(constantTable, new NamespaceSetTableModel(abc)); - break; - case 7: - View.autoResizeColWidth(constantTable, new MultinameTableModel(abc)); - break; - } - //DefaultTableColumnModel colModel = (DefaultTableColumnModel) constantTable.getColumnModel(); - //colModel.getColumn(0).setMaxWidth(50); - } - - public SWF getSwf() { - return abc == null ? null : abc.getSwf(); - } - - public List getAbcList() { - SWF swf = getSwf(); - return swf == null ? null : swf.getAbcList(); - } - - public void clearSwf() { - this.abc = null; - constantTable.setModel(new DefaultTableModel()); - navigator.clearAbc(); - decompiledTextArea.clearScript(); - } - - private boolean isFreeing; - - @Override - public boolean isFreeing() { - return isFreeing; - } - - @Override - public void free() { - isFreeing = true; - Helper.emptyObject(this); - } - - public ABCPanel(MainPanel mainPanel) { - - this.mainPanel = mainPanel; - setLayout(new BorderLayout()); - - decompiledTextArea = new DecompiledEditorPane(this); - - decompiledTextArea.setLinkHandler(new LinkHandler() { - - @Override - public boolean isLink(Token token) { - return hasDeclaration(token.length == 1 ? token.start : token.start + 1); - } - - @Override - public void handleLink(Token token) { - gotoDeclaration(token.length == 1 ? token.start : token.start + 1); - } - - @Override - public Highlighter.HighlightPainter linkPainter() { - return decompiledTextArea.linkPainter(); - } - }); - - searchPanel = new SearchPanel<>(new FlowLayout(), this); - - decompiledScrollPane = new JScrollPane(decompiledTextArea); - - JPanel iconDecPanel = new JPanel(); - iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS)); - JPanel iconsPanel = new JPanel(); - iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS)); - - JButton newTraitButton = new JButton(View.getIcon("traitadd16")); - newTraitButton.setMargin(new Insets(5, 5, 5, 5)); - newTraitButton.addActionListener(this::addTraitButtonActionPerformed); - newTraitButton.setToolTipText(AppStrings.translate("button.addtrait")); - iconsPanel.add(newTraitButton); - - scriptNameLabel = new JLabel("-"); - scriptNameLabel.setAlignmentX(0); - iconsPanel.setAlignmentX(0); - decompiledScrollPane.setAlignmentX(0); - iconDecPanel.add(scriptNameLabel); - iconDecPanel.add(iconsPanel); - iconDecPanel.add(decompiledScrollPane); - - JPanel decButtonsPan = new JPanel(new FlowLayout()); - decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); - decButtonsPan.add(editDecompiledButton); - decButtonsPan.add(experimentalLabel); - decButtonsPan.add(saveDecompiledButton); - decButtonsPan.add(cancelDecompiledButton); - - editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - - saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed); - editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed); - cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed); - - saveDecompiledButton.setVisible(false); - cancelDecompiledButton.setVisible(false); - decButtonsPan.setAlignmentX(0); - - JPanel decPanel = new JPanel(new BorderLayout()); - decPanel.add(searchPanel, BorderLayout.NORTH); - decPanel.add(iconDecPanel, BorderLayout.CENTER); - decPanel.add(decButtonsPan, BorderLayout.SOUTH); - detailPanel = new DetailPanel(this); - JPanel panB = new JPanel(); - panB.setLayout(new BorderLayout()); - panB.add(decPanel, BorderLayout.CENTER); - panB.add(decLabel, BorderLayout.NORTH); - decLabel.setHorizontalAlignment(SwingConstants.CENTER); - //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - splitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, panB, detailPanel, Configuration.guiAvm2SplitPaneDividerLocationPercent); - splitPane.setContinuousLayout(true); - - decompiledTextArea.setContentType("text/actionscript"); - decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize())); - - View.addEditorAction(decompiledTextArea, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - int multinameIndex = decompiledTextArea.getMultinameUnderCaret(); - if (multinameIndex > -1) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, false); - usageFrame.setVisible(true); - } - } - }, "find-usages", AppStrings.translate("abc.action.find-usages"), "control U"); - - View.addEditorAction(decompiledTextArea, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - gotoDeclaration(decompiledTextArea.getCaretPosition()); - } - }, "find-declaration", AppStrings.translate("abc.action.find-declaration"), "control B"); - - CtrlClickHandler cch = new CtrlClickHandler(); - decompiledTextArea.addKeyListener(cch); - decompiledTextArea.addMouseListener(cch); - decompiledTextArea.addMouseMotionListener(cch); - - navigator = new TraitsList(this); - - navPanel = new JPanel(new BorderLayout()); - JPanel navIconsPanel = new JPanel(); - navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS)); - final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16")); - sortButton.setMargin(new Insets(3, 3, 3, 3)); - navIconsPanel.add(sortButton); - navPanel.add(navIconsPanel, BorderLayout.SOUTH); - navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER); - sortButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - navigator.setSorted(sortButton.isSelected()); - navigator.updateUI(); - } - }); - - tabbedPane = new JTabbedPane(); - tabbedPane.addTab(AppStrings.translate("traits"), navPanel); - add(splitPane, BorderLayout.CENTER); - - JPanel panConstants = new JPanel(); - panConstants.setLayout(new BorderLayout()); - - constantTypeList = new JComboBox<>(new String[]{"UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"}); - constantTable = new JTable(); - if (abc != null) { - View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); - } - constantTable.setAutoCreateRowSorter(true); - - final ABCPanel t = this; - constantTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - if (constantTypeList.getSelectedIndex() == 7) { //MULTINAME - int rowIndex = constantTable.getSelectedRow(); - if (rowIndex == -1) { - return; - } - int multinameIndex = constantTable.convertRowIndexToModel(rowIndex); - if (multinameIndex > 0) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, t, false); - usageFrame.setVisible(true); - } - } - } - } - }); - constantTypeList.addItemListener(this); - panConstants.add(constantTypeList, BorderLayout.NORTH); - panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER); - tabbedPane.addTab(AppStrings.translate("constants"), panConstants); - } - - private boolean hasDeclaration(int pos) { - - SyntaxDocument sd = (SyntaxDocument) decompiledTextArea.getDocument(); - Token t = sd.getTokenAt(pos); - if (t == null || (t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD && t.type != TokenType.REGEX)) { - return false; - } - Reference abcIndex = new Reference<>(0); - Reference classIndex = new Reference<>(0); - Reference traitIndex = new Reference<>(0); - Reference multinameIndexRef = new Reference<>(0); - Reference classTrait = new Reference<>(false); - - if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { - return true; - } - int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); - if (multinameIndex > -1) { - if (multinameIndex == 0) { - return false; - } - List usages = abc.findMultinameDefinition(multinameIndex); - - Multiname m = abc.constants.constant_multiname.get(multinameIndex); - //search other ABC tags if this is not private multiname - if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { - for (ABCContainerTag at : getAbcList()) { - ABC a = at.getABC(); - if (a == abc) { - continue; - } - int mid = a.constants.getMultinameId(m, false); - if (mid > 0) { - usages.addAll(a.findMultinameDefinition(mid)); - } - } - } - - //more than one? display list - if (!usages.isEmpty()) { - return true; - } - } - - return decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")) != -1; - } - - private void gotoDeclaration(int pos) { - Reference abcIndex = new Reference<>(0); - Reference classIndex = new Reference<>(0); - Reference traitIndex = new Reference<>(0); - Reference classTrait = new Reference<>(false); - Reference multinameIndexRef = new Reference<>(0); - - if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { - UsageFrame.gotoUsage(ABCPanel.this, new TraitMultinameUsage(getAbcList().get(abcIndex.getVal()).getABC(), multinameIndexRef.getVal(), classIndex.getVal(), traitIndex.getVal(), classTrait.getVal(), null, -1) { - }); - return; - } - int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); - if (multinameIndex > -1) { - List usages = abc.findMultinameDefinition(multinameIndex); - - Multiname m = abc.constants.constant_multiname.get(multinameIndex); - //search other ABC tags if this is not private multiname - if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { - for (ABCContainerTag at : getAbcList()) { - ABC a = at.getABC(); - if (a == abc) { - continue; - } - int mid = a.constants.getMultinameId(m, false); - if (mid > 0) { - usages.addAll(a.findMultinameDefinition(mid)); - } - } - } - - //more than one? display list - if (usages.size() > 1) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, true); - usageFrame.setVisible(true); - return; - } else if (!usages.isEmpty()) { //one - UsageFrame.gotoUsage(ABCPanel.this, usages.get(0)); - return; - } - } - - int dpos = decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")); - if (dpos > -1) { - decompiledTextArea.setCaretPosition(dpos); - } - - } - - private class CtrlClickHandler extends KeyAdapter implements MouseListener, MouseMotionListener { - - private boolean ctrlDown = false; - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == 17 && !decompiledTextArea.isEditable()) { - ctrlDown = true; - //decompiledTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - } - - @Override - public void keyReleased(KeyEvent e) { - if (e.getKeyCode() == 17) { - ctrlDown = false; - //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - } - - @Override - public void mouseClicked(MouseEvent e) { - if (ctrlDown && e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1 && !decompiledTextArea.isEditable()) { - ctrlDown = false; - //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - //gotoDeclaration(); - } - } - - @Override - public void mousePressed(MouseEvent e) { - - } - - @Override - public void mouseReleased(MouseEvent e) { - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - ctrlDown = false; - decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - - @Override - public void mouseDragged(MouseEvent e) { - } - - @Override - public void mouseMoved(MouseEvent e) { - if (ctrlDown && decompiledTextArea.isEditable()) { - ctrlDown = false; - decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - } - } - - public void reload() { - lastDecompiled = ""; - getSwf().clearScriptCache(); - decompiledTextArea.reloadClass(); - detailPanel.methodTraitPanel.methodCodePanel.clear(); - } - - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getSource() == constantTypeList) { - int index = ((JComboBox) e.getSource()).getSelectedIndex(); - if (index == -1) { - return; - } - updateConstList(); - } - } - - public void display() { - setVisible(true); - } - - public void hilightScript(SWF swf, String name) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreeItem scriptsNode = ttm.getScriptsNode(swf); - if (scriptsNode instanceof ClassesListTreeModel) { - ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; - ScriptPack pack = null; - for (ScriptPack item : clModel.getList()) { - ClassPath classPath = item.getClassPath(); - - // first check the className to avoid calling unnecessary toString - if (name.endsWith(classPath.className) && classPath.toString().equals(name)) { - pack = item; - break; - } - } - if (pack != null) { - hilightScript(pack); - } - } - } - - public void hilightScript(ScriptPack pack) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - final TreePath tp = ttm.getTreePath(pack); - View.execInEventDispatchLater(() -> { - mainPanel.tagTree.setSelectionPath(tp); - mainPanel.tagTree.scrollPathToVisible(tp); - }); - - } - - @Override - public void updateSearchPos(ABCPanelSearchResult item) { - ScriptPack pack = item.scriptPack; - setAbc(pack.abc); - decompiledTextArea.setScript(pack); - hilightScript(pack); - decompiledTextArea.setCaretPosition(0); - - View.execInEventDispatchLater(() -> { - searchPanel.showQuickFindDialog(decompiledTextArea); - }); - - } - - public boolean isDirectEditing() { - return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); - } - - public void setDecompiledEditMode(boolean val) { - if (val) { - lastDecompiled = decompiledTextArea.getText(); - decompiledTextArea.setEditable(true); - saveDecompiledButton.setVisible(true); - editDecompiledButton.setVisible(false); - experimentalLabel.setVisible(false); - cancelDecompiledButton.setVisible(true); - decompiledTextArea.getCaret().setVisible(true); - decLabel.setIcon(View.getIcon("editing16")); - detailPanel.setVisible(false); - } else { - decompiledTextArea.setText(lastDecompiled); - decompiledTextArea.setEditable(false); - saveDecompiledButton.setVisible(false); - editDecompiledButton.setVisible(true); - experimentalLabel.setVisible(true); - cancelDecompiledButton.setVisible(false); - decompiledTextArea.getCaret().setVisible(true); - decLabel.setIcon(null); - detailPanel.setVisible(true); - } - - decompiledTextArea.ignoreCarret = val; - decompiledTextArea.requestFocusInWindow(); - } - - private void editDecompiledButtonActionPerformed(ActionEvent evt) { - File swc = Configuration.getPlayerSWC(); - final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html"; - if (swc == null) { - if (View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.needed").replace("%adobehomepage%", adobePage), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION) { - - View.navigateUrl(adobePage); - - int ret; - do { - ret = View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.place").replace("%libpath%", Configuration.getFlashLibPath().getAbsolutePath()), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); - swc = Configuration.getPlayerSWC(); - } while (ret == JOptionPane.OK_OPTION && swc == null); - } - } - if (swc != null) { - if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS3Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { - setDecompiledEditMode(true); - } - } - } - - private void cancelDecompiledButtonActionPerformed(ActionEvent evt) { - setDecompiledEditMode(false); - } - - private void saveDecompiledButtonActionPerformed(ActionEvent evt) { - ScriptPack pack = decompiledTextArea.getScriptLeaf(); - int oldIndex = pack.scriptIndex; - SWF.uncache(pack); - - try { - String oldSp = null; - List packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null); - if (!packs.isEmpty()) { - oldSp = packs.get(0).getClassPath().toString(); - } - - String as = decompiledTextArea.getText(); - abc.replaceScriptPack(pack, as); - lastDecompiled = as; - mainPanel.updateClassesList(); - - if (oldSp != null) { - hilightScript(getSwf(), oldSp); - } - setDecompiledEditMode(false); - reload(); - View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); - } catch (AVM2ParseException ex) { - abc.script_info.get(oldIndex).delete(abc, false); - decompiledTextArea.gotoLine((int) ex.line); - decompiledTextArea.markError(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } catch (CompilationException ex) { - abc.script_info.get(oldIndex).delete(abc, false); - decompiledTextArea.gotoLine((int) ex.line); - decompiledTextArea.markError(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } catch (IOException | InterruptedException ex) { - //ignore - } - } - - private void addTraitButtonActionPerformed(ActionEvent evt) { - int class_index = decompiledTextArea.getClassIndex(); - if (class_index < 0) { - return; - } - if (newTraitDialog == null) { - newTraitDialog = new NewTraitDialog(); - } - int void_type = abc.constants.getPublicQnameId("void", true);//abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); - int int_type = abc.constants.getPublicQnameId("int", true); //abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); - - Trait t = null; - int kind; - int nskind; - String name = null; - boolean isStatic; - Multiname m; - - boolean again = false; - loopm: - do { - if (again) { - View.showMessageDialog(null, AppStrings.translate("error.trait.exists").replace("%name%", name), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - again = false; - if (newTraitDialog.showDialog() != AppDialog.OK_OPTION) { - return; - } - kind = newTraitDialog.getTraitType(); - nskind = newTraitDialog.getNamespaceKind(); - name = newTraitDialog.getTraitName(); - isStatic = newTraitDialog.getStatic(); - m = new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(nskind, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList<>()); - int mid = abc.constants.getMultinameId(m); - if (mid == 0) { - break; - } - for (Trait tr : abc.class_info.get(class_index).static_traits.traits) { - if (tr.name_index == mid) { - again = true; - break; - } - } - - for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) { - if (tr.name_index == mid) { - again = true; - break; - } - } - } while (again); - switch (kind) { - case Trait.TRAIT_GETTER: - case Trait.TRAIT_SETTER: - case Trait.TRAIT_METHOD: - TraitMethodGetterSetter tm = new TraitMethodGetterSetter(); - MethodInfo mi = new MethodInfo(new int[0], void_type, abc.constants.getStringId(name, true), 0, new ValueKind[0], new int[0]); - int method_info = abc.addMethodInfo(mi); - tm.method_info = method_info; - MethodBody body = new MethodBody(); - body.method_info = method_info; - body.init_scope_depth = 1; - body.max_regs = 1; - body.max_scope_depth = 1; - body.max_stack = 1; - body.exceptions = new ABCException[0]; - AVM2Code code = new AVM2Code(); - code.code.add(new AVM2Instruction(0, new GetLocal0Ins(), new int[0])); - code.code.add(new AVM2Instruction(0, new PushScopeIns(), new int[0])); - code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[0])); - body.setCode(code); - Traits traits = new Traits(); - traits.traits = new ArrayList<>(); - body.traits = traits; - abc.addMethodBody(body); - mi.setBody(body); - t = tm; - break; - case Trait.TRAIT_SLOT: - case Trait.TRAIT_CONST: - TraitSlotConst ts = new TraitSlotConst(); - ts.type_index = int_type; - ts.value_kind = ValueKind.CONSTANT_Int; - ts.value_index = abc.constants.getIntId(0, true); - t = ts; - break; - } - if (t != null) { - t.kindType = kind; - t.name_index = abc.constants.getMultinameId(m, true); - int traitId; - if (isStatic) { - traitId = abc.class_info.get(class_index).static_traits.addTrait(t); - } else { - traitId = abc.class_info.get(class_index).static_traits.traits.size() + abc.instance_info.get(class_index).instance_traits.addTrait(t); - } - reload(); - decompiledTextArea.gotoTrait(traitId); - } - } - - @Override - public boolean tryAutoSave() { - // todo: implement - return false; - } - - @Override - public boolean isEditing() { - return detailPanel.isEditing() - || (saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled()); - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.abc; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.TraitMultinameUsage; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.AppDialog; +import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.HeaderLabel; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.MainPanel; +import com.jpexs.decompiler.flash.gui.SearchListener; +import com.jpexs.decompiler.flash.gui.SearchPanel; +import com.jpexs.decompiler.flash.gui.SearchResultsDialog; +import com.jpexs.decompiler.flash.gui.TagEditorPanel; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.DecimalTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.DoubleTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.IntTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.MultinameTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceSetTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.StringTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.editor.LinkHandler; +import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; +import com.jpexs.decompiler.flash.helpers.Freed; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import javax.swing.AbstractAction; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JToggleButton; +import javax.swing.SwingConstants; +import javax.swing.border.BevelBorder; +import javax.swing.table.DefaultTableModel; +import javax.swing.text.Highlighter; +import javax.swing.tree.TreePath; +import jsyntaxpane.SyntaxDocument; +import jsyntaxpane.Token; +import jsyntaxpane.TokenType; + +public class ABCPanel extends JPanel implements ItemListener, SearchListener, Freed, TagEditorPanel { + + private MainPanel mainPanel; + + public TraitsList navigator; + + public ABC abc; + + public JComboBox abcComboBox; + + public DecompiledEditorPane decompiledTextArea; + + public JScrollPane decompiledScrollPane; + + public JPersistentSplitPane splitPane; + + private JTable constantTable; + + public JComboBox constantTypeList; + + public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); + + public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); + + public DetailPanel detailPanel; + + public JPanel navPanel; + + public JTabbedPane tabbedPane; + + public SearchPanel searchPanel; + + private NewTraitDialog newTraitDialog; + + public JLabel scriptNameLabel; + + private JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); + + private JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); + + private JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); + + private JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); + + private String lastDecompiled = null; + + public MainPanel getMainPanel() { + return mainPanel; + } + + public boolean search(final String txt, boolean ignoreCase, boolean regexp) { + if ((txt != null) && (!txt.isEmpty())) { + searchPanel.setOptions(ignoreCase, regexp); + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreeItem scriptsNode = ttm.getScriptsNode(mainPanel.getCurrentSwf()); + final List found = new ArrayList<>(); + if (scriptsNode instanceof ClassesListTreeModel) { + ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; + List allpacks = clModel.getList(); + final Pattern pat = regexp + ? Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0) + : Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + int pos = 0; + for (final ScriptPack pack : allpacks) { + pos++; + String workText = AppStrings.translate("work.searching"); + String decAdd = ""; + if (!SWF.isCached(pack)) { + decAdd = ", " + AppStrings.translate("work.decompiling"); + } + + try { + CancellableWorker worker = new CancellableWorker() { + + @Override + public Void doInBackground() throws Exception { + if (pat.matcher(SWF.getCached(pack).text).find()) { + ABCPanelSearchResult searchResult = new ABCPanelSearchResult(); + searchResult.scriptPack = pack; + found.add(searchResult); + } + return null; + } + }; + worker.execute(); + Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + pack.getClassPath().toString() + "... ", worker); + worker.get(); + } catch (InterruptedException ex) { + break; + } catch (ExecutionException ex) { + Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + Main.stopWork(); + + searchPanel.setSearchText(txt); + + View.execInEventDispatch(() -> { + SearchResultsDialog sr = new SearchResultsDialog<>(ABCPanel.this.mainPanel.getMainFrame().getWindow(), txt, ABCPanel.this); + sr.setResults(found); + sr.setVisible(true); + }); + + return true; + + //return searchPanel.setResults(found); + } + return false; + } + + public void setAbc(ABC abc) { + this.abc = abc; + navigator.setAbc(abc); + updateConstList(); + } + + public void updateConstList() { + switch (constantTypeList.getSelectedIndex()) { + case 0: + View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); + break; + case 1: + View.autoResizeColWidth(constantTable, new IntTableModel(abc)); + break; + case 2: + View.autoResizeColWidth(constantTable, new DoubleTableModel(abc)); + break; + case 3: + View.autoResizeColWidth(constantTable, new DecimalTableModel(abc)); + break; + case 4: + View.autoResizeColWidth(constantTable, new StringTableModel(abc)); + break; + case 5: + View.autoResizeColWidth(constantTable, new NamespaceTableModel(abc)); + break; + case 6: + View.autoResizeColWidth(constantTable, new NamespaceSetTableModel(abc)); + break; + case 7: + View.autoResizeColWidth(constantTable, new MultinameTableModel(abc)); + break; + } + //DefaultTableColumnModel colModel = (DefaultTableColumnModel) constantTable.getColumnModel(); + //colModel.getColumn(0).setMaxWidth(50); + } + + public SWF getSwf() { + return abc == null ? null : abc.getSwf(); + } + + public List getAbcList() { + SWF swf = getSwf(); + return swf == null ? null : swf.getAbcList(); + } + + public void clearSwf() { + this.abc = null; + constantTable.setModel(new DefaultTableModel()); + navigator.clearAbc(); + decompiledTextArea.clearScript(); + } + + private boolean isFreeing; + + @Override + public boolean isFreeing() { + return isFreeing; + } + + @Override + public void free() { + isFreeing = true; + Helper.emptyObject(this); + } + + public ABCPanel(MainPanel mainPanel) { + + this.mainPanel = mainPanel; + setLayout(new BorderLayout()); + + decompiledTextArea = new DecompiledEditorPane(this); + decompiledTextArea.addTextChangedListener(this::decompiledTextAreaTextChanged); + + decompiledTextArea.setLinkHandler(new LinkHandler() { + + @Override + public boolean isLink(Token token) { + return hasDeclaration(token.length == 1 ? token.start : token.start + 1); + } + + @Override + public void handleLink(Token token) { + gotoDeclaration(token.length == 1 ? token.start : token.start + 1); + } + + @Override + public Highlighter.HighlightPainter linkPainter() { + return decompiledTextArea.linkPainter(); + } + }); + + searchPanel = new SearchPanel<>(new FlowLayout(), this); + + decompiledScrollPane = new JScrollPane(decompiledTextArea); + + JPanel iconDecPanel = new JPanel(); + iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS)); + JPanel iconsPanel = new JPanel(); + iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS)); + + JButton newTraitButton = new JButton(View.getIcon("traitadd16")); + newTraitButton.setMargin(new Insets(5, 5, 5, 5)); + newTraitButton.addActionListener(this::addTraitButtonActionPerformed); + newTraitButton.setToolTipText(AppStrings.translate("button.addtrait")); + iconsPanel.add(newTraitButton); + + scriptNameLabel = new JLabel("-"); + scriptNameLabel.setAlignmentX(0); + iconsPanel.setAlignmentX(0); + decompiledScrollPane.setAlignmentX(0); + iconDecPanel.add(scriptNameLabel); + iconDecPanel.add(iconsPanel); + iconDecPanel.add(decompiledScrollPane); + + JPanel decButtonsPan = new JPanel(new FlowLayout()); + decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); + decButtonsPan.add(editDecompiledButton); + decButtonsPan.add(experimentalLabel); + decButtonsPan.add(saveDecompiledButton); + decButtonsPan.add(cancelDecompiledButton); + + editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + + saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed); + editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed); + cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed); + + saveDecompiledButton.setVisible(false); + cancelDecompiledButton.setVisible(false); + decButtonsPan.setAlignmentX(0); + + JPanel decPanel = new JPanel(new BorderLayout()); + decPanel.add(searchPanel, BorderLayout.NORTH); + decPanel.add(iconDecPanel, BorderLayout.CENTER); + decPanel.add(decButtonsPan, BorderLayout.SOUTH); + detailPanel = new DetailPanel(this); + JPanel panB = new JPanel(); + panB.setLayout(new BorderLayout()); + panB.add(decPanel, BorderLayout.CENTER); + panB.add(decLabel, BorderLayout.NORTH); + decLabel.setHorizontalAlignment(SwingConstants.CENTER); + //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + splitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, panB, detailPanel, Configuration.guiAvm2SplitPaneDividerLocationPercent); + splitPane.setContinuousLayout(true); + + decompiledTextArea.changeContentType("text/actionscript"); + decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize())); + + View.addEditorAction(decompiledTextArea, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int multinameIndex = decompiledTextArea.getMultinameUnderCaret(); + if (multinameIndex > -1) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, false); + usageFrame.setVisible(true); + } + } + }, "find-usages", AppStrings.translate("abc.action.find-usages"), "control U"); + + View.addEditorAction(decompiledTextArea, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + gotoDeclaration(decompiledTextArea.getCaretPosition()); + } + }, "find-declaration", AppStrings.translate("abc.action.find-declaration"), "control B"); + + CtrlClickHandler cch = new CtrlClickHandler(); + decompiledTextArea.addKeyListener(cch); + decompiledTextArea.addMouseListener(cch); + decompiledTextArea.addMouseMotionListener(cch); + + navigator = new TraitsList(this); + + navPanel = new JPanel(new BorderLayout()); + JPanel navIconsPanel = new JPanel(); + navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS)); + final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16")); + sortButton.setMargin(new Insets(3, 3, 3, 3)); + navIconsPanel.add(sortButton); + navPanel.add(navIconsPanel, BorderLayout.SOUTH); + navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER); + sortButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + navigator.setSorted(sortButton.isSelected()); + navigator.updateUI(); + } + }); + + tabbedPane = new JTabbedPane(); + tabbedPane.addTab(AppStrings.translate("traits"), navPanel); + add(splitPane, BorderLayout.CENTER); + + JPanel panConstants = new JPanel(); + panConstants.setLayout(new BorderLayout()); + + constantTypeList = new JComboBox<>(new String[]{"UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"}); + constantTable = new JTable(); + if (abc != null) { + View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); + } + constantTable.setAutoCreateRowSorter(true); + + final ABCPanel t = this; + constantTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + if (constantTypeList.getSelectedIndex() == 7) { //MULTINAME + int rowIndex = constantTable.getSelectedRow(); + if (rowIndex == -1) { + return; + } + int multinameIndex = constantTable.convertRowIndexToModel(rowIndex); + if (multinameIndex > 0) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, t, false); + usageFrame.setVisible(true); + } + } + } + } + }); + constantTypeList.addItemListener(this); + panConstants.add(constantTypeList, BorderLayout.NORTH); + panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER); + tabbedPane.addTab(AppStrings.translate("constants"), panConstants); + } + + private void decompiledTextAreaTextChanged() { + setModified(true); + } + + private boolean isModified() { + return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); + } + + private void setModified(boolean value) { + saveDecompiledButton.setEnabled(value); + } + + private boolean hasDeclaration(int pos) { + + SyntaxDocument sd = (SyntaxDocument) decompiledTextArea.getDocument(); + Token t = sd.getTokenAt(pos); + if (t == null || (t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD && t.type != TokenType.REGEX)) { + return false; + } + Reference abcIndex = new Reference<>(0); + Reference classIndex = new Reference<>(0); + Reference traitIndex = new Reference<>(0); + Reference multinameIndexRef = new Reference<>(0); + Reference classTrait = new Reference<>(false); + + if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { + return true; + } + int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); + if (multinameIndex > -1) { + if (multinameIndex == 0) { + return false; + } + List usages = abc.findMultinameDefinition(multinameIndex); + + Multiname m = abc.constants.constant_multiname.get(multinameIndex); + //search other ABC tags if this is not private multiname + if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { + for (ABCContainerTag at : getAbcList()) { + ABC a = at.getABC(); + if (a == abc) { + continue; + } + int mid = a.constants.getMultinameId(m, false); + if (mid > 0) { + usages.addAll(a.findMultinameDefinition(mid)); + } + } + } + + //more than one? display list + if (!usages.isEmpty()) { + return true; + } + } + + return decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")) != -1; + } + + private void gotoDeclaration(int pos) { + Reference abcIndex = new Reference<>(0); + Reference classIndex = new Reference<>(0); + Reference traitIndex = new Reference<>(0); + Reference classTrait = new Reference<>(false); + Reference multinameIndexRef = new Reference<>(0); + + if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { + UsageFrame.gotoUsage(ABCPanel.this, new TraitMultinameUsage(getAbcList().get(abcIndex.getVal()).getABC(), multinameIndexRef.getVal(), classIndex.getVal(), traitIndex.getVal(), classTrait.getVal(), null, -1) { + }); + return; + } + int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); + if (multinameIndex > -1) { + List usages = abc.findMultinameDefinition(multinameIndex); + + Multiname m = abc.constants.constant_multiname.get(multinameIndex); + //search other ABC tags if this is not private multiname + if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { + for (ABCContainerTag at : getAbcList()) { + ABC a = at.getABC(); + if (a == abc) { + continue; + } + int mid = a.constants.getMultinameId(m, false); + if (mid > 0) { + usages.addAll(a.findMultinameDefinition(mid)); + } + } + } + + //more than one? display list + if (usages.size() > 1) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, true); + usageFrame.setVisible(true); + return; + } else if (!usages.isEmpty()) { //one + UsageFrame.gotoUsage(ABCPanel.this, usages.get(0)); + return; + } + } + + int dpos = decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")); + if (dpos > -1) { + decompiledTextArea.setCaretPosition(dpos); + } + + } + + private class CtrlClickHandler extends KeyAdapter implements MouseListener, MouseMotionListener { + + private boolean ctrlDown = false; + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == 17 && !decompiledTextArea.isEditable()) { + ctrlDown = true; + //decompiledTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == 17) { + ctrlDown = false; + //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + if (ctrlDown && e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1 && !decompiledTextArea.isEditable()) { + ctrlDown = false; + //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + //gotoDeclaration(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + ctrlDown = false; + decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + if (ctrlDown && decompiledTextArea.isEditable()) { + ctrlDown = false; + decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + } + } + + public void reload() { + lastDecompiled = ""; + getSwf().clearScriptCache(); + decompiledTextArea.reloadClass(); + detailPanel.methodTraitPanel.methodCodePanel.clear(); + } + + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getSource() == constantTypeList) { + int index = ((JComboBox) e.getSource()).getSelectedIndex(); + if (index == -1) { + return; + } + updateConstList(); + } + } + + public void display() { + setVisible(true); + } + + public void hilightScript(SWF swf, String name) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreeItem scriptsNode = ttm.getScriptsNode(swf); + if (scriptsNode instanceof ClassesListTreeModel) { + ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; + ScriptPack pack = null; + for (ScriptPack item : clModel.getList()) { + ClassPath classPath = item.getClassPath(); + + // first check the className to avoid calling unnecessary toString + if (name.endsWith(classPath.className) && classPath.toString().equals(name)) { + pack = item; + break; + } + } + if (pack != null) { + hilightScript(pack); + } + } + } + + public void hilightScript(ScriptPack pack) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + final TreePath tp = ttm.getTreePath(pack); + View.execInEventDispatchLater(() -> { + mainPanel.tagTree.setSelectionPath(tp); + mainPanel.tagTree.scrollPathToVisible(tp); + }); + + } + + @Override + public void updateSearchPos(ABCPanelSearchResult item) { + ScriptPack pack = item.scriptPack; + setAbc(pack.abc); + decompiledTextArea.setScript(pack); + hilightScript(pack); + decompiledTextArea.setCaretPosition(0); + + View.execInEventDispatchLater(() -> { + searchPanel.showQuickFindDialog(decompiledTextArea); + }); + + } + + public boolean isDirectEditing() { + return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); + } + + public void setDecompiledEditMode(boolean val) { + if (val) { + lastDecompiled = decompiledTextArea.getText(); + } else { + decompiledTextArea.setText(lastDecompiled); + } + + decompiledTextArea.setEditable(val); + saveDecompiledButton.setVisible(val); + saveDecompiledButton.setEnabled(false); + editDecompiledButton.setVisible(!val); + experimentalLabel.setVisible(!val); + cancelDecompiledButton.setVisible(val); + decompiledTextArea.getCaret().setVisible(true); + decLabel.setIcon(val ? View.getIcon("editing16") : null); + detailPanel.setVisible(!val); + + decompiledTextArea.ignoreCarret = val; + decompiledTextArea.requestFocusInWindow(); + } + + private void editDecompiledButtonActionPerformed(ActionEvent evt) { + File swc = Configuration.getPlayerSWC(); + final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html"; + if (swc == null) { + if (View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.needed").replace("%adobehomepage%", adobePage), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION) { + + View.navigateUrl(adobePage); + + int ret; + do { + ret = View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.place").replace("%libpath%", Configuration.getFlashLibPath().getAbsolutePath()), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); + swc = Configuration.getPlayerSWC(); + } while (ret == JOptionPane.OK_OPTION && swc == null); + } + } + if (swc != null) { + if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS3Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { + setDecompiledEditMode(true); + } + } + } + + private void cancelDecompiledButtonActionPerformed(ActionEvent evt) { + setDecompiledEditMode(false); + } + + private void saveDecompiledButtonActionPerformed(ActionEvent evt) { + ScriptPack pack = decompiledTextArea.getScriptLeaf(); + int oldIndex = pack.scriptIndex; + SWF.uncache(pack); + + try { + String oldSp = null; + List packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null); + if (!packs.isEmpty()) { + oldSp = packs.get(0).getClassPath().toString(); + } + + String as = decompiledTextArea.getText(); + abc.replaceScriptPack(pack, as); + lastDecompiled = as; + mainPanel.updateClassesList(); + + if (oldSp != null) { + hilightScript(getSwf(), oldSp); + } + setDecompiledEditMode(false); + reload(); + View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); + } catch (AVM2ParseException ex) { + abc.script_info.get(oldIndex).delete(abc, false); + decompiledTextArea.gotoLine((int) ex.line); + decompiledTextArea.markError(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } catch (CompilationException ex) { + abc.script_info.get(oldIndex).delete(abc, false); + decompiledTextArea.gotoLine((int) ex.line); + decompiledTextArea.markError(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } catch (IOException | InterruptedException ex) { + //ignore + } + } + + private void addTraitButtonActionPerformed(ActionEvent evt) { + int class_index = decompiledTextArea.getClassIndex(); + if (class_index < 0) { + return; + } + if (newTraitDialog == null) { + newTraitDialog = new NewTraitDialog(); + } + int void_type = abc.constants.getPublicQnameId("void", true);//abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); + int int_type = abc.constants.getPublicQnameId("int", true); //abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); + + Trait t = null; + int kind; + int nskind; + String name = null; + boolean isStatic; + Multiname m; + + boolean again = false; + loopm: + do { + if (again) { + View.showMessageDialog(null, AppStrings.translate("error.trait.exists").replace("%name%", name), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + again = false; + if (newTraitDialog.showDialog() != AppDialog.OK_OPTION) { + return; + } + kind = newTraitDialog.getTraitType(); + nskind = newTraitDialog.getNamespaceKind(); + name = newTraitDialog.getTraitName(); + isStatic = newTraitDialog.getStatic(); + m = new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(nskind, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList<>()); + int mid = abc.constants.getMultinameId(m); + if (mid == 0) { + break; + } + for (Trait tr : abc.class_info.get(class_index).static_traits.traits) { + if (tr.name_index == mid) { + again = true; + break; + } + } + + for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) { + if (tr.name_index == mid) { + again = true; + break; + } + } + } while (again); + switch (kind) { + case Trait.TRAIT_GETTER: + case Trait.TRAIT_SETTER: + case Trait.TRAIT_METHOD: + TraitMethodGetterSetter tm = new TraitMethodGetterSetter(); + MethodInfo mi = new MethodInfo(new int[0], void_type, abc.constants.getStringId(name, true), 0, new ValueKind[0], new int[0]); + int method_info = abc.addMethodInfo(mi); + tm.method_info = method_info; + MethodBody body = new MethodBody(); + body.method_info = method_info; + body.init_scope_depth = 1; + body.max_regs = 1; + body.max_scope_depth = 1; + body.max_stack = 1; + body.exceptions = new ABCException[0]; + AVM2Code code = new AVM2Code(); + code.code.add(new AVM2Instruction(0, new GetLocal0Ins(), new int[0])); + code.code.add(new AVM2Instruction(0, new PushScopeIns(), new int[0])); + code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[0])); + body.setCode(code); + Traits traits = new Traits(); + traits.traits = new ArrayList<>(); + body.traits = traits; + abc.addMethodBody(body); + mi.setBody(body); + t = tm; + break; + case Trait.TRAIT_SLOT: + case Trait.TRAIT_CONST: + TraitSlotConst ts = new TraitSlotConst(); + ts.type_index = int_type; + ts.value_kind = ValueKind.CONSTANT_Int; + ts.value_index = abc.constants.getIntId(0, true); + t = ts; + break; + } + if (t != null) { + t.kindType = kind; + t.name_index = abc.constants.getMultinameId(m, true); + int traitId; + if (isStatic) { + traitId = abc.class_info.get(class_index).static_traits.addTrait(t); + } else { + traitId = abc.class_info.get(class_index).static_traits.traits.size() + abc.instance_info.get(class_index).instance_traits.addTrait(t); + } + reload(); + decompiledTextArea.gotoTrait(traitId); + } + } + + @Override + public boolean tryAutoSave() { + // todo: implement + return false; + } + + @Override + public boolean isEditing() { + return detailPanel.isEditing() || isModified(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java index 9a59b547d..2cf4a8366 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java @@ -1,340 +1,341 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2Graph; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; -import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.MissingSymbolHandler; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.gui.GraphDialog; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.helpers.HighlightedText; -import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; -import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; -import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.helpers.Helper; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; - -public class ASMSourceEditorPane extends LineMarkedEditorPane implements CaretListener { - - public ABC abc; - - public int bodyIndex = -1; - - private int scriptIndex = -1; - - public int getScriptIndex() { - return scriptIndex; - } - - private List disassembledHilights = new ArrayList<>(); - - private List specialHilights = new ArrayList<>(); - - private final DecompiledEditorPane decompiledEditor; - - private boolean ignoreCarret = false; - - private String name; - - private HighlightedText textWithHex; - - private HighlightedText textNoHex; - - private HighlightedText textHexOnly; - - private ScriptExportMode exportMode = ScriptExportMode.PCODE; - - private Trait trait; - - public ABCPanel getAbcPanel() { - return decompiledEditor.getAbcPanel(); - } - - public ScriptExportMode getExportMode() { - return exportMode; - } - - private HighlightedText getHighlightedText(ScriptExportMode exportMode) { - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - abc.bodies.get(bodyIndex).getCode().toASMSource(abc.constants, trait, abc.method_info.get(abc.bodies.get(bodyIndex).method_info), abc.bodies.get(bodyIndex), exportMode, writer); - return new HighlightedText(writer); - } - - public void setHex(ScriptExportMode exportMode, boolean force) { - if (this.exportMode == exportMode && !force) { - return; - } - this.exportMode = exportMode; - long oldOffset = getSelectedOffset(); - if (exportMode == ScriptExportMode.PCODE) { - setContentType("text/flasm3"); - if (textNoHex == null) { - textNoHex = getHighlightedText(exportMode); - } - setText(textNoHex); - } else if (exportMode == ScriptExportMode.PCODE_HEX) { - setContentType("text/flasm3"); - if (textWithHex == null) { - textWithHex = getHighlightedText(exportMode); - } - setText(textWithHex); - } else { - setContentType("text/plain"); - if (textHexOnly == null) { - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - Helper.byteArrayToHexWithHeader(writer, abc.bodies.get(bodyIndex).getCode().getBytes()); - textHexOnly = new HighlightedText(writer); - } - setText(textHexOnly); - } - hilighOffset(oldOffset); - } - - public void setIgnoreCarret(boolean ignoreCarret) { - this.ignoreCarret = ignoreCarret; - } - - public ASMSourceEditorPane(DecompiledEditorPane decompiledEditor) { - this.decompiledEditor = decompiledEditor; - addCaretListener(this); - } - - public void hilighSpecial(HighlightSpecialType type, String specialValue) { - Highlighting h2 = null; - for (Highlighting sh : specialHilights) { - if (type.equals(sh.getProperties().subtype)) { - if (sh.getProperties().specialValue.equals(specialValue)) { - h2 = sh; - break; - } - } - } - if (h2 != null) { - ignoreCarret = true; - if (h2.startPos <= getDocument().getLength()) { - setCaretPosition(h2.startPos); - } - getCaret().setVisible(true); - ignoreCarret = false; - } - } - - public void hilighOffset(long offset) { - if (isEditable()) { - return; - } - Highlighting h2 = Highlighting.searchOffset(disassembledHilights, offset); - if (h2 != null) { - ignoreCarret = true; - if (h2.startPos <= getDocument().getLength()) { - setCaretPosition(h2.startPos); - } - getCaret().setVisible(true); - ignoreCarret = false; - } - } - - @Override - public String getName() { - return super.getName(); - } - - public void setBodyIndex(int bodyIndex, ABC abc, String name, Trait trait, int scriptIndex) { - this.bodyIndex = bodyIndex; - this.abc = abc; - this.name = name; - this.trait = trait; - this.scriptIndex = scriptIndex; - if (bodyIndex == -1) { - return; - } - textWithHex = null; - textNoHex = null; - textHexOnly = null; - setHex(exportMode, true); - } - - public void graph() { - try { - AVM2Graph gr = new AVM2Graph(abc.bodies.get(bodyIndex).getCode(), abc, abc.bodies.get(bodyIndex), false, -1, -1, new HashMap<>(), new ScopeStack(), new HashMap<>(), new ArrayList<>(), new HashMap<>(), abc.bodies.get(bodyIndex).getCode().visitCode(abc.bodies.get(bodyIndex))); - (new GraphDialog(getAbcPanel().getMainPanel().getMainFrame().getWindow(), gr, name)).setVisible(true); - } catch (InterruptedException ex) { - Logger.getLogger(ASMSourceEditorPane.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public void exec() { - HashMap args = new HashMap<>(); - args.put(0, new Object()); //object "this" - args.put(1, 466561L); //param1 - Object o = abc.bodies.get(bodyIndex).getCode().execute(args, abc.constants); - View.showMessageDialog(this, "Returned object:" + o.toString()); - } - - public boolean save() { - try { - String text = getText(); - if (text.trim().startsWith(Helper.hexData)) { - byte[] data = Helper.getBytesFromHexaText(text); - MethodBody mb = abc.bodies.get(bodyIndex); - mb.setCodeBytes(data); - } else { - AVM2Code acode = ASM3Parser.parse(new StringReader(text), abc.constants, trait, new MissingSymbolHandler() { - //no longer ask for adding new constants - @Override - public boolean missingString(String value) { - return true; - } - - @Override - public boolean missingInt(long value) { - return true; - } - - @Override - public boolean missingUInt(long value) { - return true; - } - - @Override - public boolean missingDouble(double value) { - return true; - } - }, abc.bodies.get(bodyIndex), abc.method_info.get(abc.bodies.get(bodyIndex).method_info)); - //acode.getBytes(abc.bodies.get(bodyIndex).getCodeBytes()); - abc.bodies.get(bodyIndex).setCode(acode); - } - - ((Tag) abc.parentTag).setModified(true); - abc.script_info.get(scriptIndex).setModified(true); - textWithHex = null; - textNoHex = null; - textHexOnly = null; - } catch (IOException ex) { - } catch (InterruptedException ex) { - } catch (AVM2ParseException ex) { - View.showMessageDialog(this, (ex.text + " on line " + ex.line)); - gotoLine((int) ex.line); - markError(); - return false; - } - return true; - } - - @Override - public void setText(String t) { - disassembledHilights = new ArrayList<>(); - specialHilights = new ArrayList<>(); - super.setText(t); - setCaretPosition(0); - } - - public void setText(HighlightedText HighlightedText) { - disassembledHilights = HighlightedText.instructionHilights; - specialHilights = HighlightedText.specialHilights; - super.setText(HighlightedText.text); - setCaretPosition(0); - } - - public void clear() { - setText(""); - bodyIndex = -1; - setCaretPosition(0); - } - - public void selectInstruction(int pos) { - String text = getText(); - int lineCnt = 1; - int lineStart = 0; - int lineEnd; - int instrCount = 0; - int dot = -2; - for (int i = 0; i < text.length(); i++) { - if (text.charAt(i) == '\n') { - - lineCnt++; - lineEnd = i; - String ins = text.substring(lineStart, lineEnd).trim(); - if (!((i > 0) && (text.charAt(i - 1) == ':'))) { - if (!ins.startsWith("exception ")) { - instrCount++; - } - } - if (instrCount == pos + 1) { - break; - } - lineStart = i + 1; - } - } - //if (lineCnt == -1) { - // lineEnd = text.length() - 1; - //} - //select(lineStart, lineEnd); - setCaretPosition(lineStart); - //requestFocus(); - } - - public Highlighting getSelectedSpecial() { - return Highlighting.searchPos(specialHilights, getCaretPosition()); - } - - public long getSelectedOffset() { - int pos = getCaretPosition(); - Highlighting lastH = null; - for (Highlighting h : disassembledHilights) { - if (pos < h.startPos) { - break; - } - lastH = h; - } - return lastH == null ? 0 : lastH.getProperties().offset; - } - - @Override - public void caretUpdate(CaretEvent e) { - if (isEditable()) { - return; - } - if (ignoreCarret) { - return; - } - getCaret().setVisible(true); - - decompiledEditor.hilightOffset(getSelectedOffset()); - Highlighting spec = getSelectedSpecial(); - if (spec != null) { - decompiledEditor.hilightSpecial(spec.getProperties().subtype, spec.getProperties().index); - } - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.abc; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2Graph; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; +import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.MissingSymbolHandler; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.gui.GraphDialog; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.helpers.HighlightedText; +import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; +import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.helpers.Helper; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; + +public class ASMSourceEditorPane extends LineMarkedEditorPane implements CaretListener { + + public ABC abc; + + public int bodyIndex = -1; + + private int scriptIndex = -1; + + public int getScriptIndex() { + return scriptIndex; + } + + private List disassembledHilights = new ArrayList<>(); + + private List specialHilights = new ArrayList<>(); + + private final DecompiledEditorPane decompiledEditor; + + private boolean ignoreCarret = false; + + private String name; + + private HighlightedText textWithHex; + + private HighlightedText textNoHex; + + private HighlightedText textHexOnly; + + private ScriptExportMode exportMode = ScriptExportMode.PCODE; + + private Trait trait; + + public ABCPanel getAbcPanel() { + return decompiledEditor.getAbcPanel(); + } + + public ScriptExportMode getExportMode() { + return exportMode; + } + + private HighlightedText getHighlightedText(ScriptExportMode exportMode) { + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + abc.bodies.get(bodyIndex).getCode().toASMSource(abc.constants, trait, abc.method_info.get(abc.bodies.get(bodyIndex).method_info), abc.bodies.get(bodyIndex), exportMode, writer); + return new HighlightedText(writer); + } + + public void setHex(ScriptExportMode exportMode, boolean force) { + if (this.exportMode == exportMode && !force) { + return; + } + this.exportMode = exportMode; + long oldOffset = getSelectedOffset(); + if (exportMode == ScriptExportMode.PCODE) { + changeContentType("text/flasm3"); + if (textNoHex == null) { + textNoHex = getHighlightedText(exportMode); + } + setText(textNoHex); + } else if (exportMode == ScriptExportMode.PCODE_HEX) { + changeContentType("text/flasm3"); + if (textWithHex == null) { + textWithHex = getHighlightedText(exportMode); + } + setText(textWithHex); + } else { + changeContentType("text/plain"); + if (textHexOnly == null) { + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + Helper.byteArrayToHexWithHeader(writer, abc.bodies.get(bodyIndex).getCode().getBytes()); + textHexOnly = new HighlightedText(writer); + } + setText(textHexOnly); + } + hilighOffset(oldOffset); + } + + public void setIgnoreCarret(boolean ignoreCarret) { + this.ignoreCarret = ignoreCarret; + } + + public ASMSourceEditorPane(DecompiledEditorPane decompiledEditor) { + this.decompiledEditor = decompiledEditor; + addCaretListener(this); + } + + public void hilighSpecial(HighlightSpecialType type, String specialValue) { + Highlighting h2 = null; + for (Highlighting sh : specialHilights) { + if (type.equals(sh.getProperties().subtype)) { + if (sh.getProperties().specialValue.equals(specialValue)) { + h2 = sh; + break; + } + } + } + if (h2 != null) { + ignoreCarret = true; + if (h2.startPos <= getDocument().getLength()) { + setCaretPosition(h2.startPos); + } + getCaret().setVisible(true); + ignoreCarret = false; + } + } + + public void hilighOffset(long offset) { + if (isEditable()) { + return; + } + Highlighting h2 = Highlighting.searchOffset(disassembledHilights, offset); + if (h2 != null) { + ignoreCarret = true; + if (h2.startPos <= getDocument().getLength()) { + setCaretPosition(h2.startPos); + } + getCaret().setVisible(true); + ignoreCarret = false; + } + } + + @Override + public String getName() { + return super.getName(); + } + + public void setBodyIndex(int bodyIndex, ABC abc, String name, Trait trait, int scriptIndex) { + this.bodyIndex = bodyIndex; + this.abc = abc; + this.name = name; + this.trait = trait; + this.scriptIndex = scriptIndex; + if (bodyIndex == -1) { + return; + } + textWithHex = null; + textNoHex = null; + textHexOnly = null; + setHex(exportMode, true); + } + + public void graph() { + try { + AVM2Graph gr = new AVM2Graph(abc.bodies.get(bodyIndex).getCode(), abc, abc.bodies.get(bodyIndex), false, -1, -1, new HashMap<>(), new ScopeStack(), new HashMap<>(), new ArrayList<>(), new HashMap<>(), abc.bodies.get(bodyIndex).getCode().visitCode(abc.bodies.get(bodyIndex))); + (new GraphDialog(getAbcPanel().getMainPanel().getMainFrame().getWindow(), gr, name)).setVisible(true); + } catch (InterruptedException ex) { + Logger.getLogger(ASMSourceEditorPane.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void exec() { + HashMap args = new HashMap<>(); + args.put(0, new Object()); //object "this" + args.put(1, 466561L); //param1 + Object o = abc.bodies.get(bodyIndex).getCode().execute(args, abc.constants); + View.showMessageDialog(this, "Returned object:" + o.toString()); + } + + public boolean save() { + try { + String text = getText(); + if (text.trim().startsWith(Helper.hexData)) { + byte[] data = Helper.getBytesFromHexaText(text); + MethodBody mb = abc.bodies.get(bodyIndex); + mb.setCodeBytes(data); + } else { + AVM2Code acode = ASM3Parser.parse(new StringReader(text), abc.constants, trait, new MissingSymbolHandler() { + //no longer ask for adding new constants + @Override + public boolean missingString(String value) { + return true; + } + + @Override + public boolean missingInt(long value) { + return true; + } + + @Override + public boolean missingUInt(long value) { + return true; + } + + @Override + public boolean missingDouble(double value) { + return true; + } + }, abc.bodies.get(bodyIndex), abc.method_info.get(abc.bodies.get(bodyIndex).method_info)); + //acode.getBytes(abc.bodies.get(bodyIndex).getCodeBytes()); + abc.bodies.get(bodyIndex).setCode(acode); + } + + ((Tag) abc.parentTag).setModified(true); + abc.script_info.get(scriptIndex).setModified(true); + textWithHex = null; + textNoHex = null; + textHexOnly = null; + } catch (IOException ex) { + } catch (InterruptedException ex) { + } catch (AVM2ParseException ex) { + View.showMessageDialog(this, (ex.text + " on line " + ex.line)); + gotoLine((int) ex.line); + markError(); + return false; + } + return true; + } + + @Override + public void setText(String t) { + disassembledHilights = new ArrayList<>(); + specialHilights = new ArrayList<>(); + super.setText(t); + setCaretPosition(0); + } + + public void setText(HighlightedText HighlightedText) { + disassembledHilights = HighlightedText.instructionHilights; + specialHilights = HighlightedText.specialHilights; + super.setText(HighlightedText.text); + setCaretPosition(0); + } + + public void clear() { + setText(""); + bodyIndex = -1; + setCaretPosition(0); + } + + public void selectInstruction(int pos) { + String text = getText(); + int lineCnt = 1; + int lineStart = 0; + int lineEnd; + int instrCount = 0; + int dot = -2; + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == '\n') { + + lineCnt++; + lineEnd = i; + String ins = text.substring(lineStart, lineEnd).trim(); + if (!((i > 0) && (text.charAt(i - 1) == ':'))) { + if (!ins.startsWith("exception ")) { + instrCount++; + } + } + if (instrCount == pos + 1) { + break; + } + lineStart = i + 1; + } + } + //if (lineCnt == -1) { + // lineEnd = text.length() - 1; + //} + //select(lineStart, lineEnd); + setCaretPosition(lineStart); + //requestFocus(); + } + + public Highlighting getSelectedSpecial() { + return Highlighting.searchPos(specialHilights, getCaretPosition()); + } + + public long getSelectedOffset() { + int pos = getCaretPosition(); + Highlighting lastH = null; + for (Highlighting h : disassembledHilights) { + if (pos < h.startPos) { + break; + } + lastH = h; + } + return lastH == null ? 0 : lastH.getProperties().offset; + } + + @Override + public void caretUpdate(CaretEvent e) { + if (isEditable()) { + return; + } + if (ignoreCarret) { + return; + } + getCaret().setVisible(true); + + decompiledEditor.hilightOffset(getSelectedOffset()); + Highlighting spec = getSelectedSpecial(); + if (spec != null) { + decompiledEditor.hilightSpecial(spec.getProperties().subtype, spec.getProperties().index); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java index 2027faf76..135649d7d 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java @@ -1,700 +1,701 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.CachedDecompilation; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; -import com.jpexs.decompiler.flash.abc.types.ClassInfo; -import com.jpexs.decompiler.flash.abc.types.InstanceInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.gui.AppStrings; -import com.jpexs.decompiler.flash.helpers.hilight.HighlightData; -import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; -import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import java.awt.Point; -import java.util.ArrayList; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; -import jsyntaxpane.SyntaxDocument; -import jsyntaxpane.Token; -import jsyntaxpane.TokenType; - -public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretListener { - - private List highlights = new ArrayList<>(); - - private List specialHighlights = new ArrayList<>(); - - private List traitHighlights = new ArrayList<>(); - - private List methodHighlights = new ArrayList<>(); - - private List classHighlights = new ArrayList<>(); - - private Highlighting currentMethodHighlight; - - private Highlighting currentTraitHighlight; - - private ScriptPack script; - - public int lastTraitIndex = 0; - - public boolean ignoreCarret = false; - - private boolean reset = false; - - private final ABCPanel abcPanel; - - private int classIndex = -1; - - private boolean isStatic = false; - - private final List scriptListeners = new ArrayList<>(); - - public void addScriptListener(Runnable l) { - scriptListeners.add(l); - } - - public ABCPanel getAbcPanel() { - return abcPanel; - } - - public void removeScriptListener(Runnable l) { - scriptListeners.remove(l); - } - - public void fireScript() { - for (Runnable scriptListener : scriptListeners) { - scriptListener.run(); - } - } - - public Trait getCurrentTrait() { - return script.abc.findTraitByTraitId(classIndex, lastTraitIndex); - } - - public ScriptPack getScriptLeaf() { - return script; - } - - public boolean getIsStatic() { - return isStatic; - } - - public void setNoTrait() { - abcPanel.detailPanel.showCard(DetailPanel.UNSUPPORTED_TRAIT_CARD, null); - } - - public void hilightSpecial(HighlightSpecialType type, long index) { - int startPos; - int endPos; - if (currentMethodHighlight == null) { - if (currentTraitHighlight == null) { - return; - } - startPos = currentTraitHighlight.startPos; - endPos = currentTraitHighlight.startPos + currentTraitHighlight.len; - } else { - startPos = currentMethodHighlight.startPos; - endPos = currentMethodHighlight.startPos + currentMethodHighlight.len; - } - - List allh = new ArrayList<>(); - for (Highlighting h : traitHighlights) { - if (h.getProperties().index == lastTraitIndex) { - for (Highlighting sh : specialHighlights) { - if (sh.startPos >= h.startPos && (sh.startPos + sh.len < h.startPos + h.len)) { - allh.add(sh); - } - } - } - } - if (currentMethodHighlight != null) { - for (Highlighting h : specialHighlights) { - if (h.startPos >= startPos && (h.startPos + h.len < endPos)) { - allh.add(h); - } - } - } - for (Highlighting h : allh) { - if (h.getProperties().subtype.equals(type) && (h.getProperties().index == index)) { - ignoreCarret = true; - if (h.startPos <= getDocument().getLength()) { - setCaretPosition(h.startPos); - } - getCaret().setVisible(true); - ignoreCarret = false; - break; - } - } - } - - public void hilightOffset(long offset) { - if (currentMethodHighlight == null) { - return; - } - for (Highlighting h : traitHighlights) { - if (h.getProperties().index == lastTraitIndex) { - Highlighting h2 = Highlighting.searchOffset(highlights, offset, h.startPos, h.startPos + h.len); - if (h2 != null) { - ignoreCarret = true; - if (h2.startPos <= getDocument().getLength()) { - setCaretPosition(h2.startPos); - } - getCaret().setVisible(true); - ignoreCarret = false; - } - - } - } - } - - public void setClassIndex(int classIndex) { - this.classIndex = classIndex; - } - - private boolean displayMethod(int pos, int methodIndex, String name, Trait trait, boolean isStatic) { - ABC abc = getABC(); - if (abc == null) { - return false; - } - int bi = abc.findBodyIndex(methodIndex); - if (bi == -1) { - return false; - } - - //fix for inner functions: - if (trait instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tm = (TraitMethodGetterSetter) trait; - if (tm.method_info != methodIndex) { - trait = null; - } - } - if (trait instanceof TraitFunction) { - TraitFunction tf = (TraitFunction) trait; - if (tf.method_info != methodIndex) { - trait = null; - } - } - abcPanel.detailPanel.showCard(DetailPanel.METHOD_TRAIT_CARD, trait); - MethodCodePanel methodCodePanel = abcPanel.detailPanel.methodTraitPanel.methodCodePanel; - if (reset || (methodCodePanel.getBodyIndex() != bi)) { - methodCodePanel.setBodyIndex(bi, abc, name, trait, script.scriptIndex); - abcPanel.detailPanel.setEditMode(false); - this.isStatic = isStatic; - } - boolean success = false; - Highlighting h = Highlighting.searchPos(highlights, pos); - if (h != null) { - methodCodePanel.hilighOffset(h.getProperties().offset); - success = true; - } - Highlighting sh = Highlighting.searchPos(specialHighlights, pos); - if (sh != null) { - methodCodePanel.hilighSpecial(sh.getProperties().subtype, sh.getProperties().specialValue); - success = true; - } - return success; - } - - public void displayClass(int classIndex, int scriptIndex) { - if (abcPanel.navigator.getClassIndex() != classIndex) { - abcPanel.navigator.setClassIndex(classIndex, scriptIndex); - } - } - - public void resetEditing() { - reset = true; - caretUpdate(null); - reset = false; - } - - public int getMultinameUnderMouseCursor(Point pt) { - return getMultinameAtPos(viewToModel(pt)); - } - - public int getMultinameUnderCaret() { - return getMultinameAtPos(getCaretPosition()); - } - - public int getLocalDeclarationOfPos(int pos, Reference type) { - Highlighting sh = Highlighting.searchPos(specialHighlights, pos); - Highlighting h = Highlighting.searchPos(highlights, pos); - - if (h == null) { - return -1; - } - - List tms = Highlighting.searchAllPos(methodHighlights, pos); - if (tms.isEmpty()) { - return -1; - } - for (Highlighting tm : tms) { - - List tm_tms = Highlighting.searchAllIndexes(methodHighlights, tm.getProperties().index); - //is it already declaration? - if (h.getProperties().declaration || (sh != null && sh.getProperties().declaration)) { - return -1; //no jump - } - - String lname = h.getProperties().localName; - if ("this".equals(lname)) { - Highlighting ch = Highlighting.searchPos(classHighlights, pos); - int cindex = (int) ch.getProperties().index; - ABC abc = getABC(); - type.setVal(abc.instance_info.get(cindex).getName(abc.constants).getNameWithNamespace(abc.constants, true)); - return ch.startPos; - } - - HighlightData hData = h.getProperties(); - HighlightData search = new HighlightData(); - search.declaration = hData.declaration; - search.declaredType = hData.declaredType; - search.localName = hData.localName; - search.specialValue = hData.specialValue; - if (search.isEmpty()) { - return -1; - } - search.declaration = true; - - for (Highlighting tm1 : tm_tms) { - Highlighting rh = Highlighting.search(highlights, search, tm1.startPos, tm1.startPos + tm1.len); - if (rh == null) { - rh = Highlighting.search(specialHighlights, search, tm1.startPos, tm1.startPos + tm1.len); - } - if (rh != null) { - type.setVal(rh.getProperties().declaredType); - return rh.startPos; - } - } - } - - return -1; - } - - public boolean getPropertyTypeAtPos(int pos, Reference abcIndex, Reference classIndex, Reference traitIndex, Reference classTrait, Reference multinameIndex) { - - int m = getMultinameAtPos(pos, true); - if (m <= 0) { - return false; - } - SyntaxDocument sd = (SyntaxDocument) getDocument(); - Token t = sd.getTokenAt(pos + 1); - Token lastToken = t; - Token prev; - while (t.type == TokenType.IDENTIFIER || t.type == TokenType.KEYWORD || t.type == TokenType.REGEX) { - prev = sd.getPrevToken(t); - if (prev != null) { - if (!".".equals(prev.getString(sd))) { - break; - } - t = sd.getPrevToken(prev); - } else { - break; - } - } - if (t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD || t.type == TokenType.REGEX) { - return false; - } - Reference locTypeRef = new Reference<>(""); - getLocalDeclarationOfPos(t.start, locTypeRef); - String currentType = locTypeRef.getVal(); - if (currentType.equals("*")) { - return false; - } - boolean found; - t = sd.getNextToken(t); - while (t != lastToken && !currentType.equals("*")) { - t = sd.getNextToken(t); - String ident = t.getString(sd); - found = false; - List abcList = getABC().getSwf().getAbcList(); - loopi: - for (int i = 0; i < abcList.size(); i++) { - ABC a = abcList.get(i).getABC(); - int cindex = a.findClassByName(currentType); - if (cindex > -1) { - InstanceInfo ii = a.instance_info.get(cindex); - for (int j = 0; j < ii.instance_traits.traits.size(); j++) { - Trait tr = ii.instance_traits.traits.get(j); - if (ident.equals(tr.getName(a).getName(a.constants, new ArrayList<>(), false /*NOT RAW!*/))) { - classIndex.setVal(cindex); - abcIndex.setVal(i); - traitIndex.setVal(j); - classTrait.setVal(false); - multinameIndex.setVal(tr.name_index); - currentType = ii.getName(a.constants).getNameWithNamespace(a.constants, true); - found = true; - break loopi; - } - } - - ClassInfo ci = a.class_info.get(cindex); - for (int j = 0; j < ci.static_traits.traits.size(); j++) { - Trait tr = ci.static_traits.traits.get(j); - if (ident.equals(tr.getName(a).getName(a.constants, new ArrayList<>(), false /*NOT RAW!*/))) { - classIndex.setVal(cindex); - abcIndex.setVal(i); - traitIndex.setVal(j); - classTrait.setVal(true); - multinameIndex.setVal(tr.name_index); - currentType = ii.getName(a.constants).getNameWithNamespace(a.constants, true); - found = true; - break loopi; - } - } - } - } - if (!found) { - return false; - } - - t = sd.getNextToken(t); - if (!".".equals(t.getString(sd))) { - break; - } - } - return true; - } - - public int getMultinameAtPos(int pos) { - return getMultinameAtPos(pos, false); - } - - public int getMultinameAtPos(int pos, boolean codeOnly) { - Highlighting tm = Highlighting.searchPos(methodHighlights, pos); - Trait currentTrait = null; - int currentMethod = -1; - ABC abc = getABC(); - if (tm != null) { - - int mi = (int) tm.getProperties().index; - currentMethod = mi; - int bi = abc.findBodyIndex(mi); - Highlighting h = Highlighting.searchPos(highlights, pos); - if (h != null) { - List list = abc.bodies.get(bi).getCode().code; - AVM2Instruction lastIns = null; - long inspos = 0; - AVM2Instruction selIns = null; - for (AVM2Instruction ins : list) { - if (h.getProperties().offset == ins.getOffset()) { - selIns = ins; - break; - } - if (ins.getOffset() > h.getProperties().offset) { - inspos = h.getProperties().offset - lastIns.offset; - selIns = lastIns; - break; - } - lastIns = ins; - } - if (selIns != null) { - if (!codeOnly && ((selIns.definition instanceof ConstructSuperIns) || (selIns.definition instanceof CallSuperIns) || (selIns.definition instanceof CallSuperVoidIns))) { - Highlighting tc = Highlighting.searchPos(classHighlights, pos); - if (tc != null) { - int cindex = (int) tc.getProperties().index; - if (cindex > -1) { - return abc.instance_info.get(cindex).super_index; - } - } - } else { - for (int i = 0; i < selIns.definition.operands.length; i++) { - if (selIns.definition.operands[i] == AVM2Code.DAT_MULTINAME_INDEX) { - return selIns.operands[i]; - } - } - } - } - } - - } - if (codeOnly) { - return -1; - } - - Highlighting ch = Highlighting.searchPos(classHighlights, pos); - if (ch != null) { - Highlighting th = Highlighting.searchPos(traitHighlights, pos); - if (th != null) { - currentTrait = abc.findTraitByTraitId((int) ch.getProperties().index, (int) th.getProperties().index); - } - } - - if (currentTrait instanceof TraitMethodGetterSetter) { - currentMethod = ((TraitMethodGetterSetter) currentTrait).method_info; - } - Highlighting sh = Highlighting.searchPos(specialHighlights, pos); - if (sh != null) { - switch (sh.getProperties().subtype) { - case TYPE_NAME: - String typeName = sh.getProperties().specialValue; - for (int i = 1; i < abc.constants.constant_multiname.size(); i++) { - Multiname m = abc.constants.constant_multiname.get(i); - if (m != null) { - if (typeName.equals(m.getNameWithNamespace(abc.constants, true))) { - return i; - } - } - } - case TRAIT_TYPE_NAME: - if (currentTrait instanceof TraitSlotConst) { - TraitSlotConst ts = (TraitSlotConst) currentTrait; - return ts.type_index; - } - break; - case TRAIT_NAME: - if (currentTrait != null) { - //return currentTrait.name_index; - } - break; - case RETURNS: - if (currentMethod > -1) { - return abc.method_info.get(currentMethod).ret_type; - } - break; - case PARAM: - if (currentMethod > -1) { - return abc.method_info.get(currentMethod).param_types[(int) sh.getProperties().index]; - } - break; - } - } - return -1; - } - - @Override - public void caretUpdate(final CaretEvent e) { - ABC abc = getABC(); - if (abc == null) { - return; - } - if (ignoreCarret) { - return; - } - - getCaret().setVisible(true); - int pos = getCaretPosition(); - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setIgnoreCarret(true); - try { - classIndex = -1; - Highlighting cm = Highlighting.searchPos(classHighlights, pos); - if (cm != null) { - classIndex = (int) cm.getProperties().index; - displayClass(classIndex, script.scriptIndex); - } - Highlighting tm = Highlighting.searchPos(methodHighlights, pos); - if (tm != null) { - String name = ""; - if (classIndex > -1) { - name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); - } - - Trait currentTrait = null; - currentTraitHighlight = Highlighting.searchPos(traitHighlights, pos); - if (currentTraitHighlight != null) { - lastTraitIndex = (int) currentTraitHighlight.getProperties().index; - if (classIndex != -1) { - currentTrait = getCurrentTrait(); - isStatic = abc.isStaticTraitId(classIndex, lastTraitIndex); - if (currentTrait != null) { - name += ":" + currentTrait.getName(abc).getName(abc.constants, new ArrayList<>(), false); - } - } - } - - displayMethod(pos, (int) tm.getProperties().index, name, currentTrait, isStatic); - currentMethodHighlight = tm; - return; - } - - if (classIndex == -1) { - abcPanel.navigator.setClassIndex(-1, script.scriptIndex); - setNoTrait(); - return; - } - Trait currentTrait; - currentTraitHighlight = Highlighting.searchPos(traitHighlights, pos); - if (currentTraitHighlight != null) { - lastTraitIndex = (int) currentTraitHighlight.getProperties().index; - currentTrait = getCurrentTrait(); - if (currentTrait != null) { - if (currentTrait instanceof TraitSlotConst) { - abcPanel.detailPanel.slotConstTraitPanel.load((TraitSlotConst) currentTrait, abc, - abc.isStaticTraitId(classIndex, lastTraitIndex)); - abcPanel.detailPanel.showCard(DetailPanel.SLOT_CONST_TRAIT_CARD, currentTrait); - abcPanel.detailPanel.setEditMode(false); - currentMethodHighlight = null; - Highlighting spec = Highlighting.searchPos(specialHighlights, pos, currentTraitHighlight.startPos, currentTraitHighlight.startPos + currentTraitHighlight.len); - if (spec != null) { - abcPanel.detailPanel.slotConstTraitPanel.hilightSpecial(spec); - } - - return; - } - } - currentMethodHighlight = null; - currentTrait = null; - String name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); - currentTrait = getCurrentTrait(); - isStatic = abc.isStaticTraitId(classIndex, lastTraitIndex); - if (currentTrait != null) { - name += ":" + currentTrait.getName(abc).getName(abc.constants, new ArrayList<>(), false); - } - - displayMethod(pos, abc.findMethodIdByTraitId(classIndex, lastTraitIndex), name, currentTrait, isStatic); - return; - } - setNoTrait(); - } finally { - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setIgnoreCarret(false); - } - } - - public void gotoLastTrait() { - gotoTrait(lastTraitIndex); - } - - public void gotoTrait(int traitId) { - if (traitId == -1) { - return; - } - - Highlighting tc = Highlighting.searchIndex(classHighlights, classIndex); - if (tc != null) { - Highlighting th = Highlighting.searchIndex(traitHighlights, traitId, tc.startPos, tc.startPos + tc.len); - int pos; - if (th != null) { - if (th.len > 1) { - ignoreCarret = true; - int startPos = th.startPos + th.len - 1; - if (startPos <= getDocument().getLength()) { - setCaretPosition(startPos); - } - ignoreCarret = false; - } - pos = th.startPos; - } else { - pos = tc.startPos; - } - - final int fpos = pos; - new Timer().schedule(new TimerTask() { - @Override - public void run() { - if (fpos <= getDocument().getLength()) { - setCaretPosition(fpos); - } - } - }, 100); - } - } - - public DecompiledEditorPane(ABCPanel abcPanel) { - super(); - setEditable(false); - getCaret().setVisible(true); - addCaretListener(this); - this.abcPanel = abcPanel; - } - - public void clearScript() { - script = null; - } - - public void setScript(ScriptPack scriptLeaf) { - abcPanel.scriptNameLabel.setText(scriptLeaf.getClassPath().toString()); - int scriptIndex = scriptLeaf.scriptIndex; - ScriptInfo script = null; - ABC abc = scriptLeaf.abc; - if (scriptIndex > -1) { - script = abc.script_info.get(scriptIndex); - } - if (script == null) { - highlights = new ArrayList<>(); - specialHighlights = new ArrayList<>(); - traitHighlights = new ArrayList<>(); - methodHighlights = new ArrayList<>(); - this.script = scriptLeaf; - return; - } - setText("// " + AppStrings.translate("pleasewait") + "..."); - - this.script = scriptLeaf; - CachedDecompilation cd = null; - try { - cd = SWF.getCached(scriptLeaf); - } catch (InterruptedException ex) { - } - - if (cd != null) { - String hilightedCode = cd.text; - highlights = cd.getInstructionHighlights(); - specialHighlights = cd.getSpecialHighligths(); - traitHighlights = cd.getTraitHighlights(); - methodHighlights = cd.getMethodHighlights(); - classHighlights = cd.getClassHighlights(); - setText(hilightedCode); - - if (classHighlights.size() > 0) { - setCaretPosition(classHighlights.get(0).startPos); - } - } - fireScript(); - } - - public void reloadClass() { - int ci = classIndex; - SWF.uncache(script); - if (script != null && getABC() != null) { - setScript(script); - } - setNoTrait(); - setClassIndex(ci); - } - - public int getClassIndex() { - return classIndex; - } - - private ABC getABC() { - return script == null ? null : script.abc; - } - - @Override - public void setText(String t) { - super.setText(t); - setCaretPosition(0); - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.abc; + +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.CachedDecompilation; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; +import com.jpexs.decompiler.flash.abc.types.ClassInfo; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightData; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; +import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import jsyntaxpane.SyntaxDocument; +import jsyntaxpane.Token; +import jsyntaxpane.TokenType; + +public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretListener { + + private List highlights = new ArrayList<>(); + + private List specialHighlights = new ArrayList<>(); + + private List traitHighlights = new ArrayList<>(); + + private List methodHighlights = new ArrayList<>(); + + private List classHighlights = new ArrayList<>(); + + private Highlighting currentMethodHighlight; + + private Highlighting currentTraitHighlight; + + private ScriptPack script; + + public int lastTraitIndex = 0; + + public boolean ignoreCarret = false; + + private boolean reset = false; + + private final ABCPanel abcPanel; + + private int classIndex = -1; + + private boolean isStatic = false; + + private final List scriptListeners = new ArrayList<>(); + + public void addScriptListener(Runnable l) { + scriptListeners.add(l); + } + + public ABCPanel getAbcPanel() { + return abcPanel; + } + + public void removeScriptListener(Runnable l) { + scriptListeners.remove(l); + } + + public void fireScript() { + for (Runnable scriptListener : scriptListeners) { + scriptListener.run(); + } + } + + public Trait getCurrentTrait() { + return script.abc.findTraitByTraitId(classIndex, lastTraitIndex); + } + + public ScriptPack getScriptLeaf() { + return script; + } + + public boolean getIsStatic() { + return isStatic; + } + + public void setNoTrait() { + abcPanel.detailPanel.showCard(DetailPanel.UNSUPPORTED_TRAIT_CARD, null); + } + + public void hilightSpecial(HighlightSpecialType type, long index) { + int startPos; + int endPos; + if (currentMethodHighlight == null) { + if (currentTraitHighlight == null) { + return; + } + startPos = currentTraitHighlight.startPos; + endPos = currentTraitHighlight.startPos + currentTraitHighlight.len; + } else { + startPos = currentMethodHighlight.startPos; + endPos = currentMethodHighlight.startPos + currentMethodHighlight.len; + } + + List allh = new ArrayList<>(); + for (Highlighting h : traitHighlights) { + if (h.getProperties().index == lastTraitIndex) { + for (Highlighting sh : specialHighlights) { + if (sh.startPos >= h.startPos && (sh.startPos + sh.len < h.startPos + h.len)) { + allh.add(sh); + } + } + } + } + if (currentMethodHighlight != null) { + for (Highlighting h : specialHighlights) { + if (h.startPos >= startPos && (h.startPos + h.len < endPos)) { + allh.add(h); + } + } + } + for (Highlighting h : allh) { + if (h.getProperties().subtype.equals(type) && (h.getProperties().index == index)) { + ignoreCarret = true; + if (h.startPos <= getDocument().getLength()) { + setCaretPosition(h.startPos); + } + getCaret().setVisible(true); + ignoreCarret = false; + break; + } + } + } + + public void hilightOffset(long offset) { + if (currentMethodHighlight == null) { + return; + } + for (Highlighting h : traitHighlights) { + if (h.getProperties().index == lastTraitIndex) { + Highlighting h2 = Highlighting.searchOffset(highlights, offset, h.startPos, h.startPos + h.len); + if (h2 != null) { + ignoreCarret = true; + if (h2.startPos <= getDocument().getLength()) { + setCaretPosition(h2.startPos); + } + getCaret().setVisible(true); + ignoreCarret = false; + } + + } + } + } + + public void setClassIndex(int classIndex) { + this.classIndex = classIndex; + } + + private boolean displayMethod(int pos, int methodIndex, String name, Trait trait, boolean isStatic) { + ABC abc = getABC(); + if (abc == null) { + return false; + } + int bi = abc.findBodyIndex(methodIndex); + if (bi == -1) { + return false; + } + + //fix for inner functions: + if (trait instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) trait; + if (tm.method_info != methodIndex) { + trait = null; + } + } + if (trait instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) trait; + if (tf.method_info != methodIndex) { + trait = null; + } + } + abcPanel.detailPanel.showCard(DetailPanel.METHOD_TRAIT_CARD, trait); + MethodCodePanel methodCodePanel = abcPanel.detailPanel.methodTraitPanel.methodCodePanel; + if (reset || (methodCodePanel.getBodyIndex() != bi)) { + methodCodePanel.setBodyIndex(bi, abc, name, trait, script.scriptIndex); + abcPanel.detailPanel.setEditMode(false); + this.isStatic = isStatic; + } + boolean success = false; + Highlighting h = Highlighting.searchPos(highlights, pos); + if (h != null) { + methodCodePanel.hilighOffset(h.getProperties().offset); + success = true; + } + Highlighting sh = Highlighting.searchPos(specialHighlights, pos); + if (sh != null) { + methodCodePanel.hilighSpecial(sh.getProperties().subtype, sh.getProperties().specialValue); + success = true; + } + return success; + } + + public void displayClass(int classIndex, int scriptIndex) { + if (abcPanel.navigator.getClassIndex() != classIndex) { + abcPanel.navigator.setClassIndex(classIndex, scriptIndex); + } + } + + public void resetEditing() { + reset = true; + caretUpdate(null); + reset = false; + } + + public int getMultinameUnderMouseCursor(Point pt) { + return getMultinameAtPos(viewToModel(pt)); + } + + public int getMultinameUnderCaret() { + return getMultinameAtPos(getCaretPosition()); + } + + public int getLocalDeclarationOfPos(int pos, Reference type) { + Highlighting sh = Highlighting.searchPos(specialHighlights, pos); + Highlighting h = Highlighting.searchPos(highlights, pos); + + if (h == null) { + return -1; + } + + List tms = Highlighting.searchAllPos(methodHighlights, pos); + if (tms.isEmpty()) { + return -1; + } + for (Highlighting tm : tms) { + + List tm_tms = Highlighting.searchAllIndexes(methodHighlights, tm.getProperties().index); + //is it already declaration? + if (h.getProperties().declaration || (sh != null && sh.getProperties().declaration)) { + return -1; //no jump + } + + String lname = h.getProperties().localName; + if ("this".equals(lname)) { + Highlighting ch = Highlighting.searchPos(classHighlights, pos); + int cindex = (int) ch.getProperties().index; + ABC abc = getABC(); + type.setVal(abc.instance_info.get(cindex).getName(abc.constants).getNameWithNamespace(abc.constants, true)); + return ch.startPos; + } + + HighlightData hData = h.getProperties(); + HighlightData search = new HighlightData(); + search.declaration = hData.declaration; + search.declaredType = hData.declaredType; + search.localName = hData.localName; + search.specialValue = hData.specialValue; + if (search.isEmpty()) { + return -1; + } + search.declaration = true; + + for (Highlighting tm1 : tm_tms) { + Highlighting rh = Highlighting.search(highlights, search, tm1.startPos, tm1.startPos + tm1.len); + if (rh == null) { + rh = Highlighting.search(specialHighlights, search, tm1.startPos, tm1.startPos + tm1.len); + } + if (rh != null) { + type.setVal(rh.getProperties().declaredType); + return rh.startPos; + } + } + } + + return -1; + } + + public boolean getPropertyTypeAtPos(int pos, Reference abcIndex, Reference classIndex, Reference traitIndex, Reference classTrait, Reference multinameIndex) { + + int m = getMultinameAtPos(pos, true); + if (m <= 0) { + return false; + } + SyntaxDocument sd = (SyntaxDocument) getDocument(); + Token t = sd.getTokenAt(pos + 1); + Token lastToken = t; + Token prev; + while (t.type == TokenType.IDENTIFIER || t.type == TokenType.KEYWORD || t.type == TokenType.REGEX) { + prev = sd.getPrevToken(t); + if (prev != null) { + if (!".".equals(prev.getString(sd))) { + break; + } + t = sd.getPrevToken(prev); + } else { + break; + } + } + if (t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD || t.type == TokenType.REGEX) { + return false; + } + Reference locTypeRef = new Reference<>(""); + getLocalDeclarationOfPos(t.start, locTypeRef); + String currentType = locTypeRef.getVal(); + if (currentType.equals("*")) { + return false; + } + boolean found; + t = sd.getNextToken(t); + while (t != lastToken && !currentType.equals("*")) { + t = sd.getNextToken(t); + String ident = t.getString(sd); + found = false; + List abcList = getABC().getSwf().getAbcList(); + loopi: + for (int i = 0; i < abcList.size(); i++) { + ABC a = abcList.get(i).getABC(); + int cindex = a.findClassByName(currentType); + if (cindex > -1) { + InstanceInfo ii = a.instance_info.get(cindex); + for (int j = 0; j < ii.instance_traits.traits.size(); j++) { + Trait tr = ii.instance_traits.traits.get(j); + if (ident.equals(tr.getName(a).getName(a.constants, new ArrayList<>(), false /*NOT RAW!*/))) { + classIndex.setVal(cindex); + abcIndex.setVal(i); + traitIndex.setVal(j); + classTrait.setVal(false); + multinameIndex.setVal(tr.name_index); + currentType = ii.getName(a.constants).getNameWithNamespace(a.constants, true); + found = true; + break loopi; + } + } + + ClassInfo ci = a.class_info.get(cindex); + for (int j = 0; j < ci.static_traits.traits.size(); j++) { + Trait tr = ci.static_traits.traits.get(j); + if (ident.equals(tr.getName(a).getName(a.constants, new ArrayList<>(), false /*NOT RAW!*/))) { + classIndex.setVal(cindex); + abcIndex.setVal(i); + traitIndex.setVal(j); + classTrait.setVal(true); + multinameIndex.setVal(tr.name_index); + currentType = ii.getName(a.constants).getNameWithNamespace(a.constants, true); + found = true; + break loopi; + } + } + } + } + if (!found) { + return false; + } + + t = sd.getNextToken(t); + if (!".".equals(t.getString(sd))) { + break; + } + } + return true; + } + + public int getMultinameAtPos(int pos) { + return getMultinameAtPos(pos, false); + } + + public int getMultinameAtPos(int pos, boolean codeOnly) { + Highlighting tm = Highlighting.searchPos(methodHighlights, pos); + Trait currentTrait = null; + int currentMethod = -1; + ABC abc = getABC(); + if (tm != null) { + + int mi = (int) tm.getProperties().index; + currentMethod = mi; + int bi = abc.findBodyIndex(mi); + Highlighting h = Highlighting.searchPos(highlights, pos); + if (h != null) { + List list = abc.bodies.get(bi).getCode().code; + AVM2Instruction lastIns = null; + long inspos = 0; + AVM2Instruction selIns = null; + for (AVM2Instruction ins : list) { + if (h.getProperties().offset == ins.getOffset()) { + selIns = ins; + break; + } + if (ins.getOffset() > h.getProperties().offset) { + inspos = h.getProperties().offset - lastIns.offset; + selIns = lastIns; + break; + } + lastIns = ins; + } + if (selIns != null) { + if (!codeOnly && ((selIns.definition instanceof ConstructSuperIns) || (selIns.definition instanceof CallSuperIns) || (selIns.definition instanceof CallSuperVoidIns))) { + Highlighting tc = Highlighting.searchPos(classHighlights, pos); + if (tc != null) { + int cindex = (int) tc.getProperties().index; + if (cindex > -1) { + return abc.instance_info.get(cindex).super_index; + } + } + } else { + for (int i = 0; i < selIns.definition.operands.length; i++) { + if (selIns.definition.operands[i] == AVM2Code.DAT_MULTINAME_INDEX) { + return selIns.operands[i]; + } + } + } + } + } + + } + if (codeOnly) { + return -1; + } + + Highlighting ch = Highlighting.searchPos(classHighlights, pos); + if (ch != null) { + Highlighting th = Highlighting.searchPos(traitHighlights, pos); + if (th != null) { + currentTrait = abc.findTraitByTraitId((int) ch.getProperties().index, (int) th.getProperties().index); + } + } + + if (currentTrait instanceof TraitMethodGetterSetter) { + currentMethod = ((TraitMethodGetterSetter) currentTrait).method_info; + } + Highlighting sh = Highlighting.searchPos(specialHighlights, pos); + if (sh != null) { + switch (sh.getProperties().subtype) { + case TYPE_NAME: + String typeName = sh.getProperties().specialValue; + for (int i = 1; i < abc.constants.constant_multiname.size(); i++) { + Multiname m = abc.constants.constant_multiname.get(i); + if (m != null) { + if (typeName.equals(m.getNameWithNamespace(abc.constants, true))) { + return i; + } + } + } + case TRAIT_TYPE_NAME: + if (currentTrait instanceof TraitSlotConst) { + TraitSlotConst ts = (TraitSlotConst) currentTrait; + return ts.type_index; + } + break; + case TRAIT_NAME: + if (currentTrait != null) { + //return currentTrait.name_index; + } + break; + case RETURNS: + if (currentMethod > -1) { + return abc.method_info.get(currentMethod).ret_type; + } + break; + case PARAM: + if (currentMethod > -1) { + return abc.method_info.get(currentMethod).param_types[(int) sh.getProperties().index]; + } + break; + } + } + return -1; + } + + @Override + public void caretUpdate(final CaretEvent e) { + ABC abc = getABC(); + if (abc == null) { + return; + } + if (ignoreCarret) { + return; + } + + getCaret().setVisible(true); + int pos = getCaretPosition(); + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setIgnoreCarret(true); + try { + classIndex = -1; + Highlighting cm = Highlighting.searchPos(classHighlights, pos); + if (cm != null) { + classIndex = (int) cm.getProperties().index; + displayClass(classIndex, script.scriptIndex); + } + Highlighting tm = Highlighting.searchPos(methodHighlights, pos); + if (tm != null) { + String name = ""; + if (classIndex > -1) { + name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); + } + + Trait currentTrait = null; + currentTraitHighlight = Highlighting.searchPos(traitHighlights, pos); + if (currentTraitHighlight != null) { + lastTraitIndex = (int) currentTraitHighlight.getProperties().index; + if (classIndex != -1) { + currentTrait = getCurrentTrait(); + isStatic = abc.isStaticTraitId(classIndex, lastTraitIndex); + if (currentTrait != null) { + name += ":" + currentTrait.getName(abc).getName(abc.constants, new ArrayList<>(), false); + } + } + } + + displayMethod(pos, (int) tm.getProperties().index, name, currentTrait, isStatic); + currentMethodHighlight = tm; + return; + } + + if (classIndex == -1) { + abcPanel.navigator.setClassIndex(-1, script.scriptIndex); + setNoTrait(); + return; + } + Trait currentTrait; + currentTraitHighlight = Highlighting.searchPos(traitHighlights, pos); + if (currentTraitHighlight != null) { + lastTraitIndex = (int) currentTraitHighlight.getProperties().index; + currentTrait = getCurrentTrait(); + if (currentTrait != null) { + if (currentTrait instanceof TraitSlotConst) { + abcPanel.detailPanel.slotConstTraitPanel.load((TraitSlotConst) currentTrait, abc, + abc.isStaticTraitId(classIndex, lastTraitIndex)); + abcPanel.detailPanel.showCard(DetailPanel.SLOT_CONST_TRAIT_CARD, currentTrait); + abcPanel.detailPanel.setEditMode(false); + currentMethodHighlight = null; + Highlighting spec = Highlighting.searchPos(specialHighlights, pos, currentTraitHighlight.startPos, currentTraitHighlight.startPos + currentTraitHighlight.len); + if (spec != null) { + abcPanel.detailPanel.slotConstTraitPanel.hilightSpecial(spec); + } + + return; + } + } + currentMethodHighlight = null; + currentTrait = null; + String name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); + currentTrait = getCurrentTrait(); + isStatic = abc.isStaticTraitId(classIndex, lastTraitIndex); + if (currentTrait != null) { + name += ":" + currentTrait.getName(abc).getName(abc.constants, new ArrayList<>(), false); + } + + displayMethod(pos, abc.findMethodIdByTraitId(classIndex, lastTraitIndex), name, currentTrait, isStatic); + return; + } + setNoTrait(); + } finally { + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setIgnoreCarret(false); + } + } + + public void gotoLastTrait() { + gotoTrait(lastTraitIndex); + } + + public void gotoTrait(int traitId) { + if (traitId == -1) { + return; + } + + Highlighting tc = Highlighting.searchIndex(classHighlights, classIndex); + if (tc != null) { + Highlighting th = Highlighting.searchIndex(traitHighlights, traitId, tc.startPos, tc.startPos + tc.len); + int pos; + if (th != null) { + if (th.len > 1) { + ignoreCarret = true; + int startPos = th.startPos + th.len - 1; + if (startPos <= getDocument().getLength()) { + setCaretPosition(startPos); + } + ignoreCarret = false; + } + pos = th.startPos; + } else { + pos = tc.startPos; + } + + final int fpos = pos; + new Timer().schedule(new TimerTask() { + @Override + public void run() { + if (fpos <= getDocument().getLength()) { + setCaretPosition(fpos); + } + } + }, 100); + } + } + + public DecompiledEditorPane(ABCPanel abcPanel) { + super(); + setEditable(false); + getCaret().setVisible(true); + addCaretListener(this); + this.abcPanel = abcPanel; + } + + public void clearScript() { + script = null; + } + + public void setScript(ScriptPack scriptLeaf) { + abcPanel.scriptNameLabel.setText(scriptLeaf.getClassPath().toString()); + int scriptIndex = scriptLeaf.scriptIndex; + ScriptInfo script = null; + ABC abc = scriptLeaf.abc; + if (scriptIndex > -1) { + script = abc.script_info.get(scriptIndex); + } + if (script == null) { + highlights = new ArrayList<>(); + specialHighlights = new ArrayList<>(); + traitHighlights = new ArrayList<>(); + methodHighlights = new ArrayList<>(); + this.script = scriptLeaf; + return; + } + setText("// " + AppStrings.translate("pleasewait") + "..."); + + this.script = scriptLeaf; + CachedDecompilation cd = null; + try { + cd = SWF.getCached(scriptLeaf); + } catch (InterruptedException ex) { + } + + if (cd != null) { + String hilightedCode = cd.text; + highlights = cd.getInstructionHighlights(); + specialHighlights = cd.getSpecialHighligths(); + traitHighlights = cd.getTraitHighlights(); + methodHighlights = cd.getMethodHighlights(); + classHighlights = cd.getClassHighlights(); + setText(hilightedCode); + + if (classHighlights.size() > 0) { + setCaretPosition(classHighlights.get(0).startPos); + } + } + fireScript(); + } + + public void reloadClass() { + int ci = classIndex; + SWF.uncache(script); + if (script != null && getABC() != null) { + setScript(script); + } + setNoTrait(); + setClassIndex(ci); + } + + public int getClassIndex() { + return classIndex; + } + + private ABC getABC() { + return script == null ? null : script.abc; + } + + @Override + public void setText(String t) { + super.setText(t); + setCaretPosition(0); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DetailPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/DetailPanel.java index 422f2c5a3..48cad84f9 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DetailPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/DetailPanel.java @@ -132,20 +132,31 @@ public class DetailPanel extends JPanel implements TagEditorPanel { //traitInfoPanel.add(new JLabel(" " + translate("abc.detail.traitname"))); traitInfoPanel.add(traitNameLabel); topPanel.add(traitInfoPanel, BorderLayout.CENTER); + methodTraitPanel.methodCodePanel.getSourceTextArea().addTextChangedListener(this::editorTextChanged); + slotConstTraitPanel.slotConstEditor.addTextChangedListener(this::editorTextChanged); add(topPanel, BorderLayout.NORTH); } + private void editorTextChanged() { + setModified(true); + } + + private boolean isModified() { + return saveButton.isVisible() && saveButton.isEnabled(); + } + + private void setModified(boolean value) { + saveButton.setEnabled(value); + } + public void setEditMode(boolean val) { slotConstTraitPanel.setEditMode(val); methodTraitPanel.setEditMode(val); saveButton.setVisible(val); + saveButton.setEnabled(false); editButton.setVisible(!val); cancelButton.setVisible(val); - if (val) { - selectedLabel.setIcon(View.getIcon("editing16")); - } else { - selectedLabel.setIcon(null); - } + selectedLabel.setIcon(val ? View.getIcon("editing16") : null); } public void showCard(final String name, final Trait trait) { diff --git a/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java b/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java index a35a7db9d..8769bde22 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java @@ -1,179 +1,181 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.gui.AppStrings; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.gui.controls.NoneSelectedButtonGroup; -import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; -import java.awt.BorderLayout; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JToggleButton; - -/** - * - * @author JPEXS - */ -public class MethodCodePanel extends JPanel { - - private final ASMSourceEditorPane sourceTextArea; - - public JPanel buttonsPanel; - - private final JToggleButton hexButton; - - private final JToggleButton hexOnlyButton; - - public void focusEditor() { - sourceTextArea.requestFocusInWindow(); - } - - public int getScriptIndex() { - return sourceTextArea.getScriptIndex(); - } - - public String getTraitName() { - return sourceTextArea.getName(); - } - - public void setIgnoreCarret(boolean ignoreCarret) { - sourceTextArea.setIgnoreCarret(ignoreCarret); - } - - public void hilighOffset(long offset) { - sourceTextArea.hilighOffset(offset); - } - - public void hilighSpecial(HighlightSpecialType type, String specialValue) { - sourceTextArea.hilighSpecial(type, specialValue); - } - - public void setBodyIndex(int bodyIndex, ABC abc, Trait trait, int scriptIndex) { - sourceTextArea.setBodyIndex(bodyIndex, abc, sourceTextArea.getName(), trait, scriptIndex); - } - - public void setBodyIndex(int bodyIndex, ABC abc, String name, Trait trait, int scriptIndex) { - sourceTextArea.setBodyIndex(bodyIndex, abc, name, trait, scriptIndex); - } - - public int getBodyIndex() { - return sourceTextArea.bodyIndex; - } - - public void clear() { - sourceTextArea.clear(); - } - - public boolean save() { - return sourceTextArea.save(); - } - - public MethodCodePanel(DecompiledEditorPane decompiledEditor) { - sourceTextArea = new ASMSourceEditorPane(decompiledEditor); - - setLayout(new BorderLayout()); - add(new JScrollPane(sourceTextArea), BorderLayout.CENTER); - sourceTextArea.setContentType("text/flasm3"); - sourceTextArea.setFont(new Font("Monospaced", Font.PLAIN, sourceTextArea.getFont().getSize())); - - buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.X_AXIS)); - - JButton graphButton = new JButton(View.getIcon("graph16")); - graphButton.addActionListener(this::graphButtonActionPerformed); - graphButton.setToolTipText(AppStrings.translate("button.viewgraph")); - graphButton.setMargin(new Insets(3, 3, 3, 3)); - - hexButton = new JToggleButton(View.getIcon("hexas16")); - hexButton.addActionListener(this::hexButtonActionPerformed); - hexButton.setToolTipText(AppStrings.translate("button.viewhex")); - hexButton.setMargin(new Insets(3, 3, 3, 3)); - - hexOnlyButton = new JToggleButton(View.getIcon("hex16")); - hexOnlyButton.addActionListener(this::hexOnlyButtonActionPerformed); - hexOnlyButton.setToolTipText(AppStrings.translate("button.viewhex")); - hexOnlyButton.setMargin(new Insets(3, 3, 3, 3)); - - NoneSelectedButtonGroup exportModeButtonGroup = new NoneSelectedButtonGroup(); - exportModeButtonGroup.add(hexButton); - exportModeButtonGroup.add(hexOnlyButton); - - buttonsPanel.add(graphButton); - buttonsPanel.add(hexButton); - buttonsPanel.add(hexOnlyButton); - buttonsPanel.add(new JPanel()); - // buttonsPanel.add(saveButton); - // buttonsPan.add(execButton); - - add(buttonsPanel, BorderLayout.NORTH); - } - - private void graphButtonActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - sourceTextArea.graph(); - } - - private void hexButtonActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - sourceTextArea.setHex(getExportMode(), false); - } - - private void hexOnlyButtonActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - sourceTextArea.setHex(getExportMode(), false); - } - - private ScriptExportMode getExportMode() { - ScriptExportMode exportMode = hexOnlyButton.isSelected() ? ScriptExportMode.HEX - : (hexButton.isSelected() ? ScriptExportMode.PCODE_HEX : ScriptExportMode.PCODE); - return exportMode; - } - - public void setEditMode(boolean val) { - ScriptExportMode exportMode = getExportMode(); - if (val) { - sourceTextArea.setHex(exportMode == ScriptExportMode.HEX ? ScriptExportMode.HEX : ScriptExportMode.PCODE, false); - } else { - if (exportMode != ScriptExportMode.PCODE) { - sourceTextArea.setHex(exportMode, false); - } - } - - sourceTextArea.setEditable(val); - sourceTextArea.getCaret().setVisible(true); - buttonsPanel.setVisible(!val); - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.abc; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.controls.NoneSelectedButtonGroup; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToggleButton; + +/** + * + * @author JPEXS + */ +public class MethodCodePanel extends JPanel { + + private final ASMSourceEditorPane sourceTextArea; + + public JPanel buttonsPanel; + + private final JToggleButton hexButton; + + private final JToggleButton hexOnlyButton; + + public void focusEditor() { + sourceTextArea.requestFocusInWindow(); + } + + public int getScriptIndex() { + return sourceTextArea.getScriptIndex(); + } + + public String getTraitName() { + return sourceTextArea.getName(); + } + + public void setIgnoreCarret(boolean ignoreCarret) { + sourceTextArea.setIgnoreCarret(ignoreCarret); + } + + public void hilighOffset(long offset) { + sourceTextArea.hilighOffset(offset); + } + + public void hilighSpecial(HighlightSpecialType type, String specialValue) { + sourceTextArea.hilighSpecial(type, specialValue); + } + + public void setBodyIndex(int bodyIndex, ABC abc, Trait trait, int scriptIndex) { + sourceTextArea.setBodyIndex(bodyIndex, abc, sourceTextArea.getName(), trait, scriptIndex); + } + + public void setBodyIndex(int bodyIndex, ABC abc, String name, Trait trait, int scriptIndex) { + sourceTextArea.setBodyIndex(bodyIndex, abc, name, trait, scriptIndex); + } + + public int getBodyIndex() { + return sourceTextArea.bodyIndex; + } + + public void clear() { + sourceTextArea.clear(); + } + + public boolean save() { + return sourceTextArea.save(); + } + + public MethodCodePanel(DecompiledEditorPane decompiledEditor) { + sourceTextArea = new ASMSourceEditorPane(decompiledEditor); + + setLayout(new BorderLayout()); + add(new JScrollPane(sourceTextArea), BorderLayout.CENTER); + sourceTextArea.changeContentType("text/flasm3"); + sourceTextArea.setFont(new Font("Monospaced", Font.PLAIN, sourceTextArea.getFont().getSize())); + + buttonsPanel = new JPanel(); + buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.X_AXIS)); + + JButton graphButton = new JButton(View.getIcon("graph16")); + graphButton.addActionListener(this::graphButtonActionPerformed); + graphButton.setToolTipText(AppStrings.translate("button.viewgraph")); + graphButton.setMargin(new Insets(3, 3, 3, 3)); + + hexButton = new JToggleButton(View.getIcon("hexas16")); + hexButton.addActionListener(this::hexButtonActionPerformed); + hexButton.setToolTipText(AppStrings.translate("button.viewhex")); + hexButton.setMargin(new Insets(3, 3, 3, 3)); + + hexOnlyButton = new JToggleButton(View.getIcon("hex16")); + hexOnlyButton.addActionListener(this::hexOnlyButtonActionPerformed); + hexOnlyButton.setToolTipText(AppStrings.translate("button.viewhex")); + hexOnlyButton.setMargin(new Insets(3, 3, 3, 3)); + + NoneSelectedButtonGroup exportModeButtonGroup = new NoneSelectedButtonGroup(); + exportModeButtonGroup.add(hexButton); + exportModeButtonGroup.add(hexOnlyButton); + + buttonsPanel.add(graphButton); + buttonsPanel.add(hexButton); + buttonsPanel.add(hexOnlyButton); + buttonsPanel.add(new JPanel()); + + add(buttonsPanel, BorderLayout.NORTH); + } + + private void graphButtonActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + sourceTextArea.graph(); + } + + private void hexButtonActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + sourceTextArea.setHex(getExportMode(), false); + } + + private void hexOnlyButtonActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + sourceTextArea.setHex(getExportMode(), false); + } + + public ASMSourceEditorPane getSourceTextArea() { + return sourceTextArea; + } + + private ScriptExportMode getExportMode() { + ScriptExportMode exportMode = hexOnlyButton.isSelected() ? ScriptExportMode.HEX + : (hexButton.isSelected() ? ScriptExportMode.PCODE_HEX : ScriptExportMode.PCODE); + return exportMode; + } + + public void setEditMode(boolean val) { + ScriptExportMode exportMode = getExportMode(); + if (val) { + sourceTextArea.setHex(exportMode == ScriptExportMode.HEX ? ScriptExportMode.HEX : ScriptExportMode.PCODE, false); + } else { + if (exportMode != ScriptExportMode.PCODE) { + sourceTextArea.setHex(exportMode, false); + } + } + + sourceTextArea.setEditable(val); + sourceTextArea.getCaret().setVisible(true); + buttonsPanel.setVisible(!val); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/MethodTraitDetailPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/MethodTraitDetailPanel.java index 126f8cdf7..8256b8ecf 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/MethodTraitDetailPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/MethodTraitDetailPanel.java @@ -1,55 +1,55 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import java.awt.BorderLayout; -import javax.swing.JPanel; - -/** - * - * @author JPEXS - */ -public class MethodTraitDetailPanel extends JPanel implements TraitDetail { - - public MethodCodePanel methodCodePanel; - - public ABCPanel abcPanel; - - public MethodTraitDetailPanel(ABCPanel abcPanel) { - this.abcPanel = abcPanel; - methodCodePanel = new MethodCodePanel(abcPanel.decompiledTextArea); - setLayout(new BorderLayout()); - add(methodCodePanel, BorderLayout.CENTER); - } - - @Override - public boolean save() { - return methodCodePanel.save(); - } - - @Override - public void setEditMode(boolean val) { - methodCodePanel.setEditMode(val); - } - - private boolean active = false; - - @Override - public void setActive(boolean val) { - this.active = val; - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.abc; + +import java.awt.BorderLayout; +import javax.swing.JPanel; + +/** + * + * @author JPEXS + */ +public class MethodTraitDetailPanel extends JPanel implements TraitDetail { + + public MethodCodePanel methodCodePanel; + + public ABCPanel abcPanel; + + private boolean active = false; + + public MethodTraitDetailPanel(ABCPanel abcPanel) { + this.abcPanel = abcPanel; + methodCodePanel = new MethodCodePanel(abcPanel.decompiledTextArea); + setLayout(new BorderLayout()); + add(methodCodePanel, BorderLayout.CENTER); + } + + @Override + public boolean save() { + return methodCodePanel.save(); + } + + @Override + public void setEditMode(boolean val) { + methodCodePanel.setEditMode(val); + } + + @Override + public void setActive(boolean val) { + this.active = val; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/SlotConstTraitDetailPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/SlotConstTraitDetailPanel.java index 7d28bfe83..dc21bad23 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/SlotConstTraitDetailPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/SlotConstTraitDetailPanel.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; @@ -33,7 +34,6 @@ import java.io.StringReader; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import javax.swing.JEditorPane; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; @@ -46,7 +46,7 @@ import javax.swing.event.CaretListener; */ public class SlotConstTraitDetailPanel extends JPanel implements TraitDetail { - public JEditorPane slotConstEditor; + public LineMarkedEditorPane slotConstEditor; private ABC abc; @@ -62,7 +62,7 @@ public class SlotConstTraitDetailPanel extends JPanel implements TraitDetail { slotConstEditor = new LineMarkedEditorPane(); setLayout(new BorderLayout()); add(new JScrollPane(slotConstEditor), BorderLayout.CENTER); - slotConstEditor.setContentType("text/flasm3"); + slotConstEditor.changeContentType("text/flasm3"); slotConstEditor.addCaretListener(new CaretListener() { @Override public void caretUpdate(CaretEvent e) { diff --git a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index a6645b9f9..0852bcd76 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -1,882 +1,898 @@ -/* - * Copyright (C) 2010-2015 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.action; - -import com.jpexs.decompiler.flash.DisassemblyListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionGraph; -import com.jpexs.decompiler.flash.action.ActionList; -import com.jpexs.decompiler.flash.action.CachedScript; -import com.jpexs.decompiler.flash.action.parser.ActionParseException; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; -import com.jpexs.decompiler.flash.action.parser.script.ActionScriptLexer; -import com.jpexs.decompiler.flash.action.parser.script.ActionScriptParser; -import com.jpexs.decompiler.flash.action.parser.script.ParsedSymbol; -import com.jpexs.decompiler.flash.action.parser.script.SymbolType; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; -import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.gui.AppStrings; -import com.jpexs.decompiler.flash.gui.GraphDialog; -import com.jpexs.decompiler.flash.gui.HeaderLabel; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.MainPanel; -import com.jpexs.decompiler.flash.gui.SearchListener; -import com.jpexs.decompiler.flash.gui.SearchPanel; -import com.jpexs.decompiler.flash.gui.SearchResultsDialog; -import com.jpexs.decompiler.flash.gui.TagEditorPanel; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.gui.abc.LineMarkedEditorPane; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.controls.NoneSelectedButtonGroup; -import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.helpers.HighlightedText; -import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; -import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.CancellationException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JToggleButton; -import javax.swing.SwingConstants; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; -import javax.swing.tree.TreePath; -import jsyntaxpane.SyntaxDocument; -import jsyntaxpane.Token; -import jsyntaxpane.TokenType; -import jsyntaxpane.actions.ActionUtils; - -public class ActionPanel extends JPanel implements SearchListener, TagEditorPanel { - - private MainPanel mainPanel; - - public LineMarkedEditorPane editor; - - public LineMarkedEditorPane decompiledEditor; - - public JPersistentSplitPane splitPane; - - public JButton saveButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); - - public JButton editButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); - - public JButton cancelButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); - - public JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); - - public JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); - - public JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); - - public JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); - - public JToggleButton hexButton; - - public JToggleButton hexOnlyButton; - - public JToggleButton constantsViewButton; - - public JToggleButton resolveConstantsButton; - - public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); - - public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); - - public List decompiledHilights = new ArrayList<>(); - - public List disassembledHilights = new ArrayList<>(); - - private boolean ignoreCarret = false; - - private boolean editMode = false; - - private boolean editDecompiledMode = false; - - private ActionList lastCode; - - private ASMSource src; - - public JPanel topButtonsPan; - - private HighlightedText srcWithHex; - - private HighlightedText srcNoHex; - - private HighlightedText srcHexOnly; - - private HighlightedText srcConstants; - - private String lastDecompiled = ""; - - private ASMSource lastASM; - - public SearchPanel searchPanel; - - private CancellableWorker setSourceWorker; - - public void clearSource() { - lastCode = null; - lastASM = null; - lastDecompiled = null; - searchPanel.clear(); - src = null; - srcWithHex = null; - srcNoHex = null; - srcHexOnly = null; - srcConstants = null; - } - - public String getStringUnderCursor() { - int pos = decompiledEditor.getCaretPosition(); - - SyntaxDocument sDoc = ActionUtils.getSyntaxDocument(decompiledEditor); - if (sDoc != null) { - Token t = sDoc.getTokenAt(pos + 1); - String ident = null; - //It should be identifier or obfuscated identifier - if (t != null && (t.type == TokenType.IDENTIFIER || t.type == TokenType.REGEX)) { - CharSequence tData = t.getText(sDoc); - ident = tData.toString(); - //We need to get unescaped identifier, so we use our Lexer - ActionScriptLexer lex = new ActionScriptLexer(new StringReader(ident)); - try { - ParsedSymbol symb = lex.lex(); - if (symb.type == SymbolType.IDENTIFIER) { - ident = (String) symb.value; - } else { - ident = null; - } - } catch (IOException | ActionParseException ex) { - ident = null; - } - } - if (ident == null) { - Highlighting h = Highlighting.searchPos(decompiledHilights, pos); - if (h != null) { - List list = lastCode; - Action lastIns = null; - int inspos = 0; - Action selIns = null; - for (Action ins : list) { - if (h.getProperties().offset == ins.getOffset()) { - selIns = ins; - break; - } - if (ins.getOffset() > h.getProperties().offset) { - inspos = (int) (h.getProperties().offset - lastIns.getAddress()); - selIns = lastIns; - break; - } - lastIns = ins; - } - if (selIns != null) { - if (selIns instanceof ActionPush) { - ActionPush ap = (ActionPush) selIns; - Object var = ap.values.get(inspos - 1); - String identifier = null; - if (var instanceof String) { - identifier = (String) var; - } - if (var instanceof ConstantIndex) { - identifier = ap.constantPool.get(((ConstantIndex) var).index); - } - return identifier; - } - } - - } - } else { - return ident; - } - } - return null; - } - - public boolean search(final String txt, boolean ignoreCase, boolean regexp) { - if ((txt != null) && (!txt.isEmpty())) { - searchPanel.setOptions(ignoreCase, regexp); - SWF swf = mainPanel.getCurrentSwf(); - Map asms = swf.getASMs(false); - final List found = new ArrayList<>(); - Pattern pat; - if (regexp) { - pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } else { - pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } - int pos = 0; - for (Entry item : asms.entrySet()) { - pos++; - String workText = AppStrings.translate("work.searching"); - String decAdd = ""; - ASMSource asm = item.getValue(); - if (!SWF.isCached(asm)) { - decAdd = ", " + AppStrings.translate("work.decompiling"); - } - Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + asms.size() + ") " + item.getKey() + "... "); - try { - if (pat.matcher(SWF.getCached(asm, null).text).find()) { - found.add(new ActionSearchResult(asm, item.getKey())); - } - } catch (InterruptedException ex) { - break; - } - } - - Main.stopWork(); - searchPanel.setSearchText(txt); - View.execInEventDispatch(() -> { - SearchResultsDialog sr = new SearchResultsDialog<>(ActionPanel.this.mainPanel.getMainFrame().getWindow(), txt, ActionPanel.this); - sr.setResults(found); - sr.setVisible(true); - }); - return true; - //return searchPanel.setResults(found); - } - return false; - } - - private void setDecompiledText(final String text) { - View.execInEventDispatch(() -> { - ignoreCarret = true; - decompiledEditor.setText(text); - ignoreCarret = false; - }); - } - - private void setEditorText(final String text, final String contentType) { - View.execInEventDispatch(() -> { - ignoreCarret = true; - editor.setContentType(contentType); - editor.setText(text); - ignoreCarret = false; - }); - } - - private void setText(final HighlightedText text, final String contentType) { - View.execInEventDispatch(() -> { - int pos = editor.getCaretPosition(); - Highlighting lastH = null; - for (Highlighting h : disassembledHilights) { - if (pos < h.startPos) { - break; - } - lastH = h; - } - Long offset = lastH == null ? 0 : lastH.getProperties().offset; - disassembledHilights = text.instructionHilights; - String stripped = text.text; - setEditorText(stripped, contentType); - Highlighting h = Highlighting.searchOffset(disassembledHilights, offset); - if (h != null) { - if (h.startPos <= editor.getDocument().getLength()) { - editor.setCaretPosition(h.startPos); - } - } - }); - } - - private HighlightedText getHighlightedText(ScriptExportMode exportMode) { - ASMSource asm = (ASMSource) src; - DisassemblyListener listener = getDisassemblyListener(); - asm.addDisassemblyListener(listener); - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - try { - asm.getASMSource(exportMode, writer, lastCode); - } catch (InterruptedException ex) { - Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); - } - asm.removeDisassemblyListener(listener); - return new HighlightedText(writer); - } - - public void setHex(ScriptExportMode exportMode) { - switch (exportMode) { - case PCODE: - if (srcNoHex == null) { - srcNoHex = getHighlightedText(exportMode); - } - - setText(srcNoHex, "text/flasm"); - break; - case PCODE_HEX: - if (srcWithHex == null) { - srcWithHex = getHighlightedText(exportMode); - } - - setText(srcWithHex, "text/flasm"); - break; - case HEX: - if (srcHexOnly == null) { - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - Helper.byteArrayToHexWithHeader(writer, src.getActionBytes()); - srcHexOnly = new HighlightedText(writer); - } - - setText(srcHexOnly, "text/plain"); - break; - case CONSTANTS: - if (srcConstants == null) { - srcConstants = getHighlightedText(exportMode); - } - - setText(srcConstants, "text/plain"); - break; - default: - throw new Error("Export mode not supported: " + exportMode); - } - } - - private DisassemblyListener getDisassemblyListener() { - DisassemblyListener listener = new DisassemblyListener() { - int percent = 0; - - String phase = ""; - - private void progress(String phase, long pos, long total) { - if (total < 1) { - return; - } - int newpercent = (int) (pos * 100 / total); - if (((newpercent > percent) || (!this.phase.equals(phase))) && newpercent <= 100) { - percent = newpercent; - this.phase = phase; - // todo: honfika: it is very slow to show every percent - setEditorText("; " + AppStrings.translate("work.disassembling") + " - " + phase + " " + percent + "%...", "text/flasm"); - } - } - - @Override - public void progressReading(long pos, long total) { - progress(AppStrings.translate("disassemblingProgress.reading"), pos, total); - } - - @Override - public void progressToString(long pos, long total) { - progress(AppStrings.translate("disassemblingProgress.toString"), pos, total); - } - - @Override - public void progressDeobfuscating(long pos, long total) { - progress(AppStrings.translate("disassemblingProgress.deobfuscating"), pos, total); - } - }; - return listener; - } - - public void setSource(final ASMSource src, final boolean useCache) { - if (setSourceWorker != null) { - setSourceWorker.cancel(true); - setSourceWorker = null; - } - - this.src = src; - final ASMSource asm = (ASMSource) src; - - CancellableWorker worker = new CancellableWorker() { - - @Override - protected Void doInBackground() throws Exception { - setEditorText("; " + AppStrings.translate("work.disassembling") + "...", "text/flasm"); - if (Configuration.decompile.get()) { - setDecompiledText("// " + AppStrings.translate("work.waitingfordissasembly") + "..."); - } else { - lastDecompiled = Helper.getDecompilationSkippedComment(); - setDecompiledText(lastDecompiled); - } - DisassemblyListener listener = getDisassemblyListener(); - asm.addDisassemblyListener(listener); - ActionList actions = asm.getActions(); - lastCode = actions; - asm.removeDisassemblyListener(listener); - srcWithHex = null; - srcNoHex = null; - srcHexOnly = null; - srcConstants = null; - setHex(getExportMode()); - if (Configuration.decompile.get()) { - setDecompiledText("// " + AppStrings.translate("work.decompiling") + "..."); - if (!useCache) { - SWF.uncache(asm); - } - - CachedScript sc = SWF.getCached(asm, actions); - decompiledHilights = sc.hilights; - lastDecompiled = sc.text; - lastASM = asm; - setDecompiledText(lastDecompiled); - } - setEditMode(false); - setDecompiledEditMode(false); - return null; - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - setSourceWorker = null; - Main.stopWork(); - - try { - get(); - } catch (CancellationException ex) { - setEditorText("; " + AppStrings.translate("work.canceled"), "text/flasm"); - } catch (Exception ex) { - setDecompiledText("// " + AppStrings.translate("decompilationError") + ": " + ex); - } - }); - } - }; - worker.execute(); - setSourceWorker = worker; - Main.startWork(AppStrings.translate("work.decompiling") + "...", worker); - } - - public void hilightOffset(long offset) { - } - - public ActionPanel(MainPanel mainPanel) { - this.mainPanel = mainPanel; - editor = new LineMarkedEditorPane(); - editor.setEditable(false); - decompiledEditor = new LineMarkedEditorPane(); - decompiledEditor.setEditable(false); - - searchPanel = new SearchPanel<>(new FlowLayout(), this); - - JButton graphButton = new JButton(View.getIcon("graph16")); - graphButton.addActionListener(this::graphButtonActionPerformed); - graphButton.setToolTipText(AppStrings.translate("button.viewgraph")); - graphButton.setMargin(new Insets(3, 3, 3, 3)); - - hexButton = new JToggleButton(View.getIcon("hexas16")); - hexButton.addActionListener(this::hexButtonActionPerformed); - hexButton.setToolTipText(AppStrings.translate("button.viewhex")); - hexButton.setMargin(new Insets(3, 3, 3, 3)); - - hexOnlyButton = new JToggleButton(View.getIcon("hex16")); - hexOnlyButton.addActionListener(this::hexOnlyButtonActionPerformed); - hexOnlyButton.setToolTipText(AppStrings.translate("button.viewhex")); - hexOnlyButton.setMargin(new Insets(3, 3, 3, 3)); - - // todo: change icon - constantsViewButton = new JToggleButton(View.getIcon("constantpool16")); - constantsViewButton.addActionListener(this::constantsViewButtonActionPerformed); - constantsViewButton.setToolTipText(AppStrings.translate("button.viewConstants")); - constantsViewButton.setMargin(new Insets(3, 3, 3, 3)); - - NoneSelectedButtonGroup exportModeButtonGroup = new NoneSelectedButtonGroup(); - exportModeButtonGroup.add(hexButton); - exportModeButtonGroup.add(hexOnlyButton); - exportModeButtonGroup.add(constantsViewButton); - - resolveConstantsButton = new JToggleButton(View.getIcon("constantpool16")); - resolveConstantsButton.addActionListener(this::resolveConstantsButtonActionPerformed); - resolveConstantsButton.setToolTipText(AppStrings.translate("button.resolveConstants")); - resolveConstantsButton.setMargin(new Insets(3, 3, 3, 3)); - resolveConstantsButton.setSelected(Configuration.resolveConstants.get()); - - topButtonsPan = new JPanel(); - topButtonsPan.setLayout(new BoxLayout(topButtonsPan, BoxLayout.X_AXIS)); - topButtonsPan.add(graphButton); - topButtonsPan.add(hexButton); - topButtonsPan.add(hexOnlyButton); - topButtonsPan.add(constantsViewButton); - topButtonsPan.add(Box.createRigidArea(new Dimension(10, 0))); - topButtonsPan.add(resolveConstantsButton); - - JPanel panCode = new JPanel(new BorderLayout()); - panCode.add(new JScrollPane(editor), BorderLayout.CENTER); - panCode.add(topButtonsPan, BorderLayout.NORTH); - - JPanel panB = new JPanel(); - panB.setLayout(new BorderLayout()); - asmLabel.setHorizontalAlignment(SwingConstants.CENTER); - //asmLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - panB.add(asmLabel, BorderLayout.NORTH); - panB.add(panCode, BorderLayout.CENTER); - - JPanel buttonsPan = new JPanel(); - buttonsPan.setLayout(new FlowLayout()); - buttonsPan.add(editButton); - buttonsPan.add(saveButton); - buttonsPan.add(cancelButton); - - editButton.setMargin(new Insets(3, 3, 3, 10)); - saveButton.setMargin(new Insets(3, 3, 3, 10)); - cancelButton.setMargin(new Insets(3, 3, 3, 10)); - - JPanel decButtonsPan = new JPanel(new FlowLayout()); - decButtonsPan.add(editDecompiledButton); - decButtonsPan.add(experimentalLabel); - decButtonsPan.add(saveDecompiledButton); - decButtonsPan.add(cancelDecompiledButton); - - editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - - //buttonsPan.add(saveHexButton); - //buttonsPan.add(loadHexButton); - panB.add(buttonsPan, BorderLayout.SOUTH); - - saveButton.addActionListener(this::saveActionButtonActionPerformed); - editButton.addActionListener(this::editActionButtonActionPerformed); - cancelButton.addActionListener(this::cancelActionButtonActionPerformed); - saveButton.setVisible(false); - cancelButton.setVisible(false); - - saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed); - editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed); - cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed); - saveDecompiledButton.setVisible(false); - cancelDecompiledButton.setVisible(false); - - JPanel decPanel = new JPanel(new BorderLayout()); - decPanel.add(new JScrollPane(decompiledEditor), BorderLayout.CENTER); - //decPanel.add(searchPanel, BorderLayout.NORTH); - - JPanel panA = new JPanel(); - panA.setLayout(new BorderLayout()); - panA.add(decPanel, BorderLayout.CENTER); - panA.add(decLabel, BorderLayout.NORTH); - panA.add(decButtonsPan, BorderLayout.SOUTH); - decLabel.setHorizontalAlignment(SwingConstants.CENTER); - //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - setLayout(new BorderLayout()); - add(splitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, panA, panB, Configuration.guiActionSplitPaneDividerLocationPercent), BorderLayout.CENTER); - - editor.setFont(new Font("Monospaced", Font.PLAIN, editor.getFont().getSize())); - decompiledEditor.setFont(new Font("Monospaced", Font.PLAIN, decompiledEditor.getFont().getSize())); - decompiledEditor.setContentType("text/actionscript"); - - //tagTree.addTreeSelectionListener(this); - editor.addCaretListener(new CaretListener() { - @Override - public void caretUpdate(CaretEvent e) { - if (ignoreCarret) { - return; - } - if (editMode || editDecompiledMode) { - return; - } - editor.getCaret().setVisible(true); - int pos = editor.getCaretPosition(); - Highlighting lastH = null; - for (Highlighting h : disassembledHilights) { - if (pos < h.startPos) { - break; - } - lastH = h; - } - Long ofs = lastH == null ? 0 : lastH.getProperties().offset; - Highlighting h2 = Highlighting.searchOffset(decompiledHilights, ofs); - if (h2 != null) { - ignoreCarret = true; - if (h2.startPos <= decompiledEditor.getDocument().getLength()) { - decompiledEditor.setCaretPosition(h2.startPos); - } - decompiledEditor.getCaret().setVisible(true); - ignoreCarret = false; - - } - } - }); - decompiledEditor.addCaretListener(new CaretListener() { - @Override - public void caretUpdate(CaretEvent e) { - if (ignoreCarret) { - return; - } - if (editMode || editDecompiledMode) { - return; - } - decompiledEditor.getCaret().setVisible(true); - int pos = decompiledEditor.getCaretPosition(); - Highlighting h = Highlighting.searchPos(decompiledHilights, pos); - if (h != null) { - Highlighting h2 = Highlighting.searchOffset(disassembledHilights, h.getProperties().offset); - if (h2 != null) { - ignoreCarret = true; - if (h2.startPos > 0 && h2.startPos < editor.getText().length()) { - editor.setCaretPosition(h2.startPos); - } - editor.getCaret().setVisible(true); - ignoreCarret = false; - } - } - } - }); - } - - public void setEditMode(boolean val) { - if (val) { - if (hexOnlyButton.isSelected()) { - setHex(ScriptExportMode.HEX); - } else if (constantsViewButton.isSelected()) { - setHex(ScriptExportMode.CONSTANTS); - } else { - setHex(ScriptExportMode.PCODE); - } - - editor.setEditable(true); - saveButton.setVisible(true); - editButton.setVisible(false); - cancelButton.setVisible(true); - editor.getCaret().setVisible(true); - asmLabel.setIcon(View.getIcon("editing16")); - } else { - setHex(getExportMode()); - editor.setEditable(false); - saveButton.setVisible(false); - editButton.setVisible(true); - cancelButton.setVisible(false); - editor.getCaret().setVisible(true); - asmLabel.setIcon(null); - } - - topButtonsPan.setVisible(!val); - editMode = val; - editor.requestFocusInWindow(); - } - - public void setDecompiledEditMode(boolean val) { - if (lastASM == null) { - return; - } - - int lastLine = decompiledEditor.getLine(); - int prefLines = lastASM.getPrefixLineCount(); - if (val) { - String newText = lastASM.removePrefixAndSuffix(lastDecompiled); - setDecompiledText(newText); - if (lastLine > -1) { - if (lastLine - prefLines >= 0) { - decompiledEditor.gotoLine(lastLine - prefLines + 1); - } - } - decompiledEditor.setEditable(true); - saveDecompiledButton.setVisible(true); - editDecompiledButton.setVisible(false); - experimentalLabel.setVisible(false); - cancelDecompiledButton.setVisible(true); - decompiledEditor.getCaret().setVisible(true); - decLabel.setIcon(View.getIcon("editing16")); - } else { - String newText = lastDecompiled; - setDecompiledText(newText); - if (lastLine > -1) { - decompiledEditor.gotoLine(lastLine + prefLines + 1); - } - decompiledEditor.setEditable(false); - saveDecompiledButton.setVisible(false); - editDecompiledButton.setVisible(true); - experimentalLabel.setVisible(true); - cancelDecompiledButton.setVisible(false); - decompiledEditor.getCaret().setVisible(true); - decLabel.setIcon(null); - } - editDecompiledMode = val; - decompiledEditor.requestFocusInWindow(); - } - - private void graphButtonActionPerformed(ActionEvent evt) { - if (lastCode != null) { - try { - GraphDialog gf = new GraphDialog(mainPanel.getMainFrame().getWindow(), new ActionGraph(lastCode, new HashMap<>(), new HashMap<>(), new HashMap<>(), SWF.DEFAULT_VERSION), ""); - gf.setVisible(true); - } catch (InterruptedException ex) { - Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - private void editActionButtonActionPerformed(ActionEvent evt) { - setEditMode(true); - } - - private void hexButtonActionPerformed(ActionEvent evt) { - setHex(getExportMode()); - } - - private void hexOnlyButtonActionPerformed(ActionEvent evt) { - setHex(getExportMode()); - } - - private void constantsViewButtonActionPerformed(ActionEvent evt) { - setHex(getExportMode()); - } - - private void resolveConstantsButtonActionPerformed(ActionEvent evt) { - boolean resolve = resolveConstantsButton.isSelected(); - Configuration.resolveConstants.set(resolve); - - srcWithHex = null; - srcNoHex = null; - // srcHexOnly = null; is not needed since it does not contains the resolved constant names - setHex(getExportMode()); - } - - private void cancelActionButtonActionPerformed(ActionEvent evt) { - setEditMode(false); - setHex(getExportMode()); - } - - private void saveActionButtonActionPerformed(ActionEvent evt) { - try { - String text = editor.getText(); - String trimmed = text.trim(); - if (trimmed.startsWith(Helper.hexData)) { - src.setActionBytes(Helper.getBytesFromHexaText(text)); - } else if (trimmed.startsWith(Helper.constants)) { - setConstantPools(Helper.getConstantPoolsFromText(text)); - } else { - src.setActions(ASMParser.parse(0, true, text, src.getSwf().version, false)); - } - - SWF.uncache(src); - src.setModified(); - mainPanel.refreshTree(src.getSwf()); - setSource(this.src, false); - View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); - saveButton.setVisible(false); - cancelButton.setVisible(false); - editButton.setVisible(true); - editor.setEditable(false); - editMode = false; - } catch (IOException ex) { - } catch (ActionParseException ex) { - editor.gotoLine((int) ex.line); - editor.markError(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - } - - private void setConstantPools(List> constantPools) { - try { - ActionList actions = src.getActions(); - int poolIdx = 0; - for (Action action : actions) { - if (action instanceof ActionConstantPool) { - ActionConstantPool cPool = (ActionConstantPool) action; - List constantPool = constantPools.get(poolIdx); - cPool.constantPool = constantPool; - - poolIdx++; - if (constantPools.size() <= poolIdx) { - break; - } - } - } - - src.setActions(actions); - } catch (InterruptedException ex) { - Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - - private void editDecompiledButtonActionPerformed(ActionEvent evt) { - if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS12Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { - setDecompiledEditMode(true); - } - } - - private void cancelDecompiledButtonActionPerformed(ActionEvent evt) { - setDecompiledEditMode(false); - } - - private void saveDecompiledButtonActionPerformed(ActionEvent evt) { - try { - ActionScriptParser par = new ActionScriptParser(mainPanel.getCurrentSwf().version); - src.setActions(par.actionsFromString(decompiledEditor.getText())); - src.setModified(); - setSource(src, false); - - View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); - setDecompiledEditMode(false); - } catch (IOException ex) { - Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, "IOException during action compiling", ex); - } catch (ActionParseException ex) { - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } catch (CompilationException ex) { - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - } - - private ScriptExportMode getExportMode() { - ScriptExportMode exportMode = hexOnlyButton.isSelected() ? ScriptExportMode.HEX - : hexButton.isSelected() ? ScriptExportMode.PCODE_HEX - : constantsViewButton.isSelected() ? ScriptExportMode.CONSTANTS - : ScriptExportMode.PCODE; - return exportMode; - } - - @Override - public void updateSearchPos(ActionSearchResult item) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreePath tp = ttm.getTreePath(item.src); - mainPanel.tagTree.setSelectionPath(tp); - mainPanel.tagTree.scrollPathToVisible(tp); - decompiledEditor.setCaretPosition(0); - - View.execInEventDispatchLater(() -> { - searchPanel.showQuickFindDialog(decompiledEditor); - }); - } - - @Override - public boolean tryAutoSave() { - // todo: implement - return false; - } - - @Override - public boolean isEditing() { - return (saveButton.isVisible() && saveButton.isEnabled()) - || (saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled()); - } -} +/* + * Copyright (C) 2010-2015 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.action; + +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionGraph; +import com.jpexs.decompiler.flash.action.ActionList; +import com.jpexs.decompiler.flash.action.CachedScript; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; +import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; +import com.jpexs.decompiler.flash.action.parser.script.ActionScriptLexer; +import com.jpexs.decompiler.flash.action.parser.script.ActionScriptParser; +import com.jpexs.decompiler.flash.action.parser.script.ParsedSymbol; +import com.jpexs.decompiler.flash.action.parser.script.SymbolType; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.GraphDialog; +import com.jpexs.decompiler.flash.gui.HeaderLabel; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.MainPanel; +import com.jpexs.decompiler.flash.gui.SearchListener; +import com.jpexs.decompiler.flash.gui.SearchPanel; +import com.jpexs.decompiler.flash.gui.SearchResultsDialog; +import com.jpexs.decompiler.flash.gui.TagEditorPanel; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.controls.NoneSelectedButtonGroup; +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; +import com.jpexs.decompiler.flash.helpers.HighlightedText; +import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; +import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CancellationException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JToggleButton; +import javax.swing.SwingConstants; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.tree.TreePath; +import jsyntaxpane.SyntaxDocument; +import jsyntaxpane.Token; +import jsyntaxpane.TokenType; +import jsyntaxpane.actions.ActionUtils; + +public class ActionPanel extends JPanel implements SearchListener, TagEditorPanel { + + private MainPanel mainPanel; + + public LineMarkedEditorPane editor; + + public LineMarkedEditorPane decompiledEditor; + + public JPersistentSplitPane splitPane; + + public JButton saveButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); + + public JButton editButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); + + public JButton cancelButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); + + public JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); + + public JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); + + public JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); + + public JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); + + public JToggleButton hexButton; + + public JToggleButton hexOnlyButton; + + public JToggleButton constantsViewButton; + + public JToggleButton resolveConstantsButton; + + public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); + + public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); + + public List decompiledHilights = new ArrayList<>(); + + public List disassembledHilights = new ArrayList<>(); + + private boolean ignoreCarret = false; + + private boolean editMode = false; + + private boolean editDecompiledMode = false; + + private ActionList lastCode; + + private ASMSource src; + + public JPanel topButtonsPan; + + private HighlightedText srcWithHex; + + private HighlightedText srcNoHex; + + private HighlightedText srcHexOnly; + + private HighlightedText srcConstants; + + private String lastDecompiled = ""; + + private ASMSource lastASM; + + public SearchPanel searchPanel; + + private CancellableWorker setSourceWorker; + + public void clearSource() { + lastCode = null; + lastASM = null; + lastDecompiled = null; + searchPanel.clear(); + src = null; + srcWithHex = null; + srcNoHex = null; + srcHexOnly = null; + srcConstants = null; + } + + public String getStringUnderCursor() { + int pos = decompiledEditor.getCaretPosition(); + + SyntaxDocument sDoc = ActionUtils.getSyntaxDocument(decompiledEditor); + if (sDoc != null) { + Token t = sDoc.getTokenAt(pos + 1); + String ident = null; + //It should be identifier or obfuscated identifier + if (t != null && (t.type == TokenType.IDENTIFIER || t.type == TokenType.REGEX)) { + CharSequence tData = t.getText(sDoc); + ident = tData.toString(); + //We need to get unescaped identifier, so we use our Lexer + ActionScriptLexer lex = new ActionScriptLexer(new StringReader(ident)); + try { + ParsedSymbol symb = lex.lex(); + if (symb.type == SymbolType.IDENTIFIER) { + ident = (String) symb.value; + } else { + ident = null; + } + } catch (IOException | ActionParseException ex) { + ident = null; + } + } + if (ident == null) { + Highlighting h = Highlighting.searchPos(decompiledHilights, pos); + if (h != null) { + List list = lastCode; + Action lastIns = null; + int inspos = 0; + Action selIns = null; + for (Action ins : list) { + if (h.getProperties().offset == ins.getOffset()) { + selIns = ins; + break; + } + if (ins.getOffset() > h.getProperties().offset) { + inspos = (int) (h.getProperties().offset - lastIns.getAddress()); + selIns = lastIns; + break; + } + lastIns = ins; + } + if (selIns != null) { + if (selIns instanceof ActionPush) { + ActionPush ap = (ActionPush) selIns; + Object var = ap.values.get(inspos - 1); + String identifier = null; + if (var instanceof String) { + identifier = (String) var; + } + if (var instanceof ConstantIndex) { + identifier = ap.constantPool.get(((ConstantIndex) var).index); + } + return identifier; + } + } + + } + } else { + return ident; + } + } + return null; + } + + public boolean search(final String txt, boolean ignoreCase, boolean regexp) { + if ((txt != null) && (!txt.isEmpty())) { + searchPanel.setOptions(ignoreCase, regexp); + SWF swf = mainPanel.getCurrentSwf(); + Map asms = swf.getASMs(false); + final List found = new ArrayList<>(); + Pattern pat; + if (regexp) { + pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } else { + pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } + int pos = 0; + for (Entry item : asms.entrySet()) { + pos++; + String workText = AppStrings.translate("work.searching"); + String decAdd = ""; + ASMSource asm = item.getValue(); + if (!SWF.isCached(asm)) { + decAdd = ", " + AppStrings.translate("work.decompiling"); + } + Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + asms.size() + ") " + item.getKey() + "... "); + try { + if (pat.matcher(SWF.getCached(asm, null).text).find()) { + found.add(new ActionSearchResult(asm, item.getKey())); + } + } catch (InterruptedException ex) { + break; + } + } + + Main.stopWork(); + searchPanel.setSearchText(txt); + View.execInEventDispatch(() -> { + SearchResultsDialog sr = new SearchResultsDialog<>(ActionPanel.this.mainPanel.getMainFrame().getWindow(), txt, ActionPanel.this); + sr.setResults(found); + sr.setVisible(true); + }); + return true; + //return searchPanel.setResults(found); + } + return false; + } + + private void setDecompiledText(final String text) { + View.execInEventDispatch(() -> { + ignoreCarret = true; + decompiledEditor.setText(text); + ignoreCarret = false; + }); + } + + private void setEditorText(final String text, final String contentType) { + View.execInEventDispatch(() -> { + ignoreCarret = true; + editor.changeContentType(contentType); + editor.setText(text); + ignoreCarret = false; + }); + } + + private void setText(final HighlightedText text, final String contentType) { + View.execInEventDispatch(() -> { + int pos = editor.getCaretPosition(); + Highlighting lastH = null; + for (Highlighting h : disassembledHilights) { + if (pos < h.startPos) { + break; + } + lastH = h; + } + Long offset = lastH == null ? 0 : lastH.getProperties().offset; + disassembledHilights = text.instructionHilights; + String stripped = text.text; + setEditorText(stripped, contentType); + Highlighting h = Highlighting.searchOffset(disassembledHilights, offset); + if (h != null) { + if (h.startPos <= editor.getDocument().getLength()) { + editor.setCaretPosition(h.startPos); + } + } + }); + } + + private HighlightedText getHighlightedText(ScriptExportMode exportMode) { + ASMSource asm = (ASMSource) src; + DisassemblyListener listener = getDisassemblyListener(); + asm.addDisassemblyListener(listener); + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + try { + asm.getASMSource(exportMode, writer, lastCode); + } catch (InterruptedException ex) { + Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); + } + asm.removeDisassemblyListener(listener); + return new HighlightedText(writer); + } + + public void setHex(ScriptExportMode exportMode) { + switch (exportMode) { + case PCODE: + if (srcNoHex == null) { + srcNoHex = getHighlightedText(exportMode); + } + + setText(srcNoHex, "text/flasm"); + break; + case PCODE_HEX: + if (srcWithHex == null) { + srcWithHex = getHighlightedText(exportMode); + } + + setText(srcWithHex, "text/flasm"); + break; + case HEX: + if (srcHexOnly == null) { + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + Helper.byteArrayToHexWithHeader(writer, src.getActionBytes()); + srcHexOnly = new HighlightedText(writer); + } + + setText(srcHexOnly, "text/plain"); + break; + case CONSTANTS: + if (srcConstants == null) { + srcConstants = getHighlightedText(exportMode); + } + + setText(srcConstants, "text/plain"); + break; + default: + throw new Error("Export mode not supported: " + exportMode); + } + } + + private DisassemblyListener getDisassemblyListener() { + DisassemblyListener listener = new DisassemblyListener() { + int percent = 0; + + String phase = ""; + + private void progress(String phase, long pos, long total) { + if (total < 1) { + return; + } + int newpercent = (int) (pos * 100 / total); + if (((newpercent > percent) || (!this.phase.equals(phase))) && newpercent <= 100) { + percent = newpercent; + this.phase = phase; + // todo: honfika: it is very slow to show every percent + setEditorText("; " + AppStrings.translate("work.disassembling") + " - " + phase + " " + percent + "%...", "text/flasm"); + } + } + + @Override + public void progressReading(long pos, long total) { + progress(AppStrings.translate("disassemblingProgress.reading"), pos, total); + } + + @Override + public void progressToString(long pos, long total) { + progress(AppStrings.translate("disassemblingProgress.toString"), pos, total); + } + + @Override + public void progressDeobfuscating(long pos, long total) { + progress(AppStrings.translate("disassemblingProgress.deobfuscating"), pos, total); + } + }; + return listener; + } + + public void setSource(final ASMSource src, final boolean useCache) { + if (setSourceWorker != null) { + setSourceWorker.cancel(true); + setSourceWorker = null; + } + + this.src = src; + final ASMSource asm = (ASMSource) src; + + CancellableWorker worker = new CancellableWorker() { + + @Override + protected Void doInBackground() throws Exception { + setEditorText("; " + AppStrings.translate("work.disassembling") + "...", "text/flasm"); + if (Configuration.decompile.get()) { + setDecompiledText("// " + AppStrings.translate("work.waitingfordissasembly") + "..."); + } else { + lastDecompiled = Helper.getDecompilationSkippedComment(); + setDecompiledText(lastDecompiled); + } + DisassemblyListener listener = getDisassemblyListener(); + asm.addDisassemblyListener(listener); + ActionList actions = asm.getActions(); + lastCode = actions; + asm.removeDisassemblyListener(listener); + srcWithHex = null; + srcNoHex = null; + srcHexOnly = null; + srcConstants = null; + setHex(getExportMode()); + if (Configuration.decompile.get()) { + setDecompiledText("// " + AppStrings.translate("work.decompiling") + "..."); + if (!useCache) { + SWF.uncache(asm); + } + + CachedScript sc = SWF.getCached(asm, actions); + decompiledHilights = sc.hilights; + lastDecompiled = sc.text; + lastASM = asm; + setDecompiledText(lastDecompiled); + } + setEditMode(false); + setDecompiledEditMode(false); + return null; + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + setSourceWorker = null; + Main.stopWork(); + + try { + get(); + } catch (CancellationException ex) { + setEditorText("; " + AppStrings.translate("work.canceled"), "text/flasm"); + } catch (Exception ex) { + setDecompiledText("// " + AppStrings.translate("decompilationError") + ": " + ex); + } + }); + } + }; + worker.execute(); + setSourceWorker = worker; + Main.startWork(AppStrings.translate("work.decompiling") + "...", worker); + } + + public void hilightOffset(long offset) { + } + + public ActionPanel(MainPanel mainPanel) { + this.mainPanel = mainPanel; + editor = new LineMarkedEditorPane(); + editor.setEditable(false); + decompiledEditor = new LineMarkedEditorPane(); + decompiledEditor.setEditable(false); + + searchPanel = new SearchPanel<>(new FlowLayout(), this); + + JButton graphButton = new JButton(View.getIcon("graph16")); + graphButton.addActionListener(this::graphButtonActionPerformed); + graphButton.setToolTipText(AppStrings.translate("button.viewgraph")); + graphButton.setMargin(new Insets(3, 3, 3, 3)); + + hexButton = new JToggleButton(View.getIcon("hexas16")); + hexButton.addActionListener(this::hexButtonActionPerformed); + hexButton.setToolTipText(AppStrings.translate("button.viewhex")); + hexButton.setMargin(new Insets(3, 3, 3, 3)); + + hexOnlyButton = new JToggleButton(View.getIcon("hex16")); + hexOnlyButton.addActionListener(this::hexOnlyButtonActionPerformed); + hexOnlyButton.setToolTipText(AppStrings.translate("button.viewhex")); + hexOnlyButton.setMargin(new Insets(3, 3, 3, 3)); + + // todo: change icon + constantsViewButton = new JToggleButton(View.getIcon("constantpool16")); + constantsViewButton.addActionListener(this::constantsViewButtonActionPerformed); + constantsViewButton.setToolTipText(AppStrings.translate("button.viewConstants")); + constantsViewButton.setMargin(new Insets(3, 3, 3, 3)); + + NoneSelectedButtonGroup exportModeButtonGroup = new NoneSelectedButtonGroup(); + exportModeButtonGroup.add(hexButton); + exportModeButtonGroup.add(hexOnlyButton); + exportModeButtonGroup.add(constantsViewButton); + + resolveConstantsButton = new JToggleButton(View.getIcon("constantpool16")); + resolveConstantsButton.addActionListener(this::resolveConstantsButtonActionPerformed); + resolveConstantsButton.setToolTipText(AppStrings.translate("button.resolveConstants")); + resolveConstantsButton.setMargin(new Insets(3, 3, 3, 3)); + resolveConstantsButton.setSelected(Configuration.resolveConstants.get()); + + topButtonsPan = new JPanel(); + topButtonsPan.setLayout(new BoxLayout(topButtonsPan, BoxLayout.X_AXIS)); + topButtonsPan.add(graphButton); + topButtonsPan.add(hexButton); + topButtonsPan.add(hexOnlyButton); + topButtonsPan.add(constantsViewButton); + topButtonsPan.add(Box.createRigidArea(new Dimension(10, 0))); + topButtonsPan.add(resolveConstantsButton); + + JPanel panCode = new JPanel(new BorderLayout()); + panCode.add(new JScrollPane(editor), BorderLayout.CENTER); + panCode.add(topButtonsPan, BorderLayout.NORTH); + + JPanel panB = new JPanel(); + panB.setLayout(new BorderLayout()); + asmLabel.setHorizontalAlignment(SwingConstants.CENTER); + //asmLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + panB.add(asmLabel, BorderLayout.NORTH); + panB.add(panCode, BorderLayout.CENTER); + + JPanel buttonsPan = new JPanel(); + buttonsPan.setLayout(new FlowLayout()); + buttonsPan.add(editButton); + buttonsPan.add(saveButton); + buttonsPan.add(cancelButton); + + editButton.setMargin(new Insets(3, 3, 3, 10)); + saveButton.setMargin(new Insets(3, 3, 3, 10)); + cancelButton.setMargin(new Insets(3, 3, 3, 10)); + + JPanel decButtonsPan = new JPanel(new FlowLayout()); + decButtonsPan.add(editDecompiledButton); + decButtonsPan.add(experimentalLabel); + decButtonsPan.add(saveDecompiledButton); + decButtonsPan.add(cancelDecompiledButton); + + editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + + //buttonsPan.add(saveHexButton); + //buttonsPan.add(loadHexButton); + panB.add(buttonsPan, BorderLayout.SOUTH); + + saveButton.addActionListener(this::saveActionButtonActionPerformed); + editButton.addActionListener(this::editActionButtonActionPerformed); + cancelButton.addActionListener(this::cancelActionButtonActionPerformed); + saveButton.setVisible(false); + cancelButton.setVisible(false); + + saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed); + editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed); + cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed); + saveDecompiledButton.setVisible(false); + cancelDecompiledButton.setVisible(false); + + JPanel decPanel = new JPanel(new BorderLayout()); + decPanel.add(new JScrollPane(decompiledEditor), BorderLayout.CENTER); + //decPanel.add(searchPanel, BorderLayout.NORTH); + + JPanel panA = new JPanel(); + panA.setLayout(new BorderLayout()); + panA.add(decPanel, BorderLayout.CENTER); + panA.add(decLabel, BorderLayout.NORTH); + panA.add(decButtonsPan, BorderLayout.SOUTH); + decLabel.setHorizontalAlignment(SwingConstants.CENTER); + //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + setLayout(new BorderLayout()); + add(splitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, panA, panB, Configuration.guiActionSplitPaneDividerLocationPercent), BorderLayout.CENTER); + + editor.setFont(new Font("Monospaced", Font.PLAIN, editor.getFont().getSize())); + decompiledEditor.setFont(new Font("Monospaced", Font.PLAIN, decompiledEditor.getFont().getSize())); + decompiledEditor.changeContentType("text/actionscript"); + + editor.addCaretListener(new CaretListener() { + @Override + public void caretUpdate(CaretEvent e) { + if (ignoreCarret) { + return; + } + if (editMode || editDecompiledMode) { + return; + } + editor.getCaret().setVisible(true); + int pos = editor.getCaretPosition(); + Highlighting lastH = null; + for (Highlighting h : disassembledHilights) { + if (pos < h.startPos) { + break; + } + lastH = h; + } + Long ofs = lastH == null ? 0 : lastH.getProperties().offset; + Highlighting h2 = Highlighting.searchOffset(decompiledHilights, ofs); + if (h2 != null) { + ignoreCarret = true; + if (h2.startPos <= decompiledEditor.getDocument().getLength()) { + decompiledEditor.setCaretPosition(h2.startPos); + } + decompiledEditor.getCaret().setVisible(true); + ignoreCarret = false; + + } + } + }); + + decompiledEditor.addCaretListener(new CaretListener() { + @Override + public void caretUpdate(CaretEvent e) { + if (ignoreCarret) { + return; + } + if (editMode || editDecompiledMode) { + return; + } + decompiledEditor.getCaret().setVisible(true); + int pos = decompiledEditor.getCaretPosition(); + Highlighting h = Highlighting.searchPos(decompiledHilights, pos); + if (h != null) { + Highlighting h2 = Highlighting.searchOffset(disassembledHilights, h.getProperties().offset); + if (h2 != null) { + ignoreCarret = true; + if (h2.startPos > 0 && h2.startPos < editor.getText().length()) { + editor.setCaretPosition(h2.startPos); + } + editor.getCaret().setVisible(true); + ignoreCarret = false; + } + } + } + }); + + editor.addTextChangedListener(this::editorTextChanged); + decompiledEditor.addTextChangedListener(this::decompiledEditorTextChanged); + } + + private void editorTextChanged() { + setModified(true); + } + + private void decompiledEditorTextChanged() { + setDecompiledModified(true); + } + + private boolean isModified() { + return saveButton.isVisible() && saveButton.isEnabled(); + } + + private void setModified(boolean value) { + saveButton.setEnabled(value); + } + + private boolean isDecompiledModified() { + return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); + } + + private void setDecompiledModified(boolean value) { + saveDecompiledButton.setEnabled(value); + } + + public void setEditMode(boolean val) { + if (val) { + if (hexOnlyButton.isSelected()) { + setHex(ScriptExportMode.HEX); + } else if (constantsViewButton.isSelected()) { + setHex(ScriptExportMode.CONSTANTS); + } else { + setHex(ScriptExportMode.PCODE); + } + } + + editor.setEditable(val); + saveButton.setVisible(val); + saveButton.setEnabled(false); + editButton.setVisible(!val); + cancelButton.setVisible(val); + + editor.getCaret().setVisible(true); + asmLabel.setIcon(val ? View.getIcon("editing16") : null); // this line is not working + topButtonsPan.setVisible(!val); + editMode = val; + editor.requestFocusInWindow(); + } + + public void setDecompiledEditMode(boolean val) { + if (lastASM == null) { + return; + } + + int lastLine = decompiledEditor.getLine(); + int prefLines = lastASM.getPrefixLineCount(); + if (val) { + String newText = lastASM.removePrefixAndSuffix(lastDecompiled); + setDecompiledText(newText); + if (lastLine > -1) { + if (lastLine - prefLines >= 0) { + decompiledEditor.gotoLine(lastLine - prefLines + 1); + } + } + } else { + String newText = lastDecompiled; + setDecompiledText(newText); + if (lastLine > -1) { + decompiledEditor.gotoLine(lastLine + prefLines + 1); + } + } + + decompiledEditor.setEditable(val); + saveDecompiledButton.setVisible(val); + saveDecompiledButton.setEnabled(false); + editDecompiledButton.setVisible(!val); + experimentalLabel.setVisible(!val); + cancelDecompiledButton.setVisible(val); + + decompiledEditor.getCaret().setVisible(true); + decLabel.setIcon(val ? View.getIcon("editing16") : null); + editDecompiledMode = val; + decompiledEditor.requestFocusInWindow(); + } + + private void graphButtonActionPerformed(ActionEvent evt) { + if (lastCode != null) { + try { + GraphDialog gf = new GraphDialog(mainPanel.getMainFrame().getWindow(), new ActionGraph(lastCode, new HashMap<>(), new HashMap<>(), new HashMap<>(), SWF.DEFAULT_VERSION), ""); + gf.setVisible(true); + } catch (InterruptedException ex) { + Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + private void editActionButtonActionPerformed(ActionEvent evt) { + setEditMode(true); + } + + private void hexButtonActionPerformed(ActionEvent evt) { + setHex(getExportMode()); + } + + private void hexOnlyButtonActionPerformed(ActionEvent evt) { + setHex(getExportMode()); + } + + private void constantsViewButtonActionPerformed(ActionEvent evt) { + setHex(getExportMode()); + } + + private void resolveConstantsButtonActionPerformed(ActionEvent evt) { + boolean resolve = resolveConstantsButton.isSelected(); + Configuration.resolveConstants.set(resolve); + + srcWithHex = null; + srcNoHex = null; + // srcHexOnly = null; is not needed since it does not contains the resolved constant names + setHex(getExportMode()); + } + + private void cancelActionButtonActionPerformed(ActionEvent evt) { + setEditMode(false); + setHex(getExportMode()); + } + + private void saveActionButtonActionPerformed(ActionEvent evt) { + try { + String text = editor.getText(); + String trimmed = text.trim(); + if (trimmed.startsWith(Helper.hexData)) { + src.setActionBytes(Helper.getBytesFromHexaText(text)); + } else if (trimmed.startsWith(Helper.constants)) { + setConstantPools(Helper.getConstantPoolsFromText(text)); + } else { + src.setActions(ASMParser.parse(0, true, text, src.getSwf().version, false)); + } + + SWF.uncache(src); + src.setModified(); + mainPanel.refreshTree(src.getSwf()); + setSource(this.src, false); + View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); + saveButton.setVisible(false); + cancelButton.setVisible(false); + editButton.setVisible(true); + editor.setEditable(false); + editMode = false; + } catch (IOException ex) { + } catch (ActionParseException ex) { + editor.gotoLine((int) ex.line); + editor.markError(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + } + + private void setConstantPools(List> constantPools) { + try { + ActionList actions = src.getActions(); + int poolIdx = 0; + for (Action action : actions) { + if (action instanceof ActionConstantPool) { + ActionConstantPool cPool = (ActionConstantPool) action; + List constantPool = constantPools.get(poolIdx); + cPool.constantPool = constantPool; + + poolIdx++; + if (constantPools.size() <= poolIdx) { + break; + } + } + } + + src.setActions(actions); + } catch (InterruptedException ex) { + Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private void editDecompiledButtonActionPerformed(ActionEvent evt) { + if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS12Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { + setDecompiledEditMode(true); + } + } + + private void cancelDecompiledButtonActionPerformed(ActionEvent evt) { + setDecompiledEditMode(false); + } + + private void saveDecompiledButtonActionPerformed(ActionEvent evt) { + try { + ActionScriptParser par = new ActionScriptParser(mainPanel.getCurrentSwf().version); + src.setActions(par.actionsFromString(decompiledEditor.getText())); + src.setModified(); + setSource(src, false); + + View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); + setDecompiledEditMode(false); + } catch (IOException ex) { + Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, "IOException during action compiling", ex); + } catch (ActionParseException ex) { + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } catch (CompilationException ex) { + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + } + + private ScriptExportMode getExportMode() { + ScriptExportMode exportMode = hexOnlyButton.isSelected() ? ScriptExportMode.HEX + : hexButton.isSelected() ? ScriptExportMode.PCODE_HEX + : constantsViewButton.isSelected() ? ScriptExportMode.CONSTANTS + : ScriptExportMode.PCODE; + return exportMode; + } + + @Override + public void updateSearchPos(ActionSearchResult item) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreePath tp = ttm.getTreePath(item.src); + mainPanel.tagTree.setSelectionPath(tp); + mainPanel.tagTree.scrollPathToVisible(tp); + decompiledEditor.setCaretPosition(0); + + View.execInEventDispatchLater(() -> { + searchPanel.showQuickFindDialog(decompiledEditor); + }); + } + + @Override + public boolean tryAutoSave() { + // todo: implement + return false; + } + + @Override + public boolean isEditing() { + return (saveButton.isVisible() && saveButton.isEnabled()) + || (saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled()); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/LineMarkedEditorPane.java b/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java similarity index 99% rename from src/com/jpexs/decompiler/flash/gui/abc/LineMarkedEditorPane.java rename to src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java index 808f755c4..62c8e05f5 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/LineMarkedEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.jpexs.decompiler.flash.gui.abc; +package com.jpexs.decompiler.flash.gui.editor; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.gui.AppStrings; @@ -59,6 +59,12 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan private boolean error = false; + private Token lastUnderlined = null; + + private static final HighlightPainter underLinePainter = new UnderLinePainter(new Color(0, 0, 255)); + + private LinkHandler linkHandler = this; + public int getLine() { return lastLine; } @@ -144,12 +150,6 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan }); } - private Token lastUnderlined = null; - - private static final HighlightPainter underLinePainter = new UnderLinePainter(new Color(0, 0, 255)); - - private LinkHandler linkHandler = this; - private class LinkAdapter extends MouseAdapter implements KeyListener { private Point lastPos = new Point(0, 0); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/LinkHandler.java b/src/com/jpexs/decompiler/flash/gui/editor/LinkHandler.java similarity index 95% rename from src/com/jpexs/decompiler/flash/gui/abc/LinkHandler.java rename to src/com/jpexs/decompiler/flash/gui/editor/LinkHandler.java index a3ede7d1a..817321766 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/LinkHandler.java +++ b/src/com/jpexs/decompiler/flash/gui/editor/LinkHandler.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.jpexs.decompiler.flash.gui.abc; +package com.jpexs.decompiler.flash.gui.editor; import javax.swing.text.Highlighter; import jsyntaxpane.Token; diff --git a/src/com/jpexs/decompiler/flash/gui/abc/MyMarkers.java b/src/com/jpexs/decompiler/flash/gui/editor/MyMarkers.java similarity index 96% rename from src/com/jpexs/decompiler/flash/gui/abc/MyMarkers.java rename to src/com/jpexs/decompiler/flash/gui/editor/MyMarkers.java index 53fc8c82b..9de1381ae 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/MyMarkers.java +++ b/src/com/jpexs/decompiler/flash/gui/editor/MyMarkers.java @@ -1,127 +1,127 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.swing.text.BadLocationException; -import javax.swing.text.Highlighter; -import javax.swing.text.JTextComponent; -import jsyntaxpane.SyntaxDocument; -import jsyntaxpane.Token; -import jsyntaxpane.actions.ActionUtils; - -/** - * - * @author JPEXS - */ -public class MyMarkers { - - /** - * Removes only our private highlights This is public so that we can remove - * the highlights when the editorKit is unregistered. SimpleMarker can be - * null, in which case all instances of our Markers are removed. - * - * @param component the text component whose markers are to be removed - * @param marker the SimpleMarker to remove - */ - public static void removeMarkers(JTextComponent component, Highlighter.HighlightPainter marker) { - Highlighter hilite = component.getHighlighter(); - Highlighter.Highlight[] hilites = hilite.getHighlights(); - - for (int i = 0; i < hilites.length; i++) { - Highlighter.HighlightPainter hMarker = hilites[i].getPainter(); - if (marker == null || hMarker.equals(marker)) { - hilite.removeHighlight(hilites[i]); - } - } - } - - /** - * Remove all the markers from an JEditorPane - * - * @param editorPane Editor - */ - public static void removeMarkers(JTextComponent editorPane) { - removeMarkers(editorPane, null); - } - - /** - * add highlights for the given Token on the given pane - * - * @param pane Editor - * @param token Token - * @param marker Marker - */ - public static void markToken(JTextComponent pane, Token token, Highlighter.HighlightPainter marker) { - markText(pane, token.start, token.end(), marker); - } - - /** - * add highlights for the given region on the given pane - * - * @param pane Editor - * @param start Start index - * @param end End index - * @param marker Marker - */ - public static void markText(JTextComponent pane, int start, int end, Highlighter.HighlightPainter marker) { - try { - Highlighter hiliter = pane.getHighlighter(); - int selStart = pane.getSelectionStart(); - int selEnd = pane.getSelectionEnd(); - // if there is no selection or selection does not overlap - if (selStart == selEnd || end < selStart || start > selStart) { - hiliter.addHighlight(start, end, marker); - return; - } - // selection starts within the highlight, highlight before slection - if (selStart > start && selStart < end) { - hiliter.addHighlight(start, selStart, marker); - } - // selection ends within the highlight, highlight remaining - if (selEnd > start && selEnd < end) { - hiliter.addHighlight(selEnd, end, marker); - } - - } catch (BadLocationException ex) { - - } - } - - /** - * Mark all text in the document that matches the given pattern - * - * @param pane control to use - * @param pattern pattern to match - * @param marker marker to use for highlighting - */ - public static void markAll(JTextComponent pane, Pattern pattern, Highlighter.HighlightPainter marker) { - SyntaxDocument sDoc = ActionUtils.getSyntaxDocument(pane); - if (sDoc == null || pattern == null) { - return; - } - Matcher matcher = sDoc.getMatcher(pattern); - // we may not have any matcher (due to undo or something, so don't do anything. - if (matcher == null) { - return; - } - while (matcher.find()) { - markText(pane, matcher.start(), matcher.end(), marker); - } - } -} +/* + * Copyright (C) 2010-2015 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.editor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; +import jsyntaxpane.SyntaxDocument; +import jsyntaxpane.Token; +import jsyntaxpane.actions.ActionUtils; + +/** + * + * @author JPEXS + */ +public class MyMarkers { + + /** + * Removes only our private highlights This is public so that we can remove + * the highlights when the editorKit is unregistered. SimpleMarker can be + * null, in which case all instances of our Markers are removed. + * + * @param component the text component whose markers are to be removed + * @param marker the SimpleMarker to remove + */ + public static void removeMarkers(JTextComponent component, Highlighter.HighlightPainter marker) { + Highlighter hilite = component.getHighlighter(); + Highlighter.Highlight[] hilites = hilite.getHighlights(); + + for (int i = 0; i < hilites.length; i++) { + Highlighter.HighlightPainter hMarker = hilites[i].getPainter(); + if (marker == null || hMarker.equals(marker)) { + hilite.removeHighlight(hilites[i]); + } + } + } + + /** + * Remove all the markers from an JEditorPane + * + * @param editorPane Editor + */ + public static void removeMarkers(JTextComponent editorPane) { + removeMarkers(editorPane, null); + } + + /** + * add highlights for the given Token on the given pane + * + * @param pane Editor + * @param token Token + * @param marker Marker + */ + public static void markToken(JTextComponent pane, Token token, Highlighter.HighlightPainter marker) { + markText(pane, token.start, token.end(), marker); + } + + /** + * add highlights for the given region on the given pane + * + * @param pane Editor + * @param start Start index + * @param end End index + * @param marker Marker + */ + public static void markText(JTextComponent pane, int start, int end, Highlighter.HighlightPainter marker) { + try { + Highlighter hiliter = pane.getHighlighter(); + int selStart = pane.getSelectionStart(); + int selEnd = pane.getSelectionEnd(); + // if there is no selection or selection does not overlap + if (selStart == selEnd || end < selStart || start > selStart) { + hiliter.addHighlight(start, end, marker); + return; + } + // selection starts within the highlight, highlight before slection + if (selStart > start && selStart < end) { + hiliter.addHighlight(start, selStart, marker); + } + // selection ends within the highlight, highlight remaining + if (selEnd > start && selEnd < end) { + hiliter.addHighlight(selEnd, end, marker); + } + + } catch (BadLocationException ex) { + + } + } + + /** + * Mark all text in the document that matches the given pattern + * + * @param pane control to use + * @param pattern pattern to match + * @param marker marker to use for highlighting + */ + public static void markAll(JTextComponent pane, Pattern pattern, Highlighter.HighlightPainter marker) { + SyntaxDocument sDoc = ActionUtils.getSyntaxDocument(pane); + if (sDoc == null || pattern == null) { + return; + } + Matcher matcher = sDoc.getMatcher(pattern); + // we may not have any matcher (due to undo or something, so don't do anything. + if (matcher == null) { + return; + } + while (matcher.find()) { + markText(pane, matcher.start(), matcher.end(), marker); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/editor/TextChangedListener.java b/src/com/jpexs/decompiler/flash/gui/editor/TextChangedListener.java new file mode 100644 index 000000000..56cde237f --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/editor/TextChangedListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010-2015 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.editor; + +/** + * + * @author JPEXS + */ +@FunctionalInterface +public interface TextChangedListener { + + public void textChanged(); +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/UndoFixedEditorPane.java b/src/com/jpexs/decompiler/flash/gui/editor/UndoFixedEditorPane.java similarity index 65% rename from src/com/jpexs/decompiler/flash/gui/abc/UndoFixedEditorPane.java rename to src/com/jpexs/decompiler/flash/gui/editor/UndoFixedEditorPane.java index 3ca6629d1..b509983b3 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/UndoFixedEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/editor/UndoFixedEditorPane.java @@ -1,110 +1,170 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.helpers.Stopwatch; -import java.awt.event.KeyEvent; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JEditorPane; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.EditorKit; -import jsyntaxpane.SyntaxDocument; - -/** - * - * @author JPEXS - */ -public class UndoFixedEditorPane extends JEditorPane { - - private static final Object setTextLock = new Object(); - - private String originalContentType; - - @Override - public void setText(final String t) { - View.execInEventDispatch(() -> { - setText(t, getContentType()); - }); - } - - private void setText(String t, String contentType) { - synchronized (setTextLock) { - if (!t.equals(getText())) { - boolean plain = t.length() > Configuration.syntaxHighlightLimit.get(); - if (plain) { - contentType = "text/plain"; - originalContentType = getContentType(); - setContentType(contentType); - } else { - if (originalContentType != null) { - setContentType(originalContentType); - originalContentType = null; - } - } - - Stopwatch sw = Stopwatch.startNew(); - try { - Document doc = getDocument(); - setDocument(new SyntaxDocument(null)); - doc.remove(0, doc.getLength()); - Reader r = new StringReader(t); - EditorKit kit = createEditorKitForContentType(contentType); - kit.read(r, doc, 0); - setDocument(doc); - } catch (BadLocationException | IOException ex) { - Logger.getLogger(UndoFixedEditorPane.class.getName()).log(Level.SEVERE, null, ex); - } - - sw.stop(); - if (!plain && sw.getElapsedMilliseconds() > 5000) { - Logger.getLogger(UndoFixedEditorPane.class.getName()).log(Level.WARNING, "Syntax highlightig took long time. You can try to decrease the syntax highlight limit in advanced settings."); - } - - clearUndos(); - } - } - } - - @Override - protected void processKeyEvent(KeyEvent ke) { - if (!isEditable()) { - // disable Ctrl-E: delete line - // and Ctrl-H: Search and replace - if ((ke.getKeyCode() == KeyEvent.VK_E && ke.isControlDown()) - || (ke.getKeyCode() == KeyEvent.VK_H && ke.isControlDown())) { - return; - } - } - - super.processKeyEvent(ke); - } - - public void clearUndos() { - Document doc = getDocument(); - if (doc instanceof SyntaxDocument) { - SyntaxDocument sdoc = (SyntaxDocument) doc; - sdoc.clearUndos(); - } - } -} +/* + * Copyright (C) 2010-2015 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.editor; + +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.helpers.Stopwatch; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JEditorPane; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.EditorKit; +import jsyntaxpane.SyntaxDocument; + +/** + * + * @author JPEXS + */ +public class UndoFixedEditorPane extends JEditorPane { + + private static final Object setTextLock = new Object(); + + private final List textChangedListeners = new ArrayList<>(); + + private String originalContentType; + + private DocumentListener documentListener; + + public UndoFixedEditorPane() { + addDocumentListener(); + } + + private void fireTextChanged() { + for (TextChangedListener listener : textChangedListeners) { + listener.textChanged(); + } + } + + private void addDocumentListener() { + documentListener = new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + fireTextChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + fireTextChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + fireTextChanged(); + } + }; + + getDocument().addDocumentListener(documentListener); + } + + private void removeDocumentListener() { + getDocument().removeDocumentListener(documentListener); + } + + public void changeContentType(String type) { + if (!type.equals(getContentType())) { + removeDocumentListener(); + setContentType(type); + addDocumentListener(); + } + } + + @Override + public void setText(final String t) { + View.execInEventDispatch(() -> { + setText(t, getContentType()); + }); + } + + private void setText(String t, String contentType) { + synchronized (setTextLock) { + if (!t.equals(getText())) { + boolean plain = t.length() > Configuration.syntaxHighlightLimit.get(); + if (plain) { + contentType = "text/plain"; + originalContentType = getContentType(); + changeContentType(contentType); + } else { + if (originalContentType != null) { + changeContentType(originalContentType); + originalContentType = null; + } + } + + Stopwatch sw = Stopwatch.startNew(); + try { + Document doc = getDocument(); + setDocument(new SyntaxDocument(null)); + doc.remove(0, doc.getLength()); + Reader r = new StringReader(t); + EditorKit kit = createEditorKitForContentType(contentType); + kit.read(r, doc, 0); + setDocument(doc); + } catch (BadLocationException | IOException ex) { + Logger.getLogger(UndoFixedEditorPane.class.getName()).log(Level.SEVERE, null, ex); + } + + sw.stop(); + if (!plain && sw.getElapsedMilliseconds() > 5000) { + Logger.getLogger(UndoFixedEditorPane.class.getName()).log(Level.WARNING, "Syntax highlightig took long time. You can try to decrease the syntax highlight limit in advanced settings."); + } + + clearUndos(); + } + } + } + + @Override + protected void processKeyEvent(KeyEvent ke) { + if (!isEditable()) { + // disable Ctrl-E: delete line + // and Ctrl-H: Search and replace + if ((ke.getKeyCode() == KeyEvent.VK_E && ke.isControlDown()) + || (ke.getKeyCode() == KeyEvent.VK_H && ke.isControlDown())) { + return; + } + } + + super.processKeyEvent(ke); + } + + public void clearUndos() { + Document doc = getDocument(); + if (doc instanceof SyntaxDocument) { + SyntaxDocument sdoc = (SyntaxDocument) doc; + sdoc.clearUndos(); + } + } + + public void addTextChangedListener(TextChangedListener l) { + textChangedListeners.add(l); + } + + public void removeTextChangedListener(TextChangedListener l) { + textChangedListeners.remove(l); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 9333d9df5..67fa71980 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -1,352 +1,352 @@ -# Copyright (C) 2010-2015 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 . - -advancedSettings.dialog.title = Advanced Settings -advancedSettings.restartConfirmation = You must restart the program for some modifications to take effect. Do you want to restart it now? -advancedSettings.columns.name = Name -advancedSettings.columns.value = Value -advancedSettings.columns.description = Description -default = default - -config.group.name.export = Export -config.group.description.export = Configuration of exports - -config.group.name.script = Scripts -config.group.description.script = ActionScript decompilation related - -config.group.name.update = Updates -config.group.description.update = Checking for updates - -config.group.name.format = Formatting -config.group.description.format = ActionScript code formatting - -config.group.name.limit = Limits -config.group.description.limit = Decompilation limits for obfuscated code, etc. - -config.group.name.ui = Interface -config.group.description.ui = User intercace configuration - -config.group.name.debug = Debug -config.group.description.debug = Debugging settings - -config.group.name.display = Display -config.group.description.display = Flash objects display, etc. - -config.group.name.decompilation = Decompilation -config.group.description.decompilation = Global decompilation related functions - -config.group.name.other = Other -config.group.description.other = Other uncategorized configs - -config.name.openMultipleFiles = Open multiple files -config.description.openMultipleFiles = Allows opening multiple files at once in one window - -config.name.decompile = Show ActionScript source -config.description.decompile = You can disable AS decompilation, then only P-code is shown - -config.name.dumpView = Dump View -config.description.dumpView = View raw data dump - -config.name.useHexColorFormat = Hex color format -config.description.useHexColorFormat = Show the colors in hex format - -config.name.parallelSpeedUp = Parallel SpeedUp -config.description.parallelSpeedUp = Parallelism can speed up decompilation - -config.name.parallelThreadCount = Number of threads -config.description.parallelThreadCount = Number of threads for parallel speedup - -config.name.autoDeobfuscate = Automatic deobfuscation -config.description.autoDeobfuscate = Run deobfuscation on every file before ActionScript decompilation - -config.name.cacheOnDisk = Use caching on disk -config.description.cacheOnDisk = Cache already decompiled parts on hard drive instead of memory - -config.name.internalFlashViewer = Use own Flash viewer -config.description.internalFlashViewer = Use JPEXS Flash Viewer instead of standard Flash Player for flash parts display - -config.name.gotoMainClassOnStartup = Go to main class on startup (AS3) -config.description.gotoMainClassOnStartup = Navigates to document class of AS3 file on SWF opening - -config.name.autoRenameIdentifiers = Automatic rename identifiers -config.description.autoRenameIdentifiers = Automatically rename invalid indentifiers on SWF load - -config.name.offeredAssociation = (Internal) Association with SWF files displayed -config.description.offeredAssociation = Dialog about file association was already displayed - -config.name.decimalAddress = Use decimal addresses -config.description.decimalAddress = Use decimal addresses instead of hexadecimal - -config.name.showAllAddresses = Show all addresses -config.description.showAllAddresses = Display all ActionScript instruction addresses - -config.name.useFrameCache = Use frame cache -config.description.useFrameCache = Cache frames before rendering again - -config.name.useRibbonInterface = Ribbon interface -config.description.useRibbonInterface = Uncheck to use classic interface without ribbon menu - -config.name.openFolderAfterFlaExport = Open folder after FLA export -config.description.openFolderAfterFlaExport = Display output directory after exporting FLA file - -config.name.useDetailedLogging = Detailed Logging -config.description.useDetailedLogging = Log detailed error messages and info for debugging purposes - -config.name.debugMode = Debug mode -config.description.debugMode = Mode for debugging. Turns on debug menu. - -config.name.resolveConstants = Resolve constants in AS1/2 p-code -config.description.resolveConstants = Turn this off to show 'constantxx' instead of real values in P-code window - -config.name.sublimiter = Limit of code subs -config.description.sublimiter = Limit of code subs for obfuscated code. - -config.name.exportTimeout = Total export timeout (seconds) -config.description.exportTimeout = Decompiler will stop exporting after reaching this time - -config.name.decompilationTimeoutFile = Single file decompilation timeout (seconds) -config.description.decompilationTimeoutFile = Decompiler will stop ActionScript decompilation after reaching this time in one file - -config.name.paramNamesEnable = Enable parameter names in AS3 -config.description.paramNamesEnable = Using parameter names in decompiling may cause problems because official programs like Flash CS 5.5 inserts wrong parameter names indices - -config.name.displayFileName = Show SWF name in title -config.description.displayFileName = Display SWF file/url name in the window title (You can make screenshots then) - -config.name.debugCopy = Debug recompile -config.description.debugCopy = Tries to compile SWF file again just after opening to ensure it produces same binary code. Use for DEBUGGING only! - -config.name.dumpTags = Dump tags to console -config.description.dumpTags = Dump tags to console on reading SWF file - -config.name.decompilationTimeoutSingleMethod = AS3: Single method decompilation timeout (seconds) -config.description.decompilationTimeoutSingleMethod = Decompiler will stop ActionScript decompilation after reaching this time in one method - -config.name.lastRenameType = (Internal) Last rename type -config.description.lastRenameType = Last used rename identifiers type - -config.name.lastSaveDir = (Internal) Last save directory -config.description.lastSaveDir = Last used save directory - -config.name.lastOpenDir = (Internal) Last open directory -config.description.lastOpenDir = Last used open directory - -config.name.lastExportDir = (Internal) Last export directory -config.description.lastExportDir = Last used export directory - -config.name.locale = Language -config.description.locale = Locales identifier - -config.name.registerNameFormat = Register variable format -config.description.registerNameFormat = Format of local register variable names. Use %d for register number. - -config.name.maxRecentFileCount = Max recent count -config.description.maxRecentFileCount = Maximum number of recent files - -config.name.recentFiles = (Internal) Recent files -config.description.recentFiles = Recent opened files - -config.name.fontPairing = (Internal) Font pairs for import -config.description.fontPairing = Font pairs for importing new characters - -config.name.lastUpdatesCheckDate = (Internal) Last update check date -config.description.lastUpdatesCheckDate = Date of last checking for updates on server - -config.name.gui.window.width = (Internal) Last window width -config.description.gui.window.width = Last saved window width - -config.name.gui.window.height = (Internal) Last window height -config.description.gui.window.height = Last saved window height - -config.name.gui.window.maximized.horizontal = (Internal) Window maximized horizontally -config.description.gui.window.maximized.horizontal = Last window state - maximized horizontally - -config.name.gui.window.maximized.vertical = (Internal) Window maximized vertically -config.description.gui.window.maximized.vertical = Last window state - maximized vertically - -config.name.gui.avm2.splitPane.dividerLocationPercent = (Internal) AS3 Splitter location -config.description.gui.avm2.splitPane.dividerLocationPercent = - -config.name.gui.actionSplitPane.dividerLocationPercent = (Internal) AS1/2 splitter location -config.description.gui.actionSplitPane.dividerLocationPercent = - -config.name.gui.previewSplitPane.dividerLocationPercent = (Internal) Preview splitter location -config.description.gui.previewSplitPane.dividerLocationPercent = - -config.name.gui.splitPane1.dividerLocationPercent = (Internal) Splitter location 1 -config.description.gui.splitPane1.dividerLocationPercent = - -config.name.gui.splitPane2.dividerLocationPercent = (Internal) Splitter location 2 -config.description.gui.splitPane2.dividerLocationPercent = - -config.name.saveAsExeScaleMode = Save as EXE scale mode -config.description.saveAsExeScaleMode = Scaling mode for EXE export - -config.name.syntaxHighlightLimit = Syntax hilight max chars -config.description.syntaxHighlightLimit = Maximum number of characters to run syntax hilight on - -config.name.guiFontPreviewSampleText = (Internal) Last font preview sample text -config.description.guiFontPreviewSampleText = Last font preview sample text list index - -config.name.gui.fontPreviewWindow.width = (Internal) Last font preview window width -config.description.gui.fontPreviewWindow.width = - -config.name.gui.fontPreviewWindow.height = (Internal) Last font preview window height -config.description.gui.fontPreviewWindow.height = - -config.name.gui.fontPreviewWindow.posX = (Internal) Last font preview window X -config.description.gui.fontPreviewWindow.posX = - -config.name.gui.fontPreviewWindow.posY = (Internal) Last font preview window Y -config.description.gui.fontPreviewWindow.posY = - -config.name.formatting.indent.size = Characters per indent -config.description.formatting.indent.size = Number or spaces(or tabs) for one indentation - -config.name.formatting.indent.useTabs = Tabs for indent -config.description.formatting.indent.useTabs = Use tabs instead of spaces for indentation - -config.name.beginBlockOnNewLine = Curly brace on new line -config.description.beginBlockOnNewLine = Begin block with curly brace on new line - -config.name.check.updates.delay = Updates check delay -config.description.check.updates.delay = Minimum time between automatic checks for updates on application start - -config.name.check.updates.stable = Check for stable versions -config.description.check.updates.stable = Checking for stable version updates - -config.name.check.updates.nightly = Check for nightly versions -config.description.check.updates.nightly = Checking for nightly version updates - -config.name.check.updates.enabled = Updates check enabled -config.description.check.updates.enabled = Automatic checking for updates on application start - -config.name.export.formats = (Internal) Export formats -config.description.export.formats = Last used export formats - -config.name.textExportSingleFile = Export texts to single file -config.description.textExportSingleFile = Exporting texts to one file instead of multiple - -config.name.textExportSingleFileSeparator = Separator of texts in one file text export -config.description.textExportSingleFileSeparator = Text to insert between texts in single file text export - -config.name.textExportSingleFileRecordSeparator = Separator of records in one file text export -config.description.textExportSingleFileRecordSeparator = Text to insert between text records in single file text export - -config.name.warning.experimental.as12edit = Warn on AS1/2 direct edit -config.description.warning.experimental.as12edit = Show warning on AS1/2 experimental direct editation - -config.name.warning.experimental.as3edit = Warn on AS3 direct edit -config.description.warning.experimental.as3edit = Show warning on AS3 experimental direct editation - -config.name.packJavaScripts = Pack JavaScripts -config.description.packJavaScripts = Run JavaScript packer on scripts created on Canvas Export. - -config.name.textExportExportFontFace = Use font-face in SVG export -config.description.textExportExportFontFace = Embed font files in SVG using font-face instead of shapes - -config.name.lzmaFastBytes = LZMA fast bytes (valid values: 5-255) -config.description.lzmaFastBytes = Fast bytes parameter of the LZMA encoder - -#temporary setting, do not translate it -config.name.pluginPath = Plugin Path -config.description.pluginPath = - - -config.name.deobfuscationMode = Deobfuscation mode -config.description.deobfuscationMode = Run deobfuscation on every file before ActionScript decompilation - -config.name.showMethodBodyId = Show method body id -config.description.showMethodBodyId = Shows the id of the methodbody for commandline import - -config.name.export.zoom = (Internal) Export zoom -config.description.export.zoom = Last used export zoom - -config.name.debuggerPort = Debugger port -config.description.debuggerPort = Port used for socket debugging - -config.name.displayDebuggerInfo = (Internal) Display debugger info -config.description.displayDebuggerInfo = Display info about debugger before switching it - -config.name.randomDebuggerPackage = Use random package name for Debugger -config.description.randomDebuggerPackage = This renames Debugger package to random string which makes debugger presence harder to detect by ActionScript - -config.name.lastDebuggerReplaceFunction = (Internal) Last selected trace replacement -config.description.lastDebuggerReplaceFunction = Function name which was last selected in replace trace function with debugger - -config.name.getLocalNamesFromDebugInfo = AS3: Get local register names from debug info -config.description.getLocalNamesFromDebugInfo = If debug info present, renames local registers from _loc_x_ to real names. This can be turned off because some obfuscators use invalid register names there. - -config.name.tagTreeShowEmptyFolders = Show empty folders -config.description.tagTreeShowEmptyFolders = Show empty folders in tag tree. - -config.name.autoLoadEmbeddedSwfs = Auto load embedded SWFs -config.description.autoLoadEmbeddedSwfs = Automaticaly load the embedded SWFs from DefineBinaryData tags. - -config.name.overrideTextExportFileName = Override text export filename -config.description.overrideTextExportFileName = You can customize the filename of the exported text. Use {filename} placeholder to use the filename of current SWF. - -config.name.showOldTextDuringTextEditing = Show old text during text editing -config.description.showOldTextDuringTextEditing = Shows the original text of the text tag with gray color in the preview area. - -config.group.name.import = Import -config.group.description.import = Configuration of imports - -config.name.textImportResizeTextBoundsMode = Text bounds resize mode -config.description.textImportResizeTextBoundsMode = Text bounds resize mode after text editing. - -config.name.showCloseConfirmation = Show again SWF close confirmation -config.description.showCloseConfirmation = Show again SWF close confirmation for modified files. - -config.name.showCodeSavedMessage = Show again code saved message -config.description.showCodeSavedMessage = Show again code saved message - -config.name.showTraitSavedMessage = Show again trait saved message -config.description.showTraitSavedMessage = Show again trait saved message - -config.name.updateProxyAddress = Http Proxy address for checking updates -config.description.updateProxyAddress = Http Proxy address for checking updates. Format: example.com:8080 - -config.name.editorMode = Editor Mode -config.description.editorMode = Make text areas edittable automatically when you select a Text or Script node - -config.name.autoSaveTagModifications = Auto save tag modificatios -config.description.autoSaveTagModifications = Save the changes when you select a new tag in the tree - -config.name.saveSessionOnExit = Save session on exit -config.description.saveSessionOnExit = Save the current session and reopens it after FFDec restart (works only with real files) - -config.name.showDebugMenu = Show debug menu -config.description.showDebugMenu = Shows debug menu in the ribbon - -config.name.allowOnlyOneInstance = Allow only one FFDec instance (Only Windows OS) -config.description.allowOnlyOneInstance = FFDec can be then run only once, all files opened will be added to one window. It works only with Windows operating system. - -config.name.scriptExportSingleFile = Export scripts to single file -config.description.scriptExportSingleFile = Exporting texts to one file instead of multiple - -config.name.setFFDecVersionInExportedFont = Set FFDec version number in exported font -config.description.setFFDecVersionInExportedFont = When this setting is disabled, FFDec won't add the current FFDec version number to the exported font. - -config.name.gui.skin = User Interface Skin -config.description.gui.skin = Look and feel skin - -config.name.lastSessionData = Last session data -config.description.lastSessionData = Contains the opened files from the last session - -config.name.loopMedia = Loop sounds and sprites -config.description.loopMedia = Automatically restarts the playing of the sounds and sprites - -config.name.gui.timeLineSplitPane.dividerLocationPercent = (Internal) Timeline Splitter location -config.description.gui.timeLineSplitPane.dividerLocationPercent = +# Copyright (C) 2010-2015 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 . + +advancedSettings.dialog.title = Advanced Settings +advancedSettings.restartConfirmation = You must restart the program for some modifications to take effect. Do you want to restart it now? +advancedSettings.columns.name = Name +advancedSettings.columns.value = Value +advancedSettings.columns.description = Description +default = default + +config.group.name.export = Export +config.group.description.export = Configuration of exports + +config.group.name.script = Scripts +config.group.description.script = ActionScript decompilation related + +config.group.name.update = Updates +config.group.description.update = Checking for updates + +config.group.name.format = Formatting +config.group.description.format = ActionScript code formatting + +config.group.name.limit = Limits +config.group.description.limit = Decompilation limits for obfuscated code, etc. + +config.group.name.ui = Interface +config.group.description.ui = User intercace configuration + +config.group.name.debug = Debug +config.group.description.debug = Debugging settings + +config.group.name.display = Display +config.group.description.display = Flash objects display, etc. + +config.group.name.decompilation = Decompilation +config.group.description.decompilation = Global decompilation related functions + +config.group.name.other = Other +config.group.description.other = Other uncategorized configs + +config.name.openMultipleFiles = Open multiple files +config.description.openMultipleFiles = Allows opening multiple files at once in one window + +config.name.decompile = Show ActionScript source +config.description.decompile = You can disable AS decompilation, then only P-code is shown + +config.name.dumpView = Dump View +config.description.dumpView = View raw data dump + +config.name.useHexColorFormat = Hex color format +config.description.useHexColorFormat = Show the colors in hex format + +config.name.parallelSpeedUp = Parallel SpeedUp +config.description.parallelSpeedUp = Parallelism can speed up decompilation + +config.name.parallelThreadCount = Number of threads +config.description.parallelThreadCount = Number of threads for parallel speedup + +config.name.autoDeobfuscate = Automatic deobfuscation +config.description.autoDeobfuscate = Run deobfuscation on every file before ActionScript decompilation + +config.name.cacheOnDisk = Use caching on disk +config.description.cacheOnDisk = Cache already decompiled parts on hard drive instead of memory + +config.name.internalFlashViewer = Use own Flash viewer +config.description.internalFlashViewer = Use JPEXS Flash Viewer instead of standard Flash Player for flash parts display + +config.name.gotoMainClassOnStartup = Go to main class on startup (AS3) +config.description.gotoMainClassOnStartup = Navigates to document class of AS3 file on SWF opening + +config.name.autoRenameIdentifiers = Automatic rename identifiers +config.description.autoRenameIdentifiers = Automatically rename invalid indentifiers on SWF load + +config.name.offeredAssociation = (Internal) Association with SWF files displayed +config.description.offeredAssociation = Dialog about file association was already displayed + +config.name.decimalAddress = Use decimal addresses +config.description.decimalAddress = Use decimal addresses instead of hexadecimal + +config.name.showAllAddresses = Show all addresses +config.description.showAllAddresses = Display all ActionScript instruction addresses + +config.name.useFrameCache = Use frame cache +config.description.useFrameCache = Cache frames before rendering again + +config.name.useRibbonInterface = Ribbon interface +config.description.useRibbonInterface = Uncheck to use classic interface without ribbon menu + +config.name.openFolderAfterFlaExport = Open folder after FLA export +config.description.openFolderAfterFlaExport = Display output directory after exporting FLA file + +config.name.useDetailedLogging = Detailed Logging +config.description.useDetailedLogging = Log detailed error messages and info for debugging purposes + +config.name.debugMode = Debug mode +config.description.debugMode = Mode for debugging. Turns on debug menu. + +config.name.resolveConstants = Resolve constants in AS1/2 p-code +config.description.resolveConstants = Turn this off to show 'constantxx' instead of real values in P-code window + +config.name.sublimiter = Limit of code subs +config.description.sublimiter = Limit of code subs for obfuscated code. + +config.name.exportTimeout = Total export timeout (seconds) +config.description.exportTimeout = Decompiler will stop exporting after reaching this time + +config.name.decompilationTimeoutFile = Single file decompilation timeout (seconds) +config.description.decompilationTimeoutFile = Decompiler will stop ActionScript decompilation after reaching this time in one file + +config.name.paramNamesEnable = Enable parameter names in AS3 +config.description.paramNamesEnable = Using parameter names in decompiling may cause problems because official programs like Flash CS 5.5 inserts wrong parameter names indices + +config.name.displayFileName = Show SWF name in title +config.description.displayFileName = Display SWF file/url name in the window title (You can make screenshots then) + +config.name.debugCopy = Debug recompile +config.description.debugCopy = Tries to compile SWF file again just after opening to ensure it produces same binary code. Use for DEBUGGING only! + +config.name.dumpTags = Dump tags to console +config.description.dumpTags = Dump tags to console on reading SWF file + +config.name.decompilationTimeoutSingleMethod = AS3: Single method decompilation timeout (seconds) +config.description.decompilationTimeoutSingleMethod = Decompiler will stop ActionScript decompilation after reaching this time in one method + +config.name.lastRenameType = (Internal) Last rename type +config.description.lastRenameType = Last used rename identifiers type + +config.name.lastSaveDir = (Internal) Last save directory +config.description.lastSaveDir = Last used save directory + +config.name.lastOpenDir = (Internal) Last open directory +config.description.lastOpenDir = Last used open directory + +config.name.lastExportDir = (Internal) Last export directory +config.description.lastExportDir = Last used export directory + +config.name.locale = Language +config.description.locale = Locales identifier + +config.name.registerNameFormat = Register variable format +config.description.registerNameFormat = Format of local register variable names. Use %d for register number. + +config.name.maxRecentFileCount = Max recent count +config.description.maxRecentFileCount = Maximum number of recent files + +config.name.recentFiles = (Internal) Recent files +config.description.recentFiles = Recent opened files + +config.name.fontPairing = (Internal) Font pairs for import +config.description.fontPairing = Font pairs for importing new characters + +config.name.lastUpdatesCheckDate = (Internal) Last update check date +config.description.lastUpdatesCheckDate = Date of last checking for updates on server + +config.name.gui.window.width = (Internal) Last window width +config.description.gui.window.width = Last saved window width + +config.name.gui.window.height = (Internal) Last window height +config.description.gui.window.height = Last saved window height + +config.name.gui.window.maximized.horizontal = (Internal) Window maximized horizontally +config.description.gui.window.maximized.horizontal = Last window state - maximized horizontally + +config.name.gui.window.maximized.vertical = (Internal) Window maximized vertically +config.description.gui.window.maximized.vertical = Last window state - maximized vertically + +config.name.gui.avm2.splitPane.dividerLocationPercent = (Internal) AS3 Splitter location +config.description.gui.avm2.splitPane.dividerLocationPercent = + +config.name.gui.actionSplitPane.dividerLocationPercent = (Internal) AS1/2 splitter location +config.description.gui.actionSplitPane.dividerLocationPercent = + +config.name.gui.previewSplitPane.dividerLocationPercent = (Internal) Preview splitter location +config.description.gui.previewSplitPane.dividerLocationPercent = + +config.name.gui.splitPane1.dividerLocationPercent = (Internal) Splitter location 1 +config.description.gui.splitPane1.dividerLocationPercent = + +config.name.gui.splitPane2.dividerLocationPercent = (Internal) Splitter location 2 +config.description.gui.splitPane2.dividerLocationPercent = + +config.name.saveAsExeScaleMode = Save as EXE scale mode +config.description.saveAsExeScaleMode = Scaling mode for EXE export + +config.name.syntaxHighlightLimit = Syntax hilight max chars +config.description.syntaxHighlightLimit = Maximum number of characters to run syntax hilight on + +config.name.guiFontPreviewSampleText = (Internal) Last font preview sample text +config.description.guiFontPreviewSampleText = Last font preview sample text list index + +config.name.gui.fontPreviewWindow.width = (Internal) Last font preview window width +config.description.gui.fontPreviewWindow.width = + +config.name.gui.fontPreviewWindow.height = (Internal) Last font preview window height +config.description.gui.fontPreviewWindow.height = + +config.name.gui.fontPreviewWindow.posX = (Internal) Last font preview window X +config.description.gui.fontPreviewWindow.posX = + +config.name.gui.fontPreviewWindow.posY = (Internal) Last font preview window Y +config.description.gui.fontPreviewWindow.posY = + +config.name.formatting.indent.size = Characters per indent +config.description.formatting.indent.size = Number or spaces(or tabs) for one indentation + +config.name.formatting.indent.useTabs = Tabs for indent +config.description.formatting.indent.useTabs = Use tabs instead of spaces for indentation + +config.name.beginBlockOnNewLine = Curly brace on new line +config.description.beginBlockOnNewLine = Begin block with curly brace on new line + +config.name.check.updates.delay = Updates check delay +config.description.check.updates.delay = Minimum time between automatic checks for updates on application start + +config.name.check.updates.stable = Check for stable versions +config.description.check.updates.stable = Checking for stable version updates + +config.name.check.updates.nightly = Check for nightly versions +config.description.check.updates.nightly = Checking for nightly version updates + +config.name.check.updates.enabled = Updates check enabled +config.description.check.updates.enabled = Automatic checking for updates on application start + +config.name.export.formats = (Internal) Export formats +config.description.export.formats = Last used export formats + +config.name.textExportSingleFile = Export texts to single file +config.description.textExportSingleFile = Exporting texts to one file instead of multiple + +config.name.textExportSingleFileSeparator = Separator of texts in one file text export +config.description.textExportSingleFileSeparator = Text to insert between texts in single file text export + +config.name.textExportSingleFileRecordSeparator = Separator of records in one file text export +config.description.textExportSingleFileRecordSeparator = Text to insert between text records in single file text export + +config.name.warning.experimental.as12edit = Warn on AS1/2 direct edit +config.description.warning.experimental.as12edit = Show warning on AS1/2 experimental direct editation + +config.name.warning.experimental.as3edit = Warn on AS3 direct edit +config.description.warning.experimental.as3edit = Show warning on AS3 experimental direct editation + +config.name.packJavaScripts = Pack JavaScripts +config.description.packJavaScripts = Run JavaScript packer on scripts created on Canvas Export. + +config.name.textExportExportFontFace = Use font-face in SVG export +config.description.textExportExportFontFace = Embed font files in SVG using font-face instead of shapes + +config.name.lzmaFastBytes = LZMA fast bytes (valid values: 5-255) +config.description.lzmaFastBytes = Fast bytes parameter of the LZMA encoder + +#temporary setting, do not translate it +config.name.pluginPath = Plugin Path +config.description.pluginPath = - + +config.name.deobfuscationMode = Deobfuscation mode +config.description.deobfuscationMode = Run deobfuscation on every file before ActionScript decompilation + +config.name.showMethodBodyId = Show method body id +config.description.showMethodBodyId = Shows the id of the methodbody for commandline import + +config.name.export.zoom = (Internal) Export zoom +config.description.export.zoom = Last used export zoom + +config.name.debuggerPort = Debugger port +config.description.debuggerPort = Port used for socket debugging + +config.name.displayDebuggerInfo = (Internal) Display debugger info +config.description.displayDebuggerInfo = Display info about debugger before switching it + +config.name.randomDebuggerPackage = Use random package name for Debugger +config.description.randomDebuggerPackage = This renames Debugger package to random string which makes debugger presence harder to detect by ActionScript + +config.name.lastDebuggerReplaceFunction = (Internal) Last selected trace replacement +config.description.lastDebuggerReplaceFunction = Function name which was last selected in replace trace function with debugger + +config.name.getLocalNamesFromDebugInfo = AS3: Get local register names from debug info +config.description.getLocalNamesFromDebugInfo = If debug info present, renames local registers from _loc_x_ to real names. This can be turned off because some obfuscators use invalid register names there. + +config.name.tagTreeShowEmptyFolders = Show empty folders +config.description.tagTreeShowEmptyFolders = Show empty folders in tag tree. + +config.name.autoLoadEmbeddedSwfs = Auto load embedded SWFs +config.description.autoLoadEmbeddedSwfs = Automaticaly load the embedded SWFs from DefineBinaryData tags. + +config.name.overrideTextExportFileName = Override text export filename +config.description.overrideTextExportFileName = You can customize the filename of the exported text. Use {filename} placeholder to use the filename of current SWF. + +config.name.showOldTextDuringTextEditing = Show old text during text editing +config.description.showOldTextDuringTextEditing = Shows the original text of the text tag with gray color in the preview area. + +config.group.name.import = Import +config.group.description.import = Configuration of imports + +config.name.textImportResizeTextBoundsMode = Text bounds resize mode +config.description.textImportResizeTextBoundsMode = Text bounds resize mode after text editing. + +config.name.showCloseConfirmation = Show again SWF close confirmation +config.description.showCloseConfirmation = Show again SWF close confirmation for modified files. + +config.name.showCodeSavedMessage = Show again code saved message +config.description.showCodeSavedMessage = Show again code saved message + +config.name.showTraitSavedMessage = Show again trait saved message +config.description.showTraitSavedMessage = Show again trait saved message + +config.name.updateProxyAddress = Http Proxy address for checking updates +config.description.updateProxyAddress = Http Proxy address for checking updates. Format: example.com:8080 + +config.name.editorMode = Editor Mode +config.description.editorMode = Make text areas edittable automatically when you select a Text or Script node + +config.name.autoSaveTagModifications = Auto save tag modificatios +config.description.autoSaveTagModifications = Save the changes when you select a new tag in the tree + +config.name.saveSessionOnExit = Save session on exit +config.description.saveSessionOnExit = Save the current session and reopens it after FFDec restart (works only with real files) + +config.name.showDebugMenu = Show debug menu +config.description.showDebugMenu = Shows debug menu in the ribbon + +config.name.allowOnlyOneInstance = Allow only one FFDec instance (Only Windows OS) +config.description.allowOnlyOneInstance = FFDec can be then run only once, all files opened will be added to one window. It works only with Windows operating system. + +config.name.scriptExportSingleFile = Export scripts to single file +config.description.scriptExportSingleFile = Exporting texts to one file instead of multiple + +config.name.setFFDecVersionInExportedFont = Set FFDec version number in exported font +config.description.setFFDecVersionInExportedFont = When this setting is disabled, FFDec won't add the current FFDec version number to the exported font. + +config.name.gui.skin = User Interface Skin +config.description.gui.skin = Look and feel skin + +config.name.lastSessionData = Last session data +config.description.lastSessionData = Contains the opened files from the last session + +config.name.loopMedia = Loop sounds and sprites +config.description.loopMedia = Automatically restarts the playing of the sounds and sprites + +config.name.gui.timeLineSplitPane.dividerLocationPercent = (Internal) Timeline Splitter location +config.description.gui.timeLineSplitPane.dividerLocationPercent =