Added: Simple editor - add/remove frames in buttons

This commit is contained in:
Jindra Petřík
2025-05-17 17:03:59 +02:00
parent 5fecb497b4
commit b8c67a2bc3
10 changed files with 441 additions and 139 deletions

View File

@@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
### Added
- "Starting Flash content debugger" in status bar when debugging starts
- Simple editor - edit parameters of items inside buttons
- Simple editor - add/remove frames in buttons
### Fixed
- Resize export dialogs labels to match localized strings

View File

@@ -270,7 +270,7 @@ public class DefineButton2Tag extends ButtonTag implements ASMSourceContainer {
layer.filters = r.filterList;
layer.matrix = r.placeMatrix;
layer.characterId = r.characterId;
layer.depth = r.placeDepth;
layer.depth = r.placeDepth;
if (r.placeDepth > maxDepth) {
maxDepth = r.placeDepth;
@@ -278,15 +278,28 @@ public class DefineButton2Tag extends ButtonTag implements ASMSourceContainer {
if (r.buttonStateUp) {
frameUp.layers.put(r.placeDepth, new DepthState(layer, frameUp, frameUp, false));
frameUp.layers.get(r.placeDepth).key = true;
}
if (r.buttonStateDown) {
frameDown.layers.put(r.placeDepth, new DepthState(layer, frameDown, frameDown, false));
}
if (r.buttonStateOver) {
frameOver.layers.put(r.placeDepth, new DepthState(layer, frameOver, frameOver, false));
if (!r.buttonStateUp) {
frameOver.layers.get(r.placeDepth).key = true;
}
}
if (r.buttonStateDown) {
frameDown.layers.put(r.placeDepth, new DepthState(layer, frameDown, frameDown, false));
if (!r.buttonStateOver) {
frameDown.layers.get(r.placeDepth).key = true;
}
}
if (r.buttonStateHitTest) {
frameHit.layers.put(r.placeDepth, new DepthState(layer, frameHit, frameHit, false));
if (!r.buttonStateDown) {
frameHit.layers.get(r.placeDepth).key = true;
}
}
}

View File

@@ -264,14 +264,23 @@ public class DefineButtonTag extends ButtonTag implements ASMSourceContainer {
if (r.buttonStateUp) {
frameUp.layers.put(r.placeDepth, new DepthState(layer, frameUp, frameUp, false));
}
if (r.buttonStateDown) {
frameDown.layers.put(r.placeDepth, new DepthState(layer, frameDown, frameDown, false));
}
if (r.buttonStateOver) {
frameOver.layers.put(r.placeDepth, new DepthState(layer, frameOver, frameOver, false));
if (!r.buttonStateUp) {
frameOver.layers.get(r.placeDepth).key = true;
}
}
if (r.buttonStateDown) {
frameDown.layers.put(r.placeDepth, new DepthState(layer, frameDown, frameDown, false));
if (!r.buttonStateOver) {
frameDown.layers.get(r.placeDepth).key = true;
}
}
if (r.buttonStateHitTest) {
frameHit.layers.put(r.placeDepth, new DepthState(layer, frameHit, frameHit, false));
if (!r.buttonStateDown) {
frameDown.layers.get(r.placeDepth).key = true;
}
}
}

View File

@@ -35,6 +35,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@@ -278,7 +279,7 @@ public abstract class ButtonTag extends DrawableTag implements Timelined {
break;
}
}
if (addIfNotExists) {
BUTTONRECORD newRecord = new BUTTONRECORD(swf, this);
switch (frame) {
@@ -295,12 +296,52 @@ public abstract class ButtonTag extends DrawableTag implements Timelined {
newRecord.buttonStateHitTest = true;
break;
}
newRecord.placeDepth = depth;
getRecords().add(newRecord);
return newRecord;
}
return null;
}
public void packRecords() {
List<BUTTONRECORD> records = new ArrayList<>();
for (int i = records.size() - 1; i >= 0; i--) {
BUTTONRECORD rec = records.get(i);
if (rec.isEmpty()) {
records.remove(i);
}
}
}
public Set<Integer> getEmptyFrames() {
Set<Integer> ret = new LinkedHashSet<>();
ret.add(FRAME_UP);
ret.add(FRAME_OVER);
ret.add(FRAME_DOWN);
ret.add(FRAME_HITTEST);
for (BUTTONRECORD rec : getRecords()) {
if (rec.buttonStateUp) {
ret.remove(FRAME_UP);
}
if (rec.buttonStateOver) {
ret.remove(FRAME_OVER);
}
if (rec.buttonStateDown) {
ret.remove(FRAME_DOWN);
}
if (rec.buttonStateHitTest) {
ret.remove(FRAME_HITTEST);
}
}
return ret;
}
public boolean isFrameEmpty(int frame) {
return true;
}
public void setRecordFromPlaceObject(int frame, PlaceObjectTypeTag placeTag) {
BUTTONRECORD selectedRecord = null;

View File

@@ -84,6 +84,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -144,6 +145,11 @@ public class Timeline {
* Map of depth to maximum frame.
*/
private final Map<Integer, Integer> depthMaxFrame = new HashMap<>();
/**
* Map of depth to maximum frame including buttons
*/
private final Map<Integer, Integer> depthMaxFrameButtons = new HashMap<>();
/**
* List of all ASMSources.
@@ -283,6 +289,16 @@ public class Timeline {
ensureInitialized();
return depthMaxFrame;
}
/**
* Gets map of depth to max frame including buttons
*
* @return Map of depth to max frame
*/
public Map<Integer, Integer> getDepthMaxFrameButtons() {
ensureInitialized();
return depthMaxFrameButtons;
}
/**
* Gets map of soundStream id to SoundStreamFrameRanges.
@@ -325,6 +341,7 @@ public class Timeline {
initialized = false;
frames.clear();
depthMaxFrame.clear();
depthMaxFrameButtons.clear();
asmSources.clear();
asmSourceContainers.clear();
actionFrames.clear();
@@ -725,14 +742,31 @@ public class Timeline {
*/
private synchronized void calculateMaxDepthFrames() {
depthMaxFrame.clear();
depthMaxFrameButtons.clear();
for (int d = 0; d <= maxDepth; d++) {
for (int f = frames.size() - 1; f >= 0; f--) {
if (frames.get(f).layers.get(d) != null) {
depthMaxFrame.put(d, f);
depthMaxFrame.put(d, f);
break;
}
}
}
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
Set<Integer> emptyFrames = button.getEmptyFrames();
for (int d = 0; d <= maxDepth; d++) {
for (int f = frames.size() - 1; f >= 0; f--) {
if (frames.get(f).layers.get(d) != null) {
if (!emptyFrames.contains(f)) {
depthMaxFrameButtons.put(d, f);
break;
}
}
}
}
}
}
/**

View File

@@ -33,6 +33,7 @@ import com.jpexs.decompiler.flash.types.annotations.Reserved;
import com.jpexs.decompiler.flash.types.annotations.SWFArray;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.filters.FILTER;
import com.jpexs.helpers.Helper;
import java.io.Serializable;
import java.util.List;
@@ -52,12 +53,14 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh
/**
* Has blend mode?
*
* @since SWF 8
*/
public boolean buttonHasBlendMode;
/**
* Has filter list?
*
* @since SWF 8
*/
public boolean buttonHasFilterList;
@@ -146,6 +149,7 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh
/**
* Constructor.
*
* @param swf SWF
* @param tag Button tag
*/
@@ -154,6 +158,26 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh
this.tag = tag;
}
public BUTTONRECORD(BUTTONRECORD source) {
this.buttonHasBlendMode = source.buttonHasBlendMode;
this.buttonHasFilterList = source.buttonHasFilterList;
this.buttonStateHitTest =source. buttonStateHitTest;
this.buttonStateDown = source.buttonStateDown;
this.buttonStateOver = source.buttonStateOver;
this.buttonStateUp = source.buttonStateUp;
this.characterId = source.characterId;
this.placeDepth = source.placeDepth;
this.placeMatrix = new MATRIX(source.placeMatrix);
this.colorTransform = source.colorTransform == null ? null : new CXFORMWITHALPHA(source.colorTransform);
this.filterList = Helper.deepCopy(source.filterList);
this.blendMode = source.blendMode;
this.swf = source.swf;
this.tag = source.tag;
this.modified = source.modified;
}
/**
* Constructor.
*/
@@ -179,6 +203,7 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh
/**
* Sets the modified flag.
*
* @param value Modified flag
*/
public void setModified(boolean value) {
@@ -211,20 +236,69 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh
this.characterId = characterId;
}
/**
* Enables/disables specific frame
*
* @param frame Frame
* @param value Value
*/
public void setFrame(int frame, boolean value) {
switch (frame) {
case ButtonTag.FRAME_UP:
buttonStateUp = value;
break;
case ButtonTag.FRAME_OVER:
buttonStateOver = value;
break;
case ButtonTag.FRAME_DOWN:
buttonStateDown = value;
break;
case ButtonTag.FRAME_HITTEST:
buttonStateHitTest = value;
break;
}
}
/**
* Has frame
*
* @param frame Frame
* @return True if has
*/
public boolean hasFrame(int frame) {
switch (frame) {
case ButtonTag.FRAME_UP:
return buttonStateUp;
case ButtonTag.FRAME_OVER:
return buttonStateOver;
case ButtonTag.FRAME_DOWN:
return buttonStateDown;
case ButtonTag.FRAME_HITTEST:
return buttonStateHitTest;
}
return false;
}
/**
* Imports placeObject to this BUTTONRECORD
*
* @param placeObject Place tag
*/
public void fromPlaceObject(PlaceObjectTypeTag placeObject) {
placeDepth = placeObject.getDepth();
characterId = placeObject.getCharacterId();
ColorTransform importedColorTrans = placeObject.getColorTransform();
colorTransform = importedColorTrans == null ? null : new CXFORMWITHALPHA(placeObject.getColorTransform());
ColorTransform importedColorTrans = placeObject.getColorTransform();
colorTransform = importedColorTrans == null ? new CXFORMWITHALPHA() : new CXFORMWITHALPHA(placeObject.getColorTransform());
placeMatrix = placeObject.getMatrix();
blendMode = placeObject.getBlendMode();
buttonHasBlendMode = blendMode > 0;
filterList = placeObject.getFilters();
buttonHasFilterList = filterList != null && !filterList.isEmpty();
}
/**
* Converts this BUTTONRECORD to a place tag.
*
* @return Place tag
*/
public PlaceObject3Tag toPlaceObject() {
@@ -260,4 +334,20 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh
}
return placeTag;
}
public boolean isEmpty() {
if (buttonStateUp) {
return false;
}
if (buttonStateOver) {
return false;
}
if (buttonStateDown) {
return false;
}
if (buttonStateHitTest) {
return false;
}
return true;
}
}

View File

@@ -298,15 +298,21 @@ public class EasySwfPanel extends JPanel {
place.matrix = new MATRIX();
place.placeFlagHasMatrix = true;
place.setTimelined(timelined);
if (showFrameTag == null) {
timelined.addTag(place);
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
button.getButtonRecordAt(fframe, newDepth, true).fromPlaceObject(place);
} else {
timelined.addTag(timelined.indexOfTag(showFrameTag), place);
if (showFrameTag == null) {
timelined.addTag(place);
} else {
timelined.addTag(timelined.indexOfTag(showFrameTag), place);
if (fframe < timelined.getFrameCount() - 1) {
RemoveObject2Tag remove = new RemoveObject2Tag(timelined.getSwf());
remove.depth = newDepth;
timelined.addTag(timelined.indexOfTag(showFrameTag) + 1, remove);
if (fframe < timelined.getFrameCount() - 1) {
RemoveObject2Tag remove = new RemoveObject2Tag(timelined.getSwf());
remove.depth = newDepth;
timelined.addTag(timelined.indexOfTag(showFrameTag) + 1, remove);
}
}
}

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.tags.RemoveObject2Tag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
@@ -29,6 +30,8 @@ import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.BUTTONRECORD;
import com.jpexs.helpers.Helper;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
@@ -229,10 +232,16 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(getBackgroundColor());
g.fillRect(0, 0, getWidth(), getHeight());
g.fillRect(0, 0, getWidth(), getHeight());
if (timeline == null) {
return;
}
Set<Integer> emptyFrames = new LinkedHashSet<>();
if (timeline.timelined instanceof ButtonTag) {
emptyFrames = ((ButtonTag) timeline.timelined).getEmptyFrames();
}
Rectangle clip = g.getClipBounds();
int frameWidth = FRAME_WIDTH;
int frameHeight = FRAME_HEIGHT;
@@ -291,7 +300,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
}
}
Map<Integer, Integer> depthMaxFrames = timeline.getDepthMaxFrame();
Map<Integer, Integer> depthMaxFrames = timeline.getDepthMaxFrameButtons();
for (int d = start_d; d <= end_d; d++) {
int maxFrame = depthMaxFrames.containsKey(d) ? depthMaxFrames.get(d) : -1;
if (maxFrame < 0) {
@@ -313,6 +322,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
for (int f = start_f2; f <= end_f2; f++) {
DepthState fl = timeline.getFrame(f).layers.get(d);
if (emptyFrames.contains(f)) {
fl = null;
}
boolean motionTween = fl == null ? false : fl.motionTween;
DepthState flNext = f < max_f ? timeline.getFrame(f + 1).layers.get(d) : null;
@@ -608,7 +622,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
Frame fr = timeline.getFrame(frame);
DepthState ds = fr == null ? null : fr.layers.get(depth);
boolean thisEmpty = ds == null || ds.getCharacter() == null;
Set<Integer> emptyFrames = new LinkedHashSet<>();
if (timeline.timelined instanceof ButtonTag) {
emptyFrames = ((ButtonTag) timeline.timelined).getEmptyFrames();
}
boolean thisEmpty = emptyFrames.contains(frame) || ds == null || ds.getCharacter() == null;
boolean previousEmpty = true;
boolean emptyDepth = true;
boolean somethingBefore = false;
@@ -621,7 +639,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
for (int f = frame - 1; f >= 0; f--) {
fr = timeline.getFrame(f);
ds = fr == null ? null : fr.layers.get(depth);
boolean empty = ds == null || ds.getCharacter() == null;
boolean empty = emptyFrames.contains(f) || ds == null || ds.getCharacter() == null;
if (!empty) {
somethingBefore = true;
break;
@@ -630,7 +648,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
for (int f = frame + 1; f < timeline.getFrameCount(); f++) {
fr = timeline.getFrame(f);
ds = fr == null ? null : fr.layers.get(depth);
boolean empty = ds == null || ds.getCharacter() == null;
boolean empty = emptyFrames.contains(f) || ds == null || ds.getCharacter() == null;
if (!empty) {
somethingAfter = true;
break;
@@ -700,103 +718,108 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
int nf = p.x;
int nd = p.y;
int f = timelined.getFrameCount();
List<Tag> 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++) {
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
BUTTONRECORD rec = button.getButtonRecordAt(nf, nd, false);
if (rec != null) {
rec.setFrame(nf, false);
}
for(int i = nf + 1; i < button.getFrameCount(); i++) {
rec = button.getButtonRecordAt(i, nd, false);
if (rec != null) {
rec.setFrame(i - 1, true);
rec.setFrame(i, false);
}
}
button.packRecords();
} else {
int f = timelined.getFrameCount();
List<Tag> 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 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--) {
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 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) {
break;
for (Tag lt : lastFrameDepthTags) {
timelined.addTag(i, lt);
}
lastFrameDepthTags.clear();
f--;
if (f == nf) {
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 (!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);
}
}
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();
/*System.err.println("=====AFTER=======");
f = 0;
int i = 0;
for (Tag t : timelined.getTags()) {
if (t instanceof ShowFrameTag) {
System.err.println("" + i + ": frame " + f);
f++;
} else {
System.err.println("" + i + ": " + t);
}
i++;
}*/
Point firstCursor = orderedCursor.iterator().next();
frameSelect(firstCursor.x, firstCursor.y);
timeline = timelined.getTimeline();
@@ -851,9 +874,22 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
continue;
}
ds.key = true;
/*RemoveTag rm = new RemoveObject2Tag(timelined.getSwf());
rm.setDepth(depth);
rm.setTimelined(timelined);*/
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
BUTTONRECORD rec = button.getButtonRecordAt(nf, nd, false);
if (rec != null) {
BUTTONRECORD rec2 = new BUTTONRECORD(rec.getSwf(), rec.getTag());
rec2.fromPlaceObject(rec.toPlaceObject());
for(int i = nf; i < button.getFrameCount(); i++) {
rec2.setFrame(i, rec.hasFrame(i));
rec.setFrame(i, false);
}
button.getRecords().add(rec2);
}
continue;
}
PlaceObjectTypeTag place;
try {
place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag();
@@ -870,8 +906,6 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
} else {
pos = timelined.getTags().size();
}
//timelined.addTag(pos++, rm);
timelined.addTag(pos++, place);
}
@@ -910,7 +944,10 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
DepthState ds = null;
if (fframe >= timelined.getFrameCount()) {
if (!(timelined instanceof ButtonTag) && fframe >= timelined.getFrameCount()) {
if (timelined instanceof ButtonTag) {
}
int lastFrame = timelined.getFrameCount() - 1;
for (int d = 1; d <= timeline.maxDepth; d++) {
ds = timeline.getDepthState(lastFrame, d);
@@ -931,30 +968,41 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
timelined.resetTimeline();
}
int nonEmptyFrame = -1;
for (int f = fframe - 1; f >= 0; f--) {
ds = timeline.getDepthState(f, fdepth);
if (ds != null && ds.getCharacter() != null) {
nonEmptyFrame = f;
break;
}
}
PlaceObjectTypeTag place;
try {
place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag();
} catch (InterruptedException | IOException ex) {
//should not happen
return;
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
BUTTONRECORD rec = button.getButtonRecordAt(nonEmptyFrame, fdepth, false);
BUTTONRECORD rec2 = new BUTTONRECORD(rec.getSwf(), rec.getTag());
rec2.fromPlaceObject(rec.toPlaceObject());
rec2.setFrame(fframe, true);
button.getRecords().add(rec2);
} else {
PlaceObjectTypeTag place;
try {
place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag();
} catch (InterruptedException | IOException ex) {
//should not happen
return;
}
place.setTimelined(timelined);
place.setPlaceFlagMove(false);
ShowFrameTag sf = timeline.getFrame(fframe).showFrameTag;
int pos;
if (sf != null) {
pos = timelined.indexOfTag(sf);
} else {
pos = timelined.getTags().size();
}
timelined.addTag(pos++, place);
}
place.setTimelined(timelined);
place.setPlaceFlagMove(false);
ShowFrameTag sf = timeline.getFrame(fframe).showFrameTag;
int pos;
if (sf != null) {
pos = timelined.indexOfTag(sf);
} else {
pos = timelined.getTags().size();
}
timelined.addTag(pos++, place);
timelined.resetTimeline();
timeline = timelined.getTimeline();
@@ -990,14 +1038,17 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
DepthState ds;
Timelined timelined = timeline.timelined;
//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()) {
if (timelined instanceof ButtonTag) {
continue;
}
ReadOnlyTagList tags = timelined.getTags();
for (int i = tags.size() - 1; i >= 0; i--) {
Tag t = tags.get(i);
@@ -1042,9 +1093,13 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
}
boolean somethingAfter = false;
Set<Integer> emptyFrames = new LinkedHashSet<>();
if (timelined instanceof ButtonTag) {
emptyFrames = ((ButtonTag) timelined).getEmptyFrames();
}
for (int f = nf + 1; f < timeline.getFrameCount(); f++) {
ds = timeline.getDepthState(f, nd);
boolean empty = ds == null || ds.getCharacter() == null;
boolean empty = emptyFrames.contains(f) || ds == null || ds.getCharacter() == null;
if (!empty) {
somethingAfter = true;
break;
@@ -1053,10 +1108,29 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
for (int f = nf; f >= 0; f--) {
ds = timeline.getDepthState(f, nd);
boolean empty = ds == null || ds.getCharacter() == null;
boolean empty = emptyFrames.contains(f) || ds == null || ds.getCharacter() == null;
if (!empty || somethingAfter) {
int moveFrameCount = somethingAfter ? 1 : nf - f;
for (int mf = 0; mf < moveFrameCount; mf++) {
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
if (!somethingAfter) {
BUTTONRECORD rec = button.getButtonRecordAt(f, nd, true);
rec.setFrame(nf - mf, true);
} else {
for (int fx = button.getFrameCount() - 1; fx >= nf; fx--) {
BUTTONRECORD rec = button.getButtonRecordAt(fx, nd, false);
if (rec != null) {
rec.setFrame(fx, false);
rec.setFrame(fx + 1, true);
}
}
}
button.packRecords();
continue;
}
int pos = timelined.indexOfTag(timeline.getFrame(f).showFrameTag);
ReadOnlyTagList tags = timelined.getTags();
List<Tag> lastFrameDepthTags = new ArrayList<>();

View File

@@ -18,7 +18,10 @@ package com.jpexs.decompiler.flash.easygui;
import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.BUTTONRECORD;
import java.util.ArrayList;
import java.util.List;
/**
@@ -31,6 +34,7 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation
private final Timelined timelined;
protected List<Tag> tags;
protected List<BUTTONRECORD> buttonRecords;
protected boolean wasModified = false;
protected int fframe;
protected List<Integer> fdepths;
@@ -53,10 +57,26 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation
}
protected void saveTagList() {
tags = timelined.getTags().toArrayList();
if (timelined instanceof ButtonTag) {
List<BUTTONRECORD> recordsCopy = new ArrayList<>();
for (BUTTONRECORD rec :((ButtonTag) timelined).getRecords()) {
recordsCopy.add(new BUTTONRECORD(rec));
}
buttonRecords = recordsCopy;
} else {
tags = timelined.getTags().toArrayList();
}
}
protected void restoreTagList() {
protected void restoreTagList() {
if (buttonRecords != null) {
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
button.getRecords().clear();
button.getRecords().addAll(buttonRecords);
}
timelined.resetTimeline();
}
if (tags != null) {
ReadOnlyTagList newTags = timelined.getTags();
int size = newTags.size();

View File

@@ -3454,6 +3454,9 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
BoundedTag bounded = (BoundedTag) timelined;
RECT rect = bounded.getRect();
if (rect == null) {
return;
}
int width = rect.getWidth();
double scale = 1.0;
/*if (width > swf.displayRect.getWidth()) {
@@ -4140,6 +4143,10 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
return;
}
RECT timRect = timelined.getRect();
if (timRect == null) {
return;
}
/*
int h_value = horizontalScrollBar.getValue();
@@ -4813,6 +4820,9 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
Matrix parentMatrix = new Matrix();
for (int i = 0; i < parentTimelineds.size(); i++) {
DepthState parentDepthState = parentTimelineds.get(i).getTimeline().getDepthState(parentFrames.get(i), parentDepths.get(i));
if (parentDepthState == null) {
continue;
}
parentMatrix = parentMatrix.concatenate(new Matrix(parentDepthState.matrix));
}
@@ -4851,7 +4861,11 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
for (int i = 0; i < selectedDepths.size(); i++) {
if (newMatrix != null) {
DepthState ds = timeline.getFrame(frame).layers.get(selectedDepths.get(i));
Frame fr = timeline.getFrame(frame);
if (fr == null) {
continue;
}
DepthState ds = fr.layers.get(selectedDepths.get(i));
if (ds != null) {
ds.temporaryMatrix = newMatrix.concatenate(new Matrix(ds.matrix)).toMATRIX();
}