Added Changing tag position

This commit is contained in:
Jindra Petřík
2022-10-29 13:08:50 +02:00
parent 5d918104ab
commit 4dfb6de32d
10 changed files with 776 additions and 130 deletions

View File

@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- data-characterId and data-characterName tags to SVG export
- [#1731] Image viewer zoom support
- Cloning of tags and frames
- Changing tag position
### Fixed
- [#1834] PlaceObject4 tags appear as Unresolved inside of DefineSprite

View File

@@ -131,12 +131,12 @@ public class Timeline {
}
}
public List<Frame> getFrames() {
public synchronized List<Frame> getFrames() {
ensureInitialized();
return frames;
}
public Frame getFrame(int index) {
public synchronized Frame getFrame(int index) {
ensureInitialized();
if (index >= frames.size()) {
return null;
@@ -144,7 +144,7 @@ public class Timeline {
return frames.get(index);
}
public void addFrame(Frame frame) {
public synchronized void addFrame(Frame frame) {
ensureInitialized();
frames.add(frame);
maxDepth = getMaxDepthInternal();
@@ -174,7 +174,7 @@ public class Timeline {
reset(swf, swf, 0, swf.displayRect);
}
public void reset(SWF swf, Timelined timelined, int id, RECT displayRect) {
public synchronized void reset(SWF swf, Timelined timelined, int id, RECT displayRect) {
initialized = false;
frames.clear();
depthMaxFrame.clear();
@@ -196,7 +196,7 @@ public class Timeline {
return maxDepth;
}
private int getMaxDepthInternal() {
private synchronized int getMaxDepthInternal() {
int max_depth = 0;
for (Frame f : frames) {
for (int depth : f.layers.keySet()) {
@@ -212,12 +212,12 @@ public class Timeline {
return max_depth;
}
public int getFrameCount() {
public synchronized int getFrameCount() {
ensureInitialized();
return frames.size();
}
public int getRealFrameCount() {
public synchronized int getRealFrameCount() {
ensureInitialized();
int cnt = 1;
@@ -263,7 +263,7 @@ public class Timeline {
return -1;
}
private void initialize() {
private synchronized void initialize() {
int frameIdx = 0;
Frame frame = new Frame(this, frameIdx++);
frame.layersChanged = true;
@@ -418,7 +418,7 @@ public class Timeline {
initialized = true;
}
private void detectTweens() {
private synchronized void detectTweens() {
for (int d = 1; d <= maxDepth; d++) {
int characterId = -1;
int len = 0;
@@ -455,7 +455,7 @@ public class Timeline {
}
}
private void calculateMaxDepthFrames() {
private synchronized void calculateMaxDepthFrames() {
depthMaxFrame.clear();
for (int d = 1; d <= maxDepth; d++) {
for (int f = frames.size() - 1; f >= 0; f--) {

View File

@@ -3391,9 +3391,30 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
t = di.getTag();
}
showPreview(t, dumpPreviewPanel);
showPreview(t, dumpPreviewPanel, getFrameForTreeItem(t), getTimelinedForTreeItem(treeItem));
}
}
private int getFrameForTreeItem(TreeItem treeItem) {
TreePath path = tagTree.getModel().getTreePath(treeItem);
for (int i = path.getPathCount() - 1; i >= 0; i--) {
if (path.getPathComponent(i) instanceof Frame) {
Frame frame = (Frame)path.getPathComponent(i);
return frame.frame;
}
}
return -1;
}
private Timelined getTimelinedForTreeItem(TreeItem treeItem) {
TreePath path = tagTree.getModel().getTreePath(treeItem);
for (int i = path.getPathCount() - 1; i >= 0; i--) {
if (path.getPathComponent(i) instanceof Timelined) {
return (Timelined)path.getPathComponent(i);
}
}
return null;
}
public void unloadFlashPlayer() {
if (flashPanel != null) {
@@ -3436,7 +3457,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
}
}
public boolean isAdobeFlashPlayerEnabled() {
public static boolean isAdobeFlashPlayerEnabled() {
return Configuration.useAdobeFlashPlayerForPreviews.get();
}
@@ -3614,7 +3635,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
previewPanel.closeTag();
}
public void showPreview(TreeItem treeItem, PreviewPanel previewPanel) {
public static void showPreview(TreeItem treeItem, PreviewPanel previewPanel, int frame, Timelined timelinedContainer) {
previewPanel.clear();
if (treeItem == null) {
previewPanel.showEmpty();
@@ -3633,10 +3654,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
//}
}
} else if ((treeItem instanceof PlaceObjectTypeTag) && (previewPanel != dumpPreviewPanel)) {
TreePath path = tagTree.getModel().getTreePath(treeItem);
Frame frame = (Frame) path.getParentPath().getLastPathComponent();
previewPanel.showPlaceTagPanel((PlaceObjectTypeTag) treeItem, frame.frame);
} else if ((treeItem instanceof PlaceObjectTypeTag)) {// && (previewPanel != dumpPreviewPanel)) {
//TreePath path = tagTree.getModel().getTreePath(treeItem);
//Frame frame = (Frame) path.getParentPath().getLastPathComponent();
previewPanel.showPlaceTagPanel((PlaceObjectTypeTag) treeItem, frame);
} else if (treeItem instanceof MetadataTag) {
MetadataTag metadataTag = (MetadataTag) treeItem;
previewPanel.showMetaDataPanel(metadataTag);
@@ -3668,6 +3689,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
Frame fn = (Frame) treeItem;
SWF swf = fn.getSwf();
previewPanel.showImagePanel(fn.timeline.timelined, swf, fn.frame, true);
} else if (treeItem instanceof ShowFrameTag) {
SWF swf;
if (timelinedContainer instanceof DefineSpriteTag) {
swf = ((DefineSpriteTag)timelinedContainer).getSwf();
} else {
swf = (SWF)timelinedContainer;
}
previewPanel.showImagePanel(timelinedContainer, swf, frame, true);
} else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) {
previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32")));
previewPanel.setImageReplaceButtonVisible(((Tag) treeItem).isReadOnly() && (treeItem instanceof DefineSoundTag), false);
@@ -3804,44 +3833,44 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} else if (treeItem instanceof FolderItem) {
showFolderPreview((FolderItem) treeItem);
} else if (treeItem instanceof SWF) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof MetadataTag) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof DefineBinaryDataTag) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof UnknownTag) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof ASMSource && (!(treeItem instanceof DrawableTag) || preferScript)) {
getActionPanel().setSource((ASMSource) treeItem, !forceReload);
showCard(CARDACTIONSCRIPTPANEL);
} else if (treeItem instanceof ImageTag) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if ((treeItem instanceof DrawableTag) && (!(treeItem instanceof TextTag)) && (!(treeItem instanceof FontTag)) && internalViewer) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if ((treeItem instanceof FontTag) && internalViewer) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if ((treeItem instanceof TextTag) && internalViewer) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof Frame && internalViewer) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if ((treeItem instanceof Frame) || (treeItem instanceof CharacterTag) || (treeItem instanceof FontTag) || (treeItem instanceof SoundStreamHeadTypeTag)) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof PlaceObjectTypeTag) {
showPreview(treeItem, previewPanel);
showPreview(treeItem, previewPanel, getFrameForTreeItem(treeItem), null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof Tag) {
showGenericTag((Tag) treeItem);

View File

@@ -712,17 +712,18 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
showCardLeft(PLACE_TAG_CARD);
placeTag = tag;
oldMatrix = tag.getMatrix();
placeSplitPane.setDividerLocation((int) (0.6 * this.getWidth()));
placeSplitPane.setDividerLocation((int) (0.6 * this.getWidth()));
placeGenericPanel.setVisible(!readOnly);
placeGenericPanel.setEditMode(false, tag);
placeImagePanel.selectDepth(-1);
placeImagePanel.setTimelined(((Tag) tag).getTimelined(), ((Tag) tag).getSwf(), frame, true);
placeImagePanel.selectDepth(tag.getDepth());
parametersPanel.setVisible(false);
placeEditButton.setVisible(!tag.isReadOnly());
placeEditButton.setVisible(!tag.isReadOnly() && !readOnly);
placeEditButton.setEnabled(true);
placeSaveButton.setVisible(false);
placeCancelButton.setVisible(false);
placeFreeTransformButton.setVisible(true);
placeFreeTransformButton.setVisible(!readOnly);
}
public void setImageReplaceButtonVisible(boolean show, boolean showAlpha) {

View File

@@ -0,0 +1,544 @@
/*
* Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui;
import com.jpexs.decompiler.flash.SWF;
import static com.jpexs.decompiler.flash.gui.AppDialog.CANCEL_OPTION;
import com.jpexs.decompiler.flash.gui.tagtree.TagTree;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DoABC2Tag;
import com.jpexs.decompiler.flash.tags.DoABCTag;
import com.jpexs.decompiler.flash.tags.DoActionTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
import com.jpexs.decompiler.flash.tags.MetadataTag;
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.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.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.plaf.basic.BasicLabelUI;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
/**
*
* @author JPEXS
*/
public class SelectTagPositionDialog extends AppDialog {
private final JButton okButton = new JButton(translate("button.ok"));
private final JButton cancelButton = new JButton(translate("button.cancel"));
private JTree positionTree;
private PreviewPanel previewPanel;
private final SWF swf;
private int result = ERROR_OPTION;
private Tag selectedTag = null;
private Timelined selectedTimelined = null;
private static class MyTreeNode implements TreeNode {
private final List<TreeNode> children = new ArrayList<>();
private TreeNode parent;
private Object data;
@Override
public String toString() {
if (data instanceof DoInitActionTag) {
DoInitActionTag doinit = (DoInitActionTag) data;
String exportName = doinit.getSwf().getExportName(doinit.spriteId);
if (exportName != null && !exportName.isEmpty()) {
return DoInitActionTag.NAME + " (" + doinit.spriteId + ") : " + exportName;
}
}
return data.toString();
}
public void setData(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public void addChild(TreeNode node) {
children.add(node);
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
@Override
public TreeNode getChildAt(int childIndex) {
return children.get(childIndex);
}
@Override
public int getChildCount() {
return children.size();
}
@Override
public TreeNode getParent() {
return parent;
}
@Override
public int getIndex(TreeNode node) {
return children.indexOf(node);
}
@Override
public boolean getAllowsChildren() {
return true;
}
@Override
public boolean isLeaf() {
return children.isEmpty();
}
@Override
public Enumeration<? extends TreeNode> children() {
return Collections.enumeration(children);
}
}
private static class MyTimelineEnd {
@Override
public String toString() {
return AppDialog.translateForDialog("timeline.end", SelectTagPositionDialog.class);
}
}
private static class MyFrame {
private final int frame;
private boolean invalid;
public MyFrame(int frame) {
this.frame = frame;
}
public int getFrame() {
return frame;
}
public void setInvalid(boolean invalid) {
this.invalid = invalid;
}
public boolean isInvalid() {
return invalid;
}
@Override
public String toString() {
return "frame " + frame;
}
}
private void populateNodes(MyTreeNode root, Timelined tim, int currentFrame) {
int f = 1;
MyTreeNode frameNode = new MyTreeNode();
frameNode.setData(new MyFrame(1));
frameNode.setParent(root);
root.addChild(frameNode);
for (Tag t : tim.getTags()) {
MyTreeNode node = new MyTreeNode();
node.setData(t);
frameNode.addChild(node);
if (t instanceof DefineSpriteTag) {
populateNodes(node, (DefineSpriteTag) t, 1);
}
if (t instanceof ShowFrameTag) {
f++;
frameNode = new MyTreeNode();
frameNode.setData(new MyFrame(f));
frameNode.setParent(root);
root.addChild(frameNode);
}
}
if (frameNode.isLeaf()) {
root.children.remove(root.children.size() - 1);
}
MyTimelineEnd end = new MyTimelineEnd();
MyTreeNode endNode = new MyTreeNode();
endNode.setData(end);
root.addChild(endNode);
}
private void selectCurrent(MyTreeNode root, Timelined timelined, List<Object> path) {
for (int i = 0; i < root.getChildCount(); i++) {
MyTreeNode node = (MyTreeNode) root.getChildAt(i);
List<Object> subPath = new ArrayList<>(path);
subPath.add(node);
if (node.getData() == selectedTag && timelined == selectedTimelined) {
Object[] pathArray = subPath.toArray(new Object[subPath.size()]);
TreePath tpath = new TreePath(pathArray);
positionTree.setSelectionPath(tpath);
positionTree.scrollPathToVisible(tpath);
return;
}
if (node.getData() instanceof DefineSpriteTag) {
selectCurrent(node, (DefineSpriteTag) node.getData(), subPath);
} else {
selectCurrent(node, timelined, subPath);
}
}
}
public SelectTagPositionDialog(Window parent, SWF swf) {
this(parent, swf, null, null);
}
private static class PositionTreeCellRenderer extends DefaultTreeCellRenderer {
private boolean selected;
public PositionTreeCellRenderer() {
if (View.isOceanic()) {
setUI(new BasicLabelUI());
setOpaque(false);
setBackgroundNonSelectionColor(Color.white);
}
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Component renderer = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
this.selected = sel;
if (renderer instanceof JLabel) {
JLabel lab = (JLabel) renderer;
Object subValue = value;
if (value instanceof MyTreeNode) {
subValue = ((MyTreeNode) value).getData();
}
TreeNodeType nodeType;
if (subValue instanceof MyFrame) {
nodeType = TreeNodeType.FRAME;
} else if (subValue instanceof FontTag) {
nodeType = TreeNodeType.FONT;
} else if (subValue instanceof TextTag) {
nodeType = TreeNodeType.TEXT;
} else if (subValue instanceof ImageTag) {
nodeType = TreeNodeType.IMAGE;
} else if (subValue instanceof ShapeTag) {
nodeType = TreeNodeType.SHAPE;
} else if (subValue instanceof MorphShapeTag) {
nodeType = TreeNodeType.MORPH_SHAPE;
} else if (subValue instanceof DefineSpriteTag) {
nodeType = TreeNodeType.SPRITE;
} else if (subValue instanceof ButtonTag) {
nodeType = TreeNodeType.BUTTON;
} else if (subValue instanceof DefineVideoStreamTag) {
nodeType = TreeNodeType.MOVIE;
} else if ((subValue instanceof DefineSoundTag) || (subValue instanceof SoundStreamHeadTypeTag) || (subValue instanceof SoundStreamBlockTag)) {
nodeType = TreeNodeType.SOUND;
} else if (subValue instanceof DefineBinaryDataTag) {
nodeType = TreeNodeType.BINARY_DATA;
} else if ((subValue instanceof DoActionTag)
|| (subValue instanceof DoInitActionTag)
|| (subValue instanceof DoABCTag)
|| (subValue instanceof DoABC2Tag)) {
nodeType = TreeNodeType.AS;
} else if (subValue instanceof ShowFrameTag) {
nodeType = TreeNodeType.FRAME;
} else if (subValue instanceof SetBackgroundColorTag) {
nodeType = TreeNodeType.SET_BACKGROUNDCOLOR;
} else if (subValue instanceof FileAttributesTag) {
nodeType = TreeNodeType.FILE_ATTRIBUTES;
} else if (subValue instanceof MetadataTag) {
nodeType = TreeNodeType.METADATA;
} else if (subValue instanceof PlaceObjectTypeTag) {
nodeType = TreeNodeType.PLACE_OBJECT;
} else if (subValue instanceof RemoveTag) {
nodeType = TreeNodeType.REMOVE_OBJECT;
} else if (subValue instanceof MyTimelineEnd) {
nodeType = null;
} else {
nodeType = TreeNodeType.OTHER_TAG;
}
if (nodeType == null) {
//nothing
} else {
lab.setIcon(TagTree.getIconForType(nodeType));
}
}
return renderer;
}
}
public SelectTagPositionDialog(Window parent, SWF swf, Tag selectedTag, Timelined selectedTimelined) {
super(parent);
this.swf = swf;
this.selectedTag = selectedTag;
this.selectedTimelined = selectedTimelined;
setTitle(translate("dialog.title"));
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
Container cnt = getContentPane();
cnt.setLayout(new BorderLayout());
JPanel buttonsPanel = new JPanel(new FlowLayout());
okButton.addActionListener(this::okButtonActionPerformed);
cancelButton.addActionListener(this::cancelButtonActionPerformed);
buttonsPanel.add(okButton);
buttonsPanel.add(cancelButton);
cnt.add(buttonsPanel, BorderLayout.SOUTH);
MyTreeNode root = new MyTreeNode();
root.setData("root");
populateNodes(root, swf, 1);
positionTree = new JTree(root) {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int rows[] = getSelectionRows();
if (rows.length == 1) {
int row = rows[0];
Object selection = getLastSelectedPathComponent();
boolean onFrame = false;
if (selection != null && (((MyTreeNode)selection).getData() instanceof MyFrame)) { // && !isCollapsed(row)) {
onFrame = true;
return;
}
Rectangle rect = this.getRowBounds(row);
int sideWidth = 6;
int sideHeight = 6;
int offsetX = -5;
int lineStartX = offsetX + rect.x;
int backStartX = getWidth() + offsetX;
if (onFrame) {
g.fillRect(lineStartX, rect.y + rect.height - 1, getWidth() - lineStartX, 1);
} else {
g.fillRect(lineStartX, rect.y, getWidth() - lineStartX, 1);
}
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(getForeground());
GeneralPath path = new GeneralPath();
if (onFrame) {
path.moveTo(backStartX - 6, rect.y + rect.height - 1 - 6);
path.lineTo(backStartX, rect.y + rect.height - 1);
path.lineTo(backStartX + 6, rect.y + rect.height - 1 - 6);
} else {
path.moveTo(lineStartX - 6, rect.y + 6);
path.lineTo(lineStartX, rect.y);
path.lineTo(lineStartX + 6, rect.y + 6);
}
path.closePath();
g2d.fill(path);
}
}
};
if (View.isOceanic()) {
positionTree.setBackground(Color.white);
positionTree.setUI(new BasicTreeUI() {
{
setHashColor(Color.gray);
}
});
}
positionTree.setCellRenderer(new PositionTreeCellRenderer());
positionTree.setRootVisible(false);
positionTree.setShowsRootHandles(true);
positionTree.addTreeSelectionListener(this::spriteValueChanged);
previewPanel = new PreviewPanel(Main.getMainFrame().getPanel(), null);
previewPanel.setReadOnly(true);
previewPanel.setPreferredSize(new Dimension(300, 1));
previewPanel.showEmpty();
previewPanel.setParametersPanelVisible(false);
JScrollPane positionTreeScrollPane = new FasterScrollPane(positionTree);
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, positionTreeScrollPane, previewPanel);
//splitPane.setDividerLocation(600);
cnt.add(splitPane, BorderLayout.CENTER);
List<Object> path = new ArrayList<>();
path.add(root);
selectCurrent(root, swf, path);
setSize(1024, 600);
setModal(true);
setResizable(true);
View.centerScreen(this);
View.setWindowIcon(this);
}
private int getCurrentSelectedFrame() {
TreePath path = positionTree.getSelectionPath();
for (int i = path.getPathCount() - 1; i >= 0; i--) {
MyTreeNode node = (MyTreeNode) path.getPathComponent(i);
if (node.getData() instanceof MyFrame) {
MyFrame frame = (MyFrame) node.getData();
return frame.frame;
}
}
return -1;
}
private Timelined getCurrentSelectedTimelined() {
TreePath path = positionTree.getSelectionPath();
for (int i = path.getPathCount() - 1; i >= 0; i--) {
MyTreeNode node = (MyTreeNode) path.getPathComponent(i);
if ("root".equals(node.getData())) {
return swf;
}
if (node.getData() instanceof DefineSpriteTag) {
return (Timelined) node.getData();
}
}
return null;
}
private void spriteValueChanged(TreeSelectionEvent e) {
TreePath selection = positionTree.getSelectionPath();
if (selection == null) {
previewPanel.showEmpty();
return;
}
MyTreeNode tnode = (MyTreeNode) selection.getLastPathComponent();
if (tnode.getData() instanceof Tag) {
MainPanel.showPreview((TreeItem) tnode.getData(), previewPanel, getCurrentSelectedFrame() - 1, getCurrentSelectedTimelined());
} else if (tnode.getData() instanceof MyFrame) {
int f = ((MyFrame) tnode.getData()).frame;
Object parent = ((MyTreeNode) tnode.getParent()).getData();
if (parent instanceof DefineSpriteTag) {
previewPanel.showImagePanel((DefineSpriteTag) parent, swf, f - 1, true);
} else {
previewPanel.showImagePanel(swf, swf, f - 1, true);
}
} else {
previewPanel.showEmpty();
}
}
private void cancelButtonActionPerformed(ActionEvent evt) {
result = CANCEL_OPTION;
previewPanel.clear();
setVisible(false);
}
private void okButtonActionPerformed(ActionEvent evt) {
MyTreeNode node = (MyTreeNode)positionTree.getLastSelectedPathComponent();
if (node.getData() instanceof MyFrame) {
return;
}
if (node.getData() instanceof MyTimelineEnd) {
selectedTag = null;
} else {
selectedTag = (Tag) node.getData();
}
selectedTimelined = getCurrentSelectedTimelined();
result = OK_OPTION;
previewPanel.clear();
setVisible(false);
}
public int showDialog() {
setVisible(true);
return result;
}
/**
* Gets current selected tag to determine position. null = end of timeline position
* @return
*/
public Tag getSelectedTag() {
return selectedTag;
}
/**
* Gets selected timelined
* @return
*/
public Timelined getSelectedTimelined() {
return selectedTimelined;
}
}

View File

@@ -268,111 +268,115 @@ public class SoundTagPlayer implements MediaDisplay {
}
private void playLoop() {
final byte[] data = new byte[16];
setPausedFlag(false);
setActiveFlag(true);
long posBytes = 0;
try {
int numReadBytes = 0;
while (numReadBytes != -1) {
loop: while (true) {
final byte[] data = new byte[16];
setPausedFlag(false);
setActiveFlag(true);
long posBytes = 0;
try {
int numReadBytes = 0;
while (numReadBytes != -1) {
if (getClosedFlag()) {
break;
}
if (!getPausedFlag()) {
if (newPositionMicrosec != null) {
long newPosBytes = (long) (newPositionMicrosec / microsecPerByte);
audioStream.close();
reloadAudioStream();
audioStream.skip(newPosBytes);
newPositionMicrosec = null;
posBytes = newPosBytes;
}
numReadBytes = audioStream.read(data, 0, data.length);
if (numReadBytes != -1) {
posBytes += numReadBytes;
if (sourceLine.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
//((FloatControl) sourceLine.getControl(FloatControl.Type.MASTER_GAIN)).setValue(volume_dB);
}
sourceLine.write(data, 0, numReadBytes);
synchronized (playLock) {
positionMicrosec = microsecPerByte * posBytes;
}
}
}
if (getPausedFlag()) {
synchronized (thread) {
try {
thread.wait(1000);
} catch (InterruptedException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
if (getClosedFlag()) {
break;
}
if (!getPausedFlag()) {
if (newPositionMicrosec != null) {
long newPosBytes = (long) (newPositionMicrosec / microsecPerByte);
audioStream.close();
reloadAudioStream();
audioStream.skip(newPosBytes);
newPositionMicrosec = null;
posBytes = newPosBytes;
}
numReadBytes = audioStream.read(data, 0, data.length);
if (numReadBytes != -1) {
posBytes += numReadBytes;
if (sourceLine.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
//((FloatControl) sourceLine.getControl(FloatControl.Type.MASTER_GAIN)).setValue(volume_dB);
}
sourceLine.write(data, 0, numReadBytes);
synchronized (playLock) {
positionMicrosec = microsecPerByte * posBytes;
}
}
}
if (getPausedFlag()) {
synchronized (thread) {
try {
thread.wait(1000);
} catch (InterruptedException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
}
}
sourceLine.drain();
sourceLine.stop();
sourceLine.close();
}
audioStream.close();
} catch (IOException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedAudioFileException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
}
if (getClosedFlag()) {
sourceLine.drain();
sourceLine.stop();
sourceLine.close();
}
audioStream.close();
} catch (IOException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedAudioFileException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
}
if (!getClosedFlag()) {
decreaseLoopCount();
if (!getClosedFlag()) {
decreaseLoopCount();
int currentLoopCount;
synchronized (playLock) {
currentLoopCount = loopCount;
}
if (currentLoopCount > 0) {
try {
reloadAudioStream();
playLoop();
} catch (IOException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedAudioFileException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
int currentLoopCount;
synchronized (playLock) {
currentLoopCount = loopCount;
}
} else {
setActiveFlag(false);
firePlayingFinished();
if (getClosedFlag()) {
return;
}
synchronized (thread) {
if (currentLoopCount > 0) {
try {
thread.wait();
} catch (InterruptedException ex) {
reloadAudioStream();
continue loop;
} catch (IOException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedAudioFileException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
setActiveFlag(false);
firePlayingFinished();
if (getClosedFlag()) {
return;
}
}
synchronized (thread) {
try {
thread.wait();
} catch (InterruptedException ex) {
return;
}
}
if (getClosedFlag()) {
return;
}
setActiveFlag(true);
if (getClosedFlag()) {
return;
}
setActiveFlag(true);
try {
reloadAudioStream();
playLoop();
} catch (IOException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedAudioFileException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
try {
reloadAudioStream();
continue loop;
} catch (IOException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedAudioFileException ex) {
Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
break;
}
}

View File

@@ -72,6 +72,7 @@ import com.jpexs.decompiler.flash.tags.RemoveObject2Tag;
import com.jpexs.decompiler.flash.tags.RemoveObjectTag;
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag;
import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag;
import com.jpexs.decompiler.flash.tags.Tag;
@@ -183,6 +184,7 @@ public class DumpTree extends JTree {
case DefineSoundTag.NAME:
case SoundStreamHeadTag.NAME:
case SoundStreamHead2Tag.NAME:
case SoundStreamBlockTag.NAME:
nodeType = TreeNodeType.SOUND;
break;
case DefineBinaryDataTag.NAME:

View File

@@ -859,4 +859,6 @@ work.generating_swd = Generating SWD file
button.replaceRefs = Replace references with other character ID
contextmenu.cloneTag = Clone tag
contextmenu.cloneTag = Clone tag
contextmenu.setTagPosition = Set tag position

View File

@@ -0,0 +1,19 @@
# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
dialog.title = Select tag position
button.ok = OK
button.cancel = Cancel
timeline.end = end of timeline

View File

@@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser;
import com.jpexs.decompiler.flash.gui.AppDialog;
import com.jpexs.decompiler.flash.gui.SelectTagPositionDialog;
import com.jpexs.decompiler.flash.gui.Main;
import com.jpexs.decompiler.flash.gui.MainPanel;
import com.jpexs.decompiler.flash.gui.ReplaceCharacterDialog;
@@ -157,6 +158,8 @@ public class TagTreeContextMenu extends JPopupMenu {
private JMenuItem addAs3ClassMenuItem;
private JMenuItem textSearchMenuItem;
private JMenuItem setTagPositionMenuItem;
public TagTreeContextMenu(final TagTree tagTree, MainPanel mainPanel) {
this.mainPanel = mainPanel;
@@ -226,6 +229,10 @@ public class TagTreeContextMenu extends JPopupMenu {
importSwfXmlMenuItem.addActionListener(mainPanel::importSwfXmlActionPerformed);
add(importSwfXmlMenuItem);
setTagPositionMenuItem = new JMenuItem(mainPanel.translate("contextmenu.setTagPosition"));
setTagPositionMenuItem.addActionListener(this::setTagPositionActionPerformed);
add(setTagPositionMenuItem);
addTagMenu = new JMenu(mainPanel.translate("contextmenu.addTag"));
add(addTagMenu);
@@ -233,7 +240,7 @@ public class TagTreeContextMenu extends JPopupMenu {
add(moveTagMenu);
copyTagMenu = new JMenu(mainPanel.translate("contextmenu.copyTag"));
add(copyTagMenu);
add(copyTagMenu);
copyTagWithDependenciesMenu = new JMenu(mainPanel.translate("contextmenu.copyTagWithDependencies"));
add(copyTagWithDependenciesMenu);
@@ -485,6 +492,7 @@ public class TagTreeContextMenu extends JPopupMenu {
addAs12ScriptMenuItem.setVisible(false);
addAs3ClassMenuItem.setVisible(false);
textSearchMenuItem.setVisible(hasScripts || hasTexts);
setTagPositionMenuItem.setVisible(items.size() == 1 && (items.get(0) instanceof Tag));
if (allSelectedIsTag) {
boolean canUndo = false;
@@ -688,7 +696,7 @@ public class TagTreeContextMenu extends JPopupMenu {
}
}
private int chechUniqueCharacterId(Tag tag) {
private int checkUniqueCharacterId(Tag tag) {
if (tag instanceof CharacterTag) {
CharacterTag characterTag = (CharacterTag) tag;
int characterId = characterTag.getCharacterId();
@@ -713,7 +721,7 @@ public class TagTreeContextMenu extends JPopupMenu {
sourceSwf.removeTag(tag);
tag.setSwf(targetSwf, true);
targetSwf.addTag(tag);
chechUniqueCharacterId(tag);
checkUniqueCharacterId(tag);
targetSwf.updateCharacters();
tag.setModified(true);
}
@@ -738,7 +746,7 @@ public class TagTreeContextMenu extends JPopupMenu {
Tag copyTag = tag.cloneTag();
copyTag.setSwf(targetSwf, true);
targetSwf.addTag(copyTag);
chechUniqueCharacterId(copyTag);
checkUniqueCharacterId(copyTag);
targetSwf.updateCharacters();
copyTag.setModified(true);
}
@@ -780,7 +788,7 @@ public class TagTreeContextMenu extends JPopupMenu {
copyTag.setSwf(targetSwf, true);
targetSwf.addTag(copyTag);
int oldCharacterId = neededTag.getCharacterId();
int newCharacterId = chechUniqueCharacterId(copyTag);
int newCharacterId = checkUniqueCharacterId(copyTag);
changedCharacterIds.put(oldCharacterId, newCharacterId);
targetSwf.updateCharacters();
@@ -797,7 +805,7 @@ public class TagTreeContextMenu extends JPopupMenu {
if (tag instanceof CharacterTag) {
CharacterTag characterTag = (CharacterTag) copyTag;
int oldCharacterId = characterTag.getCharacterId();
int newCharacterId = chechUniqueCharacterId(copyTag);
int newCharacterId = checkUniqueCharacterId(copyTag);
changedCharacterIds.put(oldCharacterId, newCharacterId);
}
@@ -1764,7 +1772,7 @@ public class TagTreeContextMenu extends JPopupMenu {
timelined.addTag(idx + 1, copyTag);
copyTag.setTimelined(timelined);
chechUniqueCharacterId(copyTag);
checkUniqueCharacterId(copyTag);
copyTag.setModified(true);
timelined.resetTimeline();
@@ -1792,7 +1800,7 @@ public class TagTreeContextMenu extends JPopupMenu {
timelined.addTag(++i, copyTag);
copyTag.setTimelined(timelined);
chechUniqueCharacterId(copyTag);
checkUniqueCharacterId(copyTag);
copyTag.setModified(true);
}
@@ -1818,4 +1826,40 @@ public class TagTreeContextMenu extends JPopupMenu {
logger.log(Level.SEVERE, null, ex);
}
}
private void setTagPositionActionPerformed(ActionEvent evt) {
List<TreeItem> items = tagTree.getSelected();
Tag t = (Tag)items.get(0);
TreePath path = tagTree.getSelectionPath();
Timelined timelined = null;
for (int i = path.getPathCount()-1; i >= 0; i--) {
if ((path.getPathComponent(i) instanceof DefineSpriteTag)||(path.getPathComponent(i) instanceof SWF)) {
timelined = (Timelined)path.getPathComponent(i);
}
}
if (timelined == null) { //should not happen
return;
}
SelectTagPositionDialog dialog = new SelectTagPositionDialog(Main.getDefaultDialogsOwner(), t.getSwf(), t, timelined);
if (dialog.showDialog() == AppDialog.OK_OPTION){
Tag selectedTag = dialog.getSelectedTag();
Timelined selectedTimelined = dialog.getSelectedTimelined();
if (selectedTag == t && selectedTimelined == timelined) {
return;
}
timelined.removeTag(t);
if (selectedTag == null) {
selectedTimelined.addTag(t);
} else {
selectedTimelined.addTag(selectedTimelined.indexOfTag(selectedTag), t);
}
timelined.resetTimeline();
if (timelined != selectedTimelined) {
selectedTimelined.resetTimeline();
}
mainPanel.refreshTree(t.getSwf());
}
}
}