diff --git a/Ghidra/Features/FunctionGraph/certification.manifest b/Ghidra/Features/FunctionGraph/certification.manifest index 67278e5076..b2032df49a 100644 --- a/Ghidra/Features/FunctionGraph/certification.manifest +++ b/Ghidra/Features/FunctionGraph/certification.manifest @@ -5,6 +5,7 @@ ##MODULE IP: Tango Icons - Public Domain Module.manifest||GHIDRA||||END| data/ExtensionPoint.manifest||GHIDRA||||END| +data/functiongraph.theme.properties||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||reviewed||END| diff --git a/Ghidra/Features/Base/data/base.functiongraph.theme.properties b/Ghidra/Features/FunctionGraph/data/functiongraph.theme.properties similarity index 66% rename from Ghidra/Features/Base/data/base.functiongraph.theme.properties rename to Ghidra/Features/FunctionGraph/data/functiongraph.theme.properties index 7429d9943a..6688694e7b 100644 --- a/Ghidra/Features/Base/data/base.functiongraph.theme.properties +++ b/Ghidra/Features/FunctionGraph/data/functiongraph.theme.properties @@ -24,12 +24,23 @@ color.bg.functiongraph.paint.icon = rgb(189, 221, 252) // gentle pale blue [Dark Defaults] -color.bg.functiongraph.vertex.group = rgb(226, 222, 179) // TODO confirm value +// color.bg.functiongraph = color.bg +// color.fg.label.picked = color.fg +// color.fg.label.non-picked = color.fg.disabled + +color.bg.functiongraph.vertex.group = rgb(226, 222, 179) // TODO confirm value +// color.bg.functiongraph.vertex.entry = color.palette.lightgreen +// color.bg.functiongraph.vertex.exit = color.palette.lightred +// color.bg.functiongraph.vertex.picked = color.palette.yellow + +// color.bg.functiongraph.edge.fall-through = color.flowtype.fall-through color.bg.functiongraph.edge.fall-through.highlight = rgb(165, 76, 80) +// color.bg.functiongraph.edge.jump.conditional = color.flowtype.jump.conditional color.bg.functiongraph.edge.jump.conditional.highlight = rgb(95, 160, 196) +// color.bg.functiongraph.edge.jump.unconditional = color.flowtype.jump.unconditional color.bg.functiongraph.edge.jump.unconditional.highlight = rgb(140, 162, 88) -// TODO dark version color.bg.functiongraph.paint.icon = +// TODO dark version color.bg.functiongraph.paint.icon = // TODO diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/vertex/ListingGraphComponentPanel.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/vertex/ListingGraphComponentPanel.java index 360c5cc6b2..cfb8e5377f 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/vertex/ListingGraphComponentPanel.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/vertex/ListingGraphComponentPanel.java @@ -59,8 +59,7 @@ import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.SymbolTable; import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramSelection; -import ghidra.util.HTMLUtilities; -import ghidra.util.HelpLocation; +import ghidra.util.*; import resources.ResourceManager; public class ListingGraphComponentPanel extends AbstractGraphComponentPanel { @@ -561,11 +560,7 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel { private Color getToolTipColorForEdge(FGEdge edge) { FunctionGraphOptions options = controller.getFunctionGraphOptions(); Color c = options.getColor(edge.getFlowType()); - return withAlpha(c, 125); - } - - private Color withAlpha(Color c, int alpha) { - return new Color(c.getRed(), c.getGreen(), c.getBlue(), 125); + return ColorUtils.withAlpha(c, 125); } private Address getPreviewAddress(boolean forward) { diff --git a/Ghidra/Features/GraphFunctionCalls/data/functioncall.theme.properties b/Ghidra/Features/GraphFunctionCalls/data/functioncall.theme.properties index e017ff6a17..e62242507f 100644 --- a/Ghidra/Features/GraphFunctionCalls/data/functioncall.theme.properties +++ b/Ghidra/Features/GraphFunctionCalls/data/functioncall.theme.properties @@ -1,3 +1,25 @@ [Defaults] -color.fcg.satellite.edge = rgba(0,0,0,0.1) +color.bg.fcg.vertex.default = rgb(110, 197, 174) // chill green +color.bg.fcg.vertex.toobig = color.palette.lightGray + +color.bg.fcg.edge.primary.direct = rgb(143, 197, 143) // lightGreen +color.bg.fcg.edge.primary.indirect = rgb(233, 233, 233) // lightGray + +// the satellite gets too cluttered, so wash out the edges +color.bg.fcg.edge.satellite.direct = rgba(0,0,0,0.1) // 'washed out black' +color.bg.fcg.edge.satellite.indirect = rgba(125, 125, 125, 25) // 'washed out gray' + + +[Dark Defaults] + +// TODO dark colors +// TODO color.bg.fcg.vertex.default = rgb(110, 197, 174) // chill green +// TODO color.bg.fcg.vertex.toobig = color.palette.lightGray + +// TODO color.bg.fcg.edge.primary.direct = rgb(143, 197, 143) // lightGreen +// TODO color.bg.fcg.edge.primary.indirect = rgb(233, 233, 233) // lightGray + +// the satellite gets too cluttered, so wash out the edges +// TODO color.bg.fcg.edge.satellite.direct = rgba(0,0,0,0.1) // 'washed out black' +// TODO color.bg.fcg.edge.satellite.indirect = rgba(125, 125, 125, 25) // 'washed out gray' \ No newline at end of file diff --git a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java index 2f8a1e73ef..81746e4fb6 100644 --- a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java +++ b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java @@ -26,6 +26,8 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.LineBorder; +import docking.theme.GColor; +import docking.theme.GThemeDefaults.Colors.Palette; import docking.widgets.EmptyBorderButton; import docking.widgets.label.GDLabel; import ghidra.graph.viewer.vertex.AbstractVisualVertex; @@ -41,9 +43,10 @@ import resources.ResourceManager; */ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvider { - // TODO to be made an option in an upcoming ticket - public static final Color DEFAULT_VERTEX_SHAPE_COLOR = new Color(110, 197, 174); - private static final Color TOO_BIG_VERTEX_SHAPE_COLOR = Color.LIGHT_GRAY; + //@formatter:off + public static final Color DEFAULT_VERTEX_SHAPE_COLOR = new GColor("color.bg.fcg.vertex.default"); + private static final Color TOO_BIG_VERTEX_SHAPE_COLOR = new GColor("color.bg.fcg.vertex.toobig "); + //@formatter:on public static final Icon NOT_ALLOWED_ICON = Icons.ERROR_ICON; private static final Icon EXPAND_ICON = @@ -312,8 +315,8 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid private void addToggleButtons() { // hide the button background - toggleInsButton.setBackground(new Color(255, 255, 255, 0)); - toggleOutsButton.setBackground(new Color(255, 255, 255, 0)); + toggleInsButton.setBackground(Palette.NO_COLOR); + toggleOutsButton.setBackground(Palette.NO_COLOR); Rectangle parentBounds = vertexImageLabel.getBounds(); Dimension size = toggleInsButton.getPreferredSize(); diff --git a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/renderer/FcgEdgePaintTransformer.java b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/renderer/FcgEdgePaintTransformer.java index 7b09ef621d..eda4faca1b 100644 --- a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/renderer/FcgEdgePaintTransformer.java +++ b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/renderer/FcgEdgePaintTransformer.java @@ -21,13 +21,13 @@ import java.awt.Paint; import com.google.common.base.Function; import functioncalls.graph.FcgEdge; +import ghidra.util.ColorUtils; /** * Generates colors for a given {@link FcgEdge} */ public class FcgEdgePaintTransformer implements Function { - // private static final Paint LESS_IMPORTANT_COLOR = new Color(125, 125, 125, 75); private Color directColor; private Color indirectColor; @@ -46,7 +46,7 @@ public class FcgEdgePaintTransformer implements Function { alphad[0] = c; for (int i = 1; i < 10; i++) { double newAlpha = 255 - (i * 25.5); - alphad[i] = new Color(c.getRed(), c.getGreen(), c.getBlue(), (int) newAlpha); + alphad[i] = ColorUtils.withAlpha(c, (int) newAlpha); } return alphad; } diff --git a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java index 457fe4726d..001b16844a 100644 --- a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java +++ b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java @@ -15,8 +15,6 @@ */ package functioncalls.graph.view; -import java.awt.Color; - import docking.theme.GColor; import edu.uci.ics.jung.visualization.RenderContext; import functioncalls.graph.*; @@ -38,19 +36,14 @@ public class FcgComponent extends GraphComponent inUseColors = WeakDataStructureFactory.createCopyOnReadWeakSet(); private String id; private Color delegate; + public static void refreshAll() { + for (GColor gcolor : inUseColors) { + gcolor.refresh(); + } + } + public GColor(String id) { super(0x808080); this.id = id; @@ -33,6 +42,7 @@ public class GColor extends Color implements Refreshable { if (delegate == null) { delegate = Color.gray; } + inUseColors.add(this); } public String getId() { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/GThemeDefaults.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/GThemeDefaults.java index 7fed2b8316..d4f250235f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/theme/GThemeDefaults.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/GThemeDefaults.java @@ -55,6 +55,8 @@ public class GThemeDefaults { * Generic palette colors, using color names, that may be changed along with the theme */ public static class Palette { + public static final Color NO_COLOR = new GColor("color.palette.nocolor"); + public static final Color BLACK = new GColor("color.palette.black"); public static final Color CYAN = new GColor("color.palette.cyan"); public static final Color RED = new GColor("color.palette.red"); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/GThemeDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/GThemeDialog.java index 5b8e3775f3..b4718b3ab9 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/theme/GThemeDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/GThemeDialog.java @@ -32,7 +32,7 @@ import ghidra.util.filechooser.GhidraFileFilter; public class GThemeDialog extends DialogComponentProvider { public GThemeDialog() { - super("Theme Dialog"); + super("Theme Dialog", false); addWorkPanel(createMainPanel()); addOKButton(); addCancelButton(); @@ -80,6 +80,9 @@ public class GThemeDialog extends DialogComponentProvider { colorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); GFilterTable filterTable = new GFilterTable<>(colorTableModel); filterTable.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + filterTable.getTable() + .setDefaultEditor(ColorValue.class, + new ThemeColorEditor(Gui.getAllValues(), colorTableModel)); return filterTable; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/Gui.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/Gui.java index a5029a8193..6fdaed71e2 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/theme/Gui.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/Gui.java @@ -307,4 +307,10 @@ public class Gui { return map; } + public static void setColor(String id, Color color) { + currentValues.addColor(new ColorValue(id, color)); + GColor.refreshAll(); + + } + } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/ThemeColorEditor.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/ThemeColorEditor.java new file mode 100644 index 0000000000..553d31749b --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/ThemeColorEditor.java @@ -0,0 +1,165 @@ +/* ### + * 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 docking.theme; + +import java.awt.*; +import java.awt.event.MouseEvent; +import java.util.EventObject; + +import javax.swing.*; +import javax.swing.border.BevelBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.table.TableCellEditor; + +import docking.DialogComponentProvider; +import docking.DockingWindowManager; +import docking.options.editor.GhidraColorChooser; +import docking.widgets.label.GDLabel; + +public class ThemeColorEditor extends AbstractCellEditor implements TableCellEditor { + private GhidraColorChooser colorChooser; + private Color lastUserSelectedColor; + private Color color; + + private ColorDialogProvider dialog; + private JTable table; + private ColorValue colorValue; + + private GThemeValueMap values; + private ThemeColorTableModel model; + + public ThemeColorEditor(GThemeValueMap values, ThemeColorTableModel model) { + this.values = values; + this.model = model; + } + + @Override + public Component getTableCellEditorComponent(JTable theTable, Object value, boolean isSelected, + int row, int column) { + + this.table = theTable; + colorValue = (ColorValue) value; + + JLabel label = new GDLabel(); + label.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + label.setText(colorValue.getId()); + + dialog = new ColorDialogProvider(); + dialog.setRememberSize(false); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + DockingWindowManager.showDialog(dialog); + stopCellEditing(); + } + }); + + return label; + } + + @Override + public void cancelCellEditing() { + dialog.close(); + } + + @Override + public Object getCellEditorValue() { + return null; + } + + @Override + public boolean stopCellEditing() { + ListSelectionModel columnSelectionModel = table.getColumnModel().getSelectionModel(); + columnSelectionModel.setValueIsAdjusting(true); + int columnAnchor = columnSelectionModel.getAnchorSelectionIndex(); + int columnLead = columnSelectionModel.getLeadSelectionIndex(); + + if (color != null) { + Gui.setColor(colorValue.getId(), color); + model.refresh(); + } + dialog.close(); + fireEditingStopped(); + + columnSelectionModel.setAnchorSelectionIndex(columnAnchor); + columnSelectionModel.setLeadSelectionIndex(columnLead); + columnSelectionModel.setValueIsAdjusting(false); + + return true; + } + + // only double-click edits + @Override + public boolean isCellEditable(EventObject anEvent) { + if (anEvent instanceof MouseEvent) { + return ((MouseEvent) anEvent).getClickCount() >= 2; + } + return true; + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + + class ColorDialogProvider extends DialogComponentProvider { + ColorDialogProvider() { + super("Color Editor", true); + + addWorkPanel(new ColorEditorPanel()); + addOKButton(); + addCancelButton(); + } + + @Override + protected void okCallback() { + color = lastUserSelectedColor; + close(); + } + + @Override + protected void cancelCallback() { + color = null; + close(); + } + } + + class ColorEditorPanel extends JPanel { + + ColorEditorPanel() { + + setLayout(new BorderLayout()); + + if (colorChooser == null) { + colorChooser = new GhidraColorChooser(); + } + + add(colorChooser, BorderLayout.CENTER); + colorChooser.getSelectionModel().addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + lastUserSelectedColor = colorChooser.getColor(); + // This could be a ColorUIResource, but Options only support storing Color. + lastUserSelectedColor = + new Color(lastUserSelectedColor.getRed(), lastUserSelectedColor.getGreen(), + lastUserSelectedColor.getBlue(), lastUserSelectedColor.getAlpha()); + } + }); + colorChooser.setColor(colorValue.get(values)); + } + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/ThemeColorTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/ThemeColorTableModel.java index 80b426cbbe..2b24884982 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/theme/ThemeColorTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/ThemeColorTableModel.java @@ -38,6 +38,11 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel createTableColumnDescriptor() { TableColumnDescriptor descriptor = new TableColumnDescriptor<>(); @@ -78,7 +87,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel { + class ValueColumn extends AbstractDynamicTableColumn { private ThemeColorRenderer renderer = new ThemeColorRenderer(Gui.getAllValues()); private GThemeValueMap valueMap; private String name; @@ -95,18 +104,19 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel getColumnRenderer() { + public GColumnRenderer getColumnRenderer() { return renderer; } - public Comparator getComparator() { - return (s1, s2) -> valueMap.getColor(s1).compareValue(valueMap.getColor(s2)); + public Comparator getComparator() { + return (v1, v2) -> valueMap.getColor(v1.getId()) + .compareValue(valueMap.getColor(v2.getId())); } } @@ -125,7 +135,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel { + private class ThemeColorRenderer extends AbstractGColumnRenderer { private GThemeValueMap valueMap; @@ -138,7 +148,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel