From 3133d7b6d04f912e38b6e2990df3fb07fca1b3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 10 May 2025 13:13:17 +0200 Subject: [PATCH] Simple editor - filters (editing only existing, without gradients) --- CHANGELOG.md | 2 +- .../flash/tags/PlaceObject3Tag.java | 16 +- .../flash/tags/PlaceObject4Tag.java | 15 + .../flash/tags/base/PlaceObjectTypeTag.java | 11 + .../flash/easygui/FiltersTreeTable.java | 434 +++++++++++++++--- .../panels/InstancePropertiesPanel.java | 38 +- .../decompiler/flash/gui/ImagePanel.java | 6 +- .../flash/gui/MainFrameClassicMenu.java | 3 + .../generictageditors/Amf3ValueEditor.java | 5 + .../generictageditors/BinaryDataEditor.java | 5 + .../gui/generictageditors/BooleanEditor.java | 30 +- .../gui/generictageditors/ColorEditor.java | 37 +- .../gui/generictageditors/EnumEditor.java | 5 + .../gui/generictageditors/FloatEditor.java | 26 +- .../generictageditors/GenericTagEditor.java | 2 + .../gui/generictageditors/NumberEditor.java | 57 ++- .../gui/generictageditors/StringEditor.java | 5 + .../gui/generictageditors/UUIDEditor.java | 5 + .../generictageditors/ValueNormalizer.java | 27 ++ .../flash/gui/locales/EasyPanel.properties | 8 +- src/de/javagl/treetable/JTreeTable.java | 2 +- 21 files changed, 605 insertions(+), 134 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/generictageditors/ValueNormalizer.java diff --git a/CHANGELOG.md b/CHANGELOG.md index e06d8ab23..51e04f8c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ All notable changes to this project will be documented in this file. - Option to enter custom zoom level by clicking on zoom percentage label - Show in Simple editor context menu item for timelined items (sprites, buttons, swfs) - Simple editor - change background color -- Simple editor - filters (read only yet) +- Simple editor - filters (editing only existing, without gradients) ### Fixed - [#2424] DefineEditText handling of letterSpacing, font size on incorrect values diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java index 21e5734d0..5bcd036e0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java @@ -727,22 +727,36 @@ public class PlaceObject3Tag extends PlaceObjectTypeTag implements ASMSourceCont this.placeFlagHasBlendMode = value; } + @Override public void setBitmapCache(int value) { this.bitmapCache = value; this.placeFlagHasCacheAsBitmap = true; } + @Override public void setPlaceFlagHasCacheAsBitmap(boolean value) { this.placeFlagHasCacheAsBitmap = value; } + @Override public void setBackgroundColor(RGBA value) { this.backgroundColor = value; this.placeFlagOpaqueBackground = true; } + @Override public void setPlaceFlagOpaqueBackground(boolean value) { this.placeFlagOpaqueBackground = value; } - + + @Override + public void setPlaceFlagHasFilterList(boolean placeFlagHasFilterList) { + this.placeFlagHasFilterList = placeFlagHasFilterList; + } + + @Override + public void setFilters(List filters) { + this.surfaceFilterList = new ArrayList<>(filters); + this.placeFlagHasFilterList = true; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index 6a4b40ca6..475dfbc3d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -748,21 +748,36 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont this.placeFlagHasBlendMode = value; } + @Override public void setBitmapCache(int value) { this.bitmapCache = value; this.placeFlagHasCacheAsBitmap = true; } + @Override public void setPlaceFlagHasCacheAsBitmap(boolean value) { this.placeFlagHasCacheAsBitmap = value; } + @Override public void setBackgroundColor(RGBA value) { this.backgroundColor = value; this.placeFlagOpaqueBackground = true; } + @Override public void setPlaceFlagOpaqueBackground(boolean value) { this.placeFlagOpaqueBackground = value; } + + @Override + public void setPlaceFlagHasFilterList(boolean placeFlagHasFilterList) { + this.placeFlagHasFilterList = placeFlagHasFilterList; + } + + @Override + public void setFilters(List filters) { + this.surfaceFilterList = new ArrayList<>(filters); + this.placeFlagHasFilterList = true; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java index ca847c75c..59e9343fc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java @@ -247,6 +247,14 @@ public abstract class PlaceObjectTypeTag extends Tag implements CharacterIdTag, throw new UnsupportedOperationException(); } + public void setPlaceFlagHasFilterList(boolean value) { + throw new UnsupportedOperationException(); + } + + public void setFilters(List filters) { + throw new UnsupportedOperationException(); + } + /** * Checks if place equals. * @param other Other place object type tag @@ -292,6 +300,9 @@ public abstract class PlaceObjectTypeTag extends Tag implements CharacterIdTag, if (!Objects.equals(getAmfData(), other.getAmfData())) { //? return false; } + if (!Objects.equals(getFilters(), other.getFilters())) { + return false; + } return true; } diff --git a/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java b/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java index 4e6648069..659063bba 100644 --- a/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java +++ b/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java @@ -17,31 +17,58 @@ package com.jpexs.decompiler.flash.easygui; import com.jpexs.decompiler.flash.ecma.EcmaNumberToString; +import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.generictageditors.BooleanEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.ChangeListener; +import com.jpexs.decompiler.flash.gui.generictageditors.ColorEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.FloatEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.GenericTagEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.NumberEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.ValueNormalizer; import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.filters.FILTER; import de.javagl.treetable.JTreeTable; import de.javagl.treetable.TreeTableModel; +import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.EventObject; import java.util.List; +import java.util.Objects; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.JTable; +import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.ListSelectionModel; -import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.BevelBorder; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; import javax.swing.event.TreeModelListener; import javax.swing.plaf.basic.BasicTableUI; -import javax.swing.table.TableColumn; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellEditor; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; /** @@ -50,6 +77,8 @@ import javax.swing.tree.TreePath; */ public class FiltersTreeTable extends JTreeTable { + private List filterChangedListeners = new ArrayList<>(); + public FiltersTreeTable() { super(new FiltersTreeTableModel(null)); getTree().setCellRenderer(new FiltersTreeCellRenderer()); @@ -106,7 +135,7 @@ public class FiltersTreeTable extends JTreeTable { getTree().setBackground(Color.WHITE); } - addMouseListener(new MouseAdapter() { + /*addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) { @@ -119,18 +148,278 @@ public class FiltersTreeTable extends JTreeTable { } } - }); - + });*/ getTableHeader().setReorderingAllowed(false); + /*getTree().addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (isEditing()) { + getCellEditor().stopCellEditing(); + } + } + });*/ + } + + public void addFilterChangedListener(ActionListener l) { + filterChangedListeners.add(l); + } + + public void removeFilterChangedListener(ActionListener l) { + filterChangedListeners.remove(l); + } + + private void fireFilterChanged() { + List listeners2 = new ArrayList<>(filterChangedListeners); + for (ActionListener l : listeners2) { + l.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "filterChanged")); + } + } + + public List getFilters() { + TreeModel model = getTree().getModel(); + if (model instanceof FiltersTreeTableModel) { + FiltersTreeTableModel treeTableModel = (FiltersTreeTableModel) model; + return treeTableModel.filters; + } + return new ArrayList<>(); } public void setFilters(List filters) { + if (Objects.equals(getFilters(), filters)) { + return; + } setTreeTableModel(new FiltersTreeTableModel(filters)); getColumnModel().getColumn(0).setMinWidth(200); getColumnModel().getColumn(0).setWidth(200); getColumnModel().getColumn(0).setPreferredWidth(200); getColumnModel().getColumn(1).setPreferredWidth(Integer.MAX_VALUE); + + getColumnModel().getColumn(1).setCellEditor(new FiltersValueCellEditor(this)); + getColumnModel().getColumn(1).setCellRenderer(new FiltersTableCellRenderer()); + + TreeModel ttm = getTree().getModel(); + Object root = ttm.getRoot(); + int childCount = ttm.getChildCount(root); + for (int i = 0; i < childCount; i++) { + getTree().expandPath(new TreePath(new Object[]{root, ttm.getChild(root, i)})); + } + } + + private static class FiltersValueCellEditor implements TableCellEditor { + + private Object value; + private GenericTagEditor editor; + private List listeners = new ArrayList<>(); + private final FiltersTreeTable filtersTable; + + public FiltersValueCellEditor(FiltersTreeTable filtersTable) { + this.filtersTable = filtersTable; + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + if (value == null) { + return null; + } + + if (!(value instanceof FilterValue)) { + return null; + } + + FilterValue filterValue = (FilterValue) value; + Object realValue = filterValue.getValue(); + this.value = value; + + FilterField filterField = filterValue.filterField; + + if (editor != null) { + //((Component) editor).setVisible(false); + } + editor = null; + if (realValue.getClass() == Boolean.class) { + editor = new BooleanEditor(filterField.toString(), filterField.filter, filterField.field, -1, Boolean.class); + /*editor.addChangeListener(new ChangeListener() { + @Override + public void change(GenericTagEditor editor) { + editor.save(); + } + });*/ + //((BooleanEditor) editor).setRequestFocusEnabled(false); + } else if (realValue.getClass() == Double.class || realValue.getClass() == Float.class) { + editor = new FloatEditor(filterField.toString(), filterField.filter, filterField.field, -1, realValue.getClass()); + //editor = new NumberEditor(filterField.toString(), filterField.filter, filterField.field, -1, realValue.getClass(), filterField.field.getAnnotation(SWFType.class)); + if ("angle".equals(filterField.field.getName())) { + editor.setValueNormalizer(new ValueNormalizer() { + @Override + public Object toFieldValue(Object viewValue) { + return Math.toRadians((double) viewValue); + } + + @Override + public Object toViewValue(Object fieldValue) { + return Math.round(Math.toDegrees((double) fieldValue) * 100) / 100.0; + } + }); + } + } else if (realValue.getClass() == int.class || realValue.getClass() == Integer.class) { + editor = new NumberEditor(filterField.toString(), filterField.filter, filterField.field, -1, realValue.getClass(), filterField.field.getAnnotation(SWFType.class)); + } else if (realValue.getClass() == RGBA.class) { + editor = new ColorEditor(filterField.toString(), filterField.filter, filterField.field, -1, RGBA.class); + } + + if (editor != null) { + + editor.addChangeListener(new ChangeListener() { + @Override + public void change(GenericTagEditor editor) { + editor.save(); + filtersTable.fireFilterChanged(); + } + }); + + if (table instanceof JTreeTable) { + JTreeTable treeTable = (JTreeTable) table; + if (treeTable.isRowSelected(row)) { + ((JComponent) editor).setForeground(new Color(UIManager.getColor("Table.selectionForeground").getRGB(), true)); + ((JComponent) editor).setBackground(new Color(UIManager.getColor("Table.selectionBackground").getRGB(), true)); + ((JComponent) editor).setOpaque(true); + } else { + ((JComponent) editor).setOpaque(false); + } + } + } + + return (Component) editor; + } + + @Override + public Object getCellEditorValue() { + return value; + } + + @Override + public boolean isCellEditable(EventObject anEvent) { + return true; + } + + @Override + public boolean shouldSelectCell(EventObject anEvent) { + if (value == null) { + return true; + } + + if (!(value instanceof FilterValue)) { + return false; + } + + FilterValue filterValue = (FilterValue) value; + + Object realValue = filterValue.getValue(); + + if (realValue.getClass() == Double.class || realValue.getClass() == Float.class) { + return true; + } + if (realValue.getClass() == Boolean.class) { + return false; + } + return false; + } + + @Override + public boolean stopCellEditing() { + editor.save(); + List listeners2 = new ArrayList<>(listeners); + for (CellEditorListener l : listeners2) { + l.editingStopped(new ChangeEvent(editor)); + } + return true; + } + + @Override + public void cancelCellEditing() { + List listeners2 = new ArrayList<>(listeners); + for (CellEditorListener l : listeners2) { + l.editingCanceled(new ChangeEvent(editor)); + } + editor.reset(); + } + + @Override + public void addCellEditorListener(CellEditorListener l) { + listeners.add(l); + } + + @Override + public void removeCellEditorListener(CellEditorListener l) { + listeners.remove(l); + } + + } + + private static class FiltersTableCellRenderer extends DefaultTableCellRenderer { + + JLabel label = new JLabel(); + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + label.setText(value.toString()); + + if (table instanceof JTreeTable) { + JTreeTable treeTable = (JTreeTable) table; + isSelected = treeTable.isRowSelected(row); + } + + JComponent component = label; + if (value instanceof FilterValue) { + FilterValue filterValue = (FilterValue) value; + Object fieldValue = filterValue.getValue(); + if (fieldValue != null) { + if (fieldValue.getClass() == Boolean.class) { + JPanel panel = new JPanel(new BorderLayout()); + JCheckBox checkBox = new JCheckBox(); + //checkBox.setHorizontalAlignment(CENTER); + checkBox.setSelected((Boolean) fieldValue); + checkBox.setOpaque(false); + panel.add(checkBox, BorderLayout.CENTER); + panel.setOpaque(false); + component = panel; + } + if (fieldValue.getClass() == RGBA.class) { + JPanel panel = new JPanel(new BorderLayout()); + JButton buttonChange = new JButton("") { + + @Override + protected void paintComponent(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + super.paintBorder(g); + } + + }; + buttonChange.setToolTipText(AppStrings.translate("button.selectcolor.hint")); + buttonChange.setCursor(new Cursor(Cursor.HAND_CURSOR)); + buttonChange.setBorderPainted(true); + buttonChange.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); + Dimension colorDim = new Dimension(16, 16); + buttonChange.setSize(colorDim); + buttonChange.setPreferredSize(colorDim); + buttonChange.setBackground(((RGBA) fieldValue).toColor()); + panel.add(buttonChange, BorderLayout.WEST); + component = panel; + } + } + } + + if (isSelected) { + component.setForeground(new Color(UIManager.getColor("Table.selectionForeground").getRGB(), true)); + component.setBackground(new Color(UIManager.getColor("Table.selectionBackground").getRGB(), true)); + component.setOpaque(true); + } else { + component.setOpaque(false); + } + return component; + } } private static class FiltersTreeCellRenderer extends DefaultTreeCellRenderer { @@ -155,24 +444,6 @@ public class FiltersTreeTable extends JTreeTable { } } - private static class LibraryFolder { - - private String name; - - public LibraryFolder(String name) { - this.name = name; - } - - @Override - public String toString() { - return EasyStrings.translate("library.folder." + name); - } - - public String getName() { - return name; - } - } - private static class FilterField { private final FILTER filter; @@ -206,15 +477,76 @@ public class FiltersTreeTable extends JTreeTable { } + private static class FilterValue { + + private final FilterField filterField; + + public FilterValue(FilterField filterField) { + this.filterField = filterField; + } + + public Object getValue() { + return filterField.getValue(); + } + + @Override + public String toString() { + return valueToString(filterField.getField().getName(), filterField.getValue()); + } + } + + private static String valueToString(String fieldName, Object value) { + if (value == null) { + return "null"; + } + + if (value.getClass().isArray()) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + int length = Array.getLength(value); + for (int i = 0; i < length; i++) { + Object element = Array.get(value, i); + sb.append(valueToString(fieldName, element)); + if (i < length - 1) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } + if (value.getClass() == RGBA.class) { + RGBA rgb = (RGBA) value; + return rgb.toHexARGB(); + } + if (value.getClass() == RGB.class) { + RGB rgb = (RGB) value; + return rgb.toHexRGB(); + } + + if (fieldName.equals("angle")) { + return "" + EcmaNumberToString.stringFor(Math.round(Math.toDegrees((double) value) * 100) / 100.0); + } + if (value.getClass() == Double.class) { + return EcmaNumberToString.stringFor((Double) value); + } + if (value.getClass() == Float.class) { + return EcmaNumberToString.stringFor((Float) value); + } + return "" + value; + } + private static class FiltersTreeTableModel implements TreeTableModel { private final DefaultMutableTreeNode root; + private List filters; + public FiltersTreeTableModel(List filters) { root = new DefaultMutableTreeNode("root"); + this.filters = filters; if (filters == null) { - DefaultMutableTreeNode indeterminate = new DefaultMutableTreeNode(EasyStrings.translate("properties.instance.filters.indeterminate")); + DefaultMutableTreeNode indeterminate = new DefaultMutableTreeNode(EasyStrings.translate("property.instance.filters.indeterminate")); root.add(indeterminate); return; } @@ -243,9 +575,9 @@ public class FiltersTreeTable extends JTreeTable { public String getColumnName(int column) { switch (column) { case 0: - return EasyStrings.translate("properties.instance.filters.header.property"); + return EasyStrings.translate("property.instance.filters.header.property"); case 1: - return EasyStrings.translate("properties.instance.filters.header.value"); + return EasyStrings.translate("property.instance.filters.header.value"); default: return null; } @@ -262,46 +594,6 @@ public class FiltersTreeTable extends JTreeTable { } - private String valueToString(String fieldName, Object value) { - if (value == null) { - return "null"; - } - - if (value.getClass().isArray()) { - StringBuilder sb = new StringBuilder(); - sb.append("["); - int length = Array.getLength(value); - for (int i = 0; i < length; i++) { - Object element = Array.get(value, i); - sb.append(valueToString(fieldName, element)); - if (i < length - 1) { - sb.append(", "); - } - } - sb.append("]"); - return sb.toString(); - } - if (value.getClass() == RGBA.class) { - RGBA rgb = (RGBA) value; - return rgb.toHexARGB(); - } - if (value.getClass() == RGB.class) { - RGB rgb = (RGB) value; - return rgb.toHexRGB(); - } - - if (fieldName.equals("angle")) { - return "" + EcmaNumberToString.stringFor(Math.round(Math.toDegrees((double) value) * 100) / 100.0); - } - if (value.getClass() == Double.class) { - return EcmaNumberToString.stringFor((Double) value); - } - if (value.getClass() == Float.class) { - return EcmaNumberToString.stringFor((Float) value); - } - return "" + value; - } - @Override public Object getValueAt(Object node, int column) { DefaultMutableTreeNode n = (DefaultMutableTreeNode) node; @@ -312,7 +604,7 @@ public class FiltersTreeTable extends JTreeTable { case 1: if (o instanceof FilterField) { FilterField filterField = (FilterField) o; - return valueToString(filterField.field.getName(), filterField.getValue()); + return new FilterValue(filterField); } else { return ""; } @@ -326,6 +618,10 @@ public class FiltersTreeTable extends JTreeTable { if (column == 0) { return true; } + DefaultMutableTreeNode n = (DefaultMutableTreeNode) node; + if (column == 1 && n.getUserObject() instanceof FilterField) { + return true; + } return false; } diff --git a/src/com/jpexs/decompiler/flash/easygui/properties/panels/InstancePropertiesPanel.java b/src/com/jpexs/decompiler/flash/easygui/properties/panels/InstancePropertiesPanel.java index daf3a8350..89723176e 100644 --- a/src/com/jpexs/decompiler/flash/easygui/properties/panels/InstancePropertiesPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/properties/panels/InstancePropertiesPanel.java @@ -38,6 +38,7 @@ import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.helpers.Helper; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -337,35 +338,35 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel { JScrollPane sp = new JScrollPane(filtersTable); filtersPanel.add(sp, BorderLayout.CENTER); sp.getViewport().setBackground(filtersTable.getBackground()); - //filtersPanel.setBorder(BorderFactory.createLineBorder(Color.red, 5)); - //sp.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); + + filtersTable.addFilterChangedListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + List newFilters = filtersTable.getFilters(); + if (newFilters != null) { + undoManager.doOperation(new PlaceChangeDoableOperation("instance.filters", 3) { + List applyFilters = Helper.deepCopy(newFilters); + + @Override + public void doPlaceOperation(PlaceObjectTypeTag placeObject, DepthState depthState) { + placeObject.setFilters(applyFilters); + } + }, swfPanel.getSwf()); + } + } + }); propertiesPanel = new JPanel(); - //propertiesPanel.setLayout(new BoxLayout(propertiesPanel, BoxLayout.Y_AXIS)); - //propertiesPanel.setBorder(BorderFactory.createLineBorder(Color.GREEN, 5)); propertiesPanel.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); propertiesPanel.setLayout(new GridBagLayout()); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = -1; - /*gbc.anchor = GridBagConstraints.FIRST_LINE_START; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.gridx = 0; - gbc.gridy = 0; - gbcc.weightx = 1;*/ addCard(propertiesPanel, "positionSize", null, positionSizePanel, gbc, false); - - //gbc.gridy++; addCard(propertiesPanel, "colorEffect", null, colorEffectPanel, gbc, false); - - //gbc.gridy++; addCard(propertiesPanel, "display", null, displayPanel, gbc, false); - - //gbc.gridy++; - //gbc.fill = GridBagConstraints.BOTH; //GridBagConstraints.BOTH; - //gbc.weighty = 1; addCard(propertiesPanel, "filters", null, filtersPanel, gbc, true); setCardOpened("positionSize", true); @@ -825,12 +826,13 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel { } } + if (filters.size() == 1) { List oneFilters = filters.iterator().next(); if (oneFilters == null) { filtersTable.setFilters(new ArrayList<>()); } else { - filtersTable.setFilters(oneFilters); + filtersTable.setFilters(Helper.deepCopy(oneFilters)); } } else if (filters.isEmpty()) { filtersTable.setFilters(new ArrayList<>()); diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 66a0097d6..30620c780 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -5262,7 +5262,11 @@ public final class ImagePanel extends JPanel implements MediaDisplay { Reference boundsRef = new Reference<>(null); - RECT rect = getTopTimelined().getRect(); + Timelined t = getTopTimelined(); + if (t == null) { + return; + } + RECT rect = t.getRect(); synchronized (ImagePanel.this) { synchronized (lock) { diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameClassicMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameClassicMenu.java index ede0e6107..3da52939d 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameClassicMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameClassicMenu.java @@ -276,6 +276,9 @@ public class MainFrameClassicMenu extends MainFrameMenu { if (selected != null) { selected = mapping(selected); } + if (menuGroups == null) { + return; + } for (String path : menuGroups.get(group)) { setMenuChecked(path, selected == null ? false : path.equals(selected)); } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java index e90044c5e..e362294a4 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java @@ -264,4 +264,9 @@ public class Amf3ValueEditor extends JPanel implements GenericTagEditor, FullSiz public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java index 89d425ebb..f71617fd0 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java @@ -199,4 +199,9 @@ public class BinaryDataEditor extends JPanel implements GenericTagEditor { public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java index b603f40e0..a10c60fc5 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java @@ -17,15 +17,18 @@ package com.jpexs.decompiler.flash.gui.generictageditors; import com.jpexs.helpers.ReflectionTools; +import java.awt.BorderLayout; +import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.Field; import javax.swing.JCheckBox; +import javax.swing.JPanel; /** * @author JPEXS */ -public class BooleanEditor extends JCheckBox implements GenericTagEditor { +public class BooleanEditor extends JPanel implements GenericTagEditor { private final Object obj; @@ -36,6 +39,8 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { private final Class type; private final String fieldName; + + private final JCheckBox checkBox; @Override public void added() { @@ -49,7 +54,13 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { this.index = index; this.type = type; this.fieldName = fieldName; - reset(); + checkBox = new JCheckBox(); + checkBox.setOpaque(false); + checkBox.setRequestFocusEnabled(false); + setLayout(new BorderLayout()); + add(checkBox, BorderLayout.CENTER); + setOpaque(false); + reset(); } @Override @@ -59,7 +70,7 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { @Override public void reset() { try { - setSelected((boolean) ReflectionTools.getValue(obj, field, index)); + checkBox.setSelected((boolean) ReflectionTools.getValue(obj, field, index)); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } @@ -69,13 +80,13 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { public boolean save() { try { boolean oldValue = (boolean) ReflectionTools.getValue(obj, field, index); - boolean newValue = isSelected(); + boolean newValue = checkBox.isSelected(); if (oldValue == newValue) { return false; } - ReflectionTools.setValue(obj, field, index, isSelected()); + ReflectionTools.setValue(obj, field, index, checkBox.isSelected()); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } @@ -85,7 +96,7 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { @Override public void addChangeListener(final ChangeListener l) { final GenericTagEditor t = this; - addActionListener(new ActionListener() { + checkBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -96,7 +107,7 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { @Override public Object getChangedValue() { - return isSelected(); + return checkBox.isSelected(); } @Override @@ -118,4 +129,9 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java index f8894e49d..f0dac0a92 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java @@ -17,12 +17,14 @@ package com.jpexs.decompiler.flash.gui.generictageditors; import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.FocusablePanel; import com.jpexs.decompiler.flash.gui.ViewMessages; import com.jpexs.decompiler.flash.types.ARGB; import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.helpers.Helper; import com.jpexs.helpers.ReflectionTools; +import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; @@ -36,6 +38,8 @@ import java.awt.event.FocusEvent; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import javax.swing.BorderFactory; import javax.swing.JButton; @@ -50,7 +54,7 @@ import javax.swing.colorchooser.AbstractColorChooserPanel; /** * @author JPEXS */ -public class ColorEditor extends JPanel implements GenericTagEditor, ActionListener { +public class ColorEditor extends FocusablePanel implements GenericTagEditor, ActionListener { private final Object obj; @@ -74,6 +78,8 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe private final JButton buttonChange; + private final List changeListeners = new ArrayList<>(); + public int getColorType() { return colorType; } @@ -90,7 +96,7 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe this.type = type; this.fieldName = fieldName; - setLayout(new FlowLayout()); + setLayout(new BorderLayout()); buttonChange = new JButton("") { @@ -111,7 +117,7 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe Dimension colorDim = new Dimension(16, 16); buttonChange.setSize(colorDim); buttonChange.setPreferredSize(colorDim); - add(buttonChange); + add(buttonChange, BorderLayout.WEST); reset(); } @@ -163,15 +169,7 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe @Override public void addChangeListener(final ChangeListener l) { - final GenericTagEditor t = this; - addFocusListener(new FocusAdapter() { - - @Override - public void focusLost(FocusEvent e) { - l.change(t); - } - - }); + changeListeners.add(l); } @Override @@ -256,15 +254,15 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe } JDialog chooserDialog = null; - + private void colorCancelPerformed(ActionEvent e) { chooserDialog.setVisible(false); } - + private void colorOkPerformed(ActionEvent e) { chooserDialog.setVisible(false); } - + @Override public void actionPerformed(ActionEvent e) { @@ -283,6 +281,10 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe color = newColor; buttonChange.setBackground(color); repaint(); + List listeners2 = new ArrayList<>(changeListeners); + for (ChangeListener l : listeners2) { + l.change(this); + } } } @@ -296,4 +298,9 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/EnumEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/EnumEditor.java index 1d993291f..144b3e9bb 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/EnumEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/EnumEditor.java @@ -159,4 +159,9 @@ public class EnumEditor extends JComboBox> implements Gene public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/FloatEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/FloatEditor.java index 923f659a9..71edb3365 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/FloatEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/FloatEditor.java @@ -24,6 +24,8 @@ import java.awt.Dimension; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import javax.swing.JTextField; @@ -41,7 +43,9 @@ public class FloatEditor extends JTextField implements GenericTagEditor { private final Class type; private String fieldName; - + + private ValueNormalizer normalizer; + @Override public boolean getScrollableTracksViewportWidth() { return true; @@ -77,7 +81,10 @@ public class FloatEditor extends JTextField implements GenericTagEditor { public void reset() { try { Object val = ReflectionTools.getValue(obj, field, index); - setText(val == null ? "" : val.toString()); + if (normalizer != null) { + val = normalizer.toViewValue(val); + } + setText(val == null ? "" : EcmaScript.toString(val)); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } @@ -86,7 +93,11 @@ public class FloatEditor extends JTextField implements GenericTagEditor { @Override public boolean save() { try { - String oldValue = (String) EcmaScript.toString(ReflectionTools.getValue(obj, field, index)); + Object oldFieldValue = ReflectionTools.getValue(obj, field, index); + if (normalizer != null) { + oldFieldValue = normalizer.toViewValue(oldFieldValue); + } + String oldValue = (String) EcmaScript.toString(oldFieldValue); String newValue = getText(); if (Objects.equals(oldValue, newValue)) { return false; @@ -97,6 +108,9 @@ public class FloatEditor extends JTextField implements GenericTagEditor { } else { val = Float.valueOf(getText()); } + if (normalizer != null) { + val = normalizer.toFieldValue(val); + } ReflectionTools.setValue(obj, field, index, val); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore @@ -150,4 +164,10 @@ public class FloatEditor extends JTextField implements GenericTagEditor { public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + this.normalizer = normalizer; + reset(); + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java index a43ecded8..c6926c8ed 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java @@ -42,4 +42,6 @@ public interface GenericTagEditor { public void validateValue(); public Object getObject(); + + public void setValueNormalizer(ValueNormalizer normalizer); } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java index 9542536f5..fa5002138 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java @@ -19,12 +19,14 @@ package com.jpexs.decompiler.flash.gui.generictageditors; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.ReflectionTools; +import java.awt.BorderLayout; 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.JPanel; import javax.swing.JSpinner; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; @@ -33,7 +35,7 @@ import javax.swing.text.DefaultFormatter; /** * @author JPEXS */ -public class NumberEditor extends JSpinner implements GenericTagEditor { +public class NumberEditor extends JPanel implements GenericTagEditor { private final Object obj; @@ -46,6 +48,10 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { private final SWFType swfType; private String fieldName; + + private ValueNormalizer normalizer; + + private JSpinner spinner; @Override public BaselineResizeBehavior getBaselineResizeBehavior() { @@ -71,20 +77,28 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { this.type = type; this.swfType = swfType; this.fieldName = fieldName; + spinner = new JSpinner(); + + setLayout(new BorderLayout()); + add(spinner, BorderLayout.WEST); + setOpaque(false); - reset(); - ((JSpinner.NumberEditor) getEditor()).getFormat().setGroupingUsed(false); - JFormattedTextField jtf = ((JSpinner.NumberEditor) getEditor()).getTextField(); + reset(); + ((JSpinner.NumberEditor) spinner.getEditor()).getFormat().setGroupingUsed(false); + JFormattedTextField jtf = ((JSpinner.NumberEditor) spinner.getEditor()).getTextField(); DefaultFormatter formatter = (DefaultFormatter) jtf.getFormatter(); formatter.setCommitsOnValidEdit(true); - + } @Override public void reset() { try { Object value = ReflectionTools.getValue(obj, field, index); - setModel(getModel(swfType, value)); + if (normalizer != null) { + value = normalizer.toViewValue(value); + } + spinner.setModel(getModel(swfType, value)); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } @@ -93,14 +107,22 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { @Override public boolean save() { try { - Object oldValue = ReflectionTools.getValue(obj, field, index); - Object newValue = getChangedValue(); - if (newValue == null) { + Object oldValue = ReflectionTools.getValue(obj, field, index); + Object oldViewValue = oldValue; + if (normalizer != null) { + oldViewValue = normalizer.toViewValue(oldValue); + } + Object newViewValue = getChangedValue(); + if (newViewValue == null) { return false; } - if (Objects.equals(oldValue, newValue)) { + if (Objects.equals(oldViewValue, newViewValue)) { return false; } + Object newValue = newViewValue; + if (normalizer != null) { + newValue = normalizer.toFieldValue(newViewValue); + } ReflectionTools.setValue(obj, field, index, newValue); } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore @@ -210,15 +232,15 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { public Object getChangedValue() { Object value = null; if (type.equals(int.class) || type.equals(Integer.class)) { - value = Integer.parseInt(getValue().toString()); + value = Integer.parseInt(spinner.getValue().toString()); } else if (type.equals(short.class) || type.equals(Short.class)) { - value = Short.parseShort(getValue().toString()); + value = Short.parseShort(spinner.getValue().toString()); } else if (type.equals(long.class) || type.equals(Long.class)) { - value = Long.parseLong(getValue().toString()); + value = Long.parseLong(spinner.getValue().toString()); } else if (type.equals(double.class) || type.equals(Double.class)) { - value = Double.parseDouble(getValue().toString()); + value = Double.parseDouble(spinner.getValue().toString()); } else if (type.equals(float.class) || type.equals(Float.class)) { - value = Float.parseFloat(getValue().toString()); + value = Float.parseFloat(spinner.getValue().toString()); } return value; } @@ -242,4 +264,9 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + this.normalizer = normalizer; + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java index ea75876cb..bf8b16b03 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java @@ -151,4 +151,9 @@ public class StringEditor extends JTextArea implements GenericTagEditor { public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/UUIDEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/UUIDEditor.java index db7cf0811..4949780cf 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/UUIDEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/UUIDEditor.java @@ -165,4 +165,9 @@ public class UUIDEditor extends JTextField implements GenericTagEditor { public Object getObject() { return obj; } + + @Override + public void setValueNormalizer(ValueNormalizer normalizer) { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/ValueNormalizer.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/ValueNormalizer.java new file mode 100644 index 000000000..6485859eb --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/ValueNormalizer.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2025 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.generictageditors; + +/** + * + * @author JPEXS + */ +public interface ValueNormalizer { + public Object toFieldValue(Object viewValue); + + public Object toViewValue(Object fieldValue); +} diff --git a/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties b/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties index aadb65db9..ec65b8e26 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties @@ -121,7 +121,9 @@ property.document.backgroundColor = Background color properties.instance.header.filters = Filters -properties.instance.filters.header.property = Property -properties.instance.filters.header.value = Value +property.instance.filters.header.property = Property +property.instance.filters.header.value = Value -properties.instance.filters.indeterminate = Indeterminate \ No newline at end of file +property.instance.filters.indeterminate = Indeterminate + +property.instance.filters = Filters \ No newline at end of file diff --git a/src/de/javagl/treetable/JTreeTable.java b/src/de/javagl/treetable/JTreeTable.java index 17b3f6aae..4531d6cbd 100644 --- a/src/de/javagl/treetable/JTreeTable.java +++ b/src/de/javagl/treetable/JTreeTable.java @@ -99,7 +99,7 @@ public class JTreeTable extends JTable // Install the tree editor renderer and editor. setDefaultRenderer(TreeTableModel.class, tree); setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor()); - + // No grid. setShowGrid(false);