diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerLocationLabel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerLocationLabel.java index 689f03382e..0a43d1ffc3 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerLocationLabel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerLocationLabel.java @@ -15,12 +15,18 @@ */ package ghidra.app.plugin.core.debug.gui; +import java.awt.event.MouseEvent; import java.util.*; import javax.swing.JLabel; import org.apache.commons.collections4.ComparatorUtils; +import docking.ActionContext; +import docking.ComponentProvider; +import ghidra.app.plugin.core.debug.gui.memory.DebuggerRegionActionContext; +import ghidra.app.plugin.core.debug.gui.modules.DebuggerModuleActionContext; +import ghidra.app.plugin.core.debug.gui.modules.DebuggerSectionActionContext; import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncTimer; import ghidra.debug.api.tracemgr.DebuggerCoordinates; @@ -204,4 +210,33 @@ public class DebuggerLocationLabel extends JLabel { setText(label); setToolTipText(label); } + + public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) { + TraceProgramView view = current.getView(); + if (view == null) { + return null; + } + if (address == null) { + return null; + } + try { + TraceSection section = getNearestSectionContaining(); + if (section != null) { + return new DebuggerSectionActionContext(provider, Set.of(section), this, true); + } + TraceModule module = getNearestModuleContaining(); + if (module != null) { + return new DebuggerModuleActionContext(provider, Set.of(module), this, true); + } + TraceMemoryRegion region = getRegionContaining(); + if (region != null) { + return new DebuggerRegionActionContext(provider, Set.of(region), this, true); + } + return null; + } + catch (Throwable t) { + // The error should already be displayed in the label + return null; + } + } } 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 11b5658c48..0ed204f182 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 @@ -433,10 +433,9 @@ public class DebuggerListingProvider extends CodeViewerProvider { * little conflated, since before the debugger, no one else presented a listing that could claim * to be "main" except the "connected" one. Here, we treat "connected" to mean that the address * is synchronized exactly with the other providers. "Main" on the other hand, does not - * necessarily have that property, but it is still not a snapshot. It is the main - * listing presented by this plugin, and so it has certain unique features. Calling - * {@link DebuggerListingPlugin#getProvider()} will return the main dynamic listing, despite it - * not really being "connected." + * necessarily have that property, but it is still not a clone. It is the main listing + * presented by this plugin, and so it has certain unique features. Calling + * {@link DebuggerListingPlugin#getProvider()} will return the main dynamic listing. * * @return true if this is the main listing for the plugin. */ @@ -884,6 +883,14 @@ public class DebuggerListingProvider extends CodeViewerProvider { } } + @Override + public ActionContext getActionContext(MouseEvent event) { + if (event == null || event.getSource() != locationLabel) { + return super.getActionContext(event); + } + return locationLabel.getActionContext(this, event); + } + @Override public void programSelectionChanged(ProgramSelection selection, EventTrigger trigger) { super.programSelectionChanged(selection, trigger); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerLegacyRegionsPanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerLegacyRegionsPanel.java index 5fd0781cdc..2ac39195d3 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerLegacyRegionsPanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerLegacyRegionsPanel.java @@ -21,7 +21,6 @@ import java.util.Collection; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; -import java.util.stream.Collectors; import javax.swing.*; import javax.swing.table.TableColumn; @@ -116,27 +115,11 @@ public class DebuggerLegacyRegionsPanel extends JPanel { } } - protected static RegionRow getSelectedRegionRow(ActionContext context) { - if (!(context instanceof DebuggerRegionActionContext)) { - return null; - } - DebuggerRegionActionContext ctx = (DebuggerRegionActionContext) context; - Set regions = ctx.getSelectedRegions(); - if (regions.size() != 1) { - return null; - } - return regions.iterator().next(); - } - protected static Set getSelectedRegions(ActionContext context) { - if (!(context instanceof DebuggerRegionActionContext)) { + if (!(context instanceof DebuggerRegionActionContext ctx)) { return null; } - DebuggerRegionActionContext ctx = (DebuggerRegionActionContext) context; - return ctx.getSelectedRegions() - .stream() - .map(r -> r.getRegion()) - .collect(Collectors.toSet()); + return ctx.getSelectedRegions(); } private class RegionsListener extends TraceDomainObjectListener { @@ -279,16 +262,6 @@ public class DebuggerLegacyRegionsPanel extends JPanel { return !ctx.getSelectedRegions().isEmpty(); } - private static Set getSelectedRegions(DebuggerRegionActionContext ctx) { - if (ctx == null) { - return null; - } - return ctx.getSelectedRegions() - .stream() - .map(r -> r.getRegion()) - .collect(Collectors.toSet()); - } - protected void navigateToSelectedRegion() { if (listingService != null) { int selectedRow = regionTable.getSelectedRow(); 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 1aca2b8aa3..b7c9c62ba7 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 @@ -27,6 +27,7 @@ import javax.swing.JPanel; import org.apache.commons.lang3.StringUtils; +import docking.ActionContext; import docking.action.DockingAction; import docking.action.ToggleDockingAction; import docking.menu.MultiStateDockingAction; @@ -539,6 +540,10 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi @Override protected void writeConfigState(SaveState saveState) { super.writeConfigState(saveState); + + CONFIG_STATE_HANDLER.writeConfigState(this, saveState); + trackingTrait.writeConfigState(saveState); + readsMemTrait.writeConfigState(saveState); } @Override @@ -547,6 +552,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi CONFIG_STATE_HANDLER.readConfigState(this, saveState); trackingTrait.readConfigState(saveState); + readsMemTrait.readConfigState(saveState); if (isMainViewer()) { followsCurrentThread = true; @@ -555,7 +561,6 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi actionFollowsCurrentThread.setSelected(followsCurrentThread); updateBorder(); } - // TODO: actionAutoReadMemory } @Override @@ -583,6 +588,14 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi locationLabel.goToAddress(currentLocation == null ? null : currentLocation.getAddress()); } + @Override + public ActionContext getActionContext(MouseEvent event) { + if (event == null || event.getSource() != locationLabel) { + return super.getActionContext(event); + } + return locationLabel.getActionContext(this, event); + } + @Override public void cloneWindow() { final DebuggerMemoryBytesProvider newProvider = myPlugin.createNewDisconnectedProvider(); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionActionContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionActionContext.java index 8526b72315..654983abf2 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionActionContext.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionActionContext.java @@ -15,22 +15,41 @@ */ package ghidra.app.plugin.core.debug.gui.memory; +import java.awt.Component; import java.util.Collection; import java.util.Set; +import java.util.stream.Collectors; +import docking.ComponentProvider; import docking.DefaultActionContext; import docking.widgets.table.GTable; +import ghidra.trace.model.memory.TraceMemoryRegion; public class DebuggerRegionActionContext extends DefaultActionContext { - private final Set selectedRegions; + private final Set selectedRegions; + private final boolean forcedSingle; + + private static Set toRegions(Collection rows) { + return rows.stream().map(RegionRow::getRegion).collect(Collectors.toUnmodifiableSet()); + } public DebuggerRegionActionContext(DebuggerRegionsProvider provider, - Collection selected, GTable table) { - super(provider, selected, table); - this.selectedRegions = Set.copyOf(selected); + Collection rows, GTable table) { + this(provider, toRegions(rows), table, false); } - public Set getSelectedRegions() { + public DebuggerRegionActionContext(ComponentProvider provider, + Set selected, Component sourceComponent, boolean forcedSingle) { + super(provider, selected, sourceComponent); + this.selectedRegions = selected; + this.forcedSingle = forcedSingle; + } + + public Set getSelectedRegions() { return selectedRegions; } + + public boolean isForcedSingle() { + return forcedSingle; + } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsPanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsPanel.java index c293627c9b..f0ac6ebd59 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsPanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsPanel.java @@ -177,10 +177,8 @@ public class DebuggerRegionsPanel extends AbstractObjectsTableBasedPanel getSelectedRegions(DebuggerObjectActionContext ctx) { - return ctx == null ? null - : AbstractObjectsTableBasedPanel.getSelected(ctx, TraceObjectMemoryRegion.class) - .collect(Collectors.toSet()); + protected Set getSelectedRegions(DebuggerObjectActionContext ctx) { + return ctx == null ? null : getSelected(ctx).collect(Collectors.toSet()); } public DebuggerRegionsPanel(DebuggerRegionsProvider provider) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsProvider.java index 0a437acc0d..0f0691b7de 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsProvider.java @@ -278,20 +278,22 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter { protected void createActions() { actionMapRegions = MapRegionsAction.builder(plugin) - .enabledWhen(this::isContextNonEmpty) - .popupWhen(this::isContextNonEmpty) + .enabledWhen(ctx -> isContextNonEmpty(ctx) && isContextNotForcedSingle(ctx)) + .popupWhen(ctx -> isContextNonEmpty(ctx) && isContextNotForcedSingle(ctx)) .onAction(this::activatedMapRegions) - .buildAndInstallLocal(this); + .buildAndInstall(tool); actionMapRegionTo = MapRegionToAction.builder(plugin) .enabledWhen(ctx -> currentProgram != null && isContextSingleSelection(ctx)) .popupWhen(ctx -> currentProgram != null && isContextSingleSelection(ctx)) .onAction(this::activatedMapRegionTo) - .buildAndInstallLocal(this); + .buildAndInstall(tool); actionMapRegionsTo = MapRegionsToAction.builder(plugin) - .enabledWhen(ctx -> currentProgram != null && isContextNonEmpty(ctx)) - .popupWhen(ctx -> currentProgram != null && isContextNonEmpty(ctx)) + .enabledWhen(ctx -> currentProgram != null && isContextNonEmpty(ctx) && + isContextNotForcedSingle(ctx)) + .popupWhen(ctx -> currentProgram != null && isContextNonEmpty(ctx) && + isContextNotForcedSingle(ctx)) .onAction(this::activatedMapRegionsTo) - .buildAndInstallLocal(this); + .buildAndInstall(tool); actionSelectAddresses = new SelectAddressesAction(); actionSelectRows = SelectRowsAction.builder(plugin) .description("Select regions by dynamic selection") @@ -329,26 +331,33 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter { } private boolean isContextNonEmpty(ActionContext context) { - if (context instanceof DebuggerRegionActionContext legacyCtx) { - return legacyPanel.isContextNonEmpty(legacyCtx); + if (context instanceof DebuggerRegionActionContext ctx) { + return legacyPanel.isContextNonEmpty(ctx); } else if (context instanceof DebuggerObjectActionContext ctx) { - return DebuggerRegionsPanel.isContextNonEmpty(ctx); + return panel.isContextNonEmpty(ctx); } return false; } + private boolean isContextNotForcedSingle(ActionContext context) { + if (context instanceof DebuggerRegionActionContext ctx) { + return !ctx.isForcedSingle(); + } + return true; + } + private boolean isContextSingleSelection(ActionContext context) { Set sel = getSelectedRegions(context); return sel != null && sel.size() == 1; } - private static Set getSelectedRegions(ActionContext context) { - if (context instanceof DebuggerRegionActionContext legacyCtx) { - return DebuggerLegacyRegionsPanel.getSelectedRegions(legacyCtx); + private Set getSelectedRegions(ActionContext context) { + if (context instanceof DebuggerRegionActionContext ctx) { + return DebuggerLegacyRegionsPanel.getSelectedRegions(ctx); } else if (context instanceof DebuggerObjectActionContext ctx) { - return DebuggerRegionsPanel.getSelectedRegions(ctx); + return panel.getSelectedRegions(ctx); } return null; } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/AbstractObjectsTableBasedPanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/AbstractObjectsTableBasedPanel.java index e212883c10..d09189ec82 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/AbstractObjectsTableBasedPanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/AbstractObjectsTableBasedPanel.java @@ -43,20 +43,6 @@ import ghidra.trace.model.target.TraceObjectInterface; public abstract class AbstractObjectsTableBasedPanel extends ObjectsTablePanel implements ListSelectionListener, CellActivationListener { - public static boolean isContextNonEmpty(DebuggerObjectActionContext ctx) { - return ctx != null && !ctx.getObjectValues().isEmpty(); - } - - public static Stream getSelected( - DebuggerObjectActionContext ctx, Class iface) { - return ctx == null ? null - : ctx.getObjectValues() - .stream() - .filter(v -> v.isObject()) - .map(v -> v.getChild().queryInterface(iface)) - .filter(r -> r != null); - } - private final ComponentProvider provider; private final Class objType; @@ -82,6 +68,19 @@ public abstract class AbstractObjectsTableBasedPanel getSelected(DebuggerObjectActionContext ctx) { + return ctx == null ? null + : ctx.getObjectValues() + .stream() + .filter(v -> v.isObject()) + .map(v -> v.getChild().queryInterface(objType)) + .filter(r -> r != null); + } + public DebuggerObjectActionContext getActionContext() { return myActionContext; } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerLegacyModulesPanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerLegacyModulesPanel.java index f487b4f6e2..8aaa3e433c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerLegacyModulesPanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerLegacyModulesPanel.java @@ -46,17 +46,14 @@ public class DebuggerLegacyModulesPanel extends JPanel { protected static Set getSelectedModulesFromContext( DebuggerModuleActionContext context) { - return context.getSelectedModules() - .stream() - .map(r -> r.getModule()) - .collect(Collectors.toSet()); + return context.getSelectedModules(); } protected static Set getSelectedSectionsFromContext( DebuggerModuleActionContext context) { return context.getSelectedModules() .stream() - .flatMap(r -> r.getModule().getSections().stream()) + .flatMap(m -> m.getSections().stream()) .collect(Collectors.toSet()); } @@ -69,24 +66,6 @@ public class DebuggerLegacyModulesPanel extends JPanel { return sel; } - protected static ModuleRow getSelectedModuleRowFromContext( - DebuggerModuleActionContext context) { - Set modules = context.getSelectedModules(); - if (modules.size() != 1) { - return null; - } - return modules.iterator().next(); - } - - protected static SectionRow getSelectedSectionRowFromContext( - DebuggerSectionActionContext context) { - Set sections = context.getSelectedSections(); - if (sections.size() != 1) { - return null; - } - return sections.iterator().next(); - } - protected enum ModuleTableColumns implements EnumeratedTableColumn { BASE("Base Address", Address.class, ModuleRow::getBase), diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerLegacySectionsPanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerLegacySectionsPanel.java index e133321343..13fbc7def4 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerLegacySectionsPanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerLegacySectionsPanel.java @@ -48,24 +48,21 @@ public class DebuggerLegacySectionsPanel extends JPanel { protected static Set getSelectedModulesFromContext( DebuggerSectionActionContext context) { - return context.getSelectedSections() + return context.getSelectedSections(false) .stream() .map(r -> r.getModule()) .collect(Collectors.toSet()); } protected static Set getSelectedSectionsFromContext( - DebuggerSectionActionContext context) { - return context.getSelectedSections() - .stream() - .map(r -> r.getSection()) - .collect(Collectors.toSet()); + DebuggerSectionActionContext context, boolean allowExpansion) { + return context.getSelectedSections(allowExpansion); } protected static AddressSetView getSelectedAddressesFromContext( DebuggerSectionActionContext context) { AddressSet sel = new AddressSet(); - for (TraceSection section : getSelectedSectionsFromContext(context)) { + for (TraceSection section : getSelectedSectionsFromContext(context, false)) { sel.add(section.getRange()); } return sel; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleActionContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleActionContext.java index c4d8cf0da7..bc54451a4a 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleActionContext.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleActionContext.java @@ -15,22 +15,41 @@ */ package ghidra.app.plugin.core.debug.gui.modules; +import java.awt.Component; import java.util.Collection; import java.util.Set; +import java.util.stream.Collectors; +import docking.ComponentProvider; import docking.DefaultActionContext; import docking.widgets.table.GTable; +import ghidra.trace.model.modules.TraceModule; public class DebuggerModuleActionContext extends DefaultActionContext { - private final Set selectedModules; + private final Set selectedModules; + private final boolean forcedSingle; + + private static Set toModules(Collection rows) { + return rows.stream().map(ModuleRow::getModule).collect(Collectors.toUnmodifiableSet()); + } public DebuggerModuleActionContext(DebuggerModulesProvider provider, - Collection selected, GTable table) { - super(provider, selected, table); - this.selectedModules = Set.copyOf(selected); + Collection rows, GTable table) { + this(provider, toModules(rows), table, false); } - public Set getSelectedModules() { + public DebuggerModuleActionContext(ComponentProvider provider, Set selected, + Component sourceComponent, boolean forcedSingle) { + super(provider, selected, sourceComponent); + this.selectedModules = selected; + this.forcedSingle = forcedSingle; + } + + public Set getSelectedModules() { return selectedModules; } + + public boolean isForcedSingle() { + return forcedSingle; + } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java index 6697c4a852..d43aa899a4 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java @@ -306,12 +306,13 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { return Set.of(); } - protected static Set getSelectedSections(ActionContext context) { + protected static Set getSelectedSections(ActionContext context, + boolean allowExpansion) { if (context instanceof DebuggerModuleActionContext ctx) { return DebuggerLegacyModulesPanel.getSelectedSectionsFromContext(ctx); } if (context instanceof DebuggerSectionActionContext ctx) { - return DebuggerLegacySectionsPanel.getSelectedSectionsFromContext(ctx); + return DebuggerLegacySectionsPanel.getSelectedSectionsFromContext(ctx, allowExpansion); } if (context instanceof DebuggerObjectActionContext ctx) { return DebuggerModulesPanel.getSelectedSectionsFromContext(ctx); @@ -372,7 +373,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { public ImportFromFileSystemAction() { super(plugin); setPopupMenuData(new MenuData(new String[] { NAME }, GROUP)); - addLocalAction(this); + tool.addAction(this); setEnabled(true); } @@ -467,7 +468,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { SelectAddressesAction actionSelectAddresses; ImportFromFileSystemAction actionImportFromFileSystem; ToggleDockingAction actionShowSectionsTable; - // TODO: Save the state of this toggle? Not really compelled. ToggleDockingAction actionFilterSectionsByModules; DockingAction actionSelectCurrent; @@ -594,30 +594,32 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { .onAction(this::activatedMapManually) .buildAndInstallLocal(this); actionMapModules = MapModulesAction.builder(plugin) - .enabledWhen(this::isContextNonEmpty) - .popupWhen(this::isContextNonEmpty) + .enabledWhen(ctx -> isContextHasModules(ctx) && isContextNotForcedSingle(ctx)) + .popupWhen(ctx -> isContextHasModules(ctx) && isContextNotForcedSingle(ctx)) .onAction(this::activatedMapModules) - .buildAndInstallLocal(this); + .buildAndInstall(tool); actionMapModuleTo = MapModuleToAction.builder(plugin) .enabledWhen(ctx -> currentProgram != null && getSelectedModules(ctx).size() == 1) .popupWhen(ctx -> currentProgram != null && getSelectedModules(ctx).size() == 1) .onAction(this::activatedMapModuleTo) - .buildAndInstallLocal(this); + .buildAndInstall(tool); actionMapSections = MapSectionsAction.builder(plugin) - .enabledWhen(this::isContextNonEmpty) - .popupWhen(this::isContextNonEmpty) + .enabledWhen(ctx -> isContextHasSections(ctx) && isContextNotForcedSingle(ctx)) + .popupWhen(ctx -> isContextHasSections(ctx) && isContextNotForcedSingle(ctx)) .onAction(this::activatedMapSections) - .buildAndInstallLocal(this); + .buildAndInstall(tool); actionMapSectionTo = MapSectionToAction.builder(plugin) - .enabledWhen(ctx -> currentProgram != null && getSelectedSections(ctx).size() == 1) - .popupWhen(ctx -> currentProgram != null && getSelectedSections(ctx).size() == 1) + .enabledWhen( + ctx -> currentProgram != null && getSelectedSections(ctx, false).size() == 1) + .popupWhen( + ctx -> currentProgram != null && getSelectedSections(ctx, false).size() == 1) .onAction(this::activatedMapSectionTo) - .buildAndInstallLocal(this); + .buildAndInstall(tool); actionMapSectionsTo = MapSectionsToAction.builder(plugin) .enabledWhen(ctx -> currentProgram != null && isContextSectionsOfOneModule(ctx)) .popupWhen(ctx -> currentProgram != null && isContextSectionsOfOneModule(ctx)) .onAction(this::activatedMapSectionsTo) - .buildAndInstallLocal(this); + .buildAndInstall(tool); actionAutoMap = AutoMapAction.builder(plugin) .onActionStateChanged(this::changedAutoMapSpec) @@ -653,12 +655,20 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { contextChanged(); } + private boolean isContextHasModules(ActionContext context) { + return !getSelectedModules(context).isEmpty(); + } + + private boolean isContextHasSections(ActionContext context) { + return !getSelectedSections(context, false).isEmpty(); + } + private boolean isContextNonEmpty(ActionContext context) { if (context instanceof DebuggerModuleActionContext ctx) { return !ctx.getSelectedModules().isEmpty(); } if (context instanceof DebuggerSectionActionContext ctx) { - return !ctx.getSelectedSections().isEmpty(); + return !ctx.getSelectedSections(false).isEmpty(); } if (context instanceof DebuggerObjectActionContext ctx) { return !ctx.getObjectValues().isEmpty(); @@ -666,8 +676,18 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { return false; } - private boolean isContextSectionsOfOneModule(ActionContext ignored) { - Set sel = getSelectedSections(myActionContext); + private boolean isContextNotForcedSingle(ActionContext context) { + if (context instanceof DebuggerModuleActionContext ctx) { + return !ctx.isForcedSingle(); + } + if (context instanceof DebuggerSectionActionContext ctx) { + return !ctx.isForcedSingle(); + } + return true; + } + + private boolean isContextSectionsOfOneModule(ActionContext context) { + Set sel = getSelectedSections(context, false); if (sel == null || sel.isEmpty()) { return false; } @@ -713,40 +733,40 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { tool.showComponentProvider(provider, true); } - private void activatedMapModules(ActionContext ignored) { - Set sel = getSelectedModules(myActionContext); + private void activatedMapModules(ActionContext context) { + Set sel = getSelectedModules(context); if (sel == null || sel.isEmpty()) { return; } mapModules(sel); } - private void activatedMapModuleTo(ActionContext ignored) { - Set sel = getSelectedModules(myActionContext); + private void activatedMapModuleTo(ActionContext context) { + Set sel = getSelectedModules(context); if (sel == null || sel.size() != 1) { return; } mapModuleTo(sel.iterator().next()); } - private void activatedMapSections(ActionContext ignored) { - Set sel = getSelectedSections(myActionContext); + private void activatedMapSections(ActionContext context) { + Set sel = getSelectedSections(context, true); if (sel == null || sel.isEmpty()) { return; } mapSections(sel); } - private void activatedMapSectionsTo(ActionContext ignored) { - Set sel = getSelectedSections(myActionContext); + private void activatedMapSectionsTo(ActionContext context) { + Set sel = getSelectedSections(context, true); if (sel == null || sel.isEmpty()) { return; } mapSectionsTo(sel); } - private void activatedMapSectionTo(ActionContext ignored) { - Set sel = getSelectedSections(myActionContext); + private void activatedMapSectionTo(ActionContext context) { + Set sel = getSelectedSections(context, false); if (sel == null || sel.size() != 1) { return; } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionActionContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionActionContext.java index a93415b375..12cd5bd80d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionActionContext.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionActionContext.java @@ -15,32 +15,46 @@ */ package ghidra.app.plugin.core.debug.gui.modules; +import java.awt.Component; import java.util.Collection; import java.util.Set; +import java.util.stream.Collectors; +import docking.ComponentProvider; import docking.DefaultActionContext; import docking.widgets.table.GTable; +import ghidra.trace.model.modules.TraceSection; public class DebuggerSectionActionContext extends DefaultActionContext { - private final Set selectedSections; - //private final Set involvedModules; + private final Set selectedSections; + private final boolean forcedSingle; - public DebuggerSectionActionContext(DebuggerModulesProvider provider, - Collection selected, GTable table) { - super(provider, selected, table); - this.selectedSections = Set.copyOf(selected); - /* - * this.involvedModules = Collections.unmodifiableSet( - * selected.stream().map(SectionRecord::getModule).collect(Collectors.toSet())); - */ + private static Set toSections(Collection rows) { + return rows.stream().map(SectionRow::getSection).collect(Collectors.toUnmodifiableSet()); } - public Set getSelectedSections() { + public DebuggerSectionActionContext(DebuggerModulesProvider provider, + Collection rows, GTable table) { + this(provider, toSections(rows), table, false); + } + + public DebuggerSectionActionContext(ComponentProvider provider, + Set selected, Component sourceComponent, boolean forcedSingle) { + super(provider, selected, sourceComponent); + this.selectedSections = selected; + this.forcedSingle = forcedSingle; + } + + public Set getSelectedSections(boolean allowExpansion) { + if (forcedSingle && allowExpansion) { + return selectedSections.stream() + .flatMap(s -> s.getModule().getSections().stream()) + .collect(Collectors.toUnmodifiableSet()); + } return selectedSections; } - /* - * // TODO: Do I need this? public Set getInvolvedModules() { return - * involvedModules; } - */ + public boolean isForcedSingle() { + return forcedSingle; + } }