diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java index f5452affe4..8233abb76d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java @@ -19,6 +19,7 @@ import static ghidra.app.plugin.core.debug.gui.DebuggerResources.ICON_REGISTER_M import java.awt.BorderLayout; import java.awt.Color; +import java.awt.datatransfer.DataFlavor; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.lang.invoke.MethodHandles; @@ -26,8 +27,7 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.swing.JLabel; -import javax.swing.JPanel; +import javax.swing.*; import javax.swing.event.ChangeEvent; 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.MarkerOverviewProvider; import ghidra.app.services.*; +import ghidra.app.services.DebuggerControlService.ControlModeChangeListener; import ghidra.app.services.DebuggerListingService.LocationTrackingSpecChangeListener; import ghidra.app.util.viewer.format.FormatManager; import ghidra.app.util.viewer.listingpanel.ListingPanel; @@ -293,7 +294,7 @@ public class DebuggerListingProvider extends CodeViewerProvider { private DebuggerStaticMappingService mappingService; @AutoServiceConsumed private DebuggerConsoleService consoleService; - @AutoServiceConsumed + //@AutoServiceConsumed via method private DebuggerControlService controlService; @AutoServiceConsumed private ProgramManager programManager; @@ -354,6 +355,12 @@ public class DebuggerListingProvider extends CodeViewerProvider { protected final ForStaticSyncMappingChangeListener mappingChangeListener = new ForStaticSyncMappingChangeListener(); + private final ControlModeChangeListener controlModeChangeListener = (trace, mode) -> { + if (trace == current.getTrace()) { + // for Paste action + contextChanged(); + } + }; 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 private void setConsoleService(DebuggerConsoleService consoleService) { if (consoleService != null) { @@ -725,6 +743,14 @@ public class DebuggerListingProvider extends CodeViewerProvider { return DateUtils.formatDateTimestamp(new Date(snapshot.getRealTime())); } + @Override + public Icon getIcon() { + if (isMainListing()) { + return getBaseIcon(); + } + return super.getIcon(); + } + @Override protected ListingActionContext newListingActionContext() { return new DebuggerListingActionContext(this); @@ -740,6 +766,21 @@ public class DebuggerListingProvider extends CodeViewerProvider { } 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); + } }; } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java index 97354bfb58..7e8bb50e1e 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java @@ -92,7 +92,7 @@ public class DebuggerMemoryBytesPlugin private void createActions() { actionNewMemory = NewMemoryAction.builder(this) .enabled(true) - .onAction(c -> createNewDisconnectedProvider()) + .onAction(c -> connectedProvider.cloneWindow()) .buildAndInstall(tool); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java index b7c9c62ba7..3931bce10e 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java @@ -16,14 +16,14 @@ package ghidra.app.plugin.core.debug.gui.memory; import java.awt.BorderLayout; +import java.awt.datatransfer.DataFlavor; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.lang.invoke.MethodHandles; import java.math.BigInteger; import java.util.*; -import javax.swing.JLabel; -import javax.swing.JPanel; +import javax.swing.*; 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.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec; 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.debug.api.action.GoToInput; import ghidra.debug.api.action.LocationTrackingSpec; @@ -168,6 +170,8 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi @AutoServiceConsumed private DebuggerTraceManagerService traceManager; + //@AutoServiceConsumed via method + private DebuggerControlService controlService; @SuppressWarnings("unused") private final AutoService.Wiring autoServiceWiring; @@ -191,6 +195,12 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi // TODO: followsCurrentSnap? private final ListenerForChanges listenerForChanges = new ListenerForChanges(); + private final ControlModeChangeListener controlModeChangeListener = (trace, mode) -> { + if (trace == getCurrent().getTrace()) { + // for Paste action + contextChanged(); + } + }; DebuggerCoordinates current = DebuggerCoordinates.NOWHERE; private DebuggerCoordinates previous = DebuggerCoordinates.NOWHERE; @@ -220,6 +230,13 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi readsMemTrait.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); trackingLabel.addMouseListener(new MouseAdapter() { @@ -339,11 +356,50 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi setSubTitle(computeSubTitle()); } + @Override + public Icon getIcon() { + if (isMainViewer()) { + return getBaseIcon(); + } + return super.getIcon(); + } + @Override protected ByteViewerActionContext newByteViewerActionContext() { 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() { initTraits(); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java index 337157d8c0..45909adbf2 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java @@ -15,7 +15,7 @@ */ 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 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.listing.DebuggerListingPlugin; import ghidra.app.plugin.core.debug.service.control.DebuggerControlServicePlugin; -import ghidra.app.services.*; +import ghidra.app.services.DebuggerControlService; import ghidra.async.SwingExecutorService; import ghidra.debug.api.control.ControlMode; import ghidra.debug.api.model.TraceRecorder; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java index 7c4305c3b6..f30d2923fd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java @@ -79,7 +79,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter private static final String DIVIDER_LOCATION = "DividerLocation"; - private ImageIcon navigatableIcon; private Map programHighlighterMap = new HashMap<>(); private ProgramHighlighterProvider highlighterAdapter; @@ -845,19 +844,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter 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 public Icon getNavigatableIcon() { return getIcon(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/framework/plugintool/NavigatableComponentProviderAdapter.java b/Ghidra/Features/Base/src/main/java/ghidra/framework/plugintool/NavigatableComponentProviderAdapter.java index 5310fe48b3..b363f44dce 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/framework/plugintool/NavigatableComponentProviderAdapter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/framework/plugintool/NavigatableComponentProviderAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,11 +44,11 @@ public abstract class NavigatableComponentProviderAdapter extends ComponentProvi @Override public Icon getIcon() { if (isConnected()) { - return super.getIcon(); + return getBaseIcon(); } if (navigatableIcon == null) { - Icon primaryIcon = super.getIcon(); + Icon primaryIcon = getBaseIcon(); navigatableIcon = NavigatableIconFactory.createSnapshotOverlayIcon(primaryIcon); } return navigatableIcon; diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPlugin.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPlugin.java index 24eb2615b6..c456a7bd51 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPlugin.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPlugin.java @@ -57,7 +57,7 @@ public abstract class AbstractByteViewerPlugin

+ * 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 * toolbar