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;
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;
}
}
}

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
* 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 <em>not</em> 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 <em>not</em> 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);

View file

@ -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<RegionRow> regions = ctx.getSelectedRegions();
if (regions.size() != 1) {
return null;
}
return regions.iterator().next();
}
protected static Set<TraceMemoryRegion> 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<TraceMemoryRegion> 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();

View file

@ -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();

View file

@ -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<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,
Collection<RegionRow> selected, GTable table) {
super(provider, selected, table);
this.selectedRegions = Set.copyOf(selected);
Collection<RegionRow> rows, GTable table) {
this(provider, toRegions(rows), table, false);
}
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;
}
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));
}
protected static Set<TraceMemoryRegion> getSelectedRegions(DebuggerObjectActionContext ctx) {
return ctx == null ? null
: AbstractObjectsTableBasedPanel.getSelected(ctx, TraceObjectMemoryRegion.class)
.collect(Collectors.toSet());
protected Set<TraceMemoryRegion> getSelectedRegions(DebuggerObjectActionContext ctx) {
return ctx == null ? null : getSelected(ctx).collect(Collectors.toSet());
}
public DebuggerRegionsPanel(DebuggerRegionsProvider provider) {

View file

@ -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<TraceMemoryRegion> sel = getSelectedRegions(context);
return sel != null && sel.size() == 1;
}
private static Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) {
if (context instanceof DebuggerRegionActionContext legacyCtx) {
return DebuggerLegacyRegionsPanel.getSelectedRegions(legacyCtx);
private Set<TraceMemoryRegion> 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;
}

View file

@ -43,20 +43,6 @@ import ghidra.trace.model.target.TraceObjectInterface;
public abstract class AbstractObjectsTableBasedPanel<U extends TraceObjectInterface>
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 Class<U> objType;
@ -82,6 +68,19 @@ public abstract class AbstractObjectsTableBasedPanel<U extends TraceObjectInterf
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() {
return myActionContext;
}

View file

@ -46,17 +46,14 @@ public class DebuggerLegacyModulesPanel extends JPanel {
protected static Set<TraceModule> getSelectedModulesFromContext(
DebuggerModuleActionContext context) {
return context.getSelectedModules()
.stream()
.map(r -> r.getModule())
.collect(Collectors.toSet());
return context.getSelectedModules();
}
protected static Set<TraceSection> 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<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
implements EnumeratedTableColumn<ModuleTableColumns, ModuleRow> {
BASE("Base Address", Address.class, ModuleRow::getBase),

View file

@ -48,24 +48,21 @@ public class DebuggerLegacySectionsPanel extends JPanel {
protected static Set<TraceModule> getSelectedModulesFromContext(
DebuggerSectionActionContext context) {
return context.getSelectedSections()
return context.getSelectedSections(false)
.stream()
.map(r -> r.getModule())
.collect(Collectors.toSet());
}
protected static Set<TraceSection> 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;

View file

@ -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<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,
Collection<ModuleRow> selected, GTable table) {
super(provider, selected, table);
this.selectedModules = Set.copyOf(selected);
Collection<ModuleRow> rows, GTable table) {
this(provider, toModules(rows), table, false);
}
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;
}
public boolean isForcedSingle() {
return forcedSingle;
}
}

View file

@ -306,12 +306,13 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
return Set.of();
}
protected static Set<TraceSection> getSelectedSections(ActionContext context) {
protected static Set<TraceSection> 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<TraceSection> 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<TraceSection> 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<TraceModule> sel = getSelectedModules(myActionContext);
private void activatedMapModules(ActionContext context) {
Set<TraceModule> sel = getSelectedModules(context);
if (sel == null || sel.isEmpty()) {
return;
}
mapModules(sel);
}
private void activatedMapModuleTo(ActionContext ignored) {
Set<TraceModule> sel = getSelectedModules(myActionContext);
private void activatedMapModuleTo(ActionContext context) {
Set<TraceModule> sel = getSelectedModules(context);
if (sel == null || sel.size() != 1) {
return;
}
mapModuleTo(sel.iterator().next());
}
private void activatedMapSections(ActionContext ignored) {
Set<TraceSection> sel = getSelectedSections(myActionContext);
private void activatedMapSections(ActionContext context) {
Set<TraceSection> sel = getSelectedSections(context, true);
if (sel == null || sel.isEmpty()) {
return;
}
mapSections(sel);
}
private void activatedMapSectionsTo(ActionContext ignored) {
Set<TraceSection> sel = getSelectedSections(myActionContext);
private void activatedMapSectionsTo(ActionContext context) {
Set<TraceSection> sel = getSelectedSections(context, true);
if (sel == null || sel.isEmpty()) {
return;
}
mapSectionsTo(sel);
}
private void activatedMapSectionTo(ActionContext ignored) {
Set<TraceSection> sel = getSelectedSections(myActionContext);
private void activatedMapSectionTo(ActionContext context) {
Set<TraceSection> sel = getSelectedSections(context, false);
if (sel == null || sel.size() != 1) {
return;
}

View file

@ -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<SectionRow> selectedSections;
//private final Set<ModuleRecord> involvedModules;
private final Set<TraceSection> selectedSections;
private final boolean forcedSingle;
public DebuggerSectionActionContext(DebuggerModulesProvider provider,
Collection<SectionRow> 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<TraceSection> toSections(Collection<SectionRow> rows) {
return rows.stream().map(SectionRow::getSection).collect(Collectors.toUnmodifiableSet());
}
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;
}
/*
* // TODO: Do I need this? public Set<ModuleRecord> getInvolvedModules() { return
* involvedModules; }
*/
public boolean isForcedSingle() {
return forcedSingle;
}
}