From 3dd6e6fad81203dc54802e653cd041541945a1d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 6 Oct 2024 01:12:38 +0200 Subject: [PATCH] Simple drag and drop from library to stage. --- .../flash/tags/DefineSpriteTag.java | 4 +- .../flash/easygui/LibraryTreeTable.java | 4 + .../decompiler/flash/easygui/MainFrame.java | 223 +++++++++++++----- .../flash/easygui/TagTransferHandler.java | 59 +++++ .../flash/easygui/TagTransferable.java | 56 +++++ .../flash/easygui/TimelineBodyPanel.java | 6 + .../flash/easygui/TimelinePanel.java | 4 + 7 files changed, 298 insertions(+), 58 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/easygui/TagTransferHandler.java create mode 100644 src/com/jpexs/decompiler/flash/easygui/TagTransferable.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java index deeb2b0df..bb92ac69f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java @@ -84,11 +84,11 @@ public class DefineSpriteTag extends DrawableTag implements Timelined { private List subTags; @Internal - public ReadOnlyTagList readOnlyTags; + public transient ReadOnlyTagList readOnlyTags; public boolean hasEndTag; - private Timeline timeline; + private transient Timeline timeline; private boolean isSingleFrameInitialized; diff --git a/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java b/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java index dcf920080..df88618bc 100644 --- a/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java +++ b/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java @@ -35,6 +35,7 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.JLabel; import javax.swing.JTree; +import javax.swing.ListSelectionModel; import javax.swing.event.TreeModelListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; @@ -94,6 +95,9 @@ public class LibraryTreeTable extends JTreeTable { } } }); + setTransferHandler(new TagTransferHandler()); + setDragEnabled(true); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } public void setSwf(SWF swf) { diff --git a/src/com/jpexs/decompiler/flash/easygui/MainFrame.java b/src/com/jpexs/decompiler/flash/easygui/MainFrame.java index b3015ff82..ad0178845 100644 --- a/src/com/jpexs/decompiler/flash/easygui/MainFrame.java +++ b/src/com/jpexs/decompiler/flash/easygui/MainFrame.java @@ -21,15 +21,26 @@ import com.jpexs.decompiler.flash.gui.ImagePanel; import com.jpexs.decompiler.flash.gui.RegistrationPointPosition; import com.jpexs.decompiler.flash.gui.TimelinedMaker; import com.jpexs.decompiler.flash.gui.TransformPanel; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.RemoveObject2Tag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.types.MATRIX; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Insets; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Point2D; @@ -42,6 +53,7 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; +import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -54,27 +66,28 @@ import javax.swing.tree.DefaultMutableTreeNode; * @author JPEXS */ public class MainFrame extends JFrame { + private SWF swf; private LibraryTreeTable libraryTreeTable; private JSplitPane verticalSplitPane; private JSplitPane horizontalSplitPane; private ImagePanel libraryPreviewPanel; private ImagePanel stagePanel; - private TimelinePanel timelinePanel; + private TimelinePanel timelinePanel; private JButton undoButton; private JButton redoButton; private UndoManager undoManager; private JTabbedPane rightTabbedPane; private TransformPanel transformPanel; - + public MainFrame() { setTitle("JPEXS FFDec Easy GUI"); setSize(1024, 768); setDefaultCloseOperation(EXIT_ON_CLOSE); - + Container cnt = getContentPane(); cnt.setLayout(new BorderLayout()); - + stagePanel = new ImagePanel(); stagePanel.setTagNameResolver(new EasyTagNameResolver()); stagePanel.setShowAllDepthLevelsInfo(false); @@ -86,29 +99,29 @@ public class MainFrame extends JFrame { if (pl != null) { timelinePanel.setDepth(pl.getDepth()); } - } + } }); - + stagePanel.addTransformChangeListener(new Runnable() { @Override public void run() { final int depth = stagePanel.getSelectedDepth(); - final int frame = stagePanel.getFrame(); + final int frame = stagePanel.getFrame(); final MATRIX newMatrix = stagePanel.getNewMatrix().toMATRIX(); MATRIX previousMatrix = null; synchronized (stagePanel) { DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depth); previousMatrix = ds.matrix; } - + final Point2D regPoint = stagePanel.getRegistrationPoint(); final RegistrationPointPosition regPointPos = stagePanel.getRegistrationPointPosition(); - + final MATRIX fpreviousMatrix = previousMatrix; - + final boolean transformEnabled = transformEnabled(); undoManager.doOperation(new DoableOperation() { - + @Override public void doOperation() { timelinePanel.setFrame(frame, depth); @@ -121,7 +134,7 @@ public class MainFrame extends JFrame { if (regPointPos != null) { stagePanel.setRegistrationPointPosition(regPointPos); } - } + } } @Override @@ -138,19 +151,104 @@ public class MainFrame extends JFrame { @Override public String getDescription() { return transformEnabled ? "Transform" : "Move"; - } - }); - + } + }); + } - + }); - + + stagePanel.setTransferHandler(new TransferHandler() { + @Override + public boolean canImport(TransferHandler.TransferSupport support) { + return support.isDataFlavorSupported(TagTransferable.TAG_FLAVOR); + } + + @Override + public boolean importData(TransferHandler.TransferSupport support) { + if (!canImport(support)) { + return false; + } + try { + Transferable transferable = support.getTransferable(); + Tag tag = (Tag) transferable.getTransferData(TagTransferable.TAG_FLAVOR); + + if ((tag instanceof DefineSpriteTag) + || (tag instanceof ShapeTag) + || (tag instanceof TextTag) + || (tag instanceof ButtonTag) + ) { + + undoManager.doOperation(new DoableOperation() { + + private PlaceObject2Tag place; + private RemoveObject2Tag remove; + + @Override + public void doOperation() { + CharacterTag ch = (CharacterTag) tag; + int maxDepth = stagePanel.getTimelined().getTimeline().getMaxDepth(); + int newDepth = maxDepth + 1; + Timelined timelined = stagePanel.getTimelined(); + ShowFrameTag showFrameTag = timelined.getTimeline().getFrame(stagePanel.getFrame()).showFrameTag; + place = new PlaceObject2Tag(timelined.getSwf()); + place.depth = newDepth; + place.placeFlagHasCharacter = true; + place.characterId = ch.getCharacterId(); + place.matrix = new MATRIX(); + place.placeFlagHasMatrix = true; + place.setTimelined(timelined); + if (showFrameTag == null) { + timelined.addTag(place); + } else { + timelined.addTag(timelined.indexOfTag(showFrameTag), place); + + remove = new RemoveObject2Tag(timelined.getSwf()); + remove.depth = newDepth; + timelined.addTag(timelined.indexOfTag(showFrameTag) + 1, remove); + } + timelined.resetTimeline(); + stagePanel.repaint(); + timelinePanel.refresh(); + timelinePanel.setDepth(newDepth); + } + + @Override + public void undoOperation() { + Timelined timelined = place.getTimelined(); + timelined.removeTag(place); + if (remove != null) { + timelined.removeTag(remove); + } + timelined.resetTimeline(); + stagePanel.repaint(); + timelinePanel.refresh(); + } + + @Override + public String getDescription() { + return "Add to stage"; + } + + }); + + return true; + } + } catch (UnsupportedFlavorException | IOException ex) { + ex.printStackTrace(); + //ignored + } + + return false; + } + }); + undoManager = new UndoManager(); - + JPanel topPanel = new JPanel(new BorderLayout()); - + JPanel toolbarPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - + undoButton = new JButton(View.getIcon("rotateanticlockwise16")); undoButton.setToolTipText("Undo"); undoButton.setMargin(new Insets(5, 5, 5, 5)); @@ -158,9 +256,9 @@ public class MainFrame extends JFrame { @Override public void actionPerformed(ActionEvent e) { undoManager.undo(); - } + } }); - + redoButton = new JButton(View.getIcon("rotateclockwise16")); redoButton.setToolTipText("Redo"); redoButton.setMargin(new Insets(5, 5, 5, 5)); @@ -168,9 +266,9 @@ public class MainFrame extends JFrame { @Override public void actionPerformed(ActionEvent e) { undoManager.redo(); - } + } }); - + Runnable undoChangeListener = new Runnable() { @Override public void run() { @@ -186,49 +284,55 @@ public class MainFrame extends JFrame { } else { redoButton.setToolTipText("Cannot redo"); } - } + } }; - + undoManager.addChangeListener(undoChangeListener); undoChangeListener.run(); - + toolbarPanel.add(undoButton); toolbarPanel.add(redoButton); - + topPanel.add(toolbarPanel, BorderLayout.NORTH); topPanel.add(stagePanel, BorderLayout.CENTER); - - timelinePanel = new TimelinePanel(); + + timelinePanel = new TimelinePanel(); verticalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topPanel, timelinePanel); - + libraryTreeTable = new LibraryTreeTable(); JScrollPane libraryScrollPane = new JScrollPane(libraryTreeTable); - + JPanel libraryPanel = new JPanel(new BorderLayout()); libraryPanel.add(libraryScrollPane, BorderLayout.CENTER); - + libraryPreviewPanel = new ImagePanel(); - libraryPreviewPanel.setTopPanelVisible(false); - + libraryPreviewPanel.setTopPanelVisible(false); + libraryPanel.add(libraryPreviewPanel, BorderLayout.NORTH); - - libraryPreviewPanel.setPreferredSize(new Dimension(200,200)); - + + libraryPreviewPanel.setPreferredSize(new Dimension(200, 200)); + rightTabbedPane = new JTabbedPane(); rightTabbedPane.addTab("Library", libraryPanel); - + JPanel transformTab = new JPanel(new BorderLayout()); transformPanel = new TransformPanel(stagePanel, false); transformTab.add(new JScrollPane(transformPanel), BorderLayout.CENTER); - + rightTabbedPane.addTab("Transform", transformTab); rightTabbedPane.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { int depth = stagePanel.getSelectedDepth(); + if (depth != -1) { + DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(stagePanel.getFrame()).layers.get(depth); + if (ds == null) { + depth = -1; + } + } transformPanel.setVisible(depth != -1); if (transformEnabled()) { - stagePanel.freeTransformDepth(depth); + stagePanel.freeTransformDepth(depth); stagePanel.setTransformSelectionMode(true); } else { stagePanel.freeTransformDepth(-1); @@ -237,11 +341,11 @@ public class MainFrame extends JFrame { } } }); - - horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, verticalSplitPane, rightTabbedPane); - libraryScrollPane.getViewport().setBackground(UIManager.getColor("Tree.background")); - cnt.add(horizontalSplitPane, BorderLayout.CENTER); - + + horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, verticalSplitPane, rightTabbedPane); + libraryScrollPane.getViewport().setBackground(UIManager.getColor("Tree.background")); + cnt.add(horizontalSplitPane, BorderLayout.CENTER); + libraryTreeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { @@ -253,25 +357,25 @@ public class MainFrame extends JFrame { Object obj = n.getUserObject(); if (obj instanceof Tag) { Tag t = (Tag) obj; - libraryPreviewPanel.setTimelined(TimelinedMaker.makeTimelined(t), t.getSwf(), + libraryPreviewPanel.setTimelined(TimelinedMaker.makeTimelined(t), t.getSwf(), -1, false, true, true, true, true, false, true); libraryPreviewPanel.zoomFit(); } else { libraryPreviewPanel.clearAll(); } - } + } }); } - + private boolean transformEnabled() { return rightTabbedPane.getSelectedIndex() == 1; } - + public void open(File file) throws IOException, InterruptedException { - try(FileInputStream fis = new FileInputStream(file)) { + try (FileInputStream fis = new FileInputStream(file)) { swf = new SWF(fis, true); } - + libraryTreeTable.setSwf(swf); stagePanel.setTimelined(swf, swf, 0, true, true, true, true, true, false, true); stagePanel.pause(); @@ -281,11 +385,18 @@ public class MainFrame extends JFrame { @Override public void frameSelected(int frame, int depth) { stagePanel.pause(); - stagePanel.gotoFrame(frame + 1); - stagePanel.selectDepth(depth); + stagePanel.gotoFrame(frame + 1); + stagePanel.selectDepth(depth); if (transformEnabled()) { stagePanel.freeTransformDepth(depth); - } + } + + if (depth != -1) { + DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(stagePanel.getFrame()).layers.get(depth); + if (ds == null) { + depth = -1; + } + } transformPanel.setVisible(depth != -1); } }); @@ -296,5 +407,5 @@ public class MainFrame extends JFrame { super.setVisible(b); verticalSplitPane.setDividerLocation(0.7); horizontalSplitPane.setDividerLocation(0.7); - } + } } diff --git a/src/com/jpexs/decompiler/flash/easygui/TagTransferHandler.java b/src/com/jpexs/decompiler/flash/easygui/TagTransferHandler.java new file mode 100644 index 000000000..e5200af02 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/TagTransferHandler.java @@ -0,0 +1,59 @@ +/* + * 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.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import de.javagl.treetable.JTreeTable; +import java.awt.datatransfer.Transferable; +import javax.swing.JComponent; +import javax.swing.TransferHandler; +import javax.swing.tree.DefaultMutableTreeNode; + +/** + * + * @author JPEXS + */ + class TagTransferHandler extends TransferHandler { + + @Override + protected Transferable createTransferable(JComponent c) { + JTreeTable table = (JTreeTable) c; + int selectedRow = table.getSelectedRow(); + + if (selectedRow >= 0) { + DefaultMutableTreeNode mn = (DefaultMutableTreeNode) table.getModel().getValueAt(selectedRow, 0); + Object o = mn.getUserObject(); + if ((o instanceof DefineSpriteTag) + || (o instanceof ShapeTag) + || (o instanceof TextTag) + || (o instanceof ButtonTag) + ) { + return new TagTransferable((Tag) o); + } + } + return null; + } + + @Override + public int getSourceActions(JComponent c) { + return COPY; + } + } diff --git a/src/com/jpexs/decompiler/flash/easygui/TagTransferable.java b/src/com/jpexs/decompiler/flash/easygui/TagTransferable.java new file mode 100644 index 000000000..eb7d9003f --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/TagTransferable.java @@ -0,0 +1,56 @@ +/* + * 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.tags.Tag; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; + +/** + * + * @author JPEXS + */ +public class TagTransferable implements Transferable { + + public static final DataFlavor TAG_FLAVOR = new DataFlavor(Tag.class, "Tag"); + + private Tag tag; + + public TagTransferable(Tag tag) { + this.tag = tag; + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{TAG_FLAVOR}; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + return TAG_FLAVOR.equals(flavor); + } + + @Override + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if (TAG_FLAVOR.equals(flavor)) { + return tag; + } + return null; + } +} diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java index f85c99b15..de652a2c2 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java @@ -430,5 +430,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe rect.y = depth * FRAME_HEIGHT; return rect; } + + public void refresh() { + Dimension dim = new Dimension(FRAME_WIDTH * timeline.getFrameCount() + 1, FRAME_HEIGHT * (timeline.getMaxDepth() + 1)); + setSize(dim); + setPreferredSize(dim); + } } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java index 1b2d56890..c0d1a32e1 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java @@ -73,6 +73,10 @@ public class TimelinePanel extends JPanel { timelineBodyPanel.frameSelect(frame, depth); } + public void refresh() { + timelineBodyPanel.refresh(); + } + public void setTimelined(Timelined timelined) { this.removeAll(); if (timelined == null) {