diff --git a/CHANGELOG.md b/CHANGELOG.md index b2cb772e0..e5a41d183 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ All notable changes to this project will be documented in this file. - List of objects under cursor and coordinates not showing - ConcurrentModificationException in getCharacters on exit - Header of display panel not visible on certain color schemes +- Move tag to action did not remove original tag ### Changed - GFX - DefineExternalImage2 no longer handled as character diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 6b3512e3a..c876558a0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -370,7 +370,7 @@ public final class SWF implements SWFContainerItem, Timelined { @Internal private boolean destroyed = false; - + private Set cyclicCharacters = null; private boolean headerModified = false; @@ -463,15 +463,15 @@ public final class SWF implements SWFContainerItem, Timelined { clearScriptCache(); frameCache.clear(); soundCache.clear(); - + clearImageCache(); clearShapeCache(); clearAbcListCache(); - + characters = null; characterIdTags = null; externalImages2 = null; - + timeline = null; if (dumpInfo != null) { clearDumpInfo(dumpInfo); @@ -489,7 +489,7 @@ public final class SWF implements SWFContainerItem, Timelined { di.getChildInfos().clear(); } - public Map getCharacters() { + public Map getCharacters() { if (characters == null) { synchronized (this) { if (characters == null) { @@ -3110,7 +3110,7 @@ public final class SWF implements SWFContainerItem, Timelined { return image; } - private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline) { + private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline, TagRemoveListener listener) { Set dependingChars = new HashSet<>(); if (toRemove instanceof CharacterTag) { int characterId = ((CharacterTag) toRemove).getCharacterId(); @@ -3119,16 +3119,16 @@ public final class SWF implements SWFContainerItem, Timelined { dependingChars.add(characterId); } } - removeTagWithDependenciesFromTimeline(toRemove, timeline, dependingChars); + removeTagWithDependenciesFromTimeline(toRemove, timeline, dependingChars, listener); } - public boolean removeCharacterFromTimeline(int characterId, Timeline timeline) { + public boolean removeCharacterFromTimeline(int characterId, Timeline timeline, TagRemoveListener listener) { Set chars = new HashSet<>(); chars.add(characterId); - return removeTagWithDependenciesFromTimeline(null, timeline, chars); + return removeTagWithDependenciesFromTimeline(null, timeline, chars, listener); } - private boolean removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline, Set dependingChars) { + private boolean removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline, Set dependingChars, TagRemoveListener listener) { Map stage = new HashMap<>(); Timelined timelined = timeline.timelined; ReadOnlyTagList tags = timelined.getTags(); @@ -3142,6 +3142,9 @@ public final class SWF implements SWFContainerItem, Timelined { int currentCharId = stage.get(depth); stage.remove(depth); if (dependingChars.contains(currentCharId)) { + if (listener != null) { + listener.tagRemoved(t); + } timelined.removeTag(i); modified = true; i--; @@ -3161,6 +3164,9 @@ public final class SWF implements SWFContainerItem, Timelined { } if (placeCharId >= 0 && dependingChars.contains(placeCharId)) { + if (listener != null) { + listener.tagRemoved(t); + } timelined.removeTag(i); modified = true; i--; @@ -3171,6 +3177,10 @@ public final class SWF implements SWFContainerItem, Timelined { if (t instanceof CharacterIdTag) { CharacterIdTag c = (CharacterIdTag) t; if (dependingChars.contains(c.getCharacterId())) { + if (listener != null) { + listener.tagRemoved(t); + } + timelined.removeTag(i); modified = true; i--; @@ -3179,6 +3189,10 @@ public final class SWF implements SWFContainerItem, Timelined { } if (t == toRemove) { + if (listener != null) { + listener.tagRemoved(t); + } + timelined.removeTag(i); modified = true; i--; @@ -3186,19 +3200,19 @@ public final class SWF implements SWFContainerItem, Timelined { } if (t instanceof Timelined) { - modified |= removeTagWithDependenciesFromTimeline(toRemove, ((Timelined) t).getTimeline(), dependingChars); + modified |= removeTagWithDependenciesFromTimeline(toRemove, ((Timelined) t).getTimeline(), dependingChars, listener); } } return modified; } - private boolean removeTagFromTimeline(Tag toRemove, Timeline timeline) { + private boolean removeTagFromTimeline(Tag toRemove, Timeline timeline, TagRemoveListener listener) { boolean modified = false; int characterId = -1; if (toRemove instanceof CharacterTag) { characterId = ((CharacterTag) toRemove).getCharacterId(); if (characterId != -1) { - modified = removeCharacterFromTimeline(characterId, timeline); + modified = removeCharacterFromTimeline(characterId, timeline, listener); } } Timelined timelined = timeline.timelined; @@ -3206,6 +3220,9 @@ public final class SWF implements SWFContainerItem, Timelined { for (int i = 0; i < tags.size(); i++) { Tag t = tags.get(i); if (t == toRemove) { + if (listener != null) { + listener.tagRemoved(t); + } timelined.removeTag(t); i--; continue; @@ -3221,7 +3238,7 @@ public final class SWF implements SWFContainerItem, Timelined { if (t instanceof DefineSpriteTag) { DefineSpriteTag spr = (DefineSpriteTag) t; - boolean sprModified = removeTagFromTimeline(toRemove, spr.getTimeline()); + boolean sprModified = removeTagFromTimeline(toRemove, spr.getTimeline(), listener); if (sprModified) { spr.setModified(true); } @@ -3231,12 +3248,12 @@ public final class SWF implements SWFContainerItem, Timelined { return modified; } - public void removeTags(Collection tags, boolean removeDependencies) { + public void removeTags(Collection tags, boolean removeDependencies, TagRemoveListener listener) { Set timelineds = new HashSet<>(); for (Tag tag : tags) { Timelined timelined = tag.getTimelined(); timelineds.add(timelined); - removeTagInternal(timelined, tag, removeDependencies); + removeTagInternal(timelined, tag, removeDependencies, listener); } for (Timelined timelined : timelineds) { @@ -3262,29 +3279,32 @@ public final class SWF implements SWFContainerItem, Timelined { updateCharacters(); } - public void removeTag(Tag tag, boolean removeDependencies) { + public void removeTag(Tag tag, boolean removeDependencies, TagRemoveListener listener) { Timelined timelined = tag.getTimelined(); - removeTagInternal(timelined, tag, removeDependencies); + removeTagInternal(timelined, tag, removeDependencies, listener); resetTimelines(timelined); updateCharacters(); clearImageCache(); clearShapeCache(); } - private void removeTagInternal(Timelined timelined, Tag tag, boolean removeDependencies) { + private void removeTagInternal(Timelined timelined, Tag tag, boolean removeDependencies, TagRemoveListener listener) { if ((tag instanceof DoABC2Tag) || (tag instanceof DoABCTag)) { clearAbcListCache(); } if (tag instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(tag.getId())) { + if (listener != null) { + listener.tagRemoved(tag); + } timelined.removeTag(tag); timelined.setModified(true); timelined.resetTimeline(); } else // timeline should be always the swf here if (removeDependencies) { - removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); + removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline(), listener); timelined.setModified(true); } else { - boolean modified = removeTagFromTimeline(tag, timelined.getTimeline()); + boolean modified = removeTagFromTimeline(tag, timelined.getTimeline(), listener); if (modified) { timelined.setModified(true); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/TagRemoveListener.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/TagRemoveListener.java new file mode 100644 index 000000000..77977853b --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/TagRemoveListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash; + +import com.jpexs.decompiler.flash.tags.Tag; + +/** + * + * @author JPEXS + */ +public interface TagRemoveListener { + public void tagRemoved(Tag tag); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java index f4d102683..dda21a287 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java @@ -361,7 +361,7 @@ public class DefineSpriteTag extends DrawableTag implements Timelined { @Override public boolean removeCharacter(int characterId) { - boolean modified = getTimeline().removeCharacter(characterId); + boolean modified = getTimeline().removeCharacter(characterId, null); if (modified) { setModified(true); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 265e83149..25f9508dc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.timeline; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.TagRemoveListener; import com.jpexs.decompiler.flash.exporters.BlendModeSetable; import com.jpexs.decompiler.flash.exporters.FrameExporter; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; @@ -558,8 +559,8 @@ public class Timeline { return modified; } - public boolean removeCharacter(int characterId) { - return swf.removeCharacterFromTimeline(characterId, this); + public boolean removeCharacter(int characterId, TagRemoveListener listener) { + return swf.removeCharacterFromTimeline(characterId, this, listener); } public double roundToPixel(double val) { diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index efdaaae30..ece0527ed 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -3581,7 +3581,7 @@ public class CommandLineArgumentParser { } CharacterTag characterTag = swf.getCharacter(characterId); - swf.removeTag(characterTag, removeDependencies); + swf.removeTag(characterTag, removeDependencies, null); if (args.isEmpty() || args.peek().startsWith("-")) { break; diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index af72e75a7..cb7521b0d 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -422,6 +422,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se pinsPanel.destroy(); } + public void destroyItemPin(TreeItem item) { + pinsPanel.removeItem(item); + } + + public void replaceItemPin(TreeItem oldItem, TreeItem newItem) { + pinsPanel.replaceItem(oldItem, newItem); + } + private void handleTreeKeyReleased(KeyEvent e) { AbstractTagTree tree = (AbstractTagTree) e.getSource(); if ((e.getKeyCode() == KeyEvent.VK_UP @@ -3478,7 +3486,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } - swf.removeTags(toRemove, true); + swf.removeTags(toRemove, true, null); refreshTree(swf); } @@ -3507,7 +3515,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } - swf.removeTags(tagsToRemove, true); + swf.removeTags(tagsToRemove, true, null); refreshTree(swf); } diff --git a/src/com/jpexs/decompiler/flash/gui/PinsPanel.java b/src/com/jpexs/decompiler/flash/gui/PinsPanel.java index fd4d60a0a..732ef3c71 100644 --- a/src/com/jpexs/decompiler/flash/gui/PinsPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PinsPanel.java @@ -371,4 +371,24 @@ public class PinsPanel extends JPanel { } save(); } + + public void replaceItem(TreeItem oldItem, TreeItem newItem) { + for (int i = 0; i < items.size(); i++) { + if (items.get(i) == oldItem) { + items.set(i, newItem); + rebuild(); + break; + } + } + } + + public void removeItem(TreeItem item) { + for (int i = 0; i < items.size(); i++) { + if (items.get(i) == item) { + items.remove(i); + rebuild(); + break; + } + } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index ee35316dc..84601885c 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.gui.tagtree; import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.TagRemoveListener; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; @@ -1385,40 +1386,7 @@ public class TagTreeContextMenu extends JPopupMenu { } Tag position = selectPositionDialog.getSelectedTag(); Timelined timelined = selectPositionDialog.getSelectedTimelined(); - ReadOnlyTagList tags = timelined.getTags();; - SWF sourceSwf = items.get(0).getSwf(); - for (TreeItem item : items) { - Tag tag = (Tag) item; - timelined.removeTag(tag); - tag.setSwf(targetSwf, true); - tag.setTimelined(timelined); - checkUniqueCharacterId(tag); - int positionInt = position == null ? timelined.getTags().size() : timelined.indexOfTag(position); - timelined.addTag(positionInt, tag); - targetSwf.updateCharacters(); - tag.setModified(true); - } - - sourceSwf.assignExportNamesToSymbols(); - targetSwf.assignExportNamesToSymbols(); - sourceSwf.assignClassesToSymbols(); - targetSwf.assignClassesToSymbols(); - sourceSwf.clearImageCache(); - targetSwf.clearImageCache(); - sourceSwf.clearShapeCache(); - targetSwf.clearShapeCache(); - sourceSwf.updateCharacters(); - targetSwf.updateCharacters(); - sourceSwf.resetTimelines(sourceSwf); - targetSwf.resetTimelines(targetSwf); - sourceSwf.computeDependentCharacters(); - targetSwf.computeDependentCharacters(); - sourceSwf.computeDependentFrames(); - targetSwf.computeDependentFrames(); - - timelined.setFrameCount(timelined.getTimeline().getFrameCount()); - - mainPanel.refreshTree(new SWF[]{sourceSwf, targetSwf}); + copyOrMoveTags(new LinkedHashSet(items), true, timelined, position); } private void copyTagToActionPerformed(ActionEvent evt, List items, SWF targetSwf) { @@ -2314,6 +2282,9 @@ public class TagTreeContextMenu extends JPopupMenu { } } } + if (item instanceof TreeItem) { + mainPanel.destroyItemPin((TreeItem) item); + } } for (ABC abc : abcsToPack) { @@ -2342,10 +2313,16 @@ public class TagTreeContextMenu extends JPopupMenu { } tagsToRemoveBySwf.get(swf).add(tag); + mainPanel.destroyItemPin(tag); } for (SWF swf : tagsToRemoveBySwf.keySet()) { - swf.removeTags(tagsToRemoveBySwf.get(swf), removeDependencies); + swf.removeTags(tagsToRemoveBySwf.get(swf), removeDependencies, new TagRemoveListener() { + @Override + public void tagRemoved(Tag tag) { + mainPanel.destroyItemPin(tag); + } + }); swf.computeDependentCharacters(); swf.computeDependentFrames(); } @@ -2931,6 +2908,9 @@ public class TagTreeContextMenu extends JPopupMenu { targetSwf.getCharacters(); // force rebuild character id cache copyTag.setModified(true); newTags.add(copyTag); + if (move) { + mainPanel.replaceItemPin(tag, copyTag); + } } else if (sourceSwf == targetSwf && move) { //mainPanel.isClipboardCut() ReadOnlyTagList tags = realTargetTimelined.getTags(); int positionInt = realPosition == null ? tags.size() : tags.indexOf(realPosition);