diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/RenderContext.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/RenderContext.java index 34fad0e43..352cf883f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/RenderContext.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/RenderContext.java @@ -59,6 +59,11 @@ public class RenderContext { */ public Cache displayObjectCache; + /** + * Enable handling buttons + */ + public boolean enableButtons = true; + /** * Clear display object cache. * diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 21ac7346f..36124a3ff 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -1028,7 +1028,11 @@ public class Timeline { SerializableImage img = null; if (cacheAsBitmap && renderContext.displayObjectCache != null) { DisplayObjectCacheKey key = new DisplayObjectCacheKey(layer.placeObjectTag, unzoom, viewRect); - img = renderContext.displayObjectCache.get(key); + try { + img = renderContext.displayObjectCache.get(key); + } catch (NullPointerException npe) { + return; + } } int stateCount = renderContext.stateUnderCursor == null ? 0 : renderContext.stateUnderCursor.size(); @@ -1084,7 +1088,7 @@ public class Timeline { if (drawable instanceof ButtonTag) { dtime = time; dframe = ButtonTag.FRAME_UP; - if (renderContext.cursorPosition != null) { + if (renderContext.cursorPosition != null && renderContext.enableButtons) { int dx = (int) (viewRect.xMin * unzoom); int dy = (int) (viewRect.yMin * unzoom); Point cursorPositionInView = new Point((int) Math.round(renderContext.cursorPosition.x * unzoom) - dx, (int) Math.round(renderContext.cursorPosition.y * unzoom) - dy); @@ -1704,7 +1708,7 @@ public class Timeline { int dframe = time % drawableFrameCount; if (character instanceof ButtonTag) { dframe = ButtonTag.FRAME_UP; - if (renderContext.cursorPosition != null) { + if (renderContext.cursorPosition != null && renderContext.enableButtons) { ButtonTag buttonTag = (ButtonTag) character; Shape buttonShape = buttonTag.getOutline(fast, ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom); int dx = (int) Math.round(viewRect.xMin * unzoom); diff --git a/src/com/jpexs/decompiler/flash/easygui/EasyGuiMain.java b/src/com/jpexs/decompiler/flash/easygui/EasyGuiMain.java index f4b4e7144..c9edfaa23 100644 --- a/src/com/jpexs/decompiler/flash/easygui/EasyGuiMain.java +++ b/src/com/jpexs/decompiler/flash/easygui/EasyGuiMain.java @@ -16,9 +16,18 @@ */ package com.jpexs.decompiler.flash.easygui; -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.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; import java.awt.GraphicsEnvironment; import java.io.File; import java.io.IOException; @@ -71,5 +80,5 @@ public class EasyGuiMain { } catch (InterruptedException ex) { Logger.getLogger(EasyGuiMain.class.getName()).log(Level.SEVERE, null, ex); } - } + } } diff --git a/src/com/jpexs/decompiler/flash/easygui/EasyTagNameResolver.java b/src/com/jpexs/decompiler/flash/easygui/EasyTagNameResolver.java new file mode 100644 index 000000000..8043b76fd --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/EasyTagNameResolver.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2024 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.easygui; + +import com.jpexs.decompiler.flash.gui.TagNameResolverInterface; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; + +/** + * + * @author JPEXS + */ +public class EasyTagNameResolver implements TagNameResolverInterface { + + @Override + public String getTagName(Tag tag) { + if (tag instanceof ImageTag) { + ImageTag it = (ImageTag) tag; + return "image " + it.getCharacterId() + it.getImageFormat().getExtension(); + } + if (tag instanceof ShapeTag) { + ShapeTag st = (ShapeTag) tag; + return "graphic " + st.getCharacterId(); + } + if (tag instanceof MorphShapeTag) { + MorphShapeTag mst = (MorphShapeTag) tag; + return "shapeTween " + mst.getCharacterId(); + } + if (tag instanceof TextTag) { + TextTag t = (TextTag) tag; + return "text " + t.getCharacterId(); + } + if (tag instanceof FontTag) { + FontTag f = (FontTag) tag; + return "font " + f.getCharacterId(); + } + if (tag instanceof DefineSpriteTag) { + DefineSpriteTag st = (DefineSpriteTag) tag; + return "movieClip " + st.getCharacterId(); + } + if (tag instanceof ButtonTag) { + ButtonTag bt = (ButtonTag) tag; + return "button " + bt.getCharacterId(); + } + if (tag instanceof SoundTag) { + SoundTag st = (SoundTag) tag; + return "sound" + (st.getCharacterId() == -1 ? "" : " " + st.getCharacterId()); + } + if (tag instanceof DefineVideoStreamTag) { + DefineVideoStreamTag vt = (DefineVideoStreamTag) tag; + return "video " + vt.getCharacterId(); + } + return "unknown"; + } +} diff --git a/src/com/jpexs/decompiler/flash/easygui/FrameSelectionListener.java b/src/com/jpexs/decompiler/flash/easygui/FrameSelectionListener.java new file mode 100644 index 000000000..2a01c3113 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/FrameSelectionListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010-2024 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.easygui; + + +/** + * @author JPEXS + */ +@FunctionalInterface +public interface FrameSelectionListener { + + public void frameSelected(int frame, int depth); +} diff --git a/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java b/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java index c6135bee1..09e8d6dcd 100644 --- a/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java +++ b/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java @@ -120,47 +120,42 @@ public class LibraryTreeTable extends JTreeTable { if (object instanceof ImageTag) { ImageTag it = (ImageTag) object; label.setIcon(View.getIcon("image16")); - label.setText("image " + it.getCharacterId() + it.getImageFormat().getExtension()); } if (object instanceof ShapeTag) { ShapeTag st = (ShapeTag) object; label.setIcon(View.getIcon("shape16")); - label.setText("graphic " + st.getCharacterId()); } if (object instanceof MorphShapeTag) { MorphShapeTag mst = (MorphShapeTag) object; label.setIcon(View.getIcon("morphshape16")); - label.setText("shapeTween " + mst.getCharacterId()); } if (object instanceof TextTag) { TextTag t = (TextTag) object; label.setIcon(View.getIcon("text16")); - label.setText("text " + t.getCharacterId()); } if (object instanceof FontTag) { FontTag f = (FontTag) object; label.setIcon(View.getIcon("font16")); - label.setText("font " + f.getCharacterId()); } if (object instanceof DefineSpriteTag) { DefineSpriteTag st = (DefineSpriteTag) object; label.setIcon(View.getIcon("sprite16")); - label.setText("movieClip " + st.getCharacterId()); } if (object instanceof ButtonTag) { ButtonTag bt = (ButtonTag) object; label.setIcon(View.getIcon("button16")); - label.setText("button " + bt.getCharacterId()); } if (object instanceof SoundTag) { SoundTag st = (SoundTag) object; label.setIcon(View.getIcon("sound16")); - label.setText("sound" + (st.getCharacterId() == -1 ? "" : " " + st.getCharacterId())); } if (object instanceof DefineVideoStreamTag) { DefineVideoStreamTag vt = (DefineVideoStreamTag) object; label.setIcon(View.getIcon("movie16")); - label.setText("video " + vt.getCharacterId()); + } + if (object instanceof Tag) { + EasyTagNameResolver tagNameResolver = new EasyTagNameResolver(); + label.setText(tagNameResolver.getTagName((Tag) object)); } } return label; diff --git a/src/com/jpexs/decompiler/flash/easygui/MainFrame.java b/src/com/jpexs/decompiler/flash/easygui/MainFrame.java index 33c2ea1d4..f0af7bcb1 100644 --- a/src/com/jpexs/decompiler/flash/easygui/MainFrame.java +++ b/src/com/jpexs/decompiler/flash/easygui/MainFrame.java @@ -20,9 +20,12 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.gui.ImagePanel; import com.jpexs.decompiler.flash.gui.TimelinedMaker; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -45,8 +48,12 @@ import javax.swing.tree.DefaultMutableTreeNode; public class MainFrame extends JFrame { private SWF swf; private LibraryTreeTable libraryTreeTable; - private JSplitPane splitPane; + private JSplitPane verticalSplitPane; + private JSplitPane horizontalSplitPane; private ImagePanel libraryPreviewPanel; + private ImagePanel stagePanel; + private TimelinePanel timelinePanel; + public MainFrame() { setTitle("JPEXS FFDec Easy GUI"); setSize(1024, 768); @@ -54,7 +61,23 @@ public class MainFrame extends JFrame { Container cnt = getContentPane(); cnt.setLayout(new BorderLayout()); - + + stagePanel = new ImagePanel(); + stagePanel.setTagNameResolver(new EasyTagNameResolver()); + stagePanel.setShowAllDepthLevelsInfo(false); + stagePanel.setSelectionMode(true); + stagePanel.addPlaceObjectSelectedListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + PlaceObjectTypeTag pl = stagePanel.getPlaceTagUnderCursor(); + if (pl != null) { + timelinePanel.setDepth(pl.getDepth()); + } + } + }); + timelinePanel = new TimelinePanel(); + verticalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, stagePanel, timelinePanel); + libraryTreeTable = new LibraryTreeTable(); JScrollPane libraryScrollPane = new JScrollPane(libraryTreeTable); @@ -73,9 +96,9 @@ public class MainFrame extends JFrame { libraryPreviewPanel.setPreferredSize(new Dimension(200,200)); - splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JPanel(), libraryPanel); + horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, verticalSplitPane, libraryPanel); libraryScrollPane.getViewport().setBackground(UIManager.getColor("Tree.background")); - cnt.add(splitPane, BorderLayout.CENTER); + cnt.add(horizontalSplitPane, BorderLayout.CENTER); libraryTreeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override @@ -104,13 +127,25 @@ public class MainFrame extends JFrame { } libraryTreeTable.setSwf(swf); - + stagePanel.setTimelined(swf, swf, 0, true, true, true, true, true, false, true); + stagePanel.pause(); + stagePanel.gotoFrame(0); + timelinePanel.setTimelined(swf); + timelinePanel.addFrameSelectionListener(new FrameSelectionListener() { + @Override + public void frameSelected(int frame, int depth) { + stagePanel.selectDepth(depth); + stagePanel.pause(); + stagePanel.gotoFrame(frame); + } + }); } @Override public void setVisible(boolean b) { super.setVisible(b); - splitPane.setDividerLocation(0.7); + verticalSplitPane.setDividerLocation(0.7); + horizontalSplitPane.setDividerLocation(0.7); } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java new file mode 100644 index 000000000..e8ebf60d2 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2010-2024 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.easygui; + +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Timeline; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.SystemColor; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.swing.JPanel; +import org.pushingpixels.substance.internal.utils.SubstanceColorUtilities; + +/** + * @author JPEXS + */ +public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListener { + + private final Timeline timeline; + + public static final int FRAME_WIDTH = 8; + + public static final int FRAME_HEIGHT = 18; + + public static final Color SHAPE_TWEEN_COLOR = new Color(0x59, 0xfe, 0x7c); + + public static final Color MOTION_TWEEN_COLOR = new Color(0xd1, 0xac, 0xf1); + + //public static final Color frameColor = new Color(0xbd, 0xd8, 0xfc); + public static final Color BORDER_COLOR = Color.black; + + public static final Color EMPTY_BORDER_COLOR = new Color(0xbd, 0xd8, 0xfc); + + public static final Color KEY_COLOR = Color.black; + + public static final Color A_COLOR = Color.black; + + public static final Color STOP_COLOR = Color.white; + + public static final Color STOP_BORDER_COLOR = Color.black; + + public static final Color BORDER_LINES_COLOR = new Color(0xde, 0xde, 0xde); + + public static final Color SELECTED_COLOR = new Color(0xff, 0x99, 0x99); + + public static final Color SELECTED_BORDER_COLOR = new Color(0xcc, 0, 0); + + //public static final Color SELECTED_COLOR = new Color(113, 174, 235); + public static final int BORDER_LINES_LENGTH = 2; + + public static final float FONT_SIZE = 10.0f; + + private final List listeners = new ArrayList<>(); + + public Point cursor = null; + + + private int frame = 1; + + private enum BlockType { + + EMPTY, NORMAL, MOTION_TWEEN, SHAPE_TWEEN + } + + public static Color getEmptyFrameColor() { + return SubstanceColorUtilities.getLighterColor(getControlColor(), 0.7); + } + + public static Color getEmptyFrameSecondColor() { + return SubstanceColorUtilities.getLighterColor(getControlColor(), 0.9); + } + + public static Color getBackgroundColor() { + return SystemColor.control; + } + + public static Color getSelectedColor() { + return SystemColor.textHighlight; + } + + private static Color getControlColor() { + return SystemColor.control; + } + + public static Color getFrameColor() { + return SubstanceColorUtilities.getDarkerColor(getControlColor(), 0.1); + } + + public void addFrameSelectionListener(FrameSelectionListener l) { + listeners.add(l); + } + + public void removeFrameSelectionListener(FrameSelectionListener l) { + listeners.remove(l); + } + + public TimelineBodyPanel(Timeline timeline) { + + this.timeline = timeline; + Dimension dim = new Dimension(FRAME_WIDTH * timeline.getFrameCount() + 1, FRAME_HEIGHT * (timeline.getMaxDepth() + 1)); + setSize(dim); + setPreferredSize(dim); + addMouseListener(this); + addKeyListener(this); + setFocusable(true); + } + + @Override + protected void paintComponent(Graphics g1) { + Graphics2D g = (Graphics2D) g1; + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g.setColor(getBackgroundColor()); + g.fillRect(0, 0, getWidth(), getHeight()); + Rectangle clip = g.getClipBounds(); + int frameWidth = FRAME_WIDTH; + int frameHeight = FRAME_HEIGHT; + int start_f = clip.x / frameWidth; + int start_d = clip.y / frameHeight; + int end_f = (clip.x + clip.width) / frameWidth; + int end_d = (clip.y + clip.height) / frameHeight; + + int max_d = timeline.getMaxDepth(); + if (max_d < end_d) { + end_d = max_d; + } + int max_f = timeline.getFrameCount() - 1; + if (max_f < end_f) { + end_f = max_f; + } + + if (end_d - start_d + 1 < 0) { + return; + } + + // draw background + for (int f = start_f; f <= end_f; f++) { + g.setColor((f + 1) % 5 == 0 ? getEmptyFrameSecondColor() : getEmptyFrameColor()); + g.fillRect(f * frameWidth, start_d * frameHeight, frameWidth, (end_d - start_d + 1) * frameHeight); + g.setColor(EMPTY_BORDER_COLOR); + for (int d = start_d; d <= end_d; d++) { + g.drawRect(f * frameWidth, d * frameHeight, frameWidth, frameHeight); + } + } + + // draw selected cell + if (cursor != null) { + g.setColor(getSelectedColor()); + g.fillRect(cursor.x * frameWidth + 1, cursor.y * frameHeight + 1, frameWidth - 1, frameHeight - 1); + } + + g.setColor(A_COLOR); + g.setFont(getFont().deriveFont(FONT_SIZE)); + int awidth = g.getFontMetrics().stringWidth("a"); + for (int f = start_f; f <= end_f; f++) { + if (!timeline.getFrame(f).actions.isEmpty()) { + g.drawString("a", f * frameWidth + frameWidth / 2 - awidth / 2, frameHeight / 2 + FONT_SIZE / 2); + } + } + + Map depthMaxFrames = timeline.getDepthMaxFrame(); + for (int d = start_d; d <= end_d; d++) { + int maxFrame = depthMaxFrames.containsKey(d) ? depthMaxFrames.get(d) : -1; + if (maxFrame < 0) { + continue; + } + + int end_f2 = Math.min(end_f, maxFrame); + int start_f2 = Math.min(start_f, end_f2); + + // find the start frame number of the current block + DepthState dsStart = timeline.getFrame(start_f2).layers.get(d); + for (; start_f2 >= 1; start_f2--) { + DepthState ds = timeline.getFrame(start_f2 - 1).layers.get(d); + if (((dsStart == null) != (ds == null)) + || (ds != null && (dsStart.characterId != ds.characterId || !Objects.equals(dsStart.className, ds.className)))) { + break; + } + } + + for (int f = start_f2; f <= end_f2; f++) { + DepthState fl = timeline.getFrame(f).layers.get(d); + boolean motionTween = fl == null ? false : fl.motionTween; + + DepthState flNext = f < max_f ? timeline.getFrame(f + 1).layers.get(d) : null; + DepthState flPrev = f > 0 ? timeline.getFrame(f - 1).layers.get(d) : null; + + CharacterTag cht = fl == null ? null : fl.getCharacter(); + boolean shapeTween = cht != null && (cht instanceof MorphShapeTag); + boolean motionTweenStart = !motionTween && (flNext != null && flNext.motionTween); + boolean motionTweenEnd = !motionTween && (flPrev != null && flPrev.motionTween); + //boolean shapeTweenStart = shapeTween && (flPrev == null || flPrev.characterId != fl.characterId); + //boolean shapeTweenEnd = shapeTween && (flNext == null || flNext.characterId != fl.characterId); + + /*if (motionTweenStart || motionTweenEnd) { + motionTween = true; + }*/ + int draw_f = f; + int num_frames = 1; + Color backColor; + BlockType blockType; + if (fl == null) { + for (; f + 1 < timeline.getFrameCount(); f++) { + fl = timeline.getFrame(f + 1).layers.get(d); + if (fl != null && fl.getCharacter() != null) { + break; + } + + num_frames++; + } + + backColor = getEmptyFrameColor(); + blockType = BlockType.EMPTY; + } else { + for (; f + 1 < timeline.getFrameCount(); f++) { + fl = timeline.getFrame(f + 1).layers.get(d); + if (fl == null || fl.key) { + break; + } + + num_frames++; + } + + backColor = shapeTween ? SHAPE_TWEEN_COLOR : motionTween ? MOTION_TWEEN_COLOR : getFrameColor(); + blockType = shapeTween ? BlockType.SHAPE_TWEEN : motionTween ? BlockType.MOTION_TWEEN : BlockType.NORMAL; + } + + drawBlock(g, backColor, d, draw_f, num_frames, blockType); + } + } + + if (cursor != null && cursor.x >= start_f && cursor.x <= end_f) { + g.setColor(SELECTED_BORDER_COLOR); + g.drawLine(cursor.x * frameWidth + frameWidth / 2, 0, cursor.x * frameWidth + frameWidth / 2, getHeight()); + } + } + + private void drawBlock(Graphics2D g, Color backColor, int depth, int frame, int num_frames, BlockType blockType) { + int frameWidth = FRAME_WIDTH; + int frameHeight = FRAME_HEIGHT; + + g.setColor(backColor); + g.fillRect(frame * frameWidth, depth * frameHeight, num_frames * frameWidth, frameHeight); + g.setColor(BORDER_COLOR); + g.drawRect(frame * frameWidth, depth * frameHeight, num_frames * frameWidth, frameHeight); + + boolean selected = false; + if (cursor != null && frame <= cursor.x && (frame + num_frames) > cursor.x && depth == cursor.y) { + selected = true; + } + + if (selected) { + g.setColor(getSelectedColor()); + g.fillRect(cursor.x * frameWidth + 1, depth * frameHeight + 1, frameWidth - 1, frameHeight - 1); + } + + boolean isTween = blockType == BlockType.MOTION_TWEEN || blockType == BlockType.SHAPE_TWEEN; + + g.setColor(KEY_COLOR); + if (isTween) { + g.drawLine(frame * frameWidth, depth * frameHeight + frameHeight * 3 / 4, + frame * frameWidth + num_frames * frameWidth - frameWidth / 2, depth * frameHeight + frameHeight * 3 / 4 + ); + } + + if (blockType == BlockType.EMPTY) { + g.drawOval(frame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2); + } else { + g.fillOval(frame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2); + } + + if (num_frames > 1) { + int endFrame = frame + num_frames - 1; + if (isTween) { + g.fillOval(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2); + } else { + g.setColor(STOP_COLOR); + g.fillRect(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight / 2 - 2, frameWidth / 2, frameHeight / 2); + g.setColor(STOP_BORDER_COLOR); + g.drawRect(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight / 2 - 2, frameWidth / 2, frameHeight / 2); + } + + g.setColor(BORDER_LINES_COLOR); + for (int n = frame + 1; n < frame + num_frames; n++) { + g.drawLine(n * frameWidth, depth * frameHeight + 1, n * frameWidth, depth * frameHeight + BORDER_LINES_LENGTH); + g.drawLine(n * frameWidth, depth * frameHeight + frameHeight - 1, n * frameWidth, depth * frameHeight + frameHeight - BORDER_LINES_LENGTH); + } + } + } + + @Override + public void mouseClicked(MouseEvent e) { + + } + + public void depthSelect(int depth) { + frameSelect(frame, depth); + } + + public void frameSelect(int frame, int depth) { + if (cursor != null && cursor.x == frame && (cursor.y == depth || depth == -1)) { + return; + } + if (depth == -1 && cursor != null) { + depth = cursor.y; + } + cursor = new Point(frame, depth); + for (FrameSelectionListener l : listeners) { + l.frameSelected(frame, depth); + } + repaint(); + this.frame = frame; + } + + @Override + public void mousePressed(MouseEvent e) { + Point p = e.getPoint(); + p.x = p.x / FRAME_WIDTH; + p.y = p.y / FRAME_HEIGHT; + if (p.x >= timeline.getFrameCount()) { + p.x = timeline.getFrameCount() - 1; + } + int maxDepth = timeline.getMaxDepth(); + if (p.y > maxDepth) { + p.y = maxDepth; + } + frameSelect(p.x, p.y); + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + switch (e.getKeyCode()) { + case 37: //left + if (cursor.x > 0) { + frameSelect(cursor.x - 1, cursor.y); + } + break; + case 39: //right + if (cursor.x < timeline.getFrameCount() - 1) { + frameSelect(cursor.x + 1, cursor.y); + } + break; + case 38: //up + if (cursor.y > 0) { + frameSelect(cursor.x, cursor.y - 1); + } + break; + case 40: //down + if (cursor.y < timeline.getMaxDepth()) { + frameSelect(cursor.x, cursor.y + 1); + } + break; + } + } + + @Override + public void keyReleased(KeyEvent e) { + } + + public Rectangle getDepthBounds(int depth) { + return getFrameBounds(frame, depth); + } + public Rectangle getFrameBounds(int frame, int depth) { + Rectangle rect = new Rectangle(); + rect.width = FRAME_WIDTH; + rect.height = FRAME_HEIGHT; + rect.x = frame * FRAME_WIDTH; + rect.y = depth * FRAME_HEIGHT; + return rect; + } +} + diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineDepthPanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineDepthPanel.java new file mode 100644 index 000000000..2dca55bca --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineDepthPanel.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010-2024 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.easygui; + +import com.jpexs.decompiler.flash.timeline.Timeline; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import javax.swing.JPanel; + +/** + * @author JPEXS + */ +public class TimelineDepthPanel extends JPanel { + + private final int maxDepth; + + public static final int PADDING = 5; + + public static final float FONT_SIZE = 10.0f; + + private int scrollOffset = 0; + + public static final Color BORDER_COLOR = Color.lightGray; + + public static final Color FONT_COLOR = Color.black; + + public TimelineDepthPanel(Timeline timeline) { + maxDepth = timeline.getMaxDepth(); + String maxDepthStr = Integer.toString(maxDepth); + setFont(getFont().deriveFont(FONT_SIZE)); + int maxDepthW = getFontMetrics(getFont()).stringWidth(maxDepthStr); + Dimension dim = new Dimension(maxDepthW + 2 * PADDING, Integer.MAX_VALUE); + setSize(dim); + setPreferredSize(dim); + } + + public void scroll(int offset) { + this.scrollOffset = offset; + repaint(); + } + + @Override + protected void paintComponent(Graphics g) { + Rectangle clip = g.getClipBounds(); + int yofs = TimelineBodyPanel.FRAME_HEIGHT - (scrollOffset % TimelineBodyPanel.FRAME_HEIGHT); + int start_d = (scrollOffset + clip.y) / TimelineBodyPanel.FRAME_HEIGHT; + int end_d = (scrollOffset + clip.y + clip.height) / TimelineBodyPanel.FRAME_HEIGHT; + int d_count = end_d - start_d; + g.setColor(TimelineBodyPanel.getBackgroundColor()); + g.fillRect(0, 0, getWidth(), getHeight()); + for (int d = 0; d < d_count; d++) { + g.setColor(BORDER_COLOR); + g.drawLine(0, yofs + d * TimelineBodyPanel.FRAME_HEIGHT + 1, getWidth(), yofs + d * TimelineBodyPanel.FRAME_HEIGHT + 1); + int curr_d = start_d + d; + g.setColor(FONT_COLOR); + g.drawString(start_d + d == 0 ? "a" : Integer.toString(curr_d), PADDING, yofs + d * TimelineBodyPanel.FRAME_HEIGHT - PADDING); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java new file mode 100644 index 000000000..7aff535a7 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2010-2024 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.easygui; + +import com.jpexs.decompiler.flash.gui.FasterScrollPane; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.SystemColor; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +/** + * @author JPEXS + */ +public class TimelinePanel extends JPanel { + + private TimelineBodyPanel timelineBodyPanel; + + private TimelineTimePanel timePanel; + + private TimelineDepthPanel depthPanel; + + private Timeline timeline; + + public static final int FRAME_WIDTH = 8; + + public static final int FRAME_HEIGHT = 18; + + + private JScrollPane timelineBodyScrollPane; + + //public static final Color backgroundColor = new Color(0xd9, 0xe7, 0xfa); + public static Color getBackgroundColor() { + return SystemColor.control; + } + + public Timeline getTimeline() { + return timeline; + } + + public void addFrameSelectionListener(FrameSelectionListener l) { + timelineBodyPanel.addFrameSelectionListener(l); + } + + public void removeFrameSelectionListener(FrameSelectionListener l) { + timelineBodyPanel.removeFrameSelectionListener(l); + } + + public void setDepth(int depth) { + timelineBodyPanel.depthSelect(depth); + timelineBodyPanel.scrollRectToVisible(timelineBodyPanel.getDepthBounds(depth)); + } + + public void setTimelined(Timelined timelined) { + this.removeAll(); + if (timelined == null) { + this.revalidate(); + return; + } + timeline = timelined.getTimeline(); + timelineBodyPanel = new TimelineBodyPanel(timeline); + setLayout(new BorderLayout()); + + timelineBodyScrollPane = new FasterScrollPane(timelineBodyPanel); + + depthPanel = new TimelineDepthPanel(timeline); + + timePanel = new TimelineTimePanel(); + + JPanel row1Panel = new JPanel(); + row1Panel.setLayout(new BorderLayout()); + JPanel sepPanel = new JPanel(); + sepPanel.setBackground(getBackgroundColor()); + sepPanel.setPreferredSize(new Dimension(depthPanel.getWidth(), timePanel.getHeight())); + row1Panel.add(sepPanel, BorderLayout.WEST); + row1Panel.add(timePanel, BorderLayout.CENTER); + + JPanel row2Panel = new JPanel(); + row2Panel.setLayout(new BorderLayout()); + row2Panel.add(depthPanel, BorderLayout.WEST); + row2Panel.add(timelineBodyScrollPane, BorderLayout.CENTER); + + add(row1Panel, BorderLayout.NORTH); + add(row2Panel, BorderLayout.CENTER); + + timelineBodyScrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() { + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + timePanel.scroll(e.getValue()); + } + }); + timelineBodyScrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { + + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + depthPanel.scroll(e.getValue()); + } + }); + + final TimelineTimePanel ftime = timePanel; + timelineBodyPanel.addFrameSelectionListener(new FrameSelectionListener() { + + @Override + public void frameSelected(int frame, int depth) { + ftime.frameSelect(frame); + } + }); + final TimelineBodyPanel ftimeline = timelineBodyPanel; + timePanel.addFrameSelectionListener(new FrameSelectionListener() { + + @Override + public void frameSelected(int frame, int depth) { + ftimeline.frameSelect(frame, depth); + } + }); + this.revalidate(); + } +} diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java new file mode 100644 index 000000000..12c0d18f4 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2010-2024 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.easygui; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JPanel; + +/** + * @author JPEXS + */ +public class TimelineTimePanel extends JPanel implements MouseListener { + + public static final Color borderColor = Color.black; + + public static final int lineLength = 3; + + public static final int lineTextSpace = 3; + + public static final Color fontColor = Color.black; + + public float fontSize = 10.0f; + + private int scrollOffset = 0; + + private int selectedFrame = -1; + + private final List listeners = new ArrayList<>(); + + public TimelineTimePanel() { + Dimension dim = new Dimension(Integer.MAX_VALUE, TimelineBodyPanel.FRAME_HEIGHT); + setSize(dim); + setPreferredSize(dim); + addMouseListener(this); + } + + public void addFrameSelectionListener(FrameSelectionListener l) { + listeners.add(l); + } + + public void removeFrameSelectionListener(FrameSelectionListener l) { + listeners.remove(l); + } + + public void frameSelect(int frame) { + if (selectedFrame == frame) { + return; + } + for (FrameSelectionListener l : listeners) { + l.frameSelected(frame, -1); + } + selectedFrame = frame; + repaint(); + } + + public void scroll(int offset) { + this.scrollOffset = offset; + repaint(); + } + + @Override + protected void paintComponent(Graphics g) { + Rectangle clip = g.getClipBounds(); + int start_f = (scrollOffset + clip.x) / TimelineBodyPanel.FRAME_WIDTH; + int end_f = (scrollOffset + clip.x + clip.width) / TimelineBodyPanel.FRAME_WIDTH; + g.setColor(TimelineBodyPanel.getBackgroundColor()); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(borderColor); + int xofs = TimelineBodyPanel.FRAME_WIDTH - scrollOffset % TimelineBodyPanel.FRAME_WIDTH - 1; + for (int f = 0; f <= end_f; f++) { + g.drawLine(xofs + f * TimelineBodyPanel.FRAME_WIDTH + 1, TimelineBodyPanel.FRAME_HEIGHT - 1, xofs + f * TimelineBodyPanel.FRAME_WIDTH + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength); + } + g.setFont(g.getFont().deriveFont(fontSize)); + for (int f = 0; f <= end_f; f++) { + int cur_f = start_f + f; + if (selectedFrame == cur_f) { + g.setColor(TimelineBodyPanel.SELECTED_COLOR); + g.fillRect(xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + 1, 0, TimelineBodyPanel.FRAME_WIDTH, TimelineBodyPanel.FRAME_HEIGHT - 1); + g.setColor(TimelineBodyPanel.SELECTED_BORDER_COLOR); + g.drawRect(xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + 1, 0, TimelineBodyPanel.FRAME_WIDTH, TimelineBodyPanel.FRAME_HEIGHT - 1); + } + g.setColor(fontColor); + if ((cur_f + 1) % 5 == 0 || cur_f == 0) { + String timeStr = Integer.toString(cur_f + 1); + int w = g.getFontMetrics().stringWidth(timeStr); + g.drawString(timeStr, xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + TimelineBodyPanel.FRAME_WIDTH / 2 - w / 2, TimelineBodyPanel.FRAME_HEIGHT - lineLength - lineTextSpace); + } + } + } + + @Override + public void mouseClicked(MouseEvent e) { + } + + @Override + public void mousePressed(MouseEvent e) { + frameSelect((scrollOffset + e.getX()) / TimelineBodyPanel.FRAME_WIDTH); + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } +} diff --git a/src/com/jpexs/decompiler/flash/easygui/View.java b/src/com/jpexs/decompiler/flash/easygui/View.java index a0e4ef7e5..56c8da1ae 100644 --- a/src/com/jpexs/decompiler/flash/easygui/View.java +++ b/src/com/jpexs/decompiler/flash/easygui/View.java @@ -26,5 +26,5 @@ import javax.swing.ImageIcon; public class View { public static Icon getIcon(String name) { return new ImageIcon(com.jpexs.decompiler.flash.gui.View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); - } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/DefaultTagNameResolver.java b/src/com/jpexs/decompiler/flash/gui/DefaultTagNameResolver.java new file mode 100644 index 000000000..8308a8f34 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/DefaultTagNameResolver.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 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.tags.Tag; + +/** + * + * @author JPEXS + */ +public class DefaultTagNameResolver implements TagNameResolverInterface { + + @Override + public String getTagName(Tag tag) { + return tag.toString(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 298e4976a..134226168 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -306,6 +306,24 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private JPanel topPanel; + private TagNameResolverInterface tagNameResolver = new DefaultTagNameResolver(); + + private boolean showAllDepthLevelsInfo = true; + + private boolean selectionMode = false; + + public void setSelectionMode(boolean selectionMode) { + this.selectionMode = selectionMode; + } + + public void setTagNameResolver(TagNameResolverInterface tagNameResolver) { + this.tagNameResolver = tagNameResolver; + } + + public void setShowAllDepthLevelsInfo(boolean showAllDepthLevelsInfo) { + this.showAllDepthLevelsInfo = showAllDepthLevelsInfo; + } + public void setTopPanelVisible(boolean visible) { topPanel.setVisible(visible); } @@ -1121,8 +1139,11 @@ public final class ImagePanel extends JPanel implements MediaDisplay { @Override public void mousePressed(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { - if (altDown) { + if (altDown || selectionMode) { if (depthStateUnderCursor != null) { + if (selectionMode) { + selectDepth(depthStateUnderCursor.depth); + } firePlaceObjectSelected(); } return; @@ -2335,7 +2356,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { lastMouseEvent = e; redraw(); ButtonTag button = iconPanel.mouseOverButton; - if (button != null && freeTransformDepth == -1) { + if (button != null && freeTransformDepth == -1 && !frozen) { DefineButtonSoundTag sounds = button.getSounds(); if (!muted && sounds != null && sounds.buttonSoundChar2 != 0) { // OverUpToOverDown CharacterTag soundCharTag = swf.getCharacter(sounds.buttonSoundChar2); @@ -2380,7 +2401,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { lastMouseEvent = e; redraw(); ButtonTag button = iconPanel.mouseOverButton; - if (!muted && button != null && freeTransformDepth == -1) { + if (!muted && button != null && freeTransformDepth == -1 && !frozen) { DefineButtonSoundTag sounds = button.getSounds(); if (sounds != null && sounds.buttonSoundChar3 != 0) { // OverDownToOverUp CharacterTag soundCharTag = swf.getCharacter(sounds.buttonSoundChar3); @@ -3264,6 +3285,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { renderContext.mouseButton = mouseButton; renderContext.stateUnderCursor = new ArrayList<>(); + renderContext.enableButtons = !frozen; SerializableImage img; try { @@ -3431,7 +3453,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } } - boolean handCursor = renderContext.mouseOverButton != null || !autoPlayed; + boolean handCursor = renderContext.mouseOverButton != null || !autoPlayed && !frozen; if (showObjectsUnderCursor && autoPlayed) { @@ -3445,12 +3467,16 @@ public final class ImagePanel extends JPanel implements MediaDisplay { for (int i = renderContext.stateUnderCursor.size() - 1; i >= 0; i--) { DepthState ds = renderContext.stateUnderCursor.get(i); if (!first) { + if (!showAllDepthLevelsInfo) { + break; + } ret.append(", "); } first = false; CharacterTag c = ds.getCharacter(); - ret.append(c.toString()); + + ret.append(tagNameResolver.getTagName(c)); if (ds.depth > -1) { ret.append(" "); ret.append(AppStrings.translate("imagePanel.depth")); @@ -3500,7 +3526,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } if (freeTransformDepth == -1 && hilightedPoints == null) { Cursor newCursor; - if (iconPanel.isAltDown()) { + if (iconPanel.isAltDown() && !selectionMode) { if (depthStateUnderCursor == null) { newCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); } else { diff --git a/src/com/jpexs/decompiler/flash/gui/TagNameResolverInterface.java b/src/com/jpexs/decompiler/flash/gui/TagNameResolverInterface.java new file mode 100644 index 000000000..3f4135bd0 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/TagNameResolverInterface.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.tags.Tag; + +/** + * + * @author JPEXS + */ +public interface TagNameResolverInterface { + public String getTagName(Tag tag); +}