diff --git a/CHANGELOG.md b/CHANGELOG.md index fe3a6a3d3..6e1a2cd8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - Selectable text (DefineEditTexts with noselect=0), copy to clipboard, select all - AS1/2 P-code curly braces pair highlighting - [#2478] Simple editor - editing texts (not WYSIWYG, textual as in classic editor) +- [#2478] Simple editor - remembering last frame / timeline ### Fixed - [#2474] Gotos incorrectly decompiled diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index da1dbfa41..d5238947c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.configuration; import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.enums.GridSnapAccuracy; import com.jpexs.decompiler.flash.configuration.enums.GuidesSnapAccuracy; import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode; @@ -1399,6 +1400,16 @@ public final class Configuration { return map.get(fileName); } + + /** + * Get per-swf custom configuration. + * + * @param swf SWF + * @return SWF specific custom configuration, null if not found + */ + public static SwfSpecificCustomConfiguration getSwfSpecificCustomConfiguration(SWF swf) { + return getSwfSpecificCustomConfiguration(swf.getShortPathTitle()); + } /** * Get or create per-swf custom configuration. @@ -1415,6 +1426,16 @@ public final class Configuration { return swfConf; } + + /** + * Get or create per-swf custom configuration. + * + * @param swf SWF + * @return SWF specific custom configuration + */ + public static SwfSpecificCustomConfiguration getOrCreateSwfSpecificCustomConfiguration(SWF swf) { + return getOrCreateSwfSpecificCustomConfiguration(swf.getShortPathTitle()); + } private static String getConfigFile() throws IOException { return getFFDecHome() + storage.getConfigName(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/CustomConfigurationKeys.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/CustomConfigurationKeys.java index 4914caf73..b3e17aba8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/CustomConfigurationKeys.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/CustomConfigurationKeys.java @@ -32,4 +32,9 @@ public class CustomConfigurationKeys { public static final String KEY_BREAKPOINTS = "breakpoints"; public static final String KEY_PATH_RESOLVING = "pathResolving"; public static final String KEY_GUIDES = "guides"; + public static final String KEY_EASY_LAST_SELECTED_TIMELINE = "easy.lastSelected.timeline"; + public static final String KEY_EASY_LAST_SELECTED_FRAME = "easy.lastSelected.frame"; + public static final String KEY_EASY_LAST_SELECTED_FIRST_PARENT_FRAME = "easy.lastSelected.firstParentFrame"; + public static final String KEY_EASY_LAST_SELECTED_PARENT_DEPTHS = "easy.lastSelected.parentDepths"; + public static final String KEY_EASY_LAST_SELECTED_PARENT_FRAMES = "easy.lastSelected.parentFrames"; } diff --git a/src/com/jpexs/decompiler/flash/easygui/EasyPanel.java b/src/com/jpexs/decompiler/flash/easygui/EasyPanel.java index cf2191652..0224abe83 100644 --- a/src/com/jpexs/decompiler/flash/easygui/EasyPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/EasyPanel.java @@ -42,7 +42,7 @@ public class EasyPanel extends JPanel { tabSwitcher.addTabSwitchedListener(new TabSwitchedListener() { @Override public void tabSwitched(SWF value) { - easySwfPanel.setTimelined(value); + easySwfPanel.setSwf(value); } }); } diff --git a/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java b/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java index a65868259..8b7324b26 100644 --- a/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/EasySwfPanel.java @@ -20,6 +20,8 @@ import com.jpexs.decompiler.flash.DefineBeforeUsageFixer; import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.CustomConfigurationKeys; +import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration; import com.jpexs.decompiler.flash.easygui.properties.panels.DocumentPropertiesPanel; import com.jpexs.decompiler.flash.easygui.properties.panels.InstancePropertiesPanel; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; @@ -134,7 +136,8 @@ public class EasySwfPanel extends JPanel { setTimelined(stagePanel.getTimelined(), false); } }); - + } else { + timelinePanel.setFrame(stagePanel.getFrame(), stagePanel.getSelectedDepths()); } } @@ -427,7 +430,15 @@ public class EasySwfPanel extends JPanel { closeTimelineButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - setTimelined(timelined.getSwf()); + SwfSpecificCustomConfiguration conf = Configuration.getOrCreateSwfSpecificCustomConfiguration(timelined.getSwf()); + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_DEPTHS, new ArrayList<>()); + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_FRAMES, new ArrayList<>()); + + String firstParentFrame = conf.getCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_FIRST_PARENT_FRAME, "1"); + + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_FRAME, firstParentFrame); + + setTimelined(timelined.getSwf()); } }); @@ -458,7 +469,7 @@ public class EasySwfPanel extends JPanel { } transformPanel.setVisible(!depths.isEmpty()); - updatePropertiesPanel(); + updatePropertiesPanel(); } }); @@ -593,6 +604,23 @@ public class EasySwfPanel extends JPanel { return rightTabbedPane.getSelectedIndex() == 2; } + public void setSwf(SWF swf) { + Timelined tim = swf; + if (swf != null) { + SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(swf); + if (conf != null) { + int timId = Integer.parseInt(conf.getCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_TIMELINE, "-1")); + if (timId > -1) { + CharacterTag ct = swf.getCharacter(timId); + if (ct != null && ct instanceof Timelined) { + tim = (Timelined) ct; + } + } + } + } + setTimelined(tim); + } + public void setTimelined(Timelined timelined) { setTimelined(timelined, true); } @@ -616,21 +644,10 @@ public class EasySwfPanel extends JPanel { instancePropertiesPanel.update(); textInstancePropertiesPanel.update(); } else { - SWF swf = timelined.getSwf(); + SWF swf = timelined.getSwf(); documentPropertiesPanel.setSwf(swf); libraryTreeTable.setSwf(swf); libraryPreviewPanel.clearAll(); - if (updateStage) { - stagePanel.setTimelined(timelined, swf, 0, true, true, true, true, true, false, true, true, true); - if (timelined instanceof CharacterTag) { - stagePanel.setGuidesCharacter(swf, ((CharacterTag) timelined).getCharacterId()); - } else { - stagePanel.setGuidesCharacter(swf, -1); - } - stagePanel.pause(); - stagePanel.gotoFrame(0); - } - timelinePanel.setTimelined(timelined); if (timelined instanceof SWF) { timelineLabel.setText(EasyStrings.translate("timeline.main")); closeTimelineButton.setVisible(false); @@ -639,6 +656,23 @@ public class EasySwfPanel extends JPanel { timelineLabel.setText(EasyStrings.translate("timeline.item").replace("%item%", nameResolver.getTagName((Tag) timelined))); closeTimelineButton.setVisible(true); } + timelinePanel.setTimelined(timelined); //tohle se volá blbě rekurzivně!!! + + if (updateStage) { + stagePanel.setTimelined(timelined, swf, 0, true, true, true, true, true, false, true, true, true); + if (timelined instanceof CharacterTag) { + stagePanel.setGuidesCharacter(swf, ((CharacterTag) timelined).getCharacterId()); + } else { + stagePanel.setGuidesCharacter(swf, -1); + } + stagePanel.pause(); + //stagePanel.gotoFrame(0); + } + + if (updateStage) { + //timelinePanel.getTimelineBodyPanel().frameSelect(0, 0); + } + instancePropertiesPanel.update(); textInstancePropertiesPanel.update(); } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java index 89de0f526..5cbfb74cd 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java @@ -494,11 +494,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } public void depthSelect(int depth) { - frameSelect(getFirstFrame(), Arrays.asList(depth)); + frameSelect(getFirstFrame(), Arrays.asList(depth), true); } public void depthsSelect(List depths) { - frameSelect(getFirstFrame(), depths); + frameSelect(getFirstFrame(), depths, true); } public int getFirstFrame() { @@ -515,7 +515,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe return cursor.iterator().next().y; } - public void rectSelect(int frame, int depth, int endFrame, int endDepth) { + private 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); @@ -550,11 +550,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe fireFrameSelected(frame, depths); } - public void frameSelect(int frame, int depth) { - frameSelect(frame, Arrays.asList(depth)); + public void frameSelect(int frame, int depth, boolean notify) { + frameSelect(frame, Arrays.asList(depth), notify); } - public void frameSelect(int frame, List depths) { + public void frameSelect(int frame, List depths, boolean notify) { /*if (cursor != null && cursor.width == 1 && cursor.height == 1 && (cursor.contains(frame, depth) || (depth == -1 && cursor.contains(frame, cursor.y)))) { return; } @@ -582,7 +582,9 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe this.depth = depths.isEmpty() ? 0 : depths.get(0); this.endFrame = frame; */ - fireFrameSelected(frame, depths); + if (notify) { + fireFrameSelected(frame, depths); + } } private void fireFrameSelected(int frame, List depths) { @@ -626,7 +628,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe fireFrameSelected(frame, newDepths); } else { if (!(cursor != null && cursor.contains(p) && SwingUtilities.isRightMouseButton(e))) { - frameSelect(p.x, Arrays.asList(p.y)); + frameSelect(p.x, Arrays.asList(p.y), true); } } requestFocusInWindow(); @@ -838,7 +840,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe timelined.resetTimeline(); Point firstCursor = orderedCursor.iterator().next(); - frameSelect(firstCursor.x, firstCursor.y); + frameSelect(firstCursor.x, firstCursor.y, true); timeline = timelined.getTimeline(); refresh(); @@ -1275,22 +1277,22 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe switch (e.getKeyCode()) { case 37: //left if (firstCursorItem.x > 0) { - frameSelect(firstCursorItem.x - 1, firstCursorItem.y); + frameSelect(firstCursorItem.x - 1, firstCursorItem.y, true); } break; case 39: //right if (firstCursorItem.x < timeline.getFrameCount() - 1) { - frameSelect(firstCursorItem.x + 1, firstCursorItem.y); + frameSelect(firstCursorItem.x + 1, firstCursorItem.y, true); } break; case 38: //up if (firstCursorItem.y > 0) { - frameSelect(firstCursorItem.x, firstCursorItem.y - 1); + frameSelect(firstCursorItem.x, firstCursorItem.y - 1, true); } break; case 40: //down if (firstCursorItem.y < timeline.getMaxDepth()) { - frameSelect(firstCursorItem.x, firstCursorItem.y + 1); + frameSelect(firstCursorItem.x, firstCursorItem.y + 1, true); } break; } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java index 95fa11361..ed6c52b28 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelinePanel.java @@ -16,7 +16,10 @@ */ package com.jpexs.decompiler.flash.easygui; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.CustomConfigurationKeys; +import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration; import com.jpexs.decompiler.flash.gui.FasterScrollPane; import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.timeline.Timelined; @@ -124,7 +127,7 @@ public class TimelinePanel extends JPanel { @Override public void frameSelected(int frame, List depths) { - ftimeline.frameSelect(frame, depths); + ftimeline.frameSelect(frame, depths, true); } }); } @@ -158,11 +161,13 @@ public class TimelinePanel extends JPanel { } public void setFrame(int frame, int depth) { - timelineBodyPanel.frameSelect(frame, depth); + timePanel.frameSelect(frame); + timelineBodyPanel.frameSelect(frame, depth, false); } public void setFrame(int frame, List depths) { - timelineBodyPanel.frameSelect(frame, depths); + timePanel.frameSelect(frame); + timelineBodyPanel.frameSelect(frame, depths, false); } public void refresh() { @@ -187,7 +192,20 @@ public class TimelinePanel extends JPanel { timelineBodyPanel.setTimeline(timelined.getTimeline()); depthPanel.setTimeline(timelined.getTimeline()); timePanel.setTimeline(timelined.getTimeline()); - timelineBodyPanel.frameSelect(0, 0); + + SWF swf = timelined.getSwf(); + SwfSpecificCustomConfiguration conf = swf == null ? null : Configuration.getSwfSpecificCustomConfiguration(swf); + + int frame = conf == null ? 0 : Integer.parseInt(conf.getCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_FRAME, "1")) - 1; + + timePanel.frameSelect(frame); + timelineBodyPanel.frameSelect(frame, 0, false); } } + + public TimelineBodyPanel getTimelineBodyPanel() { + return timelineBodyPanel; + } + + } diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 070d13d5c..b9443e06e 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -1776,19 +1776,9 @@ public final class ImagePanel extends JPanel implements MediaDisplay { if (e.getClickCount() == 2 && selectionMode && !transformSelectionMode) { DepthState ds = depthStateUnderCursor; - if (ds != null) { - CharacterTag cht = ds.getCharacter(); - if (cht instanceof Timelined) { - synchronized (lock) { - parentTimelineds.add(timelined); - parentDepths.add(ds.depth); - parentFrames.add(ds.frame.frame); - timelined = (Timelined) cht; - selectedDepths.clear(); - frame = 0; - } - fireMediaDisplayStateChanged(); - } + if (ds != null) { + openDepth(frame, ds.depth); + //gotoFrame(1); } return; @@ -4757,7 +4747,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { this.timelined = drawable; this.parentTimelineds.clear(); this.parentFrames.clear(); - this.parentDepths.clear(); + this.parentDepths.clear(); centerImage(); this.swf = swf; zoomAvailable = allowZoom; @@ -4814,6 +4804,20 @@ public final class ImagePanel extends JPanel implements MediaDisplay { this.showObjectsUnderCursor = showObjectsUnderCursor; this.registrationPointPosition = RegistrationPointPosition.CENTER; iconPanel.calcRect(); + + if (selectionMode) { + SwfSpecificCustomConfiguration conf = Configuration.getOrCreateSwfSpecificCustomConfiguration(swf); + int chid = -1; + if (timelined instanceof CharacterTag) { + chid = ((CharacterTag) timelined).getCharacterId(); + } + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_TIMELINE, "" + chid); + + + loadOpenedDepths(); + } + + clearGuidesInternal(); setNoGuidesCharacter(); @@ -6258,11 +6262,19 @@ public final class ImagePanel extends JPanel implements MediaDisplay { if (frame < 1) { frame = 1; } - + this.autoPlayed = true; this.frame = frame - 1; this.prevFrame = -1; + + if (selectionMode) { + SwfSpecificCustomConfiguration conf = Configuration.getOrCreateSwfSpecificCustomConfiguration(timelined.getSwf()); + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_FRAME, "" + frame); + } + stopInternal(); + + redraw(); fireMediaDisplayStateChanged(); } @@ -6544,4 +6556,90 @@ public final class ImagePanel extends JPanel implements MediaDisplay { return timelined; } } + + public void loadOpenedDepths() { + SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(swf); + if (conf != null) { + int chid = -1; + if (!parentTimelineds.isEmpty()) { + Timelined firstTimelined = parentTimelineds.get(0); + if (firstTimelined instanceof CharacterTag) { + chid = ((CharacterTag) firstTimelined).getCharacterId(); + } + } else { + if (timelined instanceof CharacterTag) { + chid = ((CharacterTag) timelined).getCharacterId(); + } + } + int lastChid = Integer.parseInt(conf.getCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_TIMELINE, "-1")); + if (lastChid != chid) { + return; + } + List parentDepths = conf.getCustomDataAsList(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_DEPTHS); + List parentFrames = conf.getCustomDataAsList(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_FRAMES); + while (parentFrames.size() < parentDepths.size()) { + parentFrames.add("0"); + } + + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_DEPTHS, new ArrayList<>()); + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_FRAMES, new ArrayList<>()); + + int frame = Integer.parseInt(conf.getCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_FRAME, "1")); + + for (int i = 0; i < parentDepths.size(); i++) { + openDepth(Integer.parseInt(parentFrames.get(i)), Integer.parseInt(parentDepths.get(i))); + } + + gotoFrame(frame); + } else { + gotoFrame(1); + } + } + + public void openDepth(int frame, int depth) { + Timelined tim = timelined; + if (tim == null) { + return; + } + if (tim.getTimeline().getFrame(frame) == null) { + return; + } + DepthState ds = tim.getTimeline().getFrame(frame).layers.get(depth); + if (ds != null) { + CharacterTag cht = ds.getCharacter(); + if (cht instanceof Timelined) { + int newFrame = 0; + synchronized (lock) { + parentTimelineds.add(timelined); + parentDepths.add(ds.depth); + parentFrames.add(ds.frame.frame); + timelined = (Timelined) cht; + selectedDepths.clear(); + + int time = ds.time; + newFrame = time % timelined.getFrameCount(); + if (timelined instanceof ButtonTag) { + newFrame = ButtonTag.FRAME_UP; + } + frame = newFrame; + + SWF swf = parentTimelineds.get(0).getSwf(); + SwfSpecificCustomConfiguration conf = Configuration.getOrCreateSwfSpecificCustomConfiguration(swf); + if (parentTimelineds.size() == 1) { + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_FIRST_PARENT_FRAME, "" + (ds.frame.frame + 1)); + } + + List parentDepths = conf.getCustomDataAsList(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_DEPTHS); + parentDepths.add("" + ds.depth); + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_DEPTHS, parentDepths); + + List parentFrames = conf.getCustomDataAsList(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_FRAMES); + parentFrames.add("" + ds.frame.frame); + conf.setCustomData(CustomConfigurationKeys.KEY_EASY_LAST_SELECTED_PARENT_FRAMES, parentFrames); + } + gotoFrame(newFrame + 1); + fireMediaDisplayStateChanged(); + } + } + } }