diff --git a/Ghidra/Features/Base/data/base.listing.theme.properties b/Ghidra/Features/Base/data/base.listing.theme.properties index f9fc4a35c4..3d129356c7 100644 --- a/Ghidra/Features/Base/data/base.listing.theme.properties +++ b/Ghidra/Features/Base/data/base.listing.theme.properties @@ -111,7 +111,7 @@ color.fg.listing.comment.auto = color.fg color.fg.listing.comment.ref.repeatable = color.palette.darkcyan color.fg.listing.function.callfixup = color.palette.magenta color.fg.listing.function.param.dynamic = color.palette.teal -color.fg.listing.function.tag = color.palette.violetred +color.fg.listing.function.tag = #ff7090 color.fg.listing.label.primary = color.palette.cyan color.fg.listing.mnemonic.override = color.palette.pink diff --git a/Ghidra/Features/Base/data/base.theme.properties b/Ghidra/Features/Base/data/base.theme.properties index c003efd1a2..5f2b9a4f55 100644 --- a/Ghidra/Features/Base/data/base.theme.properties +++ b/Ghidra/Features/Base/data/base.theme.properties @@ -172,8 +172,6 @@ font.plugin.tips.label = font.plugin.tips[BOLD] [Dark Defaults] -color.bg = rgb(40, 42, 46) // TODO this should be in a more generic module - color.bg.undefined = #5C4D68 color.flowtype.fall.through = rgb(164, 66, 66) diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index da903893ec..9547fd487f 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -71,6 +71,11 @@ src/main/doc/sleigh_common.xsl||GHIDRA||||END| src/main/doc/sleigh_html.xsl||GHIDRA||||END| src/main/doc/sleigh_pdf.xsl||GHIDRA||||END| src/main/help/help/TOC_Source.xml||GHIDRA||||END| +src/main/help/help/shared/arrow.gif||GHIDRA||reviewed||END| +src/main/help/help/shared/close16.gif||GHIDRA||||END| +src/main/help/help/shared/note.png||Oxygen Icons - LGPL 3.0||||END| +src/main/help/help/shared/note.yellow.png||Oxygen Icons - LGPL 3.0||||END| +src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0||||END| src/main/help/help/topics/DecompilePlugin/DecompilerAnnotations.html||GHIDRA||||END| src/main/help/help/topics/DecompilePlugin/DecompilerConcepts.html||GHIDRA||||END| src/main/help/help/topics/DecompilePlugin/DecompilerIntro.html||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/data/decompiler.theme.properties b/Ghidra/Features/Decompiler/data/decompiler.theme.properties index d25ca6ec0b..685adabd1d 100644 --- a/Ghidra/Features/Decompiler/data/decompiler.theme.properties +++ b/Ghidra/Features/Decompiler/data/decompiler.theme.properties @@ -50,11 +50,8 @@ color.fg.decompiler.error = color.palette.red color.fg.decompiler.variable = color.palette.olive color.fg.decompiler.constant = color.palette.green color.fg.decompiler.type = color.palette.cyan -color.fg.decompiler.parameter = color.palette.violetred +color.fg.decompiler.parameter = #ff7090 color.fg.decompiler.global = color.palette.teal color.fg.decompiler.special = color.palette.red - -color.bg.decompiler.middle.mouse = rgb(55,59,65) color.bg.decompiler.current.variable = rgb(55, 59, 65) - diff --git a/Ghidra/Framework/Docking/data/docking.palette.theme.properties b/Ghidra/Framework/Docking/data/docking.palette.theme.properties index 00a03ef37a..ff510cd5be 100644 --- a/Ghidra/Framework/Docking/data/docking.palette.theme.properties +++ b/Ghidra/Framework/Docking/data/docking.palette.theme.properties @@ -10,6 +10,8 @@ color.palette.cornsilk = cornsilk color.palette.crimson = crimson color.palette.cyan = cyan color.palette.darkblue = DarkBlue +color.palette.darkcyan = darkcyan + color.palette.darkgray = DarkGray color.palette.darkgreen = darkgreen color.palette.darkorange = darkorange @@ -50,9 +52,9 @@ color.palette.silver = silver color.palette.steelblue = steelblue color.palette.tan = tan color.palette.teal = teal +color.palette.white = white color.palette.yellow = yellow color.palette.yellowgreen = yellowgreen -color.palette.white = white @@ -74,6 +76,5 @@ color.palette.yellow = #ffff80 color.palette.olive = #c0c080 color.palette.orange = #ffa070 color.palette.darkorange = orange -color.palette.violetred = #ff7090 diff --git a/Ghidra/Framework/Docking/data/docking.theme.properties b/Ghidra/Framework/Docking/data/docking.theme.properties index 6fdd1754d1..6d53316e9a 100644 --- a/Ghidra/Framework/Docking/data/docking.theme.properties +++ b/Ghidra/Framework/Docking/data/docking.theme.properties @@ -224,7 +224,7 @@ color.fg.dialog.status.normal = lightBlue color.bg.currentline = rgb(40,40,56) // dark bluish gray color.cursor.focused = indianRed -color.cursor.unfocussed = darkGray +color.cursor.unfocused = darkGray color.bg.textfield.hint.invalid = maroon diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/AbstractThemeReader.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/AbstractThemeReader.java index b587e94058..763f606358 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/AbstractThemeReader.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/AbstractThemeReader.java @@ -190,8 +190,15 @@ public abstract class AbstractThemeReader { } protected void error(int lineNumber, String message) { - String msg = - "Error parsing theme file \"" + source + "\" at line: " + lineNumber + ", " + message; + StringBuilder builder = new StringBuilder(); + builder.append("Error parsing theme file \"" + source + "\""); + + if (lineNumber >= 0) { + builder.append(" at line: " + lineNumber); + } + builder.append(". "); + builder.append(message); + String msg = builder.toString(); errors.add(msg); outputError(msg); } diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/GThemeValueMap.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/GThemeValueMap.java index 865a2a0478..fb20579c6d 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/GThemeValueMap.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/GThemeValueMap.java @@ -317,4 +317,27 @@ public class GThemeValueMap { } } + /** + * Returns the set of all color ids in this map + * @return the set of all color ids in this map + */ + public Set getColorIds() { + return colorMap.keySet(); + } + + /** + * Returns the set of all font ids in this map + * @return the set of all font ids in this map + */ + public Set getFontIds() { + return fontMap.keySet(); + } + + /** + * Returns the set of all icon ids in this map + * @return the set of all icon ids in this map + */ + public Set getIconIds() { + return iconMap.keySet(); + } } diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePropertyFileReader.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePropertyFileReader.java index 9a503366e8..4b1bc12c53 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePropertyFileReader.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePropertyFileReader.java @@ -26,9 +26,10 @@ import generic.jar.ResourceFile; */ public class ThemePropertyFileReader extends AbstractThemeReader { - private GThemeValueMap defaults; - private GThemeValueMap darkDefaults; + private GThemeValueMap defaults = new GThemeValueMap(); + private GThemeValueMap darkDefaults = new GThemeValueMap(); private Map customSectionsMap = new HashMap<>(); + private boolean defaultSectionProcessed; /** * Constructor for when the the theme.properties file is a {@link ResourceFile} @@ -41,7 +42,6 @@ public class ThemePropertyFileReader extends AbstractThemeReader { try (Reader reader = new InputStreamReader(file.getInputStream())) { read(reader); } - } /** @@ -60,7 +60,7 @@ public class ThemePropertyFileReader extends AbstractThemeReader { * @return the map of standard defaults values. */ public GThemeValueMap getDefaultValues() { - return defaults == null ? new GThemeValueMap() : defaults; + return defaults; } /** @@ -68,7 +68,7 @@ public class ThemePropertyFileReader extends AbstractThemeReader { * @return the map of dark defaults values. */ public GThemeValueMap getDarkDefaultValues() { - return darkDefaults == null ? new GThemeValueMap() : darkDefaults; + return darkDefaults; } /** @@ -81,20 +81,26 @@ public class ThemePropertyFileReader extends AbstractThemeReader { protected void processNoSection(Section section) throws IOException { if (!section.isEmpty()) { - error(0, "Theme properties file has values defined outside of a defined section"); + error(section.getLineNumber(), + "Theme properties file has values defined outside of a defined section"); } } @Override protected void processDefaultSection(Section section) throws IOException { - defaults = new GThemeValueMap(); + defaultSectionProcessed = true; processValues(defaults, section); } @Override protected void processDarkDefaultSection(Section section) throws IOException { - darkDefaults = new GThemeValueMap(); + if (!defaultSectionProcessed) { + error(section.getLineNumber(), + "Defaults section must be defined before Dark Defaults section!"); + return; + } processValues(darkDefaults, section); + validate("Dark Defaults", darkDefaults); } @Override @@ -102,11 +108,42 @@ public class ThemePropertyFileReader extends AbstractThemeReader { String name = section.getName(); LafType lafType = LafType.fromName(name); if (lafType == null) { - error(0, "Unknown Look and Feel section found: " + name); + error(section.getLineNumber(), "Unknown Look and Feel section found: " + name); + return; + } + if (!defaultSectionProcessed) { + error(section.getLineNumber(), + "Defaults section must be defined before " + name + " section!"); return; } GThemeValueMap customValues = new GThemeValueMap(); processValues(customValues, section); customSectionsMap.put(lafType, customValues); + validate(name, customValues); } + + private void validate(String name, GThemeValueMap valuesMap) { + for (String id : valuesMap.getColorIds()) { + if (!defaults.containsColor(id)) { + reportMissingDefaultsError("Color", name, id); + } + } + for (String id : valuesMap.getFontIds()) { + if (!defaults.containsFont(id)) { + reportMissingDefaultsError("Font", name, id); + } + } + for (String id : valuesMap.getIconIds()) { + if (!defaults.containsIcon(id)) { + reportMissingDefaultsError("Icon", name, id); + } + } + } + + private void reportMissingDefaultsError(String type, String name, String id) { + String message = type + " id found in \"" + name + + "\" section, but not defined in \"Defaults\" section: " + id; + error(-1, message); + } + } diff --git a/Ghidra/Framework/Gui/src/test/java/generic/theme/ThemePropertyFileReaderTest.java b/Ghidra/Framework/Gui/src/test/java/generic/theme/ThemePropertyFileReaderTest.java index 21f14e6c9e..c6c574d4a2 100644 --- a/Ghidra/Framework/Gui/src/test/java/generic/theme/ThemePropertyFileReaderTest.java +++ b/Ghidra/Framework/Gui/src/test/java/generic/theme/ThemePropertyFileReaderTest.java @@ -91,6 +91,14 @@ public class ThemePropertyFileReaderTest { public void testDarkDefaults() throws IOException { //@formatter:off ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n", + "[Defaults]", + " color.b.1 = red", + " color.b.2 = red", + " color.b.3 = red", + " color.b.4 = red", + " color.b.5 = red", + " color.b.6 = red", + " color.b.7 = red", "[Dark Defaults]", " color.b.1 = white", // WHITE " color.b.2 = #ff0000", // RED @@ -189,7 +197,7 @@ public class ThemePropertyFileReaderTest { List errors = reader.getErrors(); assertEquals(1, errors.size()); assertEquals( - "Error parsing theme file \"test\" at line: 3, Could not parse Color value: sdfsdf", + "Error parsing theme file \"test\" at line: 3. Could not parse Color value: sdfsdf", errors.get(0)); } @@ -236,6 +244,73 @@ public class ThemePropertyFileReaderTest { } + @Test + public void testColorIdDefinedInNonDefaultsSectionOnly() throws IOException { + //@formatter:off + ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n", + "[Defaults]", + " color.foo = red", + "[Dark Defaults]", + " color.bar = blue", + ""))); + //@formatter:on + List errors = reader.getErrors(); + assertEquals(1, errors.size()); + assertEquals( + "Error parsing theme file \"test\". Color id found in \"Dark Defaults\" section, but not defined in \"Defaults\" section: color.bar", + errors.get(0)); + } + + @Test + public void testFontIdDefinedInNonDefaultsSectionOnly() throws IOException { + //@formatter:off + ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n", + "[Defaults]", + "[Dark Defaults]", + " font.bar = dialog-PLAIN-14", + ""))); + //@formatter:on + List errors = reader.getErrors(); + assertEquals(1, errors.size()); + assertEquals( + "Error parsing theme file \"test\". Font id found in \"Dark Defaults\" section, but not defined in \"Defaults\" section: font.bar", + errors.get(0)); + } + + @Test + public void testIconIdDefinedInNonDefaultsSectionOnly() throws IOException { + //@formatter:off + ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n", + "[Defaults]", + "[Dark Defaults]", + " icon.bar = core.png", + ""))); + //@formatter:on + List errors = reader.getErrors(); + assertEquals(1, errors.size()); + assertEquals( + "Error parsing theme file \"test\". Icon id found in \"Dark Defaults\" section, but not defined in \"Defaults\" section: icon.bar", + errors.get(0)); + } + + @Test + public void testDefaultSectionMustBeFirst() throws Exception { + //@formatter:off + ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n", + "[Dark Defaults]", + " color.foo = red", + "[Defaults]", + " color.bar = blue", + ""))); + //@formatter:on + List errors = reader.getErrors(); + assertEquals(1, errors.size()); + assertEquals( + "Error parsing theme file \"test\" at line: 1. Defaults section must be defined before Dark Defaults section!", + errors.get(0)); + + } + private Color getColor(GThemeValueMap values, String id) { ColorValue color = values.getColor(id); return color.get(values);