From 8ddf009e97a5fe418b2d0ed41fc4d79977f03ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 13 Oct 2024 13:50:10 +0200 Subject: [PATCH] Multiple selection via dragging a rectangle or via Ctrl. --- .../flash/easygui/EasySwfPanel.java | 159 ++-- .../flash/easygui/FrameSelectionListener.java | 4 +- .../flash/easygui/TimelineBodyPanel.java | 774 ++++++++++-------- .../flash/easygui/TimelinePanel.java | 15 +- .../flash/easygui/TimelineTimePanel.java | 2 +- .../properties/AbstractPropertyField.java | 30 +- .../panels/GeneralPropertiesPanel.java | 131 ++- .../decompiler/flash/gui/ImagePanel.java | 550 ++++++++----- 8 files changed, 1028 insertions(+), 637 deletions(-) diff --git a/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java b/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java index 1aea3747d..33db2c7d1 100644 --- a/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.easygui.properties.panels.DocumentPropertiesPanel; import com.jpexs.decompiler.flash.easygui.properties.panels.GeneralPropertiesPanel; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.gui.FasterScrollPane; import com.jpexs.decompiler.flash.gui.ImagePanel; import com.jpexs.decompiler.flash.gui.Main; @@ -60,6 +61,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Point2D; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import javax.swing.Box; import javax.swing.JButton; @@ -110,14 +112,13 @@ public class EasySwfPanel extends JPanel { stagePanel.setTagNameResolver(new EasyTagNameResolver()); stagePanel.setShowAllDepthLevelsInfo(false); stagePanel.setSelectionMode(true); + stagePanel.setMultiSelect(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); + List depths = stagePanel.getSelectedDepths(); + timelinePanel.setDepths(depths); + transformPanel.setVisible(!depths.isEmpty()); updatePropertiesPanel(); } }); @@ -125,37 +126,54 @@ public class EasySwfPanel extends JPanel { stagePanel.addTransformChangeListener(new Runnable() { @Override public void run() { - final int depth = stagePanel.getSelectedDepth(); + final List depths = stagePanel.getSelectedDepths(); 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); + final Matrix newMatrix = stagePanel.getNewMatrix(); + MATRIX previousMatrix = new MATRIX(); + /*synchronized (stagePanel) { + DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depths); previousMatrix = ds.placeObjectTag.getMatrix(); - } + }*/ final Point2D regPoint = stagePanel.getRegistrationPoint(); final RegistrationPointPosition regPointPos = stagePanel.getRegistrationPointPosition(); - final MATRIX fpreviousMatrix = previousMatrix; + final List fpreviousMatrices = new ArrayList<>(); + + { + List dss = getSelectedDepthStates(); + for (DepthState ds : dss) { + if (ds == null) { + fpreviousMatrices.add(null); + } else { + fpreviousMatrices.add(ds.matrix); + } + } + } final boolean transformEnabled = transformEnabled(); undoManager.doOperation(new DoableOperation() { - private boolean wasModified = false; + private final List wasModified = new ArrayList<>(); @Override public void doOperation() { - timelinePanel.setFrame(frame, depth); - DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depth); - wasModified = ds.placeObjectTag.isModified(); - ds.placeObjectTag.setMatrix(newMatrix); - ds.placeObjectTag.setPlaceFlagHasMatrix(newMatrix != null); - ds.placeObjectTag.setModified(true); + timelinePanel.setFrame(frame, depths); + for (int i = 0; i < depths.size(); i++) { + int depth = depths.get(i); + DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depth); + wasModified.add(ds.placeObjectTag.isModified()); + + Matrix contMat = new Matrix(fpreviousMatrices.get(i)).preConcatenate(newMatrix); + + ds.placeObjectTag.setMatrix(contMat.toMATRIX()); + ds.placeObjectTag.setPlaceFlagHasMatrix(newMatrix != null); + ds.placeObjectTag.setModified(true); + } stagePanel.getTimelined().resetTimeline(); stagePanel.repaint(); if (transformEnabled()) { - stagePanel.freeTransformDepth(depth); + stagePanel.freeTransformDepths(depths); stagePanel.setRegistrationPoint(regPoint); if (regPointPos != null) { stagePanel.setRegistrationPointPosition(regPointPos); @@ -166,17 +184,20 @@ public class EasySwfPanel extends JPanel { @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); - if (!wasModified) { - ds.placeObjectTag.setModified(false); + timelinePanel.setFrame(frame, depths); + for (int i = 0; i < depths.size(); i++) { + int depth = depths.get(i); + DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(frame).layers.get(depth); + ds.placeObjectTag.setMatrix(fpreviousMatrices.get(i)); + ds.placeObjectTag.setPlaceFlagHasMatrix(fpreviousMatrices != null); + if (!wasModified.get(i)) { + ds.placeObjectTag.setModified(false); + } } stagePanel.getTimelined().resetTimeline(); stagePanel.repaint(); if (transformEnabled()) { - stagePanel.freeTransformDepth(depth); + stagePanel.freeTransformDepths(depths); transformPanel.setVisible(true); } } @@ -216,12 +237,12 @@ public class EasySwfPanel extends JPanel { private List swfTags; private final int fframe = stagePanel.getFrame(); - private final int fdepth = stagePanel.getSelectedDepth(); + private final List fdepths = stagePanel.getSelectedDepths(); @Override public void doOperation() { super.doOperation(); - timelinePanel.setFrame(fframe, fdepth); + timelinePanel.setFrame(fframe, fdepths); CharacterTag ch = (CharacterTag) tag; int maxDepth = stagePanel.getTimelined().getTimeline().getMaxDepth(); int newDepth = maxDepth + 1; @@ -282,7 +303,7 @@ public class EasySwfPanel extends JPanel { } stagePanel.repaint(); timelinePanel.refresh(); - timelinePanel.setFrame(fframe, fdepth); + timelinePanel.setFrame(fframe, fdepths); } @Override @@ -381,21 +402,15 @@ public class EasySwfPanel extends JPanel { }); timelinePanel.addFrameSelectionListener(new FrameSelectionListener() { @Override - public void frameSelected(int frame, int depth) { + public void frameSelected(int frame, List depths) { stagePanel.pause(); stagePanel.gotoFrame(frame + 1); - stagePanel.selectDepth(depth); + stagePanel.selectDepths(depths); if (transformEnabled()) { - stagePanel.freeTransformDepth(depth); + stagePanel.freeTransformDepths(depths); } - if (depth != -1) { - DepthState ds = stagePanel.getTimelined().getTimeline().getFrame(stagePanel.getFrame()).layers.get(depth); - if (ds == null) { - depth = -1; - } - } - transformPanel.setVisible(depth != -1); + transformPanel.setVisible(!depths.isEmpty()); updatePropertiesPanel(); } }); @@ -442,28 +457,31 @@ public class EasySwfPanel extends JPanel { if (stagePanel.getTimelined() == null) { return; } - int depth = stagePanel.getSelectedDepth(); + List depths = stagePanel.getSelectedDepths(); if (stagePanel.getFrame() >= stagePanel.getTimelined().getFrameCount()) { - depth = -1; + depths.clear(); } - if (depth != -1) { + if (!depths.isEmpty()) { Frame frame = stagePanel.getTimelined().getTimeline().getFrame(stagePanel.getFrame()); if (frame == null) { - depth = -1; + depths.clear(); } else { - DepthState ds = frame.layers.get(depth); - if (ds == null) { - depth = -1; - } + for (int i = 0; i < depths.size(); i++) { + DepthState ds = frame.layers.get(depths.get(i)); + if (ds == null) { + depths.remove(i); + i--; + } + } } } - transformPanel.setVisible(depth != -1); + transformPanel.setVisible(!depths.isEmpty()); if (transformEnabled()) { - stagePanel.freeTransformDepth(depth); + stagePanel.freeTransformDepths(depths); stagePanel.setTransformSelectionMode(true); } else { stagePanel.freeTransformDepth(-1); - stagePanel.selectDepth(depth); + stagePanel.selectDepths(depths); stagePanel.setTransformSelectionMode(false); } } @@ -501,8 +519,8 @@ public class EasySwfPanel extends JPanel { private void updatePropertiesPanel() { CardLayout cl = (CardLayout) propertiesPanel.getLayout(); - PlaceObjectTypeTag place = getSelectedPlaceTag(); - if (place == null) { + List places = getSelectedPlaceTags(); + if (places == null || places.isEmpty()) { cl.show(propertiesPanel, PROPERTIES_DOCUMENT); return; } @@ -579,8 +597,8 @@ public class EasySwfPanel extends JPanel { undoManager.clear(); } - public int getDepth() { - return stagePanel.getSelectedDepth(); + public List getDepths() { + return stagePanel.getSelectedDepths(); } public int getFrame() { @@ -595,20 +613,35 @@ public class EasySwfPanel extends JPanel { return timelined == null ? null : timelined.getSwf(); } - public DepthState getSelectedDepthState() { + public List getSelectedDepthStates() { if (timelined == null) { return null; } int frame = stagePanel.getFrame(); - int depth = stagePanel.getSelectedDepth(); - DepthState ds = timelined.getTimeline().getDepthState(frame, depth); - return ds; + List depths = stagePanel.getSelectedDepths(); + List ret = new ArrayList<>(); + for (int i = 0; i < depths.size(); i++) { + ret.add(timelined.getTimeline().getDepthState(frame, depths.get(i))); + } + return ret; } - public PlaceObjectTypeTag getSelectedPlaceTag() { - DepthState ds = getSelectedDepthState(); - if (ds == null) { + public List getSelectedPlaceTags() { + List dss = getSelectedDepthStates(); + if (dss == null) { return null; } - return ds.placeObjectTag; + List ret = new ArrayList<>(); + for (DepthState ds : dss) { + if (ds == null) { + ret.add(null); + } else { + ret.add(ds.placeObjectTag); + } + } + return ret; } + + public Timelined getTimelined() { + return timelined; + } } diff --git a/src/com/jpexs/decompiler/flash/easygui/FrameSelectionListener.java b/src/com/jpexs/decompiler/flash/easygui/FrameSelectionListener.java index 2a01c3113..693573268 100644 --- a/src/com/jpexs/decompiler/flash/easygui/FrameSelectionListener.java +++ b/src/com/jpexs/decompiler/flash/easygui/FrameSelectionListener.java @@ -16,6 +16,8 @@ */ package com.jpexs.decompiler.flash.easygui; +import java.util.List; + /** * @author JPEXS @@ -23,5 +25,5 @@ package com.jpexs.decompiler.flash.easygui; @FunctionalInterface public interface FrameSelectionListener { - public void frameSelected(int frame, int depth); + public void frameSelected(int frame, List depths); } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java index 01a34ce0f..5d320a7c4 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java @@ -47,9 +47,15 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; @@ -103,15 +109,15 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe private final List changeListeners = new ArrayList<>(); - public Rectangle cursor = null; + public Set cursor = new LinkedHashSet<>(); - private int frame = 0; + /*private int frame = 0; private int endFrame = 0; private int depth = 0; - private int endDepth = 0; + private int endDepth = 0;*/ private final UndoManager undoManager; @@ -258,9 +264,9 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } // draw selected cell - if (cursor != null) { + for (Point c : cursor) { g.setColor(getSelectedColor()); - g.fillRect(cursor.x * frameWidth + 1, cursor.y * frameHeight + 1, (frameWidth * cursor.width) - 1, (frameHeight * cursor.height) - 1); + g.fillRect(c.x * frameWidth + 1, c.y * frameHeight + 1, frameWidth - 1, frameHeight - 1); } int awidth = g.getFontMetrics().stringWidth("a"); @@ -355,9 +361,10 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } } - if (cursor != null && cursor.x >= start_f && cursor.x <= end_f) { + Point cursorFirst = cursor.isEmpty() ? null : cursor.iterator().next(); + if (cursorFirst != null && cursorFirst.x >= start_f && cursorFirst.x <= end_f) { g.setColor(SELECTED_BORDER_COLOR); - g.drawLine(cursor.x * frameWidth + frameWidth / 2, 0, cursor.x * frameWidth + frameWidth / 2, getHeight()); + g.drawLine(cursorFirst.x * frameWidth + frameWidth / 2, 0, cursorFirst.x * frameWidth + frameWidth / 2, getHeight()); } } @@ -366,7 +373,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe int frameHeight = FRAME_HEIGHT; for (int n = frame; n < frame + num_frames; n++) { - if (cursor != null && cursor.contains(n, depth)) { + if (cursor != null && cursor.contains(new Point(n, depth))) { g.setColor(getSelectedColor()); } else { g.setColor(backColor); @@ -377,19 +384,18 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe g.setColor(BORDER_COLOR); g.drawRect(frame * frameWidth, depth * frameHeight, num_frames * frameWidth, frameHeight); - boolean selected = cursor != null && cursor.contains(cursor.x, depth); + //boolean selected = cursor != null && cursor.contains(cursor.x, depth); /*if (cursor != null && cursor.contains(frame, depth) && !cursor.contains(frame + num_frames, depth)) {//frame <= cursor.x && (frame + num_frames) > cursor.x && depth == cursor.y) { selected = true; }*/ - if (cursor != null) { - for (int n = frame + 1; n < frame + num_frames; n++) { - g.setColor(getSelectedColor()); - if (cursor.contains(n, depth)) { - g.fillRect(n * frameWidth + 1, depth * frameHeight + 1, frameWidth, frameHeight); - } + for (int n = frame + 1; n < frame + num_frames; n++) { + g.setColor(getSelectedColor()); + if (cursor.contains(new Point(n, depth))) { + g.fillRect(n * frameWidth + 1, depth * frameHeight + 1, frameWidth, frameHeight); } } + /*if (selected) { g.setColor(getSelectedColor()); g.fillRect(cursor.x * frameWidth + 1, depth * frameHeight + 1, (cursor.width * frameWidth) - 1, frameHeight - 1); @@ -404,7 +410,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe ); } - if (cursor != null && cursor.contains(frame, depth)) { + if (cursor != null && cursor.contains(new Point(frame, depth))) { g.setBackground(getSelectedColorText()); } else { g.setColor(KEY_COLOR); @@ -417,7 +423,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe if (num_frames > 1) { int endFrame = frame + num_frames - 1; - if (cursor != null && cursor.contains(endFrame, depth)) { + if (cursor != null && cursor.contains(new Point(endFrame, depth))) { g.setBackground(getSelectedColorText()); } else { g.setColor(KEY_COLOR); @@ -426,13 +432,13 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe g.fillOval(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2); } else { - if (cursor != null && cursor.contains(endFrame, depth)) { + if (cursor != null && cursor.contains(new Point(endFrame, depth))) { g.setBackground(getSelectedColorText()); } else { g.setColor(STOP_COLOR); } g.fillRect(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight / 2 - 2, frameWidth / 2, frameHeight / 2); - if (cursor != null && cursor.contains(endFrame, depth)) { + if (cursor != null && cursor.contains(new Point(endFrame, depth))) { g.setBackground(getSelectedColorText()); } else { g.setColor(STOP_BORDER_COLOR); @@ -441,7 +447,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } for (int n = frame + 1; n < frame + num_frames; n++) { - if (cursor != null && cursor.contains(n, depth)) { + if (cursor != null && cursor.contains(new Point(n, depth))) { g.setBackground(getSelectedColorText()); } else { g.setColor(BORDER_LINES_COLOR); @@ -458,41 +464,102 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } public void depthSelect(int depth) { - frameSelect(frame, depth); + frameSelect(getFirstFrame(), Arrays.asList(depth)); + } + + public void depthsSelect(List depths) { + frameSelect(getFirstFrame(), depths); + } + + public int getFirstFrame() { + if (cursor.isEmpty()) { + return 0; + } + return cursor.iterator().next().x; } + public int getFirstDepth() { + if (cursor.isEmpty()) { + return 0; + } + return cursor.iterator().next().y; + } + + public void rectSelect(int frame, int depth, int endFrame, int endDepth) { int x1 = Math.min(frame, endFrame); int x2 = Math.max(frame, endFrame); int y1 = Math.min(depth, endDepth); int y2 = Math.max(depth, endDepth); - cursor = new Rectangle( + Set newCursor = new LinkedHashSet<>(); + for (int y = y1; y <= y2; y++) { + for (int x = x1; x <= x2; x++) { + newCursor.add(new Point(x, y)); + } + } + if (cursor.equals(newCursor)) { + return; + } + cursor.clear(); + cursor.addAll(newCursor); + + /*cursor = new Rectangle( x1, y1, x2 - x1 + 1, - y2 - y1 + 1); + y2 - y1 + 1);*/ repaint(); - this.frame = frame; + /*this.frame = frame; this.depth = depth; this.endFrame = endFrame; - this.endDepth = endDepth; + this.endDepth = endDepth;*/ scrollRectToVisible(getFrameBounds(frame, depth)); - fireFrameSelected(frame, depth); + List depths = new ArrayList<>(); + for (int d = depth; d <= endDepth; d++) { + depths.add(d); + } + fireFrameSelected(frame, depths); } - public void frameSelect(int frame, int depth) { - if (cursor != null && cursor.width == 1 && cursor.height == 1 && (cursor.contains(frame, depth) || (depth == -1 && cursor.contains(frame, cursor.y)))) { + public void frameSelect(int frame, int depth) { + frameSelect(frame, Arrays.asList(depth)); + } + + public void frameSelect(int frame, List depths) { + /*if (cursor != null && cursor.width == 1 && cursor.height == 1 && (cursor.contains(frame, depth) || (depth == -1 && cursor.contains(frame, cursor.y)))) { return; } if (depth == -1 && cursor != null) { depth = cursor.y; + }*/ + //rectSelect(frame, depth, frame, depth); + + Set newCursor = new LinkedHashSet<>(); + + for (int d : depths) { + newCursor.add(new Point(frame, d)); } - rectSelect(frame, depth, frame, depth); + if (depths.isEmpty()) { + newCursor.add(new Point(frame, 0)); + } + + cursor.clear(); + cursor.addAll(newCursor); + + repaint(); + + /* + this.frame = frame; + this.depth = depths.isEmpty() ? 0 : depths.get(0); + this.endFrame = frame; + */ + + fireFrameSelected(frame, depths); } - private void fireFrameSelected(int frame, int depth) { + private void fireFrameSelected(int frame, List depths) { for (FrameSelectionListener l : selectionListeners) { - l.frameSelected(frame, depth); + l.frameSelected(frame, depths); } } @@ -509,22 +576,26 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe p.y = maxDepth; }*/ if (shiftDown) { - /*int x1 = frame; - int x2 = p.x; - if (x2 < x1) { - x1 = p.x; - x2 = frame; + rectSelect(getFirstFrame(), getFirstDepth(), p.x, p.y); + } else if (ctrlDown) { + int frame = getFirstFrame(); + if (cursor.contains(p)) { + cursor.remove(p); + } else { + cursor.add(p); } - int y1 = depth; - int y2 = p.y; - if (y2 < y1) { - y1 = p.y; - y2 = depth; - }*/ - rectSelect(frame, depth, p.x, p.y); + List newDepths = new ArrayList<>(); + for (Point t : cursor) { + if (t.x == frame) { + newDepths.add(t.y); + } + } + + repaint(); + fireFrameSelected(frame, newDepths); } else { if (!(cursor != null && cursor.contains(p) && SwingUtilities.isRightMouseButton(e))) { - frameSelect(p.x, p.y); + frameSelect(p.x, Arrays.asList(p.y)); } } requestFocusInWindow(); @@ -532,6 +603,9 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe JPopupMenu popupMenu = new JPopupMenu(); + int frame = getFirstFrame(); + int depth = getFirstDepth(); + Frame fr = timeline.getFrame(frame); DepthState ds = fr == null ? null : fr.layers.get(depth); boolean thisEmpty = ds == null || ds.getCharacter() == null; @@ -573,7 +647,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe JMenuItem removeFrameMenuItem = new JMenuItem(EasyStrings.translate("action.removeFrame")); removeFrameMenuItem.addActionListener(this::removeFrame); - boolean multiSelect = cursor != null && (cursor.width > 1 || cursor.height > 1); + boolean multiSelect = cursor != null && (cursor.size() > 1); if (!thisEmpty || multiSelect) { popupMenu.add(addKeyFrameMenuItem); @@ -596,11 +670,24 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } } - private void removeFrame(ActionEvent e) { - final int fframe = Math.min(frame, endFrame); - final int fdepth = Math.min(depth, endDepth); - final int fendFrame = Math.max(frame, endFrame); - final int fendDepth = Math.max(depth, endDepth); + private Set getBackOrderedCursor() { + Set orderedCursor = new TreeSet<>(new Comparator() { + @Override + public int compare(Point o1, Point o2) { + int dx = o2.x - o1.x; //later frames first + if (dx != 0) { + return dx; + } + return o1.y - o2.y; + } + }); + orderedCursor.addAll(cursor); + return orderedCursor; + } + + private void removeFrame(ActionEvent e) { + + Set orderedCursor = getBackOrderedCursor(); undoManager.doOperation(new TimelinedTagListDoableOperation(timeline.timelined) { @Override @@ -609,92 +696,92 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe Timelined timelined = timeline.timelined; ReadOnlyTagList tags = timelined.getTags(); - for (int nf = 0; nf < fendFrame - fframe + 1; nf++) { - for (int nd = fdepth; nd <= fendDepth; nd++) { + for (Point p : orderedCursor) { + int nf = p.x; + int nd = p.y; - int f = timelined.getFrameCount(); - List lastFrameDepthTags = new ArrayList<>(); - DepthState ds = timeline.getFrame(fframe).layers.get(nd); - if (ds != null && ds.key) { - PlaceObjectTypeTag po = ds.placeObjectTag; - ShowFrameTag sf = timeline.getFrame(fframe).showFrameTag; - int pos = sf == null ? tags.size() : timelined.indexOfTag(sf); - for (int i = pos + 1; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t instanceof RemoveObject2Tag) { - RemoveObject2Tag rt = (RemoveObject2Tag) t; - if (rt.depth == nd) { - timelined.removeTag(po); - timelined.removeTag(rt); - i--; - i--; - } - } - if (t instanceof ShowFrameTag) { - break; - } - } - } - - boolean endsWithRemove = false; - for (int i = tags.size() - 1; i >= 0; i--) { + int f = timelined.getFrameCount(); + List lastFrameDepthTags = new ArrayList<>(); + DepthState ds = timeline.getFrame(nf).layers.get(nd); + if (ds != null && ds.key) { + PlaceObjectTypeTag po = ds.placeObjectTag; + ShowFrameTag sf = timeline.getFrame(nf).showFrameTag; + int pos = sf == null ? tags.size() : timelined.indexOfTag(sf); + for (int i = pos + 1; i < tags.size(); i++) { Tag t = tags.get(i); - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; - if (pt.getDepth() == nd) { - break; - } - } - if (t instanceof RemoveTag) { - RemoveTag rt = (RemoveTag) t; - if (rt.getDepth() == nd) { - endsWithRemove = true; - break; - } - } - } - - for (int i = tags.size() - 1; i >= 0; i--) { - Tag t = tags.get(i); - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; - if (pt.getDepth() == nd) { - lastFrameDepthTags.add(pt); - timelined.removeTag(i); - } - } - if (t instanceof RemoveTag) { - RemoveTag rt = (RemoveTag) t; - if (rt.getDepth() == nd) { - lastFrameDepthTags.add(rt); - timelined.removeTag(i); + if (t instanceof RemoveObject2Tag) { + RemoveObject2Tag rt = (RemoveObject2Tag) t; + if (rt.depth == nd) { + timelined.removeTag(po); + timelined.removeTag(rt); + i--; + i--; } } if (t instanceof ShowFrameTag) { - for (Tag lt : lastFrameDepthTags) { - timelined.addTag(i, lt); - } - lastFrameDepthTags.clear(); - f--; - if (f == fframe) { - break; - } - } - } - - if (!endsWithRemove) { - RemoveTag rt = new RemoveObject2Tag(timelined.getSwf()); - rt.setTimelined(timelined); - rt.setDepth(nd); - Tag lt = tags.get(tags.size() - 1); - if (lt instanceof ShowFrameTag) { - timelined.addTag(tags.size() - 1, rt); - } else { - timelined.addTag(lt); + break; } } } - } + + boolean endsWithRemove = false; + for (int i = tags.size() - 1; i >= 0; i--) { + Tag t = tags.get(i); + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; + if (pt.getDepth() == nd) { + break; + } + } + if (t instanceof RemoveTag) { + RemoveTag rt = (RemoveTag) t; + if (rt.getDepth() == nd) { + endsWithRemove = true; + break; + } + } + } + + for (int i = tags.size() - 1; i >= 0; i--) { + Tag t = tags.get(i); + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; + if (pt.getDepth() == nd) { + lastFrameDepthTags.add(pt); + timelined.removeTag(i); + } + } + if (t instanceof RemoveTag) { + RemoveTag rt = (RemoveTag) t; + if (rt.getDepth() == nd) { + lastFrameDepthTags.add(rt); + timelined.removeTag(i); + } + } + if (t instanceof ShowFrameTag) { + for (Tag lt : lastFrameDepthTags) { + timelined.addTag(i, lt); + } + lastFrameDepthTags.clear(); + f--; + if (f == nf) { + break; + } + } + } + + if (!endsWithRemove) { + RemoveTag rt = new RemoveObject2Tag(timelined.getSwf()); + rt.setTimelined(timelined); + rt.setDepth(nd); + Tag lt = tags.get(tags.size() - 1); + if (lt instanceof ShowFrameTag) { + timelined.addTag(tags.size() - 1, rt); + } else { + timelined.addTag(lt); + } + } + } timelined.resetTimeline(); @@ -710,7 +797,8 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } i++; }*/ - frameSelect(fframe, fdepth); + Point firstCursor = orderedCursor.iterator().next(); + frameSelect(firstCursor.x, firstCursor.y); timeline = timelined.getTimeline(); refresh(); @@ -736,13 +824,15 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } private void addKeyFrame(ActionEvent e) { - if (frame == endFrame && depth == endDepth && timeline.getFrame(frame).layers.get(depth).key) { - return; + if (cursor.size() == 1) { + Point p = cursor.iterator().next(); + DepthState ds = timeline.getDepthState(p.x, p.y); + if (ds != null && ds.key) { + return; + } } - final int fframe = Math.min(frame, endFrame); - final int fdepth = Math.min(depth, endDepth); - final int fendFrame = Math.max(frame, endFrame); - final int fendDepth = Math.max(depth, endDepth); + + Set orderedCursor = getBackOrderedCursor(); undoManager.doOperation(new TimelinedTagListDoableOperation(timeline.timelined) { @@ -751,37 +841,39 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe super.doOperation(); Timelined timelined = timeline.timelined; - for (int nf = frame; nf <= fendFrame; nf++) { - for (int nd = fdepth; nd <= fendDepth; nd++) { - DepthState ds = timeline.getFrame(nf).layers.get(nd); - if (ds.key) { - continue; - } - ds.key = true; - /*RemoveTag rm = new RemoveObject2Tag(timelined.getSwf()); - rm.setDepth(depth); - rm.setTimelined(timelined);*/ - PlaceObjectTypeTag place; - try { - place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag(); - } catch (InterruptedException | IOException ex) { - //should not happen - return; - } - place.setTimelined(timelined); - place.setPlaceFlagMove(true); - ShowFrameTag sf = timeline.getFrame(nf).showFrameTag; - int pos; - if (sf != null) { - pos = timelined.indexOfTag(sf); - } else { - pos = timelined.getTags().size(); - } - //timelined.addTag(pos++, rm); - - timelined.addTag(pos++, place); + //for (int nf = frame; nf <= fendFrame; nf++) { + // for (int nd = fdepth; nd <= fendDepth; nd++) + for (Point p : orderedCursor) { + int nf = p.x; + int nd = p.y; + DepthState ds = timeline.getFrame(nf).layers.get(nd); + if (ds.key) { + continue; } - } + ds.key = true; + /*RemoveTag rm = new RemoveObject2Tag(timelined.getSwf()); + rm.setDepth(depth); + rm.setTimelined(timelined);*/ + PlaceObjectTypeTag place; + try { + place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag(); + } catch (InterruptedException | IOException ex) { + //should not happen + return; + } + place.setTimelined(timelined); + place.setPlaceFlagMove(true); + ShowFrameTag sf = timeline.getFrame(nf).showFrameTag; + int pos; + if (sf != null) { + pos = timelined.indexOfTag(sf); + } else { + pos = timelined.getTags().size(); + } + //timelined.addTag(pos++, rm); + + timelined.addTag(pos++, place); + } timelined.resetTimeline(); timeline = timelined.getTimeline(); @@ -808,6 +900,8 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } private void addKeyFrameEmptyBefore(ActionEvent e) { + final int fframe = getFirstFrame(); + final int fdepth = getFirstDepth(); undoManager.doOperation(new TimelinedTagListDoableOperation(timeline.timelined) { @Override @@ -818,7 +912,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe DepthState ds = null; - if (frame >= timelined.getFrameCount()) { + if (fframe >= timelined.getFrameCount()) { int lastFrame = timelined.getFrameCount() - 1; for (int d = 1; d <= timeline.maxDepth; d++) { ds = timeline.getDepthState(lastFrame, d); @@ -830,7 +924,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } } - for (int f = timelined.getFrameCount(); f <= frame; f++) { + for (int f = timelined.getFrameCount(); f <= fframe; f++) { ShowFrameTag sf = new ShowFrameTag(timelined.getSwf()); sf.setTimelined(timelined); timelined.addTag(sf); @@ -840,8 +934,8 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } - for (int f = frame - 1; f >= 0; f--) { - ds = timeline.getDepthState(f, depth); + for (int f = fframe - 1; f >= 0; f--) { + ds = timeline.getDepthState(f, fdepth); if (ds != null && ds.getCharacter() != null) { break; } @@ -856,7 +950,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } place.setTimelined(timelined); place.setPlaceFlagMove(false); - ShowFrameTag sf = timeline.getFrame(frame).showFrameTag; + ShowFrameTag sf = timeline.getFrame(fframe).showFrameTag; int pos; if (sf != null) { pos = timelined.indexOfTag(sf); @@ -890,10 +984,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } private void addFrame(ActionEvent e) { - final int fframe = Math.min(frame, endFrame); - final int fdepth = Math.min(depth, endDepth); - final int fendFrame = Math.max(frame, endFrame); - final int fendDepth = Math.max(depth, endDepth); + Set orderedCursor = getBackOrderedCursor(); undoManager.doOperation(new TimelinedTagListDoableOperation(timeline.timelined) { @Override @@ -903,181 +994,183 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe Timelined timelined = timeline.timelined; - for (int nf = 0; nf < fendFrame - fframe + 1; nf++) { - for (int nd = fdepth; nd <= fendDepth; nd++) { - - if (fframe >= timelined.getFrameCount()) { - - ReadOnlyTagList tags = timelined.getTags(); - for (int i = tags.size() - 1; i >= 0; i--) { - Tag t = tags.get(i); - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - if (po.getDepth() == nd) { - break; - } - } - if (t instanceof RemoveTag) { - RemoveTag rt = (RemoveTag) t; - if (rt.getDepth() == nd) { - timelined.removeTag(rt); - break; - } - } - } - - int lastFrame = timelined.getFrameCount() - 1; - //Add removeTag to other layers - for (int d = 1; d <= timeline.maxDepth; d++) { - if (d == nd) { - continue; - } - ds = timeline.getDepthState(lastFrame, d); - if (ds != null && ds.getCharacter() != null) { - RemoveTag rt = new RemoveObject2Tag(timelined.getSwf()); - rt.setTimelined(timelined); - rt.setDepth(d); - timelined.addTag(rt); - } - } + //for (int nf = 0; nf < fendFrame - fframe + 1; nf++) { + // for (int nd = fdepth; nd <= fendDepth; nd++) + for (Point p : orderedCursor) { + int nf = p.x; + int nd = p.y; + + if (nf >= timelined.getFrameCount()) { - while (fframe >= timelined.getFrameCount()) { - ShowFrameTag sf = new ShowFrameTag(timelined.getSwf()); - sf.setTimelined(timelined); - timelined.addTag(sf); - timelined.setFrameCount(timelined.getFrameCount() + 1); + ReadOnlyTagList tags = timelined.getTags(); + for (int i = tags.size() - 1; i >= 0; i--) { + Tag t = tags.get(i); + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + if (po.getDepth() == nd) { + break; + } } - timelined.resetTimeline(); - continue; - } - - - boolean somethingAfter = false; - for (int f = fframe + 1; f < timeline.getFrameCount(); f++) { - ds = timeline.getDepthState(f, nd); - boolean empty = ds == null || ds.getCharacter() == null; - if (!empty) { - somethingAfter = true; - break; + if (t instanceof RemoveTag) { + RemoveTag rt = (RemoveTag) t; + if (rt.getDepth() == nd) { + timelined.removeTag(rt); + break; + } + } + } + + int lastFrame = timelined.getFrameCount() - 1; + //Add removeTag to other layers + for (int d = 1; d <= timeline.maxDepth; d++) { + if (d == nd) { + continue; + } + ds = timeline.getDepthState(lastFrame, d); + if (ds != null && ds.getCharacter() != null) { + RemoveTag rt = new RemoveObject2Tag(timelined.getSwf()); + rt.setTimelined(timelined); + rt.setDepth(d); + timelined.addTag(rt); } } - for (int f = fframe; f >= 0; f--) { - ds = timeline.getDepthState(f, nd); - boolean empty = ds == null || ds.getCharacter() == null; - if (!empty || somethingAfter) { - int moveFrameCount = fframe - f; - if (moveFrameCount == 0) { - moveFrameCount = 1; - } - boolean frameAdded = false; - for (int mf = 0; mf < moveFrameCount; mf++) { - int pos = timelined.indexOfTag(timeline.getFrame(f).showFrameTag); - ReadOnlyTagList tags = timelined.getTags(); - List lastFrameDepthTags = new ArrayList<>(); - int f2 = f + 1; - boolean endsWithRemove = false; - for (int i = pos + 1; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - if (po.getDepth() == nd) { - lastFrameDepthTags.add(po); - timelined.removeTag(po); - endsWithRemove = false; - i--; + while (nf >= timelined.getFrameCount()) { + ShowFrameTag sf = new ShowFrameTag(timelined.getSwf()); + sf.setTimelined(timelined); + timelined.addTag(sf); + timelined.setFrameCount(timelined.getFrameCount() + 1); + } + timelined.resetTimeline(); + continue; + } + + + boolean somethingAfter = false; + for (int f = nf + 1; f < timeline.getFrameCount(); f++) { + ds = timeline.getDepthState(f, nd); + boolean empty = ds == null || ds.getCharacter() == null; + if (!empty) { + somethingAfter = true; + break; + } + } + + for (int f = nf; f >= 0; f--) { + ds = timeline.getDepthState(f, nd); + boolean empty = ds == null || ds.getCharacter() == null; + if (!empty || somethingAfter) { + int moveFrameCount = nf - f; + if (moveFrameCount == 0) { + moveFrameCount = 1; + } + boolean frameAdded = false; + for (int mf = 0; mf < moveFrameCount; mf++) { + int pos = timelined.indexOfTag(timeline.getFrame(f).showFrameTag); + ReadOnlyTagList tags = timelined.getTags(); + List lastFrameDepthTags = new ArrayList<>(); + int f2 = f + 1; + boolean endsWithRemove = false; + for (int i = pos + 1; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + if (po.getDepth() == nd) { + lastFrameDepthTags.add(po); + timelined.removeTag(po); + endsWithRemove = false; + i--; + } + } + if (t instanceof RemoveTag) { + RemoveTag ro = (RemoveTag) t; + if (ro.getDepth() == nd) { + lastFrameDepthTags.add(ro); + timelined.removeTag(ro); + endsWithRemove = true; + i--; + } + } + if ((t instanceof ShowFrameTag) || (i == tags.size() - 1)) { + if (!(t instanceof ShowFrameTag) && !lastFrameDepthTags.isEmpty()) { + ShowFrameTag sf = new ShowFrameTag(timelined.getSwf()); + sf.setTimelined(timelined); + timelined.addTag(sf); + i++; + } + + boolean removeOnly = true; + for (Tag lt : lastFrameDepthTags) { + if (!(lt instanceof RemoveTag)) { + removeOnly = false; + break; } } - if (t instanceof RemoveTag) { - RemoveTag ro = (RemoveTag) t; - if (ro.getDepth() == nd) { - lastFrameDepthTags.add(ro); - timelined.removeTag(ro); - endsWithRemove = true; - i--; + + boolean onLastFrame = f2 == timelined.getFrameCount() - 1; + + if (!(removeOnly && onLastFrame)) { + for (Tag lt : lastFrameDepthTags) { + i++; + timelined.addTag(i, lt); } } - if ((t instanceof ShowFrameTag) || (i == tags.size() - 1)) { - if (!(t instanceof ShowFrameTag) && !lastFrameDepthTags.isEmpty()) { + + //Add beyond current total frameCount + if (onLastFrame) { + if ((!removeOnly && !lastFrameDepthTags.isEmpty()) || !endsWithRemove) { + //Add removeTag to other layers + for (int d = 1; d <= timeline.maxDepth; d++) { + if (d == nd) { + continue; + } + ds = timeline.getDepthState(f2, d); + if (ds != null && ds.getCharacter() != null) { + RemoveTag rt = new RemoveObject2Tag(timelined.getSwf()); + rt.setTimelined(timelined); + rt.setDepth(d); + timelined.addTag(i, rt); + i++; + } + } + + frameAdded = true; ShowFrameTag sf = new ShowFrameTag(timelined.getSwf()); sf.setTimelined(timelined); timelined.addTag(sf); i++; + timelined.setFrameCount(timelined.getFrameCount() + 1); } - - boolean removeOnly = true; - for (Tag lt : lastFrameDepthTags) { - if (!(lt instanceof RemoveTag)) { - removeOnly = false; - break; - } - } - - boolean onLastFrame = f2 == timelined.getFrameCount() - 1; - - if (!(removeOnly && onLastFrame)) { - for (Tag lt : lastFrameDepthTags) { - i++; - timelined.addTag(i, lt); - } - } - - //Add beyond current total frameCount - if (onLastFrame) { - if ((!removeOnly && !lastFrameDepthTags.isEmpty()) || !endsWithRemove) { - //Add removeTag to other layers - for (int d = 1; d <= timeline.maxDepth; d++) { - if (d == nd) { - continue; - } - ds = timeline.getDepthState(f2, d); - if (ds != null && ds.getCharacter() != null) { - RemoveTag rt = new RemoveObject2Tag(timelined.getSwf()); - rt.setTimelined(timelined); - rt.setDepth(d); - timelined.addTag(i, rt); - i++; - } - } - - frameAdded = true; - ShowFrameTag sf = new ShowFrameTag(timelined.getSwf()); - sf.setTimelined(timelined); - timelined.addTag(sf); - i++; - timelined.setFrameCount(timelined.getFrameCount() + 1); - } - } - lastFrameDepthTags.clear(); - f2++; } + lastFrameDepthTags.clear(); + f2++; } } - /*if (!frameAdded && fframe == timelined.getFrameCount() - 1) { - //Add removeTag to other layers - for (int d = 1; d <= timeline.maxDepth; d++) { - if (d == nd) { - continue; - } - ds = timeline.getFrame(fframe).layers.get(d); - if (ds != null && ds.getCharacter() != null) { - RemoveTag rt = new RemoveObject2Tag(timelined.getSwf()); - rt.setTimelined(timelined); - rt.setDepth(d); - timelined.addTag(rt); - } - } - for (int mf = 0; mf < moveFrameCount; mf++) { - ShowFrameTag sf = new ShowFrameTag(timelined.getSwf()); - sf.setTimelined(timelined); - timelined.addTag(sf); - } - }*/ - break; } + /*if (!frameAdded && fframe == timelined.getFrameCount() - 1) { + //Add removeTag to other layers + for (int d = 1; d <= timeline.maxDepth; d++) { + if (d == nd) { + continue; + } + ds = timeline.getFrame(fframe).layers.get(d); + if (ds != null && ds.getCharacter() != null) { + RemoveTag rt = new RemoveObject2Tag(timelined.getSwf()); + rt.setTimelined(timelined); + rt.setDepth(d); + timelined.addTag(rt); + } + } + for (int mf = 0; mf < moveFrameCount; mf++) { + ShowFrameTag sf = new ShowFrameTag(timelined.getSwf()); + sf.setTimelined(timelined); + timelined.addTag(sf); + } + }*/ + break; } } - } + } timelined.resetTimeline(); timelined.setFrameCount(timelined.getTimeline().getFrameCount()); @@ -1123,26 +1216,27 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } @Override - public void keyPressed(KeyEvent e) { + public void keyPressed(KeyEvent e) { + Point firstCursorItem = cursor.isEmpty() ? new Point(1,1) : cursor.iterator().next(); switch (e.getKeyCode()) { case 37: //left - if (cursor.x > 0) { - frameSelect(cursor.x - 1, cursor.y); + if (firstCursorItem.x > 0) { + frameSelect(firstCursorItem.x - 1, firstCursorItem.y); } break; case 39: //right - if (cursor.x < timeline.getFrameCount() - 1) { - frameSelect(cursor.x + 1, cursor.y); + if (firstCursorItem.x < timeline.getFrameCount() - 1) { + frameSelect(firstCursorItem.x + 1, firstCursorItem.y); } break; case 38: //up - if (cursor.y > 0) { - frameSelect(cursor.x, cursor.y - 1); + if (firstCursorItem.y > 0) { + frameSelect(firstCursorItem.x, firstCursorItem.y - 1); } break; case 40: //down - if (cursor.y < timeline.getMaxDepth()) { - frameSelect(cursor.x, cursor.y + 1); + if (firstCursorItem.y < timeline.getMaxDepth()) { + frameSelect(firstCursorItem.x, firstCursorItem.y + 1); } break; } @@ -1153,7 +1247,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } public Rectangle getDepthBounds(int depth) { - return getFrameBounds(frame, depth); + return getFrameBounds(getFirstFrame(), depth); } public Rectangle getFrameBounds(int frame, int depth) { @@ -1176,5 +1270,5 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe public void setTimeline(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 830365a07..f7f8e2424 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java @@ -25,6 +25,7 @@ import java.awt.Dimension; import java.awt.SystemColor; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; +import java.util.List; import javax.swing.JPanel; import javax.swing.JScrollPane; @@ -103,7 +104,7 @@ public class TimelinePanel extends JPanel { timelineBodyPanel.addFrameSelectionListener(new FrameSelectionListener() { @Override - public void frameSelected(int frame, int depth) { + public void frameSelected(int frame, List depths) { ftime.frameSelect(frame); } }); @@ -111,8 +112,8 @@ public class TimelinePanel extends JPanel { timePanel.addFrameSelectionListener(new FrameSelectionListener() { @Override - public void frameSelected(int frame, int depth) { - ftimeline.frameSelect(frame, depth); + public void frameSelected(int frame, List depths) { + ftimeline.frameSelect(frame, depths); } }); } @@ -140,10 +141,18 @@ public class TimelinePanel extends JPanel { timelineBodyPanel.depthSelect(depth); } + public void setDepths(List depths) { + timelineBodyPanel.depthsSelect(depths); + } + public void setFrame(int frame, int depth) { timelineBodyPanel.frameSelect(frame, depth); } + public void setFrame(int frame, List depths) { + timelineBodyPanel.frameSelect(frame, depths); + } + public void refresh() { timelineBodyPanel.refresh(); } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java index 439126fab..58fdf42b4 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java @@ -127,7 +127,7 @@ public class TimelineTimePanel extends JPanel implements MouseListener { int frame = (scrollOffset + e.getX()) / TimelineBodyPanel.FRAME_WIDTH; frameSelect(frame); for (FrameSelectionListener l : listeners) { - l.frameSelected(frame, -1); + l.frameSelected(frame, new ArrayList<>()); } } diff --git a/src/com/jpexs/decompiler/flash/easygui/properties/AbstractPropertyField.java b/src/com/jpexs/decompiler/flash/easygui/properties/AbstractPropertyField.java index 199402d6b..375b2ef57 100644 --- a/src/com/jpexs/decompiler/flash/easygui/properties/AbstractPropertyField.java +++ b/src/com/jpexs/decompiler/flash/easygui/properties/AbstractPropertyField.java @@ -36,6 +36,7 @@ import java.awt.event.MouseEvent; import java.awt.geom.Line2D; import java.util.ArrayList; import java.util.List; +import java.util.Set; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; @@ -59,6 +60,8 @@ public abstract class AbstractPropertyField extends JPanel { private AWTEventListener aeListener; + private boolean undetermined = false; + public void addValidation(PropertyValidationInteface validation) { validations.add(validation); } @@ -173,6 +176,7 @@ public abstract class AbstractPropertyField extends JPanel { cancelEdit(); return; } + undetermined = false; readLabel.setText(valueToText(value)); ((CardLayout)AbstractPropertyField.this.getLayout()).show(AbstractPropertyField.this, CARD_READ); fireChange(); @@ -180,15 +184,37 @@ public abstract class AbstractPropertyField extends JPanel { private void cancelEdit() { Toolkit.getDefaultToolkit().removeAWTEventListener(aeListener); - writeField.setText(readLabel.getText()); + if (undetermined) { + writeField.setText(""); + } else { + writeField.setText(readLabel.getText()); + } ((CardLayout)AbstractPropertyField.this.getLayout()).show(AbstractPropertyField.this, CARD_READ); } - public E getValue() { + public E getValue() { + if (undetermined) { + return null; + } return textToValue(writeField.getText()); } + public void setValue(Set value) { + if (value.size() != 1) { + setValue((E) null); + } else { + setValue(value.iterator().next()); + } + } + public void setValue(E value) { + if (value == null) { + readLabel.setText("-"); + writeField.setText(""); + undetermined = true; + fireChange(); + return; + } String text = valueToText(value); readLabel.setText(text); writeField.setText(text); diff --git a/src/com/jpexs/decompiler/flash/easygui/properties/panels/GeneralPropertiesPanel.java b/src/com/jpexs/decompiler/flash/easygui/properties/panels/GeneralPropertiesPanel.java index fcf6a09d8..7344d0391 100644 --- a/src/com/jpexs/decompiler/flash/easygui/properties/panels/GeneralPropertiesPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/properties/panels/GeneralPropertiesPanel.java @@ -33,6 +33,10 @@ import java.awt.BorderLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import javax.swing.BoxLayout; import javax.swing.JLabel; import javax.swing.JPanel; @@ -270,74 +274,119 @@ public class GeneralPropertiesPanel extends AbstractPropertiesPanel { public void update() { updating = true; - DepthState ds = swfPanel.getSelectedDepthState(); - if (ds == null || ds.placeObjectTag == null) { + List dss = swfPanel.getSelectedDepthStates(); + if (dss == null || dss.isEmpty()) { propertiesPanel.setVisible(false); return; } propertiesPanel.setVisible(true); - ColorTransform colorTransform = ds.colorTransForm; - if (colorTransform == null) { - alphaPercentPropertyField.setValue(100); - alphaAddPropertyField.setValue(0); - redPercentPropertyField.setValue(100); - redAddPropertyField.setValue(0); - greenPercentPropertyField.setValue(100); - greenAddPropertyField.setValue(0); - bluePercentPropertyField.setValue(100); - blueAddPropertyField.setValue(0); - } else { - alphaPercentPropertyField.setValue(colorTransform.getAlphaMulti() * 100 / 256); - alphaAddPropertyField.setValue(colorTransform.getAlphaAdd()); - redPercentPropertyField.setValue(colorTransform.getRedMulti() * 100 / 256); - redAddPropertyField.setValue(colorTransform.getRedAdd()); - greenPercentPropertyField.setValue(colorTransform.getGreenMulti() * 100 / 256); - greenAddPropertyField.setValue(colorTransform.getGreenAdd()); - bluePercentPropertyField.setValue(colorTransform.getBlueMulti() * 100 / 256); - blueAddPropertyField.setValue(colorTransform.getBlueAdd()); + Set alphaPercent = new HashSet<>(); + Set alphaAdd = new HashSet<>(); + Set redPercent = new HashSet<>(); + Set redAdd = new HashSet<>(); + Set greenPercent = new HashSet<>(); + Set greenAdd = new HashSet<>(); + Set bluePercent = new HashSet<>(); + Set blueAdd = new HashSet<>(); + + for (DepthState ds : dss) { + if (ds == null) { + continue; + } + ColorTransform colorTransform = ds.colorTransForm; + if (colorTransform == null) { + alphaPercent.add(100); + alphaAdd.add(0); + redPercent.add(100); + redAdd.add(0); + greenPercent.add(100); + greenAdd.add(0); + bluePercent.add(100); + blueAdd.add(0); + } else { + alphaPercent.add(colorTransform.getAlphaMulti() * 100 / 256); + alphaAdd.add(colorTransform.getAlphaAdd()); + redPercent.add(colorTransform.getRedMulti() * 100 / 256); + redAdd.add(colorTransform.getRedAdd()); + greenPercent.add(colorTransform.getGreenMulti() * 100 / 256); + greenAdd.add(colorTransform.getGreenAdd()); + bluePercent.add(colorTransform.getBlueMulti() * 100 / 256); + blueAdd.add(colorTransform.getBlueAdd()); + } } + + + alphaPercentPropertyField.setValue(alphaPercent); + alphaAddPropertyField.setValue(alphaAdd); + redPercentPropertyField.setValue(redPercent); + redAddPropertyField.setValue(redAdd); + greenPercentPropertyField.setValue(greenPercent); + greenAddPropertyField.setValue(greenAdd); + bluePercentPropertyField.setValue(bluePercent); + blueAddPropertyField.setValue(blueAdd); + updating = false; } abstract class PlaceChangeDoableOperation extends ChangeDoableOperation { - int fdepth = swfPanel.getDepth(); + List fdepths = swfPanel.getDepths(); int fframe = swfPanel.getFrame(); - DepthState depthStateBefore = swfPanel.getSelectedDepthState(); - PlaceObjectTypeTag placeObjectBefore = depthStateBefore.placeObjectTag; - PlaceObjectTypeTag placeObjectAfter = null; - private final int minPlace; + List depthStatesBefore = swfPanel.getSelectedDepthStates(); + List placeObjectsBefore = swfPanel.getSelectedPlaceTags(); + List placeObjectsAfter = new ArrayList<>(); + + private final boolean timelinedModifiedBefore = swfPanel.getTimelined().isModified(); + + private Timelined timelined = swfPanel.getTimelined(); + - private final boolean timelinedModifiedBefore = placeObjectBefore.getTimelined().isModified(); public PlaceChangeDoableOperation(String itemIdentifier, int minPlace) { super(itemIdentifier); - this.minPlace = minPlace; + + for (int i = 0; i < fdepths.size(); i++) { + PlaceObjectTypeTag placeObjectBefore = placeObjectsBefore.get(i); + int convNum = placeObjectBefore.getPlaceObjectNum() < minPlace ? minPlace : placeObjectBefore.getPlaceObjectNum(); + PlaceObjectTypeConverter conv = new PlaceObjectTypeConverter(); + PlaceObjectTypeTag placeObjectAfter = conv.convertTagType(placeObjectBefore, timelined.getSwf(), convNum, false); + placeObjectsAfter.add(placeObjectAfter); + } } @Override public final void doOperation() { swfPanel.getStagePanel().gotoFrame(fframe + 1); - swfPanel.getStagePanel().selectDepth(fdepth); - - int convNum = placeObjectBefore.getPlaceObjectNum() < minPlace ? minPlace : placeObjectBefore.getPlaceObjectNum(); - PlaceObjectTypeConverter conv = new PlaceObjectTypeConverter(); - placeObjectAfter = conv.convertTagType(placeObjectBefore, convNum); - - doPlaceOperation(placeObjectAfter, depthStateBefore); - placeObjectAfter.getTimelined().resetTimeline(); + swfPanel.getStagePanel().selectDepths(fdepths); + + for (int i = 0; i < fdepths.size(); i++) { + PlaceObjectTypeTag placeObjectBefore = placeObjectsBefore.get(i); + PlaceObjectTypeTag placeObjectAfter = placeObjectsAfter.get(i); + + int index = timelined.indexOfTag(placeObjectBefore); + timelined.removeTag(index); + timelined.addTag(index, placeObjectAfter); + timelined.setModified(true); + DepthState depthStateBefore = depthStatesBefore.get(i); + doPlaceOperation(placeObjectAfter, depthStateBefore); + } + + timelined.resetTimeline(); update(); } @Override public final void undoOperation() { swfPanel.getStagePanel().gotoFrame(fframe + 1); - swfPanel.getStagePanel().selectDepth(fdepth); - int index = placeObjectAfter.getTimelined().indexOfTag(placeObjectAfter); - Timelined timelined = placeObjectAfter.getTimelined(); - timelined.removeTag(index); - timelined.addTag(index, placeObjectBefore); + swfPanel.getStagePanel().selectDepths(fdepths); + for (int i = 0; i < placeObjectsAfter.size(); i++) { + PlaceObjectTypeTag placeObjectAfter = placeObjectsAfter.get(i); + PlaceObjectTypeTag placeObjectBefore = placeObjectsBefore.get(i); + int index = timelined.indexOfTag(placeObjectAfter); + timelined.removeTag(index); + timelined.addTag(index, placeObjectBefore); + } if (!timelinedModifiedBefore) { timelined.setModified(false); } diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index cac98e8d8..392deb960 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -103,6 +103,7 @@ import java.io.IOException; import java.text.DecimalFormat; import java.text.ParseException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -179,9 +180,10 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private int time = 0; - private int selectedDepth = -1; + //private int selectedDepth = -1; - private int freeTransformDepth = -1; + //private int freeTransformDepth = -1; + private boolean doFreeTransform = false; private Zoom zoom = new Zoom(); @@ -313,7 +315,21 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private boolean selectionMode = false; private boolean transformSelectionMode = false; + + private boolean multiSelect = false; + + private boolean inMoving = false; + + private List selectedDepths = new ArrayList<>(); + public boolean isMultiSelect() { + return multiSelect; + } + + public void setMultiSelect(boolean multiSelect) { + this.multiSelect = multiSelect; + } + public void setSelectionMode(boolean selectionMode) { this.selectionMode = selectionMode; } @@ -604,26 +620,40 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } public synchronized void selectDepth(int depth) { - boolean wasFreeTransform = freeTransformDepth != -1; + List depths = new ArrayList<>(); + if (depth != -1) { + depths.add(depth); + } + selectDepths(depths); + } + public synchronized void selectDepths(List depths) { + + depths = new ArrayList<>(depths); + for (int i = 0; i < depths.size(); i++) { + int depth = depths.get(i); + Frame fr = timelined.getTimeline().getFrame(frame); + if (fr == null || !fr.layers.containsKey(depth)) { + depths.remove(i); + i--; + } + } transformUpdated = null; registrationPointUpdated = null; transform = null; - if (depth != selectedDepth) { - this.selectedDepth = depth; - freeTransformDepth = -1; - } + selectedDepths = new ArrayList<>(depths); + doFreeTransform = false; - if (selectionMode && depthStateUnderCursor != null) { + if (selectionMode) { calculateFreeOrSelectionTransform(); } hideMouseSelection(); redraw(); } - public int getSelectedDepth() { - return selectedDepth; + public List getSelectedDepths() { + return new ArrayList<>(selectedDepths); } public synchronized int getFrame() { @@ -631,18 +661,27 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } private void calculateFreeOrSelectionTransform() { + if (!(doFreeTransform || selectionMode)) { + return; + } + if (selectedDepths.isEmpty()) { + return; + } + DepthState ds = null; Timeline timeline = timelined.getTimeline(); - if (freeTransformDepth > -1 && timeline.getFrameCount() > frame) { - ds = timeline.getFrame(frame).layers.get(freeTransformDepth); - } - if (freeTransformDepth == -1 && selectionMode && selectedDepth != -1) { - ds = timeline.getFrame(frame).layers.get(selectedDepth); + + if (timeline.getFrameCount() <= frame) { + return; } + + /*if (selectedDepths.size() == 1) { + ds = timeline.getFrame(frame).layers.get(selectedDepths.get(0)); + }*/ _viewRect = getViewRect(); - if (ds != null) { + /*if (ds != null) { CharacterTag cht = ds.getCharacter(); if (cht != null) { if (cht instanceof DrawableTag) { @@ -659,16 +698,25 @@ public final class ImagePanel extends JPanel implements MediaDisplay { fireBoundsChange(transformBounds, new Point2D.Double(transformBounds.getCenterX(), transformBounds.getCenterY()), registrationPointPosition); } } - } + } else {*/ + transform = new Matrix(); + Rectangle2D transformBounds = getTransformBounds(); + registrationPointPosition = RegistrationPointPosition.CENTER; + fireBoundsChange(transformBounds, new Point2D.Double(transformBounds.getCenterX(), transformBounds.getCenterY()), registrationPointPosition); + //} } public synchronized void freeTransformDepth(int depth) { - if (selectedDepth != depth) { - selectedDepth = depth; - } - if (depth != freeTransformDepth) { - this.freeTransformDepth = depth; + List depths = new ArrayList<>(); + if (depth != -1) { + depths.add(depth); } + freeTransformDepths(depths); + } + + public synchronized void freeTransformDepths(List depths) { + selectedDepths = new ArrayList<>(depths); + doFreeTransform = !depths.isEmpty(); hilightedEdge = null; hilightedPoints = null; pointEditPanel.setVisible(false); @@ -956,7 +1004,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } g2.setTransform(oldTransform); } - if (!(timelined instanceof SWF) && (freeTransformDepth > -1 || hilightedPoints != null)) { + if (!(timelined instanceof SWF) && (doFreeTransform || hilightedPoints != null)) { int axisX = 0; int axisY = 0; double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; @@ -1084,7 +1132,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { repaint(); } } - if (freeTransformDepth > -1 || hilightedPoints != null) { + if (doFreeTransform || hilightedPoints != null) { if (e.getKeyCode() == KeyEvent.VK_LEFT) { move(-1, 0); } @@ -1136,40 +1184,62 @@ public final class ImagePanel extends JPanel implements MediaDisplay { selectedPoints = new ArrayList<>(pointsUnderCursor); } calculatePointsXY(); - } - - @Override - public void mousePressed(MouseEvent e) { - if (SwingUtilities.isLeftMouseButton(e)) { - if (altDown || selectionMode) { - if (depthStateUnderCursor != null) { - if (transformSelectionMode) { - if (freeTransformDepth != depthStateUnderCursor.depth && mode == Cursor.DEFAULT_CURSOR) { - freeTransformDepth(depthStateUnderCursor.depth); - firePlaceObjectSelected(); - } - } else if ((altDown && !selectionMode) || selectionMode){ - selectDepth(depthStateUnderCursor.depth); - firePlaceObjectSelected(); - } + + if (altDown || selectionMode) { + if (depthStateUnderCursor != null) { + + List newSelectedDepths = new ArrayList<>(); + if (ctrlDown) { + newSelectedDepths.addAll(selectedDepths); + if (newSelectedDepths.contains(depthStateUnderCursor.depth)) { + newSelectedDepths.remove((Integer) depthStateUnderCursor.depth); + } else { + newSelectedDepths.add(depthStateUnderCursor.depth); + } } else { - if (transformSelectionMode) { - if (mode == Cursor.DEFAULT_CURSOR) { - freeTransformDepth(-1); - selectDepth(-1); - firePlaceObjectSelected(); - } - } else if ((altDown && !selectionMode) || selectionMode) { - selectDepth(-1); + newSelectedDepths.add(depthStateUnderCursor.depth); + } + + if (transformSelectionMode) { + if (mode == Cursor.DEFAULT_CURSOR) { + freeTransformDepths(newSelectedDepths); firePlaceObjectSelected(); } + } else if ((altDown && !selectionMode) || selectionMode){ + selectDepths(newSelectedDepths); + firePlaceObjectSelected(); + } + } else { + if (transformSelectionMode) { + if (mode == Cursor.DEFAULT_CURSOR) { + freeTransformDepths(new ArrayList<>()); + selectDepths(new ArrayList<>()); + firePlaceObjectSelected(); + } + } else if ((altDown && !selectionMode) || selectionMode) { + selectDepths(new ArrayList<>()); + firePlaceObjectSelected(); + } + } + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + + if (altDown || selectionMode) { + if (depthStateUnderCursor != null && selectedDepths.contains(depthStateUnderCursor.depth)) { + inMoving = selectionMode; + calculateFreeOrSelectionTransform(); } if (!selectionMode) { return; - } + } } + mouseMoved(e); //to correctly calculate mode, because moseMoved event is not called during dragging - setDragStart(e.getPoint()); + setDragStart(e.getPoint()); if (!shiftDown) { boolean selectedUnderCursor = false; @@ -1245,11 +1315,59 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } updateScrollBarMinMax(); } + + if (dragStart != null && !inMoving && multiSelect) { + Rectangle2D selectionRect = getSelectionRect(); + if (selectionRect != null) { + Frame fr = timelined.getTimeline().getFrame(frame); + List newSelectedDepths = new ArrayList<>(); + if (fr != null) { + for (int d : fr.layers.keySet()) { + DepthState ds = fr.layers.get(d); + CharacterTag cht = ds.getCharacter(); + if (!(cht instanceof DrawableTag)) { + continue; + } + DrawableTag dt = (DrawableTag) cht; + int drawableFrameCount = dt.getNumFrames(); + if (drawableFrameCount == 0) { + drawableFrameCount = 1; + } + + double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; + if (lowQuality) { + zoomDouble /= LQ_FACTOR; + } + Matrix m = new Matrix(); + m.translate(-_viewRect.xMin * zoomDouble, -_viewRect.yMin * zoomDouble); + m.scale(zoomDouble); + int dframe = 0; + Matrix transformation = Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))); + RECT dtRect = dt.getRect(); + Rectangle2D dtRect2D = new Rectangle2D.Double(dtRect.Xmin, dtRect.Ymin, dtRect.getWidth(), dtRect.getHeight()); + Shape outline = transformation.toTransform().createTransformedShape(dtRect2D); + //dt.getOutline(dframe, time, ds.ratio, renderContext, Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))), true, viewRect, zoom); + Rectangle bounds = outline.getBounds(); + + if (selectionRect.contains(bounds)) { + newSelectedDepths.add(d); + } + } + if (transformSelectionMode) { + freeTransformDepths(newSelectedDepths); + } else { + selectDepths(newSelectedDepths); + } + firePlaceObjectSelected(); + } + } + } dragStart = null; selectionEnd = null; + inMoving = false; - if (((freeTransformDepth > -1 && mode != Cursor.DEFAULT_CURSOR) || (selectionMode && transform != null)) && registrationPointUpdated != null && transformUpdated != null) { + if (((doFreeTransform && mode != Cursor.DEFAULT_CURSOR) || (selectionMode && transform != null)) && registrationPointUpdated != null && transformUpdated != null) { synchronized (lock) { Rectangle2D transBoundsBefore = getTransformBounds(); Point2D transRegPointBefore = registrationPoint; @@ -1283,7 +1401,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { fireTransformChanged(); repaint(); } - if (selectionMode && freeTransformDepth == -1) { + if (selectionMode && !doFreeTransform) { transform = null; } mode = Cursor.DEFAULT_CURSOR; @@ -1297,6 +1415,12 @@ public final class ImagePanel extends JPanel implements MediaDisplay { @Override public void mouseDragged(MouseEvent e) { List points = hilightedPoints; + + if (dragStart != null && multiSelect && !inMoving && mode == Cursor.DEFAULT_CURSOR) { + selectionEnd = e.getPoint(); + repaint(); + return; + } if (dragStart != null && points != null) { if (pointsUnderCursor.isEmpty()) { selectionEnd = e.getPoint(); @@ -1374,7 +1498,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { return; } - if (dragStart != null && selectionMode && freeTransformDepth == -1) { + if (dragStart != null && selectionMode && !doFreeTransform) { if (transform == null) { return; } @@ -1402,7 +1526,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { return; } - if (dragStart != null && freeTransformDepth > -1) { + if (dragStart != null && doFreeTransform) { if (transform == null) { return; } @@ -1947,7 +2071,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } return; } - if (freeTransformDepth > -1) { + if (doFreeTransform) { if (bounds == null) { return; } @@ -2139,7 +2263,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } //HERE - if (freeTransformDepth > -1) { + if (doFreeTransform) { //w1 = Math.max(w1, getWidth()); //h1 = Math.max(h1, getHeight()); } @@ -2171,7 +2295,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { if (hilightedPoints != null) { setAllowMove(false); //updateScrollBars(); - } else if (freeTransformDepth > -1) { + } else if (doFreeTransform) { setAllowMove(true); } else if (selectionMode) { setAllowMove(false); @@ -2269,23 +2393,27 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } private void showSelectedName() { - if (selectedDepth > -1 && frame > -1 && timelined != null) { + if (!selectedDepths.isEmpty() && frame > -1 && timelined != null) { Frame f = timelined.getTimeline().getFrame(frame); if (f == null) { return; } - DepthState ds = f.layers.get(selectedDepth); - if (ds != null) { - CharacterTag cht = ds.getCharacter(); - if (cht != null) { - debugLabel.setText(cht.getName()); + if (selectedDepths.size() == 1) { + DepthState ds = f.layers.get(selectedDepths.get(0)); + if (ds != null) { + CharacterTag cht = ds.getCharacter(); + if (cht != null) { + debugLabel.setText(cht.getName()); + } } + } else { + debugLabel.setText("" + selectedDepths.size() + "x"); //TODO: translate } } } public void hideMouseSelection() { - if (selectedDepth > -1) { + if (!selectedDepths.isEmpty()) { showSelectedName(); } else { debugLabel.setText(DEFAULT_DEBUG_LABEL_TEXT); @@ -2411,7 +2539,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { lastMouseEvent = e; redraw(); ButtonTag button = iconPanel.mouseOverButton; - if (button != null && freeTransformDepth == -1 && !frozen) { + if (button != null && !doFreeTransform && !frozen) { DefineButtonSoundTag sounds = button.getSounds(); if (!muted && sounds != null && sounds.buttonSoundChar2 != 0) { // OverUpToOverDown CharacterTag soundCharTag = swf.getCharacter(sounds.buttonSoundChar2); @@ -2456,7 +2584,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { lastMouseEvent = e; redraw(); ButtonTag button = iconPanel.mouseOverButton; - if (!muted && button != null && freeTransformDepth == -1 && !frozen) { + if (!muted && button != null && !doFreeTransform && !frozen) { DefineButtonSoundTag sounds = button.getSounds(); if (sounds != null && sounds.buttonSoundChar3 != 0) { // OverDownToOverUp CharacterTag soundCharTag = swf.getCharacter(sounds.buttonSoundChar3); @@ -2523,7 +2651,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { */ int h_maximum = timRect.Xmax; - if (hilightedPoints != null || freeTransformDepth > -1) { + if (hilightedPoints != null || doFreeTransform) { h_maximum += SCROLL_SPACE_BEFORE; } @@ -2531,7 +2659,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { h_maximum = h_value + h_visibleAmount; }*/ int h_minimum = timRect.Xmin; - if (hilightedPoints != null || freeTransformDepth > -1) { + if (hilightedPoints != null || doFreeTransform) { h_minimum = timRect.Xmin > 0 ? 0 : timRect.Xmin; h_minimum -= SCROLL_SPACE_BEFORE; } @@ -2542,7 +2670,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { int v_visibleAmount = verticalScrollBar.getVisibleAmount(); */ int v_maximum = timRect.Ymax; - if (hilightedPoints != null || freeTransformDepth > -1) { + if (hilightedPoints != null || doFreeTransform) { v_maximum += SCROLL_SPACE_BEFORE; } /*if (v_value + v_visibleAmount > v_maximum) { @@ -2550,7 +2678,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { }*/ int v_minimum = timRect.Ymin; - if (hilightedPoints != null || freeTransformDepth > -1) { + if (hilightedPoints != null || doFreeTransform) { v_minimum = timRect.Ymin > 0 ? 0 : timRect.Ymin; v_minimum -= SCROLL_SPACE_BEFORE; } @@ -3020,7 +3148,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { fireMediaDisplayStateChanged(); } - private static SerializableImage getFrame(Rectangle realRect, RECT rect, ExportRectangle viewRect, SWF swf, int frame, int time, Timelined drawable, RenderContext renderContext, int selectedDepth, int freeTransformDepth, double zoom, Reference registrationPointRef, Reference boundsRef, Matrix transform, Matrix temporaryMatrix, Matrix newMatrix, boolean selectionMode) { + private static SerializableImage getFrame(Rectangle realRect, RECT rect, ExportRectangle viewRect, SWF swf, int frame, int time, Timelined drawable, RenderContext renderContext, List selectedDepths, boolean doFreeTransform, double zoom, Reference registrationPointRef, Reference boundsRef, Matrix transform, Matrix temporaryMatrix, Matrix newMatrix, boolean selectionMode) { Timeline timeline = drawable.getTimeline(); SerializableImage img; @@ -3042,21 +3170,21 @@ public final class ImagePanel extends JPanel implements MediaDisplay { Matrix fullM = m.clone(); - MATRIX oldMatrix = null; - if (freeTransformDepth > -1 && newMatrix != null) { - DepthState ds = timeline.getFrame(frame).layers.get(freeTransformDepth); - if (ds != null) { - oldMatrix = ds.matrix; - timeline.getFrame(frame).layers.get(freeTransformDepth).matrix = newMatrix.toMATRIX(); - } - } - if (freeTransformDepth == -1 && selectionMode && newMatrix != null) { - DepthState ds = timeline.getFrame(frame).layers.get(selectedDepth); - if (ds != null) { - oldMatrix = ds.matrix; - timeline.getFrame(frame).layers.get(selectedDepth).matrix = newMatrix.toMATRIX(); - } - } + List oldMatrices = new ArrayList<>(); + + for (int i = 0; i < selectedDepths.size(); i++) { + if (newMatrix != null) { + DepthState ds = timeline.getFrame(frame).layers.get(selectedDepths.get(i)); + if (ds != null) { + oldMatrices.add(ds.matrix); + timeline.getFrame(frame).layers.get(selectedDepths.get(i)).matrix = newMatrix.concatenate(new Matrix(ds.matrix)).toMATRIX(); + } else { + oldMatrices.add(null); + } + } else { + oldMatrices.add(null); + } + } Frame fr = timeline.getFrame(frame); if (fr == null) { @@ -3074,87 +3202,117 @@ public final class ImagePanel extends JPanel implements MediaDisplay { Graphics2D gg = (Graphics2D) image.getGraphics(); gg.setStroke(new BasicStroke(3)); gg.setPaint(Color.green); - gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); - DepthState ds = null; - if (selectedDepth > -1 && timeline.getFrameCount() > frame && fr != null) { - ds = fr.layers.get(selectedDepth); - } - - if (ds != null && freeTransformDepth == -1) { - CharacterTag cht = ds.getCharacter(); - if (cht != null) { - if (cht instanceof DrawableTag) { - DrawableTag dt = (DrawableTag) cht; - int drawableFrameCount = dt.getNumFrames(); - if (drawableFrameCount == 0) { - drawableFrameCount = 1; - } - - int dframe = time % drawableFrameCount; - Matrix transformation = Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))); - RECT dtRect = dt.getRect(); - Rectangle2D dtRect2D = new Rectangle2D.Double(dtRect.Xmin, dtRect.Ymin, dtRect.getWidth(), dtRect.getHeight()); - Shape outline = transformation.toTransform().createTransformedShape(dtRect2D); - //dt.getOutline(dframe, time, ds.ratio, renderContext, Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))), true, viewRect, zoom); - Rectangle bounds = outline.getBounds(); - gg.setStroke(new BasicStroke(2.0f, - BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, - 10.0f, new float[]{10.0f}, 0.0f)); - gg.setPaint(Color.red); - gg.draw(bounds); + gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); + + if (!doFreeTransform) { + for (int selectedDepth : selectedDepths) { + DepthState ds = null; + if (selectedDepth > -1 && timeline.getFrameCount() > frame && fr != null) { + ds = fr.layers.get(selectedDepth); } - } - } - ds = null; - if (freeTransformDepth > -1 && timeline.getFrameCount() > frame) { - ds = timeline.getFrame(frame).layers.get(freeTransformDepth); - } - if (freeTransformDepth == -1 && selectionMode && newMatrix != null) { - ds = timeline.getFrame(frame).layers.get(selectedDepth); - } + if (ds != null) { + CharacterTag cht = ds.getCharacter(); + if (cht != null) { + if (cht instanceof DrawableTag) { + DrawableTag dt = (DrawableTag) cht; + int drawableFrameCount = dt.getNumFrames(); + if (drawableFrameCount == 0) { + drawableFrameCount = 1; + } - if (ds != null) { - CharacterTag cht = ds.getCharacter(); - if (cht != null) { - if (cht instanceof DrawableTag) { - DrawableTag dt = (DrawableTag) cht; - int drawableFrameCount = dt.getNumFrames(); - if (drawableFrameCount == 0) { - drawableFrameCount = 1; - } - - int dframe = time % drawableFrameCount; - //Matrix finalMatrix = Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m).concatenate(new Matrix(ds.matrix)); - Shape outline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, transform, true, viewRect, zoom); - - if (temporaryMatrix != null) { - Shape tempOutline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, temporaryMatrix, true, viewRect, zoom); - gg.setStroke(new BasicStroke(1)); - gg.setPaint(Color.black); - gg.draw(tempOutline); - } - - Rectangle bounds = outline.getBounds(); - boundsRef.setVal(bounds); - gg.setStroke(new BasicStroke(1)); - gg.setPaint(Color.black); - if (!selectionMode || freeTransformDepth != -1) { - gg.draw(bounds); - drawHandles(gg, bounds); - Point2D regPoint = registrationPointRef.getVal(); - if (regPoint == null) { - regPoint = new Point2D.Double(bounds.getCenterX(), bounds.getCenterY()); + int dframe = time % drawableFrameCount; + Matrix transformation = Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))); + RECT dtRect = dt.getRect(); + Rectangle2D dtRect2D = new Rectangle2D.Double(dtRect.Xmin, dtRect.Ymin, dtRect.getWidth(), dtRect.getHeight()); + Shape outline = transformation.toTransform().createTransformedShape(dtRect2D); + //dt.getOutline(dframe, time, ds.ratio, renderContext, Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m.concatenate(new Matrix(ds.matrix))), true, viewRect, zoom); + Rectangle bounds = outline.getBounds(); + gg.setStroke(new BasicStroke(2.0f, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, + 10.0f, new float[]{10.0f}, 0.0f)); + gg.setPaint(Color.red); + gg.draw(bounds); } - drawRegistrationPoint(gg, regPoint); } } } } - if (freeTransformDepth > -1 && oldMatrix != null && timeline != null && timeline.getFrameCount() > frame) { - timeline.getFrame(frame).layers.get(freeTransformDepth).matrix = oldMatrix; + Rectangle totalBounds = null; + for (int i = 0; i < selectedDepths.size(); i++) { + int selectedDepth = selectedDepths.get(i); + DepthState ds = null; + if (selectedDepth > -1 && timeline.getFrameCount() > frame && fr != null) { + ds = fr.layers.get(selectedDepth); + } + if (ds != null) { + CharacterTag cht = ds.getCharacter(); + if (cht != null) { + if (cht instanceof DrawableTag) { + DrawableTag dt = (DrawableTag) cht; + int drawableFrameCount = dt.getNumFrames(); + if (drawableFrameCount == 0) { + drawableFrameCount = 1; + } + + int dframe = time % drawableFrameCount; + //Matrix finalMatrix = Matrix.getScaleInstance(1 / SWF.unitDivisor).concatenate(m).concatenate(new Matrix(ds.matrix)); + + Matrix transform2 = transform; + + if (oldMatrices != null) { + transform2 = transform2.concatenate(new Matrix(oldMatrices.get(i))); + } + + Shape outline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, transform2, true, viewRect, zoom); + + if (temporaryMatrix != null) { + Matrix tMatrix = temporaryMatrix; + tMatrix = tMatrix.concatenate(new Matrix(ds.matrix)); + Shape tempOutline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, tMatrix, true, viewRect, zoom); + gg.setStroke(new BasicStroke(1)); + gg.setPaint(Color.black); + gg.draw(tempOutline); + } + + Rectangle bounds = outline.getBounds(); + if (totalBounds == null) { + totalBounds = new Rectangle(bounds); + } else { + totalBounds.add(bounds); + } + } + } + } + } + + if (totalBounds == null) { + totalBounds = new Rectangle(0,0,1,1); + } + + boundsRef.setVal(totalBounds); + gg.setStroke(new BasicStroke(1)); + gg.setPaint(Color.black); + if (doFreeTransform) { + gg.draw(totalBounds); + drawHandles(gg, totalBounds); + Point2D regPoint = registrationPointRef.getVal(); + if (regPoint == null) { + regPoint = new Point2D.Double(totalBounds.getCenterX(), totalBounds.getCenterY()); + } + drawRegistrationPoint(gg, regPoint); + } + + if (doFreeTransform && oldMatrices != null && timeline != null && timeline.getFrameCount() > frame) { + for (int i = 0; i < selectedDepths.size(); i++) { + int selectedDepth = selectedDepths.get(i); + MATRIX oldMatrix = oldMatrices.get(i); + if (oldMatrix != null) { + timeline.getFrame(frame).layers.get(selectedDepth).matrix = oldMatrix; + } + } } img = image; @@ -3312,7 +3470,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { int time; Point cursorPosition; int mouseButton; - int selectedDepth; + List selectedDepths; Zoom zoom; SWF swf; @@ -3338,7 +3496,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } mouseButton = this.mouseButton; - selectedDepth = this.selectedDepth; + selectedDepths = this.selectedDepths; zoom = this.zoom; swf = this.swf; } @@ -3352,7 +3510,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { }*/ RenderContext renderContext = new RenderContext(); renderContext.displayObjectCache = displayObjectCache; - if (cursorPosition != null && (freeTransformDepth == -1 || transformSelectionMode)) { + if (cursorPosition != null && (!doFreeTransform || transformSelectionMode)) { renderContext.cursorPosition = new Point((int) (cursorPosition.x * SWF.unitDivisor), (int) (cursorPosition.y * SWF.unitDivisor)); } @@ -3443,7 +3601,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } else if (_viewRect.getHeight() < 0 || _viewRect.getWidth() < 0) { img = new SerializableImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR); } else { - img = getFrame(realRect, rect, _viewRect, swf, frame, frozen ? 0 : time, timelined, renderContext, selectedDepth, freeTransformDepth, zoomDouble, registrationPointRef, boundsRef, trans2, tempTrans2 == null ? null : new Matrix(tempTrans2), transform, selectionMode); + img = getFrame(realRect, rect, _viewRect, swf, frame, frozen ? 0 : time, timelined, renderContext, selectedDepths, doFreeTransform, zoomDouble, registrationPointRef, boundsRef, trans2, tempTrans2 == null ? null : new Matrix(tempTrans2), transform, selectionMode); } /*if(freeTransformDepth > -1) { @@ -3566,7 +3724,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } ButtonTag lastMouseOverButton; - int freeTransformDepth = this.freeTransformDepth; + boolean doFreeTransform = this.doFreeTransform; synchronized (ImagePanel.this) { if (timer == thisTimer) { iconPanel.setImg(img); @@ -3597,7 +3755,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { iconPanel.setCursor(newCursor); } } - if (freeTransformDepth == -1 && hilightedPoints == null) { + if (!doFreeTransform && hilightedPoints == null) { Cursor newCursor; if (iconPanel.isAltDown() && !selectionMode) { if (depthStateUnderCursor == null) { @@ -3623,7 +3781,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { if (!muted) { if (lastMouseOverButton != renderContext.mouseOverButton) { ButtonTag b = renderContext.mouseOverButton; - if (b != null && freeTransformDepth == -1) { + if (b != null && !doFreeTransform) { // New mouse entered DefineButtonSoundTag sounds = b.getSounds(); if (sounds != null && sounds.buttonSoundChar1 != 0) { // IdleToOverUp @@ -3635,7 +3793,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } b = lastMouseOverButton; - if (b != null && freeTransformDepth == -1) { + if (b != null && !doFreeTransform) { // Old mouse leave DefineButtonSoundTag sounds = b.getSounds(); if (sounds != null && sounds.buttonSoundChar0 != 0) { // OverUpToIdle @@ -4114,45 +4272,65 @@ public final class ImagePanel extends JPanel implements MediaDisplay { int time = frozen ? 0 : this.time; DepthState ds = null; Timeline timeline = timelined.getTimeline(); - if (freeTransformDepth > -1 && timeline.getFrameCount() > frame && transform != null) { + /*if (freeTransformDepth > -1 && timeline.getFrameCount() > frame && transform != null) { ds = timeline.getFrame(frame).layers.get(freeTransformDepth); } if (freeTransformDepth == -1 && selectionMode && transform != null && selectedDepth != -1 && timeline.getFrameCount() > frame) { ds = timeline.getFrame(frame).layers.get(selectedDepth); + }*/ + + if (timeline.getFrameCount() <= frame) { + return new Rectangle2D.Double(0,0,1,1); } - + Matrix newMatrix = getNewMatrix(); + + if (newMatrix == null) { + return new Rectangle2D.Double(0,0,1,1); + } RenderContext renderContext = new RenderContext(); renderContext.displayObjectCache = displayObjectCache; - if (cursorPosition != null && freeTransformDepth == -1) { + if (cursorPosition != null && !doFreeTransform) { renderContext.cursorPosition = new Point((int) (cursorPosition.x * SWF.unitDivisor), (int) (cursorPosition.y * SWF.unitDivisor)); } renderContext.mouseButton = mouseButton; renderContext.stateUnderCursor = new ArrayList<>(); - if (ds != null) { - CharacterTag cht = ds.getCharacter(); - if (cht != null) { - if (cht instanceof DrawableTag) { - DrawableTag dt = (DrawableTag) cht; - int drawableFrameCount = dt.getNumFrames(); - if (drawableFrameCount == 0) { - drawableFrameCount = 1; - } + Rectangle2D totalBounds = null; + for (int selectedDepth : selectedDepths) { + ds = timeline.getFrame(frame).layers.get(selectedDepth); + if (ds != null) { + CharacterTag cht = ds.getCharacter(); + if (cht != null) { + if (cht instanceof DrawableTag) { + DrawableTag dt = (DrawableTag) cht; + int drawableFrameCount = dt.getNumFrames(); + if (drawableFrameCount == 0) { + drawableFrameCount = 1; + } - Matrix b = getNewMatrix().concatenate(new Matrix(ds.matrix).inverse()); - int dframe = time % drawableFrameCount; - double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; - if (lowQuality) { - zoomDouble /= LQ_FACTOR; + Matrix b = newMatrix; //.concatenate(new Matrix(ds.matrix).inverse()); + int dframe = time % drawableFrameCount; + double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; + if (lowQuality) { + zoomDouble /= LQ_FACTOR; + } + Shape outline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, b.concatenate(new Matrix(ds.matrix)), true, _viewRect, zoomDouble); + if (totalBounds == null) { + Rectangle2D r = outline.getBounds2D(); + totalBounds = new Rectangle2D.Double(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } else { + totalBounds.add(outline.getBounds2D()); + } } - Shape outline = dt.getOutline(true, dframe, time, ds.ratio, renderContext, b.concatenate(new Matrix(ds.matrix)), true, _viewRect, zoomDouble); - return outline.getBounds2D(); } } } - return null; + if (totalBounds == null) { + return new Rectangle2D.Double(0, 0, 1, 1); + } + return totalBounds; } class DistanceItem {