mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-3411 - Actions - fixed key bindings being removed from options after
tool restart
This commit is contained in:
parent
8fbdec4eca
commit
599728ec49
38 changed files with 577 additions and 401 deletions
|
@ -31,14 +31,14 @@ import ghidra.util.exception.AssertException;
|
|||
*/
|
||||
public class AddBitFieldAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static String ACTION_NAME = "Add Bitfield";
|
||||
public final static String ACTION_NAME = "Add Bitfield";
|
||||
private final static String GROUP_NAME = BITFIELD_ACTION_GROUP;
|
||||
private final static String DESCRIPTION =
|
||||
"Add a bitfield at the position of a selected component";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
public AddBitFieldAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, null);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, null);
|
||||
setDescription(DESCRIPTION);
|
||||
if (!(model instanceof CompEditorModel)) {
|
||||
throw new AssertException("unsupported use");
|
||||
|
|
|
@ -27,13 +27,13 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class ApplyAction extends CompositeEditorTableAction {
|
||||
|
||||
public final static String ACTION_NAME = "Apply Editor Changes";
|
||||
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
|
||||
private final static ImageIcon APPLY_ICON = ResourceManager.loadImage("images/disk.png");
|
||||
private final static String[] popupPath = new String[] { "Apply Edits" };
|
||||
private final static ImageIcon ICON = ResourceManager.loadImage("images/disk.png");
|
||||
private final static String[] POPUP_PATH = new String[] { "Apply Edits" };
|
||||
|
||||
public ApplyAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + "Apply Editor Changes", GROUP_NAME, popupPath, null,
|
||||
APPLY_ICON);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
|
||||
setDescription("Apply editor changes");
|
||||
adjustEnablement();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,17 +15,15 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import resources.ResourceManager;
|
||||
import docking.ActionContext;
|
||||
import docking.action.KeyBindingData;
|
||||
|
||||
import ghidra.util.exception.UsrException;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Action for use in the composite data type editor.
|
||||
|
@ -34,33 +31,33 @@ import docking.action.KeyBindingData;
|
|||
*/
|
||||
public class ArrayAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static ImageIcon arrayIcon = ResourceManager.loadImage("images/Array.png");
|
||||
private final static String ACTION_NAME = "Create Array";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Create an array";
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, 0);
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private final static ImageIcon ICON = ResourceManager.loadImage("images/Array.png");
|
||||
public final static String ACTION_NAME = "Create Array";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Create an array";
|
||||
private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, 0);
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
public ArrayAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, arrayIcon);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData( new KeyBindingData( keyStroke ) );
|
||||
public ArrayAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
adjustEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
try {
|
||||
model.createArray();
|
||||
} catch (UsrException e1) {
|
||||
}
|
||||
catch (UsrException e1) {
|
||||
model.setStatus(e1.getMessage());
|
||||
}
|
||||
requestTableFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isArrayAllowed());
|
||||
}
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isArrayAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,63 +15,50 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
|
||||
import docking.action.KeyBindingData;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.UsrException;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
||||
public class ClearAction extends CompositeEditorTableAction {
|
||||
|
||||
public final static String ACTION_NAME = "Clear Components";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static ImageIcon CLEAR_ICON = ResourceManager.loadImage("images/erase16.png");
|
||||
private final static String[] popupPath = new String[] { "Clear" };
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, 0);
|
||||
private final static ImageIcon ICON = ResourceManager.loadImage("images/erase16.png");
|
||||
private final static String[] POPUP_PATH = new String[] { "Clear" };
|
||||
private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_C, 0);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param owner
|
||||
* @param cycleGroup
|
||||
*/
|
||||
public ClearAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + "Clear Components",
|
||||
GROUP_NAME, popupPath, null, CLEAR_ICON);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
|
||||
setDescription("Clear the selected components");
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
adjustEnablement();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
||||
*/
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
try {
|
||||
model.clearSelectedComponents();
|
||||
} catch (OutOfMemoryError memExc) {
|
||||
}
|
||||
catch (OutOfMemoryError memExc) {
|
||||
String errMsg = "Couldn't clear components. Out of memory.";
|
||||
Msg.showError(this, null, "Out of Memory", errMsg, memExc);
|
||||
} catch (UsrException ue) {
|
||||
}
|
||||
catch (UsrException ue) {
|
||||
model.setStatus(ue.getMessage());
|
||||
}
|
||||
requestTableFocus();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.datamanager.editor.CompositeEditorAction#adjustEnablement()
|
||||
*/
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isClearAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isClearAllowed());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,7 @@ package ghidra.app.plugin.core.compositeeditor;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.action.KeyBindingData;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
/**
|
||||
|
@ -247,26 +243,4 @@ public class CompositeEditorActionManager {
|
|||
listeners.get(i).actionsRemoved(cea);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.framework.options.OptionsChangeListener#optionsChanged(ghidra.framework.options.Options, java.lang.String, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public void optionsChanged(Options options, String name, Object oldValue, Object newValue) {
|
||||
// Update the editor actions here.
|
||||
// The favorites and cycle groups get handled by stateChanged() and cyclegroupChanged().
|
||||
CompositeEditorTableAction[] actions = getEditorActions();
|
||||
for (CompositeEditorTableAction action : actions) {
|
||||
String actionName = action.getFullName();
|
||||
if (actionName.equals(name)) {
|
||||
KeyStroke actionKs = action.getKeyBinding();
|
||||
KeyStroke oldKs = (KeyStroke) oldValue;
|
||||
KeyStroke newKs = (KeyStroke) newValue;
|
||||
if (actionKs == oldKs) {
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,12 +46,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
|
|||
|
||||
public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group,
|
||||
String[] popupPath, String[] menuPath, ImageIcon icon) {
|
||||
this(provider, name, group, popupPath, menuPath, icon, KeyBindingType.INDIVIDUAL);
|
||||
}
|
||||
|
||||
public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group,
|
||||
String[] popupPath, String[] menuPath, ImageIcon icon, KeyBindingType kbType) {
|
||||
super(name, provider.plugin.getName(), kbType);
|
||||
super(name, provider.plugin.getName(), KeyBindingType.SHARED);
|
||||
this.provider = provider;
|
||||
model = provider.getModel();
|
||||
if (menuPath != null) {
|
||||
|
|
|
@ -33,17 +33,16 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class CreateInternalStructureAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static ImageIcon createInternalStructureIcon =
|
||||
private final static ImageIcon ICON =
|
||||
ResourceManager.loadImage("images/cstruct.png");
|
||||
private final static String ACTION_NAME = "Create Structure From Selection";
|
||||
public final static String ACTION_NAME = "Create Structure From Selection";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION =
|
||||
"Create a new structure from the selected components and replace them with it.";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
public CreateInternalStructureAction(StructureEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null,
|
||||
createInternalStructureIcon);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
setDescription(DESCRIPTION);
|
||||
adjustEnablement();
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import javax.swing.KeyStroke;
|
|||
|
||||
import docking.ActionContext;
|
||||
import docking.action.KeyBindingData;
|
||||
import docking.action.KeyBindingType;
|
||||
import ghidra.program.model.data.CycleGroup;
|
||||
|
||||
/**
|
||||
|
@ -33,7 +32,7 @@ public class CycleGroupAction extends CompositeEditorTableAction {
|
|||
public CycleGroupAction(CompositeEditorProvider provider, CycleGroup cycleGroup) {
|
||||
super(provider, cycleGroup.getName(), GROUP_NAME,
|
||||
new String[] { "Cycle", cycleGroup.getName() },
|
||||
new String[] { "Cycle", cycleGroup.getName() }, null, KeyBindingType.SHARED);
|
||||
new String[] { "Cycle", cycleGroup.getName() }, null);
|
||||
this.cycleGroup = cycleGroup;
|
||||
getPopupMenuData().setParentMenuGroup(GROUP_NAME);
|
||||
initKeyStroke(cycleGroup.getDefaultKeyStroke());
|
||||
|
|
|
@ -30,17 +30,16 @@ import resources.ResourceManager;
|
|||
|
||||
public class DeleteAction extends CompositeEditorTableAction {
|
||||
|
||||
public final static String ACTION_NAME = "Delete Components";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static ImageIcon DELETE_ICON =
|
||||
ResourceManager.loadImage("images/edit-delete.png");
|
||||
private final static ImageIcon ICON = ResourceManager.loadImage("images/edit-delete.png");
|
||||
private final static String[] popupPath = new String[] { "Delete" };
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
|
||||
private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
|
||||
|
||||
public DeleteAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + "Delete Components", GROUP_NAME, popupPath, null,
|
||||
DELETE_ICON);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, ICON);
|
||||
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
setDescription("Delete the selected components");
|
||||
adjustEnablement();
|
||||
}
|
||||
|
|
|
@ -32,19 +32,19 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class DuplicateAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static ImageIcon duplicateDataIcon =
|
||||
ResourceManager.loadImage("images/DuplicateData.png");
|
||||
private final static String ACTION_NAME = "Duplicate Component";
|
||||
private final static ImageIcon ICON = ResourceManager.loadImage("images/DuplicateData.png");
|
||||
public final static String ACTION_NAME = "Duplicate Component";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Duplicate the selected component";
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.ALT_DOWN_MASK);
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private final static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
private final static KeyStroke KEY_STROKE =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.ALT_DOWN_MASK);
|
||||
|
||||
public DuplicateAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null,
|
||||
duplicateDataIcon);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null,
|
||||
ICON);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
adjustEnablement();
|
||||
}
|
||||
|
||||
|
|
|
@ -36,17 +36,17 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class DuplicateMultipleAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static ImageIcon duplicateMultipleIcon =
|
||||
private final static ImageIcon ICON =
|
||||
ResourceManager.loadImage("images/MultiDuplicateData.png");
|
||||
private final static String ACTION_NAME = "Duplicate Multiple of Component";
|
||||
public final static String ACTION_NAME = "Duplicate Multiple of Component";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Duplicate multiple of the selected component";
|
||||
private final static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_M, InputEvent.ALT_DOWN_MASK);
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
|
||||
public DuplicateMultipleAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null,
|
||||
duplicateMultipleIcon);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
adjustEnablement();
|
||||
|
|
|
@ -33,13 +33,13 @@ import ghidra.util.exception.AssertException;
|
|||
*/
|
||||
public class EditBitFieldAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static String ACTION_NAME = "Edit Bitfield";
|
||||
public final static String ACTION_NAME = "Edit Bitfield";
|
||||
private final static String GROUP_NAME = BITFIELD_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Edit an existing bitfield";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
public EditBitFieldAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, null);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, null);
|
||||
setDescription(DESCRIPTION);
|
||||
if (!(model instanceof CompEditorModel)) {
|
||||
throw new AssertException("unsupported use");
|
||||
|
|
|
@ -26,15 +26,15 @@ import ghidra.program.model.data.Enum;
|
|||
*/
|
||||
public class EditComponentAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static String ACTION_NAME = "Edit Component";
|
||||
public final static String ACTION_NAME = "Edit Component";
|
||||
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Edit the selected component";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
private static String[] MENU_PATH = new String[] { ACTION_NAME };
|
||||
private DataTypeManagerService dtmService;
|
||||
|
||||
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, POPUP_PATH, MENU_PATH, null);
|
||||
this.dtmService = provider.dtmService;
|
||||
setDescription(DESCRIPTION);
|
||||
adjustEnablement();
|
||||
|
|
|
@ -32,14 +32,14 @@ public class EditFieldAction extends CompositeEditorTableAction {
|
|||
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
|
||||
private final static String DESCRIPTION =
|
||||
"Edit the first editable field of the selected component.";
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0);
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0);
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
private static String[] MENU_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
public EditFieldAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, menuPath, null);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, MENU_PATH, null);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
adjustEnablement();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,16 +28,16 @@ import docking.menu.DockingCheckboxMenuItemUI;
|
|||
*/
|
||||
public class HexNumbersAction extends CompositeEditorTableAction implements ToggleDockingActionIf {
|
||||
|
||||
private final static String ACTION_NAME = "Show Numbers In Hex";
|
||||
public final static String ACTION_NAME = "Show Numbers In Hex";
|
||||
private final static String GROUP_NAME = DATA_ACTION_GROUP;
|
||||
private final static String defaultDescription = "Show Numbers in Hexadecimal";
|
||||
private static String[] defaultPath = new String[] { defaultDescription };
|
||||
private final static String DESCRIPTION = "Show Numbers in Hexadecimal";
|
||||
private static String[] PATH = new String[] { DESCRIPTION };
|
||||
private boolean isSelected;
|
||||
|
||||
public HexNumbersAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, defaultPath, defaultPath,
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, PATH, PATH,
|
||||
null);
|
||||
setDescription(defaultDescription);
|
||||
setDescription(DESCRIPTION);
|
||||
setEnabled(true);
|
||||
setSelected(model.isShowingNumbersInHex());
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import java.awt.Event;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
@ -33,19 +33,20 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class InsertUndefinedAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static ImageIcon insertUndefinedIcon =
|
||||
private final static ImageIcon ICON =
|
||||
ResourceManager.loadImage("images/Plus.png");
|
||||
private final static String ACTION_NAME = "Insert Undefined Byte";
|
||||
public final static String ACTION_NAME = "Insert Undefined Byte";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Insert an undefined byte before the selection";
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_U, Event.ALT_MASK);
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
private final static KeyStroke KEY_STROKE =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.ALT_DOWN_MASK);
|
||||
|
||||
public InsertUndefinedAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null,
|
||||
insertUndefinedIcon);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
adjustEnablement();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,18 +15,16 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
import java.awt.Event;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import resources.ResourceManager;
|
||||
import docking.ActionContext;
|
||||
import docking.action.KeyBindingData;
|
||||
|
||||
import ghidra.util.exception.UsrException;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Action for use in the composite data type editor.
|
||||
|
@ -35,34 +32,35 @@ import docking.action.KeyBindingData;
|
|||
*/
|
||||
public class MoveDownAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static ImageIcon moveDownIcon = ResourceManager.loadImage("images/down.png");
|
||||
private final static String ACTION_NAME = "Move Components Down";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Move the selected components down";
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.ALT_MASK);
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private final static ImageIcon ICON = ResourceManager.loadImage("images/down.png");
|
||||
public final static String ACTION_NAME = "Move Components Down";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Move the selected components down";
|
||||
private final static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
public MoveDownAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, moveDownIcon);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData( new KeyBindingData( keyStroke ) );
|
||||
private final static KeyStroke KEY_STROKE =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK);
|
||||
|
||||
public MoveDownAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
adjustEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
try {
|
||||
model.moveDown();
|
||||
} catch (UsrException e1) {
|
||||
}
|
||||
catch (UsrException e1) {
|
||||
model.setStatus(e1.getMessage(), true);
|
||||
}
|
||||
requestTableFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isMoveDownAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isMoveDownAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,18 +15,16 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
import java.awt.Event;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import resources.ResourceManager;
|
||||
import docking.ActionContext;
|
||||
import docking.action.KeyBindingData;
|
||||
|
||||
import ghidra.util.exception.UsrException;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Action for use in the composite data type editor.
|
||||
|
@ -35,34 +32,36 @@ import docking.action.KeyBindingData;
|
|||
*/
|
||||
public class MoveUpAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static ImageIcon moveUpIcon = ResourceManager.loadImage("images/up.png");
|
||||
private final static String ACTION_NAME = "Move Components Up";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Move selected components up";
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.ALT_MASK);
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private final static ImageIcon ICON = ResourceManager.loadImage("images/up.png");
|
||||
public final static String ACTION_NAME = "Move Components Up";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Move selected components up";
|
||||
private final static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
public MoveUpAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, moveUpIcon);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData( new KeyBindingData( keyStroke ) );
|
||||
private final static KeyStroke KEY_STROKE =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK);
|
||||
|
||||
public MoveUpAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
adjustEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
try {
|
||||
model.moveUp();
|
||||
} catch (UsrException e1) {
|
||||
}
|
||||
catch (UsrException e1) {
|
||||
model.setStatus(e1.getMessage(), true);
|
||||
}
|
||||
requestTableFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isMoveUpAllowed());
|
||||
}
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isMoveUpAllowed());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,10 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import static docking.KeyBindingPrecedence.DefaultLevel;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.util.exception.UsrException;
|
||||
import static docking.KeyBindingPrecedence.*;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
|
@ -27,6 +23,9 @@ import javax.swing.KeyStroke;
|
|||
|
||||
import docking.ActionContext;
|
||||
import docking.action.KeyBindingData;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
/**
|
||||
* Action for use in the composite data type editor.
|
||||
|
@ -34,45 +33,43 @@ import docking.action.KeyBindingData;
|
|||
*/
|
||||
public class PointerAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static String ACTION_NAME = "Create Pointer";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Create a pointer(s) on the selection";
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P, 0);
|
||||
private static DataType POINTER_DT = new PointerDataType();
|
||||
public final static String ACTION_NAME = "Create Pointer";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Create a pointer(s) on the selection";
|
||||
private final static DataType POINTER_DT = new PointerDataType();
|
||||
private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_P, 0);
|
||||
|
||||
public PointerAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, null, null, null);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData( new KeyBindingData( keyStroke, DefaultLevel ) );
|
||||
adjustEnablement();
|
||||
}
|
||||
public PointerAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, null, null, null);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE, DefaultLevel));
|
||||
adjustEnablement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
public void actionPerformed(ActionContext context) {
|
||||
try {
|
||||
model.add(POINTER_DT);
|
||||
} catch (UsrException e1) {
|
||||
}
|
||||
catch (UsrException e1) {
|
||||
model.setStatus(e1.getMessage());
|
||||
}
|
||||
requestTableFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
// Do nothing since we always want it enabled so the user gets a "doesn't fit" message.
|
||||
return model.getRowCount() > 0
|
||||
&& model.hasSelection()
|
||||
&& model.isContiguousSelection();
|
||||
return model.getRowCount() > 0 && model.hasSelection() && model.isContiguousSelection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
public void adjustEnablement() {
|
||||
// Allow the user to get a "doesn't fit" message on contiguous selection.
|
||||
// Also allow message indicating you must have a selection.
|
||||
boolean hasSelection = model.hasSelection();
|
||||
boolean enable = model.getRowCount() > 0
|
||||
&& (!hasSelection || (hasSelection && model.isContiguousSelection()));
|
||||
setEnabled(enable);
|
||||
}
|
||||
boolean enable = model.getRowCount() > 0 &&
|
||||
(!hasSelection || (hasSelection && model.isContiguousSelection()));
|
||||
setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,32 +25,19 @@ import ghidra.program.model.data.DataTypeComponent;
|
|||
*/
|
||||
public class ShowComponentPathAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static String ACTION_NAME = "Show Component Path";
|
||||
public final static String ACTION_NAME = "Show Component Path";
|
||||
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
|
||||
private final static String DESCRIPTION =
|
||||
"Show the category for the selected component's data type";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
private static String[] MENU_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @param group
|
||||
* @param owner
|
||||
* @param popupPath
|
||||
* @param menuPath
|
||||
* @param icon
|
||||
* @param useToolbar
|
||||
* @param checkBox
|
||||
*/
|
||||
public ShowComponentPathAction(CompositeEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, menuPath, null);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, MENU_PATH, null);
|
||||
setDescription(DESCRIPTION);
|
||||
adjustEnablement();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
||||
*/
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
String message = " ";
|
||||
|
@ -66,9 +53,6 @@ public class ShowComponentPathAction extends CompositeEditorTableAction {
|
|||
requestTableFocus();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.compositeeditor.CompositeEditorAction#adjustEnablement()
|
||||
*/
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
setEnabled(model.isSingleComponentRowSelection());
|
||||
|
|
|
@ -35,19 +35,17 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class UnpackageAction extends CompositeEditorTableAction {
|
||||
|
||||
private final static ImageIcon unpackageIcon =
|
||||
ResourceManager.loadImage("images/Unpackage.gif");
|
||||
private final static String ACTION_NAME = "Unpackage Component";
|
||||
private final static ImageIcon ICON = ResourceManager.loadImage("images/Unpackage.gif");
|
||||
public final static String ACTION_NAME = "Unpackage Component";
|
||||
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
|
||||
private final static String DESCRIPTION = "Replace the selected composite with its components";
|
||||
private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0);
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0);
|
||||
private static String[] POPUP_PATH = new String[] { ACTION_NAME };
|
||||
|
||||
public UnpackageAction(StructureEditorProvider provider) {
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null,
|
||||
unpackageIcon);
|
||||
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
|
||||
setDescription(DESCRIPTION);
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
setKeyBindingData(new KeyBindingData(KEY_STROKE));
|
||||
adjustEnablement();
|
||||
}
|
||||
|
||||
|
|
|
@ -167,7 +167,6 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
|||
tool.setMenuGroup(new String[] { DisassociateAction.MENU_NAME }, "SYNC");
|
||||
tool.setMenuGroup(new String[] { RECENTLY_OPENED_MENU }, "Recent");
|
||||
tool.setMenuGroup(new String[] { STANDARD_ARCHIVE_MENU }, "Recent");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,8 @@ import javax.swing.ComboBoxModel;
|
|||
import javax.swing.JPanel;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.actions.SharedDockingActionPlaceholder;
|
||||
import docking.actions.ToolActions;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GLabel;
|
||||
|
@ -88,9 +90,8 @@ public class DataTypeEditorManager
|
|||
* @param dt data type to be edited
|
||||
* @return true if this service can invoke an editor for changing the data type.
|
||||
*/
|
||||
public boolean isEditable(DataType dataType) {
|
||||
if ((dataType instanceof Enum) || (dataType instanceof Union) ||
|
||||
(dataType instanceof Structure)) {
|
||||
public boolean isEditable(DataType dt) {
|
||||
if ((dt instanceof Enum) || (dt instanceof Union) || (dt instanceof Structure)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -138,6 +139,34 @@ public class DataTypeEditorManager
|
|||
editorList.add(editor);
|
||||
}
|
||||
|
||||
private void installEditorActions() {
|
||||
|
||||
registerAction(ApplyAction.ACTION_NAME);
|
||||
registerAction(InsertUndefinedAction.ACTION_NAME);
|
||||
registerAction(MoveUpAction.ACTION_NAME);
|
||||
registerAction(MoveDownAction.ACTION_NAME);
|
||||
registerAction(ClearAction.ACTION_NAME);
|
||||
registerAction(DuplicateAction.ACTION_NAME);
|
||||
registerAction(DuplicateMultipleAction.ACTION_NAME);
|
||||
registerAction(DeleteAction.ACTION_NAME);
|
||||
registerAction(PointerAction.ACTION_NAME);
|
||||
registerAction(ArrayAction.ACTION_NAME);
|
||||
registerAction(FindReferencesToField.ACTION_NAME);
|
||||
registerAction(UnpackageAction.ACTION_NAME);
|
||||
registerAction(EditComponentAction.ACTION_NAME);
|
||||
registerAction(EditFieldAction.ACTION_NAME);
|
||||
registerAction(HexNumbersAction.ACTION_NAME);
|
||||
registerAction(CreateInternalStructureAction.ACTION_NAME);
|
||||
registerAction(ShowComponentPathAction.ACTION_NAME);
|
||||
registerAction(AddBitFieldAction.ACTION_NAME);
|
||||
registerAction(EditBitFieldAction.ACTION_NAME);
|
||||
}
|
||||
|
||||
private void registerAction(String name) {
|
||||
ToolActions toolActions = plugin.getTool().getToolActions();
|
||||
toolActions.registerSharedActionPlaceholder(new DtSharedActionPlaceholder(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for editor changes that have not been saved to the data type and prompts the user to save
|
||||
* them if necessary. It then closes the editor.
|
||||
|
@ -155,7 +184,8 @@ public class DataTypeEditorManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Get a list of data type path names for data types that are currently being edited.
|
||||
* Get a list of data type path names for data types that are currently being edited
|
||||
* @return a list of data type path names for data types that are currently being edited.
|
||||
*/
|
||||
public List<DataTypePath> getEditsInProgress() {
|
||||
List<DataTypePath> paths = new ArrayList<>();
|
||||
|
@ -168,7 +198,7 @@ public class DataTypeEditorManager
|
|||
/**
|
||||
* Get the category for the data type being edited; the data type
|
||||
* may be new and not yet added to the category
|
||||
* @param dataTypePathname the full path name of the data type that is being
|
||||
* @param dataTypePath the full path name of the data type that is being
|
||||
* edited if it were written to the category for this editor.
|
||||
* @return category associated with the data type or null.
|
||||
*/
|
||||
|
@ -325,9 +355,6 @@ public class DataTypeEditorManager
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all editors that a domain object restore has occurred.
|
||||
*/
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
// Create a copy of the list since restore may remove an editor from the original list.
|
||||
ArrayList<EditorProvider> list = new ArrayList<>(editorList);
|
||||
|
@ -352,7 +379,6 @@ public class DataTypeEditorManager
|
|||
/**
|
||||
* If the specified data type is being edited for the indicated category, this gets that editor.
|
||||
* @param dataType the data type
|
||||
* @param category the category where the edited data type is to be written (saved).
|
||||
* @return the editor or null.
|
||||
*/
|
||||
public EditorProvider getEditor(DataType dataType) {
|
||||
|
@ -373,6 +399,8 @@ public class DataTypeEditorManager
|
|||
private void initialize() {
|
||||
editorList = new ArrayList<>();
|
||||
editorOptionMgr = new EditorOptionManager(plugin);
|
||||
|
||||
installEditorActions();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -568,7 +596,7 @@ public class DataTypeEditorManager
|
|||
|
||||
@Override
|
||||
protected void installCallingConventionWidget(JPanel parentPanel) {
|
||||
callingConventionComboBox = new GhidraComboBox();
|
||||
callingConventionComboBox = new GhidraComboBox<>();
|
||||
GenericCallingConvention[] values = GenericCallingConvention.values();
|
||||
String[] choices = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
|
@ -662,4 +690,24 @@ public class DataTypeEditorManager
|
|||
}
|
||||
}
|
||||
|
||||
// small class to register actions by name before the various editors have been shown
|
||||
private class DtSharedActionPlaceholder implements SharedDockingActionPlaceholder {
|
||||
|
||||
private String name;
|
||||
|
||||
DtSharedActionPlaceholder(String name) {
|
||||
this.name = CompositeEditorTableAction.EDIT_ACTION_PREFIX + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwner() {
|
||||
// all of our shared actions belong to the plugin
|
||||
return plugin.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ public class LocationReferencesPlugin extends Plugin
|
|||
// providers are created, as they would only appear in the options at
|
||||
// that point.
|
||||
//
|
||||
DeleteTableRowAction.registerDummy(tool);
|
||||
DeleteTableRowAction.registerDummy(tool, getName());
|
||||
}
|
||||
|
||||
void displayProvider(ListingActionContext context) {
|
||||
|
|
|
@ -163,6 +163,6 @@ public class ScalarSearchPlugin extends ProgramPlugin implements DomainObjectLis
|
|||
// providers are created, as they would only appear in the options at
|
||||
// that point.
|
||||
//
|
||||
DeleteTableRowAction.registerDummy(tool);
|
||||
DeleteTableRowAction.registerDummy(tool, getName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,10 +89,19 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
|
|||
|
||||
@Override
|
||||
protected CompositeEditorTableAction[] createActions() {
|
||||
return new CompositeEditorTableAction[] { new ApplyAction(this), new ClearAction(this),
|
||||
new DeleteAction(this), new PointerAction(this), new ArrayAction(this),
|
||||
new ShowComponentPathAction(this), new EditComponentAction(this),
|
||||
new EditFieldAction(this), new HexNumbersAction(this) };
|
||||
//@formatter:off
|
||||
return new CompositeEditorTableAction[] {
|
||||
new ApplyAction(this),
|
||||
new ClearAction(this),
|
||||
new DeleteAction(this),
|
||||
new PointerAction(this),
|
||||
new ArrayAction(this),
|
||||
new ShowComponentPathAction(this),
|
||||
new EditComponentAction(this),
|
||||
new EditFieldAction(this),
|
||||
new HexNumbersAction(this)
|
||||
};
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -76,7 +76,7 @@ public class TableServicePlugin extends ProgramPlugin
|
|||
// providers are created, as they would only appear in the options at
|
||||
// that point.
|
||||
//
|
||||
DeleteTableRowAction.registerDummy(tool);
|
||||
DeleteTableRowAction.registerDummy(tool, getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,7 +25,7 @@ import javax.swing.table.TableModel;
|
|||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.actions.SharedDockingActionPlaceholder;
|
||||
import docking.widgets.table.GTable;
|
||||
import docking.widgets.table.RowObjectTableModel;
|
||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
|
@ -46,7 +46,7 @@ import resources.ResourceManager;
|
|||
* not altering the database.
|
||||
* <p>
|
||||
* Tip: if you are a plugin that uses transient providers, then use
|
||||
* {@link #registerDummy(PluginTool)} at creation time to install a dummy representative of
|
||||
* {@link #registerDummy(PluginTool, String)} at creation time to install a dummy representative of
|
||||
* this action in the Tool's options so that user's can update keybindings, regardless of whether
|
||||
* they have ever shown one of your transient providers.
|
||||
*/
|
||||
|
@ -65,9 +65,10 @@ public class DeleteTableRowAction extends DockingAction {
|
|||
* at the time the plugin is loaded.
|
||||
*
|
||||
* @param tool the tool whose options will updated with a dummy keybinding
|
||||
* @param owner the owner of the action that may be installed
|
||||
*/
|
||||
public static void registerDummy(PluginTool tool) {
|
||||
new DummyDeleteAction(tool);
|
||||
public static void registerDummy(PluginTool tool, String owner) {
|
||||
tool.getToolActions().registerSharedActionPlaceholder(new DeleteActionPlaceholder(owner));
|
||||
}
|
||||
|
||||
public DeleteTableRowAction(GTable table, String owner) {
|
||||
|
@ -192,26 +193,27 @@ public class DeleteTableRowAction extends DockingAction {
|
|||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private static class DummyDeleteAction extends DeleteTableRowAction {
|
||||
private static class DeleteActionPlaceholder implements SharedDockingActionPlaceholder {
|
||||
|
||||
public DummyDeleteAction(PluginTool tool) {
|
||||
super(NAME, ToolConstants.TOOL_OWNER, DEFAULT_KEYSTROKE);
|
||||
private String owner;
|
||||
|
||||
// prevent this action from appearing in the toolbar, menus, etc
|
||||
setToolBarData(null);
|
||||
setPopupMenuData(null);
|
||||
|
||||
tool.addAction(this);
|
||||
public DeleteActionPlaceholder(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
// stub
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return false; // stub
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyStroke getKeyBinding() {
|
||||
return DEFAULT_KEYSTROKE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,11 +28,7 @@ import docking.*;
|
|||
import docking.actions.KeyEntryDialog;
|
||||
import docking.actions.ToolActions;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.data.DataPlugin;
|
||||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||
import ghidra.app.plugin.core.memory.MemoryMapPlugin;
|
||||
import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin;
|
||||
import ghidra.app.plugin.core.navigation.NavigationHistoryPlugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
|
@ -47,21 +43,13 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
env = new TestEnv();
|
||||
tool = env.getTool();
|
||||
tool.addPlugin(NavigationHistoryPlugin.class.getName());
|
||||
tool.addPlugin(CodeBrowserPlugin.class.getName());
|
||||
tool.addPlugin(MemoryMapPlugin.class.getName());
|
||||
tool.addPlugin(GoToAddressLabelPlugin.class.getName());
|
||||
tool.addPlugin(DataPlugin.class.getName());
|
||||
tool.addPlugin(FunctionPlugin.class.getName());
|
||||
|
||||
env.showTool();
|
||||
tool = env.launchDefaultTool();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
close(keyEntryDialog);
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
|
@ -163,6 +151,26 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(acceleratorKey.getKeyCode(), KeyEvent.VK_G);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceholderActionsAppearInDialog() throws Exception {
|
||||
|
||||
DockingAction unboundAction = getUnboundAction();
|
||||
showDialog(unboundAction);
|
||||
|
||||
int modifiers = 0;
|
||||
int keyCode = KeyEvent.VK_DELETE;
|
||||
triggerActionKey(keyEntryField, modifiers, keyCode);
|
||||
|
||||
String placeholderText = "Remove Items";
|
||||
assertTrue("Placeholder action is not registered with the KeyEntryDialog",
|
||||
collisionPane.getText().contains(placeholderText));
|
||||
|
||||
// this can be any of the plugins that register this action placeholder
|
||||
placeholderText = "TableServicePlugin";
|
||||
assertTrue("Placeholder action is not registered with the KeyEntryDialog",
|
||||
collisionPane.getText().contains(placeholderText));
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private methods
|
||||
//==================================================================================================
|
||||
|
@ -192,6 +200,7 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
ToolActions toolActions = tool.getToolActions();
|
||||
KeyBindingsManager kbm =
|
||||
(KeyBindingsManager) getInstanceField("keyBindingsManager", toolActions);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<KeyStroke, DockingKeyBindingAction> dockingKeyMap =
|
||||
(Map<KeyStroke, DockingKeyBindingAction>) getInstanceField("dockingKeyMap", kbm);
|
||||
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0);
|
||||
|
|
|
@ -39,9 +39,13 @@ import docking.DockingUtils;
|
|||
import docking.action.DockingActionIf;
|
||||
import docking.action.ToggleDockingActionIf;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.plugin.core.compositeeditor.ApplyAction;
|
||||
import ghidra.app.plugin.core.compositeeditor.CompositeEditorTableAction;
|
||||
import ghidra.app.plugin.core.datamgr.actions.CreateTypeDefDialog;
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
|
@ -50,6 +54,7 @@ import ghidra.app.plugin.core.function.EditFunctionSignatureDialog;
|
|||
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
|
@ -57,7 +62,6 @@ import ghidra.program.database.ProgramDB;
|
|||
import ghidra.program.database.data.ProgramDataTypeManager;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.test.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassFilter;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -749,9 +753,27 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
assertEquals("Tom", fun.getArguments()[2].getName());
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private methods
|
||||
//==================================================================================================
|
||||
@Test
|
||||
public void testEditorActionsGetRegisteredWithoutEditing() {
|
||||
|
||||
// the owner for the action is the tool, since the registered item is just a placeholder
|
||||
// because the editor actions are shared actions
|
||||
String owner = " (" + ToolConstants.SHARED_OWNER + ')';
|
||||
String actionName = CompositeEditorTableAction.EDIT_ACTION_PREFIX + ApplyAction.ACTION_NAME;
|
||||
String optionName = actionName + owner;
|
||||
ToolOptions options = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
||||
String message = "Editor action was not registered before editor was shown";
|
||||
assertTrue(message, options.isRegistered(optionName));
|
||||
|
||||
DockingActionIf action = getAction(tool, ToolConstants.SHARED_OWNER, actionName);
|
||||
assertNotNull(message, action);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private methods
|
||||
//==================================================================================================
|
||||
|
||||
private void editSignature(String name, String newSignature) {
|
||||
expandNode(programNode);
|
||||
GTreeNode child = programNode.getChild(name);
|
||||
|
@ -986,7 +1008,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
|
||||
/**
|
||||
* This directory is bin in eclipse; it will be a resources directory in the classpath when run
|
||||
* in batch mode.
|
||||
* in batch mode. The directory is one specifically created by and for this test.
|
||||
* @return class output directory
|
||||
* @throws FileNotFoundException Could not find class output directory
|
||||
*/
|
||||
|
@ -1008,12 +1030,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
try {
|
||||
File binDir = getClassesDirectory();
|
||||
if (binDir.isDirectory()) {
|
||||
Msg.debug(this, "\tdeleting the bin dir...");
|
||||
boolean success = FileUtilities.deleteDir(binDir);
|
||||
Msg.debug(this, "\tsuccess?: " + success);
|
||||
}
|
||||
else {
|
||||
Msg.debug(this, "NOT a directory - not deleting!");
|
||||
FileUtilities.deleteDir(binDir);
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
|
|
|
@ -34,6 +34,7 @@ import docking.action.DockingActionIf;
|
|||
import docking.actions.KeyBindingUtils;
|
||||
import docking.options.editor.OptionsDialog;
|
||||
import docking.options.editor.OptionsPanel;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.tree.GTree;
|
||||
|
@ -322,6 +323,44 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
closeAllWindows();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedKeyBindingGetsRestoredWhenToolIsRestarted() throws Exception {
|
||||
|
||||
setKeyBindingsUpDialog();
|
||||
|
||||
// this action is known to be a 'Shared' action
|
||||
// Remove Items (Shared)
|
||||
String actionName = "Remove Items";
|
||||
DockingActionIf action = getAction(tool, ToolConstants.SHARED_OWNER, actionName);
|
||||
assertNotNull(action);
|
||||
KeyStroke defaultBinding = action.getKeyBinding();
|
||||
KeyStroke newBinding = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0);
|
||||
assertNotEquals(defaultBinding, newBinding);
|
||||
setKeyBinding(action, "x", newBinding.getKeyCode());
|
||||
|
||||
KeyStroke appliedBinding = action.getKeyBinding();
|
||||
assertEquals(newBinding, appliedBinding);
|
||||
|
||||
// reload the tool and make sure the values are those of the changes get restored
|
||||
saveAndCloseTool();
|
||||
|
||||
reopenTool(tool);
|
||||
|
||||
KeyStroke restoredBinding = action.getKeyBinding();
|
||||
assertEquals(newBinding, restoredBinding);
|
||||
|
||||
setKeyBindingsUpDialog(tool);
|
||||
ToolOptions options = (ToolOptions) getInstanceField("options", panel);
|
||||
KeyStroke optionBinding = options.getKeyStroke(action.getFullName(), null);
|
||||
assertEquals(appliedBinding, optionBinding);
|
||||
|
||||
closeAllWindows();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void reopenTool(PluginTool tool2) {
|
||||
runSwing(() -> {
|
||||
ToolServices services = tool.getProject().getToolServices();
|
||||
|
@ -438,14 +477,19 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
assertNotNull("Unable to find an action for which to set a key binding", arbitraryAction);
|
||||
setKeyBinding(arbitraryAction, keyText, keyCode);
|
||||
}
|
||||
|
||||
selectRowForAction(arbitraryAction);
|
||||
private void setKeyBinding(DockingActionIf action, String keyText, int keyCode)
|
||||
throws Exception {
|
||||
|
||||
selectRowForAction(action);
|
||||
triggerText(keyField, keyText);
|
||||
|
||||
assertEquals(keyText.toUpperCase(), keyField.getText());
|
||||
|
||||
runSwing(() -> panel.apply());
|
||||
assertEquals(KeyStroke.getKeyStroke(keyCode, 0), arbitraryAction.getKeyBinding());
|
||||
assertEquals(KeyStroke.getKeyStroke(keyCode, 0), action.getKeyBinding());
|
||||
}
|
||||
|
||||
private void selectRowForAction(DockingActionIf action) throws Exception {
|
||||
|
@ -557,11 +601,11 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private boolean compareOptionsWithKeyStrokeMap(Options oldOptions,
|
||||
Map<String, KeyStroke> panelKeyStrokeMap) {
|
||||
List<String> propertyNames = oldOptions.getOptionNames();
|
||||
for (String element : propertyNames) {
|
||||
for (String name : propertyNames) {
|
||||
|
||||
boolean match = panelKeyStrokeMap.containsKey(element);
|
||||
KeyStroke optionsKs = oldOptions.getKeyStroke(element, null);
|
||||
KeyStroke panelKs = panelKeyStrokeMap.get(element);
|
||||
boolean match = panelKeyStrokeMap.containsKey(name);
|
||||
KeyStroke optionsKs = oldOptions.getKeyStroke(name, null);
|
||||
KeyStroke panelKs = panelKeyStrokeMap.get(name);
|
||||
|
||||
// if the value is null, then it would not have been placed into the options map
|
||||
// in the key bindings panel, so we only care about non-null values
|
||||
|
@ -583,8 +627,6 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
private void assertOptionsMatch(String message, ToolOptions options1, ToolOptions options2) {
|
||||
|
||||
// System.out.println("assertOptionsMatch()");
|
||||
|
||||
List<String> propertyNames = getOptionsNamesWithValues(options1);
|
||||
List<String> otherPropertyNames = getOptionsNamesWithValues(options2);
|
||||
|
||||
|
@ -610,8 +652,6 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private void assertOptionsDontMatch(String message, ToolOptions options1,
|
||||
ToolOptions options2) {
|
||||
|
||||
// System.out.println("assertOptionsDontMatch()");
|
||||
|
||||
List<String> propertyNames = getOptionsNamesWithValues(options1);
|
||||
List<String> otherPropertyNames = getOptionsNamesWithValues(options2);
|
||||
if (propertyNames.size() != otherPropertyNames.size()) {
|
||||
|
|
|
@ -119,16 +119,19 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private JPanel createCollisionPanel() {
|
||||
JPanel p = new JPanel(new BorderLayout());
|
||||
JPanel parent = new JPanel(new BorderLayout());
|
||||
|
||||
JPanel noWrapPanel = new JPanel(new BorderLayout());
|
||||
collisionPane = new JTextPane();
|
||||
collisionPane.setEditable(false);
|
||||
collisionPane.setBackground(bgColor);
|
||||
doc = collisionPane.getStyledDocument();
|
||||
JScrollPane sp = new JScrollPane(collisionPane);
|
||||
noWrapPanel.add(collisionPane, BorderLayout.CENTER);
|
||||
JScrollPane sp = new JScrollPane(noWrapPanel);
|
||||
Dimension d = defaultPanel.getPreferredSize();
|
||||
sp.setPreferredSize(new Dimension(sp.getPreferredSize().width, d.height));
|
||||
p.add(sp, BorderLayout.CENTER);
|
||||
return p;
|
||||
parent.add(sp, BorderLayout.CENTER);
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,13 +197,19 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
list.sort((a1, a2) -> {
|
||||
String s1 = a1.getName() + a1.getOwnerDescription();
|
||||
String s2 = a2.getName() + a2.getOwnerDescription();
|
||||
return s1.compareToIgnoreCase(s2);
|
||||
});
|
||||
|
||||
String ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||
try {
|
||||
doc.insertString(0, "Actions mapped to " + ksName + "\n\n", textAttrSet);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DockingActionIf a = list.get(i);
|
||||
|
||||
String collisionStr = "\t" + a.getName() + " (" + a.getOwnerDescription() + ")\n";
|
||||
String collisionStr = "\t" + a.getName() + " (" + a.getOwnerDescription() + ")\n";
|
||||
int offset = doc.getLength();
|
||||
doc.insertString(offset, collisionStr, textAttrSet);
|
||||
doc.setParagraphAttributes(offset, 1, tabAttrSet, false);
|
||||
|
|
|
@ -34,6 +34,6 @@ public class SharedActionRegistry {
|
|||
* @param toolActions the tool action manager
|
||||
*/
|
||||
public static void installSharedActions(DockingTool tool, ToolActions toolActions) {
|
||||
GTable.createSharedActions(tool, toolActions, ToolConstants.TOOL_OWNER);
|
||||
GTable.createSharedActions(tool, toolActions, ToolConstants.SHARED_OWNER);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/* ###
|
||||
* 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 docking.actions;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.tool.ToolConstants;
|
||||
|
||||
/**
|
||||
* A marker interface to signal that the implementing action serves as an action that should
|
||||
* not be itself used in the tool, but should only be used to register and manager keybindings.
|
||||
*
|
||||
*
|
||||
* <p>This action is merely a tool by which transient components can ensure that their actions
|
||||
* are correctly managed when the component is created. Normal actions will get registered when
|
||||
* the tool first starts-up. Alternatively, transient components only appear when called upon
|
||||
* by some event, such as a user request. The issue heretofore was that the tool will remove
|
||||
* any options that are not longer used. Thus, if an action belonging to a transient component
|
||||
* does not get registered every time the tool is used, then the options (and key bindings) for
|
||||
* that action are removed from the too. This interface allows a second-party to register
|
||||
* an action on behalf of a transient provider, thus preventing the tool from removing any
|
||||
* previously applied options.
|
||||
*/
|
||||
public interface SharedDockingActionPlaceholder {
|
||||
|
||||
/**
|
||||
* The action name. This name must exactly match the name of the action represented by
|
||||
* this placeholder.
|
||||
* @return the name
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Returns an owner name to use in place of {@value ToolConstants#SHARED_OWNER}.
|
||||
* This should only be used when the client knows for certain that all shared actions are
|
||||
* shared by a single owner. This is not typical for shared actions. This can happen when one
|
||||
* owner (such as a plugin) has multiple component providers that share action key bindings.
|
||||
* @return the owner
|
||||
*/
|
||||
public default String getOwner() {
|
||||
return ToolConstants.SHARED_OWNER;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default key binding for the action represented by this placeholder
|
||||
* @return the key binding; may be null
|
||||
*/
|
||||
public default KeyStroke getKeyBinding() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ import java.util.Map.Entry;
|
|||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.apache.commons.collections4.Bag;
|
||||
import org.apache.commons.collections4.bag.HashBag;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
|
@ -34,11 +36,25 @@ import ghidra.framework.options.ToolOptions;
|
|||
* allows plugins to create actions that share keybindings without having to manage those
|
||||
* keybindings themselves.
|
||||
*
|
||||
* <p>Some ways this class is used:
|
||||
* <ol>
|
||||
* <li>As a central action to manage key bindings for multiple actions from different clients
|
||||
* (plugins) that are conceptually the same. When the plugins are loaded
|
||||
* these actions get registered and are wired to listen to key binding changes to this stub.
|
||||
* </li>
|
||||
* <li>As a placeholder action to manage key bindings for actions that have not yet been
|
||||
* registered and may not get registered during the lifetime of a single tool session.
|
||||
* This can happen when a plugin has transient component providers that only get shown
|
||||
* upon a user request. This stub allows the key binding for those actions to be managed,
|
||||
* even if they do not get registered when the tool is shown.
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>Clients should not be using this class directly.
|
||||
*/
|
||||
public class SharedStubKeyBindingAction extends DockingAction implements OptionsChangeListener {
|
||||
|
||||
static final String SHARED_OWNER = ToolConstants.TOOL_OWNER;
|
||||
static final String SHARED_OWNER = ToolConstants.SHARED_OWNER;
|
||||
|
||||
/**
|
||||
* We save the client actions for later validate and options updating. We also need the
|
||||
|
@ -50,27 +66,54 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
|||
private WeakHashMap<DockingActionIf, KeyStroke> clientActions = new WeakHashMap<>();
|
||||
|
||||
private ToolOptions keyBindingOptions;
|
||||
private Bag<String> actionOwners = new HashBag<String>();
|
||||
|
||||
/**
|
||||
* Creates a new dummy action by the given name and default keystroke value
|
||||
*
|
||||
* @param name The name of the action--this will be displayed in the options as the name of
|
||||
* key binding's action
|
||||
* @param defaultKs the default key stroke for this stub. The key stroke will be validated
|
||||
* each time an action is added to this stub to ensure that the defaults are in sync.
|
||||
* @param options the tool's key binding options
|
||||
*/
|
||||
SharedStubKeyBindingAction(String name, ToolOptions options) {
|
||||
super(name, SHARED_OWNER);
|
||||
SharedStubKeyBindingAction(String name, KeyStroke defaultKs, ToolOptions options) {
|
||||
// Note: we need to have this stub registered to use key bindings so that the options will
|
||||
// restore the saved key binding to this class, which will then notify any of the
|
||||
// shared actions using this stub.
|
||||
super(name, SHARED_OWNER, KeyBindingType.INDIVIDUAL);
|
||||
this.keyBindingOptions = options;
|
||||
|
||||
// Dummy keybinding actions don't have help--the real action does
|
||||
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
||||
|
||||
setUnvalidatedKeyBindingData(new KeyBindingData(defaultKs));
|
||||
|
||||
// A listener to keep the shared, stub keybindings in sync with their clients
|
||||
options.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given owner name to this stub. This is used to display all known clients of
|
||||
* the action represented by this stub. Normally, when this class has actions, the names
|
||||
* of each action's owner would be used directly. However, this class can also be used as
|
||||
* a placeholder, when no actions have yet been registered. In that case, the owner has
|
||||
* to be set directly on this stub.
|
||||
*
|
||||
* @param owner the name of the client that owns the actions that may get registered with
|
||||
* this stub
|
||||
*/
|
||||
void addActionOwner(String owner) {
|
||||
if (DockingWindowManager.DOCKING_WINDOWS_OWNER.equals(owner)) {
|
||||
// Special case: special system-level action owner; the user does not need to see
|
||||
return;
|
||||
}
|
||||
actionOwners.add(owner);
|
||||
}
|
||||
|
||||
void removeClientAction(DockingActionIf action) {
|
||||
clientActions.remove(action);
|
||||
actionOwners.remove(action.getOwner());
|
||||
}
|
||||
|
||||
void addClientAction(DockingActionIf action) {
|
||||
|
@ -88,44 +131,20 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
|||
|
||||
@Override
|
||||
public String getOwnerDescription() {
|
||||
List<String> owners = getDistinctOwners();
|
||||
List<String> owners = new LinkedList<>(actionOwners.uniqueSet());
|
||||
if (owners.size() == 1) {
|
||||
return owners.get(0);
|
||||
}
|
||||
|
||||
boolean hasTool = owners.remove(ToolConstants.TOOL_OWNER);
|
||||
boolean hasTool = owners.remove(SHARED_OWNER);
|
||||
Collections.sort(owners);
|
||||
if (hasTool) {
|
||||
owners.add(0, ToolConstants.TOOL_OWNER);
|
||||
owners.add(0, SHARED_OWNER);
|
||||
}
|
||||
|
||||
return StringUtils.join(owners, ", ");
|
||||
}
|
||||
|
||||
private List<String> getDistinctOwners() {
|
||||
List<String> results = new ArrayList<>();
|
||||
Set<DockingActionIf> actions = clientActions.keySet();
|
||||
for (DockingActionIf action : actions) {
|
||||
String owner = action.getOwner();
|
||||
if (DockingWindowManager.DOCKING_WINDOWS_OWNER.equals(owner)) {
|
||||
// special case: this is the owner for special system-level actions
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!results.contains(owner)) {
|
||||
results.add(owner);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
// This implies we have an action owned by the DockingWindowManager
|
||||
// (the DOCKING_WINDOWS_OWNER). In this case, use the Tool as the owner.
|
||||
results.add(SHARED_OWNER);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private KeyStroke validateActionsHaveTheSameDefaultKeyStroke(DockingActionIf newAction) {
|
||||
|
||||
// this value may be null
|
||||
|
|
|
@ -18,6 +18,7 @@ package docking.actions;
|
|||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.KeyStroke;
|
||||
|
@ -142,6 +143,13 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
}
|
||||
|
||||
KeyStroke ks = action.getKeyBinding();
|
||||
loadKeyBindingFromOptions(action, ks);
|
||||
|
||||
keyBindingsManager.addAction(provider, action);
|
||||
}
|
||||
|
||||
private void loadKeyBindingFromOptions(DockingActionIf action, KeyStroke ks) {
|
||||
|
||||
String description = "Keybinding for " + action.getFullName();
|
||||
keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, null,
|
||||
description);
|
||||
|
@ -149,8 +157,6 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
if (!Objects.equals(ks, newKs)) {
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
|
||||
}
|
||||
|
||||
keyBindingsManager.addAction(provider, action);
|
||||
}
|
||||
|
||||
private void installSharedKeyBinding(ComponentProvider provider, DockingActionIf action) {
|
||||
|
@ -161,11 +167,13 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
SharedStubKeyBindingAction stub = sharedActionMap.computeIfAbsent(name, key -> {
|
||||
|
||||
SharedStubKeyBindingAction newStub =
|
||||
new SharedStubKeyBindingAction(name, keyBindingOptions);
|
||||
new SharedStubKeyBindingAction(name, defaultKeyStroke, keyBindingOptions);
|
||||
registerStub(newStub, defaultKeyStroke);
|
||||
return newStub;
|
||||
});
|
||||
|
||||
String owner = action.getOwner();
|
||||
stub.addActionOwner(owner);
|
||||
stub.addClientAction(action);
|
||||
|
||||
if (!(action instanceof AutoGeneratedDockingAction)) {
|
||||
|
@ -176,16 +184,12 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
|
||||
private void registerStub(SharedStubKeyBindingAction stub, KeyStroke defaultKeyStroke) {
|
||||
stub.addPropertyChangeListener(this);
|
||||
String description = "Keybinding for Stub action: " + stub.getFullName();
|
||||
keyBindingOptions.registerOption(stub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
||||
defaultKeyStroke, null, description);
|
||||
|
||||
loadKeyBindingFromOptions(stub, defaultKeyStroke);
|
||||
|
||||
keyBindingsManager.addAction(null, stub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given action from the tool
|
||||
* @param action the action to be removed.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void removeGlobalAction(DockingActionIf action) {
|
||||
action.removePropertyChangeListener(this);
|
||||
|
@ -230,12 +234,6 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all actions for the given owner
|
||||
* @param owner owner of the actions
|
||||
* @return array of actions; zero length array is returned if no
|
||||
* action exists with the given name
|
||||
*/
|
||||
@Override
|
||||
public synchronized Set<DockingActionIf> getActions(String owner) {
|
||||
|
||||
|
@ -245,18 +243,18 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
result.addAll(actions);
|
||||
}
|
||||
|
||||
if (SharedStubKeyBindingAction.SHARED_OWNER.equals(owner)) {
|
||||
result.addAll(sharedActionMap.values());
|
||||
Set<Entry<String, SharedStubKeyBindingAction>> entries = sharedActionMap.entrySet();
|
||||
for (Entry<String, SharedStubKeyBindingAction> entry : entries) {
|
||||
SharedStubKeyBindingAction stub = entry.getValue();
|
||||
String stubOwner = stub.getOwner();
|
||||
if (stubOwner.equals(owner)) {
|
||||
result.add(stub);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of all actions in the tool
|
||||
*
|
||||
* @return a new set of the existing actions
|
||||
*/
|
||||
@Override
|
||||
public synchronized Set<DockingActionIf> getAllActions() {
|
||||
|
||||
|
@ -418,4 +416,31 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
return sharedActionMap.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows clients to register an action by using a placeholder. This is useful when
|
||||
* an API wishes to have a central object (like a plugin) register actions for transient
|
||||
* providers, that may not be loaded until needed.
|
||||
*
|
||||
* <p>This method may be called multiple times with the same conceptual placeholder--the
|
||||
* placeholder will only be added once.
|
||||
*
|
||||
* @param placeholder the placeholder containing information related to the action it represents
|
||||
*/
|
||||
public void registerSharedActionPlaceholder(SharedDockingActionPlaceholder placeholder) {
|
||||
|
||||
String name = placeholder.getName();
|
||||
KeyStroke defaultKeyStroke = placeholder.getKeyBinding();
|
||||
|
||||
SharedStubKeyBindingAction stub = sharedActionMap.computeIfAbsent(name, key -> {
|
||||
|
||||
SharedStubKeyBindingAction newStub =
|
||||
new SharedStubKeyBindingAction(name, defaultKeyStroke, keyBindingOptions);
|
||||
registerStub(newStub, defaultKeyStroke);
|
||||
return newStub;
|
||||
});
|
||||
|
||||
String owner = placeholder.getOwner();
|
||||
stub.addActionOwner(owner);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package docking.tool;
|
||||
|
||||
import docking.action.KeyBindingType;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
|
||||
/**
|
||||
|
@ -93,6 +94,13 @@ public interface ToolConstants extends DockingToolConstants {
|
|||
*/
|
||||
public static final String TOOL_OWNER = "Tool";
|
||||
|
||||
/**
|
||||
* This is used when many actions wish to share a key binding.
|
||||
*
|
||||
* @see KeyBindingType#SHARED
|
||||
*/
|
||||
public static final String SHARED_OWNER = "Shared";
|
||||
|
||||
/**
|
||||
* Name of options for a tool
|
||||
*/
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.Color;
|
|||
import java.awt.Font;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
@ -98,9 +99,9 @@ public class ToolOptions extends AbstractOptions {
|
|||
try {
|
||||
Element elem = (Element) iter.next();
|
||||
String optionName = elem.getAttributeValue("NAME");
|
||||
|
||||
Class<?> c = Class.forName(elem.getAttributeValue("CLASS"));
|
||||
WrappedOption wo = (WrappedOption) c.newInstance();
|
||||
Constructor<?> constructor = c.getDeclaredConstructor();
|
||||
WrappedOption wo = (WrappedOption) constructor.newInstance();
|
||||
wo.readState(new SaveState(elem));
|
||||
Option option = createUnregisteredOption(optionName, wo.getOptionType(), null);
|
||||
option.doSetCurrentValue(wo.getObject());// use doSet versus set so that it is not registered
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue