diff --git a/CHANGELOG.md b/CHANGELOG.md index 51e04f8c3..8c0805e12 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 (editing only existing, without gradients) +- Simple editor - filters (without editing gradients, convolution, colormatrix) ### Fixed - [#2424] DefineEditText handling of letterSpacing, font size on incorrect values diff --git a/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java b/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java index 659063bba..0d35bbab3 100644 --- a/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java +++ b/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.easygui; import com.jpexs.decompiler.flash.ecma.EcmaNumberToString; +import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.gui.generictageditors.BooleanEditor; @@ -28,6 +29,7 @@ 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.Reserved; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.filters.FILTER; import de.javagl.treetable.JTreeTable; @@ -62,6 +64,7 @@ import javax.swing.UIManager; import javax.swing.border.BevelBorder; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; +import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.plaf.basic.BasicTableUI; import javax.swing.table.DefaultTableCellRenderer; @@ -168,7 +171,7 @@ public class FiltersTreeTable extends JTreeTable { filterChangedListeners.remove(l); } - private void fireFilterChanged() { + public void fireFilterChanged() { List listeners2 = new ArrayList<>(filterChangedListeners); for (ActionListener l : listeners2) { l.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "filterChanged")); @@ -184,12 +187,50 @@ public class FiltersTreeTable extends JTreeTable { return new ArrayList<>(); } - public void setFilters(List filters) { - if (Objects.equals(getFilters(), filters)) { + public void addFilter(FILTER filter) { + TreeModel model = getTree().getModel(); + if (model instanceof FiltersTreeTableModel) { + FiltersTreeTableModel treeTableModel = (FiltersTreeTableModel) model; + treeTableModel.addFilter(filter); + DefaultMutableTreeNode root = ((DefaultMutableTreeNode) treeTableModel.getRoot()); + getTree().expandPath(new TreePath(new Object[]{root, root.getChildAt(root.getChildCount() - 1)})); + updateColumns(); + fireFilterChanged(); + } + } + + public void clearFilters() { + TreeModel model = getTree().getModel(); + if (model instanceof FiltersTreeTableModel) { + setTreeTableModel(new FiltersTreeTableModel(new ArrayList<>())); + fireFilterChanged(); + } + } + + public boolean isFilterSelected() { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) getTree().getLastSelectedPathComponent(); + if (node == null) { + return false; + } + return node.getUserObject() instanceof FilterName; + } + + public void removeSelectedFilter() { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) getTree().getLastSelectedPathComponent(); + if (node == null) { return; } - setTreeTableModel(new FiltersTreeTableModel(filters)); + Object object = node.getUserObject(); + if (object instanceof FilterName) { + DefaultMutableTreeNode root = (DefaultMutableTreeNode) getTree().getModel().getRoot(); + int filterIndex = root.getIndex(node); + FiltersTreeTableModel model = (FiltersTreeTableModel) getTree().getModel(); + model.removeFilter(filterIndex); + fireFilterChanged(); + } + } + private void updateColumns() { getColumnModel().getColumn(0).setMinWidth(200); getColumnModel().getColumn(0).setWidth(200); getColumnModel().getColumn(0).setPreferredWidth(200); @@ -197,7 +238,14 @@ public class FiltersTreeTable extends JTreeTable { getColumnModel().getColumn(1).setCellEditor(new FiltersValueCellEditor(this)); getColumnModel().getColumn(1).setCellRenderer(new FiltersTableCellRenderer()); - + } + + public void setFilters(List filters) { + if (Objects.equals(getFilters(), filters)) { + return; + } + setTreeTableModel(new FiltersTreeTableModel(filters)); + TreeModel ttm = getTree().getModel(); Object root = ttm.getRoot(); int childCount = ttm.getChildCount(root); @@ -374,7 +422,7 @@ public class FiltersTreeTable extends JTreeTable { if (value instanceof FilterValue) { FilterValue filterValue = (FilterValue) value; Object fieldValue = filterValue.getValue(); - if (fieldValue != null) { + if (fieldValue != null) { if (fieldValue.getClass() == Boolean.class) { JPanel panel = new JPanel(new BorderLayout()); JCheckBox checkBox = new JCheckBox(); @@ -526,44 +574,94 @@ public class FiltersTreeTable extends JTreeTable { 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); + if (value.getClass() == Double.class + || value.getClass() == Float.class + || value.getClass() == float.class + || value.getClass() == double.class + ) { + return EcmaScript.toString(value); } return "" + value; } + private static class FilterName { + + private final FILTER filter; + + public FilterName(FILTER filter) { + this.filter = filter; + } + + @Override + public String toString() { + return filter.getClass().getSimpleName(); + } + } + private static class FiltersTreeTableModel implements TreeTableModel { private final DefaultMutableTreeNode root; private List filters; + private List listeners = new ArrayList<>(); + public FiltersTreeTableModel(List filters) { root = new DefaultMutableTreeNode("root"); - this.filters = filters; if (filters == null) { DefaultMutableTreeNode indeterminate = new DefaultMutableTreeNode(EasyStrings.translate("property.instance.filters.indeterminate")); root.add(indeterminate); + this.filters = null; return; } + this.filters = new ArrayList<>(); + for (FILTER filter : filters) { - DefaultMutableTreeNode filterNode = new DefaultMutableTreeNode(filter.getClass().getSimpleName()); - root.add(filterNode); - Field[] fields = filter.getClass().getFields(); - for (Field field : fields) { - if ("id".equals(field.getName())) { - continue; - } - FilterField filterField = new FilterField(filter, field); - DefaultMutableTreeNode fieldNode = new DefaultMutableTreeNode(filterField); - filterNode.add(fieldNode); + addFilter(filter); + } + } + + public void addFilter(FILTER filter) { + if (this.filters == null) { //indeterminate + DefaultMutableTreeNode indeterminate = (DefaultMutableTreeNode) root.getChildAt(0); + root.remove(0); + this.filters = new ArrayList<>(); + for (TreeModelListener l : listeners) { + l.treeNodesRemoved(new TreeModelEvent(this, new Object[]{root}, new int[]{0}, new Object[]{indeterminate})); } } + + DefaultMutableTreeNode filterNode = new DefaultMutableTreeNode(new FilterName(filter)); + root.add(filterNode); + Field[] fields = filter.getClass().getFields(); + for (Field field : fields) { + if ("id".equals(field.getName())) { + continue; + } + Reserved reserved = field.getAnnotation(Reserved.class); + if (reserved != null) { + continue; + } + FilterField filterField = new FilterField(filter, field); + DefaultMutableTreeNode fieldNode = new DefaultMutableTreeNode(filterField); + filterNode.add(fieldNode); + } + this.filters.add(filter); + + for (TreeModelListener l : listeners) { + l.treeNodesInserted(new TreeModelEvent(this, new Object[]{root}, new int[]{this.filters.size() - 1}, new Object[]{filterNode})); + } + } + + public void removeFilter(int index) { + filters.remove(index); + DefaultMutableTreeNode node = (DefaultMutableTreeNode) root.getChildAt(index); + root.remove(node); + for (TreeModelListener l : listeners) { + l.treeNodesRemoved(new TreeModelEvent(this, new Object[]{root}, new int[]{this.filters.size()}, new Object[]{node})); + } } @Override @@ -662,10 +760,12 @@ public class FiltersTreeTable extends JTreeTable { @Override public void addTreeModelListener(TreeModelListener l) { + listeners.add(l); } @Override public void removeTreeModelListener(TreeModelListener l) { + listeners.remove(l); } } 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 89723176e..3565a9a28 100644 --- a/src/com/jpexs/decompiler/flash/easygui/properties/panels/InstancePropertiesPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/properties/panels/InstancePropertiesPanel.java @@ -28,7 +28,9 @@ import com.jpexs.decompiler.flash.easygui.properties.PropertyChangeDoableOperati import com.jpexs.decompiler.flash.easygui.properties.PropertyValidationInterface; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.gui.BoundsChangeListener; +import com.jpexs.decompiler.flash.gui.PopupButton; import com.jpexs.decompiler.flash.gui.RegistrationPointPosition; +import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.gui.ViewMessages; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.tags.converters.PlaceObjectTypeConverter; @@ -37,7 +39,15 @@ import com.jpexs.decompiler.flash.timeline.Timelined; 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.BEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.BLURFILTER; +import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; +import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; +import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; import com.jpexs.helpers.Helper; import java.awt.BorderLayout; import java.awt.Color; @@ -52,22 +62,29 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; /** * @@ -335,8 +352,79 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel { JPanel filtersPanel = new JPanel(new BorderLayout()); filtersTable = new FiltersTreeTable(); + + JPanel filtersToolbar = new JPanel(new FlowLayout(FlowLayout.LEFT)); + PopupButton addFilterButton = new PopupButton(View.getIcon("add16")) { + @Override + @SuppressWarnings("unchecked") + protected JPopupMenu getPopupMenu() { + JPopupMenu menu = new JPopupMenu(); + JMenuItem removeAllMenuItem = new JMenuItem(EasyStrings.translate("property.instance.filters.menu.add.removeAll")); + removeAllMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + filtersTable.clearFilters(); + } + }); + menu.add(removeAllMenuItem); + menu.addSeparator(); + Class[] possilbleFilters = new Class[] { + DROPSHADOWFILTER.class, + BLURFILTER.class, + GLOWFILTER.class, + BEVELFILTER.class, + GRADIENTGLOWFILTER.class, + GRADIENTBEVELFILTER.class, + COLORMATRIXFILTER.class, + CONVOLUTIONFILTER.class + }; + + for (Class filterClass : possilbleFilters) { + JMenuItem filterMenuItem = new JMenuItem(filterClass.getSimpleName()); + filterMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + FILTER filter; + try { + filter = (FILTER) filterClass.getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) { + return; + } + filtersTable.addFilter(filter); + } + }); + menu.add(filterMenuItem); + } + return menu; + } + }; + addFilterButton.setToolTipText(EasyStrings.translate("property.instance.filters.menu.add")); + + JButton removeFilterButton = new JButton(View.getIcon("removeobject16")); + removeFilterButton.setEnabled(false); + removeFilterButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + filtersTable.removeSelectedFilter(); + } + }); + removeFilterButton.setToolTipText(EasyStrings.translate("property.instance.filters.menu.remove")); + + + filtersTable.getTree().addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent e) { + removeFilterButton.setEnabled(filtersTable.isFilterSelected()); + } + }); + + + filtersToolbar.add(addFilterButton); + filtersToolbar.add(removeFilterButton); + JScrollPane sp = new JScrollPane(filtersTable); filtersPanel.add(sp, BorderLayout.CENTER); + filtersPanel.add(filtersToolbar, BorderLayout.SOUTH); sp.getViewport().setBackground(filtersTable.getBackground()); filtersTable.addFilterChangedListener(new ActionListener() { diff --git a/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties b/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties index ec65b8e26..05a03054e 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties @@ -126,4 +126,7 @@ property.instance.filters.header.value = Value property.instance.filters.indeterminate = Indeterminate -property.instance.filters = Filters \ No newline at end of file +property.instance.filters = Filters +property.instance.filters.menu.add = Add filter +property.instance.filters.menu.add.removeAll = Remove all +property.instance.filters.menu.remove = Remove filter \ No newline at end of file