GP-6085 - Flow Arrows - Change to a service-based system for margin providers to allow flow arrows in snapshots

This commit is contained in:
dragonmacher
2025-11-21 17:50:32 -05:00
parent d193345dde
commit eb10bd7e08
50 changed files with 2144 additions and 1546 deletions

View File

@@ -85,7 +85,8 @@ import ghidra.util.Msg;
servicesRequired = {
DebuggerLogicalBreakpointService.class,
MarkerService.class,
})
}
)
public class DebuggerBreakpointMarkerPlugin extends Plugin {
private static final Color COLOR_BREAKPOINT_ENABLED_MARKER =
@@ -496,7 +497,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
new ProgramLocationActionContext(null, location.getProgram(),
new ProgramLocation(location.getProgram(), location.getAddr()), null, null);
if (contextCanManipulateBreakpoints(context)) {
doToggleBreakpointsAt(ToggleBreakpointAction.NAME, context);
doToggleBreakpointsAt(AbstractToggleBreakpointAction.NAME, context);
}
}
}
@@ -753,28 +754,32 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND,
description = "Whether or not to color background for memory at an enabled breakpoint",
help = @HelpInfo(anchor = "colors"))
help = @HelpInfo(anchor = "colors")
)
private boolean breakpointEnabledColoringBackground =
DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_COLORING_BACKGROUND;
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND,
description = "Whether or not to color background for memory at a disabled breakpoint",
help = @HelpInfo(anchor = "colors"))
help = @HelpInfo(anchor = "colors")
)
private boolean breakpointDisabledColoringBackground =
DebuggerResources.DEFAULT_COLOR_DISABLED_BREAKPOINT_COLORING_BACKGROUND;
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND,
description = "Whether or not to color background for memory at an enabled, but ineffective, breakpoint",
help = @HelpInfo(anchor = "colors"))
help = @HelpInfo(anchor = "colors")
)
private boolean breakpointIneffEnColoringBackground =
DebuggerResources.DEFAULT_COLOR_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND;
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND,
description = "Whether or not to color background for memory at an disabled, but ineffective, breakpoint",
help = @HelpInfo(anchor = "colors"))
help = @HelpInfo(anchor = "colors")
)
private boolean breakpointIneffDisColoringBackground =
DebuggerResources.DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND;
@@ -832,7 +837,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
}
@AutoOptionConsumed(
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND)
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND
)
private void setEnabledBreakpointMarkerBackground(boolean breakpointColoringBackground) {
for (BreakpointMarkerSets markers : markersByProgram.values()) {
markers.setEnabledColoringBackground(breakpointColoringBackground);
@@ -840,7 +846,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
}
@AutoOptionConsumed(
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND)
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND
)
private void setDisabledBreakpointMarkerBackground(boolean breakpointColoringBackground) {
for (BreakpointMarkerSets markers : markersByProgram.values()) {
markers.setDisabledColoringBackground(breakpointColoringBackground);
@@ -848,7 +855,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
}
@AutoOptionConsumed(
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND)
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND
)
private void setIneffectiveEBreakpointMarkerBackground(boolean breakpointColoringBackground) {
for (BreakpointMarkerSets markers : markersByProgram.values()) {
markers.setIneffectiveEnabledColoringBackground(breakpointColoringBackground);
@@ -856,7 +864,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
}
@AutoOptionConsumed(
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND)
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND
)
private void setIneffectiveDBreakpointMarkerBackground(boolean breakpointColoringBackground) {
for (BreakpointMarkerSets markers : markersByProgram.values()) {
markers.setIneffectiveDisabledColoringBackground(breakpointColoringBackground);
@@ -1089,7 +1098,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
actionDisableBreakpoint = new DisableBreakpointAction();
actionClearBreakpoint = new ClearBreakpointAction();
tool.setMenuGroup(new String[] { SetBreakpointAction.NAME }, SetBreakpointAction.GROUP);
tool.setMenuGroup(new String[] { AbstractSetBreakpointAction.NAME },
SetBreakpointAction.GROUP);
}
@Override

View File

@@ -198,7 +198,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
}
@Override
public void locationChanged(CodeViewerProvider provider, ProgramLocation location) {
public void broadcastLocationChanged(CodeViewerProvider provider, ProgramLocation location) {
// TODO: Fix cursor?
// Do not fire ProgramLocationPluginEvent.
if (provider == connectedProvider) {
@@ -207,7 +207,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
}
@Override
public void selectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
public void broadcastSelectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
if (provider != connectedProvider) {
return;
}
@@ -220,7 +220,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
}
@Override
public void highlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
public void broadcastHighlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
if (provider != connectedProvider) {
return;
}

View File

@@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.debug.gui.listing;
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.ICON_REGISTER_MARKER;
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -130,13 +130,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
}
}
protected class MarkerSetChangeListener implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
getListingPanel().getFieldPanel().repaint();
}
}
protected class ForStaticSyncMappingChangeListener
implements DebuggerStaticMappingChangeListener {
@Override
@@ -335,7 +328,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
protected final JLabel trackingLabel = new JLabel();
protected final MultiBlendedListingBackgroundColorModel colorModel;
protected final MarkerSetChangeListener markerChangeListener = new MarkerSetChangeListener();
protected MarkerServiceBackgroundColorModel markerServiceColorModel;
protected MarkerMarginProvider markerMarginProvider;
protected MarkerOverviewProvider markerOverviewProvider;
@@ -573,28 +565,13 @@ public class DebuggerListingProvider extends CodeViewerProvider {
@AutoServiceConsumed
private void setMarkerService(MarkerService markerService) {
if (this.markerService != null) {
this.markerService.removeChangeListener(markerChangeListener);
removeMarginProvider(markerMarginProvider);
markerMarginProvider = null;
removeOverviewProvider(markerOverviewProvider);
markerOverviewProvider = null;
}
ListingPanel listingPanel = getListingPanel();
listingPanel.setMarkerService(markerService);
removeOldStaticTrackingMarker();
this.markerService = markerService;
createNewStaticTrackingMarker();
updateMarkerServiceColorModel();
if (this.markerService != null && !isMainListing()) {
// NOTE: Connected provider marker listener is taken care of by CodeBrowserPlugin
this.markerService.addChangeListener(markerChangeListener);
}
if (this.markerService != null) {
markerMarginProvider = markerService.createMarginProvider();
addMarginProvider(markerMarginProvider);
markerOverviewProvider = markerService.createOverviewProvider();
addOverviewProvider(markerOverviewProvider);
}
}
@AutoServiceConsumed

View File

@@ -24,7 +24,7 @@ import javax.swing.*;
import docking.action.DockingActionIf;
import ghidra.app.nav.Navigatable;
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program;
@@ -35,7 +35,7 @@ import ghidra.util.task.SwingUpdateManager;
* Overview bar component. Uses color to indicate various snap-based properties for a program.
* Uses an {@link TimeOverviewColorService} to get the appropriate color for a snaps.
*/
public class TimeOverviewColorComponent extends JPanel implements OverviewProvider {
public class TimeOverviewColorComponent extends JPanel implements ListingOverviewProvider {
private static final Color DEFAULT_COLOR = Color.GRAY;
protected TimeOverviewColorService service;
private Color[] colorsByPixel = new Color[0];
@@ -101,7 +101,7 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
stop = start;
start = tmp;
}
span = Lifespan.span(start, stop);
span = Lifespan.span(start, stop);
}
plugin.setLifespan(span);
enableDrag = false;
@@ -113,6 +113,11 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
actions = service.getActions();
}
@Override
public void dispose() {
uninstallActions();
}
/**
* Installs actions for this component
*/
@@ -233,13 +238,13 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
public Lifespan getLifespan() {
return service.getBounds();
}
public void setLifespan(Lifespan bounds) {
service.setBounds(bounds);
}
@Override
public void setProgram(Program program, AddressIndexMap map) {
public void screenDataChanged(Program program, AddressIndexMap map) {
// Ignored
}

View File

@@ -221,8 +221,8 @@ public class TimeOverviewColorPlugin extends AbstractDebuggerPlugin {
private void uninstallOverview(TimeOverviewColorService overviewColorService) {
TimeOverviewColorComponent overviewComponent = activeServices.get(overviewColorService);
overviewComponent.uninstallActions();
listingService.removeOverviewProvider(overviewComponent);
overviewComponent.dispose();
activeServices.remove(overviewColorService);
overviewColorService.setTrace(null);
}

View File

@@ -19,11 +19,9 @@ import java.awt.Color;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import docking.action.DockingAction;
import docking.widgets.fieldpanel.*;
@@ -31,7 +29,6 @@ import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.fieldpanel.support.FieldSelection;
import generic.theme.GColor;
import generic.theme.GIcon;
import ghidra.GhidraOptions;
import ghidra.app.events.ProgramHighlightPluginEvent;
import ghidra.app.events.ProgramSelectionPluginEvent;
@@ -72,24 +69,15 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
private static final GColor CURRENT_LINE_HIGHLIGHT_COLOR = new GColor("color.bg.currentline.listing");
//@formatter:on
// - Icon -
private static final Icon CURSOR_LOC_ICON =
new GIcon("icon.plugin.codebrowser.cursor.location");
protected final P connectedProvider;
protected List<P> disconnectedProviders = new ArrayList<>();
protected FormatManager formatMgr;
protected ViewManagerService viewManager;
private MarkerService markerService;
protected AddressSetView currentView = ImmutableAddressSet.EMPTY_SET;
protected Program currentProgram;
private boolean selectionChanging;
private MarkerSet currentSelectionMarkers;
private MarkerSet currentHighlightMarkers;
private MarkerSet currentCursorMarkers;
private ChangeListener markerChangeListener;
private Color cursorHighlightColor;
private boolean isHighlightCursorLine;
private ProgramDropProvider dndProvider;
public AbstractCodeBrowserPlugin(PluginTool tool) {
@@ -113,7 +101,6 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
initMiscellaneousOptions();
displayOptions.addOptionsChangeListener(this);
fieldOptions.addOptionsChangeListener(this);
markerChangeListener = new MarkerChangeListener(connectedProvider);
}
protected abstract P createProvider(FormatManager formatManager, boolean isConnected);
@@ -138,16 +125,14 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
private void viewUpdated() {
updateBackgroundColorModel();
setHighlight(connectedProvider.getHighlight());
setSelection(connectedProvider.getSelection());
connectedProvider.setHighlight(connectedProvider.getHighlight());
setConnectedProviderSelection(connectedProvider.getSelection());
}
@Override
protected void init() {
markerService = tool.getService(MarkerService.class);
if (markerService != null) {
markerService.addChangeListener(markerChangeListener);
}
MarkerService markerService = tool.getService(MarkerService.class);
setMarkerService(markerService);
updateBackgroundColorModel();
if (viewManager == null) {
@@ -161,20 +146,31 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
provider.setClipboardService(clipboardService);
}
}
ListingMarginProviderService[] marginServices =
tool.getServices(ListingMarginProviderService.class);
for (ListingMarginProviderService marginService : marginServices) {
connectedProvider.addMarginService(marginService);
}
ListingOverviewProviderService[] overviewServices =
tool.getServices(ListingOverviewProviderService.class);
for (ListingOverviewProviderService service : overviewServices) {
connectedProvider.addOverviewService(service);
}
}
protected void updateBackgroundColorModel() {
ListingPanel listingPanel = connectedProvider.getListingPanel();
if (markerService != null) {
AddressIndexMap indexMap = connectedProvider.getListingPanel().getAddressIndexMap();
listingPanel.setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerService, indexMap));
}
else {
listingPanel.setBackgroundColorModel(null);
}
// Note: this should update all providers, not just the connected provider
updateBackgroundColorModel(connectedProvider);
for (CodeViewerProvider provider : disconnectedProviders) {
updateBackgroundColorModel(provider);
}
}
protected void updateBackgroundColorModel(CodeViewerProvider provider) {
ListingPanel listingPanel = provider.getListingPanel();
listingPanel.updateBackgroundColorModel();
}
@Override
@@ -182,20 +178,46 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
P newProvider = createProvider(formatMgr.createClone(), false);
newProvider.setClipboardService(tool.getService(ClipboardService.class));
ListingPanel listingPanel = newProvider.getListingPanel();
FieldPanel fieldPanel = listingPanel.getFieldPanel();
List<ListingPanel> listingPanels = List.of(listingPanel);
List<FieldPanel> fieldPanels = List.of(fieldPanel);
initPanelOptions(listingPanels, fieldPanels);
disconnectedProviders.add(newProvider);
if (dndProvider != null) {
newProvider.addProgramDropProvider(dndProvider);
}
tool.showComponentProvider(newProvider, true);
ListingHoverService[] hoverServices = tool.getServices(ListingHoverService.class);
for (ListingHoverService hoverService : hoverServices) {
newProvider.getListingPanel().addHoverService(hoverService);
listingPanel.addHoverService(hoverService);
}
ListingMarginProviderService[] marginServices =
tool.getServices(ListingMarginProviderService.class);
for (ListingMarginProviderService service : marginServices) {
newProvider.addMarginService(service);
}
ListingOverviewProviderService[] overviewServices =
tool.getServices(ListingOverviewProviderService.class);
for (ListingOverviewProviderService service : overviewServices) {
newProvider.addOverviewService(service);
}
MarkerService markerService = tool.getService(MarkerService.class);
listingPanel.setMarkerService(markerService);
updateBackgroundColorModel(newProvider);
tool.showComponentProvider(newProvider, true);
return newProvider;
}
protected void setHighlight(FieldSelection highlight) {
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
// this is for tool highlights coming in to the plugin
protected void setConnectedProviderHighlight(FieldSelection highlight) {
if (highlight != null && !highlight.isEmpty()) {
ListingPanel listingPanel = connectedProvider.getListingPanel();
@@ -204,17 +226,9 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
firePluginEvent(
new ProgramHighlightPluginEvent(this.getName(), programHighlight, currentProgram));
if (highlightMarkers != null) {
highlightMarkers.clearAll();
highlightMarkers.add(programHighlight);
}
}
else {
connectedProvider.setHighlight(new ProgramSelection());
if (highlightMarkers != null) {
highlightMarkers.clearAll();
}
}
}
@@ -229,10 +243,11 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
viewManager = (ViewManagerService) service;
setView(viewManager.getCurrentView());
}
if (interfaceClass == MarkerService.class && markerService == null) {
markerService = tool.getService(MarkerService.class);
markerService.addChangeListener(markerChangeListener);
if (interfaceClass == MarkerService.class) {
MarkerService markerService = tool.getService(MarkerService.class);
setMarkerService(markerService);
updateBackgroundColorModel();
if (viewManager != null) {
viewUpdated();
}
@@ -248,6 +263,32 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
otherPanel.addHoverService(hoverService);
}
}
if (interfaceClass == ListingMarginProviderService.class) {
ListingMarginProviderService marginService = (ListingMarginProviderService) service;
connectedProvider.addMarginService(marginService);
for (CodeViewerProvider provider : disconnectedProviders) {
provider.addMarginService(marginService);
}
}
if (interfaceClass == ListingOverviewProviderService.class) {
ListingOverviewProviderService overviewService =
(ListingOverviewProviderService) service;
connectedProvider.addOverviewService(overviewService);
for (CodeViewerProvider provider : disconnectedProviders) {
provider.addOverviewService(overviewService);
}
}
}
private void setMarkerService(MarkerService markerService) {
ListingPanel listingPanel = connectedProvider.getListingPanel();
listingPanel.setMarkerService(markerService);
for (CodeViewerProvider provider : disconnectedProviders) {
listingPanel = provider.getListingPanel();
listingPanel.setMarkerService(markerService);
}
}
@Override
@@ -256,42 +297,55 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
viewManager = null;
setView(currentProgram.getMemory());
}
if (service == markerService) {
markerService.removeChangeListener(markerChangeListener);
clearMarkers(currentProgram);
markerService = null;
if (interfaceClass == MarkerService.class) {
setMarkerService(null);
connectedProvider.clearMarkers(currentProgram);
updateBackgroundColorModel();
}
if (interfaceClass == ListingHoverService.class) {
ListingHoverService hoverService = (ListingHoverService) service;
connectedProvider.getListingPanel().removeHoverService(hoverService);
connectedProvider.removeHoverService(hoverService);
for (CodeViewerProvider provider : disconnectedProviders) {
provider.getListingPanel().removeHoverService(hoverService);
provider.removeHoverService(hoverService);
}
ListingPanel otherPanel = connectedProvider.getOtherPanel();
if (otherPanel != null) {
otherPanel.removeHoverService(hoverService);
}
if (interfaceClass == ListingMarginProviderService.class) {
ListingMarginProviderService marginService = (ListingMarginProviderService) service;
connectedProvider.removeMarginService(marginService);
for (CodeViewerProvider provider : disconnectedProviders) {
provider.removeMarginService(marginService);
}
}
if (interfaceClass == ListingOverviewProviderService.class) {
ListingOverviewProviderService overviewService =
(ListingOverviewProviderService) service;
connectedProvider.removeOverviewService(overviewService);
for (CodeViewerProvider provider : disconnectedProviders) {
provider.removeOverviewService(overviewService);
}
}
}
@Override
public void addOverviewProvider(OverviewProvider overviewProvider) {
public void addOverviewProvider(ListingOverviewProvider overviewProvider) {
connectedProvider.addOverviewProvider(overviewProvider);
}
@Override
public void addMarginProvider(MarginProvider marginProvider) {
public void addMarginProvider(ListingMarginProvider marginProvider) {
connectedProvider.addMarginProvider(marginProvider);
}
@Override
public void removeOverviewProvider(OverviewProvider overviewProvider) {
public void removeOverviewProvider(ListingOverviewProvider overviewProvider) {
connectedProvider.removeOverviewProvider(overviewProvider);
}
@Override
public void removeMarginProvider(MarginProvider marginProvider) {
public void removeMarginProvider(ListingMarginProvider marginProvider) {
connectedProvider.removeMarginProvider(marginProvider);
}
@@ -338,10 +392,6 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
connectedProvider.setHighlightProvider(highlightProvider, highlightProgram);
}
protected void updateHighlightProvider() {
connectedProvider.updateHighlightProvider();
}
@Override
public void setListingPanel(ListingPanel lp) {
connectedProvider.setOtherPanel(lp);
@@ -379,7 +429,7 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
if (currentProgram != null) {
currentProgram.removeListener(this);
}
clearMarkers(currentProgram);
connectedProvider.clearMarkers(currentProgram);
formatMgr.dispose();
removeProvider(connectedProvider);
for (CodeViewerProvider provider : disconnectedProviders) {
@@ -391,68 +441,94 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) {
ListingPanel listingPanel = connectedProvider.getListingPanel();
if (options.getName().equals(GhidraOptions.CATEGORY_BROWSER_FIELDS)) {
if (!options.getName().equals(GhidraOptions.CATEGORY_BROWSER_FIELDS)) {
return;
}
FieldPanel fieldPanel = listingPanel.getFieldPanel();
if (optionName.equals(GhidraOptions.OPTION_SELECTION_COLOR)) {
Color color = ((Color) newValue);
fieldPanel.setSelectionColor(color);
MarkerSet selectionMarkers = getSelectionMarkers(currentProgram);
if (selectionMarkers != null) {
selectionMarkers.setMarkerColor(color);
}
ListingPanel otherPanel = connectedProvider.getOtherPanel();
if (otherPanel != null) {
otherPanel.getFieldPanel().setSelectionColor(color);
}
}
else if (optionName.equals(GhidraOptions.OPTION_HIGHLIGHT_COLOR)) {
Color color = ((Color) newValue);
fieldPanel.setHighlightColor(color);
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
if (highlightMarkers != null) {
highlightMarkers.setMarkerColor(color);
}
}
else if (optionName.equals(CURSOR_COLOR_OPTIONS_NAME)) {
Color color = ((Color) newValue);
fieldPanel.setFocusedCursorColor(color);
}
else if (optionName.equals(UNFOCUSED_CURSOR_COLOR_OPTIONS_NAME)) {
Color color = ((Color) newValue);
fieldPanel.setNonFocusCursorColor(color);
}
else if (optionName.equals(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR)) {
cursorHighlightColor = (Color) newValue;
if (currentCursorMarkers != null) {
currentCursorMarkers.setMarkerColor(cursorHighlightColor);
}
}
else if (optionName.equals(GhidraOptions.HIGHLIGHT_CURSOR_LINE)) {
isHighlightCursorLine = (Boolean) newValue;
if (currentCursorMarkers != null) {
currentCursorMarkers.setColoringBackground(isHighlightCursorLine);
}
}
else if (optionName.equals(MOUSE_WHEEL_HORIZONTAL_SCROLLING_OPTIONS_NAME)) {
fieldPanel.setHorizontalScrollingEnabled((Boolean) newValue);
}
List<ListingPanel> listingPanels = allListingPanels();
List<FieldPanel> fieldPanels = allFieldPanels();
if (optionName.equals(GhidraOptions.OPTION_SELECTION_COLOR)) {
Color color = ((Color) newValue);
onListingPanels(listingPanels, lp -> lp.setSelectionColor(color));
}
else if (optionName.equals(GhidraOptions.OPTION_HIGHLIGHT_COLOR)) {
Color color = ((Color) newValue);
onListingPanels(listingPanels, lp -> lp.setHighlightColor(color));
}
else if (optionName.equals(CURSOR_COLOR_OPTIONS_NAME)) {
Color color = ((Color) newValue);
onFieldPanels(fieldPanels, fp -> fp.setFocusedCursorColor(color));
}
else if (optionName.equals(UNFOCUSED_CURSOR_COLOR_OPTIONS_NAME)) {
Color color = ((Color) newValue);
onFieldPanels(fieldPanels, fp -> fp.setNonFocusCursorColor(color));
}
else if (optionName.equals(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR)) {
Color color = (Color) newValue;
onListingPanels(listingPanels, lp -> lp.setCursorHighlightColor(color));
}
else if (optionName.equals(GhidraOptions.HIGHLIGHT_CURSOR_LINE)) {
Boolean doHighlight = (Boolean) newValue;
onListingPanels(listingPanels, lp -> lp.setHighlightCursorLineEnabled(doHighlight));
}
else if (optionName.equals(MOUSE_WHEEL_HORIZONTAL_SCROLLING_OPTIONS_NAME)) {
Boolean doScroll = (Boolean) newValue;
onFieldPanels(fieldPanels, fp -> fp.setHorizontalScrollingEnabled(doScroll));
}
}
protected void onFieldPanels(List<FieldPanel> panels, Consumer<FieldPanel> c) {
for (FieldPanel fp : panels) {
c.accept(fp);
}
}
protected void onListingPanels(List<ListingPanel> listingPanels, Consumer<ListingPanel> c) {
for (ListingPanel lp : listingPanels) {
c.accept(lp);
}
}
private List<ListingPanel> allListingPanels() {
List<ListingPanel> results = new ArrayList<>();
results.add(connectedProvider.getListingPanel());
ListingPanel otherPanel = connectedProvider.getOtherPanel();
if (otherPanel != null) {
results.add(otherPanel);
}
for (CodeViewerProvider provider : disconnectedProviders) {
results.add(provider.getListingPanel());
}
return results;
}
private List<FieldPanel> allFieldPanels() {
List<FieldPanel> results = new ArrayList<>();
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
results.add(fieldPanel);
ListingPanel otherPanel = connectedProvider.getOtherPanel();
if (otherPanel != null) {
FieldPanel otherFieldPanel = otherPanel.getFieldPanel();
results.add(otherFieldPanel);
}
for (CodeViewerProvider provider : disconnectedProviders) {
fieldPanel = provider.getListingPanel().getFieldPanel();
results.add(fieldPanel);
}
return results;
}
@Override
public void selectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
public void broadcastSelectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
if (provider == connectedProvider) {
MarkerSet selectionMarkers = getSelectionMarkers(currentProgram);
if (selectionMarkers != null) {
selectionMarkers.clearAll();
}
if (selection != null) {
if (selectionMarkers != null) {
selectionMarkers.add(selection);
}
}
if (!selectionChanging) {
tool.firePluginEvent(new ProgramSelectionPluginEvent(getName(), selection,
connectedProvider.getProgram()));
@@ -460,88 +536,21 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
}
}
protected void setHighlight(ProgramSelection highlight) {
connectedProvider.setHighlight(highlight);
}
protected void setSelection(ProgramSelection sel) {
protected void setConnectedProviderSelection(ProgramSelection sel) {
selectionChanging = true;
connectedProvider.setSelection(sel);
selectionChanging = false;
}
protected void clearMarkers(Program program) {
if (markerService == null) {
return;
}
if (program == null) {
return; // can happen during dispose after a programDeactivated()
}
if (currentSelectionMarkers != null) {
markerService.removeMarker(currentSelectionMarkers, program);
currentSelectionMarkers = null;
}
if (currentHighlightMarkers != null) {
markerService.removeMarker(currentHighlightMarkers, program);
currentHighlightMarkers = null;
}
if (currentCursorMarkers != null) {
markerService.removeMarker(currentCursorMarkers, program);
currentCursorMarkers = null;
}
}
private MarkerSet getSelectionMarkers(Program program) {
if (markerService == null || program == null) {
return null;
}
// already created
if (currentSelectionMarkers != null) {
return currentSelectionMarkers;
}
FieldPanel fp = connectedProvider.getListingPanel().getFieldPanel();
currentSelectionMarkers = markerService.createAreaMarker("Selection", "Selection Display",
program, MarkerService.SELECTION_PRIORITY, false, true, false, fp.getSelectionColor());
return currentSelectionMarkers;
}
protected MarkerSet getHighlightMarkers(Program program) {
if (markerService == null || program == null) {
return null;
}
// already created
if (currentHighlightMarkers != null) {
return currentHighlightMarkers;
}
FieldPanel fp = connectedProvider.getListingPanel().getFieldPanel();
currentHighlightMarkers = markerService.createAreaMarker("Highlight", "Highlight Display ",
program, MarkerService.HIGHLIGHT_PRIORITY, false, true, false, fp.getHighlightColor());
return currentHighlightMarkers;
}
protected MarkerSet getCursorMarkers(Program program) {
if (markerService == null || program == null) {
return null;
}
// already created
if (currentCursorMarkers != null) {
return currentCursorMarkers;
}
currentCursorMarkers = markerService.createPointMarker("Cursor", "Cursor Location", program,
MarkerService.CURSOR_PRIORITY, true, true, isHighlightCursorLine, cursorHighlightColor,
CURSOR_LOC_ICON);
return currentCursorMarkers;
private void initMiscellaneousOptions() {
// make sure the following options are registered
HelpLocation helpLocation =
new HelpLocation("ShowInstructionInfoPlugin", "Processor_Manual_Options");
Options options = tool.getOptions(ManualViewerCommandWrappedOption.OPTIONS_CATEGORY_NAME);
options.registerOption(ManualViewerCommandWrappedOption.MANUAL_VIEWER_OPTIONS,
OptionType.CUSTOM_TYPE,
ManualViewerCommandWrappedOption.getDefaultBrowserLoaderOptions(), helpLocation,
"Options for running manual viewer", () -> new ManualViewerCommandEditor());
}
private void initOptions(Options fieldOptions) {
@@ -572,50 +581,44 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
helpLocation, "Enables horizontal scrolling by holding the Shift key while " +
"using the mouse scroll wheel");
Color color = fieldOptions.getColor(GhidraOptions.OPTION_SELECTION_COLOR,
GhidraOptions.DEFAULT_SELECTION_COLOR);
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
fieldPanel.setSelectionColor(color);
MarkerSet selectionMarkers = getSelectionMarkers(currentProgram);
if (selectionMarkers != null) {
selectionMarkers.setMarkerColor(color);
}
color = fieldOptions.getColor(GhidraOptions.OPTION_HIGHLIGHT_COLOR,
GhidraOptions.DEFAULT_HIGHLIGHT_COLOR);
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
fieldPanel.setHighlightColor(color);
if (highlightMarkers != null) {
highlightMarkers.setMarkerColor(color);
}
color = fieldOptions.getColor(CURSOR_COLOR_OPTIONS_NAME, FOCUSED_CURSOR_COLOR);
fieldPanel.setFocusedCursorColor(color);
color = fieldOptions.getColor(UNFOCUSED_CURSOR_COLOR_OPTIONS_NAME, UNFOCUSED_CURSOR_COLOR);
fieldPanel.setNonFocusCursorColor(color);
boolean horizontalScrollingEnabled =
fieldOptions.getBoolean(MOUSE_WHEEL_HORIZONTAL_SCROLLING_OPTIONS_NAME, true);
fieldPanel.setHorizontalScrollingEnabled(horizontalScrollingEnabled);
cursorHighlightColor = fieldOptions.getColor(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR,
CURRENT_LINE_HIGHLIGHT_COLOR);
isHighlightCursorLine = fieldOptions.getBoolean(GhidraOptions.HIGHLIGHT_CURSOR_LINE, true);
List<ListingPanel> listingPanels = allListingPanels();
List<FieldPanel> fieldPanels = allFieldPanels();
initPanelOptions(listingPanels, fieldPanels);
}
private void initMiscellaneousOptions() {
// make sure the following options are registered
HelpLocation helpLocation =
new HelpLocation("ShowInstructionInfoPlugin", "Processor_Manual_Options");
Options options = tool.getOptions(ManualViewerCommandWrappedOption.OPTIONS_CATEGORY_NAME);
options.registerOption(ManualViewerCommandWrappedOption.MANUAL_VIEWER_OPTIONS,
OptionType.CUSTOM_TYPE,
ManualViewerCommandWrappedOption.getDefaultBrowserLoaderOptions(), helpLocation,
"Options for running manual viewer", () -> new ManualViewerCommandEditor());
private void initPanelOptions(List<ListingPanel> listingPanels, List<FieldPanel> fieldPanels) {
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
Color selectionColor = fieldOptions.getColor(GhidraOptions.OPTION_SELECTION_COLOR,
GhidraOptions.DEFAULT_SELECTION_COLOR);
onListingPanels(listingPanels, lp -> lp.setSelectionColor(selectionColor));
Color hlColor = fieldOptions.getColor(GhidraOptions.OPTION_HIGHLIGHT_COLOR,
GhidraOptions.DEFAULT_HIGHLIGHT_COLOR);
onListingPanels(listingPanels, lp -> lp.setHighlightColor(hlColor));
Color focusedCursorColor =
fieldOptions.getColor(CURSOR_COLOR_OPTIONS_NAME, FOCUSED_CURSOR_COLOR);
onFieldPanels(fieldPanels, fp -> fp.setFocusedCursorColor(focusedCursorColor));
Color unfocusedCursorColor =
fieldOptions.getColor(UNFOCUSED_CURSOR_COLOR_OPTIONS_NAME, UNFOCUSED_CURSOR_COLOR);
onFieldPanels(fieldPanels, fp -> fp.setNonFocusCursorColor(unfocusedCursorColor));
boolean scrollingEnabled =
fieldOptions.getBoolean(MOUSE_WHEEL_HORIZONTAL_SCROLLING_OPTIONS_NAME, true);
onFieldPanels(fieldPanels, fp -> fp.setHorizontalScrollingEnabled(scrollingEnabled));
Color cursorHighlightColor =
fieldOptions.getColor(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR,
CURRENT_LINE_HIGHLIGHT_COLOR);
onListingPanels(listingPanels, lp -> lp.setCursorHighlightColor(cursorHighlightColor));
boolean isHighlightCursorLine =
fieldOptions.getBoolean(GhidraOptions.HIGHLIGHT_CURSOR_LINE, true);
onListingPanels(listingPanels,
lp -> lp.setHighlightCursorLineEnabled(isHighlightCursorLine));
}
@Override
@@ -876,21 +879,4 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
disconnectedProviders.remove(codeViewerProvider);
}
}
//==================================================================================================
// Inner Classes
//==================================================================================================
static class MarkerChangeListener implements ChangeListener {
private FieldPanel fieldPanel;
MarkerChangeListener(CodeViewerProvider provider) {
this.fieldPanel = provider.getListingPanel().getFieldPanel();
}
@Override
public void stateChanged(ChangeEvent e) {
fieldPanel.repaint();
}
}
}

View File

@@ -97,22 +97,21 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
}
@Override
public void highlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
if (highlightMarkers != null) {
highlightMarkers.clearAll();
}
if (highlight != null && currentProgram != null) {
if (highlightMarkers != null) {
highlightMarkers.add(highlight);
}
}
public void broadcastHighlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
if (provider == connectedProvider) {
tool.firePluginEvent(new ProgramHighlightPluginEvent(getName(), highlight,
connectedProvider.getProgram()));
}
}
@Override
public void broadcastLocationChanged(CodeViewerProvider provider, ProgramLocation location) {
if (provider == connectedProvider) {
tool.firePluginEvent(new ProgramLocationPluginEvent(getName(), location,
connectedProvider.getProgram()));
}
}
@Override
public void processEvent(PluginEvent event) {
if (event instanceof ProgramClosedPluginEvent) {
@@ -125,7 +124,7 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
currentProgram.removeListener(this);
}
ProgramActivatedPluginEvent evt = (ProgramActivatedPluginEvent) event;
clearMarkers(currentProgram); // do this just before changing the program
connectedProvider.clearMarkers(currentProgram); // do this before changing the program
currentProgram = evt.getActiveProgram();
if (currentProgram != null) {
@@ -135,12 +134,13 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
else {
currentView = new AddressSet();
}
connectedProvider.doSetProgram(currentProgram);
updateHighlightProvider();
updateBackgroundColorModel();
setHighlight(new FieldSelection());
setSelection(new ProgramSelection());
connectedProvider.updateHighlightProvider();
updateBackgroundColorModel(connectedProvider);
setConnectedProviderHighlight(new FieldSelection());
setConnectedProviderSelection(new ProgramSelection());
}
else if (event instanceof ProgramLocationPluginEvent) {
ProgramLocationPluginEvent evt = (ProgramLocationPluginEvent) event;
@@ -156,12 +156,12 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
}
else if (event instanceof ProgramSelectionPluginEvent) {
ProgramSelectionPluginEvent evt = (ProgramSelectionPluginEvent) event;
setSelection(evt.getSelection());
setConnectedProviderSelection(evt.getSelection());
}
else if (event instanceof ProgramHighlightPluginEvent) {
ProgramHighlightPluginEvent evt = (ProgramHighlightPluginEvent) event;
if (evt.getProgram() == currentProgram) {
setHighlight(evt.getHighlight());
connectedProvider.setHighlight(evt.getHighlight());
}
}
else if (event instanceof ViewChangedPluginEvent) {
@@ -206,10 +206,13 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
if (location != null) {
connectedProvider.setLocation(location);
}
setHighlight(highlight);
connectedProvider.setHighlight(highlight);
if (selection != null) {
connectedProvider.setSelection(selection);
}
if (vp != null) {
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
fieldPanel.setViewerPosition(vp.getIndex(), vp.getXOffset(), vp.getYOffset());
@@ -268,7 +271,7 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
FieldSelection highlight = new FieldSelection();
highlight.load(saveState);
if (!highlight.isEmpty()) {
setHighlight(highlight);
setConnectedProviderHighlight(highlight);
}
}
@@ -286,19 +289,6 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
connectedProvider.readState(saveState);
}
@Override
public void locationChanged(CodeViewerProvider provider, ProgramLocation location) {
if (provider == connectedProvider) {
MarkerSet cursorMarkers = getCursorMarkers(currentProgram);
if (cursorMarkers != null) {
cursorMarkers.clearAll();
cursorMarkers.add(location.getAddress());
}
tool.firePluginEvent(new ProgramLocationPluginEvent(getName(), location,
connectedProvider.getProgram()));
}
}
@Override
public ViewManagerService getViewManager(CodeViewerProvider codeViewerProvider) {
if (codeViewerProvider == connectedProvider) {

View File

@@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
@@ -23,22 +22,21 @@ import ghidra.program.util.ProgramSelection;
public interface CodeBrowserPluginInterface {
PluginTool getTool();
public PluginTool getTool();
String getName();
public String getName();
void providerClosed(CodeViewerProvider codeViewerProvider);
public boolean isDisposed();
boolean isDisposed();
public void providerClosed(CodeViewerProvider provider);
void locationChanged(CodeViewerProvider codeViewerProvider, ProgramLocation loc);
public void broadcastLocationChanged(CodeViewerProvider provider, ProgramLocation loc);
void selectionChanged(CodeViewerProvider codeViewerProvider, ProgramSelection currentSelection);
public void broadcastSelectionChanged(CodeViewerProvider provider, ProgramSelection selection);
void highlightChanged(CodeViewerProvider codeViewerProvider, ProgramSelection highlight);
public void broadcastHighlightChanged(CodeViewerProvider provider, ProgramSelection highlight);
ViewManagerService getViewManager(CodeViewerProvider codeViewerProvider);
CodeViewerProvider createNewDisconnectedProvider();
public ViewManagerService getViewManager(CodeViewerProvider provider);
public CodeViewerProvider createNewDisconnectedProvider();
}

View File

@@ -20,7 +20,6 @@ import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
@@ -112,7 +111,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
liveSelection = selection;
updateSubTitle();
};
private FocusingMouseListener focusingMouseListener;
private CodeBrowserClipboardProvider codeViewerClipboardProvider;
private ClipboardService clipboardService;
@@ -151,6 +149,11 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
setDefaultWindowPosition(WindowPosition.RIGHT);
listingPanel = new ListingPanel(formatMgr);
if (!isConnected) {
// update the marker set names to be unique so that each listing can have its own
listingPanel.setUseMarkerNameSuffix(true);
}
listingPanel.enablePropertyBasedColorModel(true);
decorationPanel = new ListingPanelContainer(listingPanel, isConnected);
@@ -253,6 +256,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
public void dispose() {
super.dispose();
clearMarkers(program);
tool.removePopupActionProvider(this);
if (clipboardService != null) {
@@ -324,8 +329,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
Object source = event.getSource();
List<MarginProvider> marginProviders = lp.getMarginProviders();
for (MarginProvider marginProvider : marginProviders) {
List<ListingMarginProvider> marginProviders = lp.getMarginProviders();
for (ListingMarginProvider marginProvider : marginProviders) {
JComponent c = marginProvider.getComponent();
if (c == source) {
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
@@ -337,8 +342,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
}
}
}
List<OverviewProvider> overviewProviders = lp.getOverviewProviders();
for (OverviewProvider overviewProvider : overviewProviders) {
List<ListingOverviewProvider> overviewProviders = lp.getOverviewProviders();
for (ListingOverviewProvider overviewProvider : overviewProviders) {
JComponent c = overviewProvider.getComponent();
if (c == source) {
return source;
@@ -444,6 +449,10 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
contextChanged();
}
void clearMarkers(Program p) {
listingPanel.clearMarkers(p);
}
protected void updateTitle() {
String subTitle = program == null ? "" : ' ' + program.getDomainFile().getName();
String newTitle = TITLE + subTitle;
@@ -657,6 +666,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
}
}
// events coming from the ListingPanel
@Override
public void programLocationChanged(ProgramLocation loc, EventTrigger trigger) {
if (plugin.isDisposed()) {
@@ -665,7 +675,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
if (!loc.equals(currentLocation)) {
codeViewerClipboardProvider.setLocation(loc);
currentLocation = loc;
plugin.locationChanged(this, loc);
plugin.broadcastLocationChanged(this, loc);
contextChanged();
}
}
@@ -696,7 +706,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
currentSelection = selection;
codeViewerClipboardProvider.setSelection(currentSelection);
listingPanel.setSelection(currentSelection);
plugin.selectionChanged(this, currentSelection);
plugin.broadcastSelectionChanged(this, currentSelection);
contextChanged();
updateSubTitle();
}
@@ -792,7 +802,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
private void doSetHighlight(ProgramSelection highlight) {
listingPanel.setHighlight(highlight);
currentHighlight = highlight;
plugin.highlightChanged(this, highlight);
plugin.broadcastHighlightChanged(this, highlight);
contextChanged();
}
@@ -1161,45 +1171,51 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
listingPanel.removeDisplayListener(listener);
}
private synchronized void createFocusingMouseListener() {
if (focusingMouseListener == null) {
focusingMouseListener = new FocusingMouseListener();
}
}
public void addOverviewProvider(OverviewProvider overviewProvider) {
createFocusingMouseListener();
JComponent component = overviewProvider.getComponent();
// just in case we get repeated calls
component.removeMouseListener(focusingMouseListener);
component.addMouseListener(focusingMouseListener);
public void addOverviewProvider(ListingOverviewProvider overviewProvider) {
overviewProvider.setNavigatable(this);
getListingPanel().addOverviewProvider(overviewProvider);
}
public void addMarginProvider(MarginProvider marginProvider) {
createFocusingMouseListener();
JComponent component = marginProvider.getComponent();
// just in case we get repeated calls
component.removeMouseListener(focusingMouseListener);
component.addMouseListener(focusingMouseListener);
getListingPanel().addMarginProvider(marginProvider);
}
public void removeOverviewProvider(OverviewProvider overviewProvider) {
JComponent component = overviewProvider.getComponent();
component.removeMouseListener(focusingMouseListener);
public void removeOverviewProvider(ListingOverviewProvider overviewProvider) {
getListingPanel().removeOverviewProvider(overviewProvider);
}
public void removeMarginProvider(MarginProvider marginProvider) {
JComponent component = marginProvider.getComponent();
component.removeMouseListener(focusingMouseListener);
public void addMarginProvider(ListingMarginProvider marginProvider) {
getListingPanel().addMarginProvider(marginProvider);
}
public void removeMarginProvider(ListingMarginProvider marginProvider) {
getListingPanel().removeMarginProvider(marginProvider);
}
public void addMarginService(ListingMarginProviderService service) {
getListingPanel().addMarginService(service, isConnected());
}
public void addOverviewService(ListingOverviewProviderService service) {
getListingPanel().addOverviewService(service, this, isConnected());
}
public void removeOverviewService(ListingOverviewProviderService service) {
getListingPanel().removeOverviewService(service);
}
public void removeMarginService(ListingMarginProviderService service) {
getListingPanel().removeMarginService(service);
}
public void addHoverService(ListingHoverService hoverService) {
getListingPanel().addHoverService(hoverService);
}
public void removeHoverService(ListingHoverService hoverService) {
getListingPanel().removeHoverService(hoverService);
if (otherPanel != null) {
otherPanel.removeHoverService(hoverService);
}
}
//==================================================================================================
// Inner Classes
//==================================================================================================
@@ -1280,11 +1296,4 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
return list.toArray(new Highlight[list.size()]);
}
}
private class FocusingMouseListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
getListingPanel().getFieldPanel().requestFocus();
}
}
}

View File

@@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
@@ -16,11 +15,11 @@
*/
package ghidra.app.plugin.core.flowarrow;
import java.awt.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.RefType;
import java.awt.*;
class ConditionalFlowArrow extends FlowArrow {
private static final Stroke CONDITIONAL_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE,
@@ -28,9 +27,9 @@ class ConditionalFlowArrow extends FlowArrow {
private static final Stroke NORMAL_ACTIVE_STROKE = new BasicStroke(2, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER);
ConditionalFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end,
RefType referenceType) {
super(plugin, canvas, start, end, referenceType);
ConditionalFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start,
Address end, RefType referenceType) {
super(provider, canvas, start, end, referenceType);
}
@Override

View File

@@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
@@ -16,11 +15,11 @@
*/
package ghidra.app.plugin.core.flowarrow;
import java.awt.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.RefType;
import java.awt.*;
class DefaultFlowArrow extends FlowArrow {
private static final Stroke NORMAL_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE,
@@ -28,9 +27,9 @@ class DefaultFlowArrow extends FlowArrow {
private static final Stroke NORMAL_ACTIVE_STROKE = new BasicStroke(2, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER);
DefaultFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end,
DefaultFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start, Address end,
RefType referenceType) {
super(plugin, canvas, start, end, referenceType);
super(provider, canvas, start, end, referenceType);
}
@Override

View File

@@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
@@ -16,11 +15,11 @@
*/
package ghidra.app.plugin.core.flowarrow;
import java.awt.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.RefType;
import java.awt.*;
class FallthroughFlowArrow extends FlowArrow {
private static final Stroke FALLTHROUGH_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE,
@@ -28,9 +27,9 @@ class FallthroughFlowArrow extends FlowArrow {
private static final Stroke FALLTHROUGH_ACTIVE_STROKE = new BasicStroke(2,
BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10, new float[] { 8, 3, 2, 3 }, 0);
FallthroughFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end,
RefType referenceType) {
super(plugin, canvas, start, end, referenceType);
FallthroughFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start,
Address end, RefType referenceType) {
super(provider, canvas, start, end, referenceType);
}
@Override

View File

@@ -4,9 +4,9 @@
* 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.
@@ -15,6 +15,8 @@
*/
package ghidra.app.plugin.core.flowarrow;
import static ghidra.util.HTMLUtilities.*;
import java.awt.*;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;
@@ -23,7 +25,6 @@ import java.util.List;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.RefType;
import ghidra.util.HTMLUtilities;
import ghidra.util.exception.AssertException;
abstract class FlowArrow {
@@ -36,15 +37,16 @@ abstract class FlowArrow {
Address start;
Address end;
AddressSet addressSet;
int depth = -1;
AddressSet addresses;
int column = -1;
RefType refType;
private int maxColumn;
private boolean isUp;
boolean active;
boolean selected;
private FlowArrowPlugin plugin;
private FlowArrowMarginProvider provider;
private Component canvas;
protected Shape arrowBody;
protected Shape arrowHead;
@@ -52,15 +54,16 @@ abstract class FlowArrow {
/** The shape of the arrow body, but with added size */
private List<Shape> clickableShapes = new ArrayList<>();
FlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end,
FlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start, Address end,
RefType referenceType) {
this.plugin = plugin;
this.provider = provider;
this.canvas = canvas;
this.start = start;
this.end = end;
this.refType = referenceType;
this.addressSet = new AddressSet(new AddressRangeImpl(start, end));
isUp = start.compareTo(end) > 0;
this.maxColumn = provider.getMaxColumn();
this.addresses = new AddressSet(new AddressRangeImpl(start, end));
this.isUp = start.compareTo(end) > 0;
}
abstract Stroke getSelectedStroke();
@@ -101,7 +104,7 @@ abstract class FlowArrow {
g2.setStroke(oldStroke);
}
/** True if this arrow points up instead of down */
/** {@return true if this arrow points up instead of down} */
boolean isUp() {
return isUp;
}
@@ -242,20 +245,20 @@ abstract class FlowArrow {
int displayWidth = canvas.getWidth();// - FlowArrowPlugin.LEFT_OFFSET;
int lineWidth = calculateLineWidth(displayWidth);
arrowBody = FlowArrowShapeFactory.createArrowBody(plugin, this, displayWidth, displayHeight,
lineWidth);
arrowBody =
FlowArrowShapeFactory.createArrowBody(provider, this, displayWidth, displayHeight,
lineWidth);
arrowHead = FlowArrowShapeFactory.createArrowHead(plugin, this, displayWidth, displayHeight,
lineWidth);
arrowHead =
FlowArrowShapeFactory.createArrowHead(provider, this, displayWidth, displayHeight,
lineWidth);
}
private int calculateLineWidth(int displayWidth) {
// Crunch or stretch spacing depending upon width and maximum depth
int lineWidth = DEFAULT_LINE_SPACING;
int maxDepth = plugin.getMaxDepth();
if (maxDepth >= 0) {
int availabeWidth = displayWidth - FlowArrowPlugin.LEFT_OFFSET;
if (maxColumn >= 0) {
int availabeWidth = displayWidth - FlowArrowMarginProvider.LEFT_OFFSET;
lineWidth = (int) (availabeWidth * ARROW_SPACING_RATIO);
}
if (lineWidth < MIN_LINE_SPACING) {
@@ -321,9 +324,17 @@ abstract class FlowArrow {
}
public String getDisplayString() {
return "<html><table><tr><td>start</td><td>" + HTMLUtilities.escapeHTML(start.toString()) +
"</td><tr><td>end</td><td>" + HTMLUtilities.escapeHTML(end.toString()) +
"</td><tr><td>ref type</td><td>" + refType + "</td></tr></table>";
//@formatter:off
return """
<html><table>
<tr><td>start</td><td>%s</td></tr>
<tr><td>end</td><td>%s</td></tr>
<tr><td>ref type</td><td>%s</td></tr>
</table>
""".formatted(escapeHTML(start.toString()),
escapeHTML(end.toString()),
refType).trim();
//@formatter:on
}
@Override

View File

@@ -0,0 +1,737 @@
/* ###
* 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 ghidra.app.plugin.core.flowarrow;
import java.awt.Color;
import java.awt.KeyboardFocusManager;
import java.awt.event.*;
import java.util.*;
import javax.swing.JComponent;
import docking.widgets.fieldpanel.FieldPanel;
import ghidra.app.util.viewer.field.ListingColors;
import ghidra.app.util.viewer.field.ListingColors.FlowArrowColors;
import ghidra.app.util.viewer.listingpanel.*;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.symbol.*;
import ghidra.program.util.MarkerLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.UniversalID;
class FlowArrowMarginProvider implements ListingMarginProvider {
static final int LEFT_OFFSET = 3;
private static final int MAX_DEPTH = 16;
private static final int MAX_REFSTO_SHOW = 10;
/** Start address to the index of the layout for that start address */
private Map<Address, Integer> startAddressToPixel = new HashMap<>();
/** End address to the index of the layout for that end address */
private Map<Address, Integer> endAddressToPixel = new HashMap<>();
private FlowArrowPlugin plugin;
private ListingPanel listingPanel;
private Program program;
private Address currentAddr;
private FlowArrowPanel flowArrowPanel;
/** The column furthers away from the listing, to the left */
private int maxColumn;
private boolean isShowing = true;
private boolean validState = false;
/** On-screen layouts and their start/end addresses */
private VerticalPixelAddressMap layoutToPixel;
private Address screenTop;
private Address screenBottom;
/**
* We keep arrows in 3 sets: all arrows, selected arrows, and active arrows.
* Further, we rebuild the full set of arrows as the screen moves. However, the selected and
* active arrows do not get cleared when we move the screen. This allows us to keep painting
* selected arrows as the screen changes. The selected arrows are changed by user clicking. The
* active arrows are changed by program location updates.
*/
private List<FlowArrow> flowArrows = new ArrayList<>();
/** Arrows manually clicked by the user */
private Set<FlowArrow> selectedArrows = new HashSet<>();
/** Those arrows that start at the current address */
private Set<FlowArrow> activeArrows = new HashSet<>();
FlowArrowMarginProvider(FlowArrowPlugin plugin) {
this.plugin = plugin;
flowArrowPanel = new FlowArrowPanel(this);
flowArrowPanel.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
boolean previousState = isShowing;
isShowing = flowArrowPanel.getWidth() > LEFT_OFFSET;
if (isShowing && !previousState) {
updateAndRepaint();
}
}
@Override
public void componentShown(ComponentEvent e) {
boolean previousState = isShowing;
isShowing = flowArrowPanel.getWidth() > LEFT_OFFSET;
if (isShowing && !previousState) {
updateAndRepaint();
}
}
@Override
public void componentHidden(ComponentEvent e) {
isShowing = false;
}
});
flowArrowPanel.setBackground(ListingColors.BACKGROUND);
flowArrowPanel.setForeground(FlowArrowColors.INACTIVE);
flowArrowPanel.setHighlightColor(FlowArrowColors.ACTIVE);
flowArrowPanel.setSelectedColor(FlowArrowColors.SELECTED);
}
@Override
public void setOwnerId(UniversalID ownerId) {
// we don't need the owner id
}
@Override
public JComponent getComponent() {
return flowArrowPanel;
}
@Override
public MarkerLocation getMarkerLocation(int x, int y) {
return null;
}
@Override
public boolean isResizeable() {
return true;
}
@Override
public void setLocation(ProgramLocation location) {
currentAddr = location.getAddress();
clearActiveArrows();
assignActiveArrows();
flowArrowPanel.repaint();
}
@Override
public void screenDataChanged(ListingPanel listing, AddressIndexMap addrMap,
VerticalPixelAddressMap pixMap) {
this.listingPanel = listing;
Program currentProgram = listing.getProgram();
if (this.program != currentProgram) {
clearAllArrows();
}
this.program = currentProgram;
this.layoutToPixel = pixMap;
validateState();
updateAndRepaint();
}
Address getCurrentAddress() {
return currentAddr;
}
Address getScreenBottomAddr() {
return screenBottom;
}
int getMaxColumn() {
return maxColumn;
}
boolean isOnScreen(Address address) {
if (screenBottom == null || screenTop == null) {
return true; // shouldn't happen
}
if (address.compareTo(screenTop) < 0) {
// above the top of the screen
return false;
}
if (address.compareTo(screenBottom) > 0) {
// below the bottom of the screen
return false;
}
return true;
}
boolean isOffscreen(FlowArrow arrow) {
if (screenBottom == null || screenTop == null) {
return true; // shouldn't happen
}
if (arrow.start.compareTo(screenTop) < 0 && arrow.end.compareTo(screenTop) < 0) {
// start and end are above the top of the screen
return true;
}
if (arrow.start.compareTo(screenBottom) > 0 && arrow.end.compareTo(screenBottom) > 0) {
// start and end are below the bottom of the screen
return true;
}
return false;
}
boolean isBelowScreen(Address address) {
if (screenBottom == null || screenTop == null) {
return true; // shouldn't happen
}
return address.compareTo(screenBottom) > 0;
}
/* The y value of the start of the layout at the given address. */
Integer getStartPos(Address addr) {
return startAddressToPixel.get(addr);
}
/* The y value of the end of the layout at the given address. */
Integer getEndPos(Address addr) {
return endAddressToPixel.get(addr);
}
void setArrowSelected(FlowArrow arrow, boolean selected) {
if (selected) {
selectedArrows.add(arrow);
}
else {
selectedArrows.remove(arrow);
}
}
Iterator<FlowArrow> getSelectedFlowArrows() {
return selectedArrows.iterator();
}
Iterator<FlowArrow> getFlowArrowIterator() {
return flowArrows.iterator();
}
/* Those arrows starting at the current address */
Iterator<FlowArrow> getActiveArrows() {
return activeArrows.iterator();
}
private void resetSelectedArrows() {
for (FlowArrow arrow : selectedArrows) {
arrow.resetShape();
}
}
private void clearAllArrows() {
flowArrows.clear();
activeArrows.clear();
selectedArrows.clear();
}
private void clearActiveArrows() {
for (FlowArrow f : activeArrows) {
f.active = false;
}
activeArrows.clear();
}
private void resetActiveArrows() {
for (FlowArrow arrow : activeArrows) {
arrow.resetShape();
}
}
private void assignActiveArrows() {
if (!activeArrows.isEmpty()) {
resetActiveArrows();
return; // don't overwrite existing values
}
if (currentAddr == null) {
return;
}
for (FlowArrow arrow : flowArrows) {
if (currentAddr.equals(arrow.start)) {
arrow.active = true;
activeArrows.add(arrow);
}
}
}
private void mapArrowsByEndpoints(Map<Address, List<FlowArrow>> arrowsByStart,
Map<Address, List<FlowArrow>> arrowsByEnd) {
for (FlowArrow arrow : flowArrows) {
arrowsByStart.computeIfAbsent(arrow.start, f -> new ArrayList<>()).add(arrow);
arrowsByEnd.computeIfAbsent(arrow.end, f -> new ArrayList<>()).add(arrow);
}
}
private List<ArrowGroup> groupArrowsBySharedEndpoints() {
// not sure this is still needed; keeping for posterity
Collections.sort(flowArrows, (a1, a2) -> (a1).end.compareTo((a2).end));
Map<Address, List<FlowArrow>> arrowsByStart = new HashMap<>();
Map<Address, List<FlowArrow>> arrowsByEnd = new HashMap<>();
mapArrowsByEndpoints(arrowsByStart, arrowsByEnd);
List<ArrowGroup> groups = new ArrayList<>();
Set<FlowArrow> unprocessed = new HashSet<>(flowArrows);
for (FlowArrow arrow : flowArrows) {
if (!unprocessed.contains(arrow)) {
continue; // already grouped
}
// put all arrows in this group that share a start or end, as they will all occupy the
// same column
ArrowGroup group = new ArrowGroup();
List<FlowArrow> starts = arrowsByStart.get(arrow.start);
for (FlowArrow f : starts) {
group.add(f);
unprocessed.remove(arrow);
}
List<FlowArrow> ends = arrowsByEnd.get(arrow.end);
for (FlowArrow f : ends) {
group.add(f);
unprocessed.remove(arrow);
}
group.add(arrow);
unprocessed.remove(arrow);
groups.add(group);
}
// Sort the groups so that the lowest end address is first. I'm assuming that we wish to
// start at the top of the screen and paint incoming arrows first, closest to the Listing.
groups.sort((g1, g2) -> g1.getSortAddress().compareTo(g2.getSortAddress()));
return groups;
}
/**
* Assigns all arrow columns (horizontal positioning). All arrows that share a start and or
* end point will all share the same column. This reduce clutter by having them all share the
* same vertical segment.
* <p>
* As the groups are assigned columns, each arrow in the group is updated. When this method is
* finished, all arrows will have a column assigned.
*/
private void assignArrowColumns() {
Collections.sort(flowArrows, (a1, a2) -> (a1).end.compareTo((a2).end));
// assign groups and then assign columns to the groups
int count = 0;
List<ArrowGroup> groups = groupArrowsBySharedEndpoints();
for (ArrowGroup group : groups) {
// assignGroupColumn(group, groups);
int column = Math.min(MAX_DEPTH, count++);
group.setColumn(column);
maxColumn = column;
}
}
private List<FlowArrow> getFlowArrowsForScreenInstructions(AddressSetView screenAddresses) {
// A cache of arrows encountered going off the screen, above or below. For any given arrow
// start, we wish to only show one arrow for each of three references flow types. The cache
// will record when we have seen each of the types so that we can skip adding arrows for
// that type from that address again.
OffscreenArrowsFlow offscreenArrows = new OffscreenArrowsFlow();
Set<FlowArrow> results = new HashSet<>();
Listing listing = program.getListing();
InstructionIterator it = listing.getInstructions(screenAddresses, true);
for (Instruction inst : it) {
// incoming
ReferenceManager refManager = program.getReferenceManager();
int refCount = refManager.getReferenceCountTo(inst.getMinAddress());
if (refCount < MAX_REFSTO_SHOW) {
for (Reference ref : inst.getReferenceIteratorTo()) {
createFlowArrow(results, offscreenArrows, ref);
}
}
// clearing the cache resets the check for duplicates, keeping incoming and outgoing
// references separate
offscreenArrows.clear();
// outgoing
for (Reference ref : inst.getReferencesFrom()) {
createFlowArrow(results, offscreenArrows, ref);
}
}
return new ArrayList<>(results);
}
private void createFlowArrow(Set<FlowArrow> results, OffscreenArrowsFlow offscreenArrows,
Reference ref) {
RefType type = ref.getReferenceType();
if (!(type.isJump() || type.isFallthrough())) {
return;
}
FlowArrow arrow = doCreateFlowArrow(ref);
if (arrow == null) {
return;
}
if (results.contains(arrow)) {
return;
}
if (offscreenArrows.exists(arrow)) {
// We have already seen an offscreen arrow coming from the same start address in the
// same direction for this arrow's flow type. No need to add another one.
return;
}
results.add(arrow);
updateArrowSets(arrow);
}
/**
* Unusual Code: We keep arrows in 3 sets: all arrows, selected arrows, and active arrows.
* Further, we rebuild arrows as the screen moves, causing the x coordinate to change as arrows
* that are no longer on the screen are removed and as new arrows are added. We want to make
* sure that we don't end up with an arrow in the selected/active sets that are the same as the
* one in the 'all' set, but with a different width. This causes both arrows to become
* visible--basically, the selected arrows can become stale as their width changes. This code is
* meant to address this out-of-sync behavior.
*
* @param arrow the updated form of the arrow
*/
private void updateArrowSets(FlowArrow arrow) {
if (selectedArrows.remove(arrow)) {
arrow.selected = true;
selectedArrows.add(arrow);
}
if (activeArrows.remove(arrow)) {
arrow.active = true;
activeArrows.add(arrow);
}
}
private FlowArrow doCreateFlowArrow(Reference ref) {
Address start = toLayoutAddress(ref.getFromAddress());
Address end = toLayoutAddress(ref.getToAddress());
if (start == null || end == null) {
return null;
}
if (!start.hasSameAddressSpace(end)) {
return null; // is this right??
}
Memory memory = program.getMemory();
if (!memory.contains(end)) {
return null; // bad disassembly
}
RefType refType = ref.getReferenceType();
if (refType.isFallthrough()) {
return new FallthroughFlowArrow(this, flowArrowPanel, start, end, refType);
}
else if (refType.isConditional()) {
return new ConditionalFlowArrow(this, flowArrowPanel, start, end, refType);
}
return new DefaultFlowArrow(this, flowArrowPanel, start, end, refType);
}
private void validateState() {
validState = true;
if (program == null || layoutToPixel == null) {
validState = false;
return;
}
int n = layoutToPixel.getNumLayouts();
validState = n != 0;
}
void updateAndRepaint() {
update();
flowArrowPanel.repaint();
}
private void update() {
if (!isShowing || !validState) {
return;
}
int n = layoutToPixel.getNumLayouts();
if (n == 0) {
return;
}
Address startAddress = layoutToPixel.getLayoutAddress(0);
Address endAddress = layoutToPixel.getLayoutAddress(n - 1);
screenTop = startAddress;
screenBottom = endAddress;
flowArrows.clear();
startAddressToPixel.clear();
endAddressToPixel.clear();
maxColumn = 0;
resetSelectedArrows();
if (screenTop == null || screenBottom == null || n > 500) {
return;
}
// find all addresses that are on the screen and compute y co-ordinate
for (int layout = 0; layout < n; layout++) {
Address addr = layoutToPixel.getLayoutAddress(layout);
if (addr != null) {
startAddressToPixel.put(addr, layoutToPixel.getBeginPosition(layout));
endAddressToPixel.put(addr, layoutToPixel.getEndPosition(layout));
}
}
AddressSetView flowSet = layoutToPixel.getAddressSet();
flowArrows = getFlowArrowsForScreenInstructions(flowSet);
assignArrowColumns();
assignActiveArrows();
}
private Address toLayoutAddress(Address addr) {
Object pixel = startAddressToPixel.get(addr);
if (pixel != null || addr.compareTo(screenTop) < 0 ||
addr.compareTo(screenBottom) > 0) {
return addr;
}
int n = layoutToPixel.getNumLayouts();
for (int i = 0; i < n; i++) {
Address layoutAddr = layoutToPixel.getLayoutAddress(i);
Address endLayoutAddr = layoutToPixel.getLayoutEndAddress(i);
if (layoutAddr == null || endLayoutAddr == null) {
continue;
}
if (layoutAddr.compareTo(addr) >= 0) {
// have gone past the address, then there is a gap
return null;
}
// we are between the start and end; inside the layout
if (addr.compareTo(endLayoutAddr) <= 0) {
return layoutAddr;
}
}
return addr; // should never get here
}
void setBackground(Color c) {
flowArrowPanel.setBackground(c);
}
void setForeground(Color c) {
flowArrowPanel.setForeground(c);
}
void setHighlightColor(Color c) {
flowArrowPanel.setHighlightColor(c);
}
@Override
public void dispose() {
plugin.remove(this);
program = null;
layoutToPixel = null;
startAddressToPixel.clear();
endAddressToPixel.clear();
clearAllArrows();
flowArrowPanel.dispose();
}
void goTo(Address address) {
ProgramLocation location = new ProgramLocation(program, address);
listingPanel.goTo(location);
}
void scrollTo(Address address) {
ProgramLocation location = new ProgramLocation(program, address);
listingPanel.scrollTo(location);
}
Address getLastAddressOnScreen(Address end, boolean up) {
if (up) {
return screenTop;
}
return screenBottom;
}
public void forwardMouseEventToListing(MouseWheelEvent e) {
FieldPanel fieldPanel = listingPanel.getFieldPanel();
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
kfm.redispatchEvent(fieldPanel, e);
}
//==================================================================================================
// Inner Classes
//==================================================================================================
/**
* An arrow group is all arrows that will be in the same column. The column for an arrow
* will be based on the first group to assign a column.
*/
private class ArrowGroup {
private Set<FlowArrow> arrows = new HashSet<>();
private AddressSet addrs = new AddressSet();
private Address lowestEndAddress;
@SuppressWarnings("unused") // for debug
private int column;
Address getSortAddress() {
return lowestEndAddress;
}
void setColumn(int column) {
this.column = column;
for (FlowArrow f : arrows) {
f.column = column;
}
}
void add(FlowArrow f) {
if (lowestEndAddress == null) {
lowestEndAddress = f.end;
}
else if (lowestEndAddress.compareTo(f.end) >= 0) {
lowestEndAddress = f.end;
}
arrows.add(f);
addrs.add(f.addresses);
}
}
/**
* A cache of all arrows that start at a given address. This is only used while building the
* set of arrows. This tracks arrow usage from the start address to limit the number of arrows
* that go offscreen. We allow 1 offscreen arrow above and below for each of the three flow
* types: conditional, fallthrough and other. This is used to prevent too many arrows from
* cluttering the screen when there are many references starting at the same address.
*/
private class OffscreenArrowsFlow {
private Map<Address, OffScreenFlow> flowsAbove = new HashMap<>();
private Map<Address, OffScreenFlow> flowsBelow = new HashMap<>();
/**
* Tracks the given arrow and records whether we have seen an arrow at this start address,
* going offscreen in the same direction with the same flow category.
*
* @param arrow the arrow
* @return true if we already have a representative arrow
*/
boolean exists(FlowArrow arrow) {
boolean isAbove = arrow.end.compareTo(screenTop) < 0;
boolean isBelow = arrow.end.compareTo(screenBottom) > 0;
if (!(isAbove || isBelow)) {
return false; // on-screen
}
OffScreenFlow flow;
if (isAbove) {
flow = flowsAbove.get(arrow.start);
if (flow == null) {
flow = new OffScreenFlow();
flowsAbove.put(arrow.start, flow);
}
}
else { // isBelow
flow = flowsBelow.get(arrow.start);
if (flow == null) {
flow = new OffScreenFlow();
flowsBelow.put(arrow.start, flow);
}
}
// sets the flow type and returns true if that type was already set, signalling that we
// have seen this an offscreen arrow with this flow type coming from this address in the
// up or down direction
boolean alreadyHasArrow = flow.setFlow(arrow.refType);
return alreadyHasArrow;
}
void clear() {
flowsAbove.clear();
flowsBelow.clear();
}
private class OffScreenFlow {
private boolean conditional;
private boolean fallthrough;
private boolean other;
boolean setFlow(RefType type) {
boolean wasSet = false;
if (type.isConditional()) {
wasSet = conditional;
conditional = true;
}
else if (type.isFallthrough()) {
wasSet = fallthrough;
fallthrough = true;
}
else {
wasSet = other;
other = true;
}
return wasSet;
}
}
}
}

View File

@@ -36,7 +36,7 @@ class FlowArrowPanel extends JPanel {
private Cursor clickCursor;
private Cursor defaultCursor;
private FlowArrowPlugin plugin;
private FlowArrowMarginProvider provider;
private Color foregroundColor;
private Color highlightColor;
private Color selectedColor;
@@ -44,8 +44,8 @@ class FlowArrowPanel extends JPanel {
private SwingUpdateManager mouseClickUpdater;
private Point pendingMouseClickPoint;
FlowArrowPanel(FlowArrowPlugin p) {
this.plugin = p;
FlowArrowPanel(FlowArrowMarginProvider provider) {
this.provider = provider;
setMinimumSize(new Dimension(0, 0));
setPreferredSize(new Dimension(32, 1));
@@ -110,18 +110,18 @@ class FlowArrowPanel extends JPanel {
}
private FlowArrow getArrow(Point p) {
FlowArrow arrow = getArrow(p, plugin.getFlowArrowIterator());
FlowArrow arrow = getArrow(p, provider.getFlowArrowIterator());
if (arrow != null) {
return arrow;
}
// try the arrows that hang around a bit
arrow = getArrow(p, plugin.getSelectedFlowArrows());
arrow = getArrow(p, provider.getSelectedFlowArrows());
if (arrow != null) {
return arrow;
}
return getArrow(p, plugin.getActiveArrows());
return getArrow(p, provider.getActiveArrows());
}
private FlowArrow getArrow(Point p, Iterator<FlowArrow> it) {
@@ -139,26 +139,24 @@ class FlowArrowPanel extends JPanel {
return;
}
// TODO to do this, we should probably have another concept of 'navigated'/'current' as to not
// confuse the concept of selecting arrows
// select any arrow we double-click
arrow.selected = true;
plugin.setArrowSelected(arrow, true);
provider.setArrowSelected(arrow, true);
Address end = arrow.end;
if (end.equals(plugin.getCurrentAddress())) {
if (end.equals(provider.getCurrentAddress())) {
// go back the other direction
end = arrow.start;
}
if (plugin.isOnScreen(end)) {
if (provider.isOnScreen(end)) {
// don't animate arrows completely on screen
plugin.goTo(end);
provider.goTo(end);
return;
}
// Start the animation at the edge of the screen
Address start = plugin.getLastAddressOnScreen(end, arrow.isUp());
Address start = provider.getLastAddressOnScreen(end, arrow.isUp());
ScrollingCallback callback = new ScrollingCallback(start, end);
Animator animator = AnimationUtils.executeSwingAnimationCallback(callback);
@@ -169,7 +167,7 @@ class FlowArrowPanel extends JPanel {
FlowArrow arrow = getArrow(point);
if (arrow != null) {
arrow.selected = !arrow.selected; // toggle
plugin.setArrowSelected(arrow, arrow.selected);
provider.setArrowSelected(arrow, arrow.selected);
repaint();
return; // only select one line at a time
}
@@ -179,13 +177,13 @@ class FlowArrowPanel extends JPanel {
public void setBounds(int x, int y, int width, int height) {
// note: this gets called as the user drags the divider pane
super.setBounds(x, y, width, height);
plugin.updateAndRepaint();
provider.updateAndRepaint();
}
@Override
public String getToolTipText(MouseEvent e) {
Point point = e.getPoint();
Iterator<FlowArrow> it = plugin.getFlowArrowIterator();
Iterator<FlowArrow> it = provider.getFlowArrowIterator();
while (it.hasNext()) {
FlowArrow arrow = it.next();
if (arrow.intersects(point)) {
@@ -223,7 +221,7 @@ class FlowArrowPanel extends JPanel {
super.paintComponent(g);
Address currentAddress = plugin.getCurrentAddress();
Address currentAddress = provider.getCurrentAddress();
if (currentAddress == null) {
return;
}
@@ -235,7 +233,7 @@ class FlowArrowPanel extends JPanel {
//
// Non-selected arrows
//
Iterator<FlowArrow> it = plugin.getFlowArrowIterator();
Iterator<FlowArrow> it = provider.getFlowArrowIterator();
while (it.hasNext()) {
FlowArrow arrow = it.next();
if (arrow.active || arrow.selected) {
@@ -251,7 +249,7 @@ class FlowArrowPanel extends JPanel {
//
// Active arrows--those at the selected address; paint on top of normal arrows
//
it = plugin.getActiveArrows();
it = provider.getActiveArrows();
while (it.hasNext()) {
FlowArrow arrow = it.next();
if (arrow.selected) {
@@ -266,7 +264,7 @@ class FlowArrowPanel extends JPanel {
// Selected arrows
//
fgColor = selectedColor;
it = plugin.getSelectedFlowArrows();
it = provider.getSelectedFlowArrows();
while (it.hasNext()) {
FlowArrow arrow = it.next();
paintJump(g2, arrow, fgColor);
@@ -274,7 +272,7 @@ class FlowArrowPanel extends JPanel {
}
private void paintJump(Graphics2D g2, FlowArrow arrow, Color fgColor) {
if (plugin.isOffscreen(arrow)) {
if (provider.isOffscreen(arrow)) {
return; // don't paint linger arrows, such as selected or active arrows
}
@@ -329,19 +327,19 @@ class FlowArrowPanel extends JPanel {
// System.err.printf("%1.3f%%\t", (percentComplete * 100));
// System.err.println("scrolling to: " + current);
plugin.scrollTo(current);
provider.scrollTo(current);
lastAddress = current; // let's us avoid multiple duplicate requests
}
@Override
public void done() {
// set the final position
// TODO This happens after the animation is finished, which is jarring. If we want this centered,
// then we need an entirely different way of animating the transition so that the centering
// is part of the animation.
// Note: This happens after the animation is finished, which is jarring. If we want this centered,
// then we need an entirely different way of animating the transition so that the centering
// is part of the animation.
// plugin.scrollToCenter(end);
plugin.goTo(end);
provider.goTo(end);
}
void setAnimator(Animator animator) {
@@ -392,7 +390,7 @@ class FlowArrowPanel extends JPanel {
private class FlowArrowPanelMouseWheelListener implements MouseWheelListener {
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
plugin.forwardMouseEventToListing(e);
provider.forwardMouseEventToListing(e);
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -15,34 +15,18 @@
*/
package ghidra.app.plugin.core.flowarrow;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.JComponent;
import docking.widgets.fieldpanel.FieldPanel;
import ghidra.GhidraOptions;
import ghidra.app.CorePluginPackage;
import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.CodeViewerService;
import ghidra.app.util.viewer.field.ListingColors;
import ghidra.app.util.viewer.field.ListingColors.FlowArrowColors;
import ghidra.app.util.viewer.listingpanel.*;
import ghidra.app.services.ListingMarginProviderService;
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
import ghidra.app.util.viewer.options.OptionsGui;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.options.OptionsChangeListener;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.symbol.*;
import ghidra.program.util.MarkerLocation;
import ghidra.program.util.ProgramLocation;
/**
* Plugin that has a margin provider to show the program flow.
@@ -53,595 +37,53 @@ import ghidra.program.util.ProgramLocation;
packageName = CorePluginPackage.NAME,
category = PluginCategoryNames.CODE_VIEWER,
shortDescription = "Show arrows for execution flow",
description = "This plugin shows arrows to graphically illustrate "
+ "the flow of execution within a function. The arrows indicate "
+ "source and destination for jumps; solid lines indicate "
+ "unconditional jumps; dashed lines indicate conditional jumps.",
servicesRequired = { CodeViewerService.class },
description = "This plugin shows arrows to graphically illustrate the flow of execution " +
"within a function. The arrows indicate source and destination for jumps; solid lines " +
"indicate unconditional jumps; dashed lines indicate conditional jumps.",
servicesProvided = { ListingMarginProviderService.class },
eventsConsumed = { ProgramActivatedPluginEvent.class, ProgramClosedPluginEvent.class, ProgramLocationPluginEvent.class }
)
//@formatter:on
public class FlowArrowPlugin extends Plugin implements MarginProvider, OptionsChangeListener {
public class FlowArrowPlugin extends Plugin implements ListingMarginProviderService {
static final int LEFT_OFFSET = 3;
static final int MAX_DEPTH = 16;
private static final int MAX_REFSTO_SHOW = 10; // TODO this was 20--sounded like too many
private FlowArrowPanel flowArrowPanel;
private boolean enabled = true;
private boolean validState = false;
private Address currentAddr;
/** Start address to the index of the layout for that start address */
private Map<Address, Integer> startAddressToPixel = new HashMap<>();
/** End address to the index of the layout for that end address */
private Map<Address, Integer> endAddressToPixel = new HashMap<>();
/** On-screen layouts and their start/end addresses */
private VerticalPixelAddressMap layoutToPixel;
private Address screenTop;
private Address screenBottom;
private int maxDepth;
private Program program;
private CodeViewerService codeViewerService;
private List<FlowArrow> flowArrows = new ArrayList<>();
private Set<FlowArrow> selectedArrows = new HashSet<>();
/** Those arrows that start at the current address */
private Set<FlowArrow> activeArrows = new HashSet<>();
private List<FlowArrowMarginProvider> providers = new ArrayList<>();
public FlowArrowPlugin(PluginTool tool) {
super(tool);
flowArrowPanel = new FlowArrowPanel(this);
flowArrowPanel.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
boolean previousState = enabled;
enabled = flowArrowPanel.getWidth() > LEFT_OFFSET;
if (enabled && !previousState) {
updateAndRepaint();
}
}
@Override
public void componentShown(ComponentEvent e) {
boolean previousState = enabled;
enabled = flowArrowPanel.getWidth() > LEFT_OFFSET;
if (enabled && !previousState) {
updateAndRepaint();
}
}
@Override
public void componentHidden(ComponentEvent e) {
enabled = false;
}
});
getOptions();
}
@Override
public JComponent getComponent() {
return flowArrowPanel;
public ListingMarginProvider createMarginProvider() {
FlowArrowMarginProvider provider = new FlowArrowMarginProvider(this);
providers.add(provider);
return provider;
}
@Override
public MarkerLocation getMarkerLocation(int x, int y) {
return null;
public boolean isOwner(ListingMarginProvider provider) {
return providers.contains(provider);
}
@Override
public boolean isResizeable() {
return true;
void remove(FlowArrowMarginProvider provider) {
providers.remove(provider);
}
@Override
public void setProgram(Program program, AddressIndexMap addrMap,
VerticalPixelAddressMap pixmap) {
this.layoutToPixel = pixmap;
validateState();
updateFlowArrows();
}
@Override
public void processEvent(PluginEvent event) {
boolean repaintReqd = false;
if (event instanceof ProgramActivatedPluginEvent) {
ProgramActivatedPluginEvent evt = (ProgramActivatedPluginEvent) event;
program = evt.getActiveProgram();
flowArrows.clear();
validateState();
repaintReqd = true;
}
else if (event instanceof ProgramLocationPluginEvent) {
ProgramLocationPluginEvent evt = (ProgramLocationPluginEvent) event;
ProgramLocation location = evt.getLocation();
currentAddr = location.getAddress();
activeArrows.clear();
repaintReqd = true;
}
else if (event instanceof ProgramClosedPluginEvent) {
ProgramClosedPluginEvent programClosedPluginEvent = (ProgramClosedPluginEvent) event;
Program closedProgram = programClosedPluginEvent.getProgram();
if (program == closedProgram || program == null) {
program = null;
currentAddr = null;
activeArrows.clear();
flowArrows.clear();
validateState();
repaintReqd = true;
}
}
if (repaintReqd) {
updateAndRepaint();
}
}
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) {
if (optionName.equals(OptionsGui.BACKGROUND.getColorOptionName())) {
Color c = (Color) newValue;
flowArrowPanel.setBackground(c);
}
else if (optionName.equals(OptionsGui.FLOW_ARROW_NON_ACTIVE.getColorOptionName())) {
Color c = (Color) newValue;
flowArrowPanel.setForeground(c);
}
else if (optionName.equals(OptionsGui.FLOW_ARROW_ACTIVE.getColorOptionName())) {
Color c = (Color) newValue;
flowArrowPanel.setHighlightColor(c);
}
List<FlowArrowMarginProvider> getProviders() {
return Collections.unmodifiableList(providers);
}
@Override
protected void dispose() {
startAddressToPixel.clear();
endAddressToPixel.clear();
layoutToPixel = null;
flowArrows.clear();
flowArrowPanel.dispose();
codeViewerService.removeMarginProvider(this);
}
@Override
protected void init() {
codeViewerService = tool.getService(CodeViewerService.class);
codeViewerService.addMarginProvider(this);
}
Address getCurrentAddress() {
return currentAddr;
}
Address getScreenBottomAddr() {
return screenBottom;
}
int getMaxDepth() {
return maxDepth;
}
boolean isOnScreen(Address address) {
if (screenBottom == null || screenTop == null) {
return true; // shouldn't happen
}
if (address.compareTo(screenTop) < 0) {
// above the top of the screen
return false;
}
if (address.compareTo(screenBottom) > 0) {
// below the bottom of the screen
return false;
}
return true;
}
boolean isOffscreen(FlowArrow arrow) {
if (screenBottom == null || screenTop == null) {
return true; // shouldn't happen
}
if (arrow.start.compareTo(screenTop) < 0 && arrow.end.compareTo(screenTop) < 0) {
// start and end are above the top of the screen
return true;
}
if (arrow.start.compareTo(screenBottom) > 0 && arrow.end.compareTo(screenBottom) > 0) {
// start and end are below the bottom of the screen
return true;
}
return false;
}
boolean isBelowScreen(Address address) {
if (screenBottom == null || screenTop == null) {
return true; // shouldn't happen
}
return address.compareTo(screenBottom) > 0;
}
/* The y value of the start of the layout at the given address. */
Integer getStartPos(Address addr) {
return startAddressToPixel.get(addr);
}
/* The y value of the end of the layout at the given address. */
Integer getEndPos(Address addr) {
return endAddressToPixel.get(addr);
}
void setArrowSelected(FlowArrow arrow, boolean selected) {
if (selected) {
selectedArrows.add(arrow);
}
else {
selectedArrows.remove(arrow);
}
}
Iterator<FlowArrow> getSelectedFlowArrows() {
return selectedArrows.iterator();
}
Iterator<FlowArrow> getFlowArrowIterator() {
return flowArrows.iterator();
}
/* Those arrows starting at the current address */
Iterator<FlowArrow> getActiveArrows() {
return activeArrows.iterator();
}
private void resetSelectedArrows() {
for (FlowArrow arrow : selectedArrows) {
arrow.resetShape();
}
}
private void resetActiveArrows() {
for (FlowArrow arrow : activeArrows) {
arrow.resetShape();
}
}
private void saveActiveArrows() {
if (!activeArrows.isEmpty()) {
resetActiveArrows();
return; // don't overwrite existing values
}
if (currentAddr == null) {
return;
}
for (FlowArrow arrow : flowArrows) {
if (currentAddr.equals(arrow.start)) {
arrow.active = true;
activeArrows.add(arrow);
}
}
if (activeArrows.isEmpty()) {
return;
}
}
/**
* Iterate over each other FlowArrow object to check overlaps
*
* @return FlowArrow objects that have a start/end address in common
*/
private List<FlowArrow> getArrowsAtSameDepth(FlowArrow jump, List<FlowArrow> allArrows) {
List<FlowArrow> results = new ArrayList<>();
for (FlowArrow otherArrows : allArrows) {
if (jump == otherArrows) {
continue;
}
if (sharesEndpoint(jump, otherArrows)) {
results.add(otherArrows);
}
}
return results;
}
private boolean sharesEndpoint(FlowArrow a1, FlowArrow a2) {
return a1.start.equals(a2.start) || a1.end.equals(a2.end);
}
private void computeAllArrowsDepth() {
// Find overlapping arrows and compute depth
for (FlowArrow arrow : flowArrows) {
List<FlowArrow> sameDepth = null;
// If we have not already assigned a depth to this FlowArrow object
if (arrow.depth == -1) {
sameDepth = getArrowsAtSameDepth(arrow, flowArrows);
// Compute the full address set for all same-depth arrows
AddressSet sameDepthAddrs = new AddressSet(arrow.addressSet);
for (FlowArrow otherArrow : sameDepth) {
sameDepthAddrs.add(otherArrow.addressSet);
}
List<FlowArrow> differentDepth = new ArrayList<>(flowArrows);
differentDepth.removeAll(sameDepth);
differentDepth.remove(arrow);
assignArrowDepth(arrow, sameDepthAddrs, differentDepth);
}
else {
sameDepth = Collections.emptyList();
}
// If this is the deepest arrow seen, increase maxDepth
if (arrow.depth > maxDepth) {
maxDepth = arrow.depth;
}
// Make same source/dest arrows the same depth
for (FlowArrow same : sameDepth) {
same.depth = arrow.depth;
}
}
}
/** Calculates depth based on all other arrows that DO NOT share an endpoint */
private void assignArrowDepth(FlowArrow arrow, AddressSet overlappingAddresses,
List<FlowArrow> allArrows) {
//Keep track of which depths are used over current arrow range
boolean[] usedDepths = new boolean[MAX_DEPTH];
// Find all intersecting used depths
for (FlowArrow otherArrow : allArrows) {
if (otherArrow.depth == -1 || otherArrow.depth >= MAX_DEPTH) {
continue;
}
if (sharesEndpoint(arrow, otherArrow)) {
continue;
}
if (overlappingAddresses.intersects(otherArrow.addressSet)) {
usedDepths[otherArrow.depth] = true;
}
}
arrow.depth = 0;
while (arrow.depth < usedDepths.length && usedDepths[arrow.depth]) {
arrow.depth++;
}
}
private List<FlowArrow> getFlowArrowsForScreenInstructions(AddressSetView screenAddresses) {
List<FlowArrow> results = new ArrayList<>();
ArrowCache arrowCache = new ArrowCache();
CodeUnitIterator it = program.getListing()
.getCodeUnitIterator(CodeUnit.INSTRUCTION_PROPERTY, screenAddresses, true);
while (it.hasNext()) {
CodeUnit cu = it.next();
Instruction instruction = (Instruction) cu;
// incoming
int refCount = program.getReferenceManager().getReferenceCountTo(cu.getMinAddress());
if (refCount < MAX_REFSTO_SHOW) {
ReferenceIterator instructionIt = instruction.getReferenceIteratorTo();
while (instructionIt.hasNext()) {
Reference ref = instructionIt.next();
createFlowArrow(results, arrowCache, ref);
}
}
arrowCache.clear();
// outgoing
Reference[] refs = instruction.getReferencesFrom();
for (Reference ref : refs) {
createFlowArrow(results, arrowCache, ref);
}
}
return results;
}
private void createFlowArrow(List<FlowArrow> results, ArrowCache arrowCache, Reference ref) {
RefType type = ref.getReferenceType();
if (!(type.isJump() || type.isFallthrough())) {
return;
}
FlowArrow arrow = getFlowArrow(ref);
if (arrow == null) {
return;
}
if (!arrowCache.isDuplicateOffscreen(arrow.start, arrow.end, type)) {
results.add(arrow);
updateArrowSets(arrow);
}
}
/**
* Unusual Code: We keep arrows in 3 sets: all arrows, selected arrows, and active arrows.
* Further, we rebuild arrows as the screen moves, causing the x coordinate to change as arrows
* that are no longer on the screen are removed and as new arrows are added. We want to make
* sure that we don't end up with an arrow in the selected/active sets that are the same as the
* one in the 'all' set, but with a different width. This causes both arrows to become
* visible--basically, the selected arrows can become stale as their width changes. This code is
* meant to address this out-of-sync behavior.
*
* @param arrow the updated form of the arrow
*/
private void updateArrowSets(FlowArrow arrow) {
if (selectedArrows.remove(arrow)) {
arrow.selected = true;
selectedArrows.add(arrow);
}
if (activeArrows.remove(arrow)) {
arrow.active = true;
activeArrows.add(arrow);
}
}
private FlowArrow getFlowArrow(Reference ref) {
Address start = toLayoutAddress(ref.getFromAddress());
Address end = toLayoutAddress(ref.getToAddress());
if (start == null || end == null) {
return null;
}
if (!start.hasSameAddressSpace(end)) {
return null; // is this right??
}
Memory memory = program.getMemory();
if (!memory.contains(end)) {
return null; // bad disassembly
}
RefType refType = ref.getReferenceType();
if (refType.isFallthrough()) {
return new FallthroughFlowArrow(this, flowArrowPanel, start, end, refType);
}
else if (refType.isConditional()) {
return new ConditionalFlowArrow(this, flowArrowPanel, start, end, refType);
}
return new DefaultFlowArrow(this, flowArrowPanel, start, end, refType);
}
private void validateState() {
validState = false;
if (program == null || layoutToPixel == null) {
return;
}
int n = layoutToPixel.getNumLayouts();
if (n == 0) {
return;
}
Address bottomAddr = layoutToPixel.getLayoutAddress(n - 1);
if (bottomAddr != null) {
AddressSpace testSpace = bottomAddr.getAddressSpace();
validState =
(program.getAddressFactory().getAddressSpace(testSpace.getSpaceID()) == testSpace);
}
}
void updateAndRepaint() {
update();
flowArrowPanel.repaint();
}
private void update() {
if (!enabled || !validState) {
return;
}
//Compute addresses in local range
int n = layoutToPixel.getNumLayouts();
if (n == 0) {
return;
}
Address startAddress = layoutToPixel.getLayoutAddress(0);
Address endAddress = layoutToPixel.getLayoutAddress(n - 1);
screenTop = startAddress;
screenBottom = endAddress;
flowArrows.clear();
startAddressToPixel.clear();
endAddressToPixel.clear();
maxDepth = 0;
resetSelectedArrows();
if (screenTop == null || screenBottom == null || n > 500) {
return;
}
// Find all addresses that are on the screen and compute y co-ordinate
for (int layout = 0; layout < n; layout++) {
Address addr = layoutToPixel.getLayoutAddress(layout);
if (addr != null) {
startAddressToPixel.put(addr, layoutToPixel.getBeginPosition(layout));
endAddressToPixel.put(addr, layoutToPixel.getEndPosition(layout));
}
}
// Intersect the screenTop and screenBottom with the currentView
AddressSetView flowSet = layoutToPixel.getAddressSet();
// Find references at the instructions on the screen
flowArrows = getFlowArrowsForScreenInstructions(flowSet);
Collections.sort(flowArrows, (a1, a2) -> (a1).end.compareTo((a2).end));
computeAllArrowsDepth();
saveActiveArrows();
}
private Address toLayoutAddress(Address addr) {
Object pixel = startAddressToPixel.get(addr);
if (pixel != null || addr.compareTo(screenTop) < 0 || addr.compareTo(screenBottom) > 0) {
return addr;
}
int n = layoutToPixel.getNumLayouts();
for (int i = 0; i < n; i++) {
Address layoutAddr = layoutToPixel.getLayoutAddress(i);
Address endLayoutAddr = layoutToPixel.getLayoutEndAddress(i);
if (layoutAddr == null || endLayoutAddr == null) {
continue;
}
if (layoutAddr.compareTo(addr) >= 0) {
// have gone past the address, then there is a gap
return null;
}
// we are between the start and end; inside the layout
if (addr.compareTo(endLayoutAddr) <= 0) {
return layoutAddr;
}
}
return addr; // should never get here
}
private void updateFlowArrows() {
if (enabled) {
updateAndRepaint();
for (FlowArrowMarginProvider provider : new ArrayList<>(providers)) {
provider.dispose();
}
}
private void getOptions() {
// Note: these are here merely as a convenience so users don't have to use the theme editor.
ToolOptions opt = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
opt.registerThemeColorBinding(OptionsGui.FLOW_ARROW_NON_ACTIVE.getColorOptionName(),
OptionsGui.FLOW_ARROW_NON_ACTIVE.getThemeColorId(), null,
"The color for an arrow with no endpoint at the current address");
@@ -651,136 +93,6 @@ public class FlowArrowPlugin extends Plugin implements MarginProvider, OptionsCh
opt.registerThemeColorBinding(OptionsGui.FLOW_ARROW_SELECTED.getColorOptionName(),
OptionsGui.FLOW_ARROW_SELECTED.getThemeColorId(), null,
"The color for an arrow that has been selected by the user");
flowArrowPanel.setBackground(ListingColors.BACKGROUND);
flowArrowPanel.setForeground(FlowArrowColors.INACTIVE);
flowArrowPanel.setHighlightColor(FlowArrowColors.ACTIVE);
flowArrowPanel.setSelectedColor(FlowArrowColors.SELECTED);
opt.addOptionsChangeListener(this);
}
//==================================================================================================
// Inner Classes
//==================================================================================================
private class ArrowCache {
Address address;
boolean conditionalOffTop;
boolean conditionalOffBottom;
boolean fallthroughOffTop;
boolean fallthroughOffBottom;
boolean otherOffTop;
boolean otherOffBottom;
void clear() {
address = null;
conditionalOffTop = false;
conditionalOffBottom = false;
fallthroughOffTop = false;
fallthroughOffBottom = false;
otherOffTop = false;
otherOffBottom = false;
}
boolean isDuplicateOffscreen(Address pointOfInterest, Address otherEnd, RefType refType) {
if (!pointOfInterest.equals(address)) {
clear();
address = pointOfInterest;
}
// above the top of the screen
if (otherEnd.compareTo(screenTop) < 0) {
if (refType.isConditional()) {
if (conditionalOffTop) {
return true;
}
conditionalOffTop = true;
}
else if (refType.isFallthrough()) {
if (fallthroughOffTop) {
return true;
}
fallthroughOffTop = true;
}
else {
if (otherOffTop) {
return true;
}
otherOffTop = true;
}
}
// below the bottom of the screen
else if (otherEnd.compareTo(screenBottom) > 0) {
if (refType.isConditional()) {
if (conditionalOffBottom) {
return true;
}
conditionalOffBottom = true;
}
else if (refType.isFallthrough()) {
if (fallthroughOffBottom) {
return true;
}
fallthroughOffBottom = true;
}
else {
if (otherOffBottom) {
return true;
}
otherOffBottom = true;
}
}
return false;
}
}
void goTo(Address address) {
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
ListingPanel listingPanel = codeViewer.getListingPanel();
ProgramLocation location = new ProgramLocation(program, address);
listingPanel.goTo(location, false);
}
void scrollTo(Address address) {
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
ListingPanel listingPanel = codeViewer.getListingPanel();
ProgramLocation location = new ProgramLocation(program, address);
// listingPanel.setCursorPosition(location);
// listingPanel.goTo(location, true);
listingPanel.scrollTo(location);
}
void scrollToCenter(Address address) {
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
ListingPanel listingPanel = codeViewer.getListingPanel();
ProgramLocation location = new ProgramLocation(program, address);
listingPanel.center(location);
}
Address getAddressAtPoint(Point p) {
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
ListingPanel listingPanel = codeViewer.getListingPanel();
ProgramLocation location = listingPanel.getProgramLocation(p);
if (location == null) {
return null;
}
return location.getAddress();
}
Address getLastAddressOnScreen(Address end, boolean up) {
if (up) {
return screenTop;
}
return screenBottom;
}
public void forwardMouseEventToListing(MouseWheelEvent e) {
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
ListingPanel listingPanel = codeViewer.getListingPanel();
FieldPanel fieldPanel = listingPanel.getFieldPanel();
KeyboardFocusManager.getCurrentKeyboardFocusManager().redispatchEvent(fieldPanel, e);
}
}

View File

@@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
@@ -28,26 +27,27 @@ class FlowArrowShapeFactory {
private static final int TRIANGLE_HEIGHT = 9;
private static final int TRIANGLE_WIDTH = 7;
static Shape createArrowBody(FlowArrowPlugin plugin, FlowArrow arrow, int width, int height,
static Shape createArrowBody(FlowArrowMarginProvider provider, FlowArrow arrow, int width,
int height,
int lineSpacing) {
GeneralPath linePath = new GeneralPath();
// Compute the start Y coordinate (place at proper offset, fix if off screen)
Integer startTop = plugin.getStartPos(arrow.start);
Integer startBottom = plugin.getEndPos(arrow.start);
Integer startTop = provider.getStartPos(arrow.start);
Integer startBottom = provider.getEndPos(arrow.start);
int startY = 0;
if (startTop != null && startBottom != null) {
int start = startTop;
int end = startBottom;
startY = (start + end) / 2;// middle of line
}
else if (plugin.isBelowScreen(arrow.start)) {
else if (provider.isBelowScreen(arrow.start)) {
startY = height;
}
Integer endTop = plugin.getStartPos(arrow.end);
Integer endBottom = plugin.getEndPos(arrow.end);
Integer endTop = provider.getStartPos(arrow.end);
Integer endBottom = provider.getEndPos(arrow.end);
int endY = 0;
if (endTop != null && endBottom != null) {
int start = endTop;
@@ -55,11 +55,11 @@ class FlowArrowShapeFactory {
endY = (start + end) / 2;
endY = Math.min(endY, height); // ensure on screen
}
else if (plugin.isBelowScreen(arrow.end)) {
else if (provider.isBelowScreen(arrow.end)) {
endY = height;
}
int x = width - ((arrow.depth + 1) * lineSpacing);
int x = width - ((arrow.column + 1) * lineSpacing);
if (x < 3) {
x = 3;
}
@@ -126,12 +126,13 @@ class FlowArrowShapeFactory {
return linePath;
}
static Shape createArrowHead(FlowArrowPlugin plugin, FlowArrow arrow, int width, int height,
static Shape createArrowHead(FlowArrowMarginProvider provider, FlowArrow arrow, int width,
int height,
int lineSpacing) {
// Compute the start Y coordinate (place at proper offset, fix if off screen)
Integer addrStartInt = plugin.getStartPos(arrow.end);
Integer addrEndInt = plugin.getEndPos(arrow.end);
Integer addrStartInt = provider.getStartPos(arrow.end);
Integer addrEndInt = provider.getEndPos(arrow.end);
int endY = 0;
if (addrStartInt != null && addrEndInt != null) {
int start = addrStartInt;
@@ -139,11 +140,11 @@ class FlowArrowShapeFactory {
endY = (start + end) / 2;
endY = Math.min(endY, height); // ensure on screen
}
else if (plugin.isBelowScreen(arrow.end)) {
else if (provider.isBelowScreen(arrow.end)) {
endY = height;
}
int x = width - ((arrow.depth + 1) * lineSpacing);
int x = width - ((arrow.column + 1) * lineSpacing);
if (x < 0) {
x = 3;
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -40,6 +40,7 @@ import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.util.ColorUtils.ColorBlender;
import ghidra.util.UniversalID;
import ghidra.util.datastruct.*;
import ghidra.util.exception.AssertException;
import ghidra.util.task.SwingUpdateManager;
@@ -70,11 +71,9 @@ public class MarkerManager implements MarkerService {
private SwingUpdateManager updater;
private GoToService goToService;
private MarkerMarginProvider primaryMarginProvider;
private WeakSet<MarkerMarginProvider> marginProviders =
WeakDataStructureFactory.createCopyOnWriteWeakSet();
private MarkerOverviewProvider primaryOverviewProvider;
private WeakSet<MarkerOverviewProvider> overviewProviders =
WeakDataStructureFactory.createCopyOnWriteWeakSet();
@@ -101,9 +100,6 @@ public class MarkerManager implements MarkerService {
notifyListeners();
});
primaryMarginProvider = createMarginProvider();
primaryOverviewProvider = createOverviewProvider();
Gui.addThemeListener(themeListener);
}
@@ -220,8 +216,12 @@ public class MarkerManager implements MarkerService {
}
public MarkerMarginProvider getMarginProvider() {
return primaryMarginProvider;
public boolean contains(ListingMarginProvider provider) {
return marginProviders.contains(provider);
}
public boolean contains(ListingOverviewProvider provider) {
return overviewProviders.contains(provider);
}
@Override
@@ -231,8 +231,8 @@ public class MarkerManager implements MarkerService {
return provider;
}
public OverviewProvider getOverviewProvider() {
return primaryOverviewProvider;
public void removeProvider(MarkerMarginProvider provider) {
marginProviders.remove(provider);
}
@Override
@@ -246,7 +246,6 @@ public class MarkerManager implements MarkerService {
Gui.removeThemeListener(themeListener);
updater.dispose();
markerSetCache.clear();
overviewProviders.forEach(provider -> provider.dispose());
}
void navigateTo(Navigatable navigatable, Program program, int x, int y, int viewHeight,
@@ -276,13 +275,13 @@ public class MarkerManager implements MarkerService {
entry.paintNavigation(g, panel.getViewHeight(), panel.getWidth(), addrMap);
}
void paintMarkers(Program program, Graphics g, VerticalPixelAddressMap pixmap,
AddressIndexMap addrMap) {
void paintMarkers(UniversalID ownerId, Program program, Graphics g,
VerticalPixelAddressMap pixMap, AddressIndexMap addrMap) {
MarkerSetCacheEntry entry = markerSetCache.get(program);
if (entry == null) {
return;
}
entry.paintMarkers(g, pixmap, addrMap);
entry.paintMarkers(ownerId, g, pixMap, addrMap);
}
void showToolTipPopup(MouseEvent event, String tip) {
@@ -301,10 +300,6 @@ public class MarkerManager implements MarkerService {
popupWindow.showPopup(event);
}
/*testing*/ String generateToolTip(MouseEvent event) {
return primaryMarginProvider.generateToolTip(event);
}
List<String> getMarkerTooltipLines(Program program, int y, int x, Address minAddr,
Address maxAddr) {
MarkerSetCacheEntry entry = markerSetCache.get(program);
@@ -484,6 +479,7 @@ public class MarkerManager implements MarkerService {
if (program == null || program.isClosed()) {
return null;
}
MarkerSetCacheEntry entry = map.computeIfAbsent(program, this::newEntry);
if (program.isClosed()) {
map.remove(program);
@@ -571,13 +567,22 @@ public class MarkerManager implements MarkerService {
}
}
void paintMarkers(Graphics g, VerticalPixelAddressMap pixmap, AddressIndexMap addrMap) {
void paintMarkers(UniversalID ownerId, Graphics g, VerticalPixelAddressMap pixMap,
AddressIndexMap addrMap) {
int count = 0;
for (MarkerSetImpl markers : markerSets) {
count++;
if (markers.active) {
markers.paintMarkers(g, count++, pixmap, addrMap);
if (!markers.active) {
continue;
}
UniversalID markerId = markers.getOwnerId();
if (markerId != null && markerId != ownerId) {
// a non-global marker that does not match the owner being painted
continue;
}
markers.paintMarkers(g, count++, pixMap, addrMap);
}
}
@@ -638,4 +643,5 @@ public class MarkerManager implements MarkerService {
return new ArrayList<>(markerSets);
}
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -20,6 +20,8 @@ import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.*;
import ghidra.app.util.HelpTopics;
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
@@ -39,17 +41,17 @@ import ghidra.util.HelpLocation;
"supported; point markers and area markers. Area markers are used to indicate a range " +
"value such as selection. Point markers are used to represent individual addresses such " +
"as bookmarks.",
servicesRequired = { CodeViewerService.class, GoToService.class },
servicesProvided = { MarkerService.class },
servicesRequired = { GoToService.class },
servicesProvided = { MarkerService.class, ListingMarginProviderService.class, ListingOverviewProviderService.class },
eventsConsumed = {}
)
//@formatter:on
/**
* Plugin to manage marker and navigation panels.
*/
public class MarkerManagerPlugin extends Plugin {
public class MarkerManagerPlugin extends Plugin
implements ListingMarginProviderService, ListingOverviewProviderService {
private CodeViewerService codeViewerService;
private MarkerManager markerManager;
public MarkerManagerPlugin(PluginTool tool) {
@@ -65,17 +67,26 @@ public class MarkerManagerPlugin extends Plugin {
@Override
protected void dispose() {
if (codeViewerService != null) {
codeViewerService.removeMarginProvider(markerManager.getMarginProvider());
codeViewerService.removeOverviewProvider(markerManager.getOverviewProvider());
}
markerManager.dispose();
}
@Override
protected void init() {
codeViewerService = tool.getService(CodeViewerService.class);
codeViewerService.addMarginProvider(markerManager.getMarginProvider());
codeViewerService.addOverviewProvider(markerManager.getOverviewProvider());
public ListingMarginProvider createMarginProvider() {
return markerManager.createMarginProvider();
}
@Override
public boolean isOwner(ListingMarginProvider provider) {
return markerManager.contains(provider);
}
@Override
public ListingOverviewProvider createOverviewProvider() {
return markerManager.createOverviewProvider();
}
@Override
public boolean isOwner(ListingOverviewProvider provider) {
return markerManager.contains(provider);
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -28,6 +28,8 @@ import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.util.MarkerLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.UniversalID;
/**
* The provider which renders the marker margin, usually placed to the left of listing
@@ -37,7 +39,7 @@ import ghidra.program.util.MarkerLocation;
* These are managed by a {@link MarkerManager}. Obtain one via
* {@link MarkerService#createMarginProvider()}.
*/
public class MarkerMarginProvider implements MarginProvider {
public class MarkerMarginProvider implements ListingMarginProvider {
private final MarkerManager markerManager;
private final MarkerPanel markerPanel;
@@ -62,6 +64,22 @@ public class MarkerMarginProvider implements MarginProvider {
});
}
@Override
public void setOwnerId(UniversalID ownerId) {
markerPanel.setOwnerId(ownerId);
}
@Override
public void setLocation(ProgramLocation location) {
// we don't use locations
}
@Override
public void dispose() {
markerManager.removeProvider(this);
markerPanel.dispose();
}
void repaintPanel() {
markerPanel.repaint();
}
@@ -95,12 +113,11 @@ public class MarkerMarginProvider implements MarginProvider {
}
@Override
public void setProgram(Program program, AddressIndexMap addrMap,
VerticalPixelAddressMap pixmap) {
this.program = program;
this.pixmap = pixmap;
this.markerPanel.setProgram(program, addrMap, pixmap);
public void screenDataChanged(ListingPanel listing, AddressIndexMap addrMap,
VerticalPixelAddressMap pixMap) {
this.program = listing.getProgram();
this.pixmap = pixMap;
this.markerPanel.listingUpdated(listing, addrMap, pixMap);
markerManager.updateMarkerSets(program, true, false, true);
}

View File

@@ -30,7 +30,7 @@ import ghidra.GhidraOptions;
import ghidra.app.nav.Navigatable;
import ghidra.app.services.MarkerService;
import ghidra.app.util.HelpTopics;
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginTool;
@@ -46,7 +46,7 @@ import ghidra.util.Swing;
* These are managed by a {@link MarkerManager}. Obtain one via
* {@link MarkerService#createOverviewProvider()}.
*/
public class MarkerOverviewProvider implements OverviewProvider {
public class MarkerOverviewProvider implements ListingOverviewProvider {
private final PluginTool tool;
private final String owner;
@@ -74,7 +74,8 @@ public class MarkerOverviewProvider implements OverviewProvider {
actionList = new MarkerActionList();
}
void dispose() {
@Override
public void dispose() {
actionList.dispose();
}
@@ -88,11 +89,11 @@ public class MarkerOverviewProvider implements OverviewProvider {
}
@Override
public void setProgram(Program program, AddressIndexMap map) {
this.program = program;
public void screenDataChanged(Program p, AddressIndexMap addressMap) {
this.program = p;
navigationPanel.setProgram(program, map);
markerManager.updateMarkerSets(program, true, true, false);
navigationPanel.setProgram(p, addressMap);
markerManager.updateMarkerSets(p, true, true, false);
actionList.refresh();
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -24,10 +24,12 @@ import javax.swing.JPanel;
import javax.swing.ToolTipManager;
import docking.widgets.fieldpanel.FieldPanel;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.listingpanel.VerticalPixelAddressMap;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.util.UniversalID;
/**
* Panel to display markers. Normally placed to the left hand side of the scrolled
@@ -36,29 +38,40 @@ import ghidra.program.model.listing.Program;
public class MarkerPanel extends JPanel {
private MarkerManager manager;
private Program program;
private AddressIndexMap addrMap;
private VerticalPixelAddressMap pixmap;
private VerticalPixelAddressMap pixMap;
private UniversalID ownerId;
MarkerPanel(MarkerManager manager) {
super();
this.manager = manager;
this.setPreferredSize(new Dimension(16, 1));
ToolTipManager.sharedInstance().registerComponent(this);
}
void setProgram(Program program, AddressIndexMap addrMap, VerticalPixelAddressMap pixmap) {
this.program = program;
this.addrMap = addrMap;
this.pixmap = pixmap;
void setOwnerId(UniversalID ownerId) {
this.ownerId = ownerId;
}
void dispose() {
this.program = null;
this.addrMap = null;
this.pixMap = null;
ToolTipManager.sharedInstance().unregisterComponent(this);
}
void listingUpdated(ListingPanel listingPanel, AddressIndexMap addressMap,
VerticalPixelAddressMap pixelMap) {
this.program = listingPanel.getProgram();
this.addrMap = addressMap;
this.pixMap = pixelMap;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
manager.paintMarkers(program, g, pixmap, addrMap);
manager.paintMarkers(ownerId, program, g, pixMap, addrMap);
}
@Override
@@ -81,14 +94,14 @@ public class MarkerPanel extends JPanel {
}
String generateToolTip(MouseEvent event) {
if (pixmap == null) {
if (pixMap == null) {
return null;
}
int y = event.getY();
int x = event.getX();
int layoutIndex = pixmap.findLayoutAt(y);
Address layoutAddress = pixmap.getLayoutAddress(layoutIndex);
int layoutIndex = pixMap.findLayoutAt(y);
Address layoutAddress = pixMap.getLayoutAddress(layoutIndex);
if (layoutAddress == null) {
return null;
}
@@ -99,7 +112,8 @@ public class MarkerPanel extends JPanel {
private List<String> getMarkerTooltipLines(int y, int x, int layoutIndex,
Address layoutAddress) {
Address endAddr = pixmap.getLayoutEndAddress(layoutIndex);
Address endAddr = pixMap.getLayoutEndAddress(layoutIndex);
return manager.getMarkerTooltipLines(program, y, layoutIndex, layoutAddress, endAddr);
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -35,8 +35,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.MarkerLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.ColorUtils;
import ghidra.util.Swing;
import ghidra.util.*;
import ghidra.util.datastruct.SortedRangeList;
abstract class MarkerSetImpl implements MarkerSet {
@@ -52,7 +51,7 @@ abstract class MarkerSetImpl implements MarkerSet {
protected AddressSetCollection markers;
protected SortedRangeList overview = null;
protected VerticalPixelAddressMap activePixmap = null;
protected VerticalPixelAddressMap activePixMap = null;
protected List<Integer> activeLayouts = null;
protected Color markerColor;
@@ -72,9 +71,11 @@ abstract class MarkerSetImpl implements MarkerSet {
private boolean colorBackground;
private boolean isPreferred;
/** Optional owner ID. A null ID implies a global marker set. */
private UniversalID ownerId;
MarkerSetImpl(MarkerManager mgr, Program program, String name, String desc, int priority,
boolean showMarkers,
boolean showNavigation, boolean colorBackground, Color markerColor,
boolean showMarkers, boolean showNavigation, boolean colorBackground, Color markerColor,
boolean isPreferred) {
this.mgr = mgr;
@@ -93,6 +94,16 @@ abstract class MarkerSetImpl implements MarkerSet {
markers = new ModifiableAddressSetCollection();
}
@Override
public void setOwnerId(UniversalID ownerId) {
this.ownerId = ownerId;
}
@Override
public UniversalID getOwnerId() {
return ownerId;
}
protected abstract void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixmap, int index,
AddressIndexMap map, List<Integer> layouts);
@@ -244,11 +255,11 @@ abstract class MarkerSetImpl implements MarkerSet {
}
}
public final void paintMarkers(Graphics g, int index, VerticalPixelAddressMap pixmap,
public final void paintMarkers(Graphics g, int index, VerticalPixelAddressMap pixMap,
AddressIndexMap map) {
if (showMarkers) {
List<Integer> layouts = computeActiveLayouts(pixmap, map);
doPaintMarkers(g, pixmap, index, map, layouts);
List<Integer> layouts = computeActiveLayouts(pixMap, map);
doPaintMarkers(g, pixMap, index, map, layouts);
}
}
@@ -289,32 +300,32 @@ abstract class MarkerSetImpl implements MarkerSet {
return markers.contains(addr);
}
private List<Integer> computeActiveLayouts(VerticalPixelAddressMap pixmap,
private List<Integer> computeActiveLayouts(VerticalPixelAddressMap pixMap,
AddressIndexMap map) {
if (pixmap == null) {
if (pixMap == null) {
return null;
}
if (activeLayouts != null && activePixmap == pixmap) {
if (activeLayouts != null && activePixMap == pixMap) {
return activeLayouts; // use cache
}
List<Integer> newLayouts = new ArrayList<>();
int n = pixmap.getNumLayouts();
int n = pixMap.getNumLayouts();
for (int i = 0; i < n; i++) {
Address addr = pixmap.getLayoutAddress(i);
Address addr = pixMap.getLayoutAddress(i);
if (addr == null) {
continue;
}
Address end = pixmap.getLayoutEndAddress(i);
Address end = pixMap.getLayoutEndAddress(i);
if (markers.intersects(addr, end)) {
newLayouts.add(i);
}
}
activePixmap = pixmap;
activePixMap = pixMap;
activeLayouts = newLayouts;
return newLayouts;
}
@@ -536,6 +547,6 @@ abstract class MarkerSetImpl implements MarkerSet {
@Override
public String toString() {
return Json.toString(this, "active", "colorBackground", "markers");
return Json.toString(this, "name", "active", "colorBackground", "markers");
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -95,7 +95,7 @@ class PointMarkerSet extends MarkerSetImpl {
}
@Override
protected void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixmap, int index,
protected void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixMap, int index,
AddressIndexMap map, List<Integer> layouts) {
if (layouts == null) {
@@ -105,20 +105,20 @@ class PointMarkerSet extends MarkerSetImpl {
Iterator<Integer> it = layouts.iterator();
while (it.hasNext()) {
int i = it.next().intValue();
int yStart = pixmap.getMarkPosition(i);
int yStart = pixMap.getMarkPosition(i);
Image curImage = getMarkerImage(pixmap, i, yStart);
Image curImage = getMarkerImage(pixMap, i, yStart);
g.drawImage(curImage, 0, yStart, imageObserver);
}
}
private Image getMarkerImage(VerticalPixelAddressMap pixmap, int i, int yStart) {
private Image getMarkerImage(VerticalPixelAddressMap pixMap, int i, int yStart) {
if (markerDescriptor == null) {
return image;
}
Address address = pixmap.getLayoutAddress(i);
Address address = pixMap.getLayoutAddress(i);
MarkerLocation loc = new MarkerLocation(this, program, address, 0, yStart);
ImageIcon icon = markerDescriptor.getIcon(loc);
if (icon != null) {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -28,7 +28,7 @@ import docking.action.DockingActionIf;
import generic.theme.GColor;
import ghidra.app.nav.Navigatable;
import ghidra.app.services.GoToService;
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
@@ -40,13 +40,13 @@ import help.Help;
* Overview bar component. Uses color to indicate various address based properties for a program.
* Uses an {@link OverviewColorService} to get the appropriate color for an address.
*/
public class OverviewColorComponent extends JPanel implements OverviewProvider {
public class OverviewColorComponent extends JPanel implements ListingOverviewProvider {
private static final Color DEFAULT_COLOR = new GColor("color.bg.plugin.overview.defalt");
private OverviewColorService service;
private Color[] colors = new Color[0];
private final SwingUpdateManager refreshUpdater =
new SwingUpdateManager(100, 15000, () -> doRefresh());
private AddressIndexMap map;
private AddressIndexMap addressMap;
private Navigatable navigatable;
private PluginTool tool;
private List<DockingActionIf> actions;
@@ -76,6 +76,12 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
actions = service.getActions();
}
@Override
public void dispose() {
service = null;
ToolTipManager.sharedInstance().unregisterComponent(this);
}
/**
* Installs actions for this component
*/
@@ -154,10 +160,10 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
}
private void doRefresh() {
if (map == null) {
if (addressMap == null) {
return;
}
BigInteger indexCount = map.getIndexCount();
BigInteger indexCount = addressMap.getIndexCount();
if (indexCount.equals(BigInteger.ZERO)) {
Arrays.fill(colors, DEFAULT_COLOR);
repaint();
@@ -168,7 +174,7 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
for (int i = 0; i < colors.length; i++) {
if (colors[i] == null) {
BigInteger index = indexCount.multiply(BigInteger.valueOf(i)).divide(bigTotal);
Address address = map.getAddress(index);
Address address = addressMap.getAddress(index);
colors[i] = service.getColor(address);
}
}
@@ -178,17 +184,17 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
private Address getAddress(int pixelIndex) {
BigInteger bigHeight = BigInteger.valueOf(getOverviewPixelCount());
BigInteger bigPixelIndex = BigInteger.valueOf(pixelIndex);
BigInteger bigIndex = map.getIndexCount().multiply(bigPixelIndex).divide(bigHeight);
return map.getAddress(bigIndex);
BigInteger bigIndex = addressMap.getIndexCount().multiply(bigPixelIndex).divide(bigHeight);
return addressMap.getAddress(bigIndex);
}
private int getPixelIndex(Address address) {
BigInteger addressIndex = map.getIndex(address);
BigInteger addressIndex = addressMap.getIndex(address);
if (addressIndex == null) {
return -1;
}
BigInteger bigHeight = BigInteger.valueOf(getOverviewPixelCount());
BigInteger indexCount = map.getIndexCount();
BigInteger indexCount = addressMap.getIndexCount();
return addressIndex.multiply(bigHeight).divide(indexCount).intValue();
}
@@ -198,10 +204,10 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
}
@Override
public void setProgram(Program program, AddressIndexMap map) {
this.map = map;
colors = new Color[getOverviewPixelCount()];
refreshUpdater.updateLater();
public void screenDataChanged(Program program, AddressIndexMap map) {
this.addressMap = map;
this.colors = new Color[getOverviewPixelCount()];
this.refreshUpdater.updateLater();
}
@Override
@@ -248,5 +254,4 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
public PluginTool getTool() {
return tool;
}
}

View File

@@ -92,5 +92,4 @@ public interface OverviewColorService extends ExtensionPoint {
* @return the current program used by the service.
*/
public Program getProgram();
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -143,6 +143,8 @@ public class AddressTypeOverviewColorService
@Override
public void setOverviewComponent(OverviewColorComponent component) {
this.overviewComponent = component;
this.overviewComponent.getAccessibleContext()
.setAccessibleName("Address Type Overview Component");
}
/**

View File

@@ -4,9 +4,9 @@
* 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.
@@ -33,6 +33,7 @@ import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.services.CodeViewerService;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
@@ -147,7 +148,10 @@ public class PrintingPlugin extends ProgramPlugin {
format = job.defaultPage();
}
LayoutModel lm = cvService.getFieldPanel().getLayoutModel();
ListingPanel listingPanel = cvService.getListingPanel();
FieldPanel fp = listingPanel.getFieldPanel();
LayoutModel lm = fp.getLayoutModel();
//Scale everything down if appropriate to fit on the page
double scaleAmount =
format.getImageableWidth() / lm.getPreferredViewSize().width;
@@ -316,7 +320,8 @@ public class PrintingPlugin extends ProgramPlugin {
private void printVisibleContent(TaskMonitor monitor, Date startDate,
PrinterJob job, Book book, LayoutModel lm, double scaleAmount,
int maxPageHeight) {
FieldPanel fp = cvService.getFieldPanel();
ListingPanel listingPanel = cvService.getListingPanel();
FieldPanel fp = listingPanel.getFieldPanel();
List<AnchoredLayout> visibleLayouts = fp.getVisibleLayouts();
BigInteger startIndex = visibleLayouts.get(0).getIndex();
BigInteger endIndex = visibleLayouts.get(visibleLayouts.size() - 1).getIndex();

View File

@@ -4,9 +4,9 @@
* 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.
@@ -34,39 +34,48 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
/**
* Service provided by a plugin that shows the listing from a Program, i.e., a
* Code Viewer. The service allows other plugins to add components and
* actions local to the Code Viewer.
*
*
* Service provided by a plugin that shows the Code Viewer listing for a Program. The service
* allows other plugins to add components and actions local to the Code Viewer.
*/
@ServiceInfo(defaultProvider = CodeBrowserPlugin.class)
public interface CodeViewerService {
/**
* Add a provider that shows an overview of the program.
* <p>
* Note: the provider passed here will only appear in the primary code viewer, not in any of the
* snapshot windows. to have an overview provider appear in all windows, you must create a
* {@link ListingOverviewProviderService} implementation instead of using this method.
*
* @param overviewProvider provider to add
*/
public void addOverviewProvider(OverviewProvider overviewProvider);
public void addOverviewProvider(ListingOverviewProvider overviewProvider);
/**
* Remove a provider that shows an overview of the program.
* @param overviewProvider provider to remove
* @see CodeViewerService#addOverviewProvider(ListingOverviewProvider)
*/
public void removeOverviewProvider(OverviewProvider overviewProvider);
public void removeOverviewProvider(ListingOverviewProvider overviewProvider);
/**
* Add a provider that shows markers in a program for the portion
* that is visible.
* @param marginProvider provider to add
* @deprecated clients that wish to be margin providers should now create a
* {@link ListingMarginProviderService}.
*/
public void addMarginProvider(MarginProvider marginProvider);
@Deprecated(since = "12.1", forRemoval = true)
public void addMarginProvider(ListingMarginProvider marginProvider);
/**
* Remove a provider that shows markers in a program for the portion
* that is visible.
* @param marginProvider provider to remove
* @deprecated clients that wish to be margin providers should now create a
* {@link ListingMarginProviderService}.
*/
public void removeMarginProvider(MarginProvider marginProvider);
@Deprecated(since = "12.1", forRemoval = true)
public void removeMarginProvider(ListingMarginProvider marginProvider);
/**
* Add an action that is local to the Code Viewer.
@@ -88,13 +97,13 @@ public interface CodeViewerService {
/**
* Add a listener that is notified when a mouse button is pressed.
* @param listener
* @param listener the listener
*/
public void addButtonPressedListener(ButtonPressedListener listener);
/**
* Remove the button pressed listener.
* @param listener
* @param listener the listener
*/
public void removeButtonPressedListener(ButtonPressedListener listener);
@@ -114,7 +123,8 @@ public interface CodeViewerService {
public void removeHighlightProvider(ListingHighlightProvider provider, Program program);
/**
* Set a listing panel on the code viewer.
* Set a listing panel on the code viewer. The given listing panel is a secondary listing panel
* to be displayed along with the primary listing panel.
* @param listingPanel the panel to add.
*/
public void setListingPanel(ListingPanel listingPanel);
@@ -127,11 +137,12 @@ public interface CodeViewerService {
/**
* Remove the given listing panel from the code viewer.
* @param listingPanel the listing panel
*/
public void removeListingPanel(ListingPanel listingPanel);
/**
* Get Current view that the CodeViewer is showing.
* @return the current set of addresses that the CodeViewer is showing.}
*/
public AddressSetView getView();
@@ -145,15 +156,21 @@ public interface CodeViewerService {
public boolean goTo(ProgramLocation loc, boolean centerOnScreen);
/**
* Return the fieldPanel.
* Returns the current field panel.
* @return the current field panel.
* @deprecated use {@link #getListingPanel()} to get the field panel
*/
@Deprecated(since = "12.1", forRemoval = true)
public FieldPanel getFieldPanel();
/**
* Returns the current address-index-map
* {@return the current address index map.}
*/
public AddressIndexMap getAddressIndexMap();
/**
* {@return the format manager.}
*/
public FormatManager getFormatManager();
/**

View File

@@ -0,0 +1,37 @@
/* ###
* 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 ghidra.app.services;
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
/**
* A service to provide a widget that is designed to appear on the left-hand side of the Code Viewer
*/
public interface ListingMarginProviderService {
/**
* Creates a new margin provider.
* @return the provider
*/
public ListingMarginProvider createMarginProvider();
/**
* True if this service is the owner of the given provider.
* @param provider the provider to check
* @return true if the owner
*/
public boolean isOwner(ListingMarginProvider provider);
}

View File

@@ -0,0 +1,34 @@
/* ###
* 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 ghidra.app.services;
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
public interface ListingOverviewProviderService {
/**
* Creates a new overview provider.
* @return the provider
*/
public ListingOverviewProvider createOverviewProvider();
/**
* True if this service is the owner of the given provider.
* @param provider the provider to check
* @return true if the owner
*/
public boolean isOwner(ListingOverviewProvider provider);
}

View File

@@ -18,6 +18,7 @@ package ghidra.app.services;
import java.awt.Color;
import ghidra.program.model.address.*;
import ghidra.util.UniversalID;
/**
* Defines methods for working with a set of addresses that correspond to markers.
@@ -25,6 +26,19 @@ import ghidra.program.model.address.*;
*/
public interface MarkerSet extends Comparable<MarkerSet> {
/**
* Sets an optional owner ID that signals when this marker set should be painted. A null ID
* means that this marker set is global and should always be painted. Otherwise, this marker
* set will be painted when its owner ID matches the owner ID being painted.
* @param ownerId the ID
*/
public void setOwnerId(UniversalID ownerId);
/**
* {@return the owner ID. See #setOwner(UniversalID).}
*/
public UniversalID getOwnerId();
/**
* Add a marker at the address
* @param addr the address
@@ -151,8 +165,9 @@ public interface MarkerSet extends Comparable<MarkerSet> {
public void setMarkerColor(Color color);
/**
* Set the marker manager listener to use for user interaction
* with markers owned by this manager.
* Set the marker set descriptor. This allows clients to control tooltip text and program
* location generation for individual markers on-the-fly.
*
* @param markerDescriptor the descriptor
*/
public void setMarkerDescriptor(MarkerDescriptor markerDescriptor);

View File

@@ -965,7 +965,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* A library that has not been processed by the loader yet
*
* @param name The name of the library
* @param depth The recursive load depth of the library (based on the original binary being
* @param column The recursive load depth of the library (based on the original binary being
* loaded)
* @param temporary True if the library is temporary and should be discarded prior to returning
* from the load

View File

@@ -0,0 +1,88 @@
/* ###
* 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 ghidra.app.util.viewer.listingpanel;
import javax.swing.JComponent;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.util.MarkerLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.UniversalID;
/**
* Interface for objects that want to add a component to the listing's left margin.
*/
public interface ListingMarginProvider {
/**
* Sets the optional owner ID to be used with this margin provider. Implementations may use
* this ID to determine when they should paint.
* @param ownerId the ID
*/
/**
* Sets an optional owner ID that signals when the markers for this provider should be painted.
* A null ID means that this provider is a non-snapshot provider and should paint all markers.
* A non-null ID means this provider's markers will be painted when the marker's owner ID
* this provider's ID
* .
* @param ownerId the ID
*/
public void setOwnerId(UniversalID ownerId);
/**
* Get the component to show the margin markers.
* @return the component
*/
public JComponent getComponent();
/**
* Return true if can be resized.
* @return true if can be resized.
*/
public boolean isResizeable();
/**
* Called to notify this margin provider that the current screen information has changed.
*
* @param listingPanel the listing panel.
* @param addressIndexMap the address index map to use.
* @param pixelMap the vertical pixel map to use.
*/
public void screenDataChanged(ListingPanel listingPanel, AddressIndexMap addressIndexMap,
VerticalPixelAddressMap pixelMap);
/**
* Called from the client when their location changes internally. This is different from a tool
* location event, which is considered a global event.
* @param location the location
*/
public void setLocation(ProgramLocation location);
/**
* Get the marker location for the given x, y point.
*
* @param x the horizontal coordinate.
* @param y the vertical coordinate.
* @return the location
*/
public MarkerLocation getMarkerLocation(int x, int y);
/**
* Called when the client is done with this provider.
*/
public void dispose();
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -22,28 +22,32 @@ import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.model.listing.Program;
/**
* Interface implemented by classes that provide overview components to the right side of the
* listing.
* An overview component that will be placed to the right side of the listing.
*/
public interface OverviewProvider {
public interface ListingOverviewProvider {
/**
* Returns the component to display in the right margin of the listing.
* @return the component
*/
JComponent getComponent();
public JComponent getComponent();
/**
* Sets the current program and associated address-index map
* Called to notify this margin provider that the current screen information has changed.
*
* @param program the program to use.
* @param map the address-index map to use.
* @param program the program to use
* @param map the address index map to use
*/
void setProgram(Program program, AddressIndexMap map);
public void screenDataChanged(Program program, AddressIndexMap map);
/**
* Set the component provider that this overview navigates
*
* @param navigatable the navigatable provider
*/
void setNavigatable(Navigatable navigatable);
public void setNavigatable(Navigatable navigatable);
/**
* Clients call this when they are done with this provider.
*/
public void dispose();
}

View File

@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import docking.action.DockingActionIf;
@@ -31,10 +32,13 @@ import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.listener.*;
import docking.widgets.fieldpanel.support.*;
import docking.widgets.indexedscrollpane.IndexedScrollPane;
import generic.theme.GIcon;
import generic.theme.GThemeDefaults.Colors;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.codebrowser.LayeredColorModel;
import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
import ghidra.app.plugin.core.codebrowser.hover.ListingHoverService;
import ghidra.app.services.ButtonPressedListener;
import ghidra.app.services.*;
import ghidra.app.util.ListingHighlightProvider;
import ghidra.app.util.viewer.field.*;
import ghidra.app.util.viewer.format.FieldHeader;
@@ -44,14 +48,15 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.program.util.*;
import ghidra.util.Msg;
import ghidra.util.Swing;
import ghidra.util.*;
import ghidra.util.layout.HorizontalLayout;
public class ListingPanel extends JPanel implements FieldMouseListener, FieldLocationListener,
FieldSelectionListener, LayoutListener {
public static final int DEFAULT_DIVIDER_LOCATION = 70;
private static final Icon CURSOR_LOC_ICON =
new GIcon("icon.plugin.codebrowser.cursor.location");
private FormatManager formatManager;
private ListingModelAdapter layoutModel;
@@ -61,6 +66,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
private JSplitPane splitPane;
private int splitPaneDividerLocation = DEFAULT_DIVIDER_LOCATION;
private FocusingMouseListener focusingMouseListener = new FocusingMouseListener();
private ProgramLocationListener programLocationListener;
private ProgramSelectionListener programSelectionListener;
private ProgramSelectionListener liveProgramSelectionListener;
@@ -84,8 +90,21 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
private ListingHoverProvider listingHoverHandler;
private List<MarginProvider> marginProviders = new ArrayList<>();
private List<OverviewProvider> overviewProviders = new ArrayList<>();
private List<ListingMarginProvider> marginProviders = new ArrayList<>();
private List<ListingOverviewProvider> overviewProviders = new ArrayList<>();
private String currentTextSelection;
private boolean useMarkerNameSuffix;
private UniversalID marginOwnerId = UniversalIdGenerator.nextID();
private ChangeListener markerChangeListener;
private MarkerService markerService;
private Color cursorLineHighlightColor;
private boolean isHighlightCursorLineEnabled;
private MarkerSet selectionMarkers;
private MarkerSet highlightMarkers;
private MarkerSet cursorMarkers;
private VerticalPixelAddressMapImpl pixmap;
private PropertyBasedBackgroundColorModel propertyBasedColorModel;
private LayeredColorModel layeredColorModel;
@@ -103,8 +122,6 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
};
private List<AddressSetDisplayListener> displayListeners = new ArrayList<>();
private String currentTextSelection;
/**
* Constructs a new ListingPanel using the given FormatManager
*
@@ -128,7 +145,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
fieldPanel.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
for (MarginProvider provider : marginProviders) {
for (ListingMarginProvider provider : marginProviders) {
provider.getComponent().invalidate();
}
validate();
@@ -138,6 +155,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
String viewName = "Assembly Listing View";
fieldPanel.setName(viewName);
fieldPanel.getAccessibleContext().setAccessibleName(viewName);
markerChangeListener = new MarkerChangeListener();
}
/**
@@ -301,8 +320,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
private void updateProviders() {
AddressIndexMap addressIndexMap = layoutModel.getAddressIndexMap();
for (OverviewProvider element : overviewProviders) {
element.setProgram(getProgram(), addressIndexMap);
for (ListingMarginProvider provider : marginProviders) {
provider.screenDataChanged(this, addressIndexMap, pixmap);
}
for (ListingOverviewProvider provider : overviewProviders) {
provider.screenDataChanged(getProgram(), addressIndexMap);
}
for (ChangeListener indexMapChangeListener : indexMapChangeListeners) {
indexMapChangeListener.stateChanged(null);
@@ -323,20 +345,99 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
layoutModel.dataChanged(updateImmediately);
}
public void removeMarginService(ListingMarginProviderService service) {
for (ListingMarginProvider provider : marginProviders) {
if (service.isOwner(provider)) {
removeMarginProvider(provider);
provider.dispose();
return;
}
}
}
public void addMarginService(ListingMarginProviderService service, boolean isConnected) {
if (containsMarginProviver(service)) {
return;
}
ListingMarginProvider provider = service.createMarginProvider();
provider.setOwnerId(marginOwnerId);
addMarginProvider(provider);
}
private boolean containsMarginProviver(ListingMarginProviderService service) {
for (ListingMarginProvider provider : marginProviders) {
if (service.isOwner(provider)) {
return true;
}
}
return false;
}
public void removeOverviewService(ListingOverviewProviderService service) {
for (ListingOverviewProvider provider : overviewProviders) {
if (service.isOwner(provider)) {
removeOverviewProvider(provider);
provider.dispose();
return;
}
}
}
public void addOverviewService(ListingOverviewProviderService service, Navigatable navigatable,
boolean connected) {
if (containsOverviewProvider(service)) {
return;
}
ListingOverviewProvider provider = service.createOverviewProvider();
provider.setNavigatable(navigatable);
addOverviewProvider(provider);
}
private boolean containsOverviewProvider(ListingOverviewProviderService service) {
for (ListingOverviewProvider provider : overviewProviders) {
if (service.isOwner(provider)) {
return true;
}
}
return false;
}
/**
* Adds the MarginProvider to this panel
* Removes the given margin provider from this panel
*
* @param provider the MarginProvider that will provide components to display in this panel's
* left margin area.
* @param provider the MarginProvider to remove.
*/
public void addMarginProvider(MarginProvider provider) {
public void removeMarginProvider(ListingMarginProvider provider) {
JComponent component = provider.getComponent();
component.removeMouseListener(focusingMouseListener);
marginProviders.remove(provider);
buildPanels();
}
/**
* Adds the margin provider to this panel.
* <p>
* This method is for clients that create and manage their own listing panels that are not the
* main listing panel.
*
* @param provider the provider that will display in this listing panel's left margin area
*/
public void addMarginProvider(ListingMarginProvider provider) {
JComponent component = provider.getComponent();
component.removeMouseListener(focusingMouseListener);
component.addMouseListener(focusingMouseListener);
if (provider.isResizeable()) {
marginProviders.add(0, provider);
}
else {
marginProviders.add(provider);
}
provider.setProgram(getProgram(), layoutModel.getAddressIndexMap(), pixmap);
provider.screenDataChanged(this, layoutModel.getAddressIndexMap(), pixmap);
buildPanels();
}
@@ -363,26 +464,26 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
return null;
}
JPanel rightPanel = new JPanel(new HorizontalLayout(0));
for (OverviewProvider overviewProvider : overviewProviders) {
for (ListingOverviewProvider overviewProvider : overviewProviders) {
rightPanel.add(overviewProvider.getComponent());
}
return rightPanel;
}
private JComponent buildLeftComponent() {
List<MarginProvider> marginProviderList = getNonResizeableMarginProviders();
List<ListingMarginProvider> marginProviderList = getNonResizeableMarginProviders();
JPanel leftPanel = new JPanel(new ScrollpaneAlignedHorizontalLayout(scroller));
for (MarginProvider marginProvider : marginProviderList) {
for (ListingMarginProvider marginProvider : marginProviderList) {
leftPanel.add(marginProvider.getComponent());
}
return leftPanel;
}
private List<MarginProvider> getNonResizeableMarginProviders() {
private List<ListingMarginProvider> getNonResizeableMarginProviders() {
if (marginProviders.isEmpty()) {
return marginProviders;
}
MarginProvider firstMarginProvider = marginProviders.get(0);
ListingMarginProvider firstMarginProvider = marginProviders.get(0);
if (firstMarginProvider.isResizeable()) {
return marginProviders.subList(1, marginProviders.size());
}
@@ -391,7 +492,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
private JComponent buildCenterComponent() {
JComponent centerComponent = scroller;
MarginProvider resizeableMarginProvider = getResizeableMarginProvider();
ListingMarginProvider resizeableMarginProvider = getResizeableMarginProvider();
if (resizeableMarginProvider != null) {
if (splitPane != null) {
splitPaneDividerLocation = splitPane.getDividerLocation();
@@ -414,11 +515,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
return centerComponent;
}
private MarginProvider getResizeableMarginProvider() {
private ListingMarginProvider getResizeableMarginProvider() {
if (marginProviders.isEmpty()) {
return null;
}
MarginProvider marginProvider = marginProviders.get(0);
ListingMarginProvider marginProvider = marginProviders.get(0);
return marginProvider.isResizeable() ? marginProvider : null;
}
@@ -440,24 +541,22 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
indexMapChangeListeners.remove(listener);
}
/**
* Removes the given margin provider from this panel
*
* @param provider the MarginProvider to remove.
*/
public void removeMarginProvider(MarginProvider provider) {
marginProviders.remove(provider);
buildPanels();
}
/**
* Adds the given OverviewProvider with will be displayed in this panels right margin area.
* <p>
* This method is for clients that create and manage their own listing panels that are not the
* main listing panel.
*
* @param provider the OverviewProvider to display.
*/
public void addOverviewProvider(OverviewProvider provider) {
public void addOverviewProvider(ListingOverviewProvider provider) {
JComponent component = provider.getComponent();
component.removeMouseListener(focusingMouseListener);
component.addMouseListener(focusingMouseListener);
overviewProviders.add(provider);
provider.setProgram(getProgram(), layoutModel.getAddressIndexMap());
provider.screenDataChanged(getProgram(), layoutModel.getAddressIndexMap());
buildPanels();
}
@@ -466,7 +565,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
*
* @param provider the OverviewProvider to remove.
*/
public void removeOverviewProvider(OverviewProvider provider) {
public void removeOverviewProvider(ListingOverviewProvider provider) {
JComponent component = provider.getComponent();
component.removeMouseListener(focusingMouseListener);
overviewProviders.remove(provider);
buildPanels();
}
@@ -525,8 +628,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
public void layoutsChanged(List<AnchoredLayout> layouts) {
AddressIndexMap addrMap = layoutModel.getAddressIndexMap();
this.pixmap = new VerticalPixelAddressMapImpl(layouts, addrMap);
for (MarginProvider element : marginProviders) {
element.setProgram(getProgram(), addrMap, pixmap);
for (ListingMarginProvider provider : marginProviders) {
provider.screenDataChanged(this, addrMap, pixmap);
}
for (AddressSetDisplayListener listener : displayListeners) {
@@ -594,6 +697,10 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
setListingModel(null);
for (ListingMarginProvider provider : marginProviders) {
provider.dispose();
}
removeAll();
listingHoverHandler.dispose();
layoutModel.dispose();
@@ -885,11 +992,19 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
headerPanel.setTabLock(false);
}
ProgramLocation pLoc = layoutModel.getProgramLocation(location, field);
if (pLoc == null) {
return;
}
if (programLocationListener != null) {
ProgramLocation pLoc = layoutModel.getProgramLocation(location, field);
if (pLoc != null) {
programLocationListener.programLocationChanged(pLoc, trigger);
}
programLocationListener.programLocationChanged(pLoc, trigger);
}
setCursorMarkerAddress(pLoc.getAddress());
for (ListingMarginProvider provider : marginProviders) {
provider.setLocation(pLoc);
}
}
@@ -1005,7 +1120,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
*
* @return the providers
*/
public List<MarginProvider> getMarginProviders() {
public List<ListingMarginProvider> getMarginProviders() {
return marginProviders;
}
@@ -1014,7 +1129,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
*
* @return the providers
*/
public List<OverviewProvider> getOverviewProviders() {
public List<ListingOverviewProvider> getOverviewProviders() {
return overviewProviders;
}
@@ -1182,8 +1297,16 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
* @param trigger the cause of the change
*/
public void setSelection(ProgramSelection sel, EventTrigger trigger) {
Program program = getProgram();
MarkerSet markers = getSelectionMarkers(program);
if (sel == null) {
fieldPanel.setSelection(layoutModel.getFieldSelection(null), trigger);
if (markers != null) {
markers.clearAll();
}
return;
}
@@ -1216,6 +1339,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
}
}
fieldPanel.setSelection(layoutModel.getFieldSelection(sel), trigger);
if (markers != null) {
markers.clearAll();
markers.add(sel);
}
}
/**
@@ -1225,6 +1353,19 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
*/
public void setHighlight(ProgramSelection highlight) {
fieldPanel.setHighlight(layoutModel.getFieldSelection(highlight));
Program program = getProgram();
MarkerSet markers = getHighlightMarkers(program);
if (markers == null) {
return;
}
markers.clearAll();
if (highlight != null && program != null) {
markers.setAddressSet(highlight);
}
}
public ProgramSelection getProgramHighlight() {
@@ -1320,4 +1461,192 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
// we are not focusable, defer to contained field panel
fieldPanel.removeFocusListener(l);
}
//==================================================================================================
// Markers
//==================================================================================================
public void setUseMarkerNameSuffix(boolean b) {
// Note: this happens just after construction, so no need to recreate the markers
this.useMarkerNameSuffix = b;
}
public void setMarkerService(MarkerService markerService) {
if (this.markerService != null) {
this.markerService.removeChangeListener(markerChangeListener);
}
if (markerService != null) {
markerService.addChangeListener(markerChangeListener);
}
else {
doClearMarkers(getProgram());
}
this.markerService = markerService;
}
public void clearMarkers(Program program) {
doClearMarkers(program);
}
private void doClearMarkers(Program program) {
if (markerService == null) {
return;
}
if (program == null) {
return; // can happen during dispose after a programDeactivated()
}
if (selectionMarkers != null) {
markerService.removeMarker(selectionMarkers, program);
selectionMarkers = null;
}
if (highlightMarkers != null) {
markerService.removeMarker(highlightMarkers, program);
highlightMarkers = null;
}
if (cursorMarkers != null) {
markerService.removeMarker(cursorMarkers, program);
cursorMarkers = null;
}
}
public void setSelectionColor(Color color) {
fieldPanel.setSelectionColor(color);
if (selectionMarkers != null) {
selectionMarkers.setMarkerColor(color);
}
}
public void setHighlightColor(Color color) {
fieldPanel.setHighlightColor(color);
if (highlightMarkers != null) {
highlightMarkers.setMarkerColor(color);
}
}
private String getMarkerName(String baseName) {
if (useMarkerNameSuffix) {
return baseName + ' ' + marginOwnerId.toString();
}
return baseName;
}
private MarkerSet getSelectionMarkers(Program program) {
if (markerService == null || program == null) {
return null;
}
// already created
if (selectionMarkers != null) {
return selectionMarkers;
}
String markerName = getMarkerName("Selection");
Color color = fieldPanel.getSelectionColor();
selectionMarkers = markerService.createAreaMarker(markerName, "Selection Display",
program, MarkerService.SELECTION_PRIORITY, false, true, false, color);
selectionMarkers.setOwnerId(marginOwnerId);
return selectionMarkers;
}
private MarkerSet getHighlightMarkers(Program program) {
if (markerService == null || program == null) {
return null;
}
// already created
if (highlightMarkers != null) {
return highlightMarkers;
}
String markerName = getMarkerName("Highlight");
Color color = fieldPanel.getHighlightColor();
highlightMarkers = markerService.createAreaMarker(markerName, "Highlight Display ",
program, MarkerService.HIGHLIGHT_PRIORITY, false, true, false, color);
highlightMarkers.setOwnerId(marginOwnerId);
return highlightMarkers;
}
private MarkerSet getCursorMarkers(Program program) {
if (markerService == null || program == null) {
return null;
}
// already created
if (cursorMarkers != null) {
return cursorMarkers;
}
String markerName = getMarkerName("Cursor");
cursorMarkers = markerService.createPointMarker(markerName, "Cursor Location",
program, MarkerService.CURSOR_PRIORITY, true, true, isHighlightCursorLineEnabled,
cursorLineHighlightColor, CURSOR_LOC_ICON);
cursorMarkers.setOwnerId(marginOwnerId);
return cursorMarkers;
}
public void setCursorHighlightColor(Color cursorHighlightColor) {
this.cursorLineHighlightColor = cursorHighlightColor;
if (cursorMarkers != null) {
cursorMarkers.setMarkerColor(cursorHighlightColor);
}
}
public void setHighlightCursorLineEnabled(boolean b) {
this.isHighlightCursorLineEnabled = b;
if (cursorMarkers != null) {
cursorMarkers.setColoringBackground(b);
}
}
public void setCursorMarkerAddress(Address address) {
MarkerSet markers = getCursorMarkers(getProgram());
if (markers != null) {
markers.clearAll();
markers.add(address);
}
}
public void updateBackgroundColorModel() {
if (markerService == null) {
setBackgroundColorModel(null);
}
else {
AddressIndexMap indexMap = getAddressIndexMap();
setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerService, indexMap));
}
}
//==================================================================================================
// End Markers
//==================================================================================================
//==================================================================================================
// Inner Classes
//==================================================================================================
private class MarkerChangeListener implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
fieldPanel.repaint();
}
}
private class FocusingMouseListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
fieldPanel.requestFocus();
}
}
}

View File

@@ -1,60 +0,0 @@
/* ###
* 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 ghidra.app.util.viewer.listingpanel;
import javax.swing.JComponent;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.model.listing.Program;
import ghidra.program.util.MarkerLocation;
/**
* Interface for objects that want to add a component to the listing's left margin.
*/
public interface MarginProvider {
/**
* Get the component to show the margin markers.
* @return the component
*/
JComponent getComponent();
/**
* Return true if can be resized.
* @return true if can be resized.
*/
boolean isResizeable();
/**
* Set the program and associated maps.
*
* @param program the program to use.
* @param addressIndexMap the address-index map to use.
* @param pixelMap the vertical pixel map to use.
*/
void setProgram(Program program, AddressIndexMap addressIndexMap,
VerticalPixelAddressMap pixelMap);
/**
* Get the marker location for the given x, y point.
*
* @param x the horizontal coordinate.
* @param y the vertical coordinate.
* @return the location
*/
public MarkerLocation getMarkerLocation(int x, int y);
}

View File

@@ -394,8 +394,8 @@ public class ListingCodeComparisonView
public Object getContextObjectForMarginPanels(ListingPanel panel, MouseEvent event) {
Object source = event.getSource();
// Is event source a marker margin provider on the left side of the listing?
List<MarginProvider> marginProviders = panel.getMarginProviders();
for (MarginProvider marginProvider : marginProviders) {
List<ListingMarginProvider> marginProviders = panel.getMarginProviders();
for (ListingMarginProvider marginProvider : marginProviders) {
JComponent c = marginProvider.getComponent();
if (c == source) {
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
@@ -406,8 +406,8 @@ public class ListingCodeComparisonView
}
}
// Is event source an overview provider on the right side of the listing?
List<OverviewProvider> overviewProviders = panel.getOverviewProviders();
for (OverviewProvider overviewProvider : overviewProviders) {
List<ListingOverviewProvider> overviewProviders = panel.getOverviewProviders();
for (ListingOverviewProvider overviewProvider : overviewProviders) {
JComponent c = overviewProvider.getComponent();
if (c == source) {
return source; // Return the overview provider that was clicked.
@@ -623,8 +623,8 @@ public class ListingCodeComparisonView
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
Object source = event.getSource();
List<MarginProvider> marginProvidersForLP = lp.getMarginProviders();
for (MarginProvider marginProvider : marginProvidersForLP) {
List<ListingMarginProvider> marginProvidersForLP = lp.getMarginProviders();
for (ListingMarginProvider marginProvider : marginProvidersForLP) {
JComponent c = marginProvider.getComponent();
if (c == source) {
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
@@ -634,8 +634,8 @@ public class ListingCodeComparisonView
return source;
}
}
List<OverviewProvider> overviewProvidersForLP = lp.getOverviewProviders();
for (OverviewProvider overviewProvider : overviewProvidersForLP) {
List<ListingOverviewProvider> overviewProvidersForLP = lp.getOverviewProviders();
for (ListingOverviewProvider overviewProvider : overviewProvidersForLP) {
JComponent c = overviewProvider.getComponent();
if (c == source) {
return source;

View File

@@ -18,6 +18,7 @@ package ghidra.features.base.codecompare.listing;
import static ghidra.GhidraOptions.*;
import java.awt.Color;
import java.util.List;
import javax.swing.Icon;
@@ -94,9 +95,12 @@ public class ListingDisplay implements ListingDiffChangeListener {
private void createMarkerManager(String owner) {
markerManager = new ListingDisplayMarkerManager(tool, owner);
markerManager.addChangeListener(e -> listingPanel.repaint());
MarginProvider marginProvider = markerManager.getMarginProvider();
// Manually install our custom margin provider. We are not calling setMarginService(),
// which means that many of the listing markers will not work in the compare view.
ListingMarginProvider marginProvider = markerManager.createMarginProvider();
listingPanel.addMarginProvider(marginProvider);
OverviewProvider overviewProvider = markerManager.getOverviewProvider();
ListingOverviewProvider overviewProvider = markerManager.createOverviewProvider();
listingPanel.addOverviewProvider(overviewProvider);
}
@@ -171,7 +175,12 @@ public class ListingDisplay implements ListingDiffChangeListener {
markerManager.clearAll();
listingPanel.setView(view);
AddressIndexMap indexMap = listingPanel.getAddressIndexMap();
markerManager.getOverviewProvider().setProgram(program, indexMap);
List<ListingOverviewProvider> providers = listingPanel.getOverviewProviders();
for (ListingOverviewProvider provider : providers) {
provider.screenDataChanged(program, indexMap);
}
listingPanel.setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManager, program, indexMap));
setUpAreaMarkerSets(program, name);

View File

@@ -4,9 +4,9 @@
* 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.
@@ -68,11 +68,11 @@ public class MarkerLocation implements Serializable {
}
/**
* Returns the Marker Manager.
* Returns the marker set.
*
* @return the marker manager
* @return the marker set
*/
public MarkerSet getMarkerManager() {
public MarkerSet getMarkerSet() {
return markers;
}

View File

@@ -64,7 +64,7 @@ import ghidra.app.util.viewer.field.FieldFactory;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.listingpanel.MarginProvider;
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.framework.ToolUtils;
import ghidra.framework.plugintool.Plugin;
@@ -1247,9 +1247,9 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
CodeBrowserPlugin plugin = getPlugin(tool, CodeBrowserPlugin.class);
ListingPanel listingPanel = plugin.getListingPanel();
@SuppressWarnings("unchecked")
List<MarginProvider> list =
(List<MarginProvider>) getInstanceField("marginProviders", listingPanel);
for (MarginProvider marginProvider : list) {
List<ListingMarginProvider> list =
(List<ListingMarginProvider>) getInstanceField("marginProviders", listingPanel);
for (ListingMarginProvider marginProvider : list) {
listingPanel.removeMarginProvider(marginProvider);
}
});

View File

@@ -669,6 +669,8 @@ public class CommentsPluginTest extends AbstractGhidraHeadedIntegrationTest {
addReference(0x1001020, 0x1008294, RefType.DATA);
addReference(0x1001030, 0x1008394, RefType.DATA);
waitForProgram(program);
Address srcAddr = addr(0x01006990);
CodeUnit cu = program.getListing().getCodeUnitAt(srcAddr);

View File

@@ -44,17 +44,15 @@ import ghidra.app.plugin.core.clear.ClearPlugin;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.highlight.SetHighlightPlugin;
import ghidra.app.services.*;
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.cmd.CompoundCmd;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.BookmarkType;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.test.*;
import ghidra.util.Msg;
import ghidra.util.datastruct.WeakSet;
public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
@@ -162,8 +160,13 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
setSelection(fp, sel);
MarkerManager mm = (MarkerManager) markerService;
OverviewProvider op = mm.getOverviewProvider();
JPanel navPanel = (JPanel) op.getComponent();
@SuppressWarnings("unchecked")
WeakSet<MarkerOverviewProvider> overviewProviders =
(WeakSet<MarkerOverviewProvider>) getInstanceField("overviewProviders", mm);
MarkerOverviewProvider provider = overviewProviders.iterator().next();
JPanel navPanel = (JPanel) provider.getComponent();
waitForProgram(program);
@@ -224,8 +227,13 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(addrSet.contains(addr("0xf000131b")));
MarkerManager mm = (MarkerManager) markerService;
OverviewProvider op = mm.getOverviewProvider();
JPanel navPanel = (JPanel) op.getComponent();
@SuppressWarnings("unchecked")
WeakSet<MarkerOverviewProvider> overviewProviders =
(WeakSet<MarkerOverviewProvider>) getInstanceField("overviewProviders", mm);
MarkerOverviewProvider provider = overviewProviders.iterator().next();
JPanel navPanel = (JPanel) provider.getComponent();
waitForProgram(program);
@@ -278,7 +286,12 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
MouseEvent dummyEvent =
new MouseEvent(cb.getFieldPanel(), (int) time, time, 0, x, y, 1, false);
MarkerManager mm = (MarkerManager) markerService;
String tooltip = runSwing(() -> mm.generateToolTip(dummyEvent));
@SuppressWarnings("unchecked")
WeakSet<MarkerMarginProvider> marginProviders =
(WeakSet<MarkerMarginProvider>) getInstanceField("marginProviders", mm);
MarkerMarginProvider provider = marginProviders.iterator().next();
String tooltip = runSwing(() -> provider.generateToolTip(dummyEvent));
assertEquals(
"<html><font size=\"4\">Cursor<BR>Note [TEST]: comment<BR>Changes: Unsaved<BR>",
tooltip);
@@ -316,11 +329,11 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
MouseEvent dummyEvent = new MouseEvent(cb.getFieldPanel(), (int) System.currentTimeMillis(),
System.currentTimeMillis(), 0, x, y, 1, false);
// debug
ProgramLocation location = cb.getListingPanel().getProgramLocation(dummyEvent.getPoint());
Msg.debug(this, "location for point: " + location + "; at " + location.getAddress());
String tooltip = runSwing(() -> mm.generateToolTip(dummyEvent));
@SuppressWarnings("unchecked")
WeakSet<MarkerMarginProvider> marginProviders =
(WeakSet<MarkerMarginProvider>) getInstanceField("marginProviders", mm);
MarkerMarginProvider provider = marginProviders.iterator().next();
String tooltip = runSwing(() -> provider.generateToolTip(dummyEvent));
assertNotNull("No tooltip for field: " + cb.getCurrentField() + "\n\tat address: " +
cb.getCurrentAddress(), tooltip);

View File

@@ -83,9 +83,9 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener {
}
@Override
protected void cleanup() {
protected void dispose() {
fidFileManager.removeChangeListener(this);
super.cleanup();
super.dispose();
}
/**

View File

@@ -4,9 +4,9 @@
* 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.
@@ -133,7 +133,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
private DiffController diffControl;
private Program primaryProgram;
private Program secondaryDiffProgram;
private AddressFactory p2AddressFactory;
private ProgramDiffDetails diffDetails;
private ProgramSelection p2DiffHighlight;
@@ -286,33 +285,39 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (primaryProgram != null && !showingSecondProgram) {
return;
}
p1ViewAddrSet = p1AddressSet;
if (!showingSecondProgram) {
return;
}
if (showingSecondProgram) {
ProgramSelection previousP1Selection = currentSelection;
ProgramSelection previousP2DiffHighlight = p2DiffHighlight;
ProgramSelection previousP2Selection = p2Selection;
ProgramSelection previousP1Selection = currentSelection;
ProgramSelection previousP2DiffHighlight = p2DiffHighlight;
ProgramSelection previousP2Selection = p2Selection;
AddressSet p2ViewAddrSet =
DiffUtility.getCompatibleAddressSet(p1ViewAddrSet, secondaryDiffProgram);
diffListingPanel.setView(p2ViewAddrSet);
FieldPanel fp = diffListingPanel.getFieldPanel();
AddressSet p2ViewAddrSet =
DiffUtility.getCompatibleAddressSet(p1ViewAddrSet, secondaryDiffProgram);
diffListingPanel.setView(p2ViewAddrSet);
FieldPanel fp = diffListingPanel.getFieldPanel();
AddressSet p1AddressSetAsP2 =
DiffUtility.getCompatibleAddressSet(p1AddressSet, secondaryDiffProgram);
AddressIndexMap p2IndexMap = new AddressIndexMap(p1AddressSetAsP2);
markerManager.getOverviewProvider().setProgram(secondaryDiffProgram, p2IndexMap);
fp.setBackgroundColorModel(new MarkerServiceBackgroundColorModel(markerManager,
secondaryDiffProgram, p2IndexMap));
AddressSet p1AddressSetAsP2 =
DiffUtility.getCompatibleAddressSet(p1AddressSet, secondaryDiffProgram);
AddressIndexMap p2IndexMap = new AddressIndexMap(p1AddressSetAsP2);
List<ListingOverviewProvider> providers = diffListingPanel.getOverviewProviders();
for (ListingOverviewProvider provider : providers) {
provider.screenDataChanged(secondaryDiffProgram, p2IndexMap);
}
currentSelection = previousP1Selection;
p2DiffHighlight = previousP2DiffHighlight;
fp.setBackgroundColorModel(new MarkerServiceBackgroundColorModel(markerManager,
secondaryDiffProgram, p2IndexMap));
p2Selection = previousP2Selection;
setProgram2Selection(p2Selection);
if (p2DiffHighlight != null) {
setDiffHighlight(p2DiffHighlight);
}
currentSelection = previousP1Selection;
p2DiffHighlight = previousP2DiffHighlight;
p2Selection = previousP2Selection;
setProgram2Selection(p2Selection);
if (p2DiffHighlight != null) {
setDiffHighlight(p2DiffHighlight);
}
}
@@ -556,9 +561,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
return;
}
if (currentSelection == null) {
AddressFactory p1AddressFactory =
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
currentSelection = new ProgramSelection(p1AddressFactory);
currentSelection = new ProgramSelection();
}
actionManager.setP1SelectToP2ActionEnabled(
secondaryDiffProgram != null && !currentSelection.isEmpty());
@@ -605,8 +608,13 @@ public class ProgramDiffPlugin extends ProgramPlugin
diffListingPanel.setProgramLocationListener(this);
diffListingPanel.setProgramSelectionListener(this);
diffListingPanel.getFieldPanel().addFieldMouseListener(new MyFieldMouseListener());
diffListingPanel.addMarginProvider(markerManager.getMarginProvider());
diffListingPanel.addOverviewProvider(markerManager.getOverviewProvider());
// Manually install our custom margin provider. We are not calling setMarginService(),
// which means that many of the listing markers will not work in the diff view.
MarkerService markerService = tool.getService(MarkerService.class);
diffListingPanel.addMarginProvider(markerService.createMarginProvider());
diffListingPanel.addOverviewProvider(markerService.createOverviewProvider());
diffNavigatable = new DiffNavigatable(this, codeViewerService.getNavigatable());
diffFieldNavigator = new FieldNavigator(diffServiceProvider, diffNavigatable);
diffListingPanel.addButtonPressedListener(diffFieldNavigator);
@@ -678,9 +686,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
ProgramSelection getCurrentSelection() {
if (currentSelection == null) {
AddressFactory p1AddressFactory =
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
currentSelection = new ProgramSelection(p1AddressFactory);
currentSelection = new ProgramSelection();
}
return currentSelection;
}
@@ -753,10 +759,8 @@ public class ProgramDiffPlugin extends ProgramPlugin
// Make sure that the Diff selection is to the code unit boundary.
ProgramSelection p2CodeUnitSelection =
new ProgramSelection(DiffUtility.getCodeUnitSet(newP2Selection, secondaryDiffProgram));
AddressFactory p1AddressFactory =
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
ProgramSelection intersection =
new ProgramSelection(p2AddressFactory, p2CodeUnitSelection.intersect(p2DiffHighlight));
new ProgramSelection(p2CodeUnitSelection.intersect(p2DiffHighlight));
p2Selection = intersection;
AddressSet p2SelectionAsP1Set =
@@ -767,8 +771,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
// of the MultiListing Layout being used.
///////////////////////////////////////////
ProgramSelection p2SelectionAsP1 =
new ProgramSelection(p1AddressFactory, p2SelectionAsP1Set);
ProgramSelection p2SelectionAsP1 = new ProgramSelection(p2SelectionAsP1Set);
runSwing(() -> {
MarkerSet selectionMarkers = getSelectionMarkers();
selectionMarkers.clearAll();
@@ -783,7 +786,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
actionManager.setP1SelectToP2ActionEnabled(
(secondaryDiffProgram != null) && !currentSelection.isEmpty());
firePluginEvent(new ProgramSelectionPluginEvent(this.getName(),
new ProgramSelection(p1AddressFactory, currentSelection), primaryProgram));
new ProgramSelection(currentSelection), primaryProgram));
}
}
@@ -873,7 +876,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
}
AddressSet p2DiffSet = DiffUtility.getCompatibleAddressSet(p1DiffSet, secondaryDiffProgram);
ProgramSelection p2DiffSelection = new ProgramSelection(p2AddressFactory, p2DiffSet);
ProgramSelection p2DiffSelection = new ProgramSelection(p2DiffSet);
p2DiffHighlight = p2DiffSelection;
AddressSet p2DiffSetAsP1 = DiffUtility.getCompatibleAddressSet(p2DiffSet, primaryProgram);
@@ -918,7 +921,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (diffControl.hasNext()) {
diffControl.next();
}
setProgram2Selection(new ProgramSelection(p2AddressFactory, getDiffHighlightBlock()));
setProgram2Selection(new ProgramSelection(getDiffHighlightBlock()));
}
void previousDiff() {
@@ -926,7 +929,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (diffControl.hasPrevious()) {
diffControl.previous();
}
setProgram2Selection(new ProgramSelection(p2AddressFactory, getDiffHighlightBlock()));
setProgram2Selection(new ProgramSelection(getDiffHighlightBlock()));
}
private void clearDiff() {
@@ -992,8 +995,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
AddressSet p1IgnoreSet =
DiffUtility.getCompatibleAddressSet(p2IgnoreSet, primaryProgram);
diffControl.ignore(p1IgnoreSet, null);
p2DiffHighlight =
new ProgramSelection(p2AddressFactory, p2DiffHighlight.subtract(p2IgnoreSet));
p2DiffHighlight = new ProgramSelection(p2DiffHighlight.subtract(p2IgnoreSet));
adjustDiffDisplay();
@@ -1047,7 +1049,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
}
AddressSetView p2ViewAddrSet =
DiffUtility.getCompatibleAddressSet(adjustedView, secondaryDiffProgram);
setProgram2Selection(new ProgramSelection(p2AddressFactory, p2ViewAddrSet));
setProgram2Selection(new ProgramSelection(p2ViewAddrSet));
}
finally {
diffListingPanel.getFieldPanel().requestFocus();
@@ -1078,7 +1080,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
primaryProgram.removeListener(this);
primaryProgram = null;
secondaryDiffProgram = null;
p2AddressFactory = null;
}
sameProgramContext = false;
updatePgm2Enablement();
@@ -1221,7 +1222,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (!currentSelection.isEmpty()) {
AddressSet p2SelectionSet =
DiffUtility.getCompatibleAddressSet(currentSelection, secondaryDiffProgram);
setProgram2Selection(new ProgramSelection(p2AddressFactory,
setProgram2Selection(new ProgramSelection(
DiffUtility.getCodeUnitSet(p2SelectionSet, secondaryDiffProgram)));
}
if (p2Selection.isEmpty()) {
@@ -1630,7 +1631,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
primaryProgram = currentProgram;
secondaryDiffProgram = newProgram;
p2AddressFactory = secondaryDiffProgram.getAddressFactory();
applyFilter = applySettingsMgr.getDefaultApplyFilter();
diffDetails = new ProgramDiffDetails(primaryProgram, secondaryDiffProgram);
primaryProgram.addListener(this);
@@ -1884,14 +1884,14 @@ public class ProgramDiffPlugin extends ProgramPlugin
}
}
if (set.equals(p2Selection)) {
AddressSetView asView = p2Selection;
if (set.equals(asView)) {
return; // Selection is unchanged so do nothing.
}
MarkerSet selectionMarkers = getSelectionMarkers();
selectionMarkers.clearAll();
programSelectionChanged(new ProgramSelection(p2AddressFactory, set),
EventTrigger.GUI_ACTION);
programSelectionChanged(new ProgramSelection(set), EventTrigger.GUI_ACTION);
updatePgm2Enablement();
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -762,7 +762,8 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
}
private boolean isCursorOnScreen(CodeViewerService service) {
FieldPanel fieldPanel = service.getFieldPanel();
ListingPanel listingPanel = service.getListingPanel();
FieldPanel fieldPanel = listingPanel.getFieldPanel();
int cursorOffset = fieldPanel.getCursorOffset();
return cursorOffset >= 0; // negative offset means offscreen
}
@@ -779,7 +780,10 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
if (service == null) {
return null;
}
FieldSelection selection = service.getFieldPanel().getSelection();
ListingPanel listingPanel = service.getListingPanel();
FieldPanel fieldPanel = listingPanel.getFieldPanel();
FieldSelection selection = fieldPanel.getSelection();
AddressIndexMap addressIndexMap = service.getListingPanel().getAddressIndexMap();
AddressSet addressSet = addressIndexMap.getAddressSet(selection);
return addressSet;
@@ -788,13 +792,11 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
/**
* Sets the address set to be the selection in the tool.
*
* @param tool
* the tool
* @param set
* the addressSet to use for the selection
* @param tool the tool
* @param addresses the addressSet to use for the selection
*/
private void setSelectionInTool(PluginTool tool, AddressSetView addressSet) {
ProgramSelection programSelection = new ProgramSelection(addressSet);
private void setSelectionInTool(PluginTool tool, AddressSetView addresses) {
ProgramSelection programSelection = new ProgramSelection(addresses);
CodeViewerService service = tool.getService(CodeViewerService.class);
if (service == null) {
return;

View File

@@ -268,6 +268,10 @@ public abstract class Plugin implements ExtensionPoint, PluginEventListener, Ser
}
}
/**
* Called by the framework to dispose of this plugin and unregister for events and services.
* Subclasses should not override this method, but should instead override {@link #dispose()}.
*/
protected void cleanup() {
if (!disposed) {
Throwable thr = null;

View File

@@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.debug.gui.control;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import java.util.*;
import java.util.stream.Collectors;
@@ -26,6 +26,7 @@ import org.junit.Test;
import db.Transaction;
import docking.action.DockingActionIf;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerIntegrationTest;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
@@ -50,6 +51,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
@Before
public void setUpMethodActionsTest() throws Exception {
addPlugin(tool, CodeBrowserPlugin.class);
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
methodsPlugin = addPlugin(tool, DebuggerMethodActionsPlugin.class);

View File

@@ -4,9 +4,9 @@
* 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.
@@ -31,7 +31,7 @@ import ghidra.app.util.viewer.field.*;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.listingpanel.MarginProvider;
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address;
@@ -138,11 +138,11 @@ public class BlockModelScreenShots extends GhidraScreenShotGenerator {
final ListingPanel lp = cb.getListingPanel();
@SuppressWarnings("unchecked")
final List<MarginProvider> list =
new ArrayList<>((List<MarginProvider>) getInstanceField("marginProviders", lp));
final List<ListingMarginProvider> list =
new ArrayList<>((List<ListingMarginProvider>) getInstanceField("marginProviders", lp));
runSwing(() -> {
invokeInstanceMethod("buildPanels", lp);
for (MarginProvider marginProvider : list) {
for (ListingMarginProvider marginProvider : list) {
lp.removeMarginProvider(marginProvider);
}
});

View File

@@ -39,7 +39,7 @@ import ghidra.app.plugin.core.datamgr.DataTypesProvider;
import ghidra.app.plugin.core.programtree.ViewManagerComponentProvider;
import ghidra.app.util.viewer.field.*;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.CommentType;
@@ -265,10 +265,10 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
public void testCaptureMarkerPopup() {
setToolSize(1400, 1200);
ListingPanel listingPanel = plugin.getListingPanel();
List<OverviewProvider> overviewProviders = listingPanel.getOverviewProviders();
List<ListingOverviewProvider> overviewProviders = listingPanel.getOverviewProviders();
assertEquals(1, overviewProviders.size());
OverviewProvider provider = overviewProviders.get(0);
ListingOverviewProvider provider = overviewProviders.get(0);
rightClick(provider.getComponent(), 1, 1);
captureMenu();