From c2ceec862829ea09f016af47c4f1324229190d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Thu, 22 Dec 2022 18:15:29 +0100 Subject: [PATCH] Added #1901 Editor mode and autosave feature for header, raw editor, transform --- CHANGELOG.md | 2 +- .../jpexs/decompiler/flash/gui/FontPanel.java | 12 +- .../decompiler/flash/gui/GenericTagPanel.java | 544 +----------------- .../flash/gui/GenericTagTreePanel.java | 198 ++++--- .../decompiler/flash/gui/HeaderInfoPanel.java | 68 ++- .../jpexs/decompiler/flash/gui/MainPanel.java | 2 +- .../decompiler/flash/gui/PreviewPanel.java | 100 +++- .../generictageditors/Amf3ValueEditor.java | 14 +- .../generictageditors/BinaryDataEditor.java | 11 +- .../gui/generictageditors/BooleanEditor.java | 10 +- .../gui/generictageditors/ColorEditor.java | 15 +- .../gui/generictageditors/EnumEditor.java | 105 +--- .../generictageditors/GenericTagEditor.java | 2 +- .../gui/generictageditors/NumberEditor.java | 15 +- .../gui/generictageditors/StringEditor.java | 11 +- .../gui/generictageditors/UUIDEditor.java | 23 +- .../flash/gui/locales/MainFrame.properties | 4 +- .../flash/gui/locales/MainFrame_cs.properties | 4 +- 18 files changed, 362 insertions(+), 778 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51bde4953..f7f4a5029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ All notable changes to this project will be documented in this file. ("PNG/GIF/JPEG+alpha" option in GUI, "-format image:png_gif_jpeg_alpha" for commandline) - [#1910] Copy/paste transform matrix to/from the clipboard - [#1912] Persist selected item in the tree upon quick search (Ctrl+F) -- [#1901] Editor mode for raw edit +- [#1901] Editor mode and autosave feature for header, raw editor, transform ### Fixed - [#1904] NullPointerException when renaming invalid identifiers in AS1/2 files caused by missing charset diff --git a/src/com/jpexs/decompiler/flash/gui/FontPanel.java b/src/com/jpexs/decompiler/flash/gui/FontPanel.java index 9704ea205..44631715f 100644 --- a/src/com/jpexs/decompiler/flash/gui/FontPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FontPanel.java @@ -67,7 +67,7 @@ import layout.TableLayout; * * @author JPEXS */ -public class FontPanel extends JPanel { +public class FontPanel extends JPanel implements TagEditorPanel { private final MainPanel mainPanel; @@ -110,6 +110,7 @@ public class FontPanel extends JPanel { return new DefaultComboBoxModel<>(new Vector<>(faceSet)); } + @Override public boolean isEditing() { return saveButton.isVisible(); } @@ -861,4 +862,13 @@ public class FontPanel extends JPanel { private JPanel contentPanel; private JScrollPane contentScrollPane; + + @Override + public boolean tryAutoSave() { + if (Configuration.autoSaveTagModifications.get()) { + saveButtonActionPerformed(null); + return !(saveButton.isVisible() && saveButton.isEnabled()); + } + return false; + } } diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java index 9c751b23d..5272214aa 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; +import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.gui.generictageditors.Amf3ValueEditor; import com.jpexs.decompiler.flash.gui.generictageditors.BinaryDataEditor; import com.jpexs.decompiler.flash.gui.generictageditors.BooleanEditor; @@ -71,550 +72,25 @@ import javax.swing.SpringLayout; * * @author JPEXS */ -public class GenericTagPanel extends JPanel implements ChangeListener { - - private static final Logger logger = Logger.getLogger(GenericTagPanel.class.getName()); +public abstract class GenericTagPanel extends JPanel { protected final MainPanel mainPanel; - private final JEditorPane genericTagPropertiesEditorPane; - - private final JPanel genericTagPropertiesEditPanel; - - private final JScrollPane genericTagPropertiesEditorPaneScrollPanel; - - private final JScrollPane genericTagPropertiesEditPanelScrollPanel; - - private Tag tag; - - private Tag editedTag; - - private List keys = new ArrayList<>(); - - private Map editors = new HashMap<>(); - - private Map labels = new HashMap<>(); - - private Map types = new HashMap<>(); - - private Map> fieldPaths = new HashMap<>(); - - private Map> fieldIndices = new HashMap<>(); - - private HeaderLabel hdr; - - private Set addKeys = new HashSet<>(); - - private Map addButtons = new HashMap<>(); - - private Map removeButtons = new HashMap<>(); - public GenericTagPanel(MainPanel mainPanel) { super(new BorderLayout()); this.mainPanel = mainPanel; - hdr = new HeaderLabel(""); - add(hdr, BorderLayout.NORTH); - genericTagPropertiesEditorPane = new JEditorPane() { - @Override - public boolean getScrollableTracksViewportWidth() { - return true; - } - }; - genericTagPropertiesEditorPane.setEditable(false); - genericTagPropertiesEditorPaneScrollPanel = new FasterScrollPane(genericTagPropertiesEditorPane); - add(genericTagPropertiesEditorPaneScrollPanel, BorderLayout.CENTER); - - genericTagPropertiesEditPanel = new JPanel(); - genericTagPropertiesEditPanel.setLayout(new SpringLayout()); - JPanel edPanel = new JPanel(new BorderLayout()); - edPanel.add(genericTagPropertiesEditPanel, BorderLayout.NORTH); - genericTagPropertiesEditPanelScrollPanel = new FasterScrollPane(edPanel); } - public void clear() { - editors.clear(); - fieldPaths.clear(); - fieldIndices.clear(); - labels.clear(); - types.clear(); - keys.clear(); - addKeys.clear(); - addButtons.clear(); - removeButtons.clear(); - genericTagPropertiesEditPanel.removeAll(); - genericTagPropertiesEditPanel.setSize(0, 0); - tag = null; - editedTag = null; - } + public abstract void clear(); - public void setEditMode(boolean edit, Tag tag) { - if (tag == null) { - tag = this.tag; - } + public abstract void setEditMode(boolean edit, Tag tag); + + public abstract boolean tryAutoSave(); + public abstract boolean save(); - this.tag = tag; - this.editedTag = Helper.deepCopy(tag); - generateEditControls(editedTag, !edit); + public abstract Tag getTag(); - if (edit) { - remove(genericTagPropertiesEditorPaneScrollPanel); - add(genericTagPropertiesEditPanelScrollPanel, BorderLayout.CENTER); - } else { - genericTagPropertiesEditPanel.removeAll(); - genericTagPropertiesEditPanel.setSize(0, 0); - remove(genericTagPropertiesEditPanelScrollPanel); - add(genericTagPropertiesEditorPaneScrollPanel, BorderLayout.CENTER); - setTagText(this.tag); - } - revalidate(); - repaint(); - } - - private void setTagText(Tag tag) { - clear(); - generateEditControls(tag, true); - StringBuilder val = new StringBuilder(); - for (String key : keys) { - GenericTagEditor ed = editors.get(key); - if (((Component) ed).isVisible()) { - val.append(key).append(" : ").append(ed.getReadOnlyValue()).append("
"); - } - } - //HTML for colors: - val.insert(0, "").append(""); - genericTagPropertiesEditorPane.setContentType("text/html"); - genericTagPropertiesEditorPane.setText(val.toString()); - genericTagPropertiesEditorPane.setCaretPosition(0); - hdr.setText(tag.toString()); - } - - private void generateEditControls(Tag tag, boolean readonly) { - clear(); - generateEditControlsRecursive(tag, "", new ArrayList<>(), new ArrayList<>(), readonly); - change(null); - } - - private void relayout(int propCount) { - //Lay out the panel. - SpringUtilities.makeCompactGrid(genericTagPropertiesEditPanel, - propCount, 3, //rows, cols - 6, 6, //initX, initY - 6, 6); //xPad, yPad - revalidate(); - repaint(); - } - - private int generateEditControlsRecursive(final Object obj, String parent, List parentFields, List parentIndices, boolean readonly) { - if (obj == null) { - return 0; - } - Field[] fields = obj.getClass().getDeclaredFields(); - int propCount = 0; - for (final Field field : fields) { - try { - if (Modifier.isStatic(field.getModifiers())) { - continue; - } - field.setAccessible(true); - String name = parent + field.getName(); - final Object value = field.get(obj); - if (List.class.isAssignableFrom(field.getType())) { - if (value != null) { - int i = 0; - for (Object obj1 : (Iterable) value) { - final String subname = name + "[" + i + "]"; - propCount += addEditor(subname, obj, field, i, obj1.getClass(), obj1, parentFields, parentIndices, readonly); - final int fi = i; - i++; - JButton removeButton = new JButton(View.getIcon("close16")); - removeButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - removeItem(obj, field, fi); - } - }); - removeButtons.put(subname, removeButton); - } - } - } else if (field.getType().isArray()) { - if (value != null) { - for (int i = 0; i < Array.getLength(value); i++) { - Object item = Array.get(value, i); - String subname = name + "[" + i + "]"; - propCount += addEditor(subname, obj, field, i, item.getClass(), item, parentFields, parentIndices, readonly); - final int fi = i; - JButton removeButton = new JButton(View.getIcon("close16")); - removeButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - removeItem(obj, field, fi); - } - }); - removeButtons.put(subname, removeButton); - } - } - } else { - propCount += addEditor(name, obj, field, 0, field.getType(), value, parentFields, parentIndices, readonly); - } - if (ReflectionTools.needsIndex(field) && !readonly && !field.getName().equals("clipActionRecords")) { //No clip actions, sorry - JButton addButton = new JButton(View.getIcon("add16")); - addButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - addItem(obj, field); - } - }); - name += "[]"; - - List parList = new ArrayList<>(parentFields); - parList.add(field); - fieldPaths.put(name, parList); - - List parIndices = new ArrayList<>(parentIndices); - parIndices.add(0); - fieldIndices.put(name, parIndices); - - addRow(name, addButton, field); - addKeys.add(name); - addButtons.put(name, addButton); - } - } catch (IllegalArgumentException | IllegalAccessException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - return propCount; - } - - private void removeItem(Object obj, Field field, int index) { - final JScrollBar sb = genericTagPropertiesEditPanelScrollPanel.getVerticalScrollBar(); - final int val = sb.getValue(); //save scroll top - SWFType swfType = field.getAnnotation(SWFType.class); - if (swfType != null && !swfType.countField().isEmpty()) { //Fields with same countField must be removed from too - Field[] fields = obj.getClass().getDeclaredFields(); - for (int f = 0; f < fields.length; f++) { - SWFType fieldSwfType = fields[f].getAnnotation(SWFType.class); - if (fieldSwfType != null && fieldSwfType.countField().equals(swfType.countField())) { - ReflectionTools.removeFromField(obj, fields[f], index); - } - } - try { - //If countField exists, decrement, otherwise do nothing - Field countField = obj.getClass().getDeclaredField(swfType.countField()); - int cnt = countField.getInt(obj); - cnt--; - countField.setInt(obj, cnt); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { - //ignored - } - } else { - ReflectionTools.removeFromField(obj, field, index); - } - - generateEditControls(editedTag, false); - - //Restore scroll top after some time. TODO: Handle this better. I don't know how :-(. - new Thread() { - - @Override - public void run() { - try { - Thread.sleep(500); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - - View.execInEventDispatch(() -> { - genericTagPropertiesEditPanelScrollPanel.getVerticalScrollBar().setValue(val); - }); - } - - }.start(); - revalidate(); - repaint(); - } - - private void addItem(Object obj, Field field) { - final JScrollBar sb = genericTagPropertiesEditPanelScrollPanel.getVerticalScrollBar(); - final int val = sb.getValue(); //save scroll top - SWFType swfType = field.getAnnotation(SWFType.class); - if (swfType != null && !swfType.countField().isEmpty()) { //Fields with same countField must be enlarged too - Field[] fields = obj.getClass().getDeclaredFields(); - for (int f = 0; f < fields.length; f++) { - SWFType fieldSwfType = fields[f].getAnnotation(SWFType.class); - if (fieldSwfType != null && fieldSwfType.countField().equals(swfType.countField())) { - ReflectionTools.addToField(obj, fields[f], ReflectionTools.getFieldSubSize(obj, fields[f]), true, null); - } - } - try { - //If countField exists, increment, otherwise do nothing - Field countField = obj.getClass().getDeclaredField(swfType.countField()); - int cnt = countField.getInt(obj); - cnt++; - countField.setInt(obj, cnt); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { - //ignored - } - } else { - ReflectionTools.addToField(obj, field, ReflectionTools.getFieldSubSize(obj, field), true, null); - } - generateEditControls(editedTag, false); - - //Restore scroll top after some time. TODO: Handle this better. I don't know how :-(. - new Thread() { - - @Override - public void run() { - try { - Thread.sleep(500); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - - View.execInEventDispatch(() -> { - genericTagPropertiesEditPanelScrollPanel.getVerticalScrollBar().setValue(val); - }); - } - - }.start(); - revalidate(); - repaint(); - } - - private int addEditor(String name, Object obj, Field field, int index, Class type, Object value, List parentList, List parentIndices, boolean readonly) throws IllegalArgumentException, IllegalAccessException { - Calculated calculated = field.getAnnotation(Calculated.class); - if (calculated != null) { - return 0; - } - List parList = new ArrayList<>(parentList); - parList.add(field); - - List parIndices = new ArrayList<>(parentIndices); - parIndices.add(index); - Internal inter = field.getAnnotation(Internal.class); - if (inter != null) { - return 0; - } - SWFType swfType = field.getAnnotation(SWFType.class); - Multiline multiline = field.getAnnotation(Multiline.class); - Component editor; - if (type.equals(int.class) || type.equals(Integer.class) - || type.equals(short.class) || type.equals(Short.class) - || type.equals(long.class) || type.equals(Long.class) - || type.equals(double.class) || type.equals(Double.class) - || type.equals(float.class) || type.equals(Float.class)) { - editor = new NumberEditor(name, obj, field, index, type, swfType); - } else if (type.equals(boolean.class) || type.equals(Boolean.class)) { - editor = new BooleanEditor(name, obj, field, index, type); - } else if (type.equals(String.class)) { - editor = new StringEditor(name, obj, field, index, type, multiline != null); - } else if (type.equals(RGB.class) || type.equals(RGBA.class) || type.equals(ARGB.class)) { - editor = new ColorEditor(name, obj, field, index, type); - } else if (type.equals(ByteArrayRange.class)) { - editor = new BinaryDataEditor(mainPanel, name, obj, field, index, type); - } else if (type.equals(Amf3Value.class)) { - editor = new Amf3ValueEditor(name, obj, field, index, type); - } else { - if (value == null) { - if (readonly) { - return 0; - } - Optional opt = field.getAnnotation(Optional.class); - if (opt == null) { - try { - value = ReflectionTools.newInstanceOf(field.getType()); - field.set(obj, value); - } catch (InstantiationException | IllegalAccessException ex) { - logger.log(Level.SEVERE, null, ex); - return 0; - } - } else { - return 0; - } - } - return generateEditControlsRecursive(value, name + ".", parList, parIndices, readonly); - } - if (editor instanceof GenericTagEditor) { - GenericTagEditor ce = (GenericTagEditor) editor; - ce.addChangeListener(this); - editors.put(name, ce); - fieldPaths.put(name, parList); - fieldIndices.put(name, parIndices); - addRow(name, editor, field); - - ce.added(); - } - return 1; - } - - private void addRow(String name, Component editor, Field field) { - JLabel label = new JLabel(name + ":", JLabel.TRAILING); - label.setVerticalAlignment(JLabel.TOP); - genericTagPropertiesEditPanel.add(label); - label.setLabelFor(editor); - labels.put(name, label); - genericTagPropertiesEditPanel.add(editor); - JLabel typeLabel = new JLabel(swfTypeToString(field.getAnnotation(SWFType.class)), JLabel.TRAILING); - typeLabel.setVerticalAlignment(JLabel.TOP); - genericTagPropertiesEditPanel.add(typeLabel); - types.put(name, typeLabel); - keys.add(name); - } - - public String swfTypeToString(SWFType swfType) { - if (swfType == null) { - return null; - } - String result = swfType.value().toString(); - if (swfType.count() > 0) { - result += "[" + swfType.count(); - if (swfType.countAdd() > 0) { - result += " + " + swfType.countAdd(); - } - result += "]"; - } else if (!swfType.countField().isEmpty()) { - result += "[" + swfType.countField(); - if (swfType.countAdd() > 0) { - result += " + " + swfType.countAdd(); - } - result += "]"; - } - return result; - } - - private void assignTag(Tag t, Tag assigned) { - if (t.getClass() != assigned.getClass()) { - return; - } - for (Field f : t.getClass().getDeclaredFields()) { - if ((f.getModifiers() & Modifier.FINAL) == Modifier.FINAL) { - continue; - } - if ((f.getModifiers() & Modifier.STATIC) == Modifier.STATIC) { - continue; - } - try { - f.set(t, f.get(assigned)); - } catch (IllegalArgumentException | IllegalAccessException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - - public boolean save() { - for (Object component : genericTagPropertiesEditPanel.getComponents()) { - if (component instanceof GenericTagEditor) { - try { - ((GenericTagEditor) component).validateValue(); - ((GenericTagEditor) component).save(); - } catch (IllegalArgumentException iex) { - return false; - } - } - } - SWF swf = tag.getSwf(); - Timelined tim = tag.getTimelined(); - assignTag(tag, editedTag); - tag.setModified(true); - tag.setSwf(swf); - tag.setTimelined(tim); - setTagText(tag); - return true; - } - - public Tag getTag() { - return tag; - } - - @Override - public void change(GenericTagEditor ed) { - for (String key : editors.keySet()) { - GenericTagEditor dependentEditor = editors.get(key); - Component dependentLabel = labels.get(key); - Component dependentTypeLabel = types.get(key); - List path = fieldPaths.get(key); - List indices = fieldIndices.get(key); - String p = ""; - boolean conditionMet = true; - for (int i = 0; i < path.size(); i++) { - Field f = path.get(i); - int index = indices.get(i); - String par = p; - if (!p.isEmpty()) { - p += "."; - } - p += f.getName(); - if (ReflectionTools.needsIndex(f)) { - p += "[" + index + "]"; - } - Conditional cond = f.getAnnotation(Conditional.class); - if (cond != null) { - ConditionEvaluator ev = new ConditionEvaluator(cond); - - try { - Set fieldNames = ev.getFields(); - Map fields = new HashMap<>(); - for (String fld : fieldNames) { - String ckey = ""; - if (!par.isEmpty()) { - ckey = par + "."; - } - ckey += fld; - if (editors.containsKey(ckey)) { - GenericTagEditor editor = editors.get(ckey); - Object val = editor.getChangedValue(); - fields.put(fld, true); - if (val instanceof Boolean) { - fields.put(fld, (Boolean) val); - } - } - } - boolean ok = ev.eval(fields, tag.getId()); - if (conditionMet) { - conditionMet = ok; - } - ((Component) dependentEditor).setVisible(conditionMet); - dependentLabel.setVisible(conditionMet); - dependentTypeLabel.setVisible(conditionMet); - } catch (AnnotationParseException ex) { - logger.log(Level.SEVERE, "Invalid condition", ex); - } - } - if (!conditionMet) { - break; - } - } - } - genericTagPropertiesEditPanel.removeAll(); - genericTagPropertiesEditPanel.setSize(0, 0); - int propCount = 0; - for (String key : keys) { - - Component dependentEditor; - if (addKeys.contains(key)) { - dependentEditor = addButtons.get(key); - } else if (removeButtons.containsKey(key)) { //It's array/list, add remove button - JPanel editRemPanel = new JPanel(new BorderLayout()); - editRemPanel.add((Component) editors.get(key), BorderLayout.CENTER); - editRemPanel.add(removeButtons.get(key), BorderLayout.EAST); - dependentEditor = editRemPanel; - } else { - dependentEditor = (Component) editors.get(key); - } - Component dependentLabel = labels.get(key); - Component dependentTypeLabel = types.get(key); - if (dependentEditor.isVisible()) { - genericTagPropertiesEditPanel.add(dependentLabel); - genericTagPropertiesEditPanel.add(((Component) dependentEditor)); - genericTagPropertiesEditPanel.add(dependentTypeLabel); - propCount++; - } - } - /*genericTagPropertiesEditPanel.add(new JPanel()); - genericTagPropertiesEditPanel.add(new JPanel()); - genericTagPropertiesEditPanel.add(new JPanel());*/ - relayout(propCount /*+ 1*/); - } + //@Override + //public abstract void change(GenericTagEditor ed); } diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java index a27a3e4c0..ddd498a1c 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; +import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.gui.generictageditors.Amf3ValueEditor; import com.jpexs.decompiler.flash.gui.generictageditors.BinaryDataEditor; import com.jpexs.decompiler.flash.gui.generictageditors.BooleanEditor; @@ -119,16 +120,20 @@ public class GenericTagTreePanel extends GenericTagPanel { private static final int FIELD_INDEX = 0; + private List listeners = new ArrayList<>(); + public void addTreeModelListener(TreeModelListener listener) { - ((MyTreeModel)tree.getModel()).addTreeModelListener(listener); + listeners.add(listener); + ((DefaultTreeModel) tree.getModel()).addTreeModelListener(listener); } - + public void removeTreeModelListener(TreeModelListener listener) { - ((MyTreeModel)tree.getModel()).removeTreeModelListener(listener); + listeners.remove(listener); + ((DefaultTreeModel) tree.getModel()).removeTreeModelListener(listener); } - + private class MyTree extends JTree { - + public MyTree() { if (View.isOceanic()) { setBackground(Color.white); @@ -146,7 +151,7 @@ public class GenericTagTreePanel extends GenericTagPanel { } } - + private static SWFType evalSwfType(MyTreeModel mod, String parentPath, SWFType swfType) { if (swfType == null) { return null; @@ -157,7 +162,7 @@ public class GenericTagTreePanel extends GenericTagPanel { Conditional cond = new Conditional() { @Override public String[] value() { - return new String[] {swfType.alternateCondition()}; + return new String[]{swfType.alternateCondition()}; } @Override @@ -189,7 +194,7 @@ public class GenericTagTreePanel extends GenericTagPanel { public Class annotationType() { return Conditional.class; } - }; + }; ConditionEvaluator ev = new ConditionEvaluator(cond); try { Map fieldMap = new HashMap<>(); @@ -219,41 +224,41 @@ public class GenericTagTreePanel extends GenericTagPanel { return swfType; } return new SWFType() { - @Override - public BasicType value() { - return swfType.alternateValue(); - } + @Override + public BasicType value() { + return swfType.alternateValue(); + } - @Override - public BasicType alternateValue() { - return BasicType.NONE; - } + @Override + public BasicType alternateValue() { + return BasicType.NONE; + } - @Override - public String alternateCondition() { - return ""; - } + @Override + public String alternateCondition() { + return ""; + } - @Override - public int count() { - return swfType.count(); - } + @Override + public int count() { + return swfType.count(); + } - @Override - public String countField() { - return swfType.countField(); - } + @Override + public String countField() { + return swfType.countField(); + } - @Override - public int countAdd() { - return swfType.countAdd(); - } + @Override + public int countAdd() { + return swfType.countAdd(); + } - @Override - public Class annotationType() { - return SWFType.class; - } - }; + @Override + public Class annotationType() { + return SWFType.class; + } + }; } catch (AnnotationParseException | IllegalArgumentException | IllegalAccessException ex) { logger.log(Level.SEVERE, null, ex); return swfType; @@ -270,7 +275,7 @@ public class GenericTagTreePanel extends GenericTagPanel { public MyTreeCellEditor(JTree tree) { this.tree = tree; - } + } @Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { @@ -298,8 +303,8 @@ public class GenericTagTreePanel extends GenericTagPanel { } GenericTagEditor editor = null; SWFType swfType = field.getAnnotation(SWFType.class); - MyTreeModel model = (MyTreeModel)tree.getModel(); - + MyTreeModel model = (MyTreeModel) tree.getModel(); + SWFArray swfArray = field.getAnnotation(SWFArray.class); boolean isArray = ReflectionTools.needsIndex(field) || swfArray != null; boolean isArrayParent = isArray && index == -1; @@ -309,11 +314,11 @@ public class GenericTagTreePanel extends GenericTagPanel { if (isArray && !isArrayParent) { parentPath = parentPath.substring(0, parentPath.lastIndexOf(".")); } - + swfType = evalSwfType(model, parentPath, swfType); - + UUID uuid = field.getAnnotation(UUID.class); - + Multiline multiline = field.getAnnotation(Multiline.class); EnumValues enumValues = field.getAnnotation(EnumValues.class); if (uuid != null) { @@ -415,10 +420,10 @@ public class GenericTagTreePanel extends GenericTagPanel { return false; } Object obj = path.getLastPathComponent(); - + FieldNode fnode = (FieldNode) obj; Field field = fnode.fieldSet.get(FIELD_INDEX); - + boolean ret = super.isCellEditable(e) && tree.getModel().isLeaf(obj) && hasEditor(fnode.obj, field, fnode.index); return ret; @@ -426,11 +431,14 @@ public class GenericTagTreePanel extends GenericTagPanel { @Override public boolean stopCellEditing() { + boolean modified = false; if (editors != null) { for (GenericTagEditor editor : editors) { try { editor.validateValue(); - editor.save(); + if (editor.save()) { + modified = true; + } } catch (IllegalArgumentException iex) { return false; } @@ -440,15 +448,29 @@ public class GenericTagTreePanel extends GenericTagPanel { editors = null; - TreePath sp = tree.getSelectionPath(); - if (sp != null) { - ((MyTreeModel) tree.getModel()).vchanged(sp); + if (modified) { + TreePath sp = tree.getSelectionPath(); + if (sp != null) { + ((MyTreeModel) tree.getModel()).vchanged(sp); + } + refreshTree(); } - refreshTree(); return true; } } + + @Override + public boolean tryAutoSave() { + if (Configuration.autoSaveTagModifications.get()) { + if (tag == null) { + return true; + } + return save(); + } + return true; + } + public GenericTagTreePanel(MainPanel mainPanel) { super(mainPanel); setLayout(new BorderLayout()); @@ -659,20 +681,19 @@ public class GenericTagTreePanel extends GenericTagPanel { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { - Component component = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - if (component instanceof JLabel) { - JLabel lab = (JLabel) component; - if (value == tree.getModel().getRoot()) { - //It still does not matter since root is hidden - if (editedTag != null) { - lab.setIcon(AbstractTagTree.getIconForType(AbstractTagTree.getTreeNodeType(editedTag))); - } - } - } - return component; + Component component = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + if (component instanceof JLabel) { + JLabel lab = (JLabel) component; + if (value == tree.getModel().getRoot()) { + //It still does not matter since root is hidden + if (editedTag != null) { + lab.setIcon(AbstractTagTree.getIconForType(AbstractTagTree.getTreeNodeType(editedTag))); + } + } + } + return component; } - - + } @Override @@ -702,7 +723,7 @@ public class GenericTagTreePanel extends GenericTagPanel { private FieldSet fieldSet; private int index; - + private MyTreeModel model; public FieldNode(MyTreeModel model, Tag tag, Object obj, FieldSet fieldSet, int index) { @@ -746,7 +767,7 @@ public class GenericTagTreePanel extends GenericTagPanel { } return ret.toString(); } - + if (fieldSet.size() == 1) { ret.append(toString(0)); } else { @@ -766,11 +787,11 @@ public class GenericTagTreePanel extends GenericTagPanel { public String toString(int fieldIndex) { String valStr = ""; Field field = fieldSet.get(fieldIndex); - + if (field.getAnnotation(UUID.class) != null) { StringBuilder sb = new StringBuilder(); byte[] val = (byte[]) getValue(fieldIndex); - for(int i = 0; i < val.length; i++) { + for (int i = 0; i < val.length; i++) { String h = Integer.toHexString(val[i] & 0xff); if (h.length() == 1) { h = "0" + h; @@ -829,15 +850,14 @@ public class GenericTagTreePanel extends GenericTagPanel { Class declaredType = fieldSet.get(fieldIndex).getType(); boolean isArray = ReflectionTools.needsIndex(fieldSet.get(fieldIndex)) || swfArray != null; boolean isArrayParent = isArray && index == -1; - - SWFType swfType = fieldSet.get(fieldIndex).getAnnotation(SWFType.class); + + SWFType swfType = fieldSet.get(fieldIndex).getAnnotation(SWFType.class); String thisPath = model.getNodePathName(this); String parentPath = thisPath.substring(0, thisPath.lastIndexOf(".")); if (isArray && !isArrayParent) { parentPath = parentPath.substring(0, parentPath.lastIndexOf(".")); } swfType = evalSwfType(model, parentPath, swfType); - Class declaredSubType = isArray ? ReflectionTools.getFieldSubType(obj, fieldSet.get(fieldIndex)) : null; @@ -1016,7 +1036,7 @@ public class GenericTagTreePanel extends GenericTagPanel { } public String getNodePathName(Object find) { - + if (nodeCacheReverse.containsKey(find)) { return nodeCacheReverse.get(find); } @@ -1112,19 +1132,17 @@ public class GenericTagTreePanel extends GenericTagPanel { if (parent == mtroot) { return filterFields(this, mtroot.getClass().getSimpleName(), mtroot.getClass(), limited, mtroot.getId()).size(); } - + FieldNode fnode = (FieldNode) parent; - - + Field field = fnode.fieldSet.get(FIELD_INDEX); - + boolean isByteArray = field.getType().equals(byte[].class); - - + if (hasEditor(fnode.obj, field, fnode.index) || isByteArray) { return 0; - } - + } + if (ReflectionTools.needsIndex(field) && (fnode.index == -1)) { //Arrays or Lists try { if (field.get(fnode.obj) == null) { @@ -1137,8 +1155,8 @@ public class GenericTagTreePanel extends GenericTagPanel { return ReflectionTools.getFieldSubSize(fnode.obj, field); } - parent = fnode.getValue(FIELD_INDEX); - + parent = fnode.getValue(FIELD_INDEX); + return filterFields(this, getNodePathName(fnode), parent.getClass(), limited, mtroot.getId()).size(); } @@ -1184,7 +1202,7 @@ public class GenericTagTreePanel extends GenericTagPanel { } if (!edit && tree.isEditing()) { tree.stopEditing(); - } + } tree.setEditable(edit); refreshTree(); } @@ -1200,13 +1218,13 @@ public class GenericTagTreePanel extends GenericTagPanel { tag.setModified(true); tag.setSwf(swf); if (tag instanceof Timelined) { - ((Timelined)tag).resetTimeline(); + ((Timelined) tag).resetTimeline(); } //For example DefineButton and its DefineButtonCxForm if ((tag instanceof CharacterIdTag) && (!(tag instanceof CharacterTag))) { - CharacterTag parentCharacter = swf.getCharacter(((CharacterIdTag)tag).getCharacterId()); + CharacterTag parentCharacter = swf.getCharacter(((CharacterIdTag) tag).getCharacterId()); if (parentCharacter instanceof Timelined) { - ((Timelined)parentCharacter).resetTimeline(); + ((Timelined) parentCharacter).resetTimeline(); } } swf.computeDependentCharacters(); @@ -1249,9 +1267,9 @@ public class GenericTagTreePanel extends GenericTagPanel { type = val.getClass(); } catch (IllegalArgumentException | IllegalAccessException ex) { return false; - } + } UUID uuid = field.getAnnotation(UUID.class); - + if (uuid != null) { return true; } else if (type.equals(int.class) || type.equals(Integer.class) @@ -1475,7 +1493,7 @@ public class GenericTagTreePanel extends GenericTagPanel { if ((obj instanceof CLIPACTIONS) && (v instanceof CLIPACTIONRECORD)) { ((CLIPACTIONRECORD) v).setParentClipActions((CLIPACTIONS) obj); } - + if (obj instanceof HasSwfAndTag) { ((HasSwfAndTag) obj).setSourceTag(editedTag); } @@ -1484,11 +1502,15 @@ public class GenericTagTreePanel extends GenericTagPanel { //ignore } } + ((MyTreeModel) tree.getModel()).vchanged(new TreePath(tree.getModel().getRoot())); refreshTree(); } public void refreshTree() { View.refreshTree(tree, getModel()); + for (TreeModelListener listener:listeners) { + ((DefaultTreeModel) tree.getModel()).addTreeModelListener(listener); + } revalidate(); repaint(); } diff --git a/src/com/jpexs/decompiler/flash/gui/HeaderInfoPanel.java b/src/com/jpexs/decompiler/flash/gui/HeaderInfoPanel.java index d2de30aa9..af660f5e0 100644 --- a/src/com/jpexs/decompiler/flash/gui/HeaderInfoPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/HeaderInfoPanel.java @@ -18,12 +18,15 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFCompression; +import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.gui.helpers.TableLayoutHelper; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; @@ -34,6 +37,7 @@ import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.border.BevelBorder; import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import layout.TableLayout; /** @@ -190,14 +194,24 @@ public class HeaderInfoPanel extends JPanel implements TagEditorPanel { add(propertiesPanel, BorderLayout.CENTER); - editButton.setVisible(false); editButton.addActionListener(this::editButtonActionPerformed); - saveButton.setVisible(false); saveButton.addActionListener(this::saveButtonActionPerformed); - cancelButton.setVisible(false); cancelButton.addActionListener(this::cancelButtonActionPerformed); + + if (Configuration.editorMode.get()) { + editButton.setVisible(false); + saveButton.setVisible(false); + saveButton.setEnabled(false); + cancelButton.setVisible(false); + cancelButton.setEnabled(false); + } else { + editButton.setVisible(false); + saveButton.setVisible(false); + cancelButton.setVisible(false); + } + buttonsPanel.setLayout(new FlowLayout()); buttonsPanel.setBorder(new BevelBorder(BevelBorder.RAISED)); @@ -294,7 +308,27 @@ public class HeaderInfoPanel extends JPanel implements TagEditorPanel { yMinEditor.setModel(new SpinnerNumberModel(swf.displayRect.Ymin, -0x80000000, 0x7fffffff, 1)); yMaxEditor.setModel(new SpinnerNumberModel(swf.displayRect.Ymax, -0x80000000, 0x7fffffff, 1)); - setEditMode(false); + compressionComboBox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + setModified(); + } + }); + versionEditor.addChangeListener((ChangeEvent e) -> {setModified();}); + gfxCheckBox.addChangeListener((ChangeEvent e) -> {setModified();}); + frameRateEditor.addChangeListener((ChangeEvent e) -> {setModified();}); + xMinEditor.addChangeListener((ChangeEvent e) -> {setModified();}); + xMaxEditor.addChangeListener((ChangeEvent e) -> {setModified();}); + yMinEditor.addChangeListener((ChangeEvent e) -> {setModified();}); + yMaxEditor.addChangeListener((ChangeEvent e) -> {setModified();}); + + setEditMode(Configuration.editorMode.get()); + } + + private void setModified() { + saveButton.setEnabled(true); + cancelButton.setEnabled(true); + mainPanel.setEditingStatus(); } public void clear() { @@ -311,6 +345,10 @@ public class HeaderInfoPanel extends JPanel implements TagEditorPanel { } private void setEditMode(boolean edit) { + + if (Configuration.editorMode.get()) { + edit = true; + } compressionLabel.setVisible(!edit); compressionEditorPanel.setVisible(edit); versionLabel.setVisible(!edit); @@ -326,9 +364,17 @@ public class HeaderInfoPanel extends JPanel implements TagEditorPanel { warningPanel.setVisible(false); - editButton.setVisible(!edit); - saveButton.setVisible(edit); - cancelButton.setVisible(edit); + if (Configuration.editorMode.get()) { + editButton.setVisible(false); + saveButton.setVisible(true); + saveButton.setEnabled(false); + cancelButton.setVisible(true); + cancelButton.setEnabled(false); + } else { + editButton.setVisible(!edit); + saveButton.setVisible(edit); + cancelButton.setVisible(edit); + } } private boolean validateHeader() { @@ -362,13 +408,15 @@ public class HeaderInfoPanel extends JPanel implements TagEditorPanel { @Override public boolean tryAutoSave() { - // todo: implement - return false; + if (saveButton.isVisible() && saveButton.isEnabled()) { + saveButtonActionPerformed(null); + } + return !(saveButton.isVisible() && saveButton.isEnabled()); } @Override public boolean isEditing() { - return saveButton.isVisible(); + return saveButton.isVisible() && saveButton.isEnabled(); } public void startEdit() { diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index d5b7d0427..89970162c 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -846,7 +846,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } public void setEditingStatus() { - statusPanel.setStatus(translate("status.editing")); + statusPanel.setStatus(translate(Configuration.autoSaveTagModifications.get() ? "status.editing.autosave" : "status.editing")); } public void clearEditingStatus() { diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index e3903cc59..c2858bac8 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -63,6 +63,8 @@ import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -422,7 +424,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel genericSaveButton.setVisible(true); genericSaveButton.setEnabled(false); genericCancelButton.setVisible(true); - genericSaveButton.setEnabled(false); + genericCancelButton.setEnabled(false); } else { genericEditButton.setVisible(true); genericSaveButton.setVisible(false); @@ -524,7 +526,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel mainPanel.setTagTreeSelectedNode(mainPanel.getCurrentTree(), placeObject); } } - }); + }); imagePanel.setLoop(Configuration.loopMedia.get()); @@ -687,6 +689,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel genericTagPanel = new GenericTagTreePanel(mainPanel); genericTagCard.add(genericTagPanel, BorderLayout.CENTER); genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); + addGenericListener(); return genericTagCard; } @@ -708,6 +711,16 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } }); + if (Configuration.editorMode.get()) { + placeImagePanel.addBoundsChangeListener(new BoundsChangeListener() { + @Override + public void boundsChanged(Rectangle2D newBounds, Point2D registrationPoint, RegistrationPointPosition registrationPointPosition) { + if (placeSaveButton.isVisible()) { + placeSaveButton.setEnabled(true); + } + } + }); + } placeTransformPanel = new TransformPanel(placeImagePanel); //imagePanel.setLoop(Configuration.loopMedia.get()); previewCnt.add(placeTransformSplitPane = new JPersistentSplitPane( @@ -727,6 +740,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel previewPanel.add(prevIntLabel, BorderLayout.NORTH); placeGenericPanel = new GenericTagTreePanel(mainPanel); + addPlaceGenericListener(); placeSplitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, previewPanel, placeGenericPanel, Configuration.guiSplitPanePlaceDividerLocationPercent); placeTagCard.add(placeSplitPane, BorderLayout.CENTER); @@ -1058,7 +1072,6 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel genericEditButton.setEnabled(true); if (Configuration.editorMode.get()) { genericTagPanel.setEditMode(!tag.isReadOnly(), tag); - addGenericListener(); genericSaveButton.setVisible(!tag.isReadOnly()); genericCancelButton.setVisible(!tag.isReadOnly()); } else { @@ -1079,7 +1092,6 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (Configuration.editorMode.get()) { placeGenericPanel.setEditMode(!tag.isReadOnly(), tag); - addPlaceGenericListener(); placeEditButton.setVisible(false); placeSaveButton.setVisible(!tag.isReadOnly()); placeCancelButton.setVisible(!tag.isReadOnly()); @@ -1292,7 +1304,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } } - private void saveGenericTagButtonActionPerformed(ActionEvent evt) { + private void saveGenericTag(boolean refreshTree) { if (genericTagPanel.save()) { Tag tag = genericTagPanel.getTag(); SWF swf = tag.getSwf(); @@ -1302,7 +1314,9 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel tag.getTimelined().resetTimeline(); swf.assignClassesToSymbols(); swf.assignExportNamesToSymbols(); - mainPanel.refreshTree(swf); + if (refreshTree) { + mainPanel.refreshTree(swf); + } if (Configuration.editorMode.get()) { genericEditButton.setVisible(false); genericSaveButton.setVisible(true); @@ -1319,26 +1333,29 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel mainPanel.clearEditingStatus(); } } + + private void saveGenericTagButtonActionPerformed(ActionEvent evt) { + saveGenericTag(true); + } private void cancelGenericTagButtonActionPerformed(ActionEvent evt) { if (Configuration.editorMode.get()) { + genericTagPanel.setEditMode(true, null); genericEditButton.setVisible(false); genericSaveButton.setVisible(true); genericSaveButton.setEnabled(false); genericCancelButton.setVisible(true); - genericCancelButton.setEnabled(false); - genericTagPanel.setEditMode(true, null); - addGenericListener(); + genericCancelButton.setEnabled(false); } else { + genericTagPanel.setEditMode(false, null); genericEditButton.setVisible(true); genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); + genericCancelButton.setVisible(false); } mainPanel.clearEditingStatus(); } - private void savePlaceTagButtonActionPerformed(ActionEvent evt) { + private void savePlaceTag(boolean refreshTree) { if (placeEditMode == PLACE_EDIT_TRANSFORM) { Matrix matrix = placeImagePanel.getNewMatrix(); placeTag.setPlaceFlagHasMatrix(true); @@ -1348,7 +1365,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel placeImagePanel.freeTransformDepth(-1); placeTag.getTimelined().resetTimeline(); placeTransformScrollPane.setVisible(false); - placeGenericPanel.setVisible(true); + placeGenericPanel.setVisible(true); } Tag hilightTag = null; if (placeEditMode == PLACE_EDIT_RAW) { @@ -1356,7 +1373,9 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel Tag tag = placeGenericPanel.getTag(); SWF swf = tag.getSwf(); tag.getTimelined().resetTimeline(); - mainPanel.refreshTree(swf); + if (refreshTree) { + mainPanel.refreshTree(swf); + } hilightTag = tag; } placeGenericPanel.setEditMode(false, null); @@ -1385,6 +1404,10 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } } + private void savePlaceTagButtonActionPerformed(ActionEvent evt) { + savePlaceTag(true); + } + private void editPlaceTagButtonActionPerformed(ActionEvent evt) { placeEditMode = PLACE_EDIT_RAW; placeGenericPanel.setEditMode(true, placeTag); @@ -1410,7 +1433,12 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel placeTransformButton.setVisible(false); placeSaveButton.setVisible(true); placeCancelButton.setVisible(true); - placeSaveButton.setEnabled(true); + + if (Configuration.editorMode.get()) { + placeSaveButton.setEnabled(false); + } else { + placeSaveButton.setEnabled(true); + } placeCancelButton.setEnabled(true); mainPanel.setEditingStatus(); @@ -1430,7 +1458,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel }, 40); //add some delay before controls are hidden } - private void saveImageTransformButtonActionPerformed(ActionEvent evt) { + private void saveImageTransform(boolean refreshTree) { Matrix matrix = imagePanel.getNewMatrix(); imageTransformScrollPane.setVisible(false); @@ -1470,7 +1498,13 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } mainPanel.clearEditingStatus(); - mainPanel.reload(true); + if (refreshTree) { + mainPanel.reload(true); + } + } + + private void saveImageTransformButtonActionPerformed(ActionEvent evt) { + saveImageTransform(true); } private void cancelImageTransformButtonActionPerformed(ActionEvent evt) { @@ -1615,8 +1649,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (Configuration.editorMode.get()) { if (placeEditMode == PLACE_EDIT_RAW) { - placeGenericPanel.setEditMode(true, null); - addPlaceGenericListener(); + placeGenericPanel.setEditMode(true, null); } placeEditButton.setVisible(false); placeSaveButton.setVisible(true); @@ -1634,7 +1667,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel mainPanel.clearEditingStatus(); placeTransformButton.setVisible(true); - + if (placeEditMode == PLACE_EDIT_TRANSFORM) { placeEditMode = PLACE_EDIT_RAW; } @@ -1660,8 +1693,31 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel @Override public boolean tryAutoSave() { - // todo: implement - return textPanel.tryAutoSave() && false; + boolean ok = true; + + if (imageTransformSaveButton.isVisible() && imageTransformSaveButton.isEnabled() && Configuration.autoSaveTagModifications.get()) { + saveImageTransform(false); + ok = ok && !(imageTransformSaveButton.isVisible() && imageTransformSaveButton.isEnabled()); + } + + if (placeSaveButton.isVisible() && placeSaveButton.isEnabled() && Configuration.autoSaveTagModifications.get()) { + savePlaceTag(false); + ok = ok && !(placeSaveButton.isVisible() && placeSaveButton.isEnabled()); + } + if (genericSaveButton.isVisible() && genericSaveButton.isEnabled()) { + saveGenericTag(false); + ok = ok && !(genericSaveButton.isVisible() && genericSaveButton.isEnabled()); + } + if (metadataSaveButton.isVisible() && metadataSaveButton.isEnabled() && Configuration.autoSaveTagModifications.get()) { + saveMetadataButtonActionPerformed(null); + ok = ok && !(metadataSaveButton.isVisible() && metadataSaveButton.isEnabled()); + } + if (fontPanel.isEditing() && Configuration.autoSaveTagModifications.get()) { + ok = ok && fontPanel.tryAutoSave(); + } + ok = ok && textPanel.tryAutoSave(); + + return ok; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java index 135f2f657..aca33fb43 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java @@ -38,6 +38,7 @@ import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.io.IOException; import java.lang.reflect.Field; +import java.util.Objects; import java.util.Timer; import javax.swing.JButton; import javax.swing.JLabel; @@ -175,14 +176,19 @@ public class Amf3ValueEditor extends JPanel implements GenericTagEditor, FullSiz } @Override - public void save() { + public boolean save() { try { - Object val = getChangedValue(); - ReflectionTools.setValue(obj, field, index, val); - value = (Amf3Value) val; + Object oldValue = (Amf3Value) ReflectionTools.getValue(obj, field, index); + Object newValue = getChangedValue(); + if (Objects.equals(oldValue, newValue)) { + return false; + } + ReflectionTools.setValue(obj, field, index, newValue); + value = (Amf3Value) newValue; } catch (IllegalAccessException ex) { //ignore } + return true; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java index eb656d0ee..1fb3240d5 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java @@ -26,6 +26,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.lang.reflect.Field; +import java.util.Objects; import javax.swing.JButton; /** @@ -99,12 +100,18 @@ public class BinaryDataEditor extends JButton implements GenericTagEditor { } @Override - public void save() { + public boolean save() { try { - ReflectionTools.setValue(obj, field, index, value); + Object oldValue = ReflectionTools.getValue(obj, field, index); + Object newValue = value; + if (Objects.equals(oldValue, newValue)) { + return false; + } + ReflectionTools.setValue(obj, field, index, newValue); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } + return true; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java index 3b373bc9a..33ba50a43 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java @@ -67,12 +67,20 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { } @Override - public void save() { + public boolean save() { try { + boolean oldValue = (boolean) ReflectionTools.getValue(obj, field, index); + boolean newValue = isSelected(); + + if (oldValue == newValue) { + return false; + } + ReflectionTools.setValue(obj, field, index, isSelected()); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } + return true; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java index fddbddc62..91cec033d 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java @@ -35,6 +35,7 @@ import java.awt.event.FocusEvent; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Objects; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JColorChooser; @@ -145,13 +146,19 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe } @Override - public void save() { - Object val = getChangedValue(); + public boolean save() { try { - ReflectionTools.setValue(obj, field, index, val); - } catch (IllegalAccessException ex) { + Object oldValue = ReflectionTools.getValue(obj, field, index); + Object newValue = getChangedValue(); + if (Objects.equals(oldValue, newValue)) { + return false; + } + + ReflectionTools.setValue(obj, field, index, newValue); + } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } + return true; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/EnumEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/EnumEditor.java index 1be8df94d..c5efdbf21 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/EnumEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/EnumEditor.java @@ -26,6 +26,7 @@ import java.awt.event.FocusEvent; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Map; +import java.util.Objects; import javax.swing.JComboBox; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; @@ -103,104 +104,22 @@ public class EnumEditor extends JComboBox> implements Gene } @Override - public void save() { + public boolean save() { try { - Object value = getChangedValue(); - if (value != null) { - ReflectionTools.setValue(obj, field, index, value); + Integer oldValue = (Integer) ReflectionTools.getValue(obj, field, index); + Integer newValue = (Integer)getChangedValue(); + if (newValue == null) { + return false; } + if (Objects.equals(oldValue, newValue)) { + return false; + } + ReflectionTools.setValue(obj, field, index, newValue); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } - } - - private SpinnerModel getModel(SWFType swfType, Object value) { - SpinnerNumberModel m = null; - BasicType basicType = swfType == null ? BasicType.NONE : swfType.value(); - switch (basicType) { - case UI8: - m = new SpinnerNumberModel(toInt(value), 0, 0xff, 1); - break; - case UI16: - m = new SpinnerNumberModel(toInt(value), 0, 0xffff, 1); - break; - case UB: { - long max = 1; - if (swfType.count() > 0) { - max <<= swfType.count(); - } else { - max <<= 31; - } - m = new SpinnerNumberModel((Number) toLong(value), 0L, (long) max - 1, 1L); - } - break; - case UI32: - case EncodedU32: - case NONE: - m = new SpinnerNumberModel((Number) toLong(value), 0L, 0xffffffffL, 1L); - break; - case SI8: - m = new SpinnerNumberModel(toInt(value), -0x80, 0x7f, 1); - break; - case SI16: - case FLOAT16: - m = new SpinnerNumberModel(toInt(value), -0x8000, 0x7fff, 1); - break; - case FB: - case SB: { - long max = 1; - if (swfType.count() > 0) { - max <<= (swfType.count() - 1); - } else { - max <<= 30; - } - m = new SpinnerNumberModel((Number) toLong(value), (long) (-max), (long) max - 1, 1L); - } - break; - case SI32: - m = new SpinnerNumberModel(toDouble(value), -0x80000000, 0x7fffffff, 1); - break; - case FLOAT: - case FIXED: - case FIXED8: - m = new SpinnerNumberModel(toDouble(value), -0x80000000, 0x7fffffff, 0.01); - break; - } - return m; - } - - private double toDouble(Object value) { - if (value instanceof Float) { - return (double) (Float) value; - } - if (value instanceof Double) { - return (double) (Double) value; - } - return 0; - } - - private int toInt(Object value) { - if (value instanceof Short) { - return (int) (Short) value; - } - if (value instanceof Integer) { - return (int) (Integer) value; - } - return 0; - } - - private long toLong(Object value) { - if (value instanceof Short) { - return (long) (Short) value; - } - if (value instanceof Integer) { - return (long) (Integer) value; - } - if (value instanceof Long) { - return (long) (Long) value; - } - return 0; - } + return true; + } @Override public void addChangeListener(final ChangeListener l) { diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java index 118e56914..341e82802 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java @@ -28,7 +28,7 @@ public interface GenericTagEditor { public void reset(); - public void save(); + public boolean save(); public void addChangeListener(ChangeListener l); diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java index 9bd062c95..c26af4cae 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java @@ -23,6 +23,7 @@ import java.awt.Component; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.lang.reflect.Field; +import java.util.Objects; import javax.swing.JFormattedTextField; import javax.swing.JSpinner; import javax.swing.SpinnerModel; @@ -91,15 +92,21 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { } @Override - public void save() { + public boolean save() { try { - Object value = getChangedValue(); - if (value != null) { - ReflectionTools.setValue(obj, field, index, value); + Object oldValue = ReflectionTools.getValue(obj, field, index); + Object newValue = getChangedValue(); + if (newValue == null) { + return false; } + if (Objects.equals(oldValue, newValue)) { + return false; + } + ReflectionTools.setValue(obj, field, index, newValue); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } + return true; } private SpinnerModel getModel(SWFType swfType, Object value) { diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java index d3201d9fc..39b8a2c49 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java @@ -23,6 +23,7 @@ import java.awt.Dimension; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.lang.reflect.Field; +import java.util.Objects; import javax.swing.JTextArea; /** @@ -91,12 +92,18 @@ public class StringEditor extends JTextArea implements GenericTagEditor { } @Override - public void save() { + public boolean save() { try { + String oldValue = (String) ReflectionTools.getValue(obj, field, index); + String newValue = getText(); + if (Objects.equals(oldValue, newValue)) { + return false; + } ReflectionTools.setValue(obj, field, index, getText()); - } catch (IllegalAccessException ex) { + } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } + return true; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/UUIDEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/UUIDEditor.java index f2e3b7e24..dbf5e0cbc 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/UUIDEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/UUIDEditor.java @@ -23,6 +23,7 @@ import java.awt.Dimension; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.lang.reflect.Field; +import java.util.Objects; import javax.swing.JTextArea; import javax.swing.JTextField; @@ -65,7 +66,7 @@ public class UUIDEditor extends JTextField implements GenericTagEditor { } public UUIDEditor(String fieldName, Object obj, Field field, int index, Class type) { - super(32+4); + super(32 + 4); this.obj = obj; this.field = field; this.index = index; @@ -79,7 +80,7 @@ public class UUIDEditor extends JTextField implements GenericTagEditor { try { byte[] val = (byte[]) ReflectionTools.getValue(obj, field, index); StringBuilder sb = new StringBuilder(); - for(int i = 0; i < val.length; i++) { + for (int i = 0; i < val.length; i++) { String h = Integer.toHexString(val[i] & 0xff); if (h.length() == 1) { h = "0" + h; @@ -96,22 +97,28 @@ public class UUIDEditor extends JTextField implements GenericTagEditor { } @Override - public void save() { + public boolean save() { try { + byte[] oldValue = (byte[]) ReflectionTools.getValue(obj, field, index); + String text = getText(); text = text.replace("-", "").trim(); if (!text.matches("[a-fA-F0-9]{32}")) { - return; + return false; } - byte[] val = new byte[16]; + byte[] newValue = new byte[16]; for (int i = 0; i < 16; i++) { String ch = text.substring(i * 2, i * 2 + 2); - val[i] = (byte) Integer.parseInt(ch, 16); + newValue[i] = (byte) Integer.parseInt(ch, 16); } - ReflectionTools.setValue(obj, field, index, val); - } catch (IllegalAccessException ex) { + if (Objects.equals(oldValue, newValue)) { + return false; + } + ReflectionTools.setValue(obj, field, index, newValue); + } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } + return true; } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 4dfbd14c7..5d88a533b 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1080,4 +1080,6 @@ message.info.importImages2 = During importing images, you need to select a FOLDE transform.clipboard = Clipboard transform.clipboard.copy = Copy matrix to clipboard -transform.clipboard.paste = Paste matrix from clipboard \ No newline at end of file +transform.clipboard.paste = Paste matrix from clipboard + +status.editing.autosave = You are in the EDIT mode. Make changes, then press Save button. Or discard changes with Cancel button. If you switch to other tag, current editation will be automatically saved. diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 04941ab9c..cc4cffa9b 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1065,4 +1065,6 @@ message.info.importImages2 = B\u011bhem importu text\u016f mus\u00edte vybrat SL transform.clipboard = Schr\u00e1nka transform.clipboard.copy = Kop\u00edrovat matici do schr\u00e1nky -transform.clipboard.paste = Vlo\u017eit matici ze schr\u00e1nky \ No newline at end of file +transform.clipboard.paste = Vlo\u017eit matici ze schr\u00e1nky + +status.editing.autosave = Jste v EDITA\u010cN\u00cdM re\u017eimu. Prove\u010fte zm\u011bny a stiskn\u011bte tla\u010d\u00edtko Ulo\u017eit. Nebo zru\u0161te zm\u011bny tla\u010d\u00edtkem Storno. Pokud p\u0159epnete na jin\u00fd tag, aktu\u00e1ln\u00ed editace bude automaticky ulo\u017eena.