Merge remote-tracking branch

'origin/GP-6085-dragonmacher-flow-arrows-for-all--SQUASHED'
(Closes #8601)
This commit is contained in:
Ryan Kurtz
2025-11-24 10:49:53 -05:00
50 changed files with 2144 additions and 1546 deletions
@@ -85,7 +85,8 @@ import ghidra.util.Msg;
servicesRequired = { servicesRequired = {
DebuggerLogicalBreakpointService.class, DebuggerLogicalBreakpointService.class,
MarkerService.class, MarkerService.class,
}) }
)
public class DebuggerBreakpointMarkerPlugin extends Plugin { public class DebuggerBreakpointMarkerPlugin extends Plugin {
private static final Color COLOR_BREAKPOINT_ENABLED_MARKER = private static final Color COLOR_BREAKPOINT_ENABLED_MARKER =
@@ -496,7 +497,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
new ProgramLocationActionContext(null, location.getProgram(), new ProgramLocationActionContext(null, location.getProgram(),
new ProgramLocation(location.getProgram(), location.getAddr()), null, null); new ProgramLocation(location.getProgram(), location.getAddr()), null, null);
if (contextCanManipulateBreakpoints(context)) { if (contextCanManipulateBreakpoints(context)) {
doToggleBreakpointsAt(ToggleBreakpointAction.NAME, context); doToggleBreakpointsAt(AbstractToggleBreakpointAction.NAME, context);
} }
} }
} }
@@ -753,28 +754,32 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
@AutoOptionDefined( @AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND, name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND,
description = "Whether or not to color background for memory at an enabled breakpoint", description = "Whether or not to color background for memory at an enabled breakpoint",
help = @HelpInfo(anchor = "colors")) help = @HelpInfo(anchor = "colors")
)
private boolean breakpointEnabledColoringBackground = private boolean breakpointEnabledColoringBackground =
DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_COLORING_BACKGROUND; DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_COLORING_BACKGROUND;
@AutoOptionDefined( @AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND, name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND,
description = "Whether or not to color background for memory at a disabled breakpoint", description = "Whether or not to color background for memory at a disabled breakpoint",
help = @HelpInfo(anchor = "colors")) help = @HelpInfo(anchor = "colors")
)
private boolean breakpointDisabledColoringBackground = private boolean breakpointDisabledColoringBackground =
DebuggerResources.DEFAULT_COLOR_DISABLED_BREAKPOINT_COLORING_BACKGROUND; DebuggerResources.DEFAULT_COLOR_DISABLED_BREAKPOINT_COLORING_BACKGROUND;
@AutoOptionDefined( @AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND, 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", 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 = private boolean breakpointIneffEnColoringBackground =
DebuggerResources.DEFAULT_COLOR_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND; DebuggerResources.DEFAULT_COLOR_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND;
@AutoOptionDefined( @AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND, 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", 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 = private boolean breakpointIneffDisColoringBackground =
DebuggerResources.DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND; DebuggerResources.DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND;
@@ -832,7 +837,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
} }
@AutoOptionConsumed( @AutoOptionConsumed(
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND) name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND
)
private void setEnabledBreakpointMarkerBackground(boolean breakpointColoringBackground) { private void setEnabledBreakpointMarkerBackground(boolean breakpointColoringBackground) {
for (BreakpointMarkerSets markers : markersByProgram.values()) { for (BreakpointMarkerSets markers : markersByProgram.values()) {
markers.setEnabledColoringBackground(breakpointColoringBackground); markers.setEnabledColoringBackground(breakpointColoringBackground);
@@ -840,7 +846,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
} }
@AutoOptionConsumed( @AutoOptionConsumed(
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND) name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND
)
private void setDisabledBreakpointMarkerBackground(boolean breakpointColoringBackground) { private void setDisabledBreakpointMarkerBackground(boolean breakpointColoringBackground) {
for (BreakpointMarkerSets markers : markersByProgram.values()) { for (BreakpointMarkerSets markers : markersByProgram.values()) {
markers.setDisabledColoringBackground(breakpointColoringBackground); markers.setDisabledColoringBackground(breakpointColoringBackground);
@@ -848,7 +855,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
} }
@AutoOptionConsumed( @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) { private void setIneffectiveEBreakpointMarkerBackground(boolean breakpointColoringBackground) {
for (BreakpointMarkerSets markers : markersByProgram.values()) { for (BreakpointMarkerSets markers : markersByProgram.values()) {
markers.setIneffectiveEnabledColoringBackground(breakpointColoringBackground); markers.setIneffectiveEnabledColoringBackground(breakpointColoringBackground);
@@ -856,7 +864,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
} }
@AutoOptionConsumed( @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) { private void setIneffectiveDBreakpointMarkerBackground(boolean breakpointColoringBackground) {
for (BreakpointMarkerSets markers : markersByProgram.values()) { for (BreakpointMarkerSets markers : markersByProgram.values()) {
markers.setIneffectiveDisabledColoringBackground(breakpointColoringBackground); markers.setIneffectiveDisabledColoringBackground(breakpointColoringBackground);
@@ -1089,7 +1098,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
actionDisableBreakpoint = new DisableBreakpointAction(); actionDisableBreakpoint = new DisableBreakpointAction();
actionClearBreakpoint = new ClearBreakpointAction(); actionClearBreakpoint = new ClearBreakpointAction();
tool.setMenuGroup(new String[] { SetBreakpointAction.NAME }, SetBreakpointAction.GROUP); tool.setMenuGroup(new String[] { AbstractSetBreakpointAction.NAME },
SetBreakpointAction.GROUP);
} }
@Override @Override
@@ -198,7 +198,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
} }
@Override @Override
public void locationChanged(CodeViewerProvider provider, ProgramLocation location) { public void broadcastLocationChanged(CodeViewerProvider provider, ProgramLocation location) {
// TODO: Fix cursor? // TODO: Fix cursor?
// Do not fire ProgramLocationPluginEvent. // Do not fire ProgramLocationPluginEvent.
if (provider == connectedProvider) { if (provider == connectedProvider) {
@@ -207,7 +207,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
} }
@Override @Override
public void selectionChanged(CodeViewerProvider provider, ProgramSelection selection) { public void broadcastSelectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
if (provider != connectedProvider) { if (provider != connectedProvider) {
return; return;
} }
@@ -220,7 +220,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
} }
@Override @Override
public void highlightChanged(CodeViewerProvider provider, ProgramSelection highlight) { public void broadcastHighlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
if (provider != connectedProvider) { if (provider != connectedProvider) {
return; return;
} }
@@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.core.debug.gui.listing; 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.BorderLayout;
import java.awt.Color; 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 protected class ForStaticSyncMappingChangeListener
implements DebuggerStaticMappingChangeListener { implements DebuggerStaticMappingChangeListener {
@Override @Override
@@ -335,7 +328,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
protected final JLabel trackingLabel = new JLabel(); protected final JLabel trackingLabel = new JLabel();
protected final MultiBlendedListingBackgroundColorModel colorModel; protected final MultiBlendedListingBackgroundColorModel colorModel;
protected final MarkerSetChangeListener markerChangeListener = new MarkerSetChangeListener();
protected MarkerServiceBackgroundColorModel markerServiceColorModel; protected MarkerServiceBackgroundColorModel markerServiceColorModel;
protected MarkerMarginProvider markerMarginProvider; protected MarkerMarginProvider markerMarginProvider;
protected MarkerOverviewProvider markerOverviewProvider; protected MarkerOverviewProvider markerOverviewProvider;
@@ -573,28 +565,13 @@ public class DebuggerListingProvider extends CodeViewerProvider {
@AutoServiceConsumed @AutoServiceConsumed
private void setMarkerService(MarkerService markerService) { private void setMarkerService(MarkerService markerService) {
if (this.markerService != null) { ListingPanel listingPanel = getListingPanel();
this.markerService.removeChangeListener(markerChangeListener); listingPanel.setMarkerService(markerService);
removeMarginProvider(markerMarginProvider);
markerMarginProvider = null;
removeOverviewProvider(markerOverviewProvider);
markerOverviewProvider = null;
}
removeOldStaticTrackingMarker(); removeOldStaticTrackingMarker();
this.markerService = markerService; this.markerService = markerService;
createNewStaticTrackingMarker(); createNewStaticTrackingMarker();
updateMarkerServiceColorModel(); 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 @AutoServiceConsumed
@@ -24,7 +24,7 @@ import javax.swing.*;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import ghidra.app.nav.Navigatable; 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.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program; 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. * 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. * 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; private static final Color DEFAULT_COLOR = Color.GRAY;
protected TimeOverviewColorService service; protected TimeOverviewColorService service;
private Color[] colorsByPixel = new Color[0]; private Color[] colorsByPixel = new Color[0];
@@ -101,7 +101,7 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
stop = start; stop = start;
start = tmp; start = tmp;
} }
span = Lifespan.span(start, stop); span = Lifespan.span(start, stop);
} }
plugin.setLifespan(span); plugin.setLifespan(span);
enableDrag = false; enableDrag = false;
@@ -113,6 +113,11 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
actions = service.getActions(); actions = service.getActions();
} }
@Override
public void dispose() {
uninstallActions();
}
/** /**
* Installs actions for this component * Installs actions for this component
*/ */
@@ -239,7 +244,7 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
} }
@Override @Override
public void setProgram(Program program, AddressIndexMap map) { public void screenDataChanged(Program program, AddressIndexMap map) {
// Ignored // Ignored
} }
@@ -221,8 +221,8 @@ public class TimeOverviewColorPlugin extends AbstractDebuggerPlugin {
private void uninstallOverview(TimeOverviewColorService overviewColorService) { private void uninstallOverview(TimeOverviewColorService overviewColorService) {
TimeOverviewColorComponent overviewComponent = activeServices.get(overviewColorService); TimeOverviewColorComponent overviewComponent = activeServices.get(overviewColorService);
overviewComponent.uninstallActions();
listingService.removeOverviewProvider(overviewComponent); listingService.removeOverviewProvider(overviewComponent);
overviewComponent.dispose();
activeServices.remove(overviewColorService); activeServices.remove(overviewColorService);
overviewColorService.setTrace(null); overviewColorService.setTrace(null);
} }
@@ -97,22 +97,21 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
} }
@Override @Override
public void highlightChanged(CodeViewerProvider provider, ProgramSelection highlight) { public void broadcastHighlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
if (highlightMarkers != null) {
highlightMarkers.clearAll();
}
if (highlight != null && currentProgram != null) {
if (highlightMarkers != null) {
highlightMarkers.add(highlight);
}
}
if (provider == connectedProvider) { if (provider == connectedProvider) {
tool.firePluginEvent(new ProgramHighlightPluginEvent(getName(), highlight, tool.firePluginEvent(new ProgramHighlightPluginEvent(getName(), highlight,
connectedProvider.getProgram())); connectedProvider.getProgram()));
} }
} }
@Override
public void broadcastLocationChanged(CodeViewerProvider provider, ProgramLocation location) {
if (provider == connectedProvider) {
tool.firePluginEvent(new ProgramLocationPluginEvent(getName(), location,
connectedProvider.getProgram()));
}
}
@Override @Override
public void processEvent(PluginEvent event) { public void processEvent(PluginEvent event) {
if (event instanceof ProgramClosedPluginEvent) { if (event instanceof ProgramClosedPluginEvent) {
@@ -125,7 +124,7 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
currentProgram.removeListener(this); currentProgram.removeListener(this);
} }
ProgramActivatedPluginEvent evt = (ProgramActivatedPluginEvent) event; 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(); currentProgram = evt.getActiveProgram();
if (currentProgram != null) { if (currentProgram != null) {
@@ -135,12 +134,13 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
else { else {
currentView = new AddressSet(); currentView = new AddressSet();
} }
connectedProvider.doSetProgram(currentProgram); connectedProvider.doSetProgram(currentProgram);
updateHighlightProvider(); connectedProvider.updateHighlightProvider();
updateBackgroundColorModel(); updateBackgroundColorModel(connectedProvider);
setHighlight(new FieldSelection()); setConnectedProviderHighlight(new FieldSelection());
setSelection(new ProgramSelection()); setConnectedProviderSelection(new ProgramSelection());
} }
else if (event instanceof ProgramLocationPluginEvent) { else if (event instanceof ProgramLocationPluginEvent) {
ProgramLocationPluginEvent evt = (ProgramLocationPluginEvent) event; ProgramLocationPluginEvent evt = (ProgramLocationPluginEvent) event;
@@ -156,12 +156,12 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
} }
else if (event instanceof ProgramSelectionPluginEvent) { else if (event instanceof ProgramSelectionPluginEvent) {
ProgramSelectionPluginEvent evt = (ProgramSelectionPluginEvent) event; ProgramSelectionPluginEvent evt = (ProgramSelectionPluginEvent) event;
setSelection(evt.getSelection()); setConnectedProviderSelection(evt.getSelection());
} }
else if (event instanceof ProgramHighlightPluginEvent) { else if (event instanceof ProgramHighlightPluginEvent) {
ProgramHighlightPluginEvent evt = (ProgramHighlightPluginEvent) event; ProgramHighlightPluginEvent evt = (ProgramHighlightPluginEvent) event;
if (evt.getProgram() == currentProgram) { if (evt.getProgram() == currentProgram) {
setHighlight(evt.getHighlight()); connectedProvider.setHighlight(evt.getHighlight());
} }
} }
else if (event instanceof ViewChangedPluginEvent) { else if (event instanceof ViewChangedPluginEvent) {
@@ -206,10 +206,13 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
if (location != null) { if (location != null) {
connectedProvider.setLocation(location); connectedProvider.setLocation(location);
} }
setHighlight(highlight);
connectedProvider.setHighlight(highlight);
if (selection != null) { if (selection != null) {
connectedProvider.setSelection(selection); connectedProvider.setSelection(selection);
} }
if (vp != null) { if (vp != null) {
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel(); FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
fieldPanel.setViewerPosition(vp.getIndex(), vp.getXOffset(), vp.getYOffset()); fieldPanel.setViewerPosition(vp.getIndex(), vp.getXOffset(), vp.getYOffset());
@@ -268,7 +271,7 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
FieldSelection highlight = new FieldSelection(); FieldSelection highlight = new FieldSelection();
highlight.load(saveState); highlight.load(saveState);
if (!highlight.isEmpty()) { if (!highlight.isEmpty()) {
setHighlight(highlight); setConnectedProviderHighlight(highlight);
} }
} }
@@ -286,19 +289,6 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
connectedProvider.readState(saveState); 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 @Override
public ViewManagerService getViewManager(CodeViewerProvider codeViewerProvider) { public ViewManagerService getViewManager(CodeViewerProvider codeViewerProvider) {
if (codeViewerProvider == connectedProvider) { if (codeViewerProvider == connectedProvider) {
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,22 +22,21 @@ import ghidra.program.util.ProgramSelection;
public interface CodeBrowserPluginInterface { 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); public ViewManagerService getViewManager(CodeViewerProvider provider);
CodeViewerProvider createNewDisconnectedProvider();
public CodeViewerProvider createNewDisconnectedProvider();
} }
@@ -20,7 +20,6 @@ import java.awt.Point;
import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable; import java.awt.datatransfer.Transferable;
import java.awt.dnd.*; import java.awt.dnd.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.*; import java.util.*;
@@ -112,7 +111,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
liveSelection = selection; liveSelection = selection;
updateSubTitle(); updateSubTitle();
}; };
private FocusingMouseListener focusingMouseListener;
private CodeBrowserClipboardProvider codeViewerClipboardProvider; private CodeBrowserClipboardProvider codeViewerClipboardProvider;
private ClipboardService clipboardService; private ClipboardService clipboardService;
@@ -151,6 +149,11 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
setDefaultWindowPosition(WindowPosition.RIGHT); setDefaultWindowPosition(WindowPosition.RIGHT);
listingPanel = new ListingPanel(formatMgr); 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); listingPanel.enablePropertyBasedColorModel(true);
decorationPanel = new ListingPanelContainer(listingPanel, isConnected); decorationPanel = new ListingPanelContainer(listingPanel, isConnected);
@@ -253,6 +256,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
public void dispose() { public void dispose() {
super.dispose(); super.dispose();
clearMarkers(program);
tool.removePopupActionProvider(this); tool.removePopupActionProvider(this);
if (clipboardService != null) { if (clipboardService != null) {
@@ -324,8 +329,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) { private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
Object source = event.getSource(); Object source = event.getSource();
List<MarginProvider> marginProviders = lp.getMarginProviders(); List<ListingMarginProvider> marginProviders = lp.getMarginProviders();
for (MarginProvider marginProvider : marginProviders) { for (ListingMarginProvider marginProvider : marginProviders) {
JComponent c = marginProvider.getComponent(); JComponent c = marginProvider.getComponent();
if (c == source) { if (c == source) {
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY()); MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
@@ -337,8 +342,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
} }
} }
} }
List<OverviewProvider> overviewProviders = lp.getOverviewProviders(); List<ListingOverviewProvider> overviewProviders = lp.getOverviewProviders();
for (OverviewProvider overviewProvider : overviewProviders) { for (ListingOverviewProvider overviewProvider : overviewProviders) {
JComponent c = overviewProvider.getComponent(); JComponent c = overviewProvider.getComponent();
if (c == source) { if (c == source) {
return source; return source;
@@ -444,6 +449,10 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
contextChanged(); contextChanged();
} }
void clearMarkers(Program p) {
listingPanel.clearMarkers(p);
}
protected void updateTitle() { protected void updateTitle() {
String subTitle = program == null ? "" : ' ' + program.getDomainFile().getName(); String subTitle = program == null ? "" : ' ' + program.getDomainFile().getName();
String newTitle = TITLE + subTitle; String newTitle = TITLE + subTitle;
@@ -657,6 +666,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
} }
} }
// events coming from the ListingPanel
@Override @Override
public void programLocationChanged(ProgramLocation loc, EventTrigger trigger) { public void programLocationChanged(ProgramLocation loc, EventTrigger trigger) {
if (plugin.isDisposed()) { if (plugin.isDisposed()) {
@@ -665,7 +675,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
if (!loc.equals(currentLocation)) { if (!loc.equals(currentLocation)) {
codeViewerClipboardProvider.setLocation(loc); codeViewerClipboardProvider.setLocation(loc);
currentLocation = loc; currentLocation = loc;
plugin.locationChanged(this, loc); plugin.broadcastLocationChanged(this, loc);
contextChanged(); contextChanged();
} }
} }
@@ -696,7 +706,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
currentSelection = selection; currentSelection = selection;
codeViewerClipboardProvider.setSelection(currentSelection); codeViewerClipboardProvider.setSelection(currentSelection);
listingPanel.setSelection(currentSelection); listingPanel.setSelection(currentSelection);
plugin.selectionChanged(this, currentSelection); plugin.broadcastSelectionChanged(this, currentSelection);
contextChanged(); contextChanged();
updateSubTitle(); updateSubTitle();
} }
@@ -792,7 +802,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
private void doSetHighlight(ProgramSelection highlight) { private void doSetHighlight(ProgramSelection highlight) {
listingPanel.setHighlight(highlight); listingPanel.setHighlight(highlight);
currentHighlight = highlight; currentHighlight = highlight;
plugin.highlightChanged(this, highlight); plugin.broadcastHighlightChanged(this, highlight);
contextChanged(); contextChanged();
} }
@@ -1161,45 +1171,51 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
listingPanel.removeDisplayListener(listener); listingPanel.removeDisplayListener(listener);
} }
private synchronized void createFocusingMouseListener() { public void addOverviewProvider(ListingOverviewProvider overviewProvider) {
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);
overviewProvider.setNavigatable(this); overviewProvider.setNavigatable(this);
getListingPanel().addOverviewProvider(overviewProvider); getListingPanel().addOverviewProvider(overviewProvider);
} }
public void addMarginProvider(MarginProvider marginProvider) { public void removeOverviewProvider(ListingOverviewProvider overviewProvider) {
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);
getListingPanel().removeOverviewProvider(overviewProvider); getListingPanel().removeOverviewProvider(overviewProvider);
} }
public void removeMarginProvider(MarginProvider marginProvider) { public void addMarginProvider(ListingMarginProvider marginProvider) {
JComponent component = marginProvider.getComponent(); getListingPanel().addMarginProvider(marginProvider);
component.removeMouseListener(focusingMouseListener); }
public void removeMarginProvider(ListingMarginProvider marginProvider) {
getListingPanel().removeMarginProvider(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 // Inner Classes
//================================================================================================== //==================================================================================================
@@ -1280,11 +1296,4 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
return list.toArray(new Highlight[list.size()]); return list.toArray(new Highlight[list.size()]);
} }
} }
private class FocusingMouseListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
getListingPanel().getFieldPanel().requestFocus();
}
}
} }
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,11 +15,11 @@
*/ */
package ghidra.app.plugin.core.flowarrow; package ghidra.app.plugin.core.flowarrow;
import java.awt.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefType;
import java.awt.*;
class ConditionalFlowArrow extends FlowArrow { class ConditionalFlowArrow extends FlowArrow {
private static final Stroke CONDITIONAL_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE, 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, private static final Stroke NORMAL_ACTIVE_STROKE = new BasicStroke(2, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER); BasicStroke.JOIN_MITER);
ConditionalFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end, ConditionalFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start,
RefType referenceType) { Address end, RefType referenceType) {
super(plugin, canvas, start, end, referenceType); super(provider, canvas, start, end, referenceType);
} }
@Override @Override
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,11 +15,11 @@
*/ */
package ghidra.app.plugin.core.flowarrow; package ghidra.app.plugin.core.flowarrow;
import java.awt.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefType;
import java.awt.*;
class DefaultFlowArrow extends FlowArrow { class DefaultFlowArrow extends FlowArrow {
private static final Stroke NORMAL_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE, 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, private static final Stroke NORMAL_ACTIVE_STROKE = new BasicStroke(2, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER); BasicStroke.JOIN_MITER);
DefaultFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end, DefaultFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start, Address end,
RefType referenceType) { RefType referenceType) {
super(plugin, canvas, start, end, referenceType); super(provider, canvas, start, end, referenceType);
} }
@Override @Override
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,11 +15,11 @@
*/ */
package ghidra.app.plugin.core.flowarrow; package ghidra.app.plugin.core.flowarrow;
import java.awt.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefType;
import java.awt.*;
class FallthroughFlowArrow extends FlowArrow { class FallthroughFlowArrow extends FlowArrow {
private static final Stroke FALLTHROUGH_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE, 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, private static final Stroke FALLTHROUGH_ACTIVE_STROKE = new BasicStroke(2,
BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10, new float[] { 8, 3, 2, 3 }, 0); BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10, new float[] { 8, 3, 2, 3 }, 0);
FallthroughFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end, FallthroughFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start,
RefType referenceType) { Address end, RefType referenceType) {
super(plugin, canvas, start, end, referenceType); super(provider, canvas, start, end, referenceType);
} }
@Override @Override
@@ -15,6 +15,8 @@
*/ */
package ghidra.app.plugin.core.flowarrow; package ghidra.app.plugin.core.flowarrow;
import static ghidra.util.HTMLUtilities.*;
import java.awt.*; import java.awt.*;
import java.awt.geom.PathIterator; import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
@@ -23,7 +25,6 @@ import java.util.List;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefType;
import ghidra.util.HTMLUtilities;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
abstract class FlowArrow { abstract class FlowArrow {
@@ -36,15 +37,16 @@ abstract class FlowArrow {
Address start; Address start;
Address end; Address end;
AddressSet addressSet; AddressSet addresses;
int depth = -1; int column = -1;
RefType refType; RefType refType;
private int maxColumn;
private boolean isUp; private boolean isUp;
boolean active; boolean active;
boolean selected; boolean selected;
private FlowArrowPlugin plugin; private FlowArrowMarginProvider provider;
private Component canvas; private Component canvas;
protected Shape arrowBody; protected Shape arrowBody;
protected Shape arrowHead; protected Shape arrowHead;
@@ -52,15 +54,16 @@ abstract class FlowArrow {
/** The shape of the arrow body, but with added size */ /** The shape of the arrow body, but with added size */
private List<Shape> clickableShapes = new ArrayList<>(); 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) { RefType referenceType) {
this.plugin = plugin; this.provider = provider;
this.canvas = canvas; this.canvas = canvas;
this.start = start; this.start = start;
this.end = end; this.end = end;
this.refType = referenceType; this.refType = referenceType;
this.addressSet = new AddressSet(new AddressRangeImpl(start, end)); this.maxColumn = provider.getMaxColumn();
isUp = start.compareTo(end) > 0; this.addresses = new AddressSet(new AddressRangeImpl(start, end));
this.isUp = start.compareTo(end) > 0;
} }
abstract Stroke getSelectedStroke(); abstract Stroke getSelectedStroke();
@@ -101,7 +104,7 @@ abstract class FlowArrow {
g2.setStroke(oldStroke); g2.setStroke(oldStroke);
} }
/** True if this arrow points up instead of down */ /** {@return true if this arrow points up instead of down} */
boolean isUp() { boolean isUp() {
return isUp; return isUp;
} }
@@ -242,20 +245,20 @@ abstract class FlowArrow {
int displayWidth = canvas.getWidth();// - FlowArrowPlugin.LEFT_OFFSET; int displayWidth = canvas.getWidth();// - FlowArrowPlugin.LEFT_OFFSET;
int lineWidth = calculateLineWidth(displayWidth); int lineWidth = calculateLineWidth(displayWidth);
arrowBody = FlowArrowShapeFactory.createArrowBody(plugin, this, displayWidth, displayHeight, arrowBody =
lineWidth); FlowArrowShapeFactory.createArrowBody(provider, this, displayWidth, displayHeight,
lineWidth);
arrowHead = FlowArrowShapeFactory.createArrowHead(plugin, this, displayWidth, displayHeight, arrowHead =
lineWidth); FlowArrowShapeFactory.createArrowHead(provider, this, displayWidth, displayHeight,
lineWidth);
} }
private int calculateLineWidth(int displayWidth) { private int calculateLineWidth(int displayWidth) {
// Crunch or stretch spacing depending upon width and maximum depth // Crunch or stretch spacing depending upon width and maximum depth
int lineWidth = DEFAULT_LINE_SPACING; int lineWidth = DEFAULT_LINE_SPACING;
int maxDepth = plugin.getMaxDepth(); if (maxColumn >= 0) {
int availabeWidth = displayWidth - FlowArrowMarginProvider.LEFT_OFFSET;
if (maxDepth >= 0) {
int availabeWidth = displayWidth - FlowArrowPlugin.LEFT_OFFSET;
lineWidth = (int) (availabeWidth * ARROW_SPACING_RATIO); lineWidth = (int) (availabeWidth * ARROW_SPACING_RATIO);
} }
if (lineWidth < MIN_LINE_SPACING) { if (lineWidth < MIN_LINE_SPACING) {
@@ -321,9 +324,17 @@ abstract class FlowArrow {
} }
public String getDisplayString() { public String getDisplayString() {
return "<html><table><tr><td>start</td><td>" + HTMLUtilities.escapeHTML(start.toString()) + //@formatter:off
"</td><tr><td>end</td><td>" + HTMLUtilities.escapeHTML(end.toString()) + return """
"</td><tr><td>ref type</td><td>" + refType + "</td></tr></table>"; <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 @Override
@@ -36,7 +36,7 @@ class FlowArrowPanel extends JPanel {
private Cursor clickCursor; private Cursor clickCursor;
private Cursor defaultCursor; private Cursor defaultCursor;
private FlowArrowPlugin plugin; private FlowArrowMarginProvider provider;
private Color foregroundColor; private Color foregroundColor;
private Color highlightColor; private Color highlightColor;
private Color selectedColor; private Color selectedColor;
@@ -44,8 +44,8 @@ class FlowArrowPanel extends JPanel {
private SwingUpdateManager mouseClickUpdater; private SwingUpdateManager mouseClickUpdater;
private Point pendingMouseClickPoint; private Point pendingMouseClickPoint;
FlowArrowPanel(FlowArrowPlugin p) { FlowArrowPanel(FlowArrowMarginProvider provider) {
this.plugin = p; this.provider = provider;
setMinimumSize(new Dimension(0, 0)); setMinimumSize(new Dimension(0, 0));
setPreferredSize(new Dimension(32, 1)); setPreferredSize(new Dimension(32, 1));
@@ -110,18 +110,18 @@ class FlowArrowPanel extends JPanel {
} }
private FlowArrow getArrow(Point p) { private FlowArrow getArrow(Point p) {
FlowArrow arrow = getArrow(p, plugin.getFlowArrowIterator()); FlowArrow arrow = getArrow(p, provider.getFlowArrowIterator());
if (arrow != null) { if (arrow != null) {
return arrow; return arrow;
} }
// try the arrows that hang around a bit // try the arrows that hang around a bit
arrow = getArrow(p, plugin.getSelectedFlowArrows()); arrow = getArrow(p, provider.getSelectedFlowArrows());
if (arrow != null) { if (arrow != null) {
return arrow; return arrow;
} }
return getArrow(p, plugin.getActiveArrows()); return getArrow(p, provider.getActiveArrows());
} }
private FlowArrow getArrow(Point p, Iterator<FlowArrow> it) { private FlowArrow getArrow(Point p, Iterator<FlowArrow> it) {
@@ -139,26 +139,24 @@ class FlowArrowPanel extends JPanel {
return; 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 // select any arrow we double-click
arrow.selected = true; arrow.selected = true;
plugin.setArrowSelected(arrow, true); provider.setArrowSelected(arrow, true);
Address end = arrow.end; Address end = arrow.end;
if (end.equals(plugin.getCurrentAddress())) { if (end.equals(provider.getCurrentAddress())) {
// go back the other direction // go back the other direction
end = arrow.start; end = arrow.start;
} }
if (plugin.isOnScreen(end)) { if (provider.isOnScreen(end)) {
// don't animate arrows completely on screen // don't animate arrows completely on screen
plugin.goTo(end); provider.goTo(end);
return; return;
} }
// Start the animation at the edge of the screen // 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); ScrollingCallback callback = new ScrollingCallback(start, end);
Animator animator = AnimationUtils.executeSwingAnimationCallback(callback); Animator animator = AnimationUtils.executeSwingAnimationCallback(callback);
@@ -169,7 +167,7 @@ class FlowArrowPanel extends JPanel {
FlowArrow arrow = getArrow(point); FlowArrow arrow = getArrow(point);
if (arrow != null) { if (arrow != null) {
arrow.selected = !arrow.selected; // toggle arrow.selected = !arrow.selected; // toggle
plugin.setArrowSelected(arrow, arrow.selected); provider.setArrowSelected(arrow, arrow.selected);
repaint(); repaint();
return; // only select one line at a time 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) { public void setBounds(int x, int y, int width, int height) {
// note: this gets called as the user drags the divider pane // note: this gets called as the user drags the divider pane
super.setBounds(x, y, width, height); super.setBounds(x, y, width, height);
plugin.updateAndRepaint(); provider.updateAndRepaint();
} }
@Override @Override
public String getToolTipText(MouseEvent e) { public String getToolTipText(MouseEvent e) {
Point point = e.getPoint(); Point point = e.getPoint();
Iterator<FlowArrow> it = plugin.getFlowArrowIterator(); Iterator<FlowArrow> it = provider.getFlowArrowIterator();
while (it.hasNext()) { while (it.hasNext()) {
FlowArrow arrow = it.next(); FlowArrow arrow = it.next();
if (arrow.intersects(point)) { if (arrow.intersects(point)) {
@@ -223,7 +221,7 @@ class FlowArrowPanel extends JPanel {
super.paintComponent(g); super.paintComponent(g);
Address currentAddress = plugin.getCurrentAddress(); Address currentAddress = provider.getCurrentAddress();
if (currentAddress == null) { if (currentAddress == null) {
return; return;
} }
@@ -235,7 +233,7 @@ class FlowArrowPanel extends JPanel {
// //
// Non-selected arrows // Non-selected arrows
// //
Iterator<FlowArrow> it = plugin.getFlowArrowIterator(); Iterator<FlowArrow> it = provider.getFlowArrowIterator();
while (it.hasNext()) { while (it.hasNext()) {
FlowArrow arrow = it.next(); FlowArrow arrow = it.next();
if (arrow.active || arrow.selected) { 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 // Active arrows--those at the selected address; paint on top of normal arrows
// //
it = plugin.getActiveArrows(); it = provider.getActiveArrows();
while (it.hasNext()) { while (it.hasNext()) {
FlowArrow arrow = it.next(); FlowArrow arrow = it.next();
if (arrow.selected) { if (arrow.selected) {
@@ -266,7 +264,7 @@ class FlowArrowPanel extends JPanel {
// Selected arrows // Selected arrows
// //
fgColor = selectedColor; fgColor = selectedColor;
it = plugin.getSelectedFlowArrows(); it = provider.getSelectedFlowArrows();
while (it.hasNext()) { while (it.hasNext()) {
FlowArrow arrow = it.next(); FlowArrow arrow = it.next();
paintJump(g2, arrow, fgColor); paintJump(g2, arrow, fgColor);
@@ -274,7 +272,7 @@ class FlowArrowPanel extends JPanel {
} }
private void paintJump(Graphics2D g2, FlowArrow arrow, Color fgColor) { 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 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.printf("%1.3f%%\t", (percentComplete * 100));
// System.err.println("scrolling to: " + current); // System.err.println("scrolling to: " + current);
plugin.scrollTo(current); provider.scrollTo(current);
lastAddress = current; // let's us avoid multiple duplicate requests lastAddress = current; // let's us avoid multiple duplicate requests
} }
@Override @Override
public void done() { public void done() {
// set the final position // set the final position
// TODO This happens after the animation is finished, which is jarring. If we want this centered, // 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 // then we need an entirely different way of animating the transition so that the centering
// is part of the animation. // is part of the animation.
// plugin.scrollToCenter(end); // plugin.scrollToCenter(end);
plugin.goTo(end); provider.goTo(end);
} }
void setAnimator(Animator animator) { void setAnimator(Animator animator) {
@@ -392,7 +390,7 @@ class FlowArrowPanel extends JPanel {
private class FlowArrowPanelMouseWheelListener implements MouseWheelListener { private class FlowArrowPanelMouseWheelListener implements MouseWheelListener {
@Override @Override
public void mouseWheelMoved(MouseWheelEvent e) { public void mouseWheelMoved(MouseWheelEvent e) {
plugin.forwardMouseEventToListing(e); provider.forwardMouseEventToListing(e);
} }
} }
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -28,26 +27,27 @@ class FlowArrowShapeFactory {
private static final int TRIANGLE_HEIGHT = 9; private static final int TRIANGLE_HEIGHT = 9;
private static final int TRIANGLE_WIDTH = 7; 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) { int lineSpacing) {
GeneralPath linePath = new GeneralPath(); GeneralPath linePath = new GeneralPath();
// Compute the start Y coordinate (place at proper offset, fix if off screen) // Compute the start Y coordinate (place at proper offset, fix if off screen)
Integer startTop = plugin.getStartPos(arrow.start); Integer startTop = provider.getStartPos(arrow.start);
Integer startBottom = plugin.getEndPos(arrow.start); Integer startBottom = provider.getEndPos(arrow.start);
int startY = 0; int startY = 0;
if (startTop != null && startBottom != null) { if (startTop != null && startBottom != null) {
int start = startTop; int start = startTop;
int end = startBottom; int end = startBottom;
startY = (start + end) / 2;// middle of line startY = (start + end) / 2;// middle of line
} }
else if (plugin.isBelowScreen(arrow.start)) { else if (provider.isBelowScreen(arrow.start)) {
startY = height; startY = height;
} }
Integer endTop = plugin.getStartPos(arrow.end); Integer endTop = provider.getStartPos(arrow.end);
Integer endBottom = plugin.getEndPos(arrow.end); Integer endBottom = provider.getEndPos(arrow.end);
int endY = 0; int endY = 0;
if (endTop != null && endBottom != null) { if (endTop != null && endBottom != null) {
int start = endTop; int start = endTop;
@@ -55,11 +55,11 @@ class FlowArrowShapeFactory {
endY = (start + end) / 2; endY = (start + end) / 2;
endY = Math.min(endY, height); // ensure on screen endY = Math.min(endY, height); // ensure on screen
} }
else if (plugin.isBelowScreen(arrow.end)) { else if (provider.isBelowScreen(arrow.end)) {
endY = height; endY = height;
} }
int x = width - ((arrow.depth + 1) * lineSpacing); int x = width - ((arrow.column + 1) * lineSpacing);
if (x < 3) { if (x < 3) {
x = 3; x = 3;
} }
@@ -126,12 +126,13 @@ class FlowArrowShapeFactory {
return linePath; 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) { int lineSpacing) {
// Compute the start Y coordinate (place at proper offset, fix if off screen) // Compute the start Y coordinate (place at proper offset, fix if off screen)
Integer addrStartInt = plugin.getStartPos(arrow.end); Integer addrStartInt = provider.getStartPos(arrow.end);
Integer addrEndInt = plugin.getEndPos(arrow.end); Integer addrEndInt = provider.getEndPos(arrow.end);
int endY = 0; int endY = 0;
if (addrStartInt != null && addrEndInt != null) { if (addrStartInt != null && addrEndInt != null) {
int start = addrStartInt; int start = addrStartInt;
@@ -139,11 +140,11 @@ class FlowArrowShapeFactory {
endY = (start + end) / 2; endY = (start + end) / 2;
endY = Math.min(endY, height); // ensure on screen endY = Math.min(endY, height); // ensure on screen
} }
else if (plugin.isBelowScreen(arrow.end)) { else if (provider.isBelowScreen(arrow.end)) {
endY = height; endY = height;
} }
int x = width - ((arrow.depth + 1) * lineSpacing); int x = width - ((arrow.column + 1) * lineSpacing);
if (x < 0) { if (x < 0) {
x = 3; x = 3;
} }
@@ -40,6 +40,7 @@ import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.ColorUtils.ColorBlender; import ghidra.util.ColorUtils.ColorBlender;
import ghidra.util.UniversalID;
import ghidra.util.datastruct.*; import ghidra.util.datastruct.*;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.task.SwingUpdateManager; import ghidra.util.task.SwingUpdateManager;
@@ -70,11 +71,9 @@ public class MarkerManager implements MarkerService {
private SwingUpdateManager updater; private SwingUpdateManager updater;
private GoToService goToService; private GoToService goToService;
private MarkerMarginProvider primaryMarginProvider;
private WeakSet<MarkerMarginProvider> marginProviders = private WeakSet<MarkerMarginProvider> marginProviders =
WeakDataStructureFactory.createCopyOnWriteWeakSet(); WeakDataStructureFactory.createCopyOnWriteWeakSet();
private MarkerOverviewProvider primaryOverviewProvider;
private WeakSet<MarkerOverviewProvider> overviewProviders = private WeakSet<MarkerOverviewProvider> overviewProviders =
WeakDataStructureFactory.createCopyOnWriteWeakSet(); WeakDataStructureFactory.createCopyOnWriteWeakSet();
@@ -101,9 +100,6 @@ public class MarkerManager implements MarkerService {
notifyListeners(); notifyListeners();
}); });
primaryMarginProvider = createMarginProvider();
primaryOverviewProvider = createOverviewProvider();
Gui.addThemeListener(themeListener); Gui.addThemeListener(themeListener);
} }
@@ -220,8 +216,12 @@ public class MarkerManager implements MarkerService {
} }
public MarkerMarginProvider getMarginProvider() { public boolean contains(ListingMarginProvider provider) {
return primaryMarginProvider; return marginProviders.contains(provider);
}
public boolean contains(ListingOverviewProvider provider) {
return overviewProviders.contains(provider);
} }
@Override @Override
@@ -231,8 +231,8 @@ public class MarkerManager implements MarkerService {
return provider; return provider;
} }
public OverviewProvider getOverviewProvider() { public void removeProvider(MarkerMarginProvider provider) {
return primaryOverviewProvider; marginProviders.remove(provider);
} }
@Override @Override
@@ -246,7 +246,6 @@ public class MarkerManager implements MarkerService {
Gui.removeThemeListener(themeListener); Gui.removeThemeListener(themeListener);
updater.dispose(); updater.dispose();
markerSetCache.clear(); markerSetCache.clear();
overviewProviders.forEach(provider -> provider.dispose());
} }
void navigateTo(Navigatable navigatable, Program program, int x, int y, int viewHeight, 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); entry.paintNavigation(g, panel.getViewHeight(), panel.getWidth(), addrMap);
} }
void paintMarkers(Program program, Graphics g, VerticalPixelAddressMap pixmap, void paintMarkers(UniversalID ownerId, Program program, Graphics g,
AddressIndexMap addrMap) { VerticalPixelAddressMap pixMap, AddressIndexMap addrMap) {
MarkerSetCacheEntry entry = markerSetCache.get(program); MarkerSetCacheEntry entry = markerSetCache.get(program);
if (entry == null) { if (entry == null) {
return; return;
} }
entry.paintMarkers(g, pixmap, addrMap); entry.paintMarkers(ownerId, g, pixMap, addrMap);
} }
void showToolTipPopup(MouseEvent event, String tip) { void showToolTipPopup(MouseEvent event, String tip) {
@@ -301,10 +300,6 @@ public class MarkerManager implements MarkerService {
popupWindow.showPopup(event); popupWindow.showPopup(event);
} }
/*testing*/ String generateToolTip(MouseEvent event) {
return primaryMarginProvider.generateToolTip(event);
}
List<String> getMarkerTooltipLines(Program program, int y, int x, Address minAddr, List<String> getMarkerTooltipLines(Program program, int y, int x, Address minAddr,
Address maxAddr) { Address maxAddr) {
MarkerSetCacheEntry entry = markerSetCache.get(program); MarkerSetCacheEntry entry = markerSetCache.get(program);
@@ -484,6 +479,7 @@ public class MarkerManager implements MarkerService {
if (program == null || program.isClosed()) { if (program == null || program.isClosed()) {
return null; return null;
} }
MarkerSetCacheEntry entry = map.computeIfAbsent(program, this::newEntry); MarkerSetCacheEntry entry = map.computeIfAbsent(program, this::newEntry);
if (program.isClosed()) { if (program.isClosed()) {
map.remove(program); 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; int count = 0;
for (MarkerSetImpl markers : markerSets) { for (MarkerSetImpl markers : markerSets) {
count++; count++;
if (markers.active) { if (!markers.active) {
markers.paintMarkers(g, count++, pixmap, addrMap); 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); return new ArrayList<>(markerSets);
} }
} }
} }
@@ -20,6 +20,8 @@ import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.HelpTopics; 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.options.Options;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus; 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 " + "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 " + "value such as selection. Point markers are used to represent individual addresses such " +
"as bookmarks.", "as bookmarks.",
servicesRequired = { CodeViewerService.class, GoToService.class }, servicesRequired = { GoToService.class },
servicesProvided = { MarkerService.class }, servicesProvided = { MarkerService.class, ListingMarginProviderService.class, ListingOverviewProviderService.class },
eventsConsumed = {} eventsConsumed = {}
) )
//@formatter:on //@formatter:on
/** /**
* Plugin to manage marker and navigation panels. * 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; private MarkerManager markerManager;
public MarkerManagerPlugin(PluginTool tool) { public MarkerManagerPlugin(PluginTool tool) {
@@ -65,17 +67,26 @@ public class MarkerManagerPlugin extends Plugin {
@Override @Override
protected void dispose() { protected void dispose() {
if (codeViewerService != null) {
codeViewerService.removeMarginProvider(markerManager.getMarginProvider());
codeViewerService.removeOverviewProvider(markerManager.getOverviewProvider());
}
markerManager.dispose(); markerManager.dispose();
} }
@Override @Override
protected void init() { public ListingMarginProvider createMarginProvider() {
codeViewerService = tool.getService(CodeViewerService.class); return markerManager.createMarginProvider();
codeViewerService.addMarginProvider(markerManager.getMarginProvider()); }
codeViewerService.addOverviewProvider(markerManager.getOverviewProvider());
@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);
} }
} }
@@ -28,6 +28,8 @@ import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.util.MarkerLocation; 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 * 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 * These are managed by a {@link MarkerManager}. Obtain one via
* {@link MarkerService#createMarginProvider()}. * {@link MarkerService#createMarginProvider()}.
*/ */
public class MarkerMarginProvider implements MarginProvider { public class MarkerMarginProvider implements ListingMarginProvider {
private final MarkerManager markerManager; private final MarkerManager markerManager;
private final MarkerPanel markerPanel; 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() { void repaintPanel() {
markerPanel.repaint(); markerPanel.repaint();
} }
@@ -95,12 +113,11 @@ public class MarkerMarginProvider implements MarginProvider {
} }
@Override @Override
public void setProgram(Program program, AddressIndexMap addrMap, public void screenDataChanged(ListingPanel listing, AddressIndexMap addrMap,
VerticalPixelAddressMap pixmap) { VerticalPixelAddressMap pixMap) {
this.program = program; this.program = listing.getProgram();
this.pixmap = pixmap; this.pixmap = pixMap;
this.markerPanel.listingUpdated(listing, addrMap, pixMap);
this.markerPanel.setProgram(program, addrMap, pixmap);
markerManager.updateMarkerSets(program, true, false, true); markerManager.updateMarkerSets(program, true, false, true);
} }
@@ -30,7 +30,7 @@ import ghidra.GhidraOptions;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.services.MarkerService; import ghidra.app.services.MarkerService;
import ghidra.app.util.HelpTopics; 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.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.options.*; import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@@ -46,7 +46,7 @@ import ghidra.util.Swing;
* These are managed by a {@link MarkerManager}. Obtain one via * These are managed by a {@link MarkerManager}. Obtain one via
* {@link MarkerService#createOverviewProvider()}. * {@link MarkerService#createOverviewProvider()}.
*/ */
public class MarkerOverviewProvider implements OverviewProvider { public class MarkerOverviewProvider implements ListingOverviewProvider {
private final PluginTool tool; private final PluginTool tool;
private final String owner; private final String owner;
@@ -74,7 +74,8 @@ public class MarkerOverviewProvider implements OverviewProvider {
actionList = new MarkerActionList(); actionList = new MarkerActionList();
} }
void dispose() { @Override
public void dispose() {
actionList.dispose(); actionList.dispose();
} }
@@ -88,11 +89,11 @@ public class MarkerOverviewProvider implements OverviewProvider {
} }
@Override @Override
public void setProgram(Program program, AddressIndexMap map) { public void screenDataChanged(Program p, AddressIndexMap addressMap) {
this.program = program; this.program = p;
navigationPanel.setProgram(program, map); navigationPanel.setProgram(p, addressMap);
markerManager.updateMarkerSets(program, true, true, false); markerManager.updateMarkerSets(p, true, true, false);
actionList.refresh(); actionList.refresh();
} }
@@ -24,10 +24,12 @@ import javax.swing.JPanel;
import javax.swing.ToolTipManager; import javax.swing.ToolTipManager;
import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.FieldPanel;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.listingpanel.VerticalPixelAddressMap; import ghidra.app.util.viewer.listingpanel.VerticalPixelAddressMap;
import ghidra.app.util.viewer.util.AddressIndexMap; import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.UniversalID;
/** /**
* Panel to display markers. Normally placed to the left hand side of the scrolled * 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 { public class MarkerPanel extends JPanel {
private MarkerManager manager; private MarkerManager manager;
private Program program; private Program program;
private AddressIndexMap addrMap; private AddressIndexMap addrMap;
private VerticalPixelAddressMap pixmap; private VerticalPixelAddressMap pixMap;
private UniversalID ownerId;
MarkerPanel(MarkerManager manager) { MarkerPanel(MarkerManager manager) {
super(); super();
this.manager = manager; this.manager = manager;
this.setPreferredSize(new Dimension(16, 1)); this.setPreferredSize(new Dimension(16, 1));
ToolTipManager.sharedInstance().registerComponent(this); ToolTipManager.sharedInstance().registerComponent(this);
} }
void setProgram(Program program, AddressIndexMap addrMap, VerticalPixelAddressMap pixmap) { void setOwnerId(UniversalID ownerId) {
this.program = program; this.ownerId = ownerId;
this.addrMap = addrMap; }
this.pixmap = pixmap;
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 @Override
protected void paintComponent(Graphics g) { protected void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
manager.paintMarkers(program, g, pixmap, addrMap); manager.paintMarkers(ownerId, program, g, pixMap, addrMap);
} }
@Override @Override
@@ -81,14 +94,14 @@ public class MarkerPanel extends JPanel {
} }
String generateToolTip(MouseEvent event) { String generateToolTip(MouseEvent event) {
if (pixmap == null) { if (pixMap == null) {
return null; return null;
} }
int y = event.getY(); int y = event.getY();
int x = event.getX(); int x = event.getX();
int layoutIndex = pixmap.findLayoutAt(y); int layoutIndex = pixMap.findLayoutAt(y);
Address layoutAddress = pixmap.getLayoutAddress(layoutIndex); Address layoutAddress = pixMap.getLayoutAddress(layoutIndex);
if (layoutAddress == null) { if (layoutAddress == null) {
return null; return null;
} }
@@ -99,7 +112,8 @@ public class MarkerPanel extends JPanel {
private List<String> getMarkerTooltipLines(int y, int x, int layoutIndex, private List<String> getMarkerTooltipLines(int y, int x, int layoutIndex,
Address layoutAddress) { Address layoutAddress) {
Address endAddr = pixmap.getLayoutEndAddress(layoutIndex); Address endAddr = pixMap.getLayoutEndAddress(layoutIndex);
return manager.getMarkerTooltipLines(program, y, layoutIndex, layoutAddress, endAddr); return manager.getMarkerTooltipLines(program, y, layoutIndex, layoutAddress, endAddr);
} }
} }
@@ -35,8 +35,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.util.MarkerLocation; import ghidra.program.util.MarkerLocation;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.ColorUtils; import ghidra.util.*;
import ghidra.util.Swing;
import ghidra.util.datastruct.SortedRangeList; import ghidra.util.datastruct.SortedRangeList;
abstract class MarkerSetImpl implements MarkerSet { abstract class MarkerSetImpl implements MarkerSet {
@@ -52,7 +51,7 @@ abstract class MarkerSetImpl implements MarkerSet {
protected AddressSetCollection markers; protected AddressSetCollection markers;
protected SortedRangeList overview = null; protected SortedRangeList overview = null;
protected VerticalPixelAddressMap activePixmap = null; protected VerticalPixelAddressMap activePixMap = null;
protected List<Integer> activeLayouts = null; protected List<Integer> activeLayouts = null;
protected Color markerColor; protected Color markerColor;
@@ -72,9 +71,11 @@ abstract class MarkerSetImpl implements MarkerSet {
private boolean colorBackground; private boolean colorBackground;
private boolean isPreferred; 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, MarkerSetImpl(MarkerManager mgr, Program program, String name, String desc, int priority,
boolean showMarkers, boolean showMarkers, boolean showNavigation, boolean colorBackground, Color markerColor,
boolean showNavigation, boolean colorBackground, Color markerColor,
boolean isPreferred) { boolean isPreferred) {
this.mgr = mgr; this.mgr = mgr;
@@ -93,6 +94,16 @@ abstract class MarkerSetImpl implements MarkerSet {
markers = new ModifiableAddressSetCollection(); 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, protected abstract void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixmap, int index,
AddressIndexMap map, List<Integer> layouts); 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) { AddressIndexMap map) {
if (showMarkers) { if (showMarkers) {
List<Integer> layouts = computeActiveLayouts(pixmap, map); List<Integer> layouts = computeActiveLayouts(pixMap, map);
doPaintMarkers(g, pixmap, index, map, layouts); doPaintMarkers(g, pixMap, index, map, layouts);
} }
} }
@@ -289,32 +300,32 @@ abstract class MarkerSetImpl implements MarkerSet {
return markers.contains(addr); return markers.contains(addr);
} }
private List<Integer> computeActiveLayouts(VerticalPixelAddressMap pixmap, private List<Integer> computeActiveLayouts(VerticalPixelAddressMap pixMap,
AddressIndexMap map) { AddressIndexMap map) {
if (pixmap == null) { if (pixMap == null) {
return null; return null;
} }
if (activeLayouts != null && activePixmap == pixmap) { if (activeLayouts != null && activePixMap == pixMap) {
return activeLayouts; // use cache return activeLayouts; // use cache
} }
List<Integer> newLayouts = new ArrayList<>(); List<Integer> newLayouts = new ArrayList<>();
int n = pixmap.getNumLayouts(); int n = pixMap.getNumLayouts();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
Address addr = pixmap.getLayoutAddress(i); Address addr = pixMap.getLayoutAddress(i);
if (addr == null) { if (addr == null) {
continue; continue;
} }
Address end = pixmap.getLayoutEndAddress(i); Address end = pixMap.getLayoutEndAddress(i);
if (markers.intersects(addr, end)) { if (markers.intersects(addr, end)) {
newLayouts.add(i); newLayouts.add(i);
} }
} }
activePixmap = pixmap; activePixMap = pixMap;
activeLayouts = newLayouts; activeLayouts = newLayouts;
return newLayouts; return newLayouts;
} }
@@ -536,6 +547,6 @@ abstract class MarkerSetImpl implements MarkerSet {
@Override @Override
public String toString() { public String toString() {
return Json.toString(this, "active", "colorBackground", "markers"); return Json.toString(this, "name", "active", "colorBackground", "markers");
} }
} }
@@ -95,7 +95,7 @@ class PointMarkerSet extends MarkerSetImpl {
} }
@Override @Override
protected void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixmap, int index, protected void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixMap, int index,
AddressIndexMap map, List<Integer> layouts) { AddressIndexMap map, List<Integer> layouts) {
if (layouts == null) { if (layouts == null) {
@@ -105,20 +105,20 @@ class PointMarkerSet extends MarkerSetImpl {
Iterator<Integer> it = layouts.iterator(); Iterator<Integer> it = layouts.iterator();
while (it.hasNext()) { while (it.hasNext()) {
int i = it.next().intValue(); 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); 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) { if (markerDescriptor == null) {
return image; return image;
} }
Address address = pixmap.getLayoutAddress(i); Address address = pixMap.getLayoutAddress(i);
MarkerLocation loc = new MarkerLocation(this, program, address, 0, yStart); MarkerLocation loc = new MarkerLocation(this, program, address, 0, yStart);
ImageIcon icon = markerDescriptor.getIcon(loc); ImageIcon icon = markerDescriptor.getIcon(loc);
if (icon != null) { if (icon != null) {
@@ -28,7 +28,7 @@ import docking.action.DockingActionIf;
import generic.theme.GColor; import generic.theme.GColor;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.services.GoToService; 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.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; 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. * 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. * 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 static final Color DEFAULT_COLOR = new GColor("color.bg.plugin.overview.defalt");
private OverviewColorService service; private OverviewColorService service;
private Color[] colors = new Color[0]; private Color[] colors = new Color[0];
private final SwingUpdateManager refreshUpdater = private final SwingUpdateManager refreshUpdater =
new SwingUpdateManager(100, 15000, () -> doRefresh()); new SwingUpdateManager(100, 15000, () -> doRefresh());
private AddressIndexMap map; private AddressIndexMap addressMap;
private Navigatable navigatable; private Navigatable navigatable;
private PluginTool tool; private PluginTool tool;
private List<DockingActionIf> actions; private List<DockingActionIf> actions;
@@ -76,6 +76,12 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
actions = service.getActions(); actions = service.getActions();
} }
@Override
public void dispose() {
service = null;
ToolTipManager.sharedInstance().unregisterComponent(this);
}
/** /**
* Installs actions for this component * Installs actions for this component
*/ */
@@ -154,10 +160,10 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
} }
private void doRefresh() { private void doRefresh() {
if (map == null) { if (addressMap == null) {
return; return;
} }
BigInteger indexCount = map.getIndexCount(); BigInteger indexCount = addressMap.getIndexCount();
if (indexCount.equals(BigInteger.ZERO)) { if (indexCount.equals(BigInteger.ZERO)) {
Arrays.fill(colors, DEFAULT_COLOR); Arrays.fill(colors, DEFAULT_COLOR);
repaint(); repaint();
@@ -168,7 +174,7 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
for (int i = 0; i < colors.length; i++) { for (int i = 0; i < colors.length; i++) {
if (colors[i] == null) { if (colors[i] == null) {
BigInteger index = indexCount.multiply(BigInteger.valueOf(i)).divide(bigTotal); BigInteger index = indexCount.multiply(BigInteger.valueOf(i)).divide(bigTotal);
Address address = map.getAddress(index); Address address = addressMap.getAddress(index);
colors[i] = service.getColor(address); colors[i] = service.getColor(address);
} }
} }
@@ -178,17 +184,17 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
private Address getAddress(int pixelIndex) { private Address getAddress(int pixelIndex) {
BigInteger bigHeight = BigInteger.valueOf(getOverviewPixelCount()); BigInteger bigHeight = BigInteger.valueOf(getOverviewPixelCount());
BigInteger bigPixelIndex = BigInteger.valueOf(pixelIndex); BigInteger bigPixelIndex = BigInteger.valueOf(pixelIndex);
BigInteger bigIndex = map.getIndexCount().multiply(bigPixelIndex).divide(bigHeight); BigInteger bigIndex = addressMap.getIndexCount().multiply(bigPixelIndex).divide(bigHeight);
return map.getAddress(bigIndex); return addressMap.getAddress(bigIndex);
} }
private int getPixelIndex(Address address) { private int getPixelIndex(Address address) {
BigInteger addressIndex = map.getIndex(address); BigInteger addressIndex = addressMap.getIndex(address);
if (addressIndex == null) { if (addressIndex == null) {
return -1; return -1;
} }
BigInteger bigHeight = BigInteger.valueOf(getOverviewPixelCount()); BigInteger bigHeight = BigInteger.valueOf(getOverviewPixelCount());
BigInteger indexCount = map.getIndexCount(); BigInteger indexCount = addressMap.getIndexCount();
return addressIndex.multiply(bigHeight).divide(indexCount).intValue(); return addressIndex.multiply(bigHeight).divide(indexCount).intValue();
} }
@@ -198,10 +204,10 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
} }
@Override @Override
public void setProgram(Program program, AddressIndexMap map) { public void screenDataChanged(Program program, AddressIndexMap map) {
this.map = map; this.addressMap = map;
colors = new Color[getOverviewPixelCount()]; this.colors = new Color[getOverviewPixelCount()];
refreshUpdater.updateLater(); this.refreshUpdater.updateLater();
} }
@Override @Override
@@ -248,5 +254,4 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
public PluginTool getTool() { public PluginTool getTool() {
return tool; return tool;
} }
} }
@@ -92,5 +92,4 @@ public interface OverviewColorService extends ExtensionPoint {
* @return the current program used by the service. * @return the current program used by the service.
*/ */
public Program getProgram(); public Program getProgram();
} }
@@ -143,6 +143,8 @@ public class AddressTypeOverviewColorService
@Override @Override
public void setOverviewComponent(OverviewColorComponent component) { public void setOverviewComponent(OverviewColorComponent component) {
this.overviewComponent = component; this.overviewComponent = component;
this.overviewComponent.getAccessibleContext()
.setAccessibleName("Address Type Overview Component");
} }
/** /**
@@ -33,6 +33,7 @@ import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.services.CodeViewerService; import ghidra.app.services.CodeViewerService;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.util.AddressIndexMap; import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@@ -147,7 +148,10 @@ public class PrintingPlugin extends ProgramPlugin {
format = job.defaultPage(); 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 //Scale everything down if appropriate to fit on the page
double scaleAmount = double scaleAmount =
format.getImageableWidth() / lm.getPreferredViewSize().width; format.getImageableWidth() / lm.getPreferredViewSize().width;
@@ -316,7 +320,8 @@ public class PrintingPlugin extends ProgramPlugin {
private void printVisibleContent(TaskMonitor monitor, Date startDate, private void printVisibleContent(TaskMonitor monitor, Date startDate,
PrinterJob job, Book book, LayoutModel lm, double scaleAmount, PrinterJob job, Book book, LayoutModel lm, double scaleAmount,
int maxPageHeight) { int maxPageHeight) {
FieldPanel fp = cvService.getFieldPanel(); ListingPanel listingPanel = cvService.getListingPanel();
FieldPanel fp = listingPanel.getFieldPanel();
List<AnchoredLayout> visibleLayouts = fp.getVisibleLayouts(); List<AnchoredLayout> visibleLayouts = fp.getVisibleLayouts();
BigInteger startIndex = visibleLayouts.get(0).getIndex(); BigInteger startIndex = visibleLayouts.get(0).getIndex();
BigInteger endIndex = visibleLayouts.get(visibleLayouts.size() - 1).getIndex(); BigInteger endIndex = visibleLayouts.get(visibleLayouts.size() - 1).getIndex();
@@ -34,39 +34,48 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
/** /**
* Service provided by a plugin that shows the listing from a Program, i.e., a * Service provided by a plugin that shows the Code Viewer listing for a Program. The service
* Code Viewer. The service allows other plugins to add components and * allows other plugins to add components and actions local to the Code Viewer.
* actions local to the Code Viewer.
*
*
*/ */
@ServiceInfo(defaultProvider = CodeBrowserPlugin.class) @ServiceInfo(defaultProvider = CodeBrowserPlugin.class)
public interface CodeViewerService { public interface CodeViewerService {
/** /**
* Add a provider that shows an overview of the program. * 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 * @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. * Remove a provider that shows an overview of the program.
* @param overviewProvider provider to remove * @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 * Add a provider that shows markers in a program for the portion
* that is visible. * that is visible.
* @param marginProvider provider to add * @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 * Remove a provider that shows markers in a program for the portion
* that is visible. * that is visible.
* @param marginProvider provider to remove * @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. * 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. * Add a listener that is notified when a mouse button is pressed.
* @param listener * @param listener the listener
*/ */
public void addButtonPressedListener(ButtonPressedListener listener); public void addButtonPressedListener(ButtonPressedListener listener);
/** /**
* Remove the button pressed listener. * Remove the button pressed listener.
* @param listener * @param listener the listener
*/ */
public void removeButtonPressedListener(ButtonPressedListener listener); public void removeButtonPressedListener(ButtonPressedListener listener);
@@ -114,7 +123,8 @@ public interface CodeViewerService {
public void removeHighlightProvider(ListingHighlightProvider provider, Program program); 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. * @param listingPanel the panel to add.
*/ */
public void setListingPanel(ListingPanel listingPanel); public void setListingPanel(ListingPanel listingPanel);
@@ -127,11 +137,12 @@ public interface CodeViewerService {
/** /**
* Remove the given listing panel from the code viewer. * Remove the given listing panel from the code viewer.
* @param listingPanel the listing panel
*/ */
public void removeListingPanel(ListingPanel listingPanel); 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(); public AddressSetView getView();
@@ -145,15 +156,21 @@ public interface CodeViewerService {
public boolean goTo(ProgramLocation loc, boolean centerOnScreen); 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(); public FieldPanel getFieldPanel();
/** /**
* Returns the current address-index-map * {@return the current address index map.}
*/ */
public AddressIndexMap getAddressIndexMap(); public AddressIndexMap getAddressIndexMap();
/**
* {@return the format manager.}
*/
public FormatManager getFormatManager(); public FormatManager getFormatManager();
/** /**
@@ -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);
}
@@ -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);
}
@@ -18,6 +18,7 @@ package ghidra.app.services;
import java.awt.Color; import java.awt.Color;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.util.UniversalID;
/** /**
* Defines methods for working with a set of addresses that correspond to markers. * 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> { 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 * Add a marker at the address
* @param addr the address * @param addr the address
@@ -151,8 +165,9 @@ public interface MarkerSet extends Comparable<MarkerSet> {
public void setMarkerColor(Color color); public void setMarkerColor(Color color);
/** /**
* Set the marker manager listener to use for user interaction * Set the marker set descriptor. This allows clients to control tooltip text and program
* with markers owned by this manager. * location generation for individual markers on-the-fly.
*
* @param markerDescriptor the descriptor * @param markerDescriptor the descriptor
*/ */
public void setMarkerDescriptor(MarkerDescriptor markerDescriptor); public void setMarkerDescriptor(MarkerDescriptor markerDescriptor);
@@ -965,7 +965,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* A library that has not been processed by the loader yet * A library that has not been processed by the loader yet
* *
* @param name The name of the library * @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) * loaded)
* @param temporary True if the library is temporary and should be discarded prior to returning * @param temporary True if the library is temporary and should be discarded prior to returning
* from the load * from the load
@@ -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();
}
@@ -22,28 +22,32 @@ import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
/** /**
* Interface implemented by classes that provide overview components to the right side of the * An overview component that will be placed to the right side of the listing.
* listing.
*/ */
public interface OverviewProvider { public interface ListingOverviewProvider {
/** /**
* Returns the component to display in the right margin of the listing. * Returns the component to display in the right margin of the listing.
* @return the component * @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 program the program to use
* @param map the address-index map 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 * Set the component provider that this overview navigates
* *
* @param navigatable the navigatable provider * @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();
} }
@@ -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);
}
@@ -394,8 +394,8 @@ public class ListingCodeComparisonView
public Object getContextObjectForMarginPanels(ListingPanel panel, MouseEvent event) { public Object getContextObjectForMarginPanels(ListingPanel panel, MouseEvent event) {
Object source = event.getSource(); Object source = event.getSource();
// Is event source a marker margin provider on the left side of the listing? // Is event source a marker margin provider on the left side of the listing?
List<MarginProvider> marginProviders = panel.getMarginProviders(); List<ListingMarginProvider> marginProviders = panel.getMarginProviders();
for (MarginProvider marginProvider : marginProviders) { for (ListingMarginProvider marginProvider : marginProviders) {
JComponent c = marginProvider.getComponent(); JComponent c = marginProvider.getComponent();
if (c == source) { if (c == source) {
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY()); 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? // Is event source an overview provider on the right side of the listing?
List<OverviewProvider> overviewProviders = panel.getOverviewProviders(); List<ListingOverviewProvider> overviewProviders = panel.getOverviewProviders();
for (OverviewProvider overviewProvider : overviewProviders) { for (ListingOverviewProvider overviewProvider : overviewProviders) {
JComponent c = overviewProvider.getComponent(); JComponent c = overviewProvider.getComponent();
if (c == source) { if (c == source) {
return source; // Return the overview provider that was clicked. return source; // Return the overview provider that was clicked.
@@ -623,8 +623,8 @@ public class ListingCodeComparisonView
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) { private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
Object source = event.getSource(); Object source = event.getSource();
List<MarginProvider> marginProvidersForLP = lp.getMarginProviders(); List<ListingMarginProvider> marginProvidersForLP = lp.getMarginProviders();
for (MarginProvider marginProvider : marginProvidersForLP) { for (ListingMarginProvider marginProvider : marginProvidersForLP) {
JComponent c = marginProvider.getComponent(); JComponent c = marginProvider.getComponent();
if (c == source) { if (c == source) {
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY()); MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
@@ -634,8 +634,8 @@ public class ListingCodeComparisonView
return source; return source;
} }
} }
List<OverviewProvider> overviewProvidersForLP = lp.getOverviewProviders(); List<ListingOverviewProvider> overviewProvidersForLP = lp.getOverviewProviders();
for (OverviewProvider overviewProvider : overviewProvidersForLP) { for (ListingOverviewProvider overviewProvider : overviewProvidersForLP) {
JComponent c = overviewProvider.getComponent(); JComponent c = overviewProvider.getComponent();
if (c == source) { if (c == source) {
return source; return source;
@@ -18,6 +18,7 @@ package ghidra.features.base.codecompare.listing;
import static ghidra.GhidraOptions.*; import static ghidra.GhidraOptions.*;
import java.awt.Color; import java.awt.Color;
import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
@@ -94,9 +95,12 @@ public class ListingDisplay implements ListingDiffChangeListener {
private void createMarkerManager(String owner) { private void createMarkerManager(String owner) {
markerManager = new ListingDisplayMarkerManager(tool, owner); markerManager = new ListingDisplayMarkerManager(tool, owner);
markerManager.addChangeListener(e -> listingPanel.repaint()); 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); listingPanel.addMarginProvider(marginProvider);
OverviewProvider overviewProvider = markerManager.getOverviewProvider(); ListingOverviewProvider overviewProvider = markerManager.createOverviewProvider();
listingPanel.addOverviewProvider(overviewProvider); listingPanel.addOverviewProvider(overviewProvider);
} }
@@ -171,7 +175,12 @@ public class ListingDisplay implements ListingDiffChangeListener {
markerManager.clearAll(); markerManager.clearAll();
listingPanel.setView(view); listingPanel.setView(view);
AddressIndexMap indexMap = listingPanel.getAddressIndexMap(); AddressIndexMap indexMap = listingPanel.getAddressIndexMap();
markerManager.getOverviewProvider().setProgram(program, indexMap);
List<ListingOverviewProvider> providers = listingPanel.getOverviewProviders();
for (ListingOverviewProvider provider : providers) {
provider.screenDataChanged(program, indexMap);
}
listingPanel.setBackgroundColorModel( listingPanel.setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManager, program, indexMap)); new MarkerServiceBackgroundColorModel(markerManager, program, indexMap));
setUpAreaMarkerSets(program, name); setUpAreaMarkerSets(program, name);
@@ -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; return markers;
} }
@@ -64,7 +64,7 @@ import ghidra.app.util.viewer.field.FieldFactory;
import ghidra.app.util.viewer.format.FieldFormatModel; import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.format.FormatManager; import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel; 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.docking.settings.SettingsDefinition;
import ghidra.framework.ToolUtils; import ghidra.framework.ToolUtils;
import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.Plugin;
@@ -1247,9 +1247,9 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
CodeBrowserPlugin plugin = getPlugin(tool, CodeBrowserPlugin.class); CodeBrowserPlugin plugin = getPlugin(tool, CodeBrowserPlugin.class);
ListingPanel listingPanel = plugin.getListingPanel(); ListingPanel listingPanel = plugin.getListingPanel();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<MarginProvider> list = List<ListingMarginProvider> list =
(List<MarginProvider>) getInstanceField("marginProviders", listingPanel); (List<ListingMarginProvider>) getInstanceField("marginProviders", listingPanel);
for (MarginProvider marginProvider : list) { for (ListingMarginProvider marginProvider : list) {
listingPanel.removeMarginProvider(marginProvider); listingPanel.removeMarginProvider(marginProvider);
} }
}); });
@@ -669,6 +669,8 @@ public class CommentsPluginTest extends AbstractGhidraHeadedIntegrationTest {
addReference(0x1001020, 0x1008294, RefType.DATA); addReference(0x1001020, 0x1008294, RefType.DATA);
addReference(0x1001030, 0x1008394, RefType.DATA); addReference(0x1001030, 0x1008394, RefType.DATA);
waitForProgram(program);
Address srcAddr = addr(0x01006990); Address srcAddr = addr(0x01006990);
CodeUnit cu = program.getListing().getCodeUnitAt(srcAddr); CodeUnit cu = program.getListing().getCodeUnitAt(srcAddr);
@@ -44,17 +44,15 @@ import ghidra.app.plugin.core.clear.ClearPlugin;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.highlight.SetHighlightPlugin; import ghidra.app.plugin.core.highlight.SetHighlightPlugin;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
import ghidra.app.util.viewer.util.AddressIndexMap; import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.framework.cmd.CompoundCmd; import ghidra.framework.cmd.CompoundCmd;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.BookmarkType; import ghidra.program.model.listing.BookmarkType;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.test.*; import ghidra.test.*;
import ghidra.util.Msg; import ghidra.util.datastruct.WeakSet;
public class MarkerTest extends AbstractGhidraHeadedIntegrationTest { public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
@@ -162,8 +160,13 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
setSelection(fp, sel); setSelection(fp, sel);
MarkerManager mm = (MarkerManager) markerService; 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); waitForProgram(program);
@@ -224,8 +227,13 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(addrSet.contains(addr("0xf000131b"))); assertTrue(addrSet.contains(addr("0xf000131b")));
MarkerManager mm = (MarkerManager) markerService; 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); waitForProgram(program);
@@ -278,7 +286,12 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
MouseEvent dummyEvent = MouseEvent dummyEvent =
new MouseEvent(cb.getFieldPanel(), (int) time, time, 0, x, y, 1, false); new MouseEvent(cb.getFieldPanel(), (int) time, time, 0, x, y, 1, false);
MarkerManager mm = (MarkerManager) markerService; 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( assertEquals(
"<html><font size=\"4\">Cursor<BR>Note [TEST]: comment<BR>Changes: Unsaved<BR>", "<html><font size=\"4\">Cursor<BR>Note [TEST]: comment<BR>Changes: Unsaved<BR>",
tooltip); tooltip);
@@ -316,11 +329,11 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
MouseEvent dummyEvent = new MouseEvent(cb.getFieldPanel(), (int) System.currentTimeMillis(), MouseEvent dummyEvent = new MouseEvent(cb.getFieldPanel(), (int) System.currentTimeMillis(),
System.currentTimeMillis(), 0, x, y, 1, false); System.currentTimeMillis(), 0, x, y, 1, false);
// debug @SuppressWarnings("unchecked")
ProgramLocation location = cb.getListingPanel().getProgramLocation(dummyEvent.getPoint()); WeakSet<MarkerMarginProvider> marginProviders =
Msg.debug(this, "location for point: " + location + "; at " + location.getAddress()); (WeakSet<MarkerMarginProvider>) getInstanceField("marginProviders", mm);
MarkerMarginProvider provider = marginProviders.iterator().next();
String tooltip = runSwing(() -> mm.generateToolTip(dummyEvent)); String tooltip = runSwing(() -> provider.generateToolTip(dummyEvent));
assertNotNull("No tooltip for field: " + cb.getCurrentField() + "\n\tat address: " + assertNotNull("No tooltip for field: " + cb.getCurrentField() + "\n\tat address: " +
cb.getCurrentAddress(), tooltip); cb.getCurrentAddress(), tooltip);
@@ -83,9 +83,9 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener {
} }
@Override @Override
protected void cleanup() { protected void dispose() {
fidFileManager.removeChangeListener(this); fidFileManager.removeChangeListener(this);
super.cleanup(); super.dispose();
} }
/** /**
@@ -133,7 +133,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
private DiffController diffControl; private DiffController diffControl;
private Program primaryProgram; private Program primaryProgram;
private Program secondaryDiffProgram; private Program secondaryDiffProgram;
private AddressFactory p2AddressFactory;
private ProgramDiffDetails diffDetails; private ProgramDiffDetails diffDetails;
private ProgramSelection p2DiffHighlight; private ProgramSelection p2DiffHighlight;
@@ -286,33 +285,39 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (primaryProgram != null && !showingSecondProgram) { if (primaryProgram != null && !showingSecondProgram) {
return; return;
} }
p1ViewAddrSet = p1AddressSet; p1ViewAddrSet = p1AddressSet;
if (!showingSecondProgram) {
return;
}
if (showingSecondProgram) { ProgramSelection previousP1Selection = currentSelection;
ProgramSelection previousP1Selection = currentSelection; ProgramSelection previousP2DiffHighlight = p2DiffHighlight;
ProgramSelection previousP2DiffHighlight = p2DiffHighlight; ProgramSelection previousP2Selection = p2Selection;
ProgramSelection previousP2Selection = p2Selection;
AddressSet p2ViewAddrSet = AddressSet p2ViewAddrSet =
DiffUtility.getCompatibleAddressSet(p1ViewAddrSet, secondaryDiffProgram); DiffUtility.getCompatibleAddressSet(p1ViewAddrSet, secondaryDiffProgram);
diffListingPanel.setView(p2ViewAddrSet); diffListingPanel.setView(p2ViewAddrSet);
FieldPanel fp = diffListingPanel.getFieldPanel(); FieldPanel fp = diffListingPanel.getFieldPanel();
AddressSet p1AddressSetAsP2 = AddressSet p1AddressSetAsP2 =
DiffUtility.getCompatibleAddressSet(p1AddressSet, secondaryDiffProgram); DiffUtility.getCompatibleAddressSet(p1AddressSet, secondaryDiffProgram);
AddressIndexMap p2IndexMap = new AddressIndexMap(p1AddressSetAsP2); AddressIndexMap p2IndexMap = new AddressIndexMap(p1AddressSetAsP2);
markerManager.getOverviewProvider().setProgram(secondaryDiffProgram, p2IndexMap); List<ListingOverviewProvider> providers = diffListingPanel.getOverviewProviders();
fp.setBackgroundColorModel(new MarkerServiceBackgroundColorModel(markerManager, for (ListingOverviewProvider provider : providers) {
secondaryDiffProgram, p2IndexMap)); provider.screenDataChanged(secondaryDiffProgram, p2IndexMap);
}
currentSelection = previousP1Selection; fp.setBackgroundColorModel(new MarkerServiceBackgroundColorModel(markerManager,
p2DiffHighlight = previousP2DiffHighlight; secondaryDiffProgram, p2IndexMap));
p2Selection = previousP2Selection; currentSelection = previousP1Selection;
setProgram2Selection(p2Selection); p2DiffHighlight = previousP2DiffHighlight;
if (p2DiffHighlight != null) {
setDiffHighlight(p2DiffHighlight); p2Selection = previousP2Selection;
} setProgram2Selection(p2Selection);
if (p2DiffHighlight != null) {
setDiffHighlight(p2DiffHighlight);
} }
} }
@@ -556,9 +561,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
return; return;
} }
if (currentSelection == null) { if (currentSelection == null) {
AddressFactory p1AddressFactory = currentSelection = new ProgramSelection();
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
currentSelection = new ProgramSelection(p1AddressFactory);
} }
actionManager.setP1SelectToP2ActionEnabled( actionManager.setP1SelectToP2ActionEnabled(
secondaryDiffProgram != null && !currentSelection.isEmpty()); secondaryDiffProgram != null && !currentSelection.isEmpty());
@@ -605,8 +608,13 @@ public class ProgramDiffPlugin extends ProgramPlugin
diffListingPanel.setProgramLocationListener(this); diffListingPanel.setProgramLocationListener(this);
diffListingPanel.setProgramSelectionListener(this); diffListingPanel.setProgramSelectionListener(this);
diffListingPanel.getFieldPanel().addFieldMouseListener(new MyFieldMouseListener()); 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()); diffNavigatable = new DiffNavigatable(this, codeViewerService.getNavigatable());
diffFieldNavigator = new FieldNavigator(diffServiceProvider, diffNavigatable); diffFieldNavigator = new FieldNavigator(diffServiceProvider, diffNavigatable);
diffListingPanel.addButtonPressedListener(diffFieldNavigator); diffListingPanel.addButtonPressedListener(diffFieldNavigator);
@@ -678,9 +686,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
ProgramSelection getCurrentSelection() { ProgramSelection getCurrentSelection() {
if (currentSelection == null) { if (currentSelection == null) {
AddressFactory p1AddressFactory = currentSelection = new ProgramSelection();
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
currentSelection = new ProgramSelection(p1AddressFactory);
} }
return currentSelection; return currentSelection;
} }
@@ -753,10 +759,8 @@ public class ProgramDiffPlugin extends ProgramPlugin
// Make sure that the Diff selection is to the code unit boundary. // Make sure that the Diff selection is to the code unit boundary.
ProgramSelection p2CodeUnitSelection = ProgramSelection p2CodeUnitSelection =
new ProgramSelection(DiffUtility.getCodeUnitSet(newP2Selection, secondaryDiffProgram)); new ProgramSelection(DiffUtility.getCodeUnitSet(newP2Selection, secondaryDiffProgram));
AddressFactory p1AddressFactory =
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
ProgramSelection intersection = ProgramSelection intersection =
new ProgramSelection(p2AddressFactory, p2CodeUnitSelection.intersect(p2DiffHighlight)); new ProgramSelection(p2CodeUnitSelection.intersect(p2DiffHighlight));
p2Selection = intersection; p2Selection = intersection;
AddressSet p2SelectionAsP1Set = AddressSet p2SelectionAsP1Set =
@@ -767,8 +771,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
// of the MultiListing Layout being used. // of the MultiListing Layout being used.
/////////////////////////////////////////// ///////////////////////////////////////////
ProgramSelection p2SelectionAsP1 = ProgramSelection p2SelectionAsP1 = new ProgramSelection(p2SelectionAsP1Set);
new ProgramSelection(p1AddressFactory, p2SelectionAsP1Set);
runSwing(() -> { runSwing(() -> {
MarkerSet selectionMarkers = getSelectionMarkers(); MarkerSet selectionMarkers = getSelectionMarkers();
selectionMarkers.clearAll(); selectionMarkers.clearAll();
@@ -783,7 +786,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
actionManager.setP1SelectToP2ActionEnabled( actionManager.setP1SelectToP2ActionEnabled(
(secondaryDiffProgram != null) && !currentSelection.isEmpty()); (secondaryDiffProgram != null) && !currentSelection.isEmpty());
firePluginEvent(new ProgramSelectionPluginEvent(this.getName(), 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); AddressSet p2DiffSet = DiffUtility.getCompatibleAddressSet(p1DiffSet, secondaryDiffProgram);
ProgramSelection p2DiffSelection = new ProgramSelection(p2AddressFactory, p2DiffSet); ProgramSelection p2DiffSelection = new ProgramSelection(p2DiffSet);
p2DiffHighlight = p2DiffSelection; p2DiffHighlight = p2DiffSelection;
AddressSet p2DiffSetAsP1 = DiffUtility.getCompatibleAddressSet(p2DiffSet, primaryProgram); AddressSet p2DiffSetAsP1 = DiffUtility.getCompatibleAddressSet(p2DiffSet, primaryProgram);
@@ -918,7 +921,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (diffControl.hasNext()) { if (diffControl.hasNext()) {
diffControl.next(); diffControl.next();
} }
setProgram2Selection(new ProgramSelection(p2AddressFactory, getDiffHighlightBlock())); setProgram2Selection(new ProgramSelection(getDiffHighlightBlock()));
} }
void previousDiff() { void previousDiff() {
@@ -926,7 +929,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (diffControl.hasPrevious()) { if (diffControl.hasPrevious()) {
diffControl.previous(); diffControl.previous();
} }
setProgram2Selection(new ProgramSelection(p2AddressFactory, getDiffHighlightBlock())); setProgram2Selection(new ProgramSelection(getDiffHighlightBlock()));
} }
private void clearDiff() { private void clearDiff() {
@@ -992,8 +995,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
AddressSet p1IgnoreSet = AddressSet p1IgnoreSet =
DiffUtility.getCompatibleAddressSet(p2IgnoreSet, primaryProgram); DiffUtility.getCompatibleAddressSet(p2IgnoreSet, primaryProgram);
diffControl.ignore(p1IgnoreSet, null); diffControl.ignore(p1IgnoreSet, null);
p2DiffHighlight = p2DiffHighlight = new ProgramSelection(p2DiffHighlight.subtract(p2IgnoreSet));
new ProgramSelection(p2AddressFactory, p2DiffHighlight.subtract(p2IgnoreSet));
adjustDiffDisplay(); adjustDiffDisplay();
@@ -1047,7 +1049,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
} }
AddressSetView p2ViewAddrSet = AddressSetView p2ViewAddrSet =
DiffUtility.getCompatibleAddressSet(adjustedView, secondaryDiffProgram); DiffUtility.getCompatibleAddressSet(adjustedView, secondaryDiffProgram);
setProgram2Selection(new ProgramSelection(p2AddressFactory, p2ViewAddrSet)); setProgram2Selection(new ProgramSelection(p2ViewAddrSet));
} }
finally { finally {
diffListingPanel.getFieldPanel().requestFocus(); diffListingPanel.getFieldPanel().requestFocus();
@@ -1078,7 +1080,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
primaryProgram.removeListener(this); primaryProgram.removeListener(this);
primaryProgram = null; primaryProgram = null;
secondaryDiffProgram = null; secondaryDiffProgram = null;
p2AddressFactory = null;
} }
sameProgramContext = false; sameProgramContext = false;
updatePgm2Enablement(); updatePgm2Enablement();
@@ -1221,7 +1222,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (!currentSelection.isEmpty()) { if (!currentSelection.isEmpty()) {
AddressSet p2SelectionSet = AddressSet p2SelectionSet =
DiffUtility.getCompatibleAddressSet(currentSelection, secondaryDiffProgram); DiffUtility.getCompatibleAddressSet(currentSelection, secondaryDiffProgram);
setProgram2Selection(new ProgramSelection(p2AddressFactory, setProgram2Selection(new ProgramSelection(
DiffUtility.getCodeUnitSet(p2SelectionSet, secondaryDiffProgram))); DiffUtility.getCodeUnitSet(p2SelectionSet, secondaryDiffProgram)));
} }
if (p2Selection.isEmpty()) { if (p2Selection.isEmpty()) {
@@ -1630,7 +1631,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
primaryProgram = currentProgram; primaryProgram = currentProgram;
secondaryDiffProgram = newProgram; secondaryDiffProgram = newProgram;
p2AddressFactory = secondaryDiffProgram.getAddressFactory();
applyFilter = applySettingsMgr.getDefaultApplyFilter(); applyFilter = applySettingsMgr.getDefaultApplyFilter();
diffDetails = new ProgramDiffDetails(primaryProgram, secondaryDiffProgram); diffDetails = new ProgramDiffDetails(primaryProgram, secondaryDiffProgram);
primaryProgram.addListener(this); 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. return; // Selection is unchanged so do nothing.
} }
MarkerSet selectionMarkers = getSelectionMarkers(); MarkerSet selectionMarkers = getSelectionMarkers();
selectionMarkers.clearAll(); selectionMarkers.clearAll();
programSelectionChanged(new ProgramSelection(p2AddressFactory, set), programSelectionChanged(new ProgramSelection(set), EventTrigger.GUI_ACTION);
EventTrigger.GUI_ACTION);
updatePgm2Enablement(); updatePgm2Enablement();
} }
} }
@@ -762,7 +762,8 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
} }
private boolean isCursorOnScreen(CodeViewerService service) { private boolean isCursorOnScreen(CodeViewerService service) {
FieldPanel fieldPanel = service.getFieldPanel(); ListingPanel listingPanel = service.getListingPanel();
FieldPanel fieldPanel = listingPanel.getFieldPanel();
int cursorOffset = fieldPanel.getCursorOffset(); int cursorOffset = fieldPanel.getCursorOffset();
return cursorOffset >= 0; // negative offset means offscreen return cursorOffset >= 0; // negative offset means offscreen
} }
@@ -779,7 +780,10 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
if (service == null) { if (service == null) {
return 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(); AddressIndexMap addressIndexMap = service.getListingPanel().getAddressIndexMap();
AddressSet addressSet = addressIndexMap.getAddressSet(selection); AddressSet addressSet = addressIndexMap.getAddressSet(selection);
return addressSet; return addressSet;
@@ -788,13 +792,11 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
/** /**
* Sets the address set to be the selection in the tool. * Sets the address set to be the selection in the tool.
* *
* @param tool * @param tool the tool
* the tool * @param addresses the addressSet to use for the selection
* @param set
* the addressSet to use for the selection
*/ */
private void setSelectionInTool(PluginTool tool, AddressSetView addressSet) { private void setSelectionInTool(PluginTool tool, AddressSetView addresses) {
ProgramSelection programSelection = new ProgramSelection(addressSet); ProgramSelection programSelection = new ProgramSelection(addresses);
CodeViewerService service = tool.getService(CodeViewerService.class); CodeViewerService service = tool.getService(CodeViewerService.class);
if (service == null) { if (service == null) {
return; return;
@@ -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() { protected void cleanup() {
if (!disposed) { if (!disposed) {
Throwable thr = null; Throwable thr = null;
@@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.core.debug.gui.control; 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.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -26,6 +26,7 @@ import org.junit.Test;
import db.Transaction; import db.Transaction;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import ghidra.app.context.ProgramLocationActionContext; 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.AbstractGhidraHeadedDebuggerIntegrationTest;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin; import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
@@ -50,6 +51,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
@Before @Before
public void setUpMethodActionsTest() throws Exception { public void setUpMethodActionsTest() throws Exception {
addPlugin(tool, CodeBrowserPlugin.class);
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class); listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class); mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
methodsPlugin = addPlugin(tool, DebuggerMethodActionsPlugin.class); methodsPlugin = addPlugin(tool, DebuggerMethodActionsPlugin.class);
@@ -31,7 +31,7 @@ import ghidra.app.util.viewer.field.*;
import ghidra.app.util.viewer.format.FieldFormatModel; import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.format.FormatManager; import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel; 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.Options;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@@ -138,11 +138,11 @@ public class BlockModelScreenShots extends GhidraScreenShotGenerator {
final ListingPanel lp = cb.getListingPanel(); final ListingPanel lp = cb.getListingPanel();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final List<MarginProvider> list = final List<ListingMarginProvider> list =
new ArrayList<>((List<MarginProvider>) getInstanceField("marginProviders", lp)); new ArrayList<>((List<ListingMarginProvider>) getInstanceField("marginProviders", lp));
runSwing(() -> { runSwing(() -> {
invokeInstanceMethod("buildPanels", lp); invokeInstanceMethod("buildPanels", lp);
for (MarginProvider marginProvider : list) { for (ListingMarginProvider marginProvider : list) {
lp.removeMarginProvider(marginProvider); lp.removeMarginProvider(marginProvider);
} }
}); });
@@ -39,7 +39,7 @@ import ghidra.app.plugin.core.datamgr.DataTypesProvider;
import ghidra.app.plugin.core.programtree.ViewManagerComponentProvider; import ghidra.app.plugin.core.programtree.ViewManagerComponentProvider;
import ghidra.app.util.viewer.field.*; import ghidra.app.util.viewer.field.*;
import ghidra.app.util.viewer.listingpanel.ListingPanel; 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.data.*;
import ghidra.program.model.listing.CommentType; import ghidra.program.model.listing.CommentType;
@@ -265,10 +265,10 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
public void testCaptureMarkerPopup() { public void testCaptureMarkerPopup() {
setToolSize(1400, 1200); setToolSize(1400, 1200);
ListingPanel listingPanel = plugin.getListingPanel(); ListingPanel listingPanel = plugin.getListingPanel();
List<OverviewProvider> overviewProviders = listingPanel.getOverviewProviders(); List<ListingOverviewProvider> overviewProviders = listingPanel.getOverviewProviders();
assertEquals(1, overviewProviders.size()); assertEquals(1, overviewProviders.size());
OverviewProvider provider = overviewProviders.get(0); ListingOverviewProvider provider = overviewProviders.get(0);
rightClick(provider.getComponent(), 1, 1); rightClick(provider.getComponent(), 1, 1);
captureMenu(); captureMenu();