GP-1625: Improve consistency of DebuggerMemoryBytesProvider

This commit is contained in:
Dan 2024-03-08 12:36:17 -05:00
parent 7d189001d6
commit 8e3f97056b
9 changed files with 131 additions and 29 deletions

View file

@ -19,6 +19,7 @@ import static ghidra.app.plugin.core.debug.gui.DebuggerResources.ICON_REGISTER_M
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -26,8 +27,7 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.swing.JLabel; import javax.swing.*;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
@ -62,6 +62,7 @@ import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.plugin.core.marker.MarkerMarginProvider; import ghidra.app.plugin.core.marker.MarkerMarginProvider;
import ghidra.app.plugin.core.marker.MarkerOverviewProvider; import ghidra.app.plugin.core.marker.MarkerOverviewProvider;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
import ghidra.app.services.DebuggerListingService.LocationTrackingSpecChangeListener; import ghidra.app.services.DebuggerListingService.LocationTrackingSpecChangeListener;
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;
@ -293,7 +294,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
private DebuggerStaticMappingService mappingService; private DebuggerStaticMappingService mappingService;
@AutoServiceConsumed @AutoServiceConsumed
private DebuggerConsoleService consoleService; private DebuggerConsoleService consoleService;
@AutoServiceConsumed //@AutoServiceConsumed via method
private DebuggerControlService controlService; private DebuggerControlService controlService;
@AutoServiceConsumed @AutoServiceConsumed
private ProgramManager programManager; private ProgramManager programManager;
@ -354,6 +355,12 @@ public class DebuggerListingProvider extends CodeViewerProvider {
protected final ForStaticSyncMappingChangeListener mappingChangeListener = protected final ForStaticSyncMappingChangeListener mappingChangeListener =
new ForStaticSyncMappingChangeListener(); new ForStaticSyncMappingChangeListener();
private final ControlModeChangeListener controlModeChangeListener = (trace, mode) -> {
if (trace == current.getTrace()) {
// for Paste action
contextChanged();
}
};
protected final boolean isMainListing; protected final boolean isMainListing;
@ -597,6 +604,17 @@ public class DebuggerListingProvider extends CodeViewerProvider {
} }
} }
@AutoServiceConsumed
private void setControlService(DebuggerControlService controlService) {
if (this.controlService != null) {
this.controlService.removeModeChangeListener(controlModeChangeListener);
}
this.controlService = controlService;
if (this.controlService != null) {
this.controlService.addModeChangeListener(controlModeChangeListener);
}
}
@AutoServiceConsumed @AutoServiceConsumed
private void setConsoleService(DebuggerConsoleService consoleService) { private void setConsoleService(DebuggerConsoleService consoleService) {
if (consoleService != null) { if (consoleService != null) {
@ -725,6 +743,14 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return DateUtils.formatDateTimestamp(new Date(snapshot.getRealTime())); return DateUtils.formatDateTimestamp(new Date(snapshot.getRealTime()));
} }
@Override
public Icon getIcon() {
if (isMainListing()) {
return getBaseIcon();
}
return super.getIcon();
}
@Override @Override
protected ListingActionContext newListingActionContext() { protected ListingActionContext newListingActionContext() {
return new DebuggerListingActionContext(this); return new DebuggerListingActionContext(this);
@ -740,6 +766,21 @@ public class DebuggerListingProvider extends CodeViewerProvider {
} }
return context.getComponentProvider() == componentProvider; return context.getComponentProvider() == componentProvider;
} }
@Override
public boolean canPaste(DataFlavor[] availableFlavors) {
if (controlService == null) {
return false;
}
Trace trace = current.getTrace();
if (trace == null) {
return false;
}
if (!controlService.getCurrentMode(trace).canEdit(current)) {
return false;
}
return super.canPaste(availableFlavors);
}
}; };
} }

View file

@ -92,7 +92,7 @@ public class DebuggerMemoryBytesPlugin
private void createActions() { private void createActions() {
actionNewMemory = NewMemoryAction.builder(this) actionNewMemory = NewMemoryAction.builder(this)
.enabled(true) .enabled(true)
.onAction(c -> createNewDisconnectedProvider()) .onAction(c -> connectedProvider.cloneWindow())
.buildAndInstall(tool); .buildAndInstall(tool);
} }

View file

@ -16,14 +16,14 @@
package ghidra.app.plugin.core.debug.gui.memory; package ghidra.app.plugin.core.debug.gui.memory;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*; import java.util.*;
import javax.swing.JLabel; import javax.swing.*;
import javax.swing.JPanel;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -40,6 +40,8 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAc
import ghidra.app.plugin.core.debug.gui.action.*; import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec; import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
import ghidra.app.plugin.core.format.ByteBlock; import ghidra.app.plugin.core.format.ByteBlock;
import ghidra.app.services.DebuggerControlService;
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
import ghidra.app.services.DebuggerTraceManagerService; import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.action.GoToInput; import ghidra.debug.api.action.GoToInput;
import ghidra.debug.api.action.LocationTrackingSpec; import ghidra.debug.api.action.LocationTrackingSpec;
@ -168,6 +170,8 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
@AutoServiceConsumed @AutoServiceConsumed
private DebuggerTraceManagerService traceManager; private DebuggerTraceManagerService traceManager;
//@AutoServiceConsumed via method
private DebuggerControlService controlService;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring; private final AutoService.Wiring autoServiceWiring;
@ -191,6 +195,12 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
// TODO: followsCurrentSnap? // TODO: followsCurrentSnap?
private final ListenerForChanges listenerForChanges = new ListenerForChanges(); private final ListenerForChanges listenerForChanges = new ListenerForChanges();
private final ControlModeChangeListener controlModeChangeListener = (trace, mode) -> {
if (trace == getCurrent().getTrace()) {
// for Paste action
contextChanged();
}
};
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE; DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
private DebuggerCoordinates previous = DebuggerCoordinates.NOWHERE; private DebuggerCoordinates previous = DebuggerCoordinates.NOWHERE;
@ -220,6 +230,13 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
readsMemTrait.goToCoordinates(current); readsMemTrait.goToCoordinates(current);
locationLabel.goToCoordinates(current); locationLabel.goToCoordinates(current);
if (isConnected) {
setTitle(DebuggerResources.TITLE_PROVIDER_MEMORY_BYTES);
}
else {
setTitle("[" + DebuggerResources.TITLE_PROVIDER_MEMORY_BYTES + "]");
}
updateTitle(); // Actually, the subtitle
setHelpLocation(DebuggerResources.HELP_PROVIDER_MEMORY_BYTES); setHelpLocation(DebuggerResources.HELP_PROVIDER_MEMORY_BYTES);
trackingLabel.addMouseListener(new MouseAdapter() { trackingLabel.addMouseListener(new MouseAdapter() {
@ -339,11 +356,50 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
setSubTitle(computeSubTitle()); setSubTitle(computeSubTitle());
} }
@Override
public Icon getIcon() {
if (isMainViewer()) {
return getBaseIcon();
}
return super.getIcon();
}
@Override @Override
protected ByteViewerActionContext newByteViewerActionContext() { protected ByteViewerActionContext newByteViewerActionContext() {
return new DebuggerMemoryBytesActionContext(this); return new DebuggerMemoryBytesActionContext(this);
} }
@Override
protected ByteViewerClipboardProvider newClipboardProvider() {
return new ByteViewerClipboardProvider(this, tool) {
@Override
public boolean canPaste(DataFlavor[] availableFlavors) {
if (controlService == null) {
return false;
}
Trace trace = current.getTrace();
if (trace == null) {
return false;
}
if (!controlService.getCurrentMode(trace).canEdit(current)) {
return false;
}
return super.canPaste(availableFlavors);
}
};
}
@AutoServiceConsumed
private void setControlService(DebuggerControlService controlService) {
if (this.controlService != null) {
this.controlService.removeModeChangeListener(controlModeChangeListener);
}
this.controlService = controlService;
if (this.controlService != null) {
this.controlService.addModeChangeListener(controlModeChangeListener);
}
}
protected void createActions() { protected void createActions() {
initTraits(); initTraits();

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.core.debug.gui.memory; package ghidra.app.plugin.core.debug.gui.memory;
import static ghidra.lifecycle.Unfinished.*; import static ghidra.lifecycle.Unfinished.TODO;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.awt.*; import java.awt.*;
@ -49,7 +49,7 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAc
import ghidra.app.plugin.core.debug.gui.action.*; import ghidra.app.plugin.core.debug.gui.action.*;
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.control.DebuggerControlServicePlugin; import ghidra.app.plugin.core.debug.service.control.DebuggerControlServicePlugin;
import ghidra.app.services.*; import ghidra.app.services.DebuggerControlService;
import ghidra.async.SwingExecutorService; import ghidra.async.SwingExecutorService;
import ghidra.debug.api.control.ControlMode; import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.model.TraceRecorder; import ghidra.debug.api.model.TraceRecorder;

View file

@ -79,7 +79,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
private static final String DIVIDER_LOCATION = "DividerLocation"; private static final String DIVIDER_LOCATION = "DividerLocation";
private ImageIcon navigatableIcon;
private Map<Program, ListingHighlightProvider> programHighlighterMap = new HashMap<>(); private Map<Program, ListingHighlightProvider> programHighlighterMap = new HashMap<>();
private ProgramHighlighterProvider highlighterAdapter; private ProgramHighlighterProvider highlighterAdapter;
@ -845,19 +844,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
return currentStringSelection; return currentStringSelection;
} }
@Override
public Icon getIcon() {
if (isConnected()) {
return super.getIcon();
}
if (navigatableIcon == null) {
Icon primaryIcon = super.getIcon();
navigatableIcon = NavigatableIconFactory.createSnapshotOverlayIcon(primaryIcon);
}
return navigatableIcon;
}
@Override @Override
public Icon getNavigatableIcon() { public Icon getNavigatableIcon() {
return getIcon(); return getIcon();

View file

@ -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.
@ -45,11 +44,11 @@ public abstract class NavigatableComponentProviderAdapter extends ComponentProvi
@Override @Override
public Icon getIcon() { public Icon getIcon() {
if (isConnected()) { if (isConnected()) {
return super.getIcon(); return getBaseIcon();
} }
if (navigatableIcon == null) { if (navigatableIcon == null) {
Icon primaryIcon = super.getIcon(); Icon primaryIcon = getBaseIcon();
navigatableIcon = NavigatableIconFactory.createSnapshotOverlayIcon(primaryIcon); navigatableIcon = NavigatableIconFactory.createSnapshotOverlayIcon(primaryIcon);
} }
return navigatableIcon; return navigatableIcon;

View file

@ -57,7 +57,7 @@ public abstract class AbstractByteViewerPlugin<P extends ProgramByteViewerCompon
public P createNewDisconnectedProvider() { public P createNewDisconnectedProvider() {
P newProvider = createProvider(false); P newProvider = createProvider(false);
disconnectedProviders.add(newProvider); addProvider(newProvider);
tool.showComponentProvider(newProvider, true); tool.showComponentProvider(newProvider, true);
return newProvider; return newProvider;
} }

View file

@ -90,7 +90,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
} }
decorationComponent = new DecoratorPanel(panel, isConnected); decorationComponent = new DecoratorPanel(panel, isConnected);
clipboardProvider = new ByteViewerClipboardProvider(this, tool); clipboardProvider = newClipboardProvider();
addToTool(); addToTool();
createProgramActions(); createProgramActions();
@ -98,6 +98,10 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
registerNavigatable(); registerNavigatable();
} }
protected ByteViewerClipboardProvider newClipboardProvider() {
return new ByteViewerClipboardProvider(this, tool);
}
public void createProgramActions() { public void createProgramActions() {
cloneByteViewerAction = new CloneByteViewerAction(); cloneByteViewerAction = new CloneByteViewerAction();
tool.addLocalAction(this, cloneByteViewerAction); tool.addLocalAction(this, cloneByteViewerAction);
@ -343,11 +347,11 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
@Override @Override
public Icon getIcon() { public Icon getIcon() {
if (isConnected()) { if (isConnected()) {
return super.getIcon(); return getBaseIcon();
} }
if (navigatableIcon == null) { if (navigatableIcon == null) {
Icon primaryIcon = super.getIcon(); Icon primaryIcon = getBaseIcon();
navigatableIcon = NavigatableIconFactory.createSnapshotOverlayIcon(primaryIcon); navigatableIcon = NavigatableIconFactory.createSnapshotOverlayIcon(primaryIcon);
} }
return navigatableIcon; return navigatableIcon;

View file

@ -628,6 +628,22 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
dockingTool.getWindowManager().setIcon(this, icon); dockingTool.getWindowManager().setIcon(this, icon);
} }
/**
* Get the icon provided to {@link #setIcon(Icon)}
*
* <p>
* This method is final, guaranteeing there is always a means for extensions of this class to
* obtain the original icon. Some classes may override {@link #getIcon()} to apply modifications
* when the icon is displayed in the UI. Further extensions of that class may wish to override
* {@link #getIcon()}, too, and so might want access to the original base icon. This method
* provides that access.
*
* @return the base icon
*/
protected final Icon getBaseIcon() {
return icon;
}
/** /**
* Signals that this provider's action for showing the provider should appear in the main * Signals that this provider's action for showing the provider should appear in the main
* toolbar * toolbar