mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-30 10:35:31 +08:00
Merge remote-tracking branch 'origin/GT-3115-dragonmacer-decompiler-find-actions'
This commit is contained in:
+3
-1
@@ -25,6 +25,7 @@ import docking.DockingUtils;
|
|||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
||||||
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.data.Composite;
|
import ghidra.program.model.data.Composite;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
@@ -32,6 +33,7 @@ import ghidra.util.*;
|
|||||||
|
|
||||||
public abstract class AbstractFindReferencesDataTypeAction extends DockingAction {
|
public abstract class AbstractFindReferencesDataTypeAction extends DockingAction {
|
||||||
|
|
||||||
|
private static final String HELP_TOPIC = HelpTopics.FIND_REFERENCES;
|
||||||
public static final String NAME = "Find References To";
|
public static final String NAME = "Find References To";
|
||||||
public static final KeyStroke DEFAULT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_F,
|
public static final KeyStroke DEFAULT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_F,
|
||||||
DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.SHIFT_DOWN_MASK);
|
DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||||
@@ -46,7 +48,7 @@ public abstract class AbstractFindReferencesDataTypeAction extends DockingAction
|
|||||||
super(name, owner, KeyBindingType.SHARED);
|
super(name, owner, KeyBindingType.SHARED);
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
|
|
||||||
setHelpLocation(new HelpLocation("LocationReferencesPlugin", "Data_Types"));
|
setHelpLocation(new HelpLocation(HELP_TOPIC, "Data_Types"));
|
||||||
setDescription("Shows all uses of the selected data type");
|
setDescription("Shows all uses of the selected data type");
|
||||||
|
|
||||||
initKeyStroke(defaultKeyStroke);
|
initKeyStroke(defaultKeyStroke);
|
||||||
|
|||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.actions;
|
||||||
|
|
||||||
|
import docking.action.KeyBindingType;
|
||||||
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
|
import ghidra.app.context.NavigatableContextAction;
|
||||||
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.util.AddressFieldLocation;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only shows addresses to the code unit at the address for the current context. This differs
|
||||||
|
* from the normal 'find references' action in that it will find references by inspecting
|
||||||
|
* context for more information, potentially searching for more than just direct references to
|
||||||
|
* the code unit at the current address.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractFindReferencesToAddressAction extends NavigatableContextAction {
|
||||||
|
|
||||||
|
public static final String NAME = "Show References To Address";
|
||||||
|
private static final String HELP_TOPIC = "LocationReferencesPlugin";
|
||||||
|
|
||||||
|
private PluginTool tool;
|
||||||
|
|
||||||
|
protected AbstractFindReferencesToAddressAction(PluginTool tool, String owner) {
|
||||||
|
super(NAME, owner, KeyBindingType.SHARED);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
setDescription("Shows references to the current Instruction or Data");
|
||||||
|
setHelpLocation(new HelpLocation(HELP_TOPIC, "Show_Refs_To_Code_Unit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(NavigatableActionContext context) {
|
||||||
|
|
||||||
|
LocationReferencesService service = tool.getService(LocationReferencesService.class);
|
||||||
|
if (service == null) {
|
||||||
|
Msg.showError(this, null, "Missing Plugin",
|
||||||
|
"The " + LocationReferencesService.class.getSimpleName() + " is not installed.\n" +
|
||||||
|
"Please add the plugin implementing this service.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program program = context.getProgram();
|
||||||
|
ProgramLocation location = getLocation(context);
|
||||||
|
Address address = location.getAddress();
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
CodeUnit cu = listing.getCodeUnitContaining(address);
|
||||||
|
|
||||||
|
int[] path = location.getComponentPath();
|
||||||
|
if (cu instanceof Data) {
|
||||||
|
Data outerData = (Data) cu;
|
||||||
|
Data data = outerData.getComponent(location.getComponentPath());
|
||||||
|
address = data.getMinAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressFieldLocation addressLocation =
|
||||||
|
new AddressFieldLocation(program, address, path, address.toString(), 0);
|
||||||
|
service.showReferencesToLocation(addressLocation, context.getNavigatable());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForContext(NavigatableActionContext context) {
|
||||||
|
|
||||||
|
Program program = context.getProgram();
|
||||||
|
ProgramLocation location = getLocation(context);
|
||||||
|
if (location == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address address = location.getAddress();
|
||||||
|
if (address == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
CodeUnit cu = listing.getCodeUnitContaining(address);
|
||||||
|
if (cu == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProgramLocation getLocation(NavigatableActionContext context) {
|
||||||
|
return context.getLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
|
import docking.action.KeyBindingType;
|
||||||
|
|
||||||
public abstract class NavigatableContextAction extends DockingAction {
|
public abstract class NavigatableContextAction extends DockingAction {
|
||||||
|
|
||||||
@@ -26,6 +27,10 @@ public abstract class NavigatableContextAction extends DockingAction {
|
|||||||
super(name, owner);
|
super(name, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NavigatableContextAction(String name, String owner, KeyBindingType type) {
|
||||||
|
super(name, owner, type);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
if (!(context instanceof NavigatableActionContext)) {
|
if (!(context instanceof NavigatableActionContext)) {
|
||||||
|
|||||||
+22
-19
@@ -15,15 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.compositeeditor;
|
package ghidra.app.plugin.core.compositeeditor;
|
||||||
|
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
|
||||||
import ghidra.framework.options.Options;
|
|
||||||
import ghidra.program.model.data.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.action.KeyBindingData;
|
import docking.action.KeyBindingData;
|
||||||
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A CompositeEditorActionManager manages the actions for a single composite editor.
|
* A CompositeEditorActionManager manages the actions for a single composite editor.
|
||||||
@@ -32,7 +31,8 @@ import docking.action.KeyBindingData;
|
|||||||
*/
|
*/
|
||||||
public class CompositeEditorActionManager {
|
public class CompositeEditorActionManager {
|
||||||
private CompositeEditorProvider provider;
|
private CompositeEditorProvider provider;
|
||||||
private ArrayList<CompositeEditorTableAction> editorActions = new ArrayList<CompositeEditorTableAction>();
|
private ArrayList<CompositeEditorTableAction> editorActions =
|
||||||
|
new ArrayList<CompositeEditorTableAction>();
|
||||||
private ArrayList<CompositeEditorTableAction> favoritesActions =
|
private ArrayList<CompositeEditorTableAction> favoritesActions =
|
||||||
new ArrayList<CompositeEditorTableAction>();
|
new ArrayList<CompositeEditorTableAction>();
|
||||||
private ArrayList<CycleGroupAction> cycleGroupActions = new ArrayList<CycleGroupAction>();
|
private ArrayList<CycleGroupAction> cycleGroupActions = new ArrayList<CycleGroupAction>();
|
||||||
@@ -46,9 +46,7 @@ public class CompositeEditorActionManager {
|
|||||||
* Constructor
|
* Constructor
|
||||||
* <BR> NOTE: After constructing a manager, you must call setEditorModel()
|
* <BR> NOTE: After constructing a manager, you must call setEditorModel()
|
||||||
* and setParentComponent() for the actions to work.
|
* and setParentComponent() for the actions to work.
|
||||||
* @param plugin the plugin that owns this composite editor action manager
|
* @param provider the provider that owns this composite editor action manager
|
||||||
* @param program the associated program for obtaining data types.
|
|
||||||
* @param dataTypeMgrService the data type manager service for the
|
|
||||||
* favorites and cycle groups.
|
* favorites and cycle groups.
|
||||||
*/
|
*/
|
||||||
public CompositeEditorActionManager(CompositeEditorProvider provider) {
|
public CompositeEditorActionManager(CompositeEditorProvider provider) {
|
||||||
@@ -56,7 +54,8 @@ public class CompositeEditorActionManager {
|
|||||||
this.dataTypeMgrService = provider.dtmService;
|
this.dataTypeMgrService = provider.dtmService;
|
||||||
adapter = new DataTypeManagerChangeListenerAdapter() {
|
adapter = new DataTypeManagerChangeListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) {
|
public void favoritesChanged(DataTypeManager dtm, DataTypePath path,
|
||||||
|
boolean isFavorite) {
|
||||||
setFavoritesActions(dataTypeMgrService.getFavorites());
|
setFavoritesActions(dataTypeMgrService.getFavorites());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -192,8 +191,8 @@ public class CompositeEditorActionManager {
|
|||||||
*/
|
*/
|
||||||
public void setEditorActions(CompositeEditorTableAction[] actions) {
|
public void setEditorActions(CompositeEditorTableAction[] actions) {
|
||||||
editorActions.clear();
|
editorActions.clear();
|
||||||
for (int i = 0; i < actions.length; i++) {
|
for (CompositeEditorTableAction action : actions) {
|
||||||
editorActions.add(actions[i]);
|
editorActions.add(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,20 +225,24 @@ public class CompositeEditorActionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void notifyActionsAdded(ArrayList<? extends CompositeEditorTableAction> actions) {
|
private void notifyActionsAdded(ArrayList<? extends CompositeEditorTableAction> actions) {
|
||||||
if (actions.size() <= 0)
|
if (actions.size() <= 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
int length = listeners.size();
|
int length = listeners.size();
|
||||||
CompositeEditorTableAction[] cea = actions.toArray(new CompositeEditorTableAction[actions.size()]);
|
CompositeEditorTableAction[] cea =
|
||||||
|
actions.toArray(new CompositeEditorTableAction[actions.size()]);
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
listeners.get(i).actionsAdded(cea);
|
listeners.get(i).actionsAdded(cea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyActionsRemoved(ArrayList<? extends CompositeEditorTableAction> actions) {
|
private void notifyActionsRemoved(ArrayList<? extends CompositeEditorTableAction> actions) {
|
||||||
if (actions.size() <= 0)
|
if (actions.size() <= 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
int length = listeners.size();
|
int length = listeners.size();
|
||||||
CompositeEditorTableAction[] cea = actions.toArray(new CompositeEditorTableAction[actions.size()]);
|
CompositeEditorTableAction[] cea =
|
||||||
|
actions.toArray(new CompositeEditorTableAction[actions.size()]);
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
listeners.get(i).actionsRemoved(cea);
|
listeners.get(i).actionsRemoved(cea);
|
||||||
}
|
}
|
||||||
@@ -252,14 +255,14 @@ public class CompositeEditorActionManager {
|
|||||||
// Update the editor actions here.
|
// Update the editor actions here.
|
||||||
// The favorites and cycle groups get handled by stateChanged() and cyclegroupChanged().
|
// The favorites and cycle groups get handled by stateChanged() and cyclegroupChanged().
|
||||||
CompositeEditorTableAction[] actions = getEditorActions();
|
CompositeEditorTableAction[] actions = getEditorActions();
|
||||||
for (int i = 0; i < actions.length; i++) {
|
for (CompositeEditorTableAction action : actions) {
|
||||||
String actionName = actions[i].getFullName();
|
String actionName = action.getFullName();
|
||||||
if (actionName.equals(name)) {
|
if (actionName.equals(name)) {
|
||||||
KeyStroke actionKs = actions[i].getKeyBinding();
|
KeyStroke actionKs = action.getKeyBinding();
|
||||||
KeyStroke oldKs = (KeyStroke) oldValue;
|
KeyStroke oldKs = (KeyStroke) oldValue;
|
||||||
KeyStroke newKs = (KeyStroke) newValue;
|
KeyStroke newKs = (KeyStroke) newValue;
|
||||||
if (actionKs == oldKs) {
|
if (actionKs == oldKs) {
|
||||||
actions[i].setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
|
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -27,7 +27,7 @@ import ghidra.program.model.data.CycleGroup;
|
|||||||
*/
|
*/
|
||||||
public class CycleGroupAction extends CompositeEditorTableAction {
|
public class CycleGroupAction extends CompositeEditorTableAction {
|
||||||
|
|
||||||
private final static String GROUP_NAME = CYCLE_ACTION_GROUP;
|
private final static String GROUP_NAME = DATA_ACTION_GROUP;
|
||||||
private CycleGroup cycleGroup;
|
private CycleGroup cycleGroup;
|
||||||
|
|
||||||
public CycleGroupAction(CompositeEditorProvider provider, CycleGroup cycleGroup) {
|
public CycleGroupAction(CompositeEditorProvider provider, CycleGroup cycleGroup) {
|
||||||
@@ -35,7 +35,7 @@ public class CycleGroupAction extends CompositeEditorTableAction {
|
|||||||
new String[] { "Cycle", cycleGroup.getName() },
|
new String[] { "Cycle", cycleGroup.getName() },
|
||||||
new String[] { "Cycle", cycleGroup.getName() }, null, KeyBindingType.SHARED);
|
new String[] { "Cycle", cycleGroup.getName() }, null, KeyBindingType.SHARED);
|
||||||
this.cycleGroup = cycleGroup;
|
this.cycleGroup = cycleGroup;
|
||||||
|
getPopupMenuData().setParentMenuGroup(GROUP_NAME);
|
||||||
initKeyStroke(cycleGroup.getDefaultKeyStroke());
|
initKeyStroke(cycleGroup.getDefaultKeyStroke());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-18
@@ -15,12 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.compositeeditor;
|
package ghidra.app.plugin.core.compositeeditor;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
import ghidra.program.model.data.Enum;
|
||||||
|
|
||||||
import docking.ActionContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action for use in the composite data type editor.
|
* Action for use in the composite data type editor.
|
||||||
* This action has help associated with it.
|
* This action has help associated with it.
|
||||||
@@ -34,16 +33,6 @@ public class EditComponentAction extends CompositeEditorTableAction {
|
|||||||
private static String[] menuPath = new String[] { ACTION_NAME + "..." };
|
private static String[] menuPath = new String[] { ACTION_NAME + "..." };
|
||||||
private DataTypeManagerService dtmService;
|
private DataTypeManagerService dtmService;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param id
|
|
||||||
* @param group
|
|
||||||
* @param owner
|
|
||||||
* @param popupPath
|
|
||||||
* @param menuPath
|
|
||||||
* @param icon
|
|
||||||
* @param useToolbar
|
|
||||||
* @param checkBox
|
|
||||||
*/
|
|
||||||
public EditComponentAction(CompositeEditorProvider provider) {
|
public EditComponentAction(CompositeEditorProvider provider) {
|
||||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, menuPath, null);
|
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, menuPath, null);
|
||||||
this.dtmService = provider.dtmService;
|
this.dtmService = provider.dtmService;
|
||||||
@@ -51,9 +40,6 @@ public class EditComponentAction extends CompositeEditorTableAction {
|
|||||||
adjustEnablement();
|
adjustEnablement();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
int row = model.getRow();
|
int row = model.getRow();
|
||||||
@@ -80,9 +66,6 @@ public class EditComponentAction extends CompositeEditorTableAction {
|
|||||||
requestTableFocus();
|
requestTableFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see ghidra.app.plugin.datamanager.editor.CompositeEditorAction#adjustEnablement()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void adjustEnablement() {
|
public void adjustEnablement() {
|
||||||
setEnabled(model.isEditComponentAllowed());
|
setEnabled(model.isEditComponentAllowed());
|
||||||
|
|||||||
+3
-4
@@ -18,10 +18,9 @@ package ghidra.app.plugin.core.compositeeditor;
|
|||||||
public interface EditorAction extends CompositeEditorModelListener {
|
public interface EditorAction extends CompositeEditorModelListener {
|
||||||
|
|
||||||
static final String BASIC_ACTION_GROUP = "1_BASIC_EDITOR_ACTION";
|
static final String BASIC_ACTION_GROUP = "1_BASIC_EDITOR_ACTION";
|
||||||
static final String FAVORITES_ACTION_GROUP = "2_FAVORITE_DT_EDITOR_ACTION";
|
static final String DATA_ACTION_GROUP = "2_DATA_EDITOR_ACTION";
|
||||||
static final String CYCLE_ACTION_GROUP = "3_CYCLE_DT_EDITOR_ACTION";
|
static final String COMPONENT_ACTION_GROUP = "3_COMPONENT_EDITOR_ACTION";
|
||||||
static final String COMPONENT_ACTION_GROUP = "4_COMPONENT_EDITOR_ACTION";
|
static final String BITFIELD_ACTION_GROUP = "4_COMPONENT_EDITOR_ACTION";
|
||||||
static final String BITFIELD_ACTION_GROUP = "5_COMPONENT_EDITOR_ACTION";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to set the action's enablement based on the associated editor
|
* Method to set the action's enablement based on the associated editor
|
||||||
|
|||||||
+32
-44
@@ -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,9 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.compositeeditor;
|
package ghidra.app.plugin.core.compositeeditor;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.util.exception.UsrException;
|
import ghidra.util.exception.UsrException;
|
||||||
import docking.ActionContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action to apply a favorite data type.
|
* Action to apply a favorite data type.
|
||||||
@@ -27,56 +26,45 @@ import docking.ActionContext;
|
|||||||
*/
|
*/
|
||||||
public class FavoritesAction extends CompositeEditorTableAction {
|
public class FavoritesAction extends CompositeEditorTableAction {
|
||||||
|
|
||||||
private final static String GROUP_NAME = FAVORITES_ACTION_GROUP;
|
private final static String GROUP_NAME = DATA_ACTION_GROUP;
|
||||||
private DataType dataType;
|
private DataType dataType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an action for applying a favorite data type.
|
||||||
|
* @param provider the provider that owns this action
|
||||||
|
* @param dt the favorite data type
|
||||||
|
*/
|
||||||
|
public FavoritesAction(CompositeEditorProvider provider, DataType dt) {
|
||||||
|
super(provider, dt.getDisplayName(), GROUP_NAME,
|
||||||
|
new String[] { "Favorite", dt.getDisplayName() },
|
||||||
|
new String[] { "Favorite", dt.getDisplayName() }, null);
|
||||||
|
this.dataType = dt;
|
||||||
|
getPopupMenuData().setParentMenuGroup(GROUP_NAME);
|
||||||
|
adjustEnablement();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an action for applying a favorite data type.
|
|
||||||
* @param owner the plugin that owns this action
|
|
||||||
* @param dt the favorite data type
|
|
||||||
*/
|
|
||||||
public FavoritesAction(CompositeEditorProvider provider, DataType dt) {
|
|
||||||
super(provider, dt.getDisplayName(), GROUP_NAME,
|
|
||||||
new String[] { "Favorite", dt.getDisplayName() },
|
|
||||||
new String[] { "Favorite", dt.getDisplayName() },
|
|
||||||
null);
|
|
||||||
this.dataType = dt;
|
|
||||||
adjustEnablement();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the favorite data type for this action.
|
|
||||||
*/
|
|
||||||
public DataType getDataType() {
|
public DataType getDataType() {
|
||||||
return dataType;
|
return dataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
@Override
|
||||||
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
public void actionPerformed(ActionContext context) {
|
||||||
*/
|
try {
|
||||||
@Override
|
model.add(dataType);
|
||||||
public void actionPerformed(ActionContext context) {
|
}
|
||||||
try {
|
catch (UsrException e1) {
|
||||||
model.add(dataType);
|
|
||||||
} catch (UsrException e1) {
|
|
||||||
model.setStatus(e1.getMessage());
|
model.setStatus(e1.getMessage());
|
||||||
}
|
}
|
||||||
requestTableFocus();
|
requestTableFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see ghidra.app.plugin.compositeeditor.CompositeEditorAction#adjustEnablement()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void adjustEnablement() {
|
|
||||||
// Do nothing since we always want it enabled so the user gets a "doesn't fit" message.
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see ghidra.app.plugin.compositeeditor.CompositeEditorAction#getHelpName()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getHelpName() {
|
public void adjustEnablement() {
|
||||||
|
// Do nothing since we always want it enabled so the user gets a "doesn't fit" message.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpName() {
|
||||||
return "Favorite";
|
return "Favorite";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.compositeeditor;
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||||
|
import ghidra.app.util.HelpTopics;
|
||||||
|
import ghidra.program.model.data.Composite;
|
||||||
|
import ghidra.program.model.data.DataTypeComponent;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action to show references to the field in the currently selected editor row
|
||||||
|
*/
|
||||||
|
public class FindReferencesToField extends CompositeEditorTableAction {
|
||||||
|
|
||||||
|
public final static String ACTION_NAME = "Find Uses of";
|
||||||
|
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
|
||||||
|
private final static String DESCRIPTION = "Find uses of field in the selected row";
|
||||||
|
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||||
|
|
||||||
|
public FindReferencesToField(CompositeEditorProvider provider) {
|
||||||
|
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, null);
|
||||||
|
setDescription(DESCRIPTION);
|
||||||
|
adjustEnablement();
|
||||||
|
setHelpLocation(new HelpLocation(HelpTopics.FIND_REFERENCES, "Data_Types"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionContext context) {
|
||||||
|
|
||||||
|
FindAppliedDataTypesService service = tool.getService(FindAppliedDataTypesService.class);
|
||||||
|
if (service == null) {
|
||||||
|
Msg.showError(this, null, "Missing Plugin",
|
||||||
|
"The FindAppliedDataTypesService is not installed.\n" +
|
||||||
|
"Please add the plugin implementing this service.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldName = getFieldName();
|
||||||
|
Composite composite = model.getOriginalComposite();
|
||||||
|
SwingUtilities.invokeLater(
|
||||||
|
() -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFieldName() {
|
||||||
|
int[] rows = model.getSelectedComponentRows();
|
||||||
|
if (rows.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int row = rows[0];
|
||||||
|
DataTypeComponent dtComponet = model.getComponent(row);
|
||||||
|
String fieldName = dtComponet.getFieldName();
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void adjustEnablement() {
|
||||||
|
setEnabled(false);
|
||||||
|
if (model.getSelectedComponentRows().length != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Composite composite = model.getOriginalComposite();
|
||||||
|
if (composite == null) {
|
||||||
|
return; // not sure if this can happen
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldName = getFieldName();
|
||||||
|
if (fieldName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEnabled(true);
|
||||||
|
updateMenuName(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMenuName(String name) {
|
||||||
|
|
||||||
|
String menuName = ACTION_NAME + ' ' + name;
|
||||||
|
MenuData data = getPopupMenuData().cloneData();
|
||||||
|
data.setMenuPath(new String[] { menuName });
|
||||||
|
setPopupMenuData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
-17
@@ -29,21 +29,11 @@ import docking.menu.DockingCheckboxMenuItemUI;
|
|||||||
public class HexNumbersAction extends CompositeEditorTableAction implements ToggleDockingActionIf {
|
public class HexNumbersAction extends CompositeEditorTableAction implements ToggleDockingActionIf {
|
||||||
|
|
||||||
private final static String ACTION_NAME = "Show Numbers In Hex";
|
private final static String ACTION_NAME = "Show Numbers In Hex";
|
||||||
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
|
private final static String GROUP_NAME = DATA_ACTION_GROUP;
|
||||||
private final static String defaultDescription = "Show Numbers in Hexadecimal";
|
private final static String defaultDescription = "Show Numbers in Hexadecimal";
|
||||||
private static String[] defaultPath = new String[] { defaultDescription };
|
private static String[] defaultPath = new String[] { defaultDescription };
|
||||||
private boolean isSelected;
|
private boolean isSelected;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param name
|
|
||||||
* @param group
|
|
||||||
* @param owner
|
|
||||||
* @param popupPath
|
|
||||||
* @param menuPath
|
|
||||||
* @param icon
|
|
||||||
* @param useToolbar
|
|
||||||
* @param checkBox
|
|
||||||
*/
|
|
||||||
public HexNumbersAction(CompositeEditorProvider provider) {
|
public HexNumbersAction(CompositeEditorProvider provider) {
|
||||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, defaultPath, defaultPath,
|
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, defaultPath, defaultPath,
|
||||||
null);
|
null);
|
||||||
@@ -52,17 +42,11 @@ public class HexNumbersAction extends CompositeEditorTableAction implements Togg
|
|||||||
setSelected(model.isShowingNumbersInHex());
|
setSelected(model.isShowingNumbersInHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
model.displayNumbersInHex(!model.isShowingNumbersInHex());
|
model.displayNumbersInHex(!model.isShowingNumbersInHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see ghidra.app.plugin.core.compositeeditor.CompositeEditorAction#adjustEnablement()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void adjustEnablement() {
|
public void adjustEnablement() {
|
||||||
// Always enabled.
|
// Always enabled.
|
||||||
|
|||||||
+2
-1
@@ -64,12 +64,13 @@ public class StructureEditorProvider extends CompositeEditorProvider {
|
|||||||
new DeleteAction(this),
|
new DeleteAction(this),
|
||||||
new PointerAction(this),
|
new PointerAction(this),
|
||||||
new ArrayAction(this),
|
new ArrayAction(this),
|
||||||
new ShowComponentPathAction(this),
|
new FindReferencesToField(this),
|
||||||
new UnpackageAction(this),
|
new UnpackageAction(this),
|
||||||
new EditComponentAction(this),
|
new EditComponentAction(this),
|
||||||
new EditFieldAction(this),
|
new EditFieldAction(this),
|
||||||
new HexNumbersAction(this),
|
new HexNumbersAction(this),
|
||||||
new CreateInternalStructureAction(this),
|
new CreateInternalStructureAction(this),
|
||||||
|
new ShowComponentPathAction(this),
|
||||||
new AddBitFieldAction(this),
|
new AddBitFieldAction(this),
|
||||||
new EditBitFieldAction(this),
|
new EditBitFieldAction(this),
|
||||||
// new ViewBitFieldAction(this)
|
// new ViewBitFieldAction(this)
|
||||||
|
|||||||
+3
-2
@@ -118,8 +118,9 @@ public class FindReferencesToAction extends ListingContextAction {
|
|||||||
menuName += itemName;
|
menuName += itemName;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPopupMenuData(new MenuData(new String[] { "References", menuName }, null,
|
setPopupMenuData(
|
||||||
"ShowReferencesTo", MenuData.NO_MNEMONIC, Integer.toString(subGroupPosition)));
|
new MenuData(new String[] { LocationReferencesService.MENU_GROUP, menuName }, null,
|
||||||
|
"ShowReferencesTo", MenuData.NO_MNEMONIC, Integer.toString(subGroupPosition)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMenuPrefix(LocationDescriptor descriptor) {
|
private String getMenuPrefix(LocationDescriptor descriptor) {
|
||||||
|
|||||||
+10
-51
@@ -15,15 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.navigation.locationreferences;
|
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||||
|
|
||||||
import docking.action.KeyBindingType;
|
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.actions.AbstractFindReferencesToAddressAction;
|
||||||
import ghidra.app.context.ListingActionContext;
|
import ghidra.app.context.ListingActionContext;
|
||||||
import ghidra.app.context.ListingContextAction;
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.listing.*;
|
|
||||||
import ghidra.program.util.AddressFieldLocation;
|
|
||||||
import ghidra.program.util.ProgramLocation;
|
|
||||||
import ghidra.util.HelpLocation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only shows addresses to the code unit at the address for the current context. This differs
|
* Only shows addresses to the code unit at the address for the current context. This differs
|
||||||
@@ -31,58 +26,22 @@ import ghidra.util.HelpLocation;
|
|||||||
* context for more information, potentially searching for more than just direct references to
|
* context for more information, potentially searching for more than just direct references to
|
||||||
* the code unit at the current address.
|
* the code unit at the current address.
|
||||||
*/
|
*/
|
||||||
public class FindReferencesToAddressAction extends ListingContextAction {
|
public class FindReferencesToAddressAction extends AbstractFindReferencesToAddressAction {
|
||||||
|
|
||||||
private LocationReferencesPlugin plugin;
|
|
||||||
|
|
||||||
public FindReferencesToAddressAction(LocationReferencesPlugin plugin, int subGroupPosition) {
|
public FindReferencesToAddressAction(LocationReferencesPlugin plugin, int subGroupPosition) {
|
||||||
super("Show References to Address", plugin.getName(), KeyBindingType.SHARED);
|
super(plugin.getTool(), plugin.getName());
|
||||||
|
|
||||||
this.plugin = plugin;
|
setPopupMenuData(new MenuData(new String[] { LocationReferencesService.MENU_GROUP, NAME },
|
||||||
|
|
||||||
setPopupMenuData(new MenuData(new String[] { "References", "Show References to Address" },
|
|
||||||
null, "ShowReferencesTo", MenuData.NO_MNEMONIC, Integer.toString(subGroupPosition)));
|
null, "ShowReferencesTo", MenuData.NO_MNEMONIC, Integer.toString(subGroupPosition)));
|
||||||
|
|
||||||
setDescription("Shows references to the current Instruction or Data");
|
|
||||||
setHelpLocation(new HelpLocation(plugin.getName(), "Show_Refs_To_Code_Unit"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ListingActionContext context) {
|
public boolean isEnabledForContext(NavigatableActionContext context) {
|
||||||
|
if (!(context instanceof ListingActionContext)) {
|
||||||
Program program = context.getProgram();
|
// Restrict this action to the Listing. We have guilty knowledge that there are
|
||||||
ProgramLocation location = context.getLocation();
|
// other sibling classes to this one for other contexts.
|
||||||
Address address = location.getAddress();
|
|
||||||
Listing listing = program.getListing();
|
|
||||||
CodeUnit cu = listing.getCodeUnitContaining(address);
|
|
||||||
|
|
||||||
int[] path = location.getComponentPath();
|
|
||||||
if (cu instanceof Data) {
|
|
||||||
Data outerData = (Data) cu;
|
|
||||||
Data data = outerData.getComponent(location.getComponentPath());
|
|
||||||
address = data.getMinAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressFieldLocation addressLocation =
|
|
||||||
new AddressFieldLocation(program, address, path, address.toString(), 0);
|
|
||||||
plugin.showReferencesToLocation(addressLocation, context.getNavigatable());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isEnabledForContext(ListingActionContext context) {
|
|
||||||
Program program = context.getProgram();
|
|
||||||
ProgramLocation location = context.getLocation();
|
|
||||||
Address address = location.getAddress();
|
|
||||||
if (address == null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return super.isEnabledForContext(context);
|
||||||
Listing listing = program.getListing();
|
|
||||||
CodeUnit cu = listing.getCodeUnitContaining(address);
|
|
||||||
if (cu == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -16,7 +16,6 @@
|
|||||||
package ghidra.app.plugin.core.navigation.locationreferences;
|
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||||
|
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
@@ -26,6 +25,8 @@ import ghidra.util.HelpLocation;
|
|||||||
*/
|
*/
|
||||||
public interface LocationReferencesService {
|
public interface LocationReferencesService {
|
||||||
|
|
||||||
|
public static final String MENU_GROUP = "References";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the help location for help content that describes this service.
|
* Returns the help location for help content that describes this service.
|
||||||
* @return the help location for help content that describes this service.
|
* @return the help location for help content that describes this service.
|
||||||
@@ -37,8 +38,6 @@ public interface LocationReferencesService {
|
|||||||
* location.
|
* location.
|
||||||
* @param location The location for which to show references.
|
* @param location The location for which to show references.
|
||||||
* @param navigatable The navigatable in which the references should be shown
|
* @param navigatable The navigatable in which the references should be shown
|
||||||
* @throws IllegalArgumentException if a call to {@link #canProcessLocation(Program, ProgramLocation)}
|
|
||||||
* returns false.
|
|
||||||
* @throws NullPointerException if <tt>location</tt> is null.
|
* @throws NullPointerException if <tt>location</tt> is null.
|
||||||
*/
|
*/
|
||||||
public void showReferencesToLocation(ProgramLocation location, Navigatable navigatable);
|
public void showReferencesToLocation(ProgramLocation location, Navigatable navigatable);
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ public interface HelpTopics {
|
|||||||
*/
|
*/
|
||||||
public final static String EXPORTER = "ExporterPlugin";
|
public final static String EXPORTER = "ExporterPlugin";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Help Topic for references searching
|
||||||
|
*/
|
||||||
|
public final static String FIND_REFERENCES = "LocationReferencesPlugin";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of options for the help topic for the front end (Ghidra
|
* Name of options for the help topic for the front end (Ghidra
|
||||||
* Project Window).
|
* Project Window).
|
||||||
|
|||||||
+14
-6
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.test;
|
package ghidra.test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -32,6 +32,7 @@ import ghidra.framework.cmd.Command;
|
|||||||
import ghidra.framework.model.UndoableDomainObject;
|
import ghidra.framework.model.UndoableDomainObject;
|
||||||
import ghidra.framework.plugintool.Plugin;
|
import ghidra.framework.plugintool.Plugin;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.framework.plugintool.mgr.ServiceManager;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
@@ -525,26 +526,33 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the given implementations of the provided service class with the given class.
|
* Replaces the given implementations of the provided service class with the given class.
|
||||||
*
|
*
|
||||||
|
* @param tool the tool whose services to update (optional)
|
||||||
* @param service the service to override
|
* @param service the service to override
|
||||||
* @param replacement the new version of the service
|
* @param replacement the new version of the service
|
||||||
|
* @param <T> the service type
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> void replaceService(Class<? extends T> service,
|
public static <T> void replaceService(PluginTool tool, Class<? extends T> service,
|
||||||
Class<? extends T> replacement) {
|
T replacement) {
|
||||||
|
|
||||||
|
ServiceManager serviceManager = (ServiceManager) getInstanceField("serviceMgr", tool);
|
||||||
|
|
||||||
Set<Class<?>> extentions =
|
Set<Class<?>> extentions =
|
||||||
(Set<Class<?>>) getInstanceField("extensionPoints", ClassSearcher.class);
|
(Set<Class<?>>) getInstanceField("extensionPoints", ClassSearcher.class);
|
||||||
HashSet<Class<?>> set = new HashSet<>(extentions);
|
Set<Class<?>> set = new HashSet<>(extentions);
|
||||||
Iterator<Class<?>> iterator = set.iterator();
|
Iterator<Class<?>> iterator = set.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Class<?> c = iterator.next();
|
Class<?> c = iterator.next();
|
||||||
if (service.isAssignableFrom(c)) {
|
if (service.isAssignableFrom(c)) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
|
T instance = tool.getService(service);
|
||||||
|
serviceManager.removeService(service, instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set.add(replacement);
|
set.add(replacement.getClass());
|
||||||
|
serviceManager.addService(service, replacement);
|
||||||
|
|
||||||
Set<Class<?>> newExtensionPoints = new HashSet<>(set);
|
Set<Class<?>> newExtensionPoints = new HashSet<>(set);
|
||||||
setInstanceField("extensionPoints", ClassSearcher.class, newExtensionPoints);
|
setInstanceField("extensionPoints", ClassSearcher.class, newExtensionPoints);
|
||||||
|
|||||||
+1
-1
@@ -704,7 +704,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
if (token == null) {
|
if (token == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Address address = DecompilerUtils.getClosestAddress(token);
|
Address address = DecompilerUtils.getClosestAddress(getProgram(), token);
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
address = DecompilerUtils.findAddressBefore(layoutMgr.getFields(), token);
|
address = DecompilerUtils.findAddressBefore(layoutMgr.getFields(), token);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-2
@@ -216,7 +216,7 @@ public class DecompilerUtils {
|
|||||||
* Returns the function represented by the given token. This will be either the
|
* Returns the function represented by the given token. This will be either the
|
||||||
* decompiled function or a function referenced within the decompiled function.
|
* decompiled function or a function referenced within the decompiled function.
|
||||||
*
|
*
|
||||||
* @param program the progam
|
* @param program the program
|
||||||
* @param token the token
|
* @param token the token
|
||||||
* @return the function
|
* @return the function
|
||||||
*/
|
*/
|
||||||
@@ -336,7 +336,14 @@ public class DecompilerUtils {
|
|||||||
return addressSet.intersects(minAddress, maxAddress);
|
return addressSet.intersects(minAddress, maxAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Address getClosestAddress(ClangToken token) {
|
public static Address getClosestAddress(Program program, ClangToken token) {
|
||||||
|
|
||||||
|
if (token instanceof ClangFuncNameToken) {
|
||||||
|
// special case: we know that name tokens do not have addresses
|
||||||
|
Function function = getFunction(program, (ClangFuncNameToken) token);
|
||||||
|
return function.getEntryPoint();
|
||||||
|
}
|
||||||
|
|
||||||
Address address = token.getMinAddress();
|
Address address = token.getMinAddress();
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
return address;
|
return address;
|
||||||
|
|||||||
+36
-3
@@ -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.
|
||||||
@@ -18,10 +17,14 @@ package ghidra.app.plugin.core.decompile;
|
|||||||
|
|
||||||
import ghidra.app.context.NavigatableActionContext;
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
import ghidra.app.context.RestrictedAddressSetContext;
|
import ghidra.app.context.RestrictedAddressSetContext;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|
||||||
public class DecompilerActionContext extends NavigatableActionContext implements
|
public class DecompilerActionContext extends NavigatableActionContext
|
||||||
RestrictedAddressSetContext {
|
implements RestrictedAddressSetContext {
|
||||||
private final Address functionEntryPoint;
|
private final Address functionEntryPoint;
|
||||||
private final boolean isDecompiling;
|
private final boolean isDecompiling;
|
||||||
|
|
||||||
@@ -40,4 +43,34 @@ public class DecompilerActionContext extends NavigatableActionContext implements
|
|||||||
return isDecompiling;
|
return isDecompiling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DecompilerProvider getComponentProvider() {
|
||||||
|
return (DecompilerProvider) super.getComponentProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecompilerPanel getDecompilerPanel() {
|
||||||
|
return getComponentProvider().getDecompilerPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProgramLocation getLocation() {
|
||||||
|
|
||||||
|
// prefer the selection over the current location
|
||||||
|
DecompilerPanel decompilerPanel = getDecompilerPanel();
|
||||||
|
ClangToken token = decompilerPanel.getSelectedToken();
|
||||||
|
if (token == null) {
|
||||||
|
token = decompilerPanel.getTokenAtCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address address = DecompilerUtils.getClosestAddress(program, token);
|
||||||
|
if (address == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ProgramLocation(program, address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-2
@@ -44,7 +44,6 @@ import ghidra.program.model.address.*;
|
|||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
@@ -780,14 +779,35 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||||||
//
|
//
|
||||||
// Search
|
// Search
|
||||||
//
|
//
|
||||||
|
|
||||||
String searchGroup = "comment2 - Search Group";
|
String searchGroup = "comment2 - Search Group";
|
||||||
subGroupPosition = 0; // reset for the next group
|
subGroupPosition = 0; // reset for the next group
|
||||||
|
|
||||||
findAction = new FindAction(tool, controller, owner);
|
findAction = new FindAction(tool, controller, owner);
|
||||||
setGroupInfo(findAction, searchGroup, subGroupPosition++);
|
setGroupInfo(findAction, searchGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
//
|
||||||
|
// References
|
||||||
|
//
|
||||||
|
|
||||||
|
// note: set the menu group so that the 'References' group is with the 'Find' action
|
||||||
|
String referencesParentGroup = searchGroup;
|
||||||
|
|
||||||
findReferencesAction = new FindReferencesToDataTypeAction(owner, tool, controller);
|
findReferencesAction = new FindReferencesToDataTypeAction(owner, tool, controller);
|
||||||
setGroupInfo(findReferencesAction, searchGroup, subGroupPosition++);
|
setGroupInfo(findReferencesAction, searchGroup, subGroupPosition++);
|
||||||
|
findReferencesAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
|
||||||
|
|
||||||
|
FindReferencesToSymbolAction findReferencesToSymbolAction =
|
||||||
|
new FindReferencesToSymbolAction(tool, owner);
|
||||||
|
setGroupInfo(findReferencesToSymbolAction, searchGroup, subGroupPosition++);
|
||||||
|
findReferencesToSymbolAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
|
||||||
|
addLocalAction(findReferencesToSymbolAction);
|
||||||
|
|
||||||
|
FindReferencesToAddressAction findReferencesToAdressAction =
|
||||||
|
new FindReferencesToAddressAction(tool, owner);
|
||||||
|
setGroupInfo(findReferencesToAdressAction, searchGroup, subGroupPosition++);
|
||||||
|
findReferencesToAdressAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
|
||||||
|
addLocalAction(findReferencesToAdressAction);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Options
|
// Options
|
||||||
@@ -839,7 +859,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||||||
private void setGroupInfo(DockingAction action, String group, int subGroupPosition) {
|
private void setGroupInfo(DockingAction action, String group, int subGroupPosition) {
|
||||||
MenuData popupMenuData = action.getPopupMenuData();
|
MenuData popupMenuData = action.getPopupMenuData();
|
||||||
popupMenuData.setMenuGroup(group);
|
popupMenuData.setMenuGroup(group);
|
||||||
popupMenuData.setMenuSubGroup(Integer.toString(subGroupPosition++));
|
popupMenuData.setMenuSubGroup(Integer.toString(subGroupPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void graphServiceRemoved() {
|
private void graphServiceRemoved() {
|
||||||
|
|||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.actions.AbstractFindReferencesToAddressAction;
|
||||||
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action to show all references to the given address
|
||||||
|
*/
|
||||||
|
public class FindReferencesToAddressAction extends AbstractFindReferencesToAddressAction {
|
||||||
|
|
||||||
|
public FindReferencesToAddressAction(PluginTool tool, String owner) {
|
||||||
|
super(tool, owner);
|
||||||
|
|
||||||
|
setPopupMenuData(new MenuData(new String[] { LocationReferencesService.MENU_GROUP, NAME }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ProgramLocation getLocation(NavigatableActionContext context) {
|
||||||
|
if (!(context instanceof DecompilerActionContext)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return context.getLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-2
@@ -21,6 +21,7 @@ import ghidra.app.actions.AbstractFindReferencesDataTypeAction;
|
|||||||
import ghidra.app.decompiler.*;
|
import ghidra.app.decompiler.*;
|
||||||
import ghidra.app.decompiler.component.*;
|
import ghidra.app.decompiler.component.*;
|
||||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.pcode.HighVariable;
|
import ghidra.program.model.pcode.HighVariable;
|
||||||
@@ -36,7 +37,8 @@ public class FindReferencesToDataTypeAction extends AbstractFindReferencesDataTy
|
|||||||
super(tool, NAME, owner, DEFAULT_KEY_STROKE);
|
super(tool, NAME, owner, DEFAULT_KEY_STROKE);
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
|
||||||
setPopupMenuData(new MenuData(new String[] { "Find Uses of " }));
|
setPopupMenuData(
|
||||||
|
new MenuData(new String[] { LocationReferencesService.MENU_GROUP, "Find Uses of " }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -136,7 +138,7 @@ public class FindReferencesToDataTypeAction extends AbstractFindReferencesDataTy
|
|||||||
}
|
}
|
||||||
|
|
||||||
MenuData data = getPopupMenuData().cloneData();
|
MenuData data = getPopupMenuData().cloneData();
|
||||||
data.setMenuPath(new String[] { menuName });
|
data.setMenuPath(new String[] { LocationReferencesService.MENU_GROUP, menuName });
|
||||||
setPopupMenuData(data);
|
setPopupMenuData(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.DockingAction;
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerProvider;
|
||||||
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
|
||||||
|
import ghidra.app.util.HelpTopics;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
import ghidra.program.model.symbol.SymbolTable;
|
||||||
|
import ghidra.program.util.LabelFieldLocation;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action to show all references to the symbol under the cursor in the Decompiler
|
||||||
|
*/
|
||||||
|
public class FindReferencesToSymbolAction extends DockingAction {
|
||||||
|
|
||||||
|
private static final String MENU_ITEM_TEXT = "Find References to";
|
||||||
|
public static final String NAME = "Find References to Symbol";
|
||||||
|
private PluginTool tool;
|
||||||
|
|
||||||
|
public FindReferencesToSymbolAction(PluginTool tool, String owner) {
|
||||||
|
super(NAME, owner);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
setPopupMenuData(
|
||||||
|
new MenuData(new String[] { LocationReferencesService.MENU_GROUP, MENU_ITEM_TEXT }));
|
||||||
|
setHelpLocation(new HelpLocation(HelpTopics.FIND_REFERENCES, HelpTopics.FIND_REFERENCES));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionContext context) {
|
||||||
|
// Note: we intentionally do this check here and not in isEnabledForContext() so
|
||||||
|
// that global events do not get triggered.
|
||||||
|
DecompilerActionContext decompilerContext = (DecompilerActionContext) context;
|
||||||
|
if (decompilerContext.isDecompiling()) {
|
||||||
|
Msg.showInfo(getClass(), context.getComponentProvider().getComponent(),
|
||||||
|
"Decompiler Action Blocked",
|
||||||
|
"You cannot perform Decompiler actions while the Decompiler is busy");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationReferencesService service = tool.getService(LocationReferencesService.class);
|
||||||
|
if (service == null) {
|
||||||
|
Msg.showError(this, null, "Missing Plugin",
|
||||||
|
"The " + LocationReferencesService.class.getSimpleName() + " is not installed.\n" +
|
||||||
|
"Please add the plugin implementing this service.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol symbol = getSymbol(decompilerContext);
|
||||||
|
LabelFieldLocation location = new LabelFieldLocation(symbol);
|
||||||
|
DecompilerProvider provider = decompilerContext.getComponentProvider();
|
||||||
|
service.showReferencesToLocation(location, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
if (!(context instanceof DecompilerActionContext)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerActionContext decompilerActionContext = (DecompilerActionContext) context;
|
||||||
|
if (decompilerActionContext.isDecompiling()) {
|
||||||
|
// Let this through here and handle it in actionPerformed(). This lets us alert
|
||||||
|
// the user that they have to wait until the decompile is finished. If we are not
|
||||||
|
// enabled at this point, then the keybinding will be propagated to the global
|
||||||
|
// actions, which is not what we want.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol symbol = getSymbol((DecompilerActionContext) context);
|
||||||
|
if (symbol == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMenuName(symbol);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Symbol getSymbol(DecompilerActionContext context) {
|
||||||
|
|
||||||
|
ProgramLocation location = context.getLocation();
|
||||||
|
if (location == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address address = location.getAddress();
|
||||||
|
Program program = context.getProgram();
|
||||||
|
SymbolTable symbolTable = program.getSymbolTable();
|
||||||
|
Symbol symbol = symbolTable.getPrimarySymbol(address);
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMenuName(Symbol symbol) {
|
||||||
|
|
||||||
|
if (symbol == null) {
|
||||||
|
return; // not sure if this can happen
|
||||||
|
}
|
||||||
|
|
||||||
|
String symbolName = symbol.getName(false);
|
||||||
|
String menuName = MENU_ITEM_TEXT + ' ' + symbolName;
|
||||||
|
|
||||||
|
MenuData data = getPopupMenuData().cloneData();
|
||||||
|
data.setMenuPath(new String[] { LocationReferencesService.MENU_GROUP, menuName });
|
||||||
|
setPopupMenuData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
-4
@@ -26,21 +26,28 @@ import docking.ActionContext;
|
|||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||||
import ghidra.app.actions.AbstractFindReferencesDataTypeAction;
|
import ghidra.app.actions.AbstractFindReferencesDataTypeAction;
|
||||||
|
import ghidra.app.actions.AbstractFindReferencesToAddressAction;
|
||||||
|
import ghidra.app.nav.Navigatable;
|
||||||
|
import ghidra.app.plugin.core.decompile.actions.FindReferencesToSymbolAction;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider;
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider;
|
||||||
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.DataTypeReference;
|
||||||
import ghidra.app.services.DataTypeReferenceFinder;
|
import ghidra.app.services.DataTypeReferenceFinder;
|
||||||
import ghidra.program.model.data.Composite;
|
import ghidra.program.model.data.Composite;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import mockit.Mock;
|
import mockit.*;
|
||||||
import mockit.MockUp;
|
|
||||||
|
|
||||||
public abstract class AbstractDecompilerFindReferencesActionTest extends AbstractDecompilerTest {
|
public abstract class AbstractDecompilerFindReferencesActionTest extends AbstractDecompilerTest {
|
||||||
|
|
||||||
protected DockingActionIf findReferencesAction;
|
protected DockingActionIf findReferencesAction;
|
||||||
|
protected DockingActionIf findReferencesToSymbolAction;
|
||||||
|
protected DockingActionIf findReferencesToAddressAction;
|
||||||
|
|
||||||
protected SpyDataTypeReferenceFinder<DataTypeReferenceFinder> spyReferenceFinder;
|
protected SpyDataTypeReferenceFinder<DataTypeReferenceFinder> spyReferenceFinder;
|
||||||
|
protected SpyLocationReferencesService<LocationReferencesService> spyLocationReferenceService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
@@ -49,6 +56,9 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
|
|||||||
super.setUp();
|
super.setUp();
|
||||||
|
|
||||||
findReferencesAction = getAction(decompiler, AbstractFindReferencesDataTypeAction.NAME);
|
findReferencesAction = getAction(decompiler, AbstractFindReferencesDataTypeAction.NAME);
|
||||||
|
findReferencesToSymbolAction = getAction(decompiler, FindReferencesToSymbolAction.NAME);
|
||||||
|
findReferencesToAddressAction =
|
||||||
|
getAction(decompiler, AbstractFindReferencesToAddressAction.NAME);
|
||||||
|
|
||||||
installSpyDataTypeReferenceFinder();
|
installSpyDataTypeReferenceFinder();
|
||||||
}
|
}
|
||||||
@@ -56,7 +66,9 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
|
|||||||
private void installSpyDataTypeReferenceFinder() {
|
private void installSpyDataTypeReferenceFinder() {
|
||||||
|
|
||||||
spyReferenceFinder = new SpyDataTypeReferenceFinder<>();
|
spyReferenceFinder = new SpyDataTypeReferenceFinder<>();
|
||||||
replaceService(DataTypeReferenceFinder.class, StubDataTypeReferenceFinder.class);
|
replaceService(tool, DataTypeReferenceFinder.class, new StubDataTypeReferenceFinder());
|
||||||
|
|
||||||
|
spyLocationReferenceService = new SpyLocationReferencesService<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertFindAllReferencesToCompositeFieldWasCalled() {
|
protected void assertFindAllReferencesToCompositeFieldWasCalled() {
|
||||||
@@ -69,7 +81,15 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
|
|||||||
assertEquals(1, spyReferenceFinder.getFindDataTypeReferencesCallCount());
|
assertEquals(1, spyReferenceFinder.getFindDataTypeReferencesCallCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ThreadedTableModel<?, ?> perfomFindDataTypes() {
|
protected void assertFindAllReferencesToSymbolWasCalled() {
|
||||||
|
assertEquals(1, spyLocationReferenceService.getShowReferencesCallCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertFindAllReferencesToAddressWasCalled() {
|
||||||
|
assertEquals(1, spyLocationReferenceService.getShowReferencesCallCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ThreadedTableModel<?, ?> performFindDataTypes() {
|
||||||
// tricky business - the 'finder' is being run in a thread pool, so we must wait for that
|
// tricky business - the 'finder' is being run in a thread pool, so we must wait for that
|
||||||
// model to finish loading
|
// model to finish loading
|
||||||
|
|
||||||
@@ -80,11 +100,34 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ThreadedTableModel<?, ?> performFindReferencesToAddress() {
|
||||||
|
// tricky business - the 'finder' is being run in a thread pool, so we must wait for that
|
||||||
|
// model to finish loading
|
||||||
|
|
||||||
|
DecompilerActionContext context = new DecompilerActionContext(provider, addr(0x0), false);
|
||||||
|
performAction(findReferencesToAddressAction, context, true);
|
||||||
|
|
||||||
|
ThreadedTableModel<?, ?> model = waitForSearchProvider();
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ThreadedTableModel<?, ?> performFindReferencesToSymbol() {
|
||||||
|
// tricky business - the 'finder' is being run in a thread pool, so we must wait for that
|
||||||
|
// model to finish loading
|
||||||
|
|
||||||
|
DecompilerActionContext context = new DecompilerActionContext(provider, addr(0x0), false);
|
||||||
|
performAction(findReferencesToSymbolAction, context, true);
|
||||||
|
|
||||||
|
ThreadedTableModel<?, ?> model = waitForSearchProvider();
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
protected ThreadedTableModel<?, ?> waitForSearchProvider() {
|
protected ThreadedTableModel<?, ?> waitForSearchProvider() {
|
||||||
|
|
||||||
LocationReferencesProvider searchProvider =
|
LocationReferencesProvider searchProvider =
|
||||||
(LocationReferencesProvider) tool.getComponentProvider(LocationReferencesProvider.NAME);
|
(LocationReferencesProvider) tool.getComponentProvider(LocationReferencesProvider.NAME);
|
||||||
|
|
||||||
|
assertNotNull("Could not find the Location References Provider", searchProvider);
|
||||||
ThreadedTableModel<?, ?> model = getTableModel(searchProvider);
|
ThreadedTableModel<?, ?> model = getTableModel(searchProvider);
|
||||||
waitForTableModel(model);
|
waitForTableModel(model);
|
||||||
|
|
||||||
@@ -138,4 +181,21 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
|
|||||||
return compositeFieldReferencesCallCount.get();
|
return compositeFieldReferencesCallCount.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SpyLocationReferencesService<T extends LocationReferencesService>
|
||||||
|
extends MockUp<T> {
|
||||||
|
|
||||||
|
private AtomicInteger showReferencesCallCount = new AtomicInteger();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
public void showReferencesToLocation(Invocation invocation, ProgramLocation location,
|
||||||
|
Navigatable navigatable) {
|
||||||
|
showReferencesCallCount.incrementAndGet();
|
||||||
|
invocation.proceed(location, navigatable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getShowReferencesCallCount() {
|
||||||
|
return showReferencesCallCount.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-3
@@ -122,7 +122,7 @@ public class DecompilerFindReferencesToActionTest
|
|||||||
int line = 2;
|
int line = 2;
|
||||||
int charPosition = 17;
|
int charPosition = 17;
|
||||||
setDecompilerLocation(line, charPosition);
|
setDecompilerLocation(line, charPosition);
|
||||||
perfomFindDataTypes();
|
performFindDataTypes();
|
||||||
|
|
||||||
assertFindAllReferencesToDataTypeWasCalled();
|
assertFindAllReferencesToDataTypeWasCalled();
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ public class DecompilerFindReferencesToActionTest
|
|||||||
int line = 5;
|
int line = 5;
|
||||||
int charPosition = 2;
|
int charPosition = 2;
|
||||||
setDecompilerLocation(line, charPosition);
|
setDecompilerLocation(line, charPosition);
|
||||||
perfomFindDataTypes();
|
performFindDataTypes();
|
||||||
|
|
||||||
assertFindAllReferencesToDataTypeWasCalled();
|
assertFindAllReferencesToDataTypeWasCalled();
|
||||||
}
|
}
|
||||||
@@ -184,11 +184,67 @@ public class DecompilerFindReferencesToActionTest
|
|||||||
int line = 5;
|
int line = 5;
|
||||||
int charPosition = 7;
|
int charPosition = 7;
|
||||||
setDecompilerLocation(line, charPosition);
|
setDecompilerLocation(line, charPosition);
|
||||||
perfomFindDataTypes();
|
performFindDataTypes();
|
||||||
|
|
||||||
assertFindAllReferencesToCompositeFieldWasCalled();
|
assertFindAllReferencesToCompositeFieldWasCalled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindDataTypeReferences_ToCastSymbol() throws Exception {
|
||||||
|
|
||||||
|
// void lzw_decompress(mytable *table,char *intstream,int len,char *output)
|
||||||
|
// output = (char *)output_string(output,&local_2c);
|
||||||
|
|
||||||
|
decompile(0x0804873f); // lzw_decompress()
|
||||||
|
|
||||||
|
int line = 18;
|
||||||
|
int charPosition = 11;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
performFindDataTypes();
|
||||||
|
|
||||||
|
assertFindAllReferencesToDataTypeWasCalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindDataTypeReferences_ToCurrentAddress() throws Exception {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Decomp of 'init_string':
|
||||||
|
|
||||||
|
1|
|
||||||
|
2| void init_string(mystring *ptr)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| ptr->alloc = 0;
|
||||||
|
6| return;
|
||||||
|
7| }
|
||||||
|
8|
|
||||||
|
*/
|
||||||
|
|
||||||
|
decompile(INIT_STRING_ADDR);
|
||||||
|
|
||||||
|
int line = 5;
|
||||||
|
int charPosition = 7;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
performFindReferencesToAddress();
|
||||||
|
|
||||||
|
assertFindAllReferencesToAddressWasCalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindDataTypeReferences_ToCurrentFunction() throws Exception {
|
||||||
|
|
||||||
|
decompile(INIT_STRING_ADDR);
|
||||||
|
|
||||||
|
int line = 2;
|
||||||
|
int charPosition = 10; // function name
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
performFindReferencesToSymbol();
|
||||||
|
|
||||||
|
assertFindAllReferencesToSymbolWasCalled();
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|||||||
+1
-1
@@ -120,7 +120,7 @@ public class DecompilerFindReferencesToNestedStructureActionTest
|
|||||||
int line = 9;
|
int line = 9;
|
||||||
int charPosition = 44;
|
int charPosition = 44;
|
||||||
setDecompilerLocation(line, charPosition);
|
setDecompilerLocation(line, charPosition);
|
||||||
perfomFindDataTypes();
|
performFindDataTypes();
|
||||||
|
|
||||||
assertFindAllReferencesToCompositeFieldWasCalled();
|
assertFindAllReferencesToCompositeFieldWasCalled();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -395,6 +395,13 @@ public abstract class DockingAction implements DockingActionIf {
|
|||||||
buffer.append('\n');
|
buffer.append('\n');
|
||||||
buffer.append(" MENU GROUP: ").append(menuBarData.getMenuGroup());
|
buffer.append(" MENU GROUP: ").append(menuBarData.getMenuGroup());
|
||||||
buffer.append('\n');
|
buffer.append('\n');
|
||||||
|
|
||||||
|
String parentGroup = popupMenuData.getParentMenuGroup();
|
||||||
|
if (parentGroup != null) {
|
||||||
|
buffer.append(" PARENT GROUP: ").append(parentGroup);
|
||||||
|
buffer.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
Icon icon = menuBarData.getMenuIcon();
|
Icon icon = menuBarData.getMenuIcon();
|
||||||
if (icon != null && icon instanceof ImageIconWrapper) {
|
if (icon != null && icon instanceof ImageIconWrapper) {
|
||||||
ImageIconWrapper wrapper = (ImageIconWrapper) icon;
|
ImageIconWrapper wrapper = (ImageIconWrapper) icon;
|
||||||
@@ -412,6 +419,12 @@ public abstract class DockingAction implements DockingActionIf {
|
|||||||
buffer.append(" POPUP GROUP: ").append(popupMenuData.getMenuGroup());
|
buffer.append(" POPUP GROUP: ").append(popupMenuData.getMenuGroup());
|
||||||
buffer.append('\n');
|
buffer.append('\n');
|
||||||
|
|
||||||
|
String parentGroup = popupMenuData.getParentMenuGroup();
|
||||||
|
if (parentGroup != null) {
|
||||||
|
buffer.append(" PARENT GROUP: ").append(parentGroup);
|
||||||
|
buffer.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
String menuSubGroup = popupMenuData.getMenuSubGroup();
|
String menuSubGroup = popupMenuData.getMenuSubGroup();
|
||||||
if (menuSubGroup != MenuData.NO_SUBGROUP) {
|
if (menuSubGroup != MenuData.NO_SUBGROUP) {
|
||||||
buffer.append(" POPUP SUB-GROUP: ").append(menuSubGroup);
|
buffer.append(" POPUP SUB-GROUP: ").append(menuSubGroup);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class MenuData {
|
|||||||
private Icon icon;
|
private Icon icon;
|
||||||
private int mnemonic = NO_MNEMONIC;
|
private int mnemonic = NO_MNEMONIC;
|
||||||
private String menuGroup;
|
private String menuGroup;
|
||||||
|
private String parentMenuGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The subgroup string. This string is used to sort items within a
|
* The subgroup string. This string is used to sort items within a
|
||||||
@@ -73,11 +74,14 @@ public class MenuData {
|
|||||||
this.icon = menuData.icon;
|
this.icon = menuData.icon;
|
||||||
this.menuGroup = menuData.menuGroup;
|
this.menuGroup = menuData.menuGroup;
|
||||||
this.menuSubGroup = menuData.menuSubGroup;
|
this.menuSubGroup = menuData.menuSubGroup;
|
||||||
|
this.parentMenuGroup = menuData.parentMenuGroup;
|
||||||
this.mnemonic = menuData.mnemonic;
|
this.mnemonic = menuData.mnemonic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MenuData cloneData() {
|
public MenuData cloneData() {
|
||||||
return new MenuData(menuPath, icon, menuGroup, mnemonic, menuSubGroup);
|
MenuData newData = new MenuData(menuPath, icon, menuGroup, mnemonic, menuSubGroup);
|
||||||
|
newData.parentMenuGroup = parentMenuGroup;
|
||||||
|
return newData;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void firePropertyChanged(MenuData oldData) {
|
protected void firePropertyChanged(MenuData oldData) {
|
||||||
@@ -113,11 +117,20 @@ public class MenuData {
|
|||||||
/**
|
/**
|
||||||
* Returns the icon assigned to this action's menu. Null indicates that this action does not
|
* Returns the icon assigned to this action's menu. Null indicates that this action does not
|
||||||
* have a menu icon
|
* have a menu icon
|
||||||
|
* @return the icon
|
||||||
*/
|
*/
|
||||||
public Icon getMenuIcon() {
|
public Icon getMenuIcon() {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the group for the menu item created by this data. This value determines which
|
||||||
|
* section inside of the tool's popup menu the menu item will be placed. If you need to
|
||||||
|
* control the ordering <b>within a section</b>, then provide a value for
|
||||||
|
* {@link #setMenuSubGroup(String)}.
|
||||||
|
*
|
||||||
|
* @return the group
|
||||||
|
*/
|
||||||
public String getMenuGroup() {
|
public String getMenuGroup() {
|
||||||
return menuGroup;
|
return menuGroup;
|
||||||
}
|
}
|
||||||
@@ -126,11 +139,24 @@ public class MenuData {
|
|||||||
* Returns the subgroup string. This string is used to sort items within a
|
* Returns the subgroup string. This string is used to sort items within a
|
||||||
* {@link #getMenuGroup() toolbar group}. This value is not required. If not specified,
|
* {@link #getMenuGroup() toolbar group}. This value is not required. If not specified,
|
||||||
* then the value will effectively place this item at the end of its specified group.
|
* then the value will effectively place this item at the end of its specified group.
|
||||||
|
* @return the sub-group
|
||||||
*/
|
*/
|
||||||
public String getMenuSubGroup() {
|
public String getMenuSubGroup() {
|
||||||
return menuSubGroup;
|
return menuSubGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the group for the parent menu of the menu item created by this data. That is,
|
||||||
|
* this value is effectively the same as {@link #getMenuGroup()}, but for the parent menu
|
||||||
|
* item of this data's item. Setting this value is only valid if the {@link #getMenuPath()}
|
||||||
|
* has a length greater than 1.
|
||||||
|
*
|
||||||
|
* @return the parent group
|
||||||
|
*/
|
||||||
|
public String getParentMenuGroup() {
|
||||||
|
return parentMenuGroup;
|
||||||
|
}
|
||||||
|
|
||||||
public void setIcon(Icon newIcon) {
|
public void setIcon(Icon newIcon) {
|
||||||
if (icon == newIcon) {
|
if (icon == newIcon) {
|
||||||
return;
|
return;
|
||||||
@@ -154,11 +180,38 @@ public class MenuData {
|
|||||||
if (SystemUtilities.isEqual(menuSubGroup, newSubGroup)) {
|
if (SystemUtilities.isEqual(menuSubGroup, newSubGroup)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newSubGroup == null) {
|
||||||
|
newSubGroup = NO_SUBGROUP;
|
||||||
|
}
|
||||||
|
|
||||||
MenuData oldData = cloneData();
|
MenuData oldData = cloneData();
|
||||||
menuSubGroup = newSubGroup;
|
menuSubGroup = newSubGroup;
|
||||||
firePropertyChanged(oldData);
|
firePropertyChanged(oldData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the description in {@link #getParentMenuGroup()}
|
||||||
|
*
|
||||||
|
* @param newParentMenuGroup the parent group
|
||||||
|
*/
|
||||||
|
public void setParentMenuGroup(String newParentMenuGroup) {
|
||||||
|
if (menuPath.length <= 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot set the parent menu group for a menu item " + "that has no parent");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SystemUtilities.isEqual(parentMenuGroup, newParentMenuGroup)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuData oldData = cloneData();
|
||||||
|
parentMenuGroup = newParentMenuGroup;
|
||||||
|
firePropertyChanged(oldData);
|
||||||
|
|
||||||
|
this.parentMenuGroup = newParentMenuGroup;
|
||||||
|
}
|
||||||
|
|
||||||
public void setMenuPath(String[] newPath) {
|
public void setMenuPath(String[] newPath) {
|
||||||
if (newPath == null || newPath.length == 0) {
|
if (newPath == null || newPath.length == 0) {
|
||||||
throw new IllegalArgumentException("Menu path cannot be null or empty");
|
throw new IllegalArgumentException("Menu path cannot be null or empty");
|
||||||
|
|||||||
@@ -102,28 +102,8 @@ public class MenuManager implements ManagedMenuItem {
|
|||||||
checkForSwingThread();
|
checkForSwingThread();
|
||||||
resetMenus();
|
resetMenus();
|
||||||
MenuData menuData = usePopupPath ? action.getPopupMenuData() : action.getMenuBarData();
|
MenuData menuData = usePopupPath ? action.getPopupMenuData() : action.getMenuBarData();
|
||||||
String[] actionMenuPath = menuData.getMenuPath();
|
if (isSubMenu(menuData)) {
|
||||||
if (actionMenuPath.length > level + 1) {
|
MenuManager mgr = getSubMenu(menuData);
|
||||||
String subMenuName = actionMenuPath[level];
|
|
||||||
String cleanSubMenuName = stripMnemonicAmp(subMenuName);
|
|
||||||
MenuManager mgr = subMenus.get(cleanSubMenuName);
|
|
||||||
|
|
||||||
if (mgr == null) {
|
|
||||||
char mnemonic = getMnemonicKey(subMenuName);
|
|
||||||
|
|
||||||
int submenuLevel = level + 1;
|
|
||||||
String[] submenuPath = new String[submenuLevel];
|
|
||||||
System.arraycopy(actionMenuPath, 0, submenuPath, 0, submenuLevel);
|
|
||||||
String submenuGroup = menuGroupMap.getMenuGroup(submenuPath);
|
|
||||||
if (submenuGroup == null) {
|
|
||||||
submenuGroup = subMenuName;
|
|
||||||
}
|
|
||||||
|
|
||||||
mgr = new MenuManager(cleanSubMenuName, submenuPath, mnemonic, submenuLevel,
|
|
||||||
submenuGroup, usePopupPath, menuHandler, menuGroupMap);
|
|
||||||
subMenus.put(cleanSubMenuName, mgr);
|
|
||||||
managedMenuItems.add(mgr);
|
|
||||||
}
|
|
||||||
mgr.addAction(action);
|
mgr.addAction(action);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -131,6 +111,70 @@ public class MenuManager implements ManagedMenuItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSubMenu(MenuData menuData) {
|
||||||
|
String[] actionMenuPath = menuData.getMenuPath();
|
||||||
|
return actionMenuPath.length > level + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuManager getSubMenu(MenuData menuData) {
|
||||||
|
|
||||||
|
String[] fullPath = menuData.getMenuPath();
|
||||||
|
String displayName = fullPath[level];
|
||||||
|
char mnemonic = getMnemonicKey(displayName);
|
||||||
|
String realName = stripMnemonicAmp(displayName);
|
||||||
|
MenuManager subMenu = subMenus.get(realName);
|
||||||
|
if (subMenu != null) {
|
||||||
|
return subMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
int subMenuLevel = level + 1;
|
||||||
|
String[] subMenuPath = new String[subMenuLevel];
|
||||||
|
System.arraycopy(fullPath, 0, subMenuPath, 0, subMenuLevel);
|
||||||
|
|
||||||
|
String subMenuGroup = getSubMenuGroup(menuData, realName, subMenuPath);
|
||||||
|
subMenu = new MenuManager(realName, subMenuPath, mnemonic, subMenuLevel, subMenuGroup,
|
||||||
|
usePopupPath, menuHandler, menuGroupMap);
|
||||||
|
subMenus.put(realName, subMenu);
|
||||||
|
managedMenuItems.add(subMenu);
|
||||||
|
|
||||||
|
return subMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSubMenuGroup(MenuData menuData, String menuName, String[] subMenuPath) {
|
||||||
|
|
||||||
|
// prefer the group defined in the menu data, if any
|
||||||
|
String pullRightGroup = getPullRightMenuGroup(menuData);
|
||||||
|
if (pullRightGroup != null) {
|
||||||
|
return pullRightGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the global registry
|
||||||
|
pullRightGroup = menuGroupMap.getMenuGroup(subMenuPath);
|
||||||
|
if (pullRightGroup != null) {
|
||||||
|
return pullRightGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// default to the menu name
|
||||||
|
return menuName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPullRightMenuGroup(MenuData menuData) {
|
||||||
|
|
||||||
|
// note: currently, the client can specify the group for the pull-right menu only for
|
||||||
|
// the immediate parent of the menu item. We can change this later if we find
|
||||||
|
// we have a need for a multi-level cascaded menu that needs to specify groups for
|
||||||
|
// each pull-right in the menu path
|
||||||
|
|
||||||
|
String[] actionMenuPath = menuData.getMenuPath();
|
||||||
|
int leafLevel = actionMenuPath.length - 1;
|
||||||
|
boolean isParentOfLeaf = level == (leafLevel - 1);
|
||||||
|
if (!isParentOfLeaf) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuData.getParentMenuGroup();
|
||||||
|
}
|
||||||
|
|
||||||
public DockingActionIf getAction(String actionName) {
|
public DockingActionIf getAction(String actionName) {
|
||||||
for (ManagedMenuItem item : managedMenuItems) {
|
for (ManagedMenuItem item : managedMenuItems) {
|
||||||
if (item instanceof MenuItemManager) {
|
if (item instanceof MenuItemManager) {
|
||||||
@@ -158,17 +202,18 @@ public class MenuManager implements ManagedMenuItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Removes the Mnemonic indicator character (&) from the text.
|
* Removes the Mnemonic indicator character (&) from the text
|
||||||
* @param str the text to strip.
|
* @param text the text to strip
|
||||||
|
* @return the stripped mnemonic
|
||||||
*/
|
*/
|
||||||
public static String stripMnemonicAmp(String str) {
|
public static String stripMnemonicAmp(String text) {
|
||||||
int ampLoc = str.indexOf('&');
|
int ampLoc = text.indexOf('&');
|
||||||
if (ampLoc < 0) {
|
if (ampLoc < 0) {
|
||||||
return str;
|
return text;
|
||||||
}
|
}
|
||||||
String s = str.substring(0, ampLoc);
|
String s = text.substring(0, ampLoc);
|
||||||
if (ampLoc < (str.length() - 1)) {
|
if (ampLoc < (text.length() - 1)) {
|
||||||
s += str.substring(++ampLoc);
|
s += text.substring(++ampLoc);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -182,7 +227,8 @@ public class MenuManager implements ManagedMenuItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Menu hierarchy of all the actions.
|
* Returns a Menu hierarchy of all the actions
|
||||||
|
* @return the menu
|
||||||
*/
|
*/
|
||||||
public JMenu getMenu() {
|
public JMenu getMenu() {
|
||||||
if (menu == null) {
|
if (menu == null) {
|
||||||
@@ -236,9 +282,6 @@ public class MenuManager implements ManagedMenuItem {
|
|||||||
return menuSubGroup;
|
return menuSubGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see docking.menu.ManagedMenuItem#dispose()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
for (ManagedMenuItem item : managedMenuItems) {
|
for (ManagedMenuItem item : managedMenuItems) {
|
||||||
@@ -249,7 +292,8 @@ public class MenuManager implements ManagedMenuItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a JPopupMenu for the action hierarchy.
|
* Returns a JPopupMenu for the action hierarchy
|
||||||
|
* @return the popup menu
|
||||||
*/
|
*/
|
||||||
public JPopupMenu getPopupMenu() {
|
public JPopupMenu getPopupMenu() {
|
||||||
if (popupMenu == null) {
|
if (popupMenu == null) {
|
||||||
|
|||||||
+1
-1
@@ -101,7 +101,7 @@ public class ServiceManager {
|
|||||||
*
|
*
|
||||||
* @see #setServiceAddedNotificationsOn(boolean)
|
* @see #setServiceAddedNotificationsOn(boolean)
|
||||||
*/
|
*/
|
||||||
public synchronized <T> void addService(Class<T> interfaceClass, T service) {
|
public synchronized <T> void addService(Class<? extends T> interfaceClass, T service) {
|
||||||
List<Object> list =
|
List<Object> list =
|
||||||
servicesByInterface.computeIfAbsent(interfaceClass, (k) -> new ArrayList<>());
|
servicesByInterface.computeIfAbsent(interfaceClass, (k) -> new ArrayList<>());
|
||||||
if (list.contains(service)) {
|
if (list.contains(service)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user