diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ApplicationInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ApplicationInfo.java index 2950a63c1..663a918bb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ApplicationInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ApplicationInfo.java @@ -59,7 +59,7 @@ public class ApplicationInfo { if (nightly) { version = version + " nightly build " + version_build; } - } catch (IOException | NullPointerException ex) { + } catch (IOException | NullPointerException | NumberFormatException ex) { //ignore version = "unknown"; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java index e768b4e22..71a45de0f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -1195,10 +1195,10 @@ public class SWFOutputStream extends OutputStream { */ public void writeGRADRECORD(GRADRECORD value, int shapeNum) throws IOException { writeUI8(value.ratio); - if (shapeNum == 1 || shapeNum == 2) { - writeRGB(value.color); - } else if (shapeNum == 3) { + if (shapeNum >= 3) { writeRGBA((RGBA) value.color); + } else { + writeRGB(value.color); } } @@ -1303,6 +1303,8 @@ public class SWFOutputStream extends OutputStream { public void writeSHAPEWITHSTYLE(SHAPEWITHSTYLE value, int shapeNum) throws IOException { writeFILLSTYLEARRAY(value.fillStyles, shapeNum); writeLINESTYLEARRAY(value.lineStyles, shapeNum); + value.numFillBits = getNeededBitsU(value.fillStyles.fillStyles.length); + value.numLineBits = getNeededBitsU(value.lineStyles.lineStyles.length); writeUB(4, value.numFillBits); writeUB(4, value.numLineBits); writeSHAPERECORDS(value.shapeRecords, value.numFillBits, value.numLineBits, shapeNum); @@ -1323,15 +1325,17 @@ public class SWFOutputStream extends OutputStream { CurvedEdgeRecord cer = (CurvedEdgeRecord) sh; writeUB(1, 1); //typeFlag writeUB(1, 0);//curvedEdge + cer.numBits = Math.max(getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2, 0); writeUB(4, cer.numBits); - writeUB(cer.numBits + 2, cer.controlDeltaX); - writeUB(cer.numBits + 2, cer.controlDeltaY); - writeUB(cer.numBits + 2, cer.anchorDeltaX); - writeUB(cer.numBits + 2, cer.anchorDeltaY); + writeSB(cer.numBits + 2, cer.controlDeltaX); + writeSB(cer.numBits + 2, cer.controlDeltaY); + writeSB(cer.numBits + 2, cer.anchorDeltaX); + writeSB(cer.numBits + 2, cer.anchorDeltaY); } else if (sh instanceof StraightEdgeRecord) { StraightEdgeRecord ser = (StraightEdgeRecord) sh; writeUB(1, 1); //typeFlag writeUB(1, 1);//straightEdge + ser.numBits = Math.max(getNeededBitsS(ser.deltaX, ser.deltaY) - 2, 0); writeUB(4, ser.numBits); writeUB(1, ser.generalLineFlag ? 1 : 0); if (!ser.generalLineFlag) { @@ -1352,9 +1356,10 @@ public class SWFOutputStream extends OutputStream { writeUB(1, scr.stateFillStyle0 ? 1 : 0); writeUB(1, scr.stateMoveTo ? 1 : 0); if (scr.stateMoveTo) { + scr.moveBits = getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY); writeUB(5, scr.moveBits); - writeUB(scr.moveBits, scr.moveDeltaX); - writeUB(scr.moveBits, scr.moveDeltaY); + writeSB(scr.moveBits, scr.moveDeltaX); + writeSB(scr.moveBits, scr.moveDeltaY); } if (scr.stateFillStyle0) { writeUB(fillBits, scr.fillStyle0); @@ -1368,6 +1373,10 @@ public class SWFOutputStream extends OutputStream { if (scr.stateNewStyles) { writeFILLSTYLEARRAY(scr.fillStyles, shapeNum); writeLINESTYLEARRAY(scr.lineStyles, shapeNum); + scr.numFillBits = getNeededBitsU(scr.fillStyles.fillStyles.length); + scr.numLineBits = getNeededBitsU(scr.lineStyles.lineStyles.length); + fillBits = scr.numFillBits; + fillBits = scr.numLineBits; writeUB(4, scr.numFillBits); writeUB(4, scr.numLineBits); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java new file mode 100644 index 000000000..d628b9c93 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; +import com.jpexs.decompiler.flash.tags.DefineShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineShape3Tag; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.DefineShapeTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import java.io.IOException; +import java.util.HashSet; + +/** + * + * @author JPEXS + */ +public class ShapeImporter { + + public Tag importImage(ShapeTag st, byte[] newData) throws IOException { + SWF swf = st.getSwf(); + DefineBitsJPEG2Tag jpeg2Tag = new DefineBitsJPEG2Tag(swf, null, swf.getNextCharacterId(), newData); + jpeg2Tag.setModified(true); + swf.tags.add(jpeg2Tag); + swf.updateCharacters(); + SHAPEWITHSTYLE shapes = jpeg2Tag.getShape(st.getRect(new HashSet()), true); + + if (st instanceof DefineShapeTag) { + DefineShapeTag dst = (DefineShapeTag) st; + dst.shapes = shapes; + } else if (st instanceof DefineShape2Tag) { + DefineShape2Tag dst = (DefineShape2Tag) st; + dst.shapes = shapes; + } else if (st instanceof DefineShape3Tag) { + DefineShape3Tag dst = (DefineShape3Tag) st; + dst.shapes = shapes; + } else if (st instanceof DefineShape4Tag) { + DefineShape4Tag dst = (DefineShape4Tag) st; + dst.shapes = shapes; + } + + return (Tag) st; + } +} 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 512df49a5..5c9309c53 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 @@ -109,6 +109,12 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag { } private SHAPEWITHSTYLE getShape() { + RECT rect = getRect(new HashSet()); + return getShape(rect, false); + } + + public SHAPEWITHSTYLE getShape(RECT rect, boolean fill) { + boolean translated = rect.Xmin != 0 || rect.Ymin != 0; SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); shape.fillStyles = new FILLSTYLEARRAY(); shape.fillStyles.fillStyles = new FILLSTYLE[1]; @@ -117,8 +123,18 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag { fillStyle.bitmapId = getCharacterId(); MATRIX matrix = new MATRIX(); matrix.hasScale = true; - matrix.scaleX = ((int) SWF.unitDivisor) << 16; - matrix.scaleY = matrix.scaleX; + if (fill) { + RECT imageRect = getRect(new HashSet()); + matrix.scaleX = (int) ((((long) SWF.unitDivisor) << 16) * rect.getWidth() / imageRect.getWidth()); + matrix.scaleY = (int) ((((long) SWF.unitDivisor) << 16) * rect.getHeight() / imageRect.getHeight()); + } else { + matrix.scaleX = ((int) SWF.unitDivisor) << 16; + matrix.scaleY = matrix.scaleX; + } + if (translated) { + matrix.translateX = rect.Xmin; + matrix.translateY = rect.Ymin; + } fillStyle.bitmapMatrix = matrix; shape.fillStyles.fillStyles[0] = fillStyle; @@ -129,8 +145,11 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag { style.stateFillStyle0 = true; style.fillStyle0 = 1; style.stateMoveTo = true; + if (translated) { + style.moveDeltaX = rect.Xmin; + style.moveDeltaY = rect.Ymin; + } shape.shapeRecords.add(style); - RECT rect = getRect(new HashSet()); StraightEdgeRecord top = new StraightEdgeRecord(); top.generalLineFlag = true; top.deltaX = rect.getWidth(); diff --git a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java index bd2aeb159..a5c450e20 100644 --- a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java @@ -270,7 +270,6 @@ public class FolderPreviewPanel extends JPanel { int h = h1 * w2 / w1; if (h > h2) { w = w1 * h2 / h1; - h = h2; } else { w = w2; } diff --git a/src/com/jpexs/decompiler/flash/gui/FontPanel.java b/src/com/jpexs/decompiler/flash/gui/FontPanel.java index ab0c88629..c5cc0e0ca 100644 --- a/src/com/jpexs/decompiler/flash/gui/FontPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FontPanel.java @@ -198,7 +198,7 @@ public class FontPanel extends javax.swing.JPanel { setAllowSave(false); String key = swf.getShortFileName() + "_" + ft.getFontId() + "_" + ft.getFontName(); - String selectedFont = ""; + String selectedFont; if (swf.sourceFontNamesMap.containsKey(ft.getFontId())) { selectedFont = swf.sourceFontNamesMap.get(ft.getFontId()); diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 5496f979c..66d6f083b 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -420,7 +420,6 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis h = h1 * w2 / w1; if (h > h2) { w = w1 * h2 / h1; - h = h2; } else { w = w2; } diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 2f2442b7f..46faa0ade 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -1306,8 +1306,8 @@ public class Main { final java.util.List versions = new ArrayList<>(); String header = ""; Pattern headerPat = Pattern.compile("\\[([a-zA-Z0-9]+)\\]"); - int updateMajor = 0; - int updateMinor = 0; + int updateMajor; + int updateMinor; Version ver = null; while ((s = br.readLine()) != null) { if (start) { diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 0d4a08c40..96d755bb3 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -70,6 +70,7 @@ import com.jpexs.decompiler.flash.gui.timeline.TimelineViewPanel; import com.jpexs.decompiler.flash.helpers.Freed; import com.jpexs.decompiler.flash.importers.BinaryDataImporter; import com.jpexs.decompiler.flash.importers.ImageImporter; +import com.jpexs.decompiler.flash.importers.ShapeImporter; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; @@ -1948,51 +1949,10 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec } if (item instanceof DefineSoundTag) { - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - fc.setFileFilter(new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".mp3")) - || (f.getName().toLowerCase().endsWith(".wav")) - || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.sounds"); - } - }); - fc.addChoosableFileFilter(new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".mp3")) - || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.sounds.mp3"); - } - }); - fc.addChoosableFileFilter(new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".wav")) - || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.sounds.wav"); - } - }); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File selfile = Helper.fixDialogFile(fc.getSelectedFile()); + File selectedFile = showImportFileChooser("filter.sounds|*.mp3;*.wav|filter.sounds.mp3|*.mp3|filter.sounds.wav|*.wav"); + if (selectedFile != null) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(selectedFile).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(selectedFile); DefineSoundTag ds = (DefineSoundTag) item; int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; if (selfile.getName().toLowerCase().endsWith(".mp3")) { @@ -2014,32 +1974,12 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec if (item instanceof ImageTag) { ImageTag it = (ImageTag) item; if (it.importSupported()) { - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - fc.setFileFilter(new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".jpg")) - || (f.getName().toLowerCase().endsWith(".jpeg")) - || (f.getName().toLowerCase().endsWith(".gif")) - || (f.getName().toLowerCase().endsWith(".png")) - || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.images"); - } - }); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File selfile = Helper.fixDialogFile(fc.getSelectedFile()); + File selectedFile = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png"); + if (selectedFile != null) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(selectedFile).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(selectedFile); byte[] data = Helper.readFile(selfile.getAbsolutePath()); try { - SWF swf = it.getSwf(); Tag newTag = new ImageImporter().importImage(it, data); if (newTag != null) { refreshTree(); @@ -2054,16 +1994,33 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec } } } + if (item instanceof ShapeTag) { + ShapeTag st = (ShapeTag) item; + File selectedFile = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png"); + if (selectedFile != null) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(selectedFile).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + try { + Tag newTag = new ShapeImporter().importImage(st, data); + if (newTag != null) { + refreshTree(); + setTagTreeSelectedNode(newTag); + } + SWF.clearImageCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid image", ex); + View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + reload(true); + } + } if (item instanceof DefineBinaryDataTag) { DefineBinaryDataTag bt = (DefineBinaryDataTag) item; - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File selfile = Helper.fixDialogFile(fc.getSelectedFile()); + File selectedFile = showImportFileChooser(""); + if (selectedFile != null) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(selectedFile).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(selectedFile); byte[] data = Helper.readFile(selfile.getAbsolutePath()); new BinaryDataImporter().importData(bt, data); reload(true); @@ -2084,6 +2041,58 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec } } + private File showImportFileChooser(String filter) { + String[] filterArray = filter.split("\\|"); + + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + boolean first = true; + for (int i = 0; i < filterArray.length; i += 2) { + final String filterName = filterArray[i]; + final String[] extensions = filterArray[i + 1].split(";"); + for (int j = 0; j < extensions.length; j++) { + if (extensions[j].startsWith("*.")) { + extensions[j] = extensions[j].substring(1); + } + } + FileFilter ff = new FileFilter() { + + @Override + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + String fileName = f.getName().toLowerCase(); + for (String ext : extensions) { + if (fileName.endsWith(ext)) { + return true; + } + } + return false; + } + + @Override + public String getDescription() { + return translate(filterName); + } + }; + if (first) { + fc.setFileFilter(ff); + } else { + fc.addChoosableFileFilter(ff); + } + first = false; + } + + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showOpenDialog(f) == JFileChooser.APPROVE_OPTION) { + return fc.getSelectedFile(); + } + + return null; + } + private int splitPos = 0; public void showDetail(String card) { diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java index e55a43133..f282c657f 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java @@ -302,7 +302,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL SyntaxDocument sd = (SyntaxDocument) getDocument(); Token t = sd.getTokenAt(pos + 1); Token lastToken = t; - Token prev = null; + Token prev; while (t.type == TokenType.IDENTIFIER || t.type == TokenType.KEYWORD || t.type == TokenType.REGEX) { prev = sd.getPrevToken(t); if (prev != null) { @@ -323,7 +323,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL if (currentType.equals("*")) { return false; } - boolean found = false; + boolean found; t = sd.getNextToken(t); while (t != lastToken && !currentType.equals("*")) { t = sd.getNextToken(t); @@ -540,7 +540,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL setNoTrait(); return; } - Trait currentTrait = null; + Trait currentTrait; currentTraitHighlight = Highlighting.search(traitHighlights, pos); if (currentTraitHighlight != null) { lastTraitIndex = (int) (long) currentTraitHighlight.getPropertyLong("index"); diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java index b0b20432a..b356960cb 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java @@ -213,7 +213,7 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe @Override public void actionPerformed(ActionEvent e) { - Color newColor = null; + Color newColor; if (colorType == COLOR_TYPE_RGB) { try { newColor = noTransparencyColorChooser(null, AppStrings.translate("dialog.selectcolor.title"), color); diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 403074c66..b5354b0bb 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -28,6 +28,7 @@ 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.Timelined; import com.jpexs.decompiler.flash.treeitems.FolderItem; @@ -223,6 +224,10 @@ public class TagTreeContextMenu extends JPopupMenu implements ActionListener { replaceMenuItem.setVisible(true); } + if (firstItem instanceof ShapeTag) { + replaceMenuItem.setVisible(true); + } + if (firstItem instanceof DefineBinaryDataTag) { replaceMenuItem.setVisible(true); }