diff --git a/src/com/jpexs/decompiler/flash/easygui/EasyPanel.java b/src/com/jpexs/decompiler/flash/easygui/EasyPanel.java index 8c02dd12d..3420fa945 100644 --- a/src/com/jpexs/decompiler/flash/easygui/EasyPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/EasyPanel.java @@ -16,451 +16,44 @@ */ package com.jpexs.decompiler.flash.easygui; -import com.jpexs.decompiler.flash.DefineBeforeUsageFixer; -import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.FasterScrollPane; -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.gui.View; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -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.Frame; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.types.MATRIX; import java.awt.BorderLayout; -import java.awt.Color; -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; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; import java.util.List; -import javax.swing.JButton; -import javax.swing.JFrame; 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; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.tree.DefaultMutableTreeNode; /** * * @author JPEXS */ public class EasyPanel extends JPanel { - - private LibraryTreeTable libraryTreeTable; - private JSplitPane verticalSplitPane; - private JSplitPane horizontalSplitPane; - private ImagePanel libraryPreviewPanel; - private ImagePanel stagePanel; - private TimelinePanel timelinePanel; - private JButton undoButton; - private JButton redoButton; - private UndoManager undoManager; - private JTabbedPane rightTabbedPane; - private TransformPanel transformPanel; - + private TabSwitcher tabSwitcher; + private EasySwfPanel easySwfPanel; public EasyPanel() { - setSize(1024, 768); + easySwfPanel = new EasySwfPanel(); + tabSwitcher = new TabSwitcher<>(easySwfPanel); setLayout(new BorderLayout()); - - stagePanel = new ImagePanel(); - stagePanel.setTagNameResolver(new EasyTagNameResolver()); - stagePanel.setShowAllDepthLevelsInfo(false); - stagePanel.setSelectionMode(true); - stagePanel.addPlaceObjectSelectedListener(new ActionListener() { + add(tabSwitcher, BorderLayout.CENTER); + tabSwitcher.addTabSwitchedListener(new TabSwitchedListener() { @Override - public void actionPerformed(ActionEvent e) { - PlaceObjectTypeTag pl = stagePanel.getPlaceTagUnderCursor(); - if (pl != null) { - timelinePanel.setDepth(pl.getDepth()); - } - transformPanel.setVisible(pl != null); - } - }); - - stagePanel.addTransformChangeListener(new Runnable() { - @Override - public void run() { - final int depth = stagePanel.getSelectedDepth(); - 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.placeObjectTag.getMatrix(); - } - - 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); - DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depth); - ds.placeObjectTag.setMatrix(newMatrix); - ds.placeObjectTag.setPlaceFlagHasMatrix(newMatrix != null); - stagePanel.getTimelined().resetTimeline(); - stagePanel.repaint(); - if (transformEnabled()) { - stagePanel.freeTransformDepth(depth); - stagePanel.setRegistrationPoint(regPoint); - if (regPointPos != null) { - stagePanel.setRegistrationPointPosition(regPointPos); - } - } - } - - @Override - public void undoOperation() { - timelinePanel.setFrame(frame, depth); - DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depth); - ds.placeObjectTag.setMatrix(fpreviousMatrix); - ds.placeObjectTag.setPlaceFlagHasMatrix(fpreviousMatrix != null); - stagePanel.getTimelined().resetTimeline(); - stagePanel.repaint(); - if (transformEnabled()) { - stagePanel.freeTransformDepth(depth); - } - } - - @Override - public String getDescription() { - return EasyStrings.translate(transformEnabled ? "action.transform" : "action.move"); - } - }); - - } - - }); - - stagePanel.setTransferHandler(new TransferHandler() { - @Override - public boolean canImport(TransferHandler.TransferSupport support) { - return support.isDataFlavorSupported(CharacterTagTransferable.CHARACTERTAG_FLAVOR); - } - - @Override - public boolean importData(TransferHandler.TransferSupport support) { - if (!canImport(support)) { - return false; - } - try { - Transferable transferable = support.getTransferable(); - Integer characterId = (Integer) transferable.getTransferData(CharacterTagTransferable.CHARACTERTAG_FLAVOR); - CharacterTag tag = stagePanel.getTimelined().getSwf().getCharacter(characterId); - if ((tag instanceof DefineSpriteTag) - || (tag instanceof ShapeTag) - || (tag instanceof TextTag) - || (tag instanceof ButtonTag) - ) { - - undoManager.doOperation(new DoableOperation() { - - private PlaceObject2Tag place; - private RemoveObject2Tag remove; - private List tags; - private int frame = stagePanel.getFrame(); - private int depth = stagePanel.getSelectedDepth(); - - @Override - public void doOperation() { - timelinePanel.setFrame(frame, depth); - CharacterTag ch = (CharacterTag) tag; - int maxDepth = stagePanel.getTimelined().getTimeline().getMaxDepth(); - int newDepth = maxDepth + 1; - Timelined timelined = stagePanel.getTimelined(); - - tags = timelined.getSwf().getTags().toArrayList(); - - ShowFrameTag showFrameTag = timelined.getTimeline().getFrame(frame).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); - } - - - DefineBeforeUsageFixer fixer = new DefineBeforeUsageFixer(); - boolean tagOrderChanged = fixer.fixDefineBeforeUsage(timelined.getSwf()); - if (!tagOrderChanged) { - tags = null; - } - - 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(); - - //Tag order changed, put the original tags back - if (tags != null) { - SWF swf = timelined.getSwf(); - ReadOnlyTagList newTags = swf.getTags(); - int size = newTags.size(); - for (int i = 0; i < size; i++) { - swf.removeTag(0); - } - for (int i = 0; i < tags.size(); i++) { - if (tags.get(i) == place) { - continue; - } - swf.addTag(tags.get(i)); - } - swf.resetTimeline(); - } - - stagePanel.repaint(); - timelinePanel.refresh(); - timelinePanel.setFrame(frame, depth); - } - - @Override - public String getDescription() { - return EasyStrings.translate("action.addToStage"); - } - - }); - - 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)); - undoButton.addActionListener(new ActionListener() { - @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)); - redoButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - undoManager.redo(); - } - }); - - Runnable undoChangeListener = new Runnable() { - @Override - public void run() { - undoButton.setEnabled(undoManager.canUndo()); - redoButton.setEnabled(undoManager.canRedo()); - if (undoManager.canUndo()) { - undoButton.setToolTipText(EasyStrings.translate("undo").replace("%action%",undoManager.getUndoName())); - } else { - undoButton.setToolTipText(EasyStrings.translate("undo.cannot")); - } - if (undoManager.canRedo()) { - redoButton.setToolTipText(EasyStrings.translate("redo").replace("%action%", undoManager.getRedoName())); - } else { - redoButton.setToolTipText(EasyStrings.translate("redo.cannot")); - } - } - }; - - undoManager.addChangeListener(undoChangeListener); - undoChangeListener.run(); - - toolbarPanel.add(undoButton); - toolbarPanel.add(redoButton); - - topPanel.add(toolbarPanel, BorderLayout.NORTH); - topPanel.add(stagePanel, BorderLayout.CENTER); - - timelinePanel = new TimelinePanel(undoManager); - - timelinePanel.addChangeListener(new Runnable() { - @Override - public void run() { - stagePanel.repaint(); + public void tabSwitched(SWF value) { + easySwfPanel.setTimelined(value); } }); - timelinePanel.addFrameSelectionListener(new FrameSelectionListener() { - @Override - public void frameSelected(int frame, int depth) { - stagePanel.pause(); - 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); - } - }); - - verticalSplitPane = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, topPanel, timelinePanel, Configuration.guiSplitPaneEasyVerticaldividerLocationPercent); - - libraryTreeTable = new LibraryTreeTable(); - JScrollPane libraryScrollPane = new FasterScrollPane(libraryTreeTable); - - JPanel libraryPanel = new JPanel(new BorderLayout()); - libraryPanel.add(libraryScrollPane, BorderLayout.CENTER); - - libraryPreviewPanel = new ImagePanel(); - libraryPreviewPanel.setTopPanelVisible(false); - - libraryPanel.add(libraryPreviewPanel, BorderLayout.NORTH); - - libraryPreviewPanel.setPreferredSize(new Dimension(200, 200)); - - rightTabbedPane = new JTabbedPane(); - rightTabbedPane.addTab(EasyStrings.translate("library"), libraryPanel); - - JPanel transformTab = new JPanel(new BorderLayout()); - transformPanel = new TransformPanel(stagePanel, false); - transformTab.add(new FasterScrollPane(transformPanel), BorderLayout.CENTER); - - rightTabbedPane.addTab(EasyStrings.translate("transform"), transformTab); - rightTabbedPane.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - int depth = stagePanel.getSelectedDepth(); - if (stagePanel.getFrame() >= stagePanel.getTimelined().getFrameCount()) { - depth = -1; - } - if (depth != -1) { - Frame frame = stagePanel.getTimelined().getTimeline().getFrame(stagePanel.getFrame()); - if (frame == null) { - depth = -1; - } else { - DepthState ds = frame.layers.get(depth); - if (ds == null) { - depth = -1; - } - } - } - transformPanel.setVisible(depth != -1); - if (transformEnabled()) { - stagePanel.freeTransformDepth(depth); - stagePanel.setTransformSelectionMode(true); - } else { - stagePanel.freeTransformDepth(-1); - stagePanel.selectDepth(depth); - stagePanel.setTransformSelectionMode(false); - } - } - }); - - horizontalSplitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, verticalSplitPane, rightTabbedPane, Configuration.guiSplitPaneEasyHorizontaldividerLocationPercent); - - if (View.isOceanic()) { - libraryScrollPane.getViewport().setBackground(Color.white); - } else { - libraryScrollPane.getViewport().setBackground(UIManager.getColor("Tree.background")); - } - add(horizontalSplitPane, BorderLayout.CENTER); - - libraryTreeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - int row = libraryTreeTable.getSelectedRow(); - if (row == -1) { - return; - } - DefaultMutableTreeNode n = (DefaultMutableTreeNode) libraryTreeTable.getModel().getValueAt(row, 0); - Object obj = n.getUserObject(); - if (obj instanceof Tag) { - Tag t = (Tag) obj; - 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 setTimelined(Timelined timelined) { - SWF swf = timelined.getSwf(); - libraryTreeTable.setSwf(swf); - stagePanel.setTimelined(swf, swf, 0, true, true, true, true, true, false, true); - stagePanel.pause(); - stagePanel.gotoFrame(0); - timelinePanel.setTimelined(swf); + public void setSwfs(List swfs) { + tabSwitcher.clear(); + for (SWF swf : swfs) { + tabSwitcher.addTab(swf, swf.getShortPathTitle(), View.getIcon("flash16")); + } + } + + public void setSwf(SWF swf) { + tabSwitcher.setValue(swf); + } + + public SWF getSwf() { + return tabSwitcher.getSelectedValue(); } - } diff --git a/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java b/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java new file mode 100644 index 000000000..2734390d1 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java @@ -0,0 +1,488 @@ +/* + * 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.DefineBeforeUsageFixer; +import com.jpexs.decompiler.flash.ReadOnlyTagList; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.FasterScrollPane; +import com.jpexs.decompiler.flash.gui.ImagePanel; +import com.jpexs.decompiler.flash.gui.Main; +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.gui.View; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +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.Frame; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.Openable; +import com.jpexs.decompiler.flash.types.MATRIX; +import java.awt.BorderLayout; +import java.awt.Color; +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; +import java.io.IOException; +import java.util.List; +import javax.swing.JButton; +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; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; + +/** + * + * @author JPEXS + */ +public class EasySwfPanel extends JPanel { + + private LibraryTreeTable libraryTreeTable; + private JSplitPane verticalSplitPane; + private JSplitPane horizontalSplitPane; + private ImagePanel libraryPreviewPanel; + private ImagePanel stagePanel; + private TimelinePanel timelinePanel; + private JButton undoButton; + private JButton redoButton; + private UndoManager undoManager; + private JTabbedPane rightTabbedPane; + private TransformPanel transformPanel; + private Timelined timelined; + + public EasySwfPanel() { + 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()); + } + transformPanel.setVisible(pl != null); + } + }); + + stagePanel.addTransformChangeListener(new Runnable() { + @Override + public void run() { + final int depth = stagePanel.getSelectedDepth(); + 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.placeObjectTag.getMatrix(); + } + + 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); + DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depth); + ds.placeObjectTag.setMatrix(newMatrix); + ds.placeObjectTag.setPlaceFlagHasMatrix(newMatrix != null); + ds.placeObjectTag.setModified(true); + stagePanel.getTimelined().resetTimeline(); + stagePanel.repaint(); + if (transformEnabled()) { + stagePanel.freeTransformDepth(depth); + stagePanel.setRegistrationPoint(regPoint); + if (regPointPos != null) { + stagePanel.setRegistrationPointPosition(regPointPos); + } + transformPanel.setVisible(true); + } + } + + @Override + public void undoOperation() { + timelinePanel.setFrame(frame, depth); + DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depth); + ds.placeObjectTag.setMatrix(fpreviousMatrix); + ds.placeObjectTag.setPlaceFlagHasMatrix(fpreviousMatrix != null); + stagePanel.getTimelined().resetTimeline(); + stagePanel.repaint(); + if (transformEnabled()) { + stagePanel.freeTransformDepth(depth); + transformPanel.setVisible(true); + } + } + + @Override + public String getDescription() { + return EasyStrings.translate(transformEnabled ? "action.transform" : "action.move"); + } + }); + + } + + }); + + stagePanel.setTransferHandler(new TransferHandler() { + @Override + public boolean canImport(TransferHandler.TransferSupport support) { + return support.isDataFlavorSupported(CharacterTagTransferable.CHARACTERTAG_FLAVOR); + } + + @Override + public boolean importData(TransferHandler.TransferSupport support) { + if (!canImport(support)) { + return false; + } + try { + Transferable transferable = support.getTransferable(); + Integer characterId = (Integer) transferable.getTransferData(CharacterTagTransferable.CHARACTERTAG_FLAVOR); + CharacterTag tag = stagePanel.getTimelined().getSwf().getCharacter(characterId); + if ((tag instanceof DefineSpriteTag) + || (tag instanceof ShapeTag) + || (tag instanceof TextTag) + || (tag instanceof ButtonTag) + ) { + + undoManager.doOperation(new DoableOperation() { + + private PlaceObject2Tag place; + private RemoveObject2Tag remove; + private List tags; + private int frame = stagePanel.getFrame(); + private int depth = stagePanel.getSelectedDepth(); + + @Override + public void doOperation() { + timelinePanel.setFrame(frame, depth); + CharacterTag ch = (CharacterTag) tag; + int maxDepth = stagePanel.getTimelined().getTimeline().getMaxDepth(); + int newDepth = maxDepth + 1; + Timelined timelined = stagePanel.getTimelined(); + + tags = timelined.getSwf().getTags().toArrayList(); + + ShowFrameTag showFrameTag = timelined.getTimeline().getFrame(frame).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); + } + + + DefineBeforeUsageFixer fixer = new DefineBeforeUsageFixer(); + boolean tagOrderChanged = fixer.fixDefineBeforeUsage(timelined.getSwf()); + if (!tagOrderChanged) { + tags = null; + } + + 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(); + + //Tag order changed, put the original tags back + if (tags != null) { + SWF swf = timelined.getSwf(); + ReadOnlyTagList newTags = swf.getTags(); + int size = newTags.size(); + for (int i = 0; i < size; i++) { + swf.removeTag(0); + } + for (int i = 0; i < tags.size(); i++) { + if (tags.get(i) == place) { + continue; + } + swf.addTag(tags.get(i)); + } + swf.resetTimeline(); + } + + stagePanel.repaint(); + timelinePanel.refresh(); + timelinePanel.setFrame(frame, depth); + } + + @Override + public String getDescription() { + return EasyStrings.translate("action.addToStage"); + } + + }); + + 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)); + undoButton.addActionListener(new ActionListener() { + @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)); + redoButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + undoManager.redo(); + } + }); + + undoButton.setEnabled(false); + redoButton.setEnabled(false); + undoButton.setToolTipText(EasyStrings.translate("undo.cannot")); + redoButton.setToolTipText(EasyStrings.translate("redo.cannot")); + + Runnable undoChangeListener = new Runnable() { + @Override + public void run() { + undoButton.setEnabled(undoManager.canUndo()); + redoButton.setEnabled(undoManager.canRedo()); + if (undoManager.canUndo()) { + undoButton.setToolTipText(EasyStrings.translate("undo").replace("%action%",undoManager.getUndoName())); + } else { + undoButton.setToolTipText(EasyStrings.translate("undo.cannot")); + } + if (undoManager.canRedo()) { + redoButton.setToolTipText(EasyStrings.translate("redo").replace("%action%", undoManager.getRedoName())); + } else { + redoButton.setToolTipText(EasyStrings.translate("redo.cannot")); + } + if (stagePanel.getTimelined() == null) { + return; + } + Main.getMainFrame().getPanel().updateUiWithCurrentOpenable(); + } + }; + + undoManager.addChangeListener(undoChangeListener); + + toolbarPanel.add(undoButton); + toolbarPanel.add(redoButton); + + topPanel.add(toolbarPanel, BorderLayout.NORTH); + topPanel.add(stagePanel, BorderLayout.CENTER); + + timelinePanel = new TimelinePanel(undoManager); + + timelinePanel.addChangeListener(new Runnable() { + @Override + public void run() { + stagePanel.repaint(); + } + }); + timelinePanel.addFrameSelectionListener(new FrameSelectionListener() { + @Override + public void frameSelected(int frame, int depth) { + stagePanel.pause(); + 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); + } + }); + + verticalSplitPane = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, topPanel, timelinePanel, Configuration.guiSplitPaneEasyVerticaldividerLocationPercent); + + libraryTreeTable = new LibraryTreeTable(); + JScrollPane libraryScrollPane = new FasterScrollPane(libraryTreeTable); + + JPanel libraryPanel = new JPanel(new BorderLayout()); + libraryPanel.add(libraryScrollPane, BorderLayout.CENTER); + + libraryPreviewPanel = new ImagePanel(); + libraryPreviewPanel.setTopPanelVisible(false); + + libraryPanel.add(libraryPreviewPanel, BorderLayout.NORTH); + + libraryPreviewPanel.setPreferredSize(new Dimension(200, 200)); + + rightTabbedPane = new JTabbedPane(); + rightTabbedPane.addTab(EasyStrings.translate("library"), libraryPanel); + + JPanel transformTab = new JPanel(new BorderLayout()); + transformPanel = new TransformPanel(stagePanel, false); + transformTab.add(new FasterScrollPane(transformPanel), BorderLayout.CENTER); + + rightTabbedPane.addTab(EasyStrings.translate("transform"), transformTab); + rightTabbedPane.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (stagePanel.getTimelined() == null) { + return; + } + int depth = stagePanel.getSelectedDepth(); + if (stagePanel.getFrame() >= stagePanel.getTimelined().getFrameCount()) { + depth = -1; + } + if (depth != -1) { + Frame frame = stagePanel.getTimelined().getTimeline().getFrame(stagePanel.getFrame()); + if (frame == null) { + depth = -1; + } else { + DepthState ds = frame.layers.get(depth); + if (ds == null) { + depth = -1; + } + } + } + transformPanel.setVisible(depth != -1); + if (transformEnabled()) { + stagePanel.freeTransformDepth(depth); + stagePanel.setTransformSelectionMode(true); + } else { + stagePanel.freeTransformDepth(-1); + stagePanel.selectDepth(depth); + stagePanel.setTransformSelectionMode(false); + } + } + }); + + horizontalSplitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, verticalSplitPane, rightTabbedPane, Configuration.guiSplitPaneEasyHorizontaldividerLocationPercent); + + if (View.isOceanic()) { + libraryScrollPane.getViewport().setBackground(Color.white); + } else { + libraryScrollPane.getViewport().setBackground(UIManager.getColor("Tree.background")); + } + add(horizontalSplitPane, BorderLayout.CENTER); + + libraryTreeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + int row = libraryTreeTable.getSelectedRow(); + if (row == -1) { + return; + } + DefaultMutableTreeNode n = (DefaultMutableTreeNode) libraryTreeTable.getModel().getValueAt(row, 0); + Object obj = n.getUserObject(); + if (obj instanceof Tag) { + Tag t = (Tag) obj; + 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 setTimelined(Timelined timelined) { + this.timelined = timelined; + if (timelined == null) { + stagePanel.clearAll(); + timelinePanel.setTimelined(null); + } else { + SWF swf = timelined.getSwf(); + libraryTreeTable.setSwf(swf); + stagePanel.setTimelined(swf, swf, 0, true, true, true, true, true, false, true); + stagePanel.pause(); + stagePanel.gotoFrame(0); + timelinePanel.setTimelined(swf); + } + undoManager.clear(); + } + + public Openable getOpenable() { + return timelined.getSwf(); + } +} diff --git a/src/com/jpexs/decompiler/flash/easygui/TabSwitchedListener.java b/src/com/jpexs/decompiler/flash/easygui/TabSwitchedListener.java new file mode 100644 index 000000000..1f21d12bc --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/TabSwitchedListener.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * Tab switching listener. + * @author JPEXS + */ +public interface TabSwitchedListener { + public void tabSwitched(E value); +} diff --git a/src/com/jpexs/decompiler/flash/easygui/TabSwitcher.java b/src/com/jpexs/decompiler/flash/easygui/TabSwitcher.java new file mode 100644 index 000000000..3e9e9f013 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/TabSwitcher.java @@ -0,0 +1,160 @@ +/* + * 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 java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.ArrayList; +import java.util.List; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * Component for switching tabs which has only single tab component. + * @author JPEXS + * @param Element + */ +public class TabSwitcher extends JPanel { + private final JTabbedPane tabbedPane; + private final List values; + private final List titles; + private Component tabComponent; + private final JPanel centralPanel; + private final List> listeners = new ArrayList<>(); + + public TabSwitcher(Component tabComponent) { + titles = new ArrayList<>(); + values = new ArrayList<>(); + tabbedPane = new JTabbedPane(); + tabbedPane.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + int index = tabbedPane.getSelectedIndex(); + if (index == -1) { + fireTabSwitched(null); + } else { + fireTabSwitched(values.get(index)); + } + } + }); + this.tabComponent = tabComponent; + centralPanel = new JPanel(); + centralPanel.setLayout(null); + centralPanel.add(tabbedPane); + centralPanel.add(tabComponent); + setLayout(new BorderLayout()); + add(centralPanel, BorderLayout.CENTER); + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + updateSizes(); + } + }); + } + + public void addTabSwitchedListener(TabSwitchedListener listener) { + listeners.add(listener); + } + + public void removeTabSwitchedListener(TabSwitchedListener listener) { + listeners.remove(listener); + } + + private void fireTabSwitched(E value) { + for (TabSwitchedListener listener : listeners) { + listener.tabSwitched(value); + } + } + + private void updateSizes() { + tabbedPane.setBounds(0, 0, centralPanel.getWidth(), centralPanel.getHeight()); + int maxH = 0; + for (int i = 0; i < tabbedPane.getTabCount(); i++) { + Rectangle r = tabbedPane.getUI().getTabBounds(tabbedPane, i); + int h = r.y + r.height; + if (h > maxH) { + maxH = h; + } + } + tabbedPane.setBounds(0, 0, centralPanel.getWidth(), maxH); + tabComponent.setBounds(0, maxH, centralPanel.getWidth(), centralPanel.getHeight() - maxH); + } + + public Component getTabComponent() { + return tabComponent; + } + + public void setTabComponent(Component component) { + if (this.tabComponent != null) { + centralPanel.remove(this.tabComponent); + } + this.tabComponent = component; + + centralPanel.add(component); + revalidate(); + } + + public void addTab(E value, String title, Icon icon) { + titles.add(title); + values.add(value); + tabbedPane.insertTab(title, icon, new JPanel(), null, values.size() - 1); + updateSizes(); + } + + public String getTabTitleAtIndex(int index) { + return titles.get(index); + } + + public E getValueAtIndex(int index) { + return values.get(index); + } + + public int getValueCount() { + return values.size(); + } + + public void clear() { + values.clear(); + titles.clear(); + tabbedPane.removeAll(); + fireTabSwitched(null); + } + + public void setSelectedIndex(int index) { + tabbedPane.setSelectedIndex(index); + } + + public void setValue(E value) { + int index = values.indexOf(value); + setSelectedIndex(index); + } + + public E getSelectedValue() { + int index = tabbedPane.getSelectedIndex(); + if (index == -1) { + return null; + } + return values.get(index); + } +} diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java index b04d6a217..6cb2a7f94 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java @@ -1082,7 +1082,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } public void setTimeline(Timeline timeline) { - this.timeline = timeline; + this.timeline = timeline; refresh(); } } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java index e97c39962..830365a07 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java @@ -158,8 +158,15 @@ public class TimelinePanel extends JPanel { public void setTimelined(Timelined timelined) { this.timelined = timelined; - timelineBodyPanel.setTimeline(timelined.getTimeline()); - depthPanel.setTimeline(timelined.getTimeline()); - timePanel.setTimeline(timelined.getTimeline()); + if (timelined == null) { + timelineBodyPanel.setTimeline(null); + depthPanel.setTimeline(null); + timePanel.setTimeline(null); + } else { + timelineBodyPanel.setTimeline(timelined.getTimeline()); + depthPanel.setTimeline(timelined.getTimeline()); + timePanel.setTimeline(timelined.getTimeline()); + timelineBodyPanel.frameSelect(0, 0); + } } } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelinedTagListDoableOperation.java b/src/com/jpexs/decompiler/flash/easygui/TimelinedTagListDoableOperation.java index fe70e7b2d..3a5f8d934 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelinedTagListDoableOperation.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelinedTagListDoableOperation.java @@ -37,6 +37,7 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation @Override public void doOperation() { saveTagList(); + timelined.setModified(true); } protected void saveTagList() { diff --git a/src/com/jpexs/decompiler/flash/easygui/UndoManager.java b/src/com/jpexs/decompiler/flash/easygui/UndoManager.java index b28729b08..a21421645 100644 --- a/src/com/jpexs/decompiler/flash/easygui/UndoManager.java +++ b/src/com/jpexs/decompiler/flash/easygui/UndoManager.java @@ -94,4 +94,10 @@ public class UndoManager { public boolean canRedo() { return history.size() > historyPos; } + + public void clear() { + history.clear(); + historyPos = 0; + fireChange(); + } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java index cd601d9ea..8b8918028 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java @@ -1125,7 +1125,7 @@ public abstract class MainFrameMenu implements MenuBuilder { setMenuEnabled("/tools/search", openableSelected); setMenuEnabled("/tools/replace", swfSelected); - setMenuEnabled("/file/view/timeline", swfSelected); + //setMenuEnabled("/file/view/timeline", swfSelected); setMenuEnabled("/tools/abcExplorer", isAs3); setMenuEnabled("/tools/gotoDocumentClass", hasAbc); @@ -1178,6 +1178,9 @@ public abstract class MainFrameMenu implements MenuBuilder { case MainPanel.VIEW_DUMP: setGroupSelection("view", "/file/view/viewHex"); break; + case MainPanel.VIEW_TIMELINE: + setMenuChecked("/file/view/timeline", true); + break; } } } @@ -1522,6 +1525,9 @@ public abstract class MainFrameMenu implements MenuBuilder { case MainPanel.VIEW_DUMP: setMenuChecked("/file/view/viewHex", true); break; + case MainPanel.VIEW_TIMELINE: + setMenuChecked("/file/view/timeline", true); + break; } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index beef88195..2ac0e2b53 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -1468,7 +1468,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } - List> expandedNodes = View.getExpandedNodes(tagTree); + List> expandedNodes = View.getExpandedNodes(tagTree); previewPanel.clear(); openables.set(index, newSwfs); @@ -1477,6 +1477,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se Main.searchResultsStorage.destroySwf(s); } Openable openable = newSwfs.size() > 0 ? newSwfs.get(0) : null; + + easyPanel.setSwfs(new ArrayList<>(getAllSwfs())); + if (openable instanceof SWF) { + easyPanel.setSwf((SWF) openable); + } if (openable != null) { updateUi(openable); } @@ -1495,6 +1500,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se previewPanel.clear(); openables.add(newOpenables); + + easyPanel.setSwfs(new ArrayList<>(getAllSwfs())); + Openable openable = newOpenables.size() > 0 ? newOpenables.get(0) : null; if (openable != null) { updateUi(openable); @@ -1556,6 +1564,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } break; case VIEW_TIMELINE: + updateUi(easyPanel.getSwf()); break; } } @@ -1564,7 +1573,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se View.checkAccess(); if (isWelcomeScreen) { - showContentPanelCard(SPLIT_PANE1); + if (currentView == VIEW_TIMELINE) { + showContentPanelCard(TIMELINE_PANEL); + } else { + showContentPanelCard(SPLIT_PANE1); + } isWelcomeScreen = false; } SWF swf = null; @@ -5695,7 +5708,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se pinsPanel.setVisible(false); currentView = view; Configuration.lastView.set(currentView); - final SWF swf = getCurrentSwf(); + /*final SWF swf = getCurrentSwf(); if (swf != null) { TreeItem item = tagTree.getCurrentTreeItem(); if (item instanceof TagScript) { @@ -5712,8 +5725,13 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return true; } else { showView(VIEW_RESOURCES); - } - return false; + }*/ + Set swfs = getAllSwfs(); + easyPanel.setSwfs(new ArrayList<>(swfs)); + if (!isWelcomeScreen) { + showContentPanelCard(TIMELINE_PANEL); + } + return true; case VIEW_TAGLIST: pinsPanel.setVisible(true); currentView = view; @@ -6061,7 +6079,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (currentView == VIEW_DUMP) { dumpViewReload(forceReload); return; - } + } /*else if (currentView == VIEW_TAGLIST) { tagListViewReload(forceReload); return; @@ -6073,6 +6091,16 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (treePath != null && tree.getFullModel().treePathExists(treePath)) { treeItem = (TreeItem) treePath.getLastPathComponent(); } + + if (currentView == VIEW_TIMELINE) { + if (treeItem != null) { + Openable op = treeItem.getOpenable(); + if (op instanceof SWF) { + easyPanel.setSwf((SWF) op); + } + } + return; + } // save last selected node to config if (treeItem != null && !(treeItem instanceof OpenableList)) {