GP-4311: Add Context menu to Location labels

This commit is contained in:
Dan 2024-02-14 16:00:09 -05:00
parent 247528c7fe
commit 1bd3fe3669
13 changed files with 231 additions and 149 deletions

View file

@ -15,12 +15,18 @@
*/ */
package ghidra.app.plugin.core.debug.gui; package ghidra.app.plugin.core.debug.gui;
import java.awt.event.MouseEvent;
import java.util.*; import java.util.*;
import javax.swing.JLabel; import javax.swing.JLabel;
import org.apache.commons.collections4.ComparatorUtils; 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.AsyncDebouncer;
import ghidra.async.AsyncTimer; import ghidra.async.AsyncTimer;
import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracemgr.DebuggerCoordinates;
@ -204,4 +210,33 @@ public class DebuggerLocationLabel extends JLabel {
setText(label); setText(label);
setToolTipText(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;
}
}
} }

View file

@ -433,10 +433,9 @@ public class DebuggerListingProvider extends CodeViewerProvider {
* little conflated, since before the debugger, no one else presented a listing that could claim * 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 * 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 * is synchronized exactly with the other providers. "Main" on the other hand, does not
* necessarily have that property, but it is still <em>not</em> a snapshot. It is the main * necessarily have that property, but it is still <em>not</em> a clone. It is the main listing
* listing presented by this plugin, and so it has certain unique features. Calling * presented by this plugin, and so it has certain unique features. Calling
* {@link DebuggerListingPlugin#getProvider()} will return the main dynamic listing, despite it * {@link DebuggerListingPlugin#getProvider()} will return the main dynamic listing.
* not really being "connected."
* *
* @return true if this is the main listing for the plugin. * @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 @Override
public void programSelectionChanged(ProgramSelection selection, EventTrigger trigger) { public void programSelectionChanged(ProgramSelection selection, EventTrigger trigger) {
super.programSelectionChanged(selection, trigger); super.programSelectionChanged(selection, trigger);

View file

@ -21,7 +21,6 @@ import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import javax.swing.*; import javax.swing.*;
import javax.swing.table.TableColumn; 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<RegionRow> regions = ctx.getSelectedRegions();
if (regions.size() != 1) {
return null;
}
return regions.iterator().next();
}
protected static Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) { protected static Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) {
if (!(context instanceof DebuggerRegionActionContext)) { if (!(context instanceof DebuggerRegionActionContext ctx)) {
return null; return null;
} }
DebuggerRegionActionContext ctx = (DebuggerRegionActionContext) context; return ctx.getSelectedRegions();
return ctx.getSelectedRegions()
.stream()
.map(r -> r.getRegion())
.collect(Collectors.toSet());
} }
private class RegionsListener extends TraceDomainObjectListener { private class RegionsListener extends TraceDomainObjectListener {
@ -279,16 +262,6 @@ public class DebuggerLegacyRegionsPanel extends JPanel {
return !ctx.getSelectedRegions().isEmpty(); return !ctx.getSelectedRegions().isEmpty();
} }
private static Set<TraceMemoryRegion> getSelectedRegions(DebuggerRegionActionContext ctx) {
if (ctx == null) {
return null;
}
return ctx.getSelectedRegions()
.stream()
.map(r -> r.getRegion())
.collect(Collectors.toSet());
}
protected void navigateToSelectedRegion() { protected void navigateToSelectedRegion() {
if (listingService != null) { if (listingService != null) {
int selectedRow = regionTable.getSelectedRow(); int selectedRow = regionTable.getSelectedRow();

View file

@ -27,6 +27,7 @@ import javax.swing.JPanel;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.ToggleDockingAction; import docking.action.ToggleDockingAction;
import docking.menu.MultiStateDockingAction; import docking.menu.MultiStateDockingAction;
@ -539,6 +540,10 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
@Override @Override
protected void writeConfigState(SaveState saveState) { protected void writeConfigState(SaveState saveState) {
super.writeConfigState(saveState); super.writeConfigState(saveState);
CONFIG_STATE_HANDLER.writeConfigState(this, saveState);
trackingTrait.writeConfigState(saveState);
readsMemTrait.writeConfigState(saveState);
} }
@Override @Override
@ -547,6 +552,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
CONFIG_STATE_HANDLER.readConfigState(this, saveState); CONFIG_STATE_HANDLER.readConfigState(this, saveState);
trackingTrait.readConfigState(saveState); trackingTrait.readConfigState(saveState);
readsMemTrait.readConfigState(saveState);
if (isMainViewer()) { if (isMainViewer()) {
followsCurrentThread = true; followsCurrentThread = true;
@ -555,7 +561,6 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
actionFollowsCurrentThread.setSelected(followsCurrentThread); actionFollowsCurrentThread.setSelected(followsCurrentThread);
updateBorder(); updateBorder();
} }
// TODO: actionAutoReadMemory
} }
@Override @Override
@ -583,6 +588,14 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
locationLabel.goToAddress(currentLocation == null ? null : currentLocation.getAddress()); 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 @Override
public void cloneWindow() { public void cloneWindow() {
final DebuggerMemoryBytesProvider newProvider = myPlugin.createNewDisconnectedProvider(); final DebuggerMemoryBytesProvider newProvider = myPlugin.createNewDisconnectedProvider();

View file

@ -15,22 +15,41 @@
*/ */
package ghidra.app.plugin.core.debug.gui.memory; package ghidra.app.plugin.core.debug.gui.memory;
import java.awt.Component;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import docking.ComponentProvider;
import docking.DefaultActionContext; import docking.DefaultActionContext;
import docking.widgets.table.GTable; import docking.widgets.table.GTable;
import ghidra.trace.model.memory.TraceMemoryRegion;
public class DebuggerRegionActionContext extends DefaultActionContext { public class DebuggerRegionActionContext extends DefaultActionContext {
private final Set<RegionRow> selectedRegions; private final Set<TraceMemoryRegion> selectedRegions;
private final boolean forcedSingle;
private static Set<TraceMemoryRegion> toRegions(Collection<RegionRow> rows) {
return rows.stream().map(RegionRow::getRegion).collect(Collectors.toUnmodifiableSet());
}
public DebuggerRegionActionContext(DebuggerRegionsProvider provider, public DebuggerRegionActionContext(DebuggerRegionsProvider provider,
Collection<RegionRow> selected, GTable table) { Collection<RegionRow> rows, GTable table) {
super(provider, selected, table); this(provider, toRegions(rows), table, false);
this.selectedRegions = Set.copyOf(selected);
} }
public Set<RegionRow> getSelectedRegions() { public DebuggerRegionActionContext(ComponentProvider provider,
Set<TraceMemoryRegion> selected, Component sourceComponent, boolean forcedSingle) {
super(provider, selected, sourceComponent);
this.selectedRegions = selected;
this.forcedSingle = forcedSingle;
}
public Set<TraceMemoryRegion> getSelectedRegions() {
return selectedRegions; return selectedRegions;
} }
public boolean isForcedSingle() {
return forcedSingle;
}
} }

View file

@ -177,10 +177,8 @@ public class DebuggerRegionsPanel extends AbstractObjectsTableBasedPanel<TraceOb
return new ModelQuery(schema.searchFor(TargetMemoryRegion.class, path, true)); return new ModelQuery(schema.searchFor(TargetMemoryRegion.class, path, true));
} }
protected static Set<TraceMemoryRegion> getSelectedRegions(DebuggerObjectActionContext ctx) { protected Set<TraceMemoryRegion> getSelectedRegions(DebuggerObjectActionContext ctx) {
return ctx == null ? null return ctx == null ? null : getSelected(ctx).collect(Collectors.toSet());
: AbstractObjectsTableBasedPanel.getSelected(ctx, TraceObjectMemoryRegion.class)
.collect(Collectors.toSet());
} }
public DebuggerRegionsPanel(DebuggerRegionsProvider provider) { public DebuggerRegionsPanel(DebuggerRegionsProvider provider) {

View file

@ -278,20 +278,22 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
protected void createActions() { protected void createActions() {
actionMapRegions = MapRegionsAction.builder(plugin) actionMapRegions = MapRegionsAction.builder(plugin)
.enabledWhen(this::isContextNonEmpty) .enabledWhen(ctx -> isContextNonEmpty(ctx) && isContextNotForcedSingle(ctx))
.popupWhen(this::isContextNonEmpty) .popupWhen(ctx -> isContextNonEmpty(ctx) && isContextNotForcedSingle(ctx))
.onAction(this::activatedMapRegions) .onAction(this::activatedMapRegions)
.buildAndInstallLocal(this); .buildAndInstall(tool);
actionMapRegionTo = MapRegionToAction.builder(plugin) actionMapRegionTo = MapRegionToAction.builder(plugin)
.enabledWhen(ctx -> currentProgram != null && isContextSingleSelection(ctx)) .enabledWhen(ctx -> currentProgram != null && isContextSingleSelection(ctx))
.popupWhen(ctx -> currentProgram != null && isContextSingleSelection(ctx)) .popupWhen(ctx -> currentProgram != null && isContextSingleSelection(ctx))
.onAction(this::activatedMapRegionTo) .onAction(this::activatedMapRegionTo)
.buildAndInstallLocal(this); .buildAndInstall(tool);
actionMapRegionsTo = MapRegionsToAction.builder(plugin) actionMapRegionsTo = MapRegionsToAction.builder(plugin)
.enabledWhen(ctx -> currentProgram != null && isContextNonEmpty(ctx)) .enabledWhen(ctx -> currentProgram != null && isContextNonEmpty(ctx) &&
.popupWhen(ctx -> currentProgram != null && isContextNonEmpty(ctx)) isContextNotForcedSingle(ctx))
.popupWhen(ctx -> currentProgram != null && isContextNonEmpty(ctx) &&
isContextNotForcedSingle(ctx))
.onAction(this::activatedMapRegionsTo) .onAction(this::activatedMapRegionsTo)
.buildAndInstallLocal(this); .buildAndInstall(tool);
actionSelectAddresses = new SelectAddressesAction(); actionSelectAddresses = new SelectAddressesAction();
actionSelectRows = SelectRowsAction.builder(plugin) actionSelectRows = SelectRowsAction.builder(plugin)
.description("Select regions by dynamic selection") .description("Select regions by dynamic selection")
@ -329,26 +331,33 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
} }
private boolean isContextNonEmpty(ActionContext context) { private boolean isContextNonEmpty(ActionContext context) {
if (context instanceof DebuggerRegionActionContext legacyCtx) { if (context instanceof DebuggerRegionActionContext ctx) {
return legacyPanel.isContextNonEmpty(legacyCtx); return legacyPanel.isContextNonEmpty(ctx);
} }
else if (context instanceof DebuggerObjectActionContext ctx) { else if (context instanceof DebuggerObjectActionContext ctx) {
return DebuggerRegionsPanel.isContextNonEmpty(ctx); return panel.isContextNonEmpty(ctx);
} }
return false; return false;
} }
private boolean isContextNotForcedSingle(ActionContext context) {
if (context instanceof DebuggerRegionActionContext ctx) {
return !ctx.isForcedSingle();
}
return true;
}
private boolean isContextSingleSelection(ActionContext context) { private boolean isContextSingleSelection(ActionContext context) {
Set<TraceMemoryRegion> sel = getSelectedRegions(context); Set<TraceMemoryRegion> sel = getSelectedRegions(context);
return sel != null && sel.size() == 1; return sel != null && sel.size() == 1;
} }
private static Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) { private Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) {
if (context instanceof DebuggerRegionActionContext legacyCtx) { if (context instanceof DebuggerRegionActionContext ctx) {
return DebuggerLegacyRegionsPanel.getSelectedRegions(legacyCtx); return DebuggerLegacyRegionsPanel.getSelectedRegions(ctx);
} }
else if (context instanceof DebuggerObjectActionContext ctx) { else if (context instanceof DebuggerObjectActionContext ctx) {
return DebuggerRegionsPanel.getSelectedRegions(ctx); return panel.getSelectedRegions(ctx);
} }
return null; return null;
} }

View file

@ -43,20 +43,6 @@ import ghidra.trace.model.target.TraceObjectInterface;
public abstract class AbstractObjectsTableBasedPanel<U extends TraceObjectInterface> public abstract class AbstractObjectsTableBasedPanel<U extends TraceObjectInterface>
extends ObjectsTablePanel implements ListSelectionListener, CellActivationListener { extends ObjectsTablePanel implements ListSelectionListener, CellActivationListener {
public static boolean isContextNonEmpty(DebuggerObjectActionContext ctx) {
return ctx != null && !ctx.getObjectValues().isEmpty();
}
public static <T extends TraceObjectInterface> Stream<T> getSelected(
DebuggerObjectActionContext ctx, Class<T> 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 ComponentProvider provider;
private final Class<U> objType; private final Class<U> objType;
@ -82,6 +68,19 @@ public abstract class AbstractObjectsTableBasedPanel<U extends TraceObjectInterf
addCellActivationListener(this); addCellActivationListener(this);
} }
public boolean isContextNonEmpty(DebuggerObjectActionContext ctx) {
return getSelected(ctx).findAny().isPresent();
}
public Stream<U> 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() { public DebuggerObjectActionContext getActionContext() {
return myActionContext; return myActionContext;
} }

View file

@ -46,17 +46,14 @@ public class DebuggerLegacyModulesPanel extends JPanel {
protected static Set<TraceModule> getSelectedModulesFromContext( protected static Set<TraceModule> getSelectedModulesFromContext(
DebuggerModuleActionContext context) { DebuggerModuleActionContext context) {
return context.getSelectedModules() return context.getSelectedModules();
.stream()
.map(r -> r.getModule())
.collect(Collectors.toSet());
} }
protected static Set<TraceSection> getSelectedSectionsFromContext( protected static Set<TraceSection> getSelectedSectionsFromContext(
DebuggerModuleActionContext context) { DebuggerModuleActionContext context) {
return context.getSelectedModules() return context.getSelectedModules()
.stream() .stream()
.flatMap(r -> r.getModule().getSections().stream()) .flatMap(m -> m.getSections().stream())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@ -69,24 +66,6 @@ public class DebuggerLegacyModulesPanel extends JPanel {
return sel; return sel;
} }
protected static ModuleRow getSelectedModuleRowFromContext(
DebuggerModuleActionContext context) {
Set<ModuleRow> modules = context.getSelectedModules();
if (modules.size() != 1) {
return null;
}
return modules.iterator().next();
}
protected static SectionRow getSelectedSectionRowFromContext(
DebuggerSectionActionContext context) {
Set<SectionRow> sections = context.getSelectedSections();
if (sections.size() != 1) {
return null;
}
return sections.iterator().next();
}
protected enum ModuleTableColumns protected enum ModuleTableColumns
implements EnumeratedTableColumn<ModuleTableColumns, ModuleRow> { implements EnumeratedTableColumn<ModuleTableColumns, ModuleRow> {
BASE("Base Address", Address.class, ModuleRow::getBase), BASE("Base Address", Address.class, ModuleRow::getBase),

View file

@ -48,24 +48,21 @@ public class DebuggerLegacySectionsPanel extends JPanel {
protected static Set<TraceModule> getSelectedModulesFromContext( protected static Set<TraceModule> getSelectedModulesFromContext(
DebuggerSectionActionContext context) { DebuggerSectionActionContext context) {
return context.getSelectedSections() return context.getSelectedSections(false)
.stream() .stream()
.map(r -> r.getModule()) .map(r -> r.getModule())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
protected static Set<TraceSection> getSelectedSectionsFromContext( protected static Set<TraceSection> getSelectedSectionsFromContext(
DebuggerSectionActionContext context) { DebuggerSectionActionContext context, boolean allowExpansion) {
return context.getSelectedSections() return context.getSelectedSections(allowExpansion);
.stream()
.map(r -> r.getSection())
.collect(Collectors.toSet());
} }
protected static AddressSetView getSelectedAddressesFromContext( protected static AddressSetView getSelectedAddressesFromContext(
DebuggerSectionActionContext context) { DebuggerSectionActionContext context) {
AddressSet sel = new AddressSet(); AddressSet sel = new AddressSet();
for (TraceSection section : getSelectedSectionsFromContext(context)) { for (TraceSection section : getSelectedSectionsFromContext(context, false)) {
sel.add(section.getRange()); sel.add(section.getRange());
} }
return sel; return sel;

View file

@ -15,22 +15,41 @@
*/ */
package ghidra.app.plugin.core.debug.gui.modules; package ghidra.app.plugin.core.debug.gui.modules;
import java.awt.Component;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import docking.ComponentProvider;
import docking.DefaultActionContext; import docking.DefaultActionContext;
import docking.widgets.table.GTable; import docking.widgets.table.GTable;
import ghidra.trace.model.modules.TraceModule;
public class DebuggerModuleActionContext extends DefaultActionContext { public class DebuggerModuleActionContext extends DefaultActionContext {
private final Set<ModuleRow> selectedModules; private final Set<TraceModule> selectedModules;
private final boolean forcedSingle;
private static Set<TraceModule> toModules(Collection<ModuleRow> rows) {
return rows.stream().map(ModuleRow::getModule).collect(Collectors.toUnmodifiableSet());
}
public DebuggerModuleActionContext(DebuggerModulesProvider provider, public DebuggerModuleActionContext(DebuggerModulesProvider provider,
Collection<ModuleRow> selected, GTable table) { Collection<ModuleRow> rows, GTable table) {
super(provider, selected, table); this(provider, toModules(rows), table, false);
this.selectedModules = Set.copyOf(selected);
} }
public Set<ModuleRow> getSelectedModules() { public DebuggerModuleActionContext(ComponentProvider provider, Set<TraceModule> selected,
Component sourceComponent, boolean forcedSingle) {
super(provider, selected, sourceComponent);
this.selectedModules = selected;
this.forcedSingle = forcedSingle;
}
public Set<TraceModule> getSelectedModules() {
return selectedModules; return selectedModules;
} }
public boolean isForcedSingle() {
return forcedSingle;
}
} }

View file

@ -306,12 +306,13 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
return Set.of(); return Set.of();
} }
protected static Set<TraceSection> getSelectedSections(ActionContext context) { protected static Set<TraceSection> getSelectedSections(ActionContext context,
boolean allowExpansion) {
if (context instanceof DebuggerModuleActionContext ctx) { if (context instanceof DebuggerModuleActionContext ctx) {
return DebuggerLegacyModulesPanel.getSelectedSectionsFromContext(ctx); return DebuggerLegacyModulesPanel.getSelectedSectionsFromContext(ctx);
} }
if (context instanceof DebuggerSectionActionContext ctx) { if (context instanceof DebuggerSectionActionContext ctx) {
return DebuggerLegacySectionsPanel.getSelectedSectionsFromContext(ctx); return DebuggerLegacySectionsPanel.getSelectedSectionsFromContext(ctx, allowExpansion);
} }
if (context instanceof DebuggerObjectActionContext ctx) { if (context instanceof DebuggerObjectActionContext ctx) {
return DebuggerModulesPanel.getSelectedSectionsFromContext(ctx); return DebuggerModulesPanel.getSelectedSectionsFromContext(ctx);
@ -372,7 +373,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
public ImportFromFileSystemAction() { public ImportFromFileSystemAction() {
super(plugin); super(plugin);
setPopupMenuData(new MenuData(new String[] { NAME }, GROUP)); setPopupMenuData(new MenuData(new String[] { NAME }, GROUP));
addLocalAction(this); tool.addAction(this);
setEnabled(true); setEnabled(true);
} }
@ -467,7 +468,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
SelectAddressesAction actionSelectAddresses; SelectAddressesAction actionSelectAddresses;
ImportFromFileSystemAction actionImportFromFileSystem; ImportFromFileSystemAction actionImportFromFileSystem;
ToggleDockingAction actionShowSectionsTable; ToggleDockingAction actionShowSectionsTable;
// TODO: Save the state of this toggle? Not really compelled.
ToggleDockingAction actionFilterSectionsByModules; ToggleDockingAction actionFilterSectionsByModules;
DockingAction actionSelectCurrent; DockingAction actionSelectCurrent;
@ -594,30 +594,32 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
.onAction(this::activatedMapManually) .onAction(this::activatedMapManually)
.buildAndInstallLocal(this); .buildAndInstallLocal(this);
actionMapModules = MapModulesAction.builder(plugin) actionMapModules = MapModulesAction.builder(plugin)
.enabledWhen(this::isContextNonEmpty) .enabledWhen(ctx -> isContextHasModules(ctx) && isContextNotForcedSingle(ctx))
.popupWhen(this::isContextNonEmpty) .popupWhen(ctx -> isContextHasModules(ctx) && isContextNotForcedSingle(ctx))
.onAction(this::activatedMapModules) .onAction(this::activatedMapModules)
.buildAndInstallLocal(this); .buildAndInstall(tool);
actionMapModuleTo = MapModuleToAction.builder(plugin) actionMapModuleTo = MapModuleToAction.builder(plugin)
.enabledWhen(ctx -> currentProgram != null && getSelectedModules(ctx).size() == 1) .enabledWhen(ctx -> currentProgram != null && getSelectedModules(ctx).size() == 1)
.popupWhen(ctx -> currentProgram != null && getSelectedModules(ctx).size() == 1) .popupWhen(ctx -> currentProgram != null && getSelectedModules(ctx).size() == 1)
.onAction(this::activatedMapModuleTo) .onAction(this::activatedMapModuleTo)
.buildAndInstallLocal(this); .buildAndInstall(tool);
actionMapSections = MapSectionsAction.builder(plugin) actionMapSections = MapSectionsAction.builder(plugin)
.enabledWhen(this::isContextNonEmpty) .enabledWhen(ctx -> isContextHasSections(ctx) && isContextNotForcedSingle(ctx))
.popupWhen(this::isContextNonEmpty) .popupWhen(ctx -> isContextHasSections(ctx) && isContextNotForcedSingle(ctx))
.onAction(this::activatedMapSections) .onAction(this::activatedMapSections)
.buildAndInstallLocal(this); .buildAndInstall(tool);
actionMapSectionTo = MapSectionToAction.builder(plugin) actionMapSectionTo = MapSectionToAction.builder(plugin)
.enabledWhen(ctx -> currentProgram != null && getSelectedSections(ctx).size() == 1) .enabledWhen(
.popupWhen(ctx -> currentProgram != null && getSelectedSections(ctx).size() == 1) ctx -> currentProgram != null && getSelectedSections(ctx, false).size() == 1)
.popupWhen(
ctx -> currentProgram != null && getSelectedSections(ctx, false).size() == 1)
.onAction(this::activatedMapSectionTo) .onAction(this::activatedMapSectionTo)
.buildAndInstallLocal(this); .buildAndInstall(tool);
actionMapSectionsTo = MapSectionsToAction.builder(plugin) actionMapSectionsTo = MapSectionsToAction.builder(plugin)
.enabledWhen(ctx -> currentProgram != null && isContextSectionsOfOneModule(ctx)) .enabledWhen(ctx -> currentProgram != null && isContextSectionsOfOneModule(ctx))
.popupWhen(ctx -> currentProgram != null && isContextSectionsOfOneModule(ctx)) .popupWhen(ctx -> currentProgram != null && isContextSectionsOfOneModule(ctx))
.onAction(this::activatedMapSectionsTo) .onAction(this::activatedMapSectionsTo)
.buildAndInstallLocal(this); .buildAndInstall(tool);
actionAutoMap = AutoMapAction.builder(plugin) actionAutoMap = AutoMapAction.builder(plugin)
.onActionStateChanged(this::changedAutoMapSpec) .onActionStateChanged(this::changedAutoMapSpec)
@ -653,12 +655,20 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
contextChanged(); 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) { private boolean isContextNonEmpty(ActionContext context) {
if (context instanceof DebuggerModuleActionContext ctx) { if (context instanceof DebuggerModuleActionContext ctx) {
return !ctx.getSelectedModules().isEmpty(); return !ctx.getSelectedModules().isEmpty();
} }
if (context instanceof DebuggerSectionActionContext ctx) { if (context instanceof DebuggerSectionActionContext ctx) {
return !ctx.getSelectedSections().isEmpty(); return !ctx.getSelectedSections(false).isEmpty();
} }
if (context instanceof DebuggerObjectActionContext ctx) { if (context instanceof DebuggerObjectActionContext ctx) {
return !ctx.getObjectValues().isEmpty(); return !ctx.getObjectValues().isEmpty();
@ -666,8 +676,18 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
return false; return false;
} }
private boolean isContextSectionsOfOneModule(ActionContext ignored) { private boolean isContextNotForcedSingle(ActionContext context) {
Set<TraceSection> sel = getSelectedSections(myActionContext); if (context instanceof DebuggerModuleActionContext ctx) {
return !ctx.isForcedSingle();
}
if (context instanceof DebuggerSectionActionContext ctx) {
return !ctx.isForcedSingle();
}
return true;
}
private boolean isContextSectionsOfOneModule(ActionContext context) {
Set<TraceSection> sel = getSelectedSections(context, false);
if (sel == null || sel.isEmpty()) { if (sel == null || sel.isEmpty()) {
return false; return false;
} }
@ -713,40 +733,40 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
tool.showComponentProvider(provider, true); tool.showComponentProvider(provider, true);
} }
private void activatedMapModules(ActionContext ignored) { private void activatedMapModules(ActionContext context) {
Set<TraceModule> sel = getSelectedModules(myActionContext); Set<TraceModule> sel = getSelectedModules(context);
if (sel == null || sel.isEmpty()) { if (sel == null || sel.isEmpty()) {
return; return;
} }
mapModules(sel); mapModules(sel);
} }
private void activatedMapModuleTo(ActionContext ignored) { private void activatedMapModuleTo(ActionContext context) {
Set<TraceModule> sel = getSelectedModules(myActionContext); Set<TraceModule> sel = getSelectedModules(context);
if (sel == null || sel.size() != 1) { if (sel == null || sel.size() != 1) {
return; return;
} }
mapModuleTo(sel.iterator().next()); mapModuleTo(sel.iterator().next());
} }
private void activatedMapSections(ActionContext ignored) { private void activatedMapSections(ActionContext context) {
Set<TraceSection> sel = getSelectedSections(myActionContext); Set<TraceSection> sel = getSelectedSections(context, true);
if (sel == null || sel.isEmpty()) { if (sel == null || sel.isEmpty()) {
return; return;
} }
mapSections(sel); mapSections(sel);
} }
private void activatedMapSectionsTo(ActionContext ignored) { private void activatedMapSectionsTo(ActionContext context) {
Set<TraceSection> sel = getSelectedSections(myActionContext); Set<TraceSection> sel = getSelectedSections(context, true);
if (sel == null || sel.isEmpty()) { if (sel == null || sel.isEmpty()) {
return; return;
} }
mapSectionsTo(sel); mapSectionsTo(sel);
} }
private void activatedMapSectionTo(ActionContext ignored) { private void activatedMapSectionTo(ActionContext context) {
Set<TraceSection> sel = getSelectedSections(myActionContext); Set<TraceSection> sel = getSelectedSections(context, false);
if (sel == null || sel.size() != 1) { if (sel == null || sel.size() != 1) {
return; return;
} }

View file

@ -15,32 +15,46 @@
*/ */
package ghidra.app.plugin.core.debug.gui.modules; package ghidra.app.plugin.core.debug.gui.modules;
import java.awt.Component;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import docking.ComponentProvider;
import docking.DefaultActionContext; import docking.DefaultActionContext;
import docking.widgets.table.GTable; import docking.widgets.table.GTable;
import ghidra.trace.model.modules.TraceSection;
public class DebuggerSectionActionContext extends DefaultActionContext { public class DebuggerSectionActionContext extends DefaultActionContext {
private final Set<SectionRow> selectedSections; private final Set<TraceSection> selectedSections;
//private final Set<ModuleRecord> involvedModules; private final boolean forcedSingle;
public DebuggerSectionActionContext(DebuggerModulesProvider provider, private static Set<TraceSection> toSections(Collection<SectionRow> rows) {
Collection<SectionRow> selected, GTable table) { return rows.stream().map(SectionRow::getSection).collect(Collectors.toUnmodifiableSet());
super(provider, selected, table);
this.selectedSections = Set.copyOf(selected);
/*
* this.involvedModules = Collections.unmodifiableSet(
* selected.stream().map(SectionRecord::getModule).collect(Collectors.toSet()));
*/
} }
public Set<SectionRow> getSelectedSections() { public DebuggerSectionActionContext(DebuggerModulesProvider provider,
Collection<SectionRow> rows, GTable table) {
this(provider, toSections(rows), table, false);
}
public DebuggerSectionActionContext(ComponentProvider provider,
Set<TraceSection> selected, Component sourceComponent, boolean forcedSingle) {
super(provider, selected, sourceComponent);
this.selectedSections = selected;
this.forcedSingle = forcedSingle;
}
public Set<TraceSection> getSelectedSections(boolean allowExpansion) {
if (forcedSingle && allowExpansion) {
return selectedSections.stream()
.flatMap(s -> s.getModule().getSections().stream())
.collect(Collectors.toUnmodifiableSet());
}
return selectedSections; return selectedSections;
} }
/* public boolean isForcedSingle() {
* // TODO: Do I need this? public Set<ModuleRecord> getInvolvedModules() { return return forcedSingle;
* involvedModules; } }
*/
} }