diff --git a/Ghidra/Framework/Docking/certification.manifest b/Ghidra/Framework/Docking/certification.manifest index bec96bb78a..63b6dea21f 100644 --- a/Ghidra/Framework/Docking/certification.manifest +++ b/Ghidra/Framework/Docking/certification.manifest @@ -85,6 +85,7 @@ src/main/resources/images/menu16.gif||GHIDRA||reviewed||END| src/main/resources/images/note-red.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/note.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/note.yellow.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| +src/main/resources/images/oxygen-edit-redo.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/page.png||FAMFAMFAM Icons - CC 2.5||||END| src/main/resources/images/page_code.png||FAMFAMFAM Icons - CC 2.5||||END| src/main/resources/images/page_excel.png||FAMFAMFAM Icons - CC 2.5||||END| @@ -92,7 +93,6 @@ src/main/resources/images/page_go.png||FAMFAMFAM Icons - CC 2.5||||END| src/main/resources/images/page_green.png||FAMFAMFAM Icons - CC 2.5||||END| src/main/resources/images/play.png||GHIDRA||||END| src/main/resources/images/preferences-system-windows.png||Tango Icons - Public Domain||||END| -src/main/resources/images/redo.png||Crystal Clear Icons - LGPL 2.1||||END| src/main/resources/images/software-update-available.png||Tango Icons - Public Domain|||tango icon set|END| src/main/resources/images/table.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/tag.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| @@ -100,7 +100,6 @@ src/main/resources/images/text_lowercase.png||FAMFAMFAM Icons - CC 2.5|||famfamf src/main/resources/images/textfield_rename.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/tip.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/trash-empty.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| -src/main/resources/images/undo.png||Crystal Clear Icons - LGPL 2.1||||END| src/main/resources/images/user-home.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/warning.help.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| diff --git a/Ghidra/Framework/Docking/data/docking.theme.properties b/Ghidra/Framework/Docking/data/docking.theme.properties index e35755127a..6df04328ca 100644 --- a/Ghidra/Framework/Docking/data/docking.theme.properties +++ b/Ghidra/Framework/Docking/data/docking.theme.properties @@ -100,8 +100,12 @@ icon.subtract = list-remove.png icon.toggle.expand = expand.gif icon.toggle.collapse = collapse.gif -icon.undo = undo.png -icon.redo = redo.png +// the oxygen-edit-redo.png image is not centered vertically in its 16x16 icon. To center it we +// create an empty 16x16 base and then move down by 3 pixels +icon.undo = EMPTY_ICON{oxygen-edit-redo.png[mirror][move(0,3)]} +icon.redo = EMPTY_ICON{oxygen-edit-redo.png[move(0,3)]} + + icon.font = text_lowercase.png icon.rename = textfield_rename.png icon.check = check.png diff --git a/Ghidra/Framework/Docking/src/main/resources/images/oxygen-edit-redo.png b/Ghidra/Framework/Docking/src/main/resources/images/oxygen-edit-redo.png new file mode 100644 index 0000000000..9bfee2d1fa Binary files /dev/null and b/Ghidra/Framework/Docking/src/main/resources/images/oxygen-edit-redo.png differ diff --git a/Ghidra/Framework/Docking/src/main/resources/images/redo.png b/Ghidra/Framework/Docking/src/main/resources/images/redo.png deleted file mode 100644 index 5d6121b821..0000000000 Binary files a/Ghidra/Framework/Docking/src/main/resources/images/redo.png and /dev/null differ diff --git a/Ghidra/Framework/Docking/src/main/resources/images/undo.png b/Ghidra/Framework/Docking/src/main/resources/images/undo.png deleted file mode 100644 index ee410a9048..0000000000 Binary files a/Ghidra/Framework/Docking/src/main/resources/images/undo.png and /dev/null differ diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/IconModifier.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/IconModifier.java index 0cb67ed483..f7489d1cd3 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/IconModifier.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/IconModifier.java @@ -26,8 +26,7 @@ import javax.swing.Icon; import resources.MultiIcon; import resources.ResourceManager; -import resources.icons.RotateIcon; -import resources.icons.TranslateIcon; +import resources.icons.*; /** * Class that can transform one icon into another. Useful for scaling, translating, disabling, @@ -39,6 +38,8 @@ public class IconModifier { Point translation; boolean disabled; Integer rotation; + boolean mirror; // mirrors the image left to right + boolean flip; // flips the image upside down List overlayIconValues = null; /** @@ -47,12 +48,17 @@ public class IconModifier { * @param translation if non-null, translates an icon by this amount * @param rotation if non-null, the amount in degrees to rotate the icon * @param disabled if true, creates a disabled version of the icon + * @param mirror if true, the image will have its x values swapped (left to right) + * @param flip if true, the image will have its y values swapped (turned upside down) */ - public IconModifier(Dimension size, Point translation, Integer rotation, boolean disabled) { + public IconModifier(Dimension size, Point translation, Integer rotation, + boolean disabled, boolean mirror, boolean flip) { this.size = size; this.translation = translation; this.rotation = rotation; this.disabled = disabled; + this.mirror = mirror; + this.flip = flip; } private IconModifier() { @@ -92,6 +98,20 @@ public class IconModifier { disabled = true; } + /** + * Sets the modifier to flip the icon side to side + */ + public void setMirror() { + mirror = true; + } + + /** + * Sets the modifier to flip the icon side to side + */ + public void setFlip() { + flip = true; + } + /** * Modifies the given icon by the any of the modifiers set. * @param icon the icon to be modified @@ -107,8 +127,14 @@ public class IconModifier { if (disabled) { modified = ResourceManager.getDisabledIcon(modified); } + if (mirror) { + modified = new ReflectedIcon(modified, true); + } + if (flip) { + modified = new ReflectedIcon(modified, false); + } if (rotation != null) { - modified = new RotateIcon(icon, rotation); + modified = new RotateIcon(modified, rotation); } if (translation != null) { modified = new TranslateIcon(modified, translation.x, translation.y); @@ -132,6 +158,12 @@ public class IconModifier { if (size != null) { builder.append("[" + "size(" + size.width + "," + size.height + ")]"); } + if (mirror) { + builder.append("[mirror]"); + } + if (flip) { + builder.append("[flip]"); + } if (rotation != null) { builder.append("[rotate(" + rotation + ")]"); } @@ -195,6 +227,12 @@ public class IconModifier { else if (modifierString.startsWith("move")) { parseMoveModifier(modifier, modifierString); } + else if (modifierString.startsWith("mirror")) { + parseMirrorModifier(modifier, modifierString); + } + else if (modifierString.startsWith("flip")) { + parseFlipModifier(modifier, modifierString); + } else if (modifierString.startsWith("rotate")) { parseRotateModifier(modifier, modifierString); } @@ -228,7 +266,7 @@ public class IconModifier { private boolean hadModifications() { return size != null || translation != null || overlayIconValues != null || - rotation != null || disabled; + rotation != null || disabled || mirror || flip; } private static void parseDisabledModifier(IconModifier modifier, String modifierString) @@ -239,6 +277,22 @@ public class IconModifier { modifier.setDisabled(); } + private static void parseMirrorModifier(IconModifier modifier, String modifierString) + throws ParseException { + if (!modifierString.equals("mirror")) { + throw new ParseException("Illegal Icon modifier: " + modifier, 0); + } + modifier.setMirror(); + } + + private static void parseFlipModifier(IconModifier modifier, String modifierString) + throws ParseException { + if (!modifierString.equals("flip")) { + throw new ParseException("Illegal Icon modifier: " + modifier, 0); + } + modifier.setFlip(); + } + private static void parseRotateModifier(IconModifier modifier, String modifierString) throws ParseException { String argsString = modifierString.substring("rotate".length()); diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/ReflectedIcon.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/ReflectedIcon.java new file mode 100644 index 0000000000..7259bd4bfe --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/ReflectedIcon.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package resources.icons; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +/** + * {@link LazyImageIcon} that creates a reflected version of an icon. This creates a version of the + * icon which has had either its x values reflected (left to right) or its y values reflected + * (upside down) + */ +public class ReflectedIcon extends DerivedImageIcon { + + private boolean leftToRight; + + /** + * Construct a icon that is reflected either left to right or upside down. + * @param baseIcon base icon + * @param leftToRight true flips x values, false flips y values + */ + public ReflectedIcon(Icon baseIcon, boolean leftToRight) { + super(baseIcon); + this.leftToRight = leftToRight; + } + + @Override + protected ImageIcon createImageIcon() { + Icon sourceIcon = getSourceIcon(); + Image image = createImage(); + int width = sourceIcon.getIconWidth(); + int height = sourceIcon.getIconHeight(); + + BufferedImage flippedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = (Graphics2D) flippedImage.getGraphics(); + if (leftToRight) { + graphics.drawImage(image, width, 0, -width, height, null); + } + else { + graphics.drawImage(image, 0, height, width, -height, null); + } + graphics.dispose(); + return new ImageIcon(flippedImage, getFilename()); + } +} diff --git a/Ghidra/Framework/Generic/src/test/java/generic/theme/IconModifierTest.java b/Ghidra/Framework/Generic/src/test/java/generic/theme/IconModifierTest.java index 414d9b2332..60acf958a7 100644 --- a/Ghidra/Framework/Generic/src/test/java/generic/theme/IconModifierTest.java +++ b/Ghidra/Framework/Generic/src/test/java/generic/theme/IconModifierTest.java @@ -83,6 +83,20 @@ public class IconModifierTest { assertNotEquals(baseIcon, modifiedIcon); } + @Test + public void testMirrorModifier() throws Exception { + IconModifier modifier = IconModifier.parse("[mirror]"); + Icon modifiedIcon = modifier.modify(baseIcon, values); + assertNotEquals(baseIcon, modifiedIcon); + } + + @Test + public void testFlipModifier() throws Exception { + IconModifier modifier = IconModifier.parse("[flip]"); + Icon modifiedIcon = modifier.modify(baseIcon, values); + assertNotEquals(baseIcon, modifiedIcon); + } + @Test public void testOverlayIcon() throws Exception { IconModifier modifier = IconModifier.parse("{images/flag.png}"); @@ -179,11 +193,13 @@ public class IconModifierTest { @Test public void testGetSerializationString() { //@formatter:off - assertEquals("[size(5,9)]", new IconModifier(new Dimension(5,9), null, null, false).getSerializationString()); - assertEquals("[move(8,7)]", new IconModifier(null, new Point(8,7), null,false).getSerializationString()); - assertEquals("[disabled]", new IconModifier(null, null, null, true).getSerializationString()); - assertEquals("[size(5,0)][move(8,7)][disabled]", new IconModifier(new Dimension(5,0), new Point(8,7), null, true).getSerializationString()); - assertEquals("[rotate(90)]", new IconModifier(null, null, 90, false).getSerializationString()); + assertEquals("[size(5,9)]", new IconModifier(new Dimension(5,9), null, null, false, false, false).getSerializationString()); + assertEquals("[move(8,7)]", new IconModifier(null, new Point(8,7), null,false, false, false).getSerializationString()); + assertEquals("[disabled]", new IconModifier(null, null, null, true, false, false).getSerializationString()); + assertEquals("[size(5,0)][move(8,7)][disabled]", new IconModifier(new Dimension(5,0), new Point(8,7), null, true, false, false).getSerializationString()); + assertEquals("[rotate(90)]", new IconModifier(null, null, 90, false, false, false).getSerializationString()); + assertEquals("[mirror]", new IconModifier(null, null, null, false, true, false).getSerializationString()); + assertEquals("[flip]", new IconModifier(null, null, null, false, false, true).getSerializationString()); //@formatter:on } }