Merge branch 'GT-3485_action_context'

Conflicts:
	Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java
This commit is contained in:
ghidravore
2020-03-16 13:22:53 -04:00
42 changed files with 628 additions and 631 deletions
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,25 +15,26 @@
*/ */
package ghidra.app.context; package ghidra.app.context;
import docking.ComponentProvider;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import docking.ComponentProvider;
public class ListingActionContext extends NavigatableActionContext { public class ListingActionContext extends NavigatableActionContext {
public ListingActionContext(ComponentProvider provider, Navigatable navigatable) { public ListingActionContext(ComponentProvider provider, Navigatable navigatable) {
super(provider, navigatable); super(provider, navigatable);
} }
public ListingActionContext(ComponentProvider provider, Navigatable navigatable, ProgramLocation location) { public ListingActionContext(ComponentProvider provider, Navigatable navigatable,
ProgramLocation location) {
super(provider, navigatable, location); super(provider, navigatable, location);
} }
public ListingActionContext(ComponentProvider provider, Navigatable navigatable, public ListingActionContext(ComponentProvider provider, Navigatable navigatable,
Program program, ProgramLocation location, ProgramSelection selection, Program program, ProgramLocation location, ProgramSelection selection,
ProgramSelection highlight ) { ProgramSelection highlight) {
super( provider, navigatable, program, location, selection, highlight ); super(provider, navigatable, program, location, selection, highlight);
} }
} }
@@ -25,45 +25,32 @@ public abstract class NavigatableContextAction extends DockingAction {
public NavigatableContextAction(String name, String owner) { public NavigatableContextAction(String name, String owner) {
super(name, owner); super(name, owner);
setSupportsDefaultToolContext(true);
} }
public NavigatableContextAction(String name, String owner, KeyBindingType type) { public NavigatableContextAction(String name, String owner, KeyBindingType type) {
super(name, owner, type); super(name, owner, type);
setSupportsDefaultToolContext(true);
} }
@Override @Override
public boolean isEnabledForContext(ActionContext context) { public boolean isEnabledForContext(ActionContext context) {
NavigatableActionContext appropriateContext = getAppropriateContext(context); if (context instanceof NavigatableActionContext) {
if (appropriateContext == null) { return isEnabledForContext((NavigatableActionContext) context);
return false;
} }
return isEnabledForContext(appropriateContext); return false;
} }
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
actionPerformed(getAppropriateContext(context)); if (context instanceof NavigatableActionContext) {
} actionPerformed((NavigatableActionContext) context);
private NavigatableActionContext getAppropriateContext(ActionContext context) {
if (context instanceof NavigatableActionContext &&
isValidNavigationContext((NavigatableActionContext) context)) {
return (NavigatableActionContext) context;
} }
ActionContext globalContext = context.getGlobalContext();
if (globalContext instanceof NavigatableActionContext) {
return (NavigatableActionContext) globalContext;
}
return null;
} }
@Override @Override
public final boolean isValidContext(ActionContext context) { public final boolean isValidContext(ActionContext context) {
return true; return context instanceof NavigatableActionContext;
}
protected boolean isValidNavigationContext(NavigatableActionContext context) {
return true;
} }
@Override @Override
@@ -30,23 +30,14 @@ public abstract class NextRangeAction extends NavigatableContextAction {
private PluginTool tool; private PluginTool tool;
private NavigationOptions navOptions; private NavigationOptions navOptions;
public NextRangeAction(PluginTool tool, String name, String owner, NavigationOptions navOptions) { public NextRangeAction(PluginTool tool, String name, String owner,
NavigationOptions navOptions) {
super(name, owner); super(name, owner);
this.tool = tool; this.tool = tool;
this.navOptions = navOptions; this.navOptions = navOptions;
setEnabled(false); setEnabled(false);
} }
@Override
protected boolean isValidNavigationContext(NavigatableActionContext context) {
//
// We want the nav actions to work in the current view that supports this, which right
// now is the ListingActionContext. If the current context does not support that, then
// we will be called later with the global context, which does support navigation.
//
return context instanceof ListingActionContext;
}
@Override @Override
public boolean isEnabledForContext(NavigatableActionContext context) { public boolean isEnabledForContext(NavigatableActionContext context) {
Address currentAddress = context.getAddress(); Address currentAddress = context.getAddress();
@@ -37,16 +37,6 @@ public abstract class PreviousRangeAction extends NavigatableContextAction {
setEnabled(false); setEnabled(false);
} }
@Override
protected boolean isValidNavigationContext(NavigatableActionContext context) {
//
// We want the nav actions to work in the current view that supports this, which right
// now is the ListingActionContext. If the current context does not support that, then
// we will be called later with the global context, which does support navigation.
//
return context instanceof ListingActionContext;
}
@Override @Override
public void actionPerformed(NavigatableActionContext context) { public void actionPerformed(NavigatableActionContext context) {
Address goToAddress = getGoToAddress(context); Address goToAddress = getGoToAddress(context);
@@ -22,6 +22,7 @@ import docking.ActionContext;
import docking.ComponentProvider; import docking.ComponentProvider;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.MenuData; import docking.action.MenuData;
import docking.action.builder.ActionBuilder;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.context.*; import ghidra.app.context.*;
@@ -96,33 +97,28 @@ public class FindPossibleReferencesPlugin extends Plugin {
} }
private void createActions() { private void createActions() {
action = new ListingContextAction(SEARCH_DIRECT_REFS_ACTION_NAME, getName()) { action = new ActionBuilder(SEARCH_DIRECT_REFS_ACTION_NAME, getName())
@Override .menuPath(ToolConstants.MENU_SEARCH, "For Direct References")
protected void actionPerformed(ListingActionContext context) { .menuGroup("search for")
findReferences(context); .supportsDefaultToolContext(true)
} .helpLocation(new HelpLocation(HelpTopics.SEARCH, SEARCH_DIRECT_REFS_ACTION_NAME))
.description(getPluginDescription().getDescription())
.withContext(ListingActionContext.class)
.onAction(this::findReferences)
.enabledWhen(this::hasCorrectAddressSize)
.buildAndInstall(tool);
@Override }
protected boolean isEnabledForContext(ListingActionContext context) {
int size =
context.getProgram().getAddressFactory().getDefaultAddressSpace().getSize();
if ((size == 64) || (size == 32) || (size == 24) || (size == 16) || (size == 20) ||
(size == 21)) {
return true;
}
return false;
}
};
action.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, SEARCH_DIRECT_REFS_ACTION_NAME));
action.setMenuBarData(
new MenuData(new String[] { ToolConstants.MENU_SEARCH, "For Direct References" }, null,
"search for"));
action.setDescription(getPluginDescription().getDescription()); private boolean hasCorrectAddressSize(ListingActionContext context) {
//enableOnLocation(action); int size =
tool.addAction(action); context.getProgram().getAddressFactory().getDefaultAddressSpace().getSize();
if ((size == 64) || (size == 32) || (size == 24) || (size == 16) || (size == 20) ||
} // end of createActions() (size == 21)) {
return true;
}
return false;
}
private void createLocalActions(ProgramLocationActionContext context, ComponentProvider p, private void createLocalActions(ProgramLocationActionContext context, ComponentProvider p,
FindReferencesTableModel model) { FindReferencesTableModel model) {
@@ -196,8 +192,10 @@ public class FindPossibleReferencesPlugin extends Plugin {
"Could not find memory associated with " + fromAddr); "Could not find memory associated with " + fromAddr);
return; return;
} }
if (currentProgram.getMemory().getBlock( if (currentProgram.getMemory()
fromAddr).getType() == MemoryBlockType.BIT_MAPPED) { .getBlock(
fromAddr)
.getType() == MemoryBlockType.BIT_MAPPED) {
Msg.showWarn(getClass(), null, "Search For Direct References", Msg.showWarn(getClass(), null, "Search For Direct References",
"Cannot search for direct references on bit memory!"); "Cannot search for direct references on bit memory!");
return; return;
@@ -30,6 +30,7 @@ public class ClearSelectionAction extends CodeViewerContextAction {
public ClearSelectionAction(String owner) { public ClearSelectionAction(String owner) {
super("Clear Selection", owner); super("Clear Selection", owner);
setSupportsDefaultToolContext(true);
setMenuBarData(new MenuData( setMenuBarData(new MenuData(
new String[] { ToolConstants.MENU_SELECTION, "&Clear Selection" }, null, "Select")); new String[] { ToolConstants.MENU_SELECTION, "&Clear Selection" }, null, "Select"));
@@ -23,8 +23,9 @@ import javax.swing.KeyStroke;
import docking.action.KeyBindingData; import docking.action.KeyBindingData;
import docking.action.MenuData; import docking.action.MenuData;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.context.NavigatableContextAction;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@@ -33,7 +34,7 @@ import ghidra.program.model.listing.*;
import ghidra.program.util.FunctionSignatureFieldLocation; import ghidra.program.util.FunctionSignatureFieldLocation;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
public class GotoNextFunctionAction extends CodeViewerContextAction { public class GotoNextFunctionAction extends NavigatableContextAction {
private PluginTool tool; private PluginTool tool;
@@ -67,7 +68,7 @@ public class GotoNextFunctionAction extends CodeViewerContextAction {
} }
@Override @Override
protected void actionPerformed(CodeViewerActionContext context) { protected void actionPerformed(NavigatableActionContext context) {
Address address = context.getAddress(); Address address = context.getAddress();
Program program = context.getProgram(); Program program = context.getProgram();
Function function = getNextFunction(program, address); Function function = getNextFunction(program, address);
@@ -79,7 +80,7 @@ public class GotoNextFunctionAction extends CodeViewerContextAction {
if (service != null) { if (service != null) {
FunctionSignatureFieldLocation location = FunctionSignatureFieldLocation location =
new FunctionSignatureFieldLocation(program, function.getEntryPoint(), null, 0, new FunctionSignatureFieldLocation(program, function.getEntryPoint(), null, 0,
function.getPrototypeString(false, false)); function.getPrototypeString(false, false));
Navigatable navigatable = context.getNavigatable(); Navigatable navigatable = context.getNavigatable();
service.goTo(navigatable, location, navigatable.getProgram()); service.goTo(navigatable, location, navigatable.getProgram());
@@ -23,8 +23,9 @@ import javax.swing.KeyStroke;
import docking.action.KeyBindingData; import docking.action.KeyBindingData;
import docking.action.MenuData; import docking.action.MenuData;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.context.NavigatableContextAction;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@@ -33,7 +34,7 @@ import ghidra.program.model.listing.*;
import ghidra.program.util.FunctionSignatureFieldLocation; import ghidra.program.util.FunctionSignatureFieldLocation;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
public class GotoPreviousFunctionAction extends CodeViewerContextAction { public class GotoPreviousFunctionAction extends NavigatableContextAction {
private PluginTool tool; private PluginTool tool;
@@ -67,7 +68,7 @@ public class GotoPreviousFunctionAction extends CodeViewerContextAction {
} }
@Override @Override
protected void actionPerformed(CodeViewerActionContext context) { protected void actionPerformed(NavigatableActionContext context) {
Address address = context.getAddress(); Address address = context.getAddress();
Program program = context.getProgram(); Program program = context.getProgram();
Function function = getPreviousFunction(program, address); Function function = getPreviousFunction(program, address);
@@ -15,10 +15,6 @@
*/ */
package ghidra.app.plugin.core.codebrowser.actions; package ghidra.app.plugin.core.codebrowser.actions;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
import java.awt.event.InputEvent; import java.awt.event.InputEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
@@ -26,27 +22,32 @@ import docking.ActionContext;
import docking.action.KeyBindingData; import docking.action.KeyBindingData;
import docking.action.MenuData; import docking.action.MenuData;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
/** /**
* Action for adding all fields to the current format. * Action for adding all fields to the current format.
*/ */
public class SelectAllAction extends CodeViewerContextAction { public class SelectAllAction extends CodeViewerContextAction {
public SelectAllAction(String owner) {
super("Select All", owner);
setMenuBarData(
new MenuData(
new String[] {ToolConstants.MENU_SELECTION, "&All in View" },null,"Select" ) );
setKeyBindingData( new KeyBindingData( public SelectAllAction(String owner) {
KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK ) ); super("Select All", owner);
// this is in the main tool menu, so make it a tool action
setSupportsDefaultToolContext(true);
setMenuBarData(
new MenuData(
new String[] { ToolConstants.MENU_SELECTION, "&All in View" }, null, "Select"));
setKeyBindingData(new KeyBindingData(
KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName())); setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
} }
@Override
public void actionPerformed(ActionContext context) {
CodeViewerProvider provider = (CodeViewerProvider) context.getComponentProvider();
provider.selectAll();
}
}
@Override
public void actionPerformed(ActionContext context) {
CodeViewerProvider provider = (CodeViewerProvider) context.getComponentProvider();
provider.selectAll();
}
}
@@ -14,14 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.plugin.core.codebrowser.actions; package ghidra.app.plugin.core.codebrowser.actions;
import docking.ActionContext;
import docking.action.MenuData;
import docking.tool.ToolConstants;
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext; import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import docking.ActionContext;
import docking.action.MenuData;
import docking.tool.ToolConstants;
/** /**
* Action for changing the selection to the complement of all the currently * Action for changing the selection to the complement of all the currently
@@ -29,29 +30,33 @@ import docking.tool.ToolConstants;
*/ */
public class SelectComplementAction extends CodeViewerContextAction { public class SelectComplementAction extends CodeViewerContextAction {
public SelectComplementAction(String owner) { public SelectComplementAction(String owner) {
super("Select Complement", owner); super("Select Complement", owner);
setMenuBarData( new MenuData(
new String[]{ToolConstants.MENU_SELECTION, "&Complement" },
null,
"Select" ) );
setEnabled(false); // this is in the main tool menu, so make it a tool action
setSupportsDefaultToolContext(true);
setMenuBarData(new MenuData(
new String[] { ToolConstants.MENU_SELECTION, "&Complement" },
null,
"Select"));
setEnabled(false);
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName())); setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
} }
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
CodeViewerProvider provider = (CodeViewerProvider) context.getComponentProvider(); CodeViewerProvider provider = (CodeViewerProvider) context.getComponentProvider();
provider.selectComplement(); provider.selectComplement();
} }
@Override
public boolean isEnabledForContext(CodeViewerActionContext context) { @Override
ProgramSelection selection = context.getSelection(); public boolean isEnabledForContext(CodeViewerActionContext context) {
if (selection != null && selection.getInteriorSelection() != null) { ProgramSelection selection = context.getSelection();
return false; if (selection != null && selection.getInteriorSelection() != null) {
} return false;
return true; }
} return true;
}
} }
@@ -245,9 +245,10 @@ public class InstructionSearchPlugin extends ProgramPlugin {
} }
@Override @Override
protected boolean isValidNavigationContext(NavigatableActionContext context) { protected boolean isEnabledForContext(NavigatableActionContext context) {
return !(context instanceof RestrictedAddressSetContext); return !(context instanceof RestrictedAddressSetContext);
} }
}; };
searchAction.setHelpLocation(new HelpLocation("Search", "Instruction_Pattern_Search")); searchAction.setHelpLocation(new HelpLocation("Search", "Instruction_Pattern_Search"));
searchAction.setMenuBarData( searchAction.setMenuBarData(
@@ -17,9 +17,10 @@ package ghidra.app.plugin.core.navigation;
import javax.swing.*; import javax.swing.*;
import docking.action.*; import docking.action.KeyBindingData;
import docking.action.ToolBarData;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.context.ListingActionContext; import ghidra.app.context.NavigatableActionContext;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext; import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
import ghidra.app.plugin.core.codebrowser.actions.CodeViewerContextAction; import ghidra.app.plugin.core.codebrowser.actions.CodeViewerContextAction;
@@ -41,16 +42,12 @@ public abstract class AbstractNextPreviousAction extends CodeViewerContextAction
public AbstractNextPreviousAction(PluginTool tool, String name, String owner, String subGroup) { public AbstractNextPreviousAction(PluginTool tool, String name, String owner, String subGroup) {
super(name, owner); super(name, owner);
this.tool = tool; this.tool = tool;
setSupportsDefaultToolContext(true);
ToolBarData toolBarData = ToolBarData toolBarData =
new ToolBarData(getIcon(), ToolConstants.TOOLBAR_GROUP_FOUR); new ToolBarData(getIcon(), ToolConstants.TOOLBAR_GROUP_FOUR);
toolBarData.setToolBarSubGroup(subGroup); toolBarData.setToolBarSubGroup(subGroup);
setToolBarData(toolBarData); setToolBarData(toolBarData);
MenuData menuData =
new MenuData(new String[] { ToolConstants.MENU_NAVIGATION, getMenuName() }, getIcon(),
ToolConstants.MENU_GROUP_NEXT_CODE_UNIT_NAV);
menuData.setMenuSubGroup(subGroup);
setMenuBarData(menuData);
setKeyBindingData(new KeyBindingData(getKeyStroke())); setKeyBindingData(new KeyBindingData(getKeyStroke()));
setHelpLocation(new HelpLocation(HelpTopics.NAVIGATION, name)); setHelpLocation(new HelpLocation(HelpTopics.NAVIGATION, name));
setDescription(getDescriptionString()); setDescription(getDescriptionString());
@@ -90,7 +87,7 @@ public abstract class AbstractNextPreviousAction extends CodeViewerContextAction
} }
} }
private void gotoAddress(ListingActionContext actionContext, Address address) { private void gotoAddress(NavigatableActionContext actionContext, Address address) {
if (address == null) { if (address == null) {
tool.setStatusInfo("Unable to locate another \"" + getNavigationTypeName() + tool.setStatusInfo("Unable to locate another \"" + getNavigationTypeName() +
"\" past the current range, in the current direction."); "\" past the current range, in the current direction.");
@@ -23,9 +23,9 @@ import javax.swing.Icon;
import docking.action.*; import docking.action.*;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.context.NavigatableContextAction;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
import ghidra.app.plugin.core.codebrowser.actions.CodeViewerContextAction;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
@@ -119,18 +119,13 @@ public class NextPrevCodeUnitPlugin extends Plugin {
bookmarkAction.setDirection(searchForward); bookmarkAction.setDirection(searchForward);
} }
private class ToggleDirectionAction extends CodeViewerContextAction { private class ToggleDirectionAction extends NavigatableContextAction {
Icon forwardIcon = ResourceManager.loadImage("images/down.png"); Icon forwardIcon = ResourceManager.loadImage("images/down.png");
Icon backwardIcon = ResourceManager.loadImage("images/up.png"); Icon backwardIcon = ResourceManager.loadImage("images/up.png");
private boolean isForward = true; private boolean isForward = true;
ToggleDirectionAction(String subGroup) { ToggleDirectionAction(String subGroup) {
super("Toggle Search Direction", NextPrevCodeUnitPlugin.this.getName()); super("Toggle Search Direction", NextPrevCodeUnitPlugin.this.getName());
MenuData menuData =
new MenuData(new String[] { ToolConstants.MENU_NAVIGATION, getName() },
forwardIcon, ToolConstants.MENU_GROUP_NEXT_CODE_UNIT_NAV);
menuData.setMenuSubGroup(subGroup);
setMenuBarData(menuData);
setToolBarData(new ToolBarData(forwardIcon, setToolBarData(new ToolBarData(forwardIcon,
ToolConstants.TOOLBAR_GROUP_FOUR, subGroup)); ToolConstants.TOOLBAR_GROUP_FOUR, subGroup));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK | setKeyBindingData(new KeyBindingData(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK |
@@ -144,7 +139,7 @@ public class NextPrevCodeUnitPlugin extends Plugin {
} }
@Override @Override
public void actionPerformed(CodeViewerActionContext context) { public void actionPerformed(NavigatableActionContext context) {
isForward = !isForward; isForward = !isForward;
getMenuBarData().setIcon(isForward ? forwardIcon : backwardIcon); getMenuBarData().setIcon(isForward ? forwardIcon : backwardIcon);
getToolBarData().setIcon(isForward ? forwardIcon : backwardIcon); getToolBarData().setIcon(isForward ? forwardIcon : backwardIcon);
@@ -29,6 +29,7 @@ import docking.menu.MultiStateDockingAction;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingActionContext;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext; import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
@@ -56,18 +57,13 @@ public class NextPreviousBookmarkAction extends MultiStateDockingAction<String>
public NextPreviousBookmarkAction(PluginTool tool, String owner, String subGroup) { public NextPreviousBookmarkAction(PluginTool tool, String owner, String subGroup) {
super("Next Bookmark", owner); super("Next Bookmark", owner);
this.tool = tool; this.tool = tool;
setSupportsDefaultToolContext(true);
ToolBarData toolBarData = ToolBarData toolBarData =
new ToolBarData(bookmarkIcon, ToolConstants.TOOLBAR_GROUP_FOUR); new ToolBarData(bookmarkIcon, ToolConstants.TOOLBAR_GROUP_FOUR);
toolBarData.setToolBarSubGroup(subGroup); toolBarData.setToolBarSubGroup(subGroup);
setToolBarData(toolBarData); setToolBarData(toolBarData);
MenuData menuData =
new MenuData(new String[] { ToolConstants.MENU_NAVIGATION, getMenuName() },
bookmarkIcon, ToolConstants.MENU_GROUP_NEXT_CODE_UNIT_NAV);
menuData.setMenuSubGroup(subGroup);
setMenuBarData(menuData);
setKeyBindingData(new KeyBindingData(getKeyStroke())); setKeyBindingData(new KeyBindingData(getKeyStroke()));
setHelpLocation(new HelpLocation(HelpTopics.NAVIGATION, getName())); setHelpLocation(new HelpLocation(HelpTopics.NAVIGATION, getName()));
@@ -93,19 +89,6 @@ public class NextPreviousBookmarkAction extends MultiStateDockingAction<String>
addActionState(warning); addActionState(warning);
addActionState(custom); addActionState(custom);
/*BookmarkPlugin bookmarkPlugin = new BookmarkPlugin(tool);
Program program = bookmarkPlugin.getCurrentProgram();
BookmarkManager bookmarkManager = program.getBookmarkManager();
BookmarkType[] bookmarkTypes = bookmarkManager.getBookmarkTypes();
for (BookmarkType bookmarkType : bookmarkTypes) {
ActionState<String> tempBookmarkType =
new ActionState<String>(bookmarkType.getTypeString(), bookmarkUnknownIcon,
bookmarkType.getTypeString());
addActionState(tempBookmarkType);
}*/
setCurrentActionState(allBookmarks); // default setCurrentActionState(allBookmarks); // default
} }
@@ -119,8 +102,8 @@ public class NextPreviousBookmarkAction extends MultiStateDockingAction<String>
@Override @Override
protected void doActionPerformed(ActionContext context) { protected void doActionPerformed(ActionContext context) {
if (context instanceof ListingActionContext) { if (context instanceof NavigatableActionContext) {
gotoNextPrevious((ListingActionContext) context, this.getCurrentUserData()); gotoNextPrevious((NavigatableActionContext) context, this.getCurrentUserData());
} }
} }
@@ -212,7 +195,8 @@ public class NextPreviousBookmarkAction extends MultiStateDockingAction<String>
//================================================================================================== //==================================================================================================
// AbstractNextPreviousAction Methods // AbstractNextPreviousAction Methods
//================================================================================================== //==================================================================================================
private void gotoNextPrevious(final ListingActionContext context, final String bookmarkType) { private void gotoNextPrevious(final NavigatableActionContext context,
final String bookmarkType) {
final Address address = final Address address =
isForward ? getNextAddress(context.getProgram(), context.getAddress(), bookmarkType) isForward ? getNextAddress(context.getProgram(), context.getAddress(), bookmarkType)
: getPreviousAddress(context.getProgram(), context.getAddress(), bookmarkType); : getPreviousAddress(context.getProgram(), context.getAddress(), bookmarkType);
@@ -225,7 +209,7 @@ public class NextPreviousBookmarkAction extends MultiStateDockingAction<String>
}); });
} }
private void gotoAddress(ListingActionContext listingActionContext, Address address) { private void gotoAddress(NavigatableActionContext listingActionContext, Address address) {
if (address == null) { if (address == null) {
tool.setStatusInfo("Unable to locate another " + getNavigationTypeName() + tool.setStatusInfo("Unable to locate another " + getNavigationTypeName() +
" past the current range, in the current direction."); " past the current range, in the current direction.");
@@ -264,40 +248,14 @@ public class NextPreviousBookmarkAction extends MultiStateDockingAction<String>
//================================================================================================== //==================================================================================================
// CodeViewerContextAction Methods // CodeViewerContextAction Methods
//================================================================================================== //==================================================================================================
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!(context instanceof CodeViewerActionContext)) {
return false;
}
return isEnabledForContext((CodeViewerActionContext) context);
}
@Override @Override
public boolean isValidContext(ActionContext context) { public boolean isValidContext(ActionContext context) {
if (!(context instanceof CodeViewerActionContext)) { return context instanceof ListingActionContext;
return false;
}
return isValidContext((CodeViewerActionContext) context);
} }
@Override @Override
public boolean isAddToPopup(ActionContext context) { public boolean isEnabledForContext(ActionContext context) {
if (!(context instanceof CodeViewerActionContext)) { return context instanceof ListingActionContext;
return false;
}
return isAddToPopup((CodeViewerActionContext) context);
}
protected boolean isValidContext(CodeViewerActionContext context) {
return true;
}
protected boolean isEnabledForContext(CodeViewerActionContext context) {
return true;
}
protected boolean isAddToPopup(CodeViewerActionContext context) {
return isEnabledForContext(context);
} }
@Override @Override
@@ -17,7 +17,6 @@ package ghidra.app.plugin.core.progmgr;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyEditor; import java.beans.PropertyEditor;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
@@ -26,16 +25,12 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.swing.Icon; import docking.action.DockingAction;
import docking.action.builder.ActionBuilder;
import docking.ActionContext;
import docking.DockingUtils;
import docking.action.*;
import docking.options.editor.*; import docking.options.editor.*;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.context.ProgramActionContext; import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
import ghidra.app.events.*; import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.ProgramManager; import ghidra.app.services.ProgramManager;
@@ -59,7 +54,6 @@ import ghidra.program.util.*;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.exception.NotFoundException; import ghidra.util.exception.NotFoundException;
import ghidra.util.task.TaskLauncher; import ghidra.util.task.TaskLauncher;
import resources.ResourceManager;
//@formatter:off //@formatter:off
@PluginInfo( @PluginInfo(
@@ -86,13 +80,15 @@ import resources.ResourceManager;
//@formatter:on //@formatter:on
public class ProgramManagerPlugin extends Plugin implements ProgramManager { public class ProgramManagerPlugin extends Plugin implements ProgramManager {
private static final String SAVE_GROUP = "DomainObjectSave";
private static final String OPEN_GROUP = "DomainObjectOpen";
private MultiProgramManager programMgr; private MultiProgramManager programMgr;
private ProgramSaveManager programSaveMgr; private ProgramSaveManager programSaveMgr;
private DockingAction openAction; private DockingAction openAction;
private DockingAction saveAllAction; private DockingAction saveAllAction;
private ProgramContextAction closeAction; private DockingAction closeAction;
private ProgramContextAction saveAction; private DockingAction saveAction;
private ProgramContextAction saveAsAction; private DockingAction saveAsAction;
private DockingAction optionsAction; private DockingAction optionsAction;
private DockingAction closeOthersAction; private DockingAction closeOthersAction;
private DockingAction closeAllAction; private DockingAction closeAllAction;
@@ -101,7 +97,6 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
private boolean locked = false; private boolean locked = false;
private UndoAction undoAction; private UndoAction undoAction;
private RedoAction redoAction; private RedoAction redoAction;
private ProgramActionContext lastProgramContext;
private ProgramLocation currentLocation; private ProgramLocation currentLocation;
public ProgramManagerPlugin(PluginTool tool) { public ProgramManagerPlugin(PluginTool tool) {
@@ -527,206 +522,72 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
int subMenuGroupOrder = 1; int subMenuGroupOrder = 1;
openAction = new DockingAction("Open File", getName()) { openAction = new ActionBuilder("Open File", getName())
@Override .onAction(c -> open())
public void actionPerformed(ActionContext context) { .menuPath(ToolConstants.MENU_FILE, "&Open...")
open(); .menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
} .keyBinding("ctrl O")
}; .buildAndInstall(tool);
MenuData menuData =
new MenuData(new String[] { ToolConstants.MENU_FILE, "&Open..." }, "DomainObjectOpen");
menuData.setMenuSubGroup(Integer.toString(subMenuGroupOrder++));
openAction.setMenuBarData(menuData);
openAction.setKeyBindingData(
new KeyBindingData(KeyEvent.VK_O, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
closeAction = new ProgramContextAction("Close File", getName()) { closeAction = new ActionBuilder("Close File", getName())
@Override .menuPath(ToolConstants.MENU_FILE, "&Close")
public void actionPerformed(ProgramActionContext programContext) { .menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
closeProgram(programContext.getProgram(), false); .withContext(ProgramActionContext.class)
} .onAction(c -> closeProgram(c.getProgram(), false))
.buildAndInstall(tool);
@Override closeOthersAction = new ActionBuilder("Close Others", getName())
public boolean isValidContext(ActionContext context) { .menuPath(ToolConstants.MENU_FILE, "Close &Others")
if (!super.isValidContext(context)) { .menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
getMenuBarData().setMenuItemName("&Close"); .enabled(false)
setDescription("Close Program"); .onAction(c -> closeOtherPrograms(false))
return false; .buildAndInstall(tool);
}
return true;
}
@Override closeAllAction = new ActionBuilder("Close All", getName())
public boolean isEnabledForContext(ProgramActionContext context) { .menuPath(ToolConstants.MENU_FILE, "Close &All")
Program program = context.getProgram(); .menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
String programName = "'" + program.getDomainFile().getName() + "'"; .onAction(c -> closeAllPrograms(false))
getMenuBarData().setMenuItemName("&Close " + programName); .enabled(false)
setDescription("<html>Close " + HTMLUtilities.escapeHTML(programName)); .buildAndInstall(tool);
return true;
}
};
String[] closeActionMenuPath = { ToolConstants.MENU_FILE, "&Close" };
menuData = new MenuData(closeActionMenuPath, null, "DomainObjectOpen");
menuData.setMenuSubGroup(Integer.toString(subMenuGroupOrder++));
closeAction.setMenuBarData(menuData);
closeOthersAction = new DockingAction("Close Others", getName()) { saveAction = new ActionBuilder("Save File", "&Save")
@Override .menuPath(ToolConstants.MENU_FILE, "Close &All")
public void actionPerformed(ActionContext context) { .description("Save Program")
closeOtherPrograms(false); .menuGroup(SAVE_GROUP, Integer.toString(subMenuGroupOrder++))
} .menuIcon(null)
}; .toolBarIcon("images/disk.png")
closeOthersAction.setEnabled(false); .toolBarGroup(ToolConstants.TOOLBAR_GROUP_ONE)
String[] menuPath = { ToolConstants.MENU_FILE, "Close &Others" }; .keyBinding("ctrl S")
menuData = new MenuData(menuPath, null, "DomainObjectOpen"); .withContext(ProgramActionContext.class)
menuData.setMenuSubGroup(Integer.toString(subMenuGroupOrder++)); .enabledWhen(c -> c.getProgram().isChanged())
closeOthersAction.setMenuBarData(menuData); .onAction(c -> programSaveMgr.saveProgram(c.getProgram()))
.buildAndInstall(tool);
closeAllAction = new DockingAction("Close All", getName()) { saveAsAction = new ActionBuilder("Save As File", getName())
@Override .menuPath(ToolConstants.MENU_FILE, "Save &As...")
public void actionPerformed(ActionContext context) { .menuGroup(SAVE_GROUP, Integer.toString(subMenuGroupOrder++))
closeAllPrograms(false); .withContext(ProgramActionContext.class)
} .onAction(c -> programSaveMgr.saveAs(c.getProgram()))
}; .buildAndInstall(tool);
closeAllAction.setEnabled(false);
String[] nenuPath = { ToolConstants.MENU_FILE, "Close &All" };
menuData = new MenuData(nenuPath, null, "DomainObjectOpen");
menuData.setMenuSubGroup(Integer.toString(subMenuGroupOrder++));
closeAllAction.setMenuBarData(menuData);
saveAction = new ProgramContextAction("Save File", getName()) { saveAllAction = new ActionBuilder("Save All Files", getName())
@Override .menuPath(ToolConstants.MENU_FILE, "Save All")
public void actionPerformed(ProgramActionContext programContext) { .description("Save All Programs")
programSaveMgr.saveProgram(programContext.getProgram()); .menuGroup(SAVE_GROUP, Integer.toString(subMenuGroupOrder++))
// setEnabled(false); .onAction(c -> programSaveMgr.saveChangedPrograms())
} .buildAndInstall(tool);
@Override optionsAction = new ActionBuilder("Program Options", getName())
public boolean isValidContext(ActionContext context) { .menuPath(ToolConstants.MENU_EDIT, "P&rogram Options...")
lastProgramContext = null; .description("Edit Options for current program")
if (!super.isValidContext(context)) { .menuGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP,
getMenuBarData().setMenuItemName("&Save"); ToolConstants.TOOL_OPTIONS_MENU_GROUP + "b")
setDescription("Save Program"); .withContext(ProgramActionContext.class)
return false; .onAction(c -> showProgramOptions(c.getProgram()))
} .buildAndInstall(tool);
return true;
}
@Override
public boolean isEnabledForContext(ProgramActionContext context) {
lastProgramContext = context;
Program program = context.getProgram();
String programName = "'" + program.getDomainFile().getName() + "'";
getMenuBarData().setMenuItemName("&Save " + programName);
setDescription("Save " + programName);
return program.isChanged();
}
@Override
protected boolean isValidContext(ProgramActionContext context) {
return super.isValidContext(context);
}
};
String[] saveMenuPath = { ToolConstants.MENU_FILE, "&Save" };
Icon saveIcon = ResourceManager.loadImage("images/disk.png");
String saveGroup = ToolConstants.TOOLBAR_GROUP_ONE;
subMenuGroupOrder = 0;
menuData = new MenuData(saveMenuPath, saveIcon, saveGroup);
menuData.setMenuSubGroup(Integer.toString(subMenuGroupOrder++));
saveAction.setMenuBarData(menuData);
saveAction.setToolBarData(new ToolBarData(saveIcon, saveGroup));
saveAction
.setKeyBindingData(new KeyBindingData('S', DockingUtils.CONTROL_KEY_MODIFIER_MASK));
saveAction.setDescription("Save Program");
saveAsAction = new ProgramContextAction("Save As File", getName()) {
@Override
public void actionPerformed(ProgramActionContext programContext) {
programSaveMgr.saveAs(programContext.getProgram());
}
@Override
public boolean isValidContext(ActionContext context) {
if (!super.isValidContext(context)) {
getMenuBarData().setMenuItemName("Save &As...");
setDescription("Save &As...");
return false;
}
return true;
}
@Override
public boolean isEnabledForContext(ProgramActionContext context) {
Program program = context.getProgram();
String programName = "'" + program.getDomainFile().getName() + "'";
String menuName = "Save " + programName + " &As...";
getMenuBarData().setMenuItemName(menuName);
setDescription(menuName);
return true;
}
};
String[] saveAsPath = { ToolConstants.MENU_FILE, "Save &As..." };
menuData = new MenuData(saveAsPath, null, "DomainObjectSave");
menuData.setMenuSubGroup(Integer.toString(subMenuGroupOrder++));
saveAsAction.setMenuBarData(menuData);
saveAllAction = new DockingAction("Save All Files", getName()) {
@Override
public void actionPerformed(ActionContext context) {
programSaveMgr.saveChangedPrograms();
}
};
menuData =
new MenuData(new String[] { ToolConstants.MENU_FILE, "Save All" }, "DomainObjectSave");
menuData.setMenuSubGroup(Integer.toString(subMenuGroupOrder++));
saveAllAction.setMenuBarData(menuData);
saveAllAction.setDescription("Save All Programs");
optionsAction = new ProgramContextAction("Program Options", getName()) {
@Override
public void actionPerformed(ProgramActionContext programContext) {
showProgramOptions(programContext.getProgram());
}
@Override
public boolean isValidContext(ActionContext context) {
if (!super.isValidContext(context)) {
getMenuBarData().setMenuItemName("Program Options");
return false;
}
return true;
}
@Override
public boolean isEnabledForContext(ProgramActionContext context) {
lastProgramContext = context;
Program program = context.getProgram();
String programName = program.getDomainFile().getName();
getMenuBarData().setMenuItemName("Options for " + programName + "...");
return true;
}
};
String[] optionsPath = { ToolConstants.MENU_EDIT, "P&rogram Options..." };
menuData = new MenuData(optionsPath, null, ToolConstants.TOOL_OPTIONS_MENU_GROUP);
// update these options to appear below those for the tool, which we know is defined
// inside of ToolConstants
menuData.setMenuSubGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP + "b");
optionsAction.setMenuBarData(menuData);
optionsAction.setDescription("Edit Options for current program");
undoAction = new UndoAction(tool, getName()); undoAction = new UndoAction(tool, getName());
redoAction = new RedoAction(tool, getName()); redoAction = new RedoAction(tool, getName());
tool.addAction(openAction);
tool.addAction(closeAction);
tool.addAction(closeOthersAction);
tool.addAction(closeAllAction);
tool.addAction(saveAction);
tool.addAction(saveAsAction);
tool.addAction(saveAllAction);
tool.addAction(optionsAction);
tool.addAction(undoAction); tool.addAction(undoAction);
tool.addAction(redoAction); tool.addAction(redoAction);
} }
@@ -791,6 +652,10 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
private void updateActions() { private void updateActions() {
Program p = programMgr.getCurrentProgram(); Program p = programMgr.getCurrentProgram();
updateCloseAction(p);
updateProgramOptionsAction(p);
updateSaveAction(p);
updateSaveAsAction(p);
closeAllAction.setEnabled(p != null); closeAllAction.setEnabled(p != null);
optionsAction.setEnabled(p != null); optionsAction.setEnabled(p != null);
Program[] programList = programMgr.getAllPrograms(); Program[] programList = programMgr.getAllPrograms();
@@ -805,6 +670,54 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
tool.contextChanged(null); tool.contextChanged(null);
} }
private void updateSaveAction(Program p) {
if (p == null) {
saveAction.getMenuBarData().setMenuItemName("&Save");
saveAction.setDescription("Save Program");
saveAction.setEnabled(false);
}
else {
String programName = "'" + p.getDomainFile().getName() + "'";
saveAction.getMenuBarData().setMenuItemName("&Save " + programName);
saveAction.setDescription("Save " + programName);
saveAction.setEnabled(p.isChanged());
}
}
private void updateSaveAsAction(Program p) {
if (p == null) {
saveAsAction.getMenuBarData().setMenuItemName("Save &As...");
}
else {
String programName = "'" + p.getDomainFile().getName() + "'";
saveAsAction.getMenuBarData().setMenuItemName("Save " + programName + " &As...");
}
}
private void updateProgramOptionsAction(Program p) {
if (p == null) {
optionsAction.getMenuBarData().setMenuItemName("Program Options");
}
else {
String programName = "'" + p.getDomainFile().getName() + "'";
optionsAction.getMenuBarData().setMenuItemName("Options for " + programName + "...");
}
optionsAction.setEnabled(p != null);
}
private void updateCloseAction(Program p) {
if (p == null) {
closeAction.getMenuBarData().setMenuItemName("&Close");
closeAction.setDescription("Close Program");
}
else {
String programName = "'" + p.getDomainFile().getName() + "'";
closeAction.getMenuBarData().setMenuItemName("&Close " + programName);
closeAction.setDescription("<html>Close " + HTMLUtilities.escapeHTML(programName));
}
closeAction.setEnabled(p != null);
}
private void open() { private void open() {
if (openDialog == null) { if (openDialog == null) {
ActionListener listener = e -> { ActionListener listener = e -> {
@@ -875,17 +788,11 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
void updateProgramActions() { void updateProgramActions() {
updateSaveAllAction(); updateSaveAllAction();
if (lastProgramContext != null) { Program p = getCurrentProgram();
updateProgramAction(undoAction); updateSaveAction(getCurrentProgram());
updateProgramAction(redoAction); updateSaveAsAction(getCurrentProgram());
updateProgramAction(saveAction); undoAction.update(p);
updateProgramAction(saveAsAction); redoAction.update(p);
}
}
private void updateProgramAction(ProgramContextAction action) {
boolean isEnabled = action.isEnabledForContext(lastProgramContext);
action.setEnabled(isEnabled);
} }
private void updateSaveAllAction() { private void updateSaveAllAction() {
@@ -15,12 +15,10 @@
*/ */
package ghidra.app.plugin.core.progmgr; package ghidra.app.plugin.core.progmgr;
import java.awt.event.InputEvent;
import java.io.IOException; import java.io.IOException;
import javax.swing.Icon; import javax.swing.Icon;
import docking.ActionContext;
import docking.action.*; import docking.action.*;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.context.ProgramActionContext; import ghidra.app.context.ProgramActionContext;
@@ -46,7 +44,7 @@ public class RedoAction extends ProgramContextAction {
menuData.setMenuSubGroup("2Redo"); // make this appear below the undo menu item menuData.setMenuSubGroup("2Redo"); // make this appear below the undo menu item
setMenuBarData(menuData); setMenuBarData(menuData);
setToolBarData(new ToolBarData(icon, group)); setToolBarData(new ToolBarData(icon, group));
setKeyBindingData(new KeyBindingData('Z', InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)); setKeyBindingData(new KeyBindingData("ctrl shift Z"));
setDescription("Redo"); setDescription("Redo");
} }
@@ -62,18 +60,41 @@ public class RedoAction extends ProgramContextAction {
} }
} }
@Override /**
protected boolean isEnabledForContext(ProgramActionContext context) { * updates the menu name of the action as the undo stack changes
Program program = context.getProgram(); * <P>
if (program.canRedo()) { * NOTE: currently, we must manage the enablement explicitly
* because contextChanged is not called for data changes. Ideally, the enablement
* would be handled by the context, but for now it doesn't work
*
* @param program the program
*/
public void update(Program program) {
if (program == null) {
getMenuBarData().setMenuItemName("Redo ");
setDescription("");
setEnabled(false);
}
else if (program.canRedo()) {
String programName = program.getDomainFile().getName(); String programName = program.getDomainFile().getName();
getMenuBarData().setMenuItemName("Redo " + programName); getMenuBarData().setMenuItemName("Redo " + programName);
String tip = HTMLUtilities.toWrappedHTML( String tip = HTMLUtilities.toWrappedHTML(
"Redo " + HTMLUtilities.escapeHTML(program.getRedoName())); "Redo " + HTMLUtilities.escapeHTML(program.getRedoName()));
setDescription(tip); setDescription(tip);
return true; setEnabled(true);
} }
return false; else {
setDescription("Redo");
setEnabled(false);
}
}
@Override
protected boolean isEnabledForContext(ProgramActionContext context) {
Program program = context.getProgram();
return program.canRedo();
} }
private void saveCurrentLocationToHistory() { private void saveCurrentLocationToHistory() {
@@ -84,13 +105,4 @@ public class RedoAction extends ProgramContextAction {
} }
} }
@Override
public boolean isEnabledForContext(ActionContext actionContext) {
if (!super.isEnabledForContext(actionContext)) {
setDescription("Redo");
getMenuBarData().setMenuItemName("Redo");
return false;
}
return true;
}
} }
@@ -15,12 +15,10 @@
*/ */
package ghidra.app.plugin.core.progmgr; package ghidra.app.plugin.core.progmgr;
import java.awt.event.InputEvent;
import java.io.IOException; import java.io.IOException;
import javax.swing.Icon; import javax.swing.Icon;
import docking.ActionContext;
import docking.action.*; import docking.action.*;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.context.ProgramActionContext; import ghidra.app.context.ProgramActionContext;
@@ -46,7 +44,7 @@ public class UndoAction extends ProgramContextAction {
setMenuBarData(menuData); setMenuBarData(menuData);
setToolBarData(new ToolBarData(icon, "Undo")); setToolBarData(new ToolBarData(icon, "Undo"));
setDescription("Undo"); setDescription("Undo");
setKeyBindingData(new KeyBindingData('Z', InputEvent.CTRL_MASK)); setKeyBindingData(new KeyBindingData("ctrl Z"));
} }
@Override @Override
@@ -69,27 +67,39 @@ public class UndoAction extends ProgramContextAction {
} }
} }
@Override /**
protected boolean isEnabledForContext(ProgramActionContext context) { * updates the menu name of the action as the undo stack changes
Program program = context.getProgram(); * <P>
if (program.canUndo()) { * NOTE: currently, we must manage the enablement explicitly
* because contextChanged is not called for data changes. Ideally, the enablement
* would be handled by the context, but for now it doesn't work
*
* @param program the program
*/
public void update(Program program) {
if (program == null) {
getMenuBarData().setMenuItemName("Undo ");
setDescription("");
setEnabled(false);
}
if (program != null && program.canUndo()) {
String programName = program.getDomainFile().getName(); String programName = program.getDomainFile().getName();
getMenuBarData().setMenuItemName("Undo " + programName); getMenuBarData().setMenuItemName("Undo " + programName);
String tip = HTMLUtilities.toWrappedHTML( String tip = HTMLUtilities.toWrappedHTML(
"Undo " + HTMLUtilities.escapeHTML(program.getUndoName())); "Undo " + HTMLUtilities.escapeHTML(program.getUndoName()));
setDescription(tip); setDescription(tip);
return true; setEnabled(true);
}
else {
setDescription("Undo");
setEnabled(false);
} }
return false;
} }
@Override @Override
public boolean isEnabledForContext(ActionContext actionContext) { protected boolean isEnabledForContext(ProgramActionContext context) {
if (!super.isEnabledForContext(actionContext)) { Program program = context.getProgram();
setDescription("Undo"); return program.canUndo();
getMenuBarData().setMenuItemName("Undo");
return false;
}
return true;
} }
} }
@@ -145,7 +145,7 @@ public class ScalarSearchPlugin extends ProgramPlugin implements DomainObjectLis
} }
@Override @Override
protected boolean isValidNavigationContext(NavigatableActionContext context) { protected boolean isEnabledForContext(NavigatableActionContext context) {
return !(context instanceof RestrictedAddressSetContext); return !(context instanceof RestrictedAddressSetContext);
} }
}; };
@@ -350,7 +350,7 @@ public class MemSearchPlugin extends Plugin implements OptionsChangeListener,
} }
@Override @Override
protected boolean isValidNavigationContext(NavigatableActionContext context) { protected boolean isEnabledForContext(NavigatableActionContext context) {
return !(context instanceof RestrictedAddressSetContext); return !(context instanceof RestrictedAddressSetContext);
} }
}; };
@@ -369,13 +369,8 @@ public class MemSearchPlugin extends Plugin implements OptionsChangeListener,
} }
@Override @Override
public boolean isEnabledForContext(NavigatableActionContext context) { protected boolean isEnabledForContext(NavigatableActionContext context) {
return searchInfo != null; return !(context instanceof RestrictedAddressSetContext) && searchInfo != null;
}
@Override
protected boolean isValidNavigationContext(NavigatableActionContext context) {
return !(context instanceof RestrictedAddressSetContext);
} }
}; };
searchAgainAction.setHelpLocation( searchAgainAction.setHelpLocation(
@@ -16,8 +16,6 @@
package ghidra.app.plugin.core.searchtext; package ghidra.app.plugin.core.searchtext;
import java.awt.*; import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@@ -26,13 +24,15 @@ import java.util.regex.Pattern;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import docking.*; import docking.*;
import docking.action.*; import docking.action.DockingAction;
import docking.action.builder.ActionBuilder;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import docking.widgets.fieldpanel.support.Highlight; import docking.widgets.fieldpanel.support.Highlight;
import docking.widgets.table.threaded.*; import docking.widgets.table.threaded.*;
import ghidra.GhidraOptions; import ghidra.GhidraOptions;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.context.*; import ghidra.app.context.ListingActionContext;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.nav.NavigatableRemovalListener; import ghidra.app.nav.NavigatableRemovalListener;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
@@ -379,49 +379,34 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList
private void createActions() { private void createActions() {
String subGroup = getClass().getName(); String subGroup = getClass().getName();
searchAction = new ListingContextAction("Search Text", getName()) { searchAction = new ActionBuilder("Search Text", getName())
@Override .menuPath("&Search", "Program &Text...")
public void actionPerformed(ListingActionContext context) { .menuGroup("search", subGroup)
setNavigatable(context.getNavigatable()); .keyBinding("ctrl shift E")
displayDialog(context); .description(DESCRIPTION)
} .helpLocation(new HelpLocation(HelpTopics.SEARCH, "Search Text"))
}; .withContext(NavigatableActionContext.class)
searchAction.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, searchAction.getName())); .supportsDefaultToolContext(true)
String[] menuPath = new String[] { "&Search", "Program &Text..." }; .onAction(c -> {
MenuData menuData = new MenuData(menuPath, "search"); setNavigatable(c.getNavigatable());
menuData.setMenuSubGroup(subGroup); displayDialog(c);
searchAction.setMenuBarData(menuData); })
searchAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_E, .buildAndInstall(tool);
InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
searchAction.setDescription(DESCRIPTION); searchAgainAction = new ActionBuilder("Repeat Text Search", getName())
searchAction.setEnabled(false); .menuPath("&Search", "Repeat Text Search")
//searchAction.setAddToPopup(false); .menuGroup("search", subGroup)
tool.addAction(searchAction); .keyBinding("ctrl shift F3")
.description(DESCRIPTION)
searchAgainAction = new ListingContextAction("Repeat Text Search", getName()) { .supportsDefaultToolContext(true)
@Override .helpLocation(new HelpLocation(HelpTopics.SEARCH, "Repeat Text Search"))
public void actionPerformed(ListingActionContext context) { .withContext(NavigatableActionContext.class)
setNavigatable(context.getNavigatable()); .enabledWhen(c -> searchedOnce)
searchDialog.repeatSearch(); .onAction(c -> {
} setNavigatable(c.getNavigatable());
searchDialog.repeatSearch();
@Override })
public boolean isEnabledForContext(ListingActionContext context) { .buildAndInstall(tool);
return searchedOnce;
}
};
searchAgainAction.setHelpLocation(
new HelpLocation(HelpTopics.SEARCH, searchAgainAction.getName()));
menuPath = new String[] { "&Search", "Repeat Text Search" };
menuData = new MenuData(menuPath, "search");
menuData.setMenuSubGroup(subGroup);
searchAgainAction.setMenuBarData(menuData);
searchAgainAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_F3,
InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
searchAgainAction.setDescription(DESCRIPTION);
tool.addAction(searchAgainAction);
} }
protected void updateNavigatable(ActionContext context) { protected void updateNavigatable(ActionContext context) {
@@ -15,14 +15,14 @@
*/ */
package ghidra.app.plugin.core.select.flow; package ghidra.app.plugin.core.select.flow;
import docking.action.MenuData;
import docking.tool.ToolConstants;
import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingActionContext;
import ghidra.app.context.ListingContextAction; import ghidra.app.context.ListingContextAction;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.CodeUnit;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import docking.action.MenuData;
import docking.tool.ToolConstants;
/** /**
* <CODE>SelectByFlowAction</CODE> allows the user to Select Code By Flowing from * <CODE>SelectByFlowAction</CODE> allows the user to Select Code By Flowing from
@@ -63,6 +63,9 @@ class SelectByFlowAction extends ListingContextAction {
this.selectByFlowPlugin = plugin; this.selectByFlowPlugin = plugin;
this.selectionType = selectionType; this.selectionType = selectionType;
// this is in the main tool menu, so make it a tool action
setSupportsDefaultToolContext(true);
String[] menuPath = null; String[] menuPath = null;
if (selectionType == SelectByFlowPlugin.SELECT_FUNCTIONS) { if (selectionType == SelectByFlowPlugin.SELECT_FUNCTIONS) {
menuPath = new String[] { ToolConstants.MENU_SELECTION, "Function" }; menuPath = new String[] { ToolConstants.MENU_SELECTION, "Function" };
@@ -91,11 +94,6 @@ class SelectByFlowAction extends ListingContextAction {
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName())); setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
} }
/**
* Method called when the action is invoked.
*
* @param ActionEvent details regarding the invocation of this action
*/
@Override @Override
public void actionPerformed(ListingActionContext context) { public void actionPerformed(ListingActionContext context) {
// Either select by following all flows or all flows except calls // Either select by following all flows or all flows except calls
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,8 +15,13 @@
*/ */
package ghidra.app.plugin.core.select.reference; package ghidra.app.plugin.core.select.reference;
import ghidra.app.context.ListingContextAction; import java.awt.event.InputEvent;
import ghidra.app.context.ListingActionContext; import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.context.NavigatableContextAction;
import ghidra.app.nav.NavigationUtils; import ghidra.app.nav.NavigationUtils;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@@ -27,13 +31,7 @@ import ghidra.program.model.symbol.Reference;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import java.awt.event.InputEvent; public class SelectForwardRefsAction extends NavigatableContextAction {
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
public class SelectForwardRefsAction extends ListingContextAction {
private final PluginTool tool; private final PluginTool tool;
@@ -42,47 +40,47 @@ public class SelectForwardRefsAction extends ListingContextAction {
this.tool = tool; this.tool = tool;
String group = "references"; String group = "references";
setMenuBarData( new MenuData( new String[] {"Select", "Forward Refs"}, null, group ) ); setMenuBarData(new MenuData(new String[] { "Select", "Forward Refs" }, null, group));
setKeyBindingData( new KeyBindingData( KeyEvent.VK_PERIOD, InputEvent.CTRL_MASK ) ); setKeyBindingData(new KeyBindingData(KeyEvent.VK_PERIOD, InputEvent.CTRL_MASK));
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, "Forward")); setHelpLocation(new HelpLocation(HelpTopics.SELECTION, "Forward"));
// setKeyBindingData( new KeyBindingData(KeyEvent.VK_SEMICOLON, InputEvent.CTRL_MASK ) ); // setKeyBindingData( new KeyBindingData(KeyEvent.VK_SEMICOLON, InputEvent.CTRL_MASK ) );
// setHelpLocation(new HelpLocation(HelpTopics.SELECTION, "Backward")); // setHelpLocation(new HelpLocation(HelpTopics.SELECTION, "Backward"));
} }
@Override @Override
protected boolean isEnabledForContext(ListingActionContext context) { protected boolean isEnabledForContext(NavigatableActionContext context) {
return context.getAddress() != null || context.hasSelection(); return context.getAddress() != null || context.hasSelection();
} }
/** /**
* Method called when the action is invoked. * Method called when the action is invoked.
* @param ActionEvent details regarding the invocation of this action * @param ActionEvent details regarding the invocation of this action
*/ */
@Override @Override
public void actionPerformed(ListingActionContext context) { public void actionPerformed(NavigatableActionContext context) {
AddressSetView addressSet = context.hasSelection() ? AddressSetView addressSet =
context.getSelection() : context.hasSelection() ? context.getSelection() : new AddressSet(context.getAddress());
new AddressSet(context.getAddress());
ProgramSelection selection = getSelection(context.getProgram(), addressSet); ProgramSelection selection = getSelection(context.getProgram(), addressSet);
NavigationUtils.setSelection(tool, context.getNavigatable(), selection); NavigationUtils.setSelection(tool, context.getNavigatable(), selection);
} }
private ProgramSelection getSelection(Program program, AddressSetView addressSetView){ private ProgramSelection getSelection(Program program, AddressSetView addressSetView) {
AddressSet addressSet = new AddressSet(); AddressSet addressSet = new AddressSet();
CodeUnitIterator iter = program.getListing().getCodeUnits(addressSetView,true); CodeUnitIterator iter = program.getListing().getCodeUnits(addressSetView, true);
while (iter.hasNext()){ while (iter.hasNext()) {
CodeUnit cu=iter.next(); CodeUnit cu = iter.next();
Reference[] memRef=cu.getReferencesFrom(); Reference[] memRef = cu.getReferencesFrom();
for (int i=0;i<memRef.length;i++){ for (Reference element : memRef) {
Address addr = memRef[i].getToAddress(); Address addr = element.getToAddress();
if ( addr.isMemoryAddress() ) { if (addr.isMemoryAddress()) {
addressSet.addRange(addr,addr); addressSet.addRange(addr, addr);
} }
} }
} }
return new ProgramSelection(addressSet); return new ProgramSelection(addressSet);
@@ -220,7 +220,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
return null; return null;
} }
Function function = controller.getFunction(); Function function = controller.getFunction();
Address entryPoint = function != null ? function.getEntryPoint() : null; if (function == null) {
return null;
}
Address entryPoint = function.getEntryPoint();
boolean isDecompiling = controller.isDecompiling(); boolean isDecompiling = controller.isDecompiling();
return new DecompilerActionContext(this, entryPoint, isDecompiling); return new DecompilerActionContext(this, entryPoint, isDecompiling);
} }
@@ -253,7 +253,7 @@ public class ActionContext {
public ActionContext getGlobalContext() { public ActionContext getGlobalContext() {
if (globalContext == null) { if (globalContext == null) {
Tool tool = getTool(); Tool tool = getTool();
globalContext = tool == null ? new ActionContext() : tool.getGlobalActionContext(); globalContext = tool == null ? new ActionContext() : tool.getDefaultToolContext();
} }
return globalContext; return globalContext;
} }
@@ -228,4 +228,14 @@ public class DockingActionProxy
public String toString() { public String toString() {
return dockingAction.toString(); return dockingAction.toString();
} }
@Override
public void setSupportsDefaultToolContext(boolean newValue) {
dockingAction.setSupportsDefaultToolContext(newValue);
}
@Override
public boolean supportsDefaultToolContext() {
return dockingAction.supportsDefaultToolContext();
}
} }
@@ -2161,14 +2161,45 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
} }
/** /**
* Returns the global action context for the tool * Returns the default action context for the tool
* @return the global action context for the tool * @return the default action context for the tool
*/ */
public ActionContext getGlobalActionContext() { public ActionContext getDefaultToolContext() {
return defaultProvider == null ? new ActionContext() return defaultProvider == null ? new ActionContext()
: defaultProvider.getActionContext(null); : defaultProvider.getActionContext(null);
} }
/**
* Gets the {@link ActionContext} appropriate for the given action. This will normally
* be the context from the currently focused {@link ComponentProvider}. If that
* context is not valid for the given action and the action supports using the default
* tool context, then the default tool context will be returned. Otherwise, returns null.
*
* @param action the action for which to get an {@link ActionContext}
* @return the {@link ActionContext} appropriate for the given action or null
*/
public ActionContext getActionContext(DockingActionIf action) {
ComponentProvider provider = getActiveComponentProvider();
ActionContext context = provider == null ? null : provider.getActionContext(null);
if (context == null) {
context = new ActionContext(provider, null);
}
if (action.isValidContext(context)) {
return context;
}
if (action.supportsDefaultToolContext()) {
ActionContext toolContext = getDefaultToolContext();
if (action.isValidContext(toolContext)) {
return toolContext;
}
}
return context;
}
void notifyContextListeners(ComponentPlaceholder placeHolder, ActionContext actionContext) { void notifyContextListeners(ComponentPlaceholder placeHolder, ActionContext actionContext) {
if (placeHolder == focusedPlaceholder) { if (placeHolder == focusedPlaceholder) {
@@ -19,12 +19,12 @@ import docking.action.DockingActionIf;
import docking.action.ToggleDockingActionIf; import docking.action.ToggleDockingActionIf;
import generic.json.Json; import generic.json.Json;
public class ExecutableKeyActionAdapter { public class ExecutableAction {
DockingActionIf action; DockingActionIf action;
ActionContext context; ActionContext context;
public ExecutableKeyActionAdapter(DockingActionIf action, ActionContext context) { public ExecutableAction(DockingActionIf action, ActionContext context) {
this.action = action; this.action = action;
this.context = context; this.context = context;
} }
@@ -46,9 +46,10 @@ public class MenuBarMenuHandler extends MenuHandler {
DockingWindowManager.clearMouseOverHelp(); DockingWindowManager.clearMouseOverHelp();
ComponentProvider provider = windowManager.getActiveComponentProvider(); ActionContext context = windowManager.getActionContext(action);
ActionContext providerContext = provider == null ? null : provider.getActionContext(null); if (context == null) {
ActionContext context = providerContext == null ? new ActionContext() : providerContext; return; // nothing to do
}
context.setSourceObject(event.getSource()); context.setSourceObject(event.getSource());
@@ -33,7 +33,7 @@ import docking.widgets.label.GLabel;
public class MultiActionDialog extends DialogComponentProvider { public class MultiActionDialog extends DialogComponentProvider {
private String keystrokeName; private String keystrokeName;
private List<ExecutableKeyActionAdapter> list; private List<ExecutableAction> list;
private JList<String> actionList; private JList<String> actionList;
private DefaultListModel<String> listModel; private DefaultListModel<String> listModel;
@@ -42,7 +42,7 @@ public class MultiActionDialog extends DialogComponentProvider {
* @param keystrokeName keystroke name * @param keystrokeName keystroke name
* @param list list of actions * @param list list of actions
*/ */
public MultiActionDialog(String keystrokeName, List<ExecutableKeyActionAdapter> list) { public MultiActionDialog(String keystrokeName, List<ExecutableAction> list) {
super("Select Action", true); super("Select Action", true);
this.keystrokeName = keystrokeName; this.keystrokeName = keystrokeName;
init(); init();
@@ -65,7 +65,7 @@ public class MultiActionDialog extends DialogComponentProvider {
close(); close();
ExecutableKeyActionAdapter actionProxy = list.get(index); ExecutableAction actionProxy = list.get(index);
actionProxy.execute(); actionProxy.execute();
} }
@@ -73,12 +73,12 @@ public class MultiActionDialog extends DialogComponentProvider {
* Set the list of actions that are enabled * Set the list of actions that are enabled
* @param list list of actions selected * @param list list of actions selected
*/ */
public void setActionList(List<ExecutableKeyActionAdapter> list) { public void setActionList(List<ExecutableAction> list) {
okButton.setEnabled(false); okButton.setEnabled(false);
this.list = list; this.list = list;
listModel.clear(); listModel.clear();
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
ExecutableKeyActionAdapter actionProxy = list.get(i); ExecutableAction actionProxy = list.get(i);
DockingActionIf action = actionProxy.getAction(); DockingActionIf action = actionProxy.getAction();
listModel.addElement(action.getName() + " (" + action.getOwnerDescription() + ")"); listModel.addElement(action.getName() + " (" + action.getOwnerDescription() + ")");
} }
@@ -305,11 +305,10 @@ public interface Tool extends ServiceProvider {
public void close(); public void close();
/** /**
* Returns the global action context for the tool. The global context is the context of * Returns the default {@link ActionContext} for the tool. The default context is the context
* the default focused component, instead of the normal action context which is the current * the default ComponentProvider for the tool.
* focused component. * @return the default {@link ActionContext} for the tool
* @return the global action context for the tool
*/ */
public ActionContext getGlobalActionContext(); public ActionContext getDefaultToolContext();
} }
@@ -120,12 +120,8 @@ public class WindowActionManager {
return; return;
} }
ComponentProvider provider = placeHolderForScheduledActionUpdate == null ? null ActionContext localContext = getContext();
: placeHolderForScheduledActionUpdate.getProvider(); ActionContext globalContext = winMgr.getDefaultToolContext();
ActionContext localContext = provider == null ? null : provider.getActionContext(null);
if (localContext == null) {
localContext = new ActionContext();
}
// Update actions - make a copy so that we don't get concurrent modification exceptions // Update actions - make a copy so that we don't get concurrent modification exceptions
List<DockingActionIf> list = new ArrayList<>(actionToProxyMap.values()); List<DockingActionIf> list = new ArrayList<>(actionToProxyMap.values());
@@ -133,6 +129,9 @@ public class WindowActionManager {
if (action.isValidContext(localContext)) { if (action.isValidContext(localContext)) {
action.setEnabled(action.isEnabledForContext(localContext)); action.setEnabled(action.isEnabledForContext(localContext));
} }
else if (isValidDefaultToolContext(action, globalContext)) {
action.setEnabled(action.isEnabledForContext(globalContext));
}
else { else {
action.setEnabled(false); action.setEnabled(false);
} }
@@ -140,4 +139,21 @@ public class WindowActionManager {
// Notify listeners if the context provider is the focused provider // Notify listeners if the context provider is the focused provider
winMgr.notifyContextListeners(placeHolderForScheduledActionUpdate, localContext); winMgr.notifyContextListeners(placeHolderForScheduledActionUpdate, localContext);
} }
private boolean isValidDefaultToolContext(DockingActionIf action, ActionContext toolContext) {
return action.supportsDefaultToolContext() &&
action.isValidContext(toolContext);
}
private ActionContext getContext() {
ComponentProvider provider = placeHolderForScheduledActionUpdate == null ? null
: placeHolderForScheduledActionUpdate.getProvider();
ActionContext context = provider == null ? null : provider.getActionContext(null);
if (context == null) {
context = new ActionContext();
}
return context;
}
} }
@@ -77,6 +77,8 @@ public abstract class DockingAction implements DockingActionIf {
private Predicate<ActionContext> popupPredicate; private Predicate<ActionContext> popupPredicate;
private Predicate<ActionContext> validContextPredicate; private Predicate<ActionContext> validContextPredicate;
private boolean supportsDefaultToolContext;
public DockingAction(String name, String owner) { public DockingAction(String name, String owner) {
this.name = name; this.name = name;
this.owner = owner; this.owner = owner;
@@ -224,6 +226,16 @@ public abstract class DockingAction implements DockingActionIf {
return !isEnabled; return !isEnabled;
} }
@Override
public void setSupportsDefaultToolContext(boolean newValue) {
supportsDefaultToolContext = newValue;
}
@Override
public boolean supportsDefaultToolContext() {
return supportsDefaultToolContext;
}
@Override @Override
public final JButton createButton() { public final JButton createButton() {
JButton button = doCreateButton(); JButton button = doCreateButton();
@@ -33,6 +33,26 @@ import docking.help.HelpDescriptor;
* client actions will use the default setting of {@link KeyBindingType#INDIVIDUAL}. To control * client actions will use the default setting of {@link KeyBindingType#INDIVIDUAL}. To control
* the level of key binding support, you can pass the desired {@link KeyBindingType} to the * the level of key binding support, you can pass the desired {@link KeyBindingType} to the
* base implementation of this interface. * base implementation of this interface.
*
* <p>ActionContext is a key concept for tool actions so that they can be context sensitive if
* appropriate. The context provides a
* consistent way for plugins and components to share tool state with actions. Actions can then
* use that context to make decisions, such as if they should be enabled or added to a popup menu.
* The context information is also typically used when the action is invoked. For example, an
* action context from a table element may provide the row in a table component that is selected and
* then a "delete table row" action can use that information to be enabled when a table selection
* exists and then delete that row if the action is invoked.
*
* <p> To make the overall action experience more convenient for the user, action processing
* supports the concept of a "default tool context". This allows actions to work on a more global
* level than just the component that is focused. The idea is that if an action is not valid for
* the current focused context (and it has be declared to work this way using
* the {@link #setSupportsDefaultToolContext(boolean)}), then it can be validated against the default
* tool context. The "default tool context" is defined to be the action context of the tool's
* primary component. This is primarily intended for tool-level actions which are the ones that appear
* in the tool's main menu bar or toolbar. This allows the tool actions to mostly work on the
* tool's main component context regardless of what has focus, and yet still work on the
* focused component if appropriate (such as a snapshot of the main component).
*/ */
public interface DockingActionIf extends HelpDescriptor { public interface DockingActionIf extends HelpDescriptor {
public static final String ENABLEMENT_PROPERTY = "enabled"; public static final String ENABLEMENT_PROPERTY = "enabled";
@@ -97,6 +117,26 @@ public interface DockingActionIf extends HelpDescriptor {
*/ */
public boolean setEnabled(boolean newValue); public boolean setEnabled(boolean newValue);
/**
* Sets whether or not this action should be activated using the default tool context if the
* current focused provider's context is not valid for this action. Typically, this should
* be set on actions that are mostly independent of which component has focus such as those
* on the tool's main toolbar.
*
* @param newValue if true, the action will be activated using the default tool context if the
* local context is not valid for this action. If false, the action will only ever be
* activated using the focused context.
*/
public void setSupportsDefaultToolContext(boolean newValue);
/**
* Returns true if this action can be activated using the default tool context if the focused
* context is invalid for this action. See {@link #setSupportsDefaultToolContext(boolean)}
* @return true if this action can be activated using the default tool context if the local
* context is invalid for this action.
*/
public boolean supportsDefaultToolContext();
/** /**
* Returns true if the action is enabled. * Returns true if the action is enabled.
* *
@@ -120,7 +160,7 @@ public interface DockingActionIf extends HelpDescriptor {
/** /**
* Returns the {@link ToolBarData} to be used to put this action in a toolbar. The ToolBarData will be * Returns the {@link ToolBarData} to be used to put this action in a toolbar. The ToolBarData will be
* null if the action in not set to be in a tool bar. * null if the action in not set to be in a toolbar.
* @return the {@link ToolBarData} for the popup menu or null if the action is not in a popup menu. * @return the {@link ToolBarData} for the popup menu or null if the action is not in a popup menu.
*/ */
public ToolBarData getToolBarData(); public ToolBarData getToolBarData();
@@ -36,6 +36,18 @@ public class KeyBindingData {
this(KeyStroke.getKeyStroke(keyCode, modifiers)); this(KeyStroke.getKeyStroke(keyCode, modifiers));
} }
public KeyBindingData(String keyStrokeString) {
this(parseKeyStrokeString(keyStrokeString));
}
private static KeyStroke parseKeyStrokeString(String keyStrokeString) {
KeyStroke keyStroke = KeyBindingUtils.parseKeyStroke(keyStrokeString);
if (keyStroke == null) {
throw new IllegalArgumentException("Invalid keystroke string: " + keyStrokeString);
}
return keyStroke;
}
public KeyBindingData(KeyStroke keyStroke, KeyBindingPrecedence precedence) { public KeyBindingData(KeyStroke keyStroke, KeyBindingPrecedence precedence) {
if (precedence == KeyBindingPrecedence.ReservedActionsLevel) { if (precedence == KeyBindingPrecedence.ReservedActionsLevel) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@@ -114,7 +114,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
@Override @Override
public void actionPerformed(final ActionEvent event) { public void actionPerformed(final ActionEvent event) {
// Build list of actions which are valid in current context // Build list of actions which are valid in current context
List<ExecutableKeyActionAdapter> list = getActionsForCurrentContext(event.getSource()); List<ExecutableAction> list = getActionsForCurrentOrDefaultContext(event.getSource());
// If menu active, disable all key bindings // If menu active, disable all key bindings
if (ignoreActionWhileMenuShowing()) { if (ignoreActionWhileMenuShowing()) {
@@ -137,7 +137,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
Swing.runLater(() -> DockingWindowManager.showDialog(dialog)); Swing.runLater(() -> DockingWindowManager.showDialog(dialog));
} }
else if (list.size() == 1) { else if (list.size() == 1) {
final ExecutableKeyActionAdapter actionProxy = list.get(0); ExecutableAction actionProxy = list.get(0);
tool.setStatusInfo(""); tool.setStatusInfo("");
actionProxy.execute(); actionProxy.execute();
} }
@@ -156,8 +156,9 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
return menuManager.getSelectedPath().length != 0; return menuManager.getSelectedPath().length != 0;
} }
private List<ExecutableKeyActionAdapter> getValidContextActions(ActionContext localContext) { private List<ExecutableAction> getValidContextActions(ActionContext localContext,
List<ExecutableKeyActionAdapter> list = new ArrayList<>(); ActionContext globalContext) {
List<ExecutableAction> list = new ArrayList<>();
boolean hasLocalActionsForKeyBinding = false; boolean hasLocalActionsForKeyBinding = false;
// //
@@ -167,14 +168,15 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
if (actionData.isMyProvider(localContext)) { if (actionData.isMyProvider(localContext)) {
hasLocalActionsForKeyBinding = true; hasLocalActionsForKeyBinding = true;
if (isValidAndEnabled(actionData, localContext)) { if (isValidAndEnabled(actionData, localContext)) {
list.add(new ExecutableKeyActionAdapter(actionData.action, localContext)); list.add(new ExecutableAction(actionData.action, localContext));
} }
} }
} }
if (hasLocalActionsForKeyBinding) { if (hasLocalActionsForKeyBinding) {
// We have locals, ignore the globals. This prevents global actions from processing // At this point, we have local actions that may or may not be enabled. Return here
// the given keybinding when a local action exits, regardless of enablement. // so that any component specific actions found below will not interfere with the
// provider's local actions
return list; return list;
} }
@@ -191,7 +193,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
if (componentAction.isValidComponentContext(localContext)) { if (componentAction.isValidComponentContext(localContext)) {
hasLocalActionsForKeyBinding = true; hasLocalActionsForKeyBinding = true;
if (isValidAndEnabled(actionData, localContext)) { if (isValidAndEnabled(actionData, localContext)) {
list.add(new ExecutableKeyActionAdapter(actionData.action, localContext)); list.add(new ExecutableAction(actionData.action, localContext));
} }
} }
} }
@@ -211,16 +213,28 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
// is a 'global' action. This allows more specific context to be used when // is a 'global' action. This allows more specific context to be used when
// available // available
if (isValidAndEnabled(actionData, localContext)) { if (isValidAndEnabled(actionData, localContext)) {
list.add(new ExecutableKeyActionAdapter(actionData.action, localContext)); list.add(new ExecutableAction(actionData.action, localContext));
}
else if (isValidAndEnabledGlobally(actionData, globalContext)) {
list.add(new ExecutableAction(actionData.action, globalContext));
} }
} }
} }
return list; return list;
} }
private boolean isValidAndEnabled(ActionData actionData, ActionContext localContext) { private boolean isValidAndEnabled(ActionData actionData, ActionContext context) {
DockingActionIf a = actionData.action; DockingActionIf a = actionData.action;
return a.isValidContext(localContext) && a.isEnabledForContext(localContext); return a.isValidContext(context) && a.isEnabledForContext(context);
}
private boolean isValidAndEnabledGlobally(ActionData actionData, ActionContext context) {
// the context may be null when we don't want global action such as when getting actions
// for a dialog
if (context == null) {
return false;
}
return actionData.supportsDefaultToolContext() && isValidAndEnabled(actionData, context);
} }
@Override @Override
@@ -242,7 +256,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
*/ */
public KeyBindingPrecedence geValidKeyBindingPrecedence(Component source) { public KeyBindingPrecedence geValidKeyBindingPrecedence(Component source) {
List<ExecutableKeyActionAdapter> validActions = getActionsForCurrentContext(source); List<ExecutableAction> validActions = getActionsForCurrentOrDefaultContext(source);
if (validActions.isEmpty()) { if (validActions.isEmpty()) {
return null; // a signal that no actions are valid for the current context return null; // a signal that no actions are valid for the current context
} }
@@ -251,31 +265,36 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
return KeyBindingPrecedence.DefaultLevel; return KeyBindingPrecedence.DefaultLevel;
} }
ExecutableKeyActionAdapter actionProxy = validActions.get(0); ExecutableAction actionProxy = validActions.get(0);
DockingActionIf action = actionProxy.getAction(); DockingActionIf action = actionProxy.getAction();
return action.getKeyBindingData().getKeyBindingPrecedence(); return action.getKeyBindingData().getKeyBindingPrecedence();
} }
private List<ExecutableKeyActionAdapter> getActionsForCurrentContext(Object eventSource) { private List<ExecutableAction> getActionsForCurrentOrDefaultContext(Object eventSource) {
DockingWindowManager dwm = tool.getWindowManager(); DockingWindowManager dwm = tool.getWindowManager();
Window window = getWindow(dwm, eventSource); Window window = getWindow(dwm, eventSource);
if (window instanceof DockingDialog) { if (window instanceof DockingDialog) {
DockingDialog dockingDialog = (DockingDialog) window; return getDialogActions(window);
DialogComponentProvider provider = dockingDialog.getDialogComponent();
if (provider == null) {
// this can happen if the dialog is closed during key event processing
return Collections.emptyList();
}
ActionContext context = provider.getActionContext(null);
List<ExecutableKeyActionAdapter> validActions = getValidContextActions(context);
return validActions;
} }
ComponentProvider localProvider = getProvider(dwm, eventSource); ComponentProvider localProvider = getProvider(dwm, eventSource);
ActionContext localContext = getLocalContext(localProvider); ActionContext localContext = getLocalContext(localProvider);
localContext.setSourceObject(eventSource); localContext.setSourceObject(eventSource);
List<ExecutableKeyActionAdapter> validActions = getValidContextActions(localContext); ActionContext globalContext = tool.getDefaultToolContext();
List<ExecutableAction> validActions = getValidContextActions(localContext, globalContext);
return validActions;
}
private List<ExecutableAction> getDialogActions(Window window) {
DockingDialog dockingDialog = (DockingDialog) window;
DialogComponentProvider provider = dockingDialog.getDialogComponent();
if (provider == null) {
// this can happen if the dialog is closed during key event processing
return Collections.emptyList();
}
ActionContext context = provider.getActionContext(null);
List<ExecutableAction> validActions = getValidContextActions(context, null);
return validActions; return validActions;
} }
@@ -337,10 +356,15 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
return provider == otherProvider; return provider == otherProvider;
} }
boolean supportsDefaultToolContext() {
return action.supportsDefaultToolContext();
}
@Override @Override
public String toString() { public String toString() {
String providerString = provider == null ? "" : provider.toString() + " - "; String providerString = provider == null ? "" : provider.toString() + " - ";
return providerString + action; return providerString + action;
} }
} }
} }
@@ -15,6 +15,7 @@
*/ */
package docking.action.builder; package docking.action.builder;
import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -23,6 +24,7 @@ import javax.swing.KeyStroke;
import docking.*; import docking.*;
import docking.action.*; import docking.action.*;
import docking.actions.KeyBindingUtils;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.Msg; import ghidra.util.Msg;
import resources.ResourceManager; import resources.ResourceManager;
@@ -50,7 +52,7 @@ import resources.ResourceManager;
* the {@link #withContext(Class)} call. * the {@link #withContext(Class)} call.
*/ */
public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends ActionContext, B extends AbstractActionBuilder<T, C, B>> { public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends ActionContext, B extends AbstractActionBuilder<T, C, B>> {
private final Predicate<C> ALWAYS_TRUE = e -> true;
/** /**
* Name for the {@code DockingAction} * Name for the {@code DockingAction}
*/ */
@@ -159,17 +161,22 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
/** /**
* Predicate for determining if an action is enabled for a given context * Predicate for determining if an action is enabled for a given context
*/ */
private Predicate<C> enabledPredicate; private Predicate<C> enabledPredicate = ALWAYS_TRUE;
/** /**
* Predicate for determining if an action should be included on the pop-up menu * Predicate for determining if an action should be included on the pop-up menu
*/ */
private Predicate<C> popupPredicate; private Predicate<C> popupPredicate = ALWAYS_TRUE;
/** /**
* Predicate for determining if an action is applicable for a given context * Predicate for determining if an action is applicable for a given context
*/ */
private Predicate<C> validContextPredicate; private Predicate<C> validContextPredicate = ALWAYS_TRUE;
/**
* Set to true if the action supports using the default tool context if the local context is invalid
*/
private boolean supportsDefaultToolContext;
/** /**
* Builder constructor * Builder constructor
@@ -484,7 +491,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
* @return this builder (for chaining) * @return this builder (for chaining)
*/ */
public B keyBinding(String keyStrokeString) { public B keyBinding(String keyStrokeString) {
this.keyBinding = KeyStroke.getKeyStroke(keyStrokeString); this.keyBinding = KeyBindingUtils.parseKeyStroke(keyStrokeString);
if (keyBinding == null && keyStrokeString != null) { if (keyBinding == null && keyStrokeString != null) {
Msg.warn(this, "Can't parse KeyStroke: " + keyStrokeString); Msg.warn(this, "Can't parse KeyStroke: " + keyStrokeString);
} }
@@ -517,7 +524,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
* @return this builder (for chaining) * @return this builder (for chaining)
*/ */
public B enabledWhen(Predicate<C> predicate) { public B enabledWhen(Predicate<C> predicate) {
enabledPredicate = predicate; enabledPredicate = Objects.requireNonNull(predicate);
return self(); return self();
} }
@@ -540,7 +547,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
* @see #popupMenuPath(String...) * @see #popupMenuPath(String...)
*/ */
public B popupWhen(Predicate<C> predicate) { public B popupWhen(Predicate<C> predicate) {
popupPredicate = predicate; popupPredicate = Objects.requireNonNull(predicate);
return self(); return self();
} }
@@ -556,7 +563,23 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
* @return this builder (for chaining) * @return this builder (for chaining)
*/ */
public B validContextWhen(Predicate<C> predicate) { public B validContextWhen(Predicate<C> predicate) {
validContextPredicate = predicate; validContextPredicate = Objects.requireNonNull(predicate);
return self();
}
/**
* Sets whether the action will support using the default tool context if the focused provider's
* context is invalid.
* <P>
* By default, actions only work on the current focused provider's context. Setting this
* to true will cause the action to be evaluated against the default tool context if the
* focused context is not valid for this action.
*
* @param b the new value
* @return this builder (for chaining)
*/
public B supportsDefaultToolContext(boolean b) {
supportsDefaultToolContext = b;
return self(); return self();
} }
@@ -603,6 +626,10 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
public <AC2 extends ActionContext, B2 extends AbstractActionBuilder<T, AC2, B2>> B2 withContext( public <AC2 extends ActionContext, B2 extends AbstractActionBuilder<T, AC2, B2>> B2 withContext(
Class<AC2> newActionContextClass) { Class<AC2> newActionContextClass) {
if (actionContextClass != ActionContext.class) {
throw new IllegalStateException("Can't set the ActionContext type more than once");
}
// To make this work, we need to return a builder whose ActionContext is AC2 and not AC // To make this work, we need to return a builder whose ActionContext is AC2 and not AC
// (which is what this builder is now) // (which is what this builder is now)
// //
@@ -627,6 +654,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
protected void decorateAction(DockingAction action) { protected void decorateAction(DockingAction action) {
action.setEnabled(isEnabled); action.setEnabled(isEnabled);
action.setDescription(description); action.setDescription(description);
action.setSupportsDefaultToolContext(supportsDefaultToolContext);
setMenuData(action); setMenuData(action);
setToolbarData(action); setToolbarData(action);
@@ -637,15 +665,9 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
action.setHelpLocation(helpLocation); action.setHelpLocation(helpLocation);
} }
if (enabledPredicate != null) { action.enabledWhen(adaptPredicate(enabledPredicate));
action.enabledWhen(adaptPredicate(enabledPredicate)); action.validContextWhen(adaptPredicate(validContextPredicate));
} action.popupWhen(adaptPredicate(popupPredicate));
if (validContextPredicate != null) {
action.validContextWhen(adaptPredicate(validContextPredicate));
}
if (popupPredicate != null) {
action.popupWhen(adaptPredicate(popupPredicate));
}
} }
/** /**
@@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package docking.action.builder; package docking.action.builder;
import docking.ActionContext; import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;
@@ -22,7 +22,8 @@ import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JButton; import javax.swing.JButton;
import docking.*; import docking.ActionContext;
import docking.DockingWindowManager;
import docking.action.*; import docking.action.*;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
@@ -148,10 +149,13 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
private ActionContext getActionContext() { private ActionContext getActionContext() {
DockingWindowManager manager = DockingWindowManager.getActiveInstance(); DockingWindowManager manager = DockingWindowManager.getActiveInstance();
ComponentProvider provider = manager.getActiveComponentProvider();
ActionContext localContext = provider == null ? null : provider.getActionContext(null); ActionContext context = manager.getActionContext(this);
ActionContext actionContext = localContext == null ? new ActionContext() : localContext;
return actionContext; if (context == null) {
context = new ActionContext();
}
return context;
} }
protected List<DockingActionIf> getStateActions() { protected List<DockingActionIf> getStateActions() {
@@ -113,11 +113,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
@Override @Override
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
DockingWindowManager.clearMouseOverHelp(); DockingWindowManager.clearMouseOverHelp();
ActionContext context = getActionContext(); ActionContext context = windowManager.getActionContext(toolBarAction);
if (!toolBarAction.isValidContext(context)) {
return;
}
context.setSourceObject(event.getSource()); context.setSourceObject(event.getSource());
@@ -139,14 +135,6 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
return toolBarAction.getName(); return toolBarAction.getName();
} }
private ActionContext getActionContext() {
ComponentProvider provider = getComponentProvider();
ActionContext context = provider == null ? null : provider.getActionContext(null);
final ActionContext actionContext =
context == null ? new ActionContext(provider, null) : context;
return actionContext;
}
private ComponentProvider getComponentProvider() { private ComponentProvider getComponentProvider() {
DockingWindowManager manager = windowManager; DockingWindowManager manager = windowManager;
if (manager == null) { if (manager == null) {
@@ -80,7 +80,7 @@ public class FakeDockingTool extends AbstractDockingTool {
} }
@Override @Override
public ActionContext getGlobalActionContext() { public ActionContext getDefaultToolContext() {
return null; return null;
} }
} }
@@ -1472,8 +1472,8 @@ public abstract class PluginTool extends AbstractDockingTool {
} }
@Override @Override
public ActionContext getGlobalActionContext() { public ActionContext getDefaultToolContext() {
return winMgr.getGlobalActionContext(); return winMgr.getDefaultToolContext();
} }
@Override @Override
@@ -95,10 +95,13 @@ class DataComponent extends DataDB {
} }
DataType pdt = parent.getBaseDataType(); DataType pdt = parent.getBaseDataType();
if (pdt instanceof Composite) { if (pdt instanceof Composite) {
DataTypeComponent c = ((Composite) pdt).getComponent(indexInParent); Composite composite = (Composite) pdt;
if (c == null) { // if we are deleted, the parent may not have as many components as it used to,
// so if our index is bigger than the number of components, then we are deleted.
if (indexInParent >= composite.getNumComponents()) {
return true; return true;
} }
DataTypeComponent c = composite.getComponent(indexInParent);
component = c; component = c;
dataType = c.getDataType(); dataType = c.getDataType();
offset = component.getOffset(); offset = component.getOffset();