GP-1981 - Checkpoint - Function Call Graph

This commit is contained in:
dragonmacher 2022-07-01 18:36:03 -04:00 committed by ghidragon
parent 16e32317f0
commit 60b1ae8b44
15 changed files with 276 additions and 41 deletions

View file

@ -5,6 +5,7 @@
##MODULE IP: Tango Icons - Public Domain ##MODULE IP: Tango Icons - Public Domain
Module.manifest||GHIDRA||||END| Module.manifest||GHIDRA||||END|
data/ExtensionPoint.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/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/shared/arrow.gif||GHIDRA||reviewed||END| src/main/help/help/shared/arrow.gif||GHIDRA||reviewed||END|
src/main/help/help/shared/close16.gif||GHIDRA||reviewed||END| src/main/help/help/shared/close16.gif||GHIDRA||reviewed||END|

View file

@ -24,12 +24,23 @@ color.bg.functiongraph.paint.icon = rgb(189, 221, 252) // gentle pale blue
[Dark Defaults] [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.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.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) 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

View file

@ -59,8 +59,7 @@ import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable; import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.util.HTMLUtilities; import ghidra.util.*;
import ghidra.util.HelpLocation;
import resources.ResourceManager; import resources.ResourceManager;
public class ListingGraphComponentPanel extends AbstractGraphComponentPanel { public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
@ -561,11 +560,7 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
private Color getToolTipColorForEdge(FGEdge edge) { private Color getToolTipColorForEdge(FGEdge edge) {
FunctionGraphOptions options = controller.getFunctionGraphOptions(); FunctionGraphOptions options = controller.getFunctionGraphOptions();
Color c = options.getColor(edge.getFlowType()); Color c = options.getColor(edge.getFlowType());
return withAlpha(c, 125); return ColorUtils.withAlpha(c, 125);
}
private Color withAlpha(Color c, int alpha) {
return new Color(c.getRed(), c.getGreen(), c.getBlue(), 125);
} }
private Address getPreviewAddress(boolean forward) { private Address getPreviewAddress(boolean forward) {

View file

@ -1,3 +1,25 @@
[Defaults] [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'

View file

@ -26,6 +26,8 @@ import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.border.LineBorder; import javax.swing.border.LineBorder;
import docking.theme.GColor;
import docking.theme.GThemeDefaults.Colors.Palette;
import docking.widgets.EmptyBorderButton; import docking.widgets.EmptyBorderButton;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import ghidra.graph.viewer.vertex.AbstractVisualVertex; import ghidra.graph.viewer.vertex.AbstractVisualVertex;
@ -41,9 +43,10 @@ import resources.ResourceManager;
*/ */
public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvider { public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvider {
// TODO to be made an option in an upcoming ticket //@formatter:off
public static final Color DEFAULT_VERTEX_SHAPE_COLOR = new Color(110, 197, 174); public static final Color DEFAULT_VERTEX_SHAPE_COLOR = new GColor("color.bg.fcg.vertex.default");
private static final Color TOO_BIG_VERTEX_SHAPE_COLOR = Color.LIGHT_GRAY; 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; public static final Icon NOT_ALLOWED_ICON = Icons.ERROR_ICON;
private static final Icon EXPAND_ICON = private static final Icon EXPAND_ICON =
@ -312,8 +315,8 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
private void addToggleButtons() { private void addToggleButtons() {
// hide the button background // hide the button background
toggleInsButton.setBackground(new Color(255, 255, 255, 0)); toggleInsButton.setBackground(Palette.NO_COLOR);
toggleOutsButton.setBackground(new Color(255, 255, 255, 0)); toggleOutsButton.setBackground(Palette.NO_COLOR);
Rectangle parentBounds = vertexImageLabel.getBounds(); Rectangle parentBounds = vertexImageLabel.getBounds();
Dimension size = toggleInsButton.getPreferredSize(); Dimension size = toggleInsButton.getPreferredSize();

View file

@ -21,13 +21,13 @@ import java.awt.Paint;
import com.google.common.base.Function; import com.google.common.base.Function;
import functioncalls.graph.FcgEdge; import functioncalls.graph.FcgEdge;
import ghidra.util.ColorUtils;
/** /**
* Generates colors for a given {@link FcgEdge} * Generates colors for a given {@link FcgEdge}
*/ */
public class FcgEdgePaintTransformer implements Function<FcgEdge, Paint> { public class FcgEdgePaintTransformer implements Function<FcgEdge, Paint> {
// private static final Paint LESS_IMPORTANT_COLOR = new Color(125, 125, 125, 75);
private Color directColor; private Color directColor;
private Color indirectColor; private Color indirectColor;
@ -46,7 +46,7 @@ public class FcgEdgePaintTransformer implements Function<FcgEdge, Paint> {
alphad[0] = c; alphad[0] = c;
for (int i = 1; i < 10; i++) { for (int i = 1; i < 10; i++) {
double newAlpha = 255 - (i * 25.5); 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; return alphad;
} }

View file

@ -15,8 +15,6 @@
*/ */
package functioncalls.graph.view; package functioncalls.graph.view;
import java.awt.Color;
import docking.theme.GColor; import docking.theme.GColor;
import edu.uci.ics.jung.visualization.RenderContext; import edu.uci.ics.jung.visualization.RenderContext;
import functioncalls.graph.*; import functioncalls.graph.*;
@ -38,19 +36,14 @@ public class FcgComponent extends GraphComponent<FcgVertex, FcgEdge, FunctionCal
private FcgVertexPaintTransformer vertexPaintTransformer = private FcgVertexPaintTransformer vertexPaintTransformer =
new FcgVertexPaintTransformer(FcgVertex.DEFAULT_VERTEX_SHAPE_COLOR); new FcgVertexPaintTransformer(FcgVertex.DEFAULT_VERTEX_SHAPE_COLOR);
private Color lightGreen = new Color(143, 197, 143);
private Color lightGray = new Color(233, 233, 233);
// the satellite gets too cluttered, so wash out the edges
private Color washedOutBlack = new GColor("color.fcg.satellite.edge");
private FcgEdgePaintTransformer edgePaintTransformer = private FcgEdgePaintTransformer edgePaintTransformer =
new FcgEdgePaintTransformer(lightGreen, lightGray); new FcgEdgePaintTransformer(new GColor("color.bg.fcg.edge.primary.direct"),
new GColor("color.bg.fcg.edge.primary.indirect"));
private FcgEdgePaintTransformer satelliteEdgePaintTransformer = private FcgEdgePaintTransformer satelliteEdgePaintTransformer =
new FcgEdgePaintTransformer(washedOutBlack, new Color(125, 125, 125, 25)); new FcgEdgePaintTransformer(new GColor("color.bg.fcg.edge.satellite.direct"),
new GColor("color.bg.fcg.edge.satellite.indirect"));
FcgComponent(FunctionCallGraph g) { FcgComponent(FunctionCallGraph g) {
setGraph(g); setGraph(g);
build(); build();
} }
@ -75,7 +68,6 @@ public class FcgComponent extends GraphComponent<FcgVertex, FcgEdge, FunctionCal
renderContext.setEdgeDrawPaintTransformer(edgePaintTransformer); renderContext.setEdgeDrawPaintTransformer(edgePaintTransformer);
renderContext.setArrowFillPaintTransformer(edgePaintTransformer); renderContext.setArrowFillPaintTransformer(edgePaintTransformer);
renderContext.setArrowDrawPaintTransformer(edgePaintTransformer); renderContext.setArrowDrawPaintTransformer(edgePaintTransformer);
} }
@Override @Override

View file

@ -1,7 +1,10 @@
[Defaults] [Defaults]
color.palette.nocolor = rgba(255,255,255,0)
color.palette.black = black color.palette.black = black
color.palette.cyan = cyan color.palette.cyan = cyan
color.palette.lightGray = rgb(192, 192, 192)
color.palette.lightgreen = rgb(127, 255, 127) color.palette.lightgreen = rgb(127, 255, 127)
color.palette.lightred = rgb(255, 127, 127) color.palette.lightred = rgb(255, 127, 127)
color.palette.red = red color.palette.red = red

View file

@ -21,11 +21,20 @@ import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel; import java.awt.image.ColorModel;
public class GColor extends Color implements Refreshable { import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
public class GColor extends Color implements Refreshable {
private static WeakSet<GColor> inUseColors = WeakDataStructureFactory.createCopyOnReadWeakSet();
private String id; private String id;
private Color delegate; private Color delegate;
public static void refreshAll() {
for (GColor gcolor : inUseColors) {
gcolor.refresh();
}
}
public GColor(String id) { public GColor(String id) {
super(0x808080); super(0x808080);
this.id = id; this.id = id;
@ -33,6 +42,7 @@ public class GColor extends Color implements Refreshable {
if (delegate == null) { if (delegate == null) {
delegate = Color.gray; delegate = Color.gray;
} }
inUseColors.add(this);
} }
public String getId() { public String getId() {

View file

@ -55,6 +55,8 @@ public class GThemeDefaults {
* Generic palette colors, using color names, that may be changed along with the theme * Generic palette colors, using color names, that may be changed along with the theme
*/ */
public static class Palette { 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 BLACK = new GColor("color.palette.black");
public static final Color CYAN = new GColor("color.palette.cyan"); public static final Color CYAN = new GColor("color.palette.cyan");
public static final Color RED = new GColor("color.palette.red"); public static final Color RED = new GColor("color.palette.red");

View file

@ -32,7 +32,7 @@ import ghidra.util.filechooser.GhidraFileFilter;
public class GThemeDialog extends DialogComponentProvider { public class GThemeDialog extends DialogComponentProvider {
public GThemeDialog() { public GThemeDialog() {
super("Theme Dialog"); super("Theme Dialog", false);
addWorkPanel(createMainPanel()); addWorkPanel(createMainPanel());
addOKButton(); addOKButton();
addCancelButton(); addCancelButton();
@ -80,6 +80,9 @@ public class GThemeDialog extends DialogComponentProvider {
colorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); colorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
GFilterTable<ColorValue> filterTable = new GFilterTable<>(colorTableModel); GFilterTable<ColorValue> filterTable = new GFilterTable<>(colorTableModel);
filterTable.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); filterTable.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
filterTable.getTable()
.setDefaultEditor(ColorValue.class,
new ThemeColorEditor(Gui.getAllValues(), colorTableModel));
return filterTable; return filterTable;
} }

View file

@ -307,4 +307,10 @@ public class Gui {
return map; return map;
} }
public static void setColor(String id, Color color) {
currentValues.addColor(new ColorValue(id, color));
GColor.refreshAll();
}
} }

View file

@ -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));
}
}
}

View file

@ -38,6 +38,11 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
colors = Gui.getAllValues().getColors(); colors = Gui.getAllValues().getColors();
} }
public void refresh() {
colors = Gui.getAllValues().getColors();
fireTableDataChanged();
}
@Override @Override
public String getName() { public String getName() {
return "Users"; return "Users";
@ -48,6 +53,10 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
return colors; return colors;
} }
public boolean isCellEditable(int row, int column) {
return getColumnName(column).equals("Current Color");
}
@Override @Override
protected TableColumnDescriptor<ColorValue> createTableColumnDescriptor() { protected TableColumnDescriptor<ColorValue> createTableColumnDescriptor() {
TableColumnDescriptor<ColorValue> descriptor = new TableColumnDescriptor<>(); TableColumnDescriptor<ColorValue> descriptor = new TableColumnDescriptor<>();
@ -78,7 +87,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
} }
} }
class ValueColumn extends AbstractDynamicTableColumn<ColorValue, String, Object> { class ValueColumn extends AbstractDynamicTableColumn<ColorValue, ColorValue, Object> {
private ThemeColorRenderer renderer = new ThemeColorRenderer(Gui.getAllValues()); private ThemeColorRenderer renderer = new ThemeColorRenderer(Gui.getAllValues());
private GThemeValueMap valueMap; private GThemeValueMap valueMap;
private String name; private String name;
@ -95,18 +104,19 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
} }
@Override @Override
public String getValue(ColorValue themeColor, Settings settings, Object data, public ColorValue getValue(ColorValue themeColor, Settings settings, Object data,
ServiceProvider provider) throws IllegalArgumentException { ServiceProvider provider) throws IllegalArgumentException {
return themeColor.getId(); return themeColor;
} }
@Override @Override
public GColumnRenderer<String> getColumnRenderer() { public GColumnRenderer<ColorValue> getColumnRenderer() {
return renderer; return renderer;
} }
public Comparator<String> getComparator() { public Comparator<ColorValue> getComparator() {
return (s1, s2) -> valueMap.getColor(s1).compareValue(valueMap.getColor(s2)); return (v1, v2) -> valueMap.getColor(v1.getId())
.compareValue(valueMap.getColor(v2.getId()));
} }
} }
@ -125,7 +135,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
} }
} }
private class ThemeColorRenderer extends AbstractGColumnRenderer<String> { private class ThemeColorRenderer extends AbstractGColumnRenderer<ColorValue> {
private GThemeValueMap valueMap; private GThemeValueMap valueMap;
@ -138,7 +148,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
public Component getTableCellRendererComponent(GTableCellRenderingData data) { public Component getTableCellRendererComponent(GTableCellRenderingData data) {
JLabel label = (JLabel) super.getTableCellRendererComponent(data); JLabel label = (JLabel) super.getTableCellRendererComponent(data);
String id = (String) data.getValue(); String id = ((ColorValue) data.getValue()).getId();
ColorValue colorValue = valueMap.getColor(id); ColorValue colorValue = valueMap.getColor(id);
Color color; Color color;
@ -169,8 +179,9 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
} }
@Override @Override
public String getFilterString(String id, Settings settings) { public String getFilterString(ColorValue t, Settings settings) {
return id; return t.getId();
} }
} }
} }

View file

@ -191,6 +191,17 @@ public class ColorUtils {
return color; return color;
} }
/**
* Returns a new color that is comprised of the given color's rgb value and the given alpha
* value.
* @param c the color
* @param alpha the alpha
* @return the new color
*/
public static Color withAlpha(Color c, int alpha) {
return new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha);
}
/** /**
* Blender of colors * Blender of colors
*/ */