From 7d3373f96ee418e9e4ea5787746e0532a7f75410 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sat, 16 May 2015 00:07:56 +0200 Subject: [PATCH] image tag refactorings/fixes --- .../flash/tags/DefineBitsJPEG2Tag.java | 88 +- .../flash/tags/DefineBitsJPEG3Tag.java | 116 +- .../flash/tags/DefineBitsJPEG4Tag.java | 157 +- .../flash/tags/DefineBitsLossless2Tag.java | 116 +- .../flash/tags/DefineBitsLosslessTag.java | 126 +- .../decompiler/flash/tags/DefineBitsTag.java | 53 +- .../decompiler/flash/tags/base/ImageTag.java | 15 + .../flash/gui/tagtree/TagTreeContextMenu.java | 1498 +++++++++-------- 8 files changed, 1069 insertions(+), 1100 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java index a19bee961..2b630492a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java @@ -45,20 +45,50 @@ public class DefineBitsJPEG2Tag extends ImageTag implements AloneTag { public static final String NAME = "DefineBitsJPEG2"; - @SWFType(BasicType.UI16) - public int characterID; - @SWFType(BasicType.UI8) public ByteArrayRange imageData; - @Override - public int getCharacterId() { - return characterID; + /** + * Constructor + * + * @param swf + */ + public DefineBitsJPEG2Tag(SWF swf) { + super(swf, ID, NAME, null); + characterID = swf.getNextCharacterId(); + imageData = ByteArrayRange.EMPTY; + forceWriteAsLong = true; + } + + public DefineBitsJPEG2Tag(SWF swf, ByteArrayRange data, int characterID, byte[] imageData) throws IOException { + super(swf, ID, NAME, data); + this.characterID = characterID; + this.imageData = new ByteArrayRange(imageData); + } + + public DefineBitsJPEG2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, NAME, data); + readData(sis, data, 0, false, false, false); } @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + characterID = sis.readUI16("characterID"); + imageData = sis.readByteRangeEx(sis.available(), "imageData"); + } + + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.write(imageData); + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); } @Override @@ -93,52 +123,10 @@ public class DefineBitsJPEG2Tag extends ImageTag implements AloneTag { return null; } - /** - * Constructor - * - * @param swf - */ - public DefineBitsJPEG2Tag(SWF swf) { - super(swf, ID, NAME, null); - characterID = swf.getNextCharacterId(); - imageData = ByteArrayRange.EMPTY; - } - - public DefineBitsJPEG2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, NAME, data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - characterID = sis.readUI16("characterID"); - imageData = sis.readByteRangeEx(sis.available(), "imageData"); - } - - public DefineBitsJPEG2Tag(SWF swf, ByteArrayRange data, int characterID, byte[] imageData) throws IOException { - super(swf, ID, NAME, data); - this.characterID = characterID; - this.imageData = new ByteArrayRange(imageData); - } - @Override public void setImage(byte[] data) { imageData = new ByteArrayRange(data); clearCache(); setModified(true); } - - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.write(imageData); - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java index 1e91a1541..f1dcdc372 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java @@ -46,23 +46,64 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { public static final String NAME = "DefineBitsJPEG3"; - @SWFType(BasicType.UI16) - public int characterID; - @SWFType(BasicType.UI8) public ByteArrayRange imageData; @SWFType(BasicType.UI8) - public byte[] bitmapAlphaData; + public ByteArrayRange bitmapAlphaData; - @Override - public int getCharacterId() { - return characterID; + /** + * Constructor + * + * @param swf + */ + public DefineBitsJPEG3Tag(SWF swf) { + super(swf, ID, NAME, null); + characterID = swf.getNextCharacterId(); + imageData = new ByteArrayRange(createEmptyImage()); + bitmapAlphaData = ByteArrayRange.EMPTY; + forceWriteAsLong = true; + } + + /** + * Constructor + * + * @param sis + * @param data + * @throws IOException + */ + public DefineBitsJPEG3Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, NAME, data); + readData(sis, data, 0, false, false, false); } @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + characterID = sis.readUI16("characterID"); + long alphaDataOffset = sis.readUI32("alphaDataOffset"); + imageData = sis.readByteRangeEx(alphaDataOffset, "imageData"); + bitmapAlphaData = sis.readByteRangeEx(sis.available(), "bitmapAlphaData"); + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.writeUI32(imageData.getLength()); + sos.write(imageData); + sos.write(bitmapAlphaData); + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); } private byte[] createEmptyImage() { @@ -80,10 +121,12 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { for (int i = 0; i < ba.length; i++) { ba[i] = (byte) 255; } - bitmapAlphaData = ba; + + bitmapAlphaData = new ByteArrayRange(SWFOutputStream.compressByteArray(ba)); } else { - bitmapAlphaData = new byte[0]; + bitmapAlphaData = ByteArrayRange.EMPTY; } + imageData = new ByteArrayRange(data); clearCache(); setModified(true); @@ -117,14 +160,15 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { } SerializableImage img = new SerializableImage(image); - if (bitmapAlphaData.length == 0) { + if (bitmapAlphaData.getLength() == 0) { cachedImage = img; return img; } + byte[] alphaData = SWFInputStream.uncompressByteArray(bitmapAlphaData.getRangeData()); int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); for (int i = 0; i < pixels.length; i++) { - int a = bitmapAlphaData[i] & 0xff; + int a = alphaData[i] & 0xff; pixels[i] = multiplyAlpha((pixels[i] & 0xffffff) | (a << 24)); } @@ -135,50 +179,4 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { } return null; } - - /** - * Constructor - * - * @param swf - */ - public DefineBitsJPEG3Tag(SWF swf) { - super(swf, ID, NAME, null); - characterID = swf.getNextCharacterId(); - imageData = ByteArrayRange.EMPTY; - bitmapAlphaData = new byte[0]; - } - - public DefineBitsJPEG3Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, NAME, data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - characterID = sis.readUI16("characterID"); - long alphaDataOffset = sis.readUI32("alphaDataOffset"); - imageData = sis.readByteRangeEx(alphaDataOffset, "imageData"); - bitmapAlphaData = sis.readBytesZlib(sis.available(), "bitmapAlphaData"); - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.writeUI32(imageData.getLength()); - sos.write(imageData); - sos.writeBytesZlib(bitmapAlphaData); - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java index 6adcb3f80..3b6aeb4f2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java @@ -46,9 +46,6 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { public static final String NAME = "DefineBitsJPEG4"; - @SWFType(BasicType.UI16) - public int characterID; - @SWFType(BasicType.UI16) public int deblockParam; @@ -58,86 +55,6 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { @SWFType(BasicType.UI8) public ByteArrayRange bitmapAlphaData; - @Override - public int getCharacterId() { - return characterID; - } - - @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; - } - - @Override - public String getImageFormat() { - String fmt = ImageTag.getImageFormat(imageData); - if (fmt.equals("jpg")) { - fmt = "png"; //transparency - } - return fmt; - } - - private byte[] createEmptyImage() { - BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); - ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); - ImageHelper.write(img, "JPG", bitmapDataOS); - return bitmapDataOS.toByteArray(); - } - - @Override - public void setImage(byte[] data) { - imageData = new ByteArrayRange(data); - if (ImageTag.getImageFormat(data).equals("jpg")) { - SerializableImage image = getImage(); - byte[] ba = new byte[image.getWidth() * image.getHeight()]; - for (int i = 0; i < ba.length; i++) { - ba[i] = (byte) 255; - } - bitmapAlphaData = new ByteArrayRange(ba); - } else { - bitmapAlphaData = ByteArrayRange.EMPTY; - } - clearCache(); - setModified(true); - } - - @Override - public InputStream getImageData() { - return new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength()); - } - - @Override - public SerializableImage getImage() { - if (cachedImage != null) { - return cachedImage; - } - try { - BufferedImage image = ImageHelper.read(getImageData()); - if (image == null) { - Logger.getLogger(DefineBitsJPEG4Tag.class.getName()).log(Level.SEVERE, "Failed to load image"); - return null; - } - - SerializableImage img = new SerializableImage(image); - if (bitmapAlphaData.getLength() == 0) { - cachedImage = img; - return img; - } - - int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); - for (int i = 0; i < pixels.length; i++) { - int a = bitmapAlphaData.get(i) & 0xff; - pixels[i] = multiplyAlpha((pixels[i] & 0xffffff) | (a << 24)); - } - - cachedImage = img; - return img; - } catch (IOException ex) { - Logger.getLogger(DefineBitsJPEG4Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex); - } - return null; - } - /** * Constructor * @@ -148,6 +65,7 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { characterID = swf.getNextCharacterId(); imageData = new ByteArrayRange(createEmptyImage()); bitmapAlphaData = ByteArrayRange.EMPTY; + forceWriteAsLong = true; } /** @@ -192,4 +110,77 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { } return baos.toByteArray(); } + + private byte[] createEmptyImage() { + BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); + ImageHelper.write(img, "JPG", bitmapDataOS); + return bitmapDataOS.toByteArray(); + } + + @Override + public void setImage(byte[] data) throws IOException { + if (ImageTag.getImageFormat(data).equals("jpg")) { + SerializableImage image = new SerializableImage(ImageHelper.read(data)); + byte[] ba = new byte[image.getWidth() * image.getHeight()]; + for (int i = 0; i < ba.length; i++) { + ba[i] = (byte) 255; + } + + bitmapAlphaData = new ByteArrayRange(SWFOutputStream.compressByteArray(ba)); + } else { + bitmapAlphaData = ByteArrayRange.EMPTY; + } + + imageData = new ByteArrayRange(data); + clearCache(); + setModified(true); + } + + @Override + public String getImageFormat() { + String fmt = ImageTag.getImageFormat(imageData); + if (fmt.equals("jpg")) { + fmt = "png"; //transparency + } + return fmt; + } + + @Override + public InputStream getImageData() { + return new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength()); + } + + @Override + public SerializableImage getImage() { + if (cachedImage != null) { + return cachedImage; + } + try { + BufferedImage image = ImageHelper.read(getImageData()); + if (image == null) { + Logger.getLogger(DefineBitsJPEG4Tag.class.getName()).log(Level.SEVERE, "Failed to load image"); + return null; + } + + SerializableImage img = new SerializableImage(image); + if (bitmapAlphaData.getLength() == 0) { + cachedImage = img; + return img; + } + + byte[] alphaData = SWFInputStream.uncompressByteArray(bitmapAlphaData.getRangeData()); + int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); + for (int i = 0; i < pixels.length; i++) { + int a = alphaData[i] & 0xff; + pixels[i] = multiplyAlpha((pixels[i] & 0xffffff) | (a << 24)); + } + + cachedImage = img; + return img; + } catch (IOException ex) { + Logger.getLogger(DefineBitsJPEG4Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex); + } + return null; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLossless2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLossless2Tag.java index 0a4fa9302..e54430950 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLossless2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLossless2Tag.java @@ -52,9 +52,6 @@ public class DefineBitsLossless2Tag extends ImageTag implements AloneTag { public static final String NAME = "DefineBitsLossless2"; - @SWFType(BasicType.UI16) - public int characterID; - @SWFType(BasicType.UI8) public int bitmapFormat; @@ -83,14 +80,61 @@ public class DefineBitsLossless2Tag extends ImageTag implements AloneTag { @Internal private boolean decompressed = false; - @Override - public int getCharacterId() { - return characterID; + /** + * Constructor + * + * @param swf + */ + public DefineBitsLossless2Tag(SWF swf) { + super(swf, ID, NAME, null); + characterID = swf.getNextCharacterId(); + bitmapFormat = DefineBitsLossless2Tag.FORMAT_32BIT_ARGB; + bitmapWidth = 1; + bitmapHeight = 1; + zlibBitmapData = new ByteArrayRange(createEmptyImage()); + forceWriteAsLong = true; + } + + public DefineBitsLossless2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, NAME, data); + readData(sis, data, 0, false, false, false); } @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + characterID = sis.readUI16("characterID"); + bitmapFormat = sis.readUI8("bitmapFormat"); + bitmapWidth = sis.readUI16("bitmapWidth"); + bitmapHeight = sis.readUI16("bitmapHeight"); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + bitmapColorTableSize = sis.readUI8("bitmapColorTableSize"); + } + zlibBitmapData = sis.readByteRangeEx(sis.available(), "zlibBitmapData"); + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.writeUI8(bitmapFormat); + sos.writeUI16(bitmapWidth); + sos.writeUI16(bitmapHeight); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + sos.writeUI8(bitmapColorTableSize); + } + sos.write(zlibBitmapData); + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); } private byte[] createEmptyImage() { @@ -148,37 +192,6 @@ public class DefineBitsLossless2Tag extends ImageTag implements AloneTag { setModified(true); } - /** - * Constructor - * - * @param swf - */ - public DefineBitsLossless2Tag(SWF swf) { - super(swf, ID, NAME, null); - characterID = swf.getNextCharacterId(); - bitmapFormat = DefineBitsLossless2Tag.FORMAT_32BIT_ARGB; - bitmapWidth = 1; - bitmapHeight = 1; - zlibBitmapData = new ByteArrayRange(createEmptyImage()); - } - - public DefineBitsLossless2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, NAME, data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - characterID = sis.readUI16("characterID"); - bitmapFormat = sis.readUI8("bitmapFormat"); - bitmapWidth = sis.readUI16("bitmapWidth"); - bitmapHeight = sis.readUI16("bitmapHeight"); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - bitmapColorTableSize = sis.readUI8("bitmapColorTableSize"); - } - zlibBitmapData = sis.readByteRangeEx(sis.available(), "zlibBitmapData"); - } - public ALPHACOLORMAPDATA getColorMapData() { if (!decompressed) { uncompressData(); @@ -207,31 +220,6 @@ public class DefineBitsLossless2Tag extends ImageTag implements AloneTag { decompressed = true; } - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.writeUI8(bitmapFormat); - sos.writeUI16(bitmapWidth); - sos.writeUI16(bitmapHeight); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - sos.writeUI8(bitmapColorTableSize); - } - sos.write(zlibBitmapData); - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } - @Override public String getImageFormat() { return "png"; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java index b1df0f32a..05549c9db 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java @@ -52,9 +52,6 @@ public class DefineBitsLosslessTag extends ImageTag implements AloneTag { public static final String NAME = "DefineBitsLossless"; - @SWFType(BasicType.UI16) - public int characterID; - @SWFType(BasicType.UI8) public int bitmapFormat; @@ -85,6 +82,63 @@ public class DefineBitsLosslessTag extends ImageTag implements AloneTag { @Internal private boolean decompressed = false; + /** + * Constructor + * + * @param swf + */ + public DefineBitsLosslessTag(SWF swf) { + super(swf, ID, NAME, null); + characterID = swf.getNextCharacterId(); + bitmapFormat = DefineBitsLosslessTag.FORMAT_24BIT_RGB; + bitmapWidth = 1; + bitmapHeight = 1; + zlibBitmapData = new ByteArrayRange(createEmptyImage()); + forceWriteAsLong = true; + } + + public DefineBitsLosslessTag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, NAME, data); + readData(sis, data, 0, false, false, false); + } + + @Override + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + characterID = sis.readUI16("characterID"); + bitmapFormat = sis.readUI8("bitmapFormat"); + bitmapWidth = sis.readUI16("bitmapWidth"); + bitmapHeight = sis.readUI16("bitmapHeight"); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + bitmapColorTableSize = sis.readUI8("bitmapColorTableSize"); + } + zlibBitmapData = sis.readByteRangeEx(sis.available(), "zlibBitmapData"); + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.writeUI8(bitmapFormat); + sos.writeUI16(bitmapWidth); + sos.writeUI16(bitmapHeight); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + sos.writeUI8(bitmapColorTableSize); + } + sos.write(zlibBitmapData); + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); + } + private byte[] createEmptyImage() { try { BITMAPDATA bitmapData = new BITMAPDATA(); @@ -188,16 +242,6 @@ public class DefineBitsLosslessTag extends ImageTag implements AloneTag { return bi; } - @Override - public int getCharacterId() { - return characterID; - } - - @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; - } - public COLORMAPDATA getColorMapData() { if (!decompressed) { uncompressData(); @@ -226,62 +270,6 @@ public class DefineBitsLosslessTag extends ImageTag implements AloneTag { decompressed = true; } - /** - * Constructor - * - * @param swf - */ - public DefineBitsLosslessTag(SWF swf) { - super(swf, ID, NAME, null); - characterID = swf.getNextCharacterId(); - bitmapFormat = DefineBitsLosslessTag.FORMAT_24BIT_RGB; - bitmapWidth = 1; - bitmapHeight = 1; - zlibBitmapData = new ByteArrayRange(createEmptyImage()); - } - - public DefineBitsLosslessTag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, NAME, data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - characterID = sis.readUI16("characterID"); - bitmapFormat = sis.readUI8("bitmapFormat"); - bitmapWidth = sis.readUI16("bitmapWidth"); - bitmapHeight = sis.readUI16("bitmapHeight"); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - bitmapColorTableSize = sis.readUI8("bitmapColorTableSize"); - } - zlibBitmapData = sis.readByteRangeEx(sis.available(), "zlibBitmapData"); - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.writeUI8(bitmapFormat); - sos.writeUI16(bitmapWidth); - sos.writeUI16(bitmapHeight); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - sos.writeUI8(bitmapColorTableSize); - } - sos.write(zlibBitmapData); - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } - @Override public String getImageFormat() { return "png"; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java index a75c4ef5a..ed9d8159e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java @@ -43,9 +43,6 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener { public static final String NAME = "DefineBits"; - @SWFType(BasicType.UI16) - public int characterID; - @SWFType(BasicType.UI8) public ByteArrayRange jpegData; @@ -69,6 +66,7 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener { super(swf, ID, NAME, null); characterID = swf.getNextCharacterId(); jpegData = ByteArrayRange.EMPTY; + forceWriteAsLong = true; } public DefineBitsTag(SWFInputStream sis, ByteArrayRange data) throws IOException { @@ -82,6 +80,25 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener { jpegData = sis.readByteRangeEx(sis.available(), "jpegData"); } + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.write(jpegData); + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); + } + @Override public InputStream getImageData() { return null; @@ -119,35 +136,6 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener { return null; } - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.write(jpegData); - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } - - @Override - public int getCharacterId() { - return characterID; - } - - @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; - } - @Override public String getImageFormat() { return "jpg"; @@ -155,6 +143,7 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener { @Override public void handleEvent(Tag tag) { + // cache should be cleared when Jtt tag changes clearCache(); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java index 26085fde1..487038dbd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ImageTag.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; +import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.FILLSTYLE; import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; @@ -31,6 +32,7 @@ import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; @@ -48,6 +50,9 @@ import java.util.Set; */ public abstract class ImageTag extends CharacterTag implements DrawableTag { + @SWFType(BasicType.UI16) + public int characterID; + protected SerializableImage cachedImage; public ImageTag(SWF swf, int id, String name, ByteArrayRange data) { @@ -239,4 +244,14 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag { public void clearCache() { cachedImage = null; } + + @Override + public int getCharacterId() { + return characterID; + } + + @Override + public void setCharacterId(int characterId) { + this.characterID = characterId; + } } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 428230a99..e660f8f66 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -1,743 +1,755 @@ -/* - * Copyright (C) 2010-2015 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.tagtree; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.gui.AppDialog; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.MainPanel; -import com.jpexs.decompiler.flash.gui.ReplaceCharacterDialog; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.FolderItem; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.helpers.Helper; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPopupMenu; -import javax.swing.SwingUtilities; -import javax.swing.tree.TreePath; - -/** - * - * @author JPEXS - */ -public class TagTreeContextMenu extends JPopupMenu { - - private static final Logger logger = Logger.getLogger(TagTreeContextMenu.class.getName()); - - private final MainPanel mainPanel; - - private final TagTree tagTree; - - private JMenuItem expandRecursiveMenuItem; - - private JMenuItem removeMenuItem; - - private JMenuItem removeWithDependenciesMenuItem; - - private JMenuItem undoTagMenuItem; - - private JMenuItem exportSelectionMenuItem; - - private JMenuItem replaceMenuItem; - - private JMenuItem replaceWithTagMenuItem; - - private JMenuItem rawEditMenuItem; - - private JMenuItem jumpToCharacterMenuItem; - - private JMenuItem exportJavaSourceMenuItem; - - private JMenuItem exportSwfXmlMenuItem; - - private JMenuItem importSwfXmlMenuItem; - - private JMenuItem closeMenuItem; - - private JMenu addTagMenu; - - private JMenu moveTagMenu; - - private JMenu copyTagMenu; - - private JMenu copyTagWithDependenciesMenu; - - private JMenuItem openSWFInsideTagMenuItem; - - public TagTreeContextMenu(final TagTree tagTree, MainPanel mainPanel) { - this.mainPanel = mainPanel; - this.tagTree = tagTree; - expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll")); - expandRecursiveMenuItem.addActionListener(this::expandRecursiveActionPerformed); - add(expandRecursiveMenuItem); - - removeMenuItem = new JMenuItem(mainPanel.translate("contextmenu.remove")); - removeMenuItem.addActionListener((ActionEvent e) -> { - removeItemActionPerformed(e, false); - }); - add(removeMenuItem); - - removeWithDependenciesMenuItem = new JMenuItem(mainPanel.translate("contextmenu.removeWithDependencies")); - removeWithDependenciesMenuItem.addActionListener((ActionEvent e) -> { - removeItemActionPerformed(e, true); - }); - add(removeWithDependenciesMenuItem); - - undoTagMenuItem = new JMenuItem(mainPanel.translate("contextmenu.undo")); - undoTagMenuItem.addActionListener(this::undoTagActionPerformed); - add(undoTagMenuItem); - - exportSelectionMenuItem = new JMenuItem(mainPanel.translate("menu.file.export.selection")); - exportSelectionMenuItem.addActionListener(mainPanel::exportSelectionActionPerformed); - add(exportSelectionMenuItem); - - replaceMenuItem = new JMenuItem(mainPanel.translate("button.replace")); - replaceMenuItem.addActionListener(mainPanel::replaceButtonActionPerformed); - add(replaceMenuItem); - - replaceWithTagMenuItem = new JMenuItem(mainPanel.translate("button.replaceWithTag")); - replaceWithTagMenuItem.addActionListener(this::replaceWithTagActionPerformed); - add(replaceWithTagMenuItem); - - rawEditMenuItem = new JMenuItem(mainPanel.translate("contextmenu.rawEdit")); - rawEditMenuItem.addActionListener(this::rawEditActionPerformed); - add(rawEditMenuItem); - - jumpToCharacterMenuItem = new JMenuItem(mainPanel.translate("contextmenu.jumpToCharacter")); - jumpToCharacterMenuItem.addActionListener(this::jumpToCharacterActionPerformed); - add(jumpToCharacterMenuItem); - - exportJavaSourceMenuItem = new JMenuItem(mainPanel.translate("contextmenu.exportJavaSource")); - exportJavaSourceMenuItem.addActionListener(mainPanel::exportJavaSourceActionPerformed); - add(exportJavaSourceMenuItem); - - exportSwfXmlMenuItem = new JMenuItem(mainPanel.translate("contextmenu.exportSwfXml")); - exportSwfXmlMenuItem.addActionListener(mainPanel::exportSwfXmlActionPerformed); - add(exportSwfXmlMenuItem); - - importSwfXmlMenuItem = new JMenuItem(mainPanel.translate("contextmenu.importSwfXml")); - importSwfXmlMenuItem.addActionListener(mainPanel::importSwfXmlActionPerformed); - add(importSwfXmlMenuItem); - - closeMenuItem = new JMenuItem(mainPanel.translate("contextmenu.closeSwf")); - closeMenuItem.addActionListener(this::closeSwfActionPerformed); - add(closeMenuItem); - - addTagMenu = new JMenu(mainPanel.translate("contextmenu.addTag")); - add(addTagMenu); - - moveTagMenu = new JMenu(mainPanel.translate("contextmenu.moveTag")); - add(moveTagMenu); - - copyTagMenu = new JMenu(mainPanel.translate("contextmenu.copyTag")); - add(copyTagMenu); - - copyTagWithDependenciesMenu = new JMenu(mainPanel.translate("contextmenu.copyTagWithDependencies")); - add(copyTagWithDependenciesMenu); - - openSWFInsideTagMenuItem = new JMenuItem(mainPanel.translate("contextmenu.openswfinside")); - add(openSWFInsideTagMenuItem); - openSWFInsideTagMenuItem.addActionListener(this::openSwfInsideActionPerformed); - - tagTree.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { - - int row = tagTree.getClosestRowForLocation(e.getX(), e.getY()); - int[] selectionRows = tagTree.getSelectionRows(); - if (!Helper.contains(selectionRows, row)) { - tagTree.setSelectionRow(row); - } - - TreePath[] paths = tagTree.getSelectionPaths(); - if (paths == null || paths.length == 0) { - return; - } - - List li = new ArrayList<>(); - for (TreePath treePath : paths) { - TreeItem item = (TreeItem) treePath.getLastPathComponent(); - li.add(item); - } - - update(li); - show(e.getComponent(), e.getX(), e.getY()); - } - } - }); - } - - public void update(final List items) { - - if (items.isEmpty()) { - return; - } - - final List swfs = mainPanel.getSwfs(); - - boolean allSelectedIsTagOrFrame = true; - for (TreeItem item : items) { - if (!(item instanceof Tag) && !(item instanceof Frame)) { - if (item instanceof TagScript) { - Tag tag = ((TagScript) item).getTag(); - if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { - continue; - } - } - - allSelectedIsTagOrFrame = false; - break; - } - } - - boolean allSelectedIsTag = true; - for (TreeItem item : items) { - if (!(item instanceof Tag)) { - if (item instanceof TagScript) { - Tag tag = ((TagScript) item).getTag(); - if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { - continue; - } - } - - allSelectedIsTag = false; - break; - } - } - - boolean allSelectedIsBinaryData = true; - for (TreeItem item : items) { - if (!(item instanceof DefineBinaryDataTag)) { - allSelectedIsBinaryData = false; - break; - } - } - - boolean allSelectedIsSwf = true; - for (TreeItem item : items) { - if (!(item instanceof SWF) && !(item instanceof SWFList)) { - allSelectedIsSwf = false; - break; - } else if (item instanceof SWF) { - SWF swf = (SWF) item; - // Do not allow to close SWF in bundle - if (swf.swfList != null && swf.swfList.isBundle()) { - allSelectedIsSwf = false; - } - } - } - - boolean allSelectedIsInTheSameSwf = true; - SWF singleSwf = null; - for (TreeItem item : items) { - if (item instanceof SWFList) { - allSelectedIsInTheSameSwf = false; - break; - } - if (singleSwf == null) { - singleSwf = item.getSwf(); - } else { - if (singleSwf != item.getSwf()) { - allSelectedIsInTheSameSwf = false; - break; - } - } - } - - expandRecursiveMenuItem.setVisible(false); - removeMenuItem.setVisible(allSelectedIsTagOrFrame); - removeWithDependenciesMenuItem.setVisible(allSelectedIsTagOrFrame); - undoTagMenuItem.setVisible(allSelectedIsTag); - exportSelectionMenuItem.setEnabled(tagTree.hasExportableNodes()); - replaceMenuItem.setVisible(false); - replaceWithTagMenuItem.setVisible(false); - rawEditMenuItem.setVisible(false); - jumpToCharacterMenuItem.setVisible(false); - exportJavaSourceMenuItem.setVisible(allSelectedIsSwf); - exportSwfXmlMenuItem.setVisible(allSelectedIsSwf); - importSwfXmlMenuItem.setVisible(allSelectedIsSwf); - closeMenuItem.setVisible(allSelectedIsSwf); - addTagMenu.setVisible(false); - moveTagMenu.setVisible(false); - copyTagMenu.setVisible(false); - copyTagWithDependenciesMenu.setVisible(false); - openSWFInsideTagMenuItem.setVisible(false); - - boolean singleSelect = items.size() == 1; - - if (singleSelect) { - final TreeItem firstItem = items.get(0); - - if (firstItem instanceof CharacterTag) { - replaceWithTagMenuItem.setVisible(true); - } - - // replace - if (firstItem instanceof ImageTag && ((ImageTag) firstItem).importSupported()) { - replaceMenuItem.setVisible(true); - } - - if (firstItem instanceof ShapeTag) { - replaceMenuItem.setVisible(true); - } - - if (firstItem instanceof DefineBinaryDataTag) { - replaceMenuItem.setVisible(true); - } - - if (firstItem instanceof DefineSoundTag) { - replaceMenuItem.setVisible(true); - } - - List allowedTagTypes = null; - if (firstItem instanceof FolderItem) { - allowedTagTypes = tagTree.getSwfFolderItemNestedTagIds(((FolderItem) firstItem).getName(), firstItem.getSwf().gfx); - } else if (firstItem instanceof Tag) { - allowedTagTypes = tagTree.getNestedTagIds((Tag) firstItem); - } - - addTagMenu.removeAll(); - if (allowedTagTypes != null) { - for (Integer tagId : allowedTagTypes) { - final Class cl = TagIdClassMap.getClassByTagId(tagId); - JMenuItem tagItem = new JMenuItem(cl.getSimpleName()); - tagItem.addActionListener((ActionEvent ae) -> { - addTagActionPerformed(ae, firstItem, cl); - }); - addTagMenu.add(tagItem); - } - addTagMenu.setVisible(true); - } - - if (tagTree.getModel().getChildCount(firstItem) > 0) { - expandRecursiveMenuItem.setVisible(true); - } - - if (firstItem instanceof CharacterIdTag && !(firstItem instanceof CharacterTag)) { - jumpToCharacterMenuItem.setVisible(true); - } - - if (firstItem instanceof Tag) { - rawEditMenuItem.setVisible(firstItem instanceof Tag); - } - } - - if (allSelectedIsInTheSameSwf && allSelectedIsTag && swfs.size() > 1) { - moveTagMenu.removeAll(); - copyTagMenu.removeAll(); - copyTagWithDependenciesMenu.removeAll(); - for (SWFList targetSwfList : swfs) { - for (final SWF targetSwf : targetSwfList) { - if (targetSwf != singleSwf) { - JMenuItem swfItem = new JMenuItem(targetSwf.getShortFileName()); - swfItem.addActionListener((ActionEvent ae) -> { - moveTagActionPerformed(ae, items, targetSwf); - }); - moveTagMenu.add(swfItem); - - swfItem = new JMenuItem(targetSwf.getShortFileName()); - swfItem.addActionListener((ActionEvent ae) -> { - copyTagActionPerformed(ae, items, targetSwf); - }); - copyTagMenu.add(swfItem); - - swfItem = new JMenuItem(targetSwf.getShortFileName()); - swfItem.addActionListener((ActionEvent ae) -> { - copyTagWithDependenciesActionPerformed(ae, items, targetSwf); - }); - copyTagWithDependenciesMenu.add(swfItem); - } - } - } - moveTagMenu.setVisible(true); - copyTagMenu.setVisible(true); - copyTagWithDependenciesMenu.setVisible(true); - } - - if (allSelectedIsBinaryData) { - boolean anyInnerSwf = false; - for (TreeItem item : items) { - DefineBinaryDataTag binary = (DefineBinaryDataTag) item; - - // inner swf is not loaded yet - if (binary.innerSwf == null && binary.isSwfData()) { - anyInnerSwf = true; - } - } - - openSWFInsideTagMenuItem.setVisible(anyInnerSwf); - } - } - - private void addTagActionPerformed(ActionEvent evt, TreeItem firstItem, Class cl) { - try { - SWF swf = firstItem.getSwf(); - Tag t = (Tag) cl.getDeclaredConstructor(SWF.class).newInstance(new Object[]{swf}); - boolean isDefineSprite = firstItem instanceof DefineSpriteTag; - Timelined timelined = isDefineSprite ? (DefineSpriteTag) firstItem : swf; - t.setTimelined(timelined); - if (isDefineSprite) { - ((DefineSpriteTag) firstItem).subTags.add(t); - } else { - if (firstItem instanceof Tag) { - if ((t instanceof CharacterIdTag) && (firstItem instanceof CharacterTag)) { - ((CharacterIdTag) t).setCharacterId(((CharacterTag) firstItem).getCharacterId()); - } - int index = swf.tags.indexOf(firstItem); - if (index > -1) { - swf.tags.add(index, t); - } else { - swf.tags.add(t); - } - } else { - swf.tags.add(t); - } - } - timelined.resetTimeline(); - swf.updateCharacters(); - mainPanel.refreshTree(swf); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - private int chechUniqueCharacterId(Tag tag) { - if (tag instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) tag; - int characterId = characterTag.getCharacterId(); - SWF swf = tag.getSwf(); - if (swf.getCharacter(characterId) != null) { - int newCharacterId = swf.getNextCharacterId(); - characterTag.setCharacterId(newCharacterId); - logger.log(Level.WARNING, "Target SWF already contained chatacter tag with id = {0} => id changed to {1}", new Object[]{characterId, newCharacterId}); - return newCharacterId; - } - - return characterId; - } - - return -1; - } - - private void moveTagActionPerformed(ActionEvent evt, List items, SWF targetSwf) { - SWF sourceSwf = items.get(0).getSwf(); - for (TreeItem item : items) { - Tag tag = (Tag) item; - sourceSwf.tags.remove(tag); - tag.setSwf(targetSwf, true); - targetSwf.tags.add(tag); - chechUniqueCharacterId(tag); - targetSwf.updateCharacters(); - tag.setModified(true); - } - - sourceSwf.assignExportNamesToSymbols(); - targetSwf.assignExportNamesToSymbols(); - sourceSwf.assignClassesToSymbols(); - targetSwf.assignClassesToSymbols(); - sourceSwf.clearImageCache(); - targetSwf.clearImageCache(); - sourceSwf.updateCharacters(); - targetSwf.updateCharacters(); - sourceSwf.resetTimelines(sourceSwf); - targetSwf.resetTimelines(targetSwf); - mainPanel.refreshTree(new SWF[]{sourceSwf, targetSwf}); - } - - private void copyTagActionPerformed(ActionEvent evt, List items, SWF targetSwf) { - try { - for (TreeItem item : items) { - Tag tag = (Tag) item; - Tag copyTag = tag.cloneTag(); - copyTag.setSwf(targetSwf, true); - targetSwf.tags.add(copyTag); - chechUniqueCharacterId(copyTag); - targetSwf.updateCharacters(); - copyTag.setModified(true); - } - - targetSwf.assignExportNamesToSymbols(); - targetSwf.assignClassesToSymbols(); - targetSwf.clearImageCache(); - targetSwf.updateCharacters(); - mainPanel.refreshTree(targetSwf); - } catch (IOException | InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - private void copyTagWithDependenciesActionPerformed(ActionEvent evt, List items, SWF targetSwf) { - try { - SWF sourceSwf = items.get(0).getSwf(); - for (TreeItem item : items) { - Set copiedTags = new HashSet<>(); - Set newTags = new HashSet<>(); - LinkedHashSet needed = new LinkedHashSet<>(); - Map changedCharacterIds = new HashMap<>(); - - Tag tag = (Tag) item; - tag.getNeededCharactersDeep(needed); - Tag copyTag; - - List neededList = new ArrayList<>(); - for (Integer characterId : needed) { - neededList.add(characterId); - } - - // first add dependencies in reverse order - for (int i = neededList.size() - 1; i >= 0; i--) { - int characterId = neededList.get(i); - Tag neededTag = (Tag) sourceSwf.getCharacter(characterId); - if (!copiedTags.contains(neededTag)) { - copyTag = neededTag.cloneTag(); - copyTag.setSwf(targetSwf, true); - targetSwf.tags.add(copyTag); - if (neededTag instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) copyTag; - int oldCharacterId = characterTag.getCharacterId(); - int newCharacterId = chechUniqueCharacterId(copyTag); - changedCharacterIds.put(oldCharacterId, newCharacterId); - } - - targetSwf.updateCharacters(); - targetSwf.getCharacters(); // force rebuild character id cache - copyTag.setModified(true); - copiedTags.add(neededTag); - newTags.add(copyTag); - } - } - - copyTag = tag.cloneTag(); - copyTag.setSwf(targetSwf, true); - targetSwf.tags.add(copyTag); - if (tag instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) copyTag; - int oldCharacterId = characterTag.getCharacterId(); - int newCharacterId = chechUniqueCharacterId(copyTag); - changedCharacterIds.put(oldCharacterId, newCharacterId); - } - - targetSwf.updateCharacters(); - targetSwf.getCharacters(); // force rebuild character id cache - copyTag.setModified(true); - copiedTags.add(tag); - newTags.add(copyTag); - - for (int oldCharacterId : changedCharacterIds.keySet()) { - int newCharacterId = changedCharacterIds.get(oldCharacterId); - for (Tag newTag : newTags) { - // todo: avoid double replaces - newTag.replaceCharacter(oldCharacterId, newCharacterId); - } - } - } - - targetSwf.assignExportNamesToSymbols(); - targetSwf.assignClassesToSymbols(); - targetSwf.clearImageCache(); - targetSwf.updateCharacters(); - mainPanel.refreshTree(targetSwf); - } catch (IOException | InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - private void openSwfInsideActionPerformed(ActionEvent evt) { - List sel = tagTree.getSelected(tagTree); - List binaryDatas = new ArrayList<>(); - for (TreeItem item : sel) { - DefineBinaryDataTag binaryData = (DefineBinaryDataTag) item; - if (binaryData.isSwfData()) { - binaryDatas.add((DefineBinaryDataTag) item); - } - } - - mainPanel.loadFromBinaryTag(binaryDatas); - } - - private void replaceWithTagActionPerformed(ActionEvent evt) { - TreeItem itemr = tagTree.getCurrentTreeItem(); - if (itemr == null) { - return; - } - - SWF swf = itemr.getSwf(); - CharacterTag characterTag = (CharacterTag) itemr; - int characterId = characterTag.getCharacterId(); - ReplaceCharacterDialog replaceCharacterDialog = new ReplaceCharacterDialog(); - if (replaceCharacterDialog.showDialog(swf, characterId) == AppDialog.OK_OPTION) { - int newCharacterId = replaceCharacterDialog.getCharacterId(); - CharacterTag newCharacter = swf.getCharacter(newCharacterId); - newCharacter.setCharacterId(characterId); - characterTag.setCharacterId(newCharacterId); - newCharacter.setModified(true); - characterTag.setModified(true); - - swf.assignExportNamesToSymbols(); - swf.assignClassesToSymbols(); - swf.clearImageCache(); - swf.updateCharacters(); - mainPanel.refreshTree(swf); - } - } - - private void rawEditActionPerformed(ActionEvent evt) { - TreeItem itemr = tagTree.getCurrentTreeItem(); - if (itemr == null) { - return; - } - - mainPanel.showGenericTag((Tag) itemr); - } - - private void jumpToCharacterActionPerformed(ActionEvent evt) { - TreeItem itemj = tagTree.getCurrentTreeItem(); - if (itemj == null || !(itemj instanceof CharacterIdTag)) { - return; - } - - CharacterIdTag characterIdTag = (CharacterIdTag) itemj; - mainPanel.setTagTreeSelectedNode(itemj.getSwf().getCharacter(characterIdTag.getCharacterId())); - } - - private void expandRecursiveActionPerformed(ActionEvent evt) { - TreePath path = tagTree.getSelectionPath(); - if (path == null) { - return; - } - View.expandTreeNodes(tagTree, path, true); - } - - private void removeItemActionPerformed(ActionEvent evt, boolean removeDependencies) { - List sel = tagTree.getSelected(tagTree); - - List tagsToRemove = new ArrayList<>(); - for (TreeItem item : sel) { - if (item instanceof Tag) { - tagsToRemove.add((Tag) item); - } else if (item instanceof TagScript) { - tagsToRemove.add(((TagScript) item).getTag()); - } else if (item instanceof Frame) { - Frame frameNode = (Frame) item; - Frame frame = frameNode.timeline.getFrame(frameNode.frame); - if (frame.showFrameTag != null) { - tagsToRemove.add(frame.showFrameTag); - } else { - // this should be the last frame, so remove the inner tags - tagsToRemove.addAll(frame.innerTags); - } - } - } - - if (tagsToRemove.size() > 0) { - String confirmationMessage; - if (tagsToRemove.size() == 1) { - Tag tag = tagsToRemove.get(0); - confirmationMessage = mainPanel.translate("message.confirm.remove").replace("%item%", tag.toString()); - } else { - confirmationMessage = mainPanel.translate("message.confirm.removemultiple").replace("%count%", Integer.toString(tagsToRemove.size())); - } - - if (View.showConfirmDialog(this, confirmationMessage, mainPanel.translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - Map> tagsToRemoveBySwf = new HashMap<>(); - for (Tag tag : tagsToRemove) { - SWF swf = tag.getSwf(); - if (!tagsToRemoveBySwf.containsKey(swf)) { - tagsToRemoveBySwf.put(swf, new ArrayList<>()); - } - - tagsToRemoveBySwf.get(swf).add(tag); - } - - for (SWF swf : tagsToRemoveBySwf.keySet()) { - swf.removeTags(tagsToRemoveBySwf.get(swf), removeDependencies); - } - - mainPanel.refreshTree(); - } - } - } - - private void undoTagActionPerformed(ActionEvent evt) { - List sel = tagTree.getSelected(tagTree); - - for (TreeItem item : sel) { - if (item instanceof Tag) { - try { - Tag tag = (Tag) item; - tag.undo(); - tag.getSwf().clearAllCache(); - tagTree.getModel().updateNode(item); - } catch (InterruptedException | IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - - mainPanel.repaintTree(); - } - - private void closeSwfActionPerformed(ActionEvent evt) { - List sel = tagTree.getSelected(tagTree); - for (TreeItem item : sel) { - if (item instanceof SWF) { - SWF swf = (SWF) item; - if (swf.binaryData != null) { - // embedded swf - swf.binaryData.innerSwf = null; - swf.clearTagSwfs(); - mainPanel.refreshTree(); - } else { - Main.closeFile(swf.swfList); - } - } else if (item instanceof SWFList) { - Main.closeFile((SWFList) item); - } - } - } -} +/* + * Copyright (C) 2010-2015 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.tagtree; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.gui.AppDialog; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.MainPanel; +import com.jpexs.decompiler.flash.gui.ReplaceCharacterDialog; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.FolderItem; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.helpers.Helper; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.tree.TreePath; + +/** + * + * @author JPEXS + */ +public class TagTreeContextMenu extends JPopupMenu { + + private static final Logger logger = Logger.getLogger(TagTreeContextMenu.class.getName()); + + private final MainPanel mainPanel; + + private final TagTree tagTree; + + private JMenuItem expandRecursiveMenuItem; + + private JMenuItem removeMenuItem; + + private JMenuItem removeWithDependenciesMenuItem; + + private JMenuItem undoTagMenuItem; + + private JMenuItem exportSelectionMenuItem; + + private JMenuItem replaceMenuItem; + + private JMenuItem replaceWithTagMenuItem; + + private JMenuItem rawEditMenuItem; + + private JMenuItem jumpToCharacterMenuItem; + + private JMenuItem exportJavaSourceMenuItem; + + private JMenuItem exportSwfXmlMenuItem; + + private JMenuItem importSwfXmlMenuItem; + + private JMenuItem closeMenuItem; + + private JMenu addTagMenu; + + private JMenu moveTagMenu; + + private JMenu copyTagMenu; + + private JMenu copyTagWithDependenciesMenu; + + private JMenuItem openSWFInsideTagMenuItem; + + public TagTreeContextMenu(final TagTree tagTree, MainPanel mainPanel) { + this.mainPanel = mainPanel; + this.tagTree = tagTree; + expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll")); + expandRecursiveMenuItem.addActionListener(this::expandRecursiveActionPerformed); + add(expandRecursiveMenuItem); + + removeMenuItem = new JMenuItem(mainPanel.translate("contextmenu.remove")); + removeMenuItem.addActionListener((ActionEvent e) -> { + removeItemActionPerformed(e, false); + }); + add(removeMenuItem); + + removeWithDependenciesMenuItem = new JMenuItem(mainPanel.translate("contextmenu.removeWithDependencies")); + removeWithDependenciesMenuItem.addActionListener((ActionEvent e) -> { + removeItemActionPerformed(e, true); + }); + add(removeWithDependenciesMenuItem); + + undoTagMenuItem = new JMenuItem(mainPanel.translate("contextmenu.undo")); + undoTagMenuItem.addActionListener(this::undoTagActionPerformed); + add(undoTagMenuItem); + + exportSelectionMenuItem = new JMenuItem(mainPanel.translate("menu.file.export.selection")); + exportSelectionMenuItem.addActionListener(mainPanel::exportSelectionActionPerformed); + add(exportSelectionMenuItem); + + replaceMenuItem = new JMenuItem(mainPanel.translate("button.replace")); + replaceMenuItem.addActionListener(mainPanel::replaceButtonActionPerformed); + add(replaceMenuItem); + + replaceWithTagMenuItem = new JMenuItem(mainPanel.translate("button.replaceWithTag")); + replaceWithTagMenuItem.addActionListener(this::replaceWithTagActionPerformed); + add(replaceWithTagMenuItem); + + rawEditMenuItem = new JMenuItem(mainPanel.translate("contextmenu.rawEdit")); + rawEditMenuItem.addActionListener(this::rawEditActionPerformed); + add(rawEditMenuItem); + + jumpToCharacterMenuItem = new JMenuItem(mainPanel.translate("contextmenu.jumpToCharacter")); + jumpToCharacterMenuItem.addActionListener(this::jumpToCharacterActionPerformed); + add(jumpToCharacterMenuItem); + + exportJavaSourceMenuItem = new JMenuItem(mainPanel.translate("contextmenu.exportJavaSource")); + exportJavaSourceMenuItem.addActionListener(mainPanel::exportJavaSourceActionPerformed); + add(exportJavaSourceMenuItem); + + exportSwfXmlMenuItem = new JMenuItem(mainPanel.translate("contextmenu.exportSwfXml")); + exportSwfXmlMenuItem.addActionListener(mainPanel::exportSwfXmlActionPerformed); + add(exportSwfXmlMenuItem); + + importSwfXmlMenuItem = new JMenuItem(mainPanel.translate("contextmenu.importSwfXml")); + importSwfXmlMenuItem.addActionListener(mainPanel::importSwfXmlActionPerformed); + add(importSwfXmlMenuItem); + + closeMenuItem = new JMenuItem(mainPanel.translate("contextmenu.closeSwf")); + closeMenuItem.addActionListener(this::closeSwfActionPerformed); + add(closeMenuItem); + + addTagMenu = new JMenu(mainPanel.translate("contextmenu.addTag")); + add(addTagMenu); + + moveTagMenu = new JMenu(mainPanel.translate("contextmenu.moveTag")); + add(moveTagMenu); + + copyTagMenu = new JMenu(mainPanel.translate("contextmenu.copyTag")); + add(copyTagMenu); + + copyTagWithDependenciesMenu = new JMenu(mainPanel.translate("contextmenu.copyTagWithDependencies")); + add(copyTagWithDependenciesMenu); + + openSWFInsideTagMenuItem = new JMenuItem(mainPanel.translate("contextmenu.openswfinside")); + add(openSWFInsideTagMenuItem); + openSWFInsideTagMenuItem.addActionListener(this::openSwfInsideActionPerformed); + + tagTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + + int row = tagTree.getClosestRowForLocation(e.getX(), e.getY()); + int[] selectionRows = tagTree.getSelectionRows(); + if (!Helper.contains(selectionRows, row)) { + tagTree.setSelectionRow(row); + } + + TreePath[] paths = tagTree.getSelectionPaths(); + if (paths == null || paths.length == 0) { + return; + } + + List li = new ArrayList<>(); + for (TreePath treePath : paths) { + TreeItem item = (TreeItem) treePath.getLastPathComponent(); + li.add(item); + } + + update(li); + show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + } + + public void update(final List items) { + + if (items.isEmpty()) { + return; + } + + final List swfs = mainPanel.getSwfs(); + + boolean allSelectedIsTagOrFrame = true; + for (TreeItem item : items) { + if (!(item instanceof Tag) && !(item instanceof Frame)) { + if (item instanceof TagScript) { + Tag tag = ((TagScript) item).getTag(); + if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { + continue; + } + } + + allSelectedIsTagOrFrame = false; + break; + } + } + + boolean allSelectedIsTag = true; + for (TreeItem item : items) { + if (!(item instanceof Tag)) { + if (item instanceof TagScript) { + Tag tag = ((TagScript) item).getTag(); + if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { + continue; + } + } + + allSelectedIsTag = false; + break; + } + } + + boolean allSelectedIsBinaryData = true; + for (TreeItem item : items) { + if (!(item instanceof DefineBinaryDataTag)) { + allSelectedIsBinaryData = false; + break; + } + } + + boolean allSelectedIsSwf = true; + for (TreeItem item : items) { + if (!(item instanceof SWF) && !(item instanceof SWFList)) { + allSelectedIsSwf = false; + break; + } else if (item instanceof SWF) { + SWF swf = (SWF) item; + // Do not allow to close SWF in bundle + if (swf.swfList != null && swf.swfList.isBundle()) { + allSelectedIsSwf = false; + } + } + } + + boolean allSelectedIsInTheSameSwf = true; + SWF singleSwf = null; + for (TreeItem item : items) { + if (item instanceof SWFList) { + allSelectedIsInTheSameSwf = false; + break; + } + if (singleSwf == null) { + singleSwf = item.getSwf(); + } else { + if (singleSwf != item.getSwf()) { + allSelectedIsInTheSameSwf = false; + break; + } + } + } + + expandRecursiveMenuItem.setVisible(false); + removeMenuItem.setVisible(allSelectedIsTagOrFrame); + removeWithDependenciesMenuItem.setVisible(allSelectedIsTagOrFrame); + undoTagMenuItem.setVisible(allSelectedIsTag); + exportSelectionMenuItem.setEnabled(tagTree.hasExportableNodes()); + replaceMenuItem.setVisible(false); + replaceWithTagMenuItem.setVisible(false); + rawEditMenuItem.setVisible(false); + jumpToCharacterMenuItem.setVisible(false); + exportJavaSourceMenuItem.setVisible(allSelectedIsSwf); + exportSwfXmlMenuItem.setVisible(allSelectedIsSwf); + importSwfXmlMenuItem.setVisible(allSelectedIsSwf); + closeMenuItem.setVisible(allSelectedIsSwf); + addTagMenu.setVisible(false); + moveTagMenu.setVisible(false); + copyTagMenu.setVisible(false); + copyTagWithDependenciesMenu.setVisible(false); + openSWFInsideTagMenuItem.setVisible(false); + + boolean singleSelect = items.size() == 1; + + if (singleSelect) { + final TreeItem firstItem = items.get(0); + + if (firstItem instanceof CharacterTag) { + replaceWithTagMenuItem.setVisible(true); + } + + // replace + if (firstItem instanceof ImageTag && ((ImageTag) firstItem).importSupported()) { + replaceMenuItem.setVisible(true); + } + + if (firstItem instanceof ShapeTag) { + replaceMenuItem.setVisible(true); + } + + if (firstItem instanceof DefineBinaryDataTag) { + replaceMenuItem.setVisible(true); + } + + if (firstItem instanceof DefineSoundTag) { + replaceMenuItem.setVisible(true); + } + + List allowedTagTypes = null; + if (firstItem instanceof FolderItem) { + allowedTagTypes = tagTree.getSwfFolderItemNestedTagIds(((FolderItem) firstItem).getName(), firstItem.getSwf().gfx); + } else if (firstItem instanceof Tag) { + allowedTagTypes = tagTree.getNestedTagIds((Tag) firstItem); + } + + addTagMenu.removeAll(); + if (allowedTagTypes != null) { + for (Integer tagId : allowedTagTypes) { + final Class cl = TagIdClassMap.getClassByTagId(tagId); + JMenuItem tagItem = new JMenuItem(cl.getSimpleName()); + tagItem.addActionListener((ActionEvent ae) -> { + addTagActionPerformed(ae, firstItem, cl); + }); + addTagMenu.add(tagItem); + } + addTagMenu.setVisible(true); + } + + if (tagTree.getModel().getChildCount(firstItem) > 0) { + expandRecursiveMenuItem.setVisible(true); + } + + if (firstItem instanceof CharacterIdTag && !(firstItem instanceof CharacterTag)) { + jumpToCharacterMenuItem.setVisible(true); + } + + if (firstItem instanceof Tag) { + rawEditMenuItem.setVisible(firstItem instanceof Tag); + } + } + + if (allSelectedIsInTheSameSwf && allSelectedIsTag && swfs.size() > 1) { + moveTagMenu.removeAll(); + copyTagMenu.removeAll(); + copyTagWithDependenciesMenu.removeAll(); + for (SWFList targetSwfList : swfs) { + for (final SWF targetSwf : targetSwfList) { + if (targetSwf != singleSwf) { + JMenuItem swfItem = new JMenuItem(targetSwf.getShortFileName()); + swfItem.addActionListener((ActionEvent ae) -> { + moveTagActionPerformed(ae, items, targetSwf); + }); + moveTagMenu.add(swfItem); + + swfItem = new JMenuItem(targetSwf.getShortFileName()); + swfItem.addActionListener((ActionEvent ae) -> { + copyTagActionPerformed(ae, items, targetSwf); + }); + copyTagMenu.add(swfItem); + + swfItem = new JMenuItem(targetSwf.getShortFileName()); + swfItem.addActionListener((ActionEvent ae) -> { + copyTagWithDependenciesActionPerformed(ae, items, targetSwf); + }); + copyTagWithDependenciesMenu.add(swfItem); + } + } + } + moveTagMenu.setVisible(true); + copyTagMenu.setVisible(true); + copyTagWithDependenciesMenu.setVisible(true); + } + + if (allSelectedIsBinaryData) { + boolean anyInnerSwf = false; + for (TreeItem item : items) { + DefineBinaryDataTag binary = (DefineBinaryDataTag) item; + + // inner swf is not loaded yet + if (binary.innerSwf == null && binary.isSwfData()) { + anyInnerSwf = true; + } + } + + openSWFInsideTagMenuItem.setVisible(anyInnerSwf); + } + } + + private void addTagActionPerformed(ActionEvent evt, TreeItem firstItem, Class cl) { + try { + SWF swf = firstItem.getSwf(); + Tag t = (Tag) cl.getDeclaredConstructor(SWF.class).newInstance(new Object[]{swf}); + boolean isDefineSprite = firstItem instanceof DefineSpriteTag; + Timelined timelined = isDefineSprite ? (DefineSpriteTag) firstItem : swf; + t.setTimelined(timelined); + if (isDefineSprite) { + ((DefineSpriteTag) firstItem).subTags.add(t); + } else { + int index; + if (firstItem instanceof Tag) { + if ((t instanceof CharacterIdTag) && (firstItem instanceof CharacterTag)) { + ((CharacterIdTag) t).setCharacterId(((CharacterTag) firstItem).getCharacterId()); + } + index = swf.tags.indexOf(firstItem); + } else { + index = -1; + if (t instanceof CharacterTag) { + // add before the last ShowFrame tag + for (int i = swf.tags.size() - 1; i >= 0; i--) { + if (swf.tags.get(i) instanceof ShowFrameTag) { + index = i; + break; + } + } + } + } + + if (index > -1) { + swf.tags.add(index, t); + } else { + swf.tags.add(t); + } + } + timelined.resetTimeline(); + swf.updateCharacters(); + mainPanel.refreshTree(swf); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + private int chechUniqueCharacterId(Tag tag) { + if (tag instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) tag; + int characterId = characterTag.getCharacterId(); + SWF swf = tag.getSwf(); + if (swf.getCharacter(characterId) != null) { + int newCharacterId = swf.getNextCharacterId(); + characterTag.setCharacterId(newCharacterId); + logger.log(Level.WARNING, "Target SWF already contained chatacter tag with id = {0} => id changed to {1}", new Object[]{characterId, newCharacterId}); + return newCharacterId; + } + + return characterId; + } + + return -1; + } + + private void moveTagActionPerformed(ActionEvent evt, List items, SWF targetSwf) { + SWF sourceSwf = items.get(0).getSwf(); + for (TreeItem item : items) { + Tag tag = (Tag) item; + sourceSwf.tags.remove(tag); + tag.setSwf(targetSwf, true); + targetSwf.tags.add(tag); + chechUniqueCharacterId(tag); + targetSwf.updateCharacters(); + tag.setModified(true); + } + + sourceSwf.assignExportNamesToSymbols(); + targetSwf.assignExportNamesToSymbols(); + sourceSwf.assignClassesToSymbols(); + targetSwf.assignClassesToSymbols(); + sourceSwf.clearImageCache(); + targetSwf.clearImageCache(); + sourceSwf.updateCharacters(); + targetSwf.updateCharacters(); + sourceSwf.resetTimelines(sourceSwf); + targetSwf.resetTimelines(targetSwf); + mainPanel.refreshTree(new SWF[]{sourceSwf, targetSwf}); + } + + private void copyTagActionPerformed(ActionEvent evt, List items, SWF targetSwf) { + try { + for (TreeItem item : items) { + Tag tag = (Tag) item; + Tag copyTag = tag.cloneTag(); + copyTag.setSwf(targetSwf, true); + targetSwf.tags.add(copyTag); + chechUniqueCharacterId(copyTag); + targetSwf.updateCharacters(); + copyTag.setModified(true); + } + + targetSwf.assignExportNamesToSymbols(); + targetSwf.assignClassesToSymbols(); + targetSwf.clearImageCache(); + targetSwf.updateCharacters(); + mainPanel.refreshTree(targetSwf); + } catch (IOException | InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + private void copyTagWithDependenciesActionPerformed(ActionEvent evt, List items, SWF targetSwf) { + try { + SWF sourceSwf = items.get(0).getSwf(); + for (TreeItem item : items) { + Set copiedTags = new HashSet<>(); + Set newTags = new HashSet<>(); + LinkedHashSet needed = new LinkedHashSet<>(); + Map changedCharacterIds = new HashMap<>(); + + Tag tag = (Tag) item; + tag.getNeededCharactersDeep(needed); + Tag copyTag; + + List neededList = new ArrayList<>(); + for (Integer characterId : needed) { + neededList.add(characterId); + } + + // first add dependencies in reverse order + for (int i = neededList.size() - 1; i >= 0; i--) { + int characterId = neededList.get(i); + Tag neededTag = (Tag) sourceSwf.getCharacter(characterId); + if (!copiedTags.contains(neededTag)) { + copyTag = neededTag.cloneTag(); + copyTag.setSwf(targetSwf, true); + targetSwf.tags.add(copyTag); + if (neededTag instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) copyTag; + int oldCharacterId = characterTag.getCharacterId(); + int newCharacterId = chechUniqueCharacterId(copyTag); + changedCharacterIds.put(oldCharacterId, newCharacterId); + } + + targetSwf.updateCharacters(); + targetSwf.getCharacters(); // force rebuild character id cache + copyTag.setModified(true); + copiedTags.add(neededTag); + newTags.add(copyTag); + } + } + + copyTag = tag.cloneTag(); + copyTag.setSwf(targetSwf, true); + targetSwf.tags.add(copyTag); + if (tag instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) copyTag; + int oldCharacterId = characterTag.getCharacterId(); + int newCharacterId = chechUniqueCharacterId(copyTag); + changedCharacterIds.put(oldCharacterId, newCharacterId); + } + + targetSwf.updateCharacters(); + targetSwf.getCharacters(); // force rebuild character id cache + copyTag.setModified(true); + copiedTags.add(tag); + newTags.add(copyTag); + + for (int oldCharacterId : changedCharacterIds.keySet()) { + int newCharacterId = changedCharacterIds.get(oldCharacterId); + for (Tag newTag : newTags) { + // todo: avoid double replaces + newTag.replaceCharacter(oldCharacterId, newCharacterId); + } + } + } + + targetSwf.assignExportNamesToSymbols(); + targetSwf.assignClassesToSymbols(); + targetSwf.clearImageCache(); + targetSwf.updateCharacters(); + mainPanel.refreshTree(targetSwf); + } catch (IOException | InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + private void openSwfInsideActionPerformed(ActionEvent evt) { + List sel = tagTree.getSelected(tagTree); + List binaryDatas = new ArrayList<>(); + for (TreeItem item : sel) { + DefineBinaryDataTag binaryData = (DefineBinaryDataTag) item; + if (binaryData.isSwfData()) { + binaryDatas.add((DefineBinaryDataTag) item); + } + } + + mainPanel.loadFromBinaryTag(binaryDatas); + } + + private void replaceWithTagActionPerformed(ActionEvent evt) { + TreeItem itemr = tagTree.getCurrentTreeItem(); + if (itemr == null) { + return; + } + + SWF swf = itemr.getSwf(); + CharacterTag characterTag = (CharacterTag) itemr; + int characterId = characterTag.getCharacterId(); + ReplaceCharacterDialog replaceCharacterDialog = new ReplaceCharacterDialog(); + if (replaceCharacterDialog.showDialog(swf, characterId) == AppDialog.OK_OPTION) { + int newCharacterId = replaceCharacterDialog.getCharacterId(); + CharacterTag newCharacter = swf.getCharacter(newCharacterId); + newCharacter.setCharacterId(characterId); + characterTag.setCharacterId(newCharacterId); + newCharacter.setModified(true); + characterTag.setModified(true); + + swf.assignExportNamesToSymbols(); + swf.assignClassesToSymbols(); + swf.clearImageCache(); + swf.updateCharacters(); + mainPanel.refreshTree(swf); + } + } + + private void rawEditActionPerformed(ActionEvent evt) { + TreeItem itemr = tagTree.getCurrentTreeItem(); + if (itemr == null) { + return; + } + + mainPanel.showGenericTag((Tag) itemr); + } + + private void jumpToCharacterActionPerformed(ActionEvent evt) { + TreeItem itemj = tagTree.getCurrentTreeItem(); + if (itemj == null || !(itemj instanceof CharacterIdTag)) { + return; + } + + CharacterIdTag characterIdTag = (CharacterIdTag) itemj; + mainPanel.setTagTreeSelectedNode(itemj.getSwf().getCharacter(characterIdTag.getCharacterId())); + } + + private void expandRecursiveActionPerformed(ActionEvent evt) { + TreePath path = tagTree.getSelectionPath(); + if (path == null) { + return; + } + View.expandTreeNodes(tagTree, path, true); + } + + private void removeItemActionPerformed(ActionEvent evt, boolean removeDependencies) { + List sel = tagTree.getSelected(tagTree); + + List tagsToRemove = new ArrayList<>(); + for (TreeItem item : sel) { + if (item instanceof Tag) { + tagsToRemove.add((Tag) item); + } else if (item instanceof TagScript) { + tagsToRemove.add(((TagScript) item).getTag()); + } else if (item instanceof Frame) { + Frame frameNode = (Frame) item; + Frame frame = frameNode.timeline.getFrame(frameNode.frame); + if (frame.showFrameTag != null) { + tagsToRemove.add(frame.showFrameTag); + } else { + // this should be the last frame, so remove the inner tags + tagsToRemove.addAll(frame.innerTags); + } + } + } + + if (tagsToRemove.size() > 0) { + String confirmationMessage; + if (tagsToRemove.size() == 1) { + Tag tag = tagsToRemove.get(0); + confirmationMessage = mainPanel.translate("message.confirm.remove").replace("%item%", tag.toString()); + } else { + confirmationMessage = mainPanel.translate("message.confirm.removemultiple").replace("%count%", Integer.toString(tagsToRemove.size())); + } + + if (View.showConfirmDialog(this, confirmationMessage, mainPanel.translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + Map> tagsToRemoveBySwf = new HashMap<>(); + for (Tag tag : tagsToRemove) { + SWF swf = tag.getSwf(); + if (!tagsToRemoveBySwf.containsKey(swf)) { + tagsToRemoveBySwf.put(swf, new ArrayList<>()); + } + + tagsToRemoveBySwf.get(swf).add(tag); + } + + for (SWF swf : tagsToRemoveBySwf.keySet()) { + swf.removeTags(tagsToRemoveBySwf.get(swf), removeDependencies); + } + + mainPanel.refreshTree(); + } + } + } + + private void undoTagActionPerformed(ActionEvent evt) { + List sel = tagTree.getSelected(tagTree); + + for (TreeItem item : sel) { + if (item instanceof Tag) { + try { + Tag tag = (Tag) item; + tag.undo(); + tag.getSwf().clearAllCache(); + tagTree.getModel().updateNode(item); + } catch (InterruptedException | IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + + mainPanel.repaintTree(); + } + + private void closeSwfActionPerformed(ActionEvent evt) { + List sel = tagTree.getSelected(tagTree); + for (TreeItem item : sel) { + if (item instanceof SWF) { + SWF swf = (SWF) item; + if (swf.binaryData != null) { + // embedded swf + swf.binaryData.innerSwf = null; + swf.clearTagSwfs(); + mainPanel.refreshTree(); + } else { + Main.closeFile(swf.swfList); + } + } else if (item instanceof SWFList) { + Main.closeFile((SWFList) item); + } + } + } +}