diff --git a/CHANGELOG.md b/CHANGELOG.md index dccad437a..e06d8ab23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +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) ### Fixed - [#2424] DefineEditText handling of letterSpacing, font size on incorrect values diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java index 4751a36f1..51053fc96 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; import java.awt.Color; +import java.util.Objects; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -253,4 +254,72 @@ public class BEVELFILTER extends FILTER { } return result; } + + @Override + public int hashCode() { + int hash = 7; + hash = 47 * hash + Objects.hashCode(this.shadowColor); + hash = 47 * hash + Objects.hashCode(this.highlightColor); + hash = 47 * hash + (int) (Double.doubleToLongBits(this.blurX) ^ (Double.doubleToLongBits(this.blurX) >>> 32)); + hash = 47 * hash + (int) (Double.doubleToLongBits(this.blurY) ^ (Double.doubleToLongBits(this.blurY) >>> 32)); + hash = 47 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); + hash = 47 * hash + (int) (Double.doubleToLongBits(this.distance) ^ (Double.doubleToLongBits(this.distance) >>> 32)); + hash = 47 * hash + Float.floatToIntBits(this.strength); + hash = 47 * hash + (this.innerShadow ? 1 : 0); + hash = 47 * hash + (this.knockout ? 1 : 0); + hash = 47 * hash + (this.compositeSource ? 1 : 0); + hash = 47 * hash + (this.onTop ? 1 : 0); + hash = 47 * hash + this.passes; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final BEVELFILTER other = (BEVELFILTER) obj; + if (Double.doubleToLongBits(this.blurX) != Double.doubleToLongBits(other.blurX)) { + return false; + } + if (Double.doubleToLongBits(this.blurY) != Double.doubleToLongBits(other.blurY)) { + return false; + } + if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(other.angle)) { + return false; + } + if (Double.doubleToLongBits(this.distance) != Double.doubleToLongBits(other.distance)) { + return false; + } + if (Float.floatToIntBits(this.strength) != Float.floatToIntBits(other.strength)) { + return false; + } + if (this.innerShadow != other.innerShadow) { + return false; + } + if (this.knockout != other.knockout) { + return false; + } + if (this.compositeSource != other.compositeSource) { + return false; + } + if (this.onTop != other.onTop) { + return false; + } + if (this.passes != other.passes) { + return false; + } + if (!Objects.equals(this.shadowColor, other.shadowColor)) { + return false; + } + return Objects.equals(this.highlightColor, other.highlightColor); + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java index 76844450a..a946c812c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java @@ -82,4 +82,40 @@ public class BLURFILTER extends FILTER { public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { return blurSvg(blurX, blurY, passes, document, filtersElement, exporter, in); } + + @Override + public int hashCode() { + int hash = 3; + hash = 37 * hash + (int) (Double.doubleToLongBits(this.blurX) ^ (Double.doubleToLongBits(this.blurX) >>> 32)); + hash = 37 * hash + (int) (Double.doubleToLongBits(this.blurY) ^ (Double.doubleToLongBits(this.blurY) >>> 32)); + hash = 37 * hash + this.passes; + hash = 37 * hash + this.reserved; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final BLURFILTER other = (BLURFILTER) obj; + if (Double.doubleToLongBits(this.blurX) != Double.doubleToLongBits(other.blurX)) { + return false; + } + if (Double.doubleToLongBits(this.blurY) != Double.doubleToLongBits(other.blurY)) { + return false; + } + if (this.passes != other.passes) { + return false; + } + return this.reserved == other.reserved; + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java index 339932815..2a000d826 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFArray; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -96,4 +97,27 @@ public class COLORMATRIXFILTER extends FILTER { return result; } + @Override + public int hashCode() { + int hash = 7; + hash = 67 * hash + Arrays.hashCode(this.matrix); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final COLORMATRIXFILTER other = (COLORMATRIXFILTER) obj; + return Arrays.equals(this.matrix, other.matrix); + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java index 5b50a43ec..52ed841fc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java @@ -23,7 +23,9 @@ import com.jpexs.decompiler.flash.types.annotations.Reserved; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Objects; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -138,4 +140,60 @@ public class CONVOLUTIONFILTER extends FILTER { return result; } + + @Override + public int hashCode() { + int hash = 3; + hash = 53 * hash + this.matrixX; + hash = 53 * hash + this.matrixY; + hash = 53 * hash + Float.floatToIntBits(this.divisor); + hash = 53 * hash + Float.floatToIntBits(this.bias); + hash = 53 * hash + Arrays.hashCode(this.matrix); + hash = 53 * hash + Objects.hashCode(this.defaultColor); + hash = 53 * hash + this.reserved; + hash = 53 * hash + (this.clamp ? 1 : 0); + hash = 53 * hash + (this.preserveAlpha ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CONVOLUTIONFILTER other = (CONVOLUTIONFILTER) obj; + if (this.matrixX != other.matrixX) { + return false; + } + if (this.matrixY != other.matrixY) { + return false; + } + if (Float.floatToIntBits(this.divisor) != Float.floatToIntBits(other.divisor)) { + return false; + } + if (Float.floatToIntBits(this.bias) != Float.floatToIntBits(other.bias)) { + return false; + } + if (this.reserved != other.reserved) { + return false; + } + if (this.clamp != other.clamp) { + return false; + } + if (this.preserveAlpha != other.preserveAlpha) { + return false; + } + if (!Arrays.equals(this.matrix, other.matrix)) { + return false; + } + return Objects.equals(this.defaultColor, other.defaultColor); + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java index b4951e890..a58ed13e9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; import java.awt.Color; +import java.util.Objects; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -114,4 +115,64 @@ public class DROPSHADOWFILTER extends FILTER { public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { return dropShadowSvg(distance, angle, dropShadowColor, innerShadow, knockout, compositeSource, blurX, blurY, strength, passes, document, filtersElement, exporter, in); } + + @Override + public int hashCode() { + int hash = 7; + hash = 97 * hash + Objects.hashCode(this.dropShadowColor); + hash = 97 * hash + (int) (Double.doubleToLongBits(this.blurX) ^ (Double.doubleToLongBits(this.blurX) >>> 32)); + hash = 97 * hash + (int) (Double.doubleToLongBits(this.blurY) ^ (Double.doubleToLongBits(this.blurY) >>> 32)); + hash = 97 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); + hash = 97 * hash + (int) (Double.doubleToLongBits(this.distance) ^ (Double.doubleToLongBits(this.distance) >>> 32)); + hash = 97 * hash + Float.floatToIntBits(this.strength); + hash = 97 * hash + (this.innerShadow ? 1 : 0); + hash = 97 * hash + (this.knockout ? 1 : 0); + hash = 97 * hash + (this.compositeSource ? 1 : 0); + hash = 97 * hash + this.passes; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final DROPSHADOWFILTER other = (DROPSHADOWFILTER) obj; + if (Double.doubleToLongBits(this.blurX) != Double.doubleToLongBits(other.blurX)) { + return false; + } + if (Double.doubleToLongBits(this.blurY) != Double.doubleToLongBits(other.blurY)) { + return false; + } + if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(other.angle)) { + return false; + } + if (Double.doubleToLongBits(this.distance) != Double.doubleToLongBits(other.distance)) { + return false; + } + if (Float.floatToIntBits(this.strength) != Float.floatToIntBits(other.strength)) { + return false; + } + if (this.innerShadow != other.innerShadow) { + return false; + } + if (this.knockout != other.knockout) { + return false; + } + if (this.compositeSource != other.compositeSource) { + return false; + } + if (this.passes != other.passes) { + return false; + } + return Objects.equals(this.dropShadowColor, other.dropShadowColor); + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java index ef7d88147..93210e6d1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; import java.awt.Color; +import java.util.Objects; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -196,4 +197,56 @@ public class GLOWFILTER extends FILTER { return feCompositeResult; } } + + @Override + public int hashCode() { + int hash = 7; + hash = 89 * hash + Objects.hashCode(this.glowColor); + hash = 89 * hash + (int) (Double.doubleToLongBits(this.blurX) ^ (Double.doubleToLongBits(this.blurX) >>> 32)); + hash = 89 * hash + (int) (Double.doubleToLongBits(this.blurY) ^ (Double.doubleToLongBits(this.blurY) >>> 32)); + hash = 89 * hash + Float.floatToIntBits(this.strength); + hash = 89 * hash + (this.innerGlow ? 1 : 0); + hash = 89 * hash + (this.knockout ? 1 : 0); + hash = 89 * hash + (this.compositeSource ? 1 : 0); + hash = 89 * hash + this.passes; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final GLOWFILTER other = (GLOWFILTER) obj; + if (Double.doubleToLongBits(this.blurX) != Double.doubleToLongBits(other.blurX)) { + return false; + } + if (Double.doubleToLongBits(this.blurY) != Double.doubleToLongBits(other.blurY)) { + return false; + } + if (Float.floatToIntBits(this.strength) != Float.floatToIntBits(other.strength)) { + return false; + } + if (this.innerGlow != other.innerGlow) { + return false; + } + if (this.knockout != other.knockout) { + return false; + } + if (this.compositeSource != other.compositeSource) { + return false; + } + if (this.passes != other.passes) { + return false; + } + return Objects.equals(this.glowColor, other.glowColor); + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java index 33c510238..7a969f71d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; import java.awt.Color; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -155,4 +156,72 @@ public class GRADIENTBEVELFILTER extends FILTER { public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { return null; //NOT SUPPORTED } + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + Arrays.deepHashCode(this.gradientColors); + hash = 37 * hash + Arrays.hashCode(this.gradientRatio); + hash = 37 * hash + (int) (Double.doubleToLongBits(this.blurX) ^ (Double.doubleToLongBits(this.blurX) >>> 32)); + hash = 37 * hash + (int) (Double.doubleToLongBits(this.blurY) ^ (Double.doubleToLongBits(this.blurY) >>> 32)); + hash = 37 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); + hash = 37 * hash + (int) (Double.doubleToLongBits(this.distance) ^ (Double.doubleToLongBits(this.distance) >>> 32)); + hash = 37 * hash + Float.floatToIntBits(this.strength); + hash = 37 * hash + (this.innerShadow ? 1 : 0); + hash = 37 * hash + (this.knockout ? 1 : 0); + hash = 37 * hash + (this.compositeSource ? 1 : 0); + hash = 37 * hash + (this.onTop ? 1 : 0); + hash = 37 * hash + this.passes; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final GRADIENTBEVELFILTER other = (GRADIENTBEVELFILTER) obj; + if (Double.doubleToLongBits(this.blurX) != Double.doubleToLongBits(other.blurX)) { + return false; + } + if (Double.doubleToLongBits(this.blurY) != Double.doubleToLongBits(other.blurY)) { + return false; + } + if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(other.angle)) { + return false; + } + if (Double.doubleToLongBits(this.distance) != Double.doubleToLongBits(other.distance)) { + return false; + } + if (Float.floatToIntBits(this.strength) != Float.floatToIntBits(other.strength)) { + return false; + } + if (this.innerShadow != other.innerShadow) { + return false; + } + if (this.knockout != other.knockout) { + return false; + } + if (this.compositeSource != other.compositeSource) { + return false; + } + if (this.onTop != other.onTop) { + return false; + } + if (this.passes != other.passes) { + return false; + } + if (!Arrays.deepEquals(this.gradientColors, other.gradientColors)) { + return false; + } + return Arrays.equals(this.gradientRatio, other.gradientRatio); + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java index b3d867939..a8e60d103 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.helpers.SerializableImage; import java.awt.Color; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -155,4 +156,72 @@ public class GRADIENTGLOWFILTER extends FILTER { public String toSvg(Document document, Element filtersElement, SVGExporter exporter, String in) { return null; //NOT SUPPORTED } + + @Override + public int hashCode() { + int hash = 5; + hash = 67 * hash + Arrays.deepHashCode(this.gradientColors); + hash = 67 * hash + Arrays.hashCode(this.gradientRatio); + hash = 67 * hash + (int) (Double.doubleToLongBits(this.blurX) ^ (Double.doubleToLongBits(this.blurX) >>> 32)); + hash = 67 * hash + (int) (Double.doubleToLongBits(this.blurY) ^ (Double.doubleToLongBits(this.blurY) >>> 32)); + hash = 67 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); + hash = 67 * hash + (int) (Double.doubleToLongBits(this.distance) ^ (Double.doubleToLongBits(this.distance) >>> 32)); + hash = 67 * hash + Float.floatToIntBits(this.strength); + hash = 67 * hash + (this.innerShadow ? 1 : 0); + hash = 67 * hash + (this.knockout ? 1 : 0); + hash = 67 * hash + (this.compositeSource ? 1 : 0); + hash = 67 * hash + (this.onTop ? 1 : 0); + hash = 67 * hash + this.passes; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final GRADIENTGLOWFILTER other = (GRADIENTGLOWFILTER) obj; + if (Double.doubleToLongBits(this.blurX) != Double.doubleToLongBits(other.blurX)) { + return false; + } + if (Double.doubleToLongBits(this.blurY) != Double.doubleToLongBits(other.blurY)) { + return false; + } + if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(other.angle)) { + return false; + } + if (Double.doubleToLongBits(this.distance) != Double.doubleToLongBits(other.distance)) { + return false; + } + if (Float.floatToIntBits(this.strength) != Float.floatToIntBits(other.strength)) { + return false; + } + if (this.innerShadow != other.innerShadow) { + return false; + } + if (this.knockout != other.knockout) { + return false; + } + if (this.compositeSource != other.compositeSource) { + return false; + } + if (this.onTop != other.onTop) { + return false; + } + if (this.passes != other.passes) { + return false; + } + if (!Arrays.deepEquals(this.gradientColors, other.gradientColors)) { + return false; + } + return Arrays.equals(this.gradientRatio, other.gradientRatio); + } + + } diff --git a/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java b/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java new file mode 100644 index 000000000..4e6648069 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/easygui/FiltersTreeTable.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2024 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.easygui; + +import com.jpexs.decompiler.flash.ecma.EcmaNumberToString; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import de.javagl.treetable.JTreeTable; +import de.javagl.treetable.TreeTableModel; +import java.awt.Color; +import java.awt.Component; +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.List; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.event.TreeModelListener; +import javax.swing.plaf.basic.BasicTableUI; +import javax.swing.table.TableColumn; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreePath; + +/** + * + * @author JPEXS + */ +public class FiltersTreeTable extends JTreeTable { + + public FiltersTreeTable() { + super(new FiltersTreeTableModel(null)); + getTree().setCellRenderer(new FiltersTreeCellRenderer()); + getTree().setRootVisible(false); + getTree().setShowsRootHandles(true); + addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + int selectedRow = getSelectedRow(); + JTree tree = getTree(); + + if (e.getKeyCode() == KeyEvent.VK_LEFT) { + TreePath path = tree.getPathForRow(selectedRow); + if (path != null && tree.isExpanded(path)) { + tree.collapsePath(path); + + int parentRow = tree.getRowForPath(path); + changeSelection(parentRow, 0, false, false); + } else if (path != null) { + TreePath parentPath = path.getParentPath(); + if (parentPath != null) { + int parentRow = tree.getRowForPath(parentPath); + changeSelection(parentRow, 0, false, false); + } + } + e.consume(); + } + + if (e.getKeyCode() == KeyEvent.VK_RIGHT) { + TreePath path = tree.getPathForRow(selectedRow); + if (path != null && !tree.isExpanded(path)) { + tree.expandPath(path); + int parentRow = tree.getRowForPath(path); + changeSelection(parentRow, 0, false, false); + } else { + TreePath childPath = tree.getPathForRow(selectedRow + 1); + if (childPath != null) { + int childRow = tree.getRowForPath(childPath); + changeSelection(childRow, 0, false, false); + } + } + e.consume(); + } + } + }); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + setUI(new BasicTableUI()); + + setRowHeight(18); + getTree().setRowHeight(18); + + if (View.isOceanic()) { + setBackground(Color.WHITE); + getTree().setBackground(Color.WHITE); + } + + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) { + int selectedRow = getSelectedRow(); + if (selectedRow == -1) { + return; + } + DefaultMutableTreeNode node = (DefaultMutableTreeNode) getValueAt(selectedRow, 0); + Object obj = node.getUserObject(); + + } + } + }); + + getTableHeader().setReorderingAllowed(false); + } + + public void setFilters(List filters) { + 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); + } + + private static class FiltersTreeCellRenderer extends DefaultTreeCellRenderer { + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + JLabel label = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + if (value instanceof DefaultMutableTreeNode) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; + Object object = node.getUserObject(); + label.setText(object.toString()); + } + if (View.isOceanic()) { + if (selected) { + label.setBackground(getBackgroundSelectionColor()); + } else { + label.setBackground(Color.white); + } + label.setOpaque(true); + } + return label; + } + } + + 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; + private final Field field; + + public FilterField(FILTER filter, Field property) { + this.filter = filter; + this.field = property; + } + + public FILTER getFilter() { + return filter; + } + + public Field getField() { + return field; + } + + public Object getValue() { + try { + return field.get(filter); + } catch (IllegalArgumentException | IllegalAccessException ex) { + return null; + } + } + + @Override + public String toString() { + return field.getName(); + } + + } + + private static class FiltersTreeTableModel implements TreeTableModel { + + private final DefaultMutableTreeNode root; + + public FiltersTreeTableModel(List filters) { + root = new DefaultMutableTreeNode("root"); + + if (filters == null) { + DefaultMutableTreeNode indeterminate = new DefaultMutableTreeNode(EasyStrings.translate("properties.instance.filters.indeterminate")); + root.add(indeterminate); + return; + } + + 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); + } + } + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return EasyStrings.translate("properties.instance.filters.header.property"); + case 1: + return EasyStrings.translate("properties.instance.filters.header.value"); + default: + return null; + } + } + + @Override + public Class getColumnClass(int column) { + switch (column) { + case 0: + return TreeTableModel.class; + default: + return String.class; + } + + } + + 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; + Object o = n.getUserObject(); + switch (column) { + case 0: + return node; + case 1: + if (o instanceof FilterField) { + FilterField filterField = (FilterField) o; + return valueToString(filterField.field.getName(), filterField.getValue()); + } else { + return ""; + } + default: + return null; + } + } + + @Override + public boolean isCellEditable(Object node, int column) { + if (column == 0) { + return true; + } + return false; + } + + @Override + public void setValueAt(Object value, Object node, int column) { + + } + + @Override + public Object getRoot() { + return root; + } + + @Override + public Object getChild(Object parent, int index) { + return ((DefaultMutableTreeNode) parent).getChildAt(index); + } + + @Override + public int getChildCount(Object parent) { + return ((DefaultMutableTreeNode) parent).getChildCount(); + } + + @Override + public boolean isLeaf(Object node) { + return ((DefaultMutableTreeNode) node).isLeaf(); + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + return ((DefaultMutableTreeNode) parent).getIndex((DefaultMutableTreeNode) child); + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + } + + } +} diff --git a/src/com/jpexs/decompiler/flash/easygui/properties/panels/AbstractPropertiesPanel.java b/src/com/jpexs/decompiler/flash/easygui/properties/panels/AbstractPropertiesPanel.java index 4b097c985..52c66dcf8 100644 --- a/src/com/jpexs/decompiler/flash/easygui/properties/panels/AbstractPropertiesPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/properties/panels/AbstractPropertiesPanel.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.easygui.properties.panels; import com.jpexs.decompiler.flash.easygui.EasyStrings; import com.jpexs.decompiler.flash.gui.View; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; @@ -30,7 +31,6 @@ import java.awt.event.MouseEvent; import java.util.LinkedHashMap; import java.util.Map; import javax.swing.BorderFactory; -import javax.swing.BoxLayout; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; @@ -49,6 +49,8 @@ public abstract class AbstractPropertiesPanel extends JPanel { private static final char PLUS_CHAR = '\u2BC8'; private static final char MINUS_CHAR = '\u2BC6'; + + private JPanel verticalFiller = new JPanel(); public AbstractPropertiesPanel(String titleIdentifier) { this.titleIdentifier = titleIdentifier; @@ -59,8 +61,8 @@ public abstract class AbstractPropertiesPanel extends JPanel { return EasyStrings.translate("property.label").replace("%item%", item); } - protected JPanel makeCard(String id, String icon, JPanel contents) { - JPanel cardPanel = new JPanel(); + protected void addCard(JPanel cardPanel, String id, String icon, JPanel contents, GridBagConstraints gbc, boolean last) { + //JPanel cardPanel = new JPanel(); JPanel headerPanel = new JPanel(new BorderLayout()); JLabel label = new JLabel(EasyStrings.translate("properties." + titleIdentifier + ".header." + id)); @@ -76,31 +78,54 @@ public abstract class AbstractPropertiesPanel extends JPanel { headerPanel.add(plusMinusLabel, BorderLayout.WEST); headerPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); headerPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30)); - headerPanel.setMinimumSize(new Dimension(0, 30)); + headerPanel.setMinimumSize(new Dimension(0, 30)); headerPanel.setAlignmentX(Component.LEFT_ALIGNMENT); - cardPanel.setLayout(new BoxLayout(cardPanel, BoxLayout.Y_AXIS)); + //cardPanel.setLayout(new BoxLayout(cardPanel, BoxLayout.Y_AXIS)); headerPanel.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { - setCardOpened(id, !isCardOpened(id)); + boolean newOpened = !isCardOpened(id); + if (last) { + verticalFiller.setVisible(!newOpened); + } + setCardOpened(id, newOpened); cardPanel.revalidate(); cardPanel.repaint(); } } }); - contents.setAlignmentX(Component.LEFT_ALIGNMENT); - contents.setVisible(false); - cardPanel.add(headerPanel); - cardPanel.add(contents); + //contents.setAlignmentX(Component.LEFT_ALIGNMENT); + contents.setVisible(false); + gbc.gridx = 0; + gbc.gridy++; + gbc.anchor = GridBagConstraints.FIRST_LINE_START; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1; + cardPanel.add(headerPanel, gbc); + gbc.gridx = 0; + gbc.gridy++; + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = last ? 1 : 0; + cardPanel.add(contents, gbc); contents.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + //contents.setBorder(BorderFactory.createLineBorder(Color.green, 5)); //contents.setMaximumSize(new Dimension(getPreferredSize().width, contents.getPreferredSize().height + 10)); - cardPanel.setAlignmentX(Component.CENTER_ALIGNMENT); - cardPanel.setAlignmentY(Component.TOP_ALIGNMENT); + //cardPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + //cardPanel.setAlignmentY(Component.TOP_ALIGNMENT); cardContents.put(id, contents); cardPlusMinusLabels.put(id, plusMinusLabel); - return cardPanel; + + if (last) { + gbc.gridy++; + gbc.weighty = 1; + gbc.fill = GridBagConstraints.BOTH; + cardPanel.add(verticalFiller, gbc); + } + + //cardPanel.setBorder(BorderFactory.createLineBorder(Color.red, 2)); + //return cardPanel; } private boolean isCardOpened(String id) { 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 3f47ce176..daf3a8350 100644 --- a/src/com/jpexs/decompiler/flash/easygui/properties/panels/InstancePropertiesPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/properties/panels/InstancePropertiesPanel.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.easygui.properties.panels; import com.jpexs.decompiler.flash.easygui.EasyStrings; import com.jpexs.decompiler.flash.easygui.EasySwfPanel; import com.jpexs.decompiler.flash.easygui.EasyTagNameResolver; +import com.jpexs.decompiler.flash.easygui.FiltersTreeTable; import com.jpexs.decompiler.flash.easygui.UndoManager; import com.jpexs.decompiler.flash.easygui.properties.FloatPropertyField; import com.jpexs.decompiler.flash.easygui.properties.IntegerPropertyField; @@ -36,9 +37,11 @@ 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.FILTER; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; @@ -55,9 +58,11 @@ import java.util.Set; 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.JPanel; +import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; import javax.swing.event.ChangeEvent; @@ -97,6 +102,8 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel { private final JPanel backgroundColorPanel = new JPanel(); private final JLabel backgroundColorLabel = new JLabel(); + private final FiltersTreeTable filtersTable; + private boolean updating = false; public InstancePropertiesPanel(EasySwfPanel swfPanel, UndoManager undoManager) { @@ -325,17 +332,44 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel { gbc.fill = GridBagConstraints.HORIZONTAL; displayPanel.add(new JPanel(), gbc); - propertiesPanel = new JPanel(); - propertiesPanel.setLayout(new BoxLayout(propertiesPanel, BoxLayout.Y_AXIS)); + JPanel filtersPanel = new JPanel(new BorderLayout()); + filtersTable = new FiltersTreeTable(); + 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)); - propertiesPanel.add(makeCard("positionSize", null, positionSizePanel)); + 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); - propertiesPanel.add(makeCard("colorEffect", null, colorEffectPanel)); - - propertiesPanel.add(makeCard("display", null, displayPanel)); - this.swfPanel = swfPanel; alphaPercentPropertyField.addChangeListener(new ChangeListener() { @@ -675,6 +709,7 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel { Set blendMode = new HashSet<>(); Set cacheAsBitmap = new HashSet<>(); Set backgroundColor = new HashSet<>(); + Set> filters = new HashSet<>(); for (DepthState ds : dss) { if (ds == null) { @@ -708,6 +743,7 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel { blendMode.add(bm); cacheAsBitmap.add(ds.cacheAsBitmap); backgroundColor.add(ds.backGroundColor); + filters.add(ds.filters); } if (visible.size() == 0) { @@ -789,6 +825,18 @@ 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); + } + } else if (filters.isEmpty()) { + filtersTable.setFilters(new ArrayList<>()); + } else { + filtersTable.setFilters(null); + } updating = false; revalidate(); } diff --git a/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties b/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties index da24f4395..aadb65db9 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/EasyPanel.properties @@ -117,4 +117,11 @@ property.instance.display.cacheAsBitmap.transparent = Transparent property.instance.display.cacheAsBitmap.opaque = Opaque #after 22.0.2 -property.document.backgroundColor = Background color \ No newline at end of file +property.document.backgroundColor = Background color + +properties.instance.header.filters = Filters + +properties.instance.filters.header.property = Property +properties.instance.filters.header.value = Value + +properties.instance.filters.indeterminate = Indeterminate \ No newline at end of file