mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-25 20:26:24 +08:00
Merge remote-tracking branch 'origin/GP-6628-dragonmacher-dialog-keybindings-visibility--SQUASHED'
This commit is contained in:
@@ -979,7 +979,6 @@ src/main/resources/images/hoverOff.gif||GHIDRA||||END|
|
||||
src/main/resources/images/hoverOn.gif||GHIDRA||||END|
|
||||
src/main/resources/images/icon_link.gif||FAMFAMFAM Mini Icons - Public Domain|||famfamfam mini icon set|END|
|
||||
src/main/resources/images/imported_bookmark.gif||GHIDRA||||END|
|
||||
src/main/resources/images/katomic.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
||||
src/main/resources/images/key.png||FAMFAMFAM Icons - CC 2.5|||silk|END|
|
||||
src/main/resources/images/kmessedwords.png||Nuvola Icons - LGPL 2.1||||END|
|
||||
src/main/resources/images/label.png||GHIDRA||||END|
|
||||
|
||||
@@ -126,13 +126,28 @@
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P><IMG alt="" border="0" src="help/shared/note.yellow.png"> When a key is mapped to multiple
|
||||
actions, and more than one of these actions is valid in the current context (i.e., the action
|
||||
is enabled), then a dialog is displayed for you to choose what action you want to
|
||||
perform.</P>
|
||||
|
||||
<P>To avoid the extra step of choosing the action from the dialog, do not map the same key to
|
||||
actions that are applicable in the same context.</P>
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG alt="" border="0" src="help/shared/note.yellow.png"> When a key is mapped to multiple
|
||||
actions, and more than one of these actions is valid in the current context (i.e., the action
|
||||
is enabled), then a dialog is displayed for you to choose what action you want to
|
||||
perform.</P>
|
||||
|
||||
<P>To avoid the extra step of choosing the action from the dialog, do not map the same key to
|
||||
actions that are applicable in the same context.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG alt="" border="0" src="help/shared/note.yellow.png">Unregistered action key
|
||||
bindings will be displayed in a lighter color in the table. These values can still
|
||||
be changed.</P>
|
||||
|
||||
<P>Unregistered actions may appear if plugins have been removed. They may also appear
|
||||
for dialogs or components that have not yet been created in the system. Because
|
||||
some actions are registred on-demand, not all actions in the system will appear in
|
||||
this table. Once an action UI has been shown in a given tool session, then its
|
||||
actions appear in this table for the remainder of the tool session.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Remove a Key Binding</H3>
|
||||
|
||||
|
||||
+11
-10
@@ -37,6 +37,7 @@ import org.junit.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.actions.ActionBindingsDescriptor;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.options.editor.*;
|
||||
import docking.tool.ToolConstants;
|
||||
@@ -887,10 +888,10 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
JTable table = (JTable) getInstanceField("actionTable", panel);
|
||||
@SuppressWarnings("unchecked")
|
||||
RowObjectFilterModel<DockingActionIf> model =
|
||||
(RowObjectFilterModel<DockingActionIf>) table.getModel();
|
||||
RowObjectFilterModel<ActionBindingsDescriptor> model =
|
||||
(RowObjectFilterModel<ActionBindingsDescriptor>) table.getModel();
|
||||
|
||||
DockingActionIf rowValue = model.getModelData().get(row);
|
||||
ActionBindingsDescriptor rowValue = model.getModelData().get(row);
|
||||
|
||||
String keyBindingColumnValue =
|
||||
(String) model.getColumnValueForRow(rowValue, 1 /* key binding column */);
|
||||
@@ -917,10 +918,10 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
JTable table = (JTable) getInstanceField("actionTable", panel);
|
||||
@SuppressWarnings("unchecked")
|
||||
RowObjectFilterModel<DockingActionIf> model =
|
||||
(RowObjectFilterModel<DockingActionIf>) table.getModel();
|
||||
RowObjectFilterModel<ActionBindingsDescriptor> model =
|
||||
(RowObjectFilterModel<ActionBindingsDescriptor>) table.getModel();
|
||||
|
||||
DockingActionIf rowValue = model.getModelData().get(row);
|
||||
ActionBindingsDescriptor rowValue = model.getModelData().get(row);
|
||||
|
||||
String keyBindingColumnValue =
|
||||
(String) model.getColumnValueForRow(rowValue, 1 /* key binding column */);
|
||||
@@ -1036,14 +1037,14 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
private int selectRowForAction(KeyBindingsPanel panel, String actionName, String actionOwner) {
|
||||
final JTable table = (JTable) getInstanceField("actionTable", panel);
|
||||
@SuppressWarnings("unchecked")
|
||||
final RowObjectFilterModel<DockingActionIf> model =
|
||||
(RowObjectFilterModel<DockingActionIf>) table.getModel();
|
||||
final RowObjectFilterModel<ActionBindingsDescriptor> model =
|
||||
(RowObjectFilterModel<ActionBindingsDescriptor>) table.getModel();
|
||||
|
||||
int actionRow = -1;
|
||||
List<DockingActionIf> modelData = model.getModelData();
|
||||
List<ActionBindingsDescriptor> modelData = model.getModelData();
|
||||
int rowCount = modelData.size();
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
DockingActionIf rowData = modelData.get(i);
|
||||
ActionBindingsDescriptor rowData = modelData.get(i);
|
||||
String rowActionName =
|
||||
(String) model.getColumnValueForRow(rowData, 0 /* action name column */);
|
||||
if (rowActionName.equals(actionName)) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/* ###
|
||||
* 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 docking.action.DockingActionIf;
|
||||
|
||||
/**
|
||||
* An interface that allows the {@link KeyBindingsModel} API to provide key and mouse binding information
|
||||
* to clients, without having to have a registered action to provide the information. Action
|
||||
* descriptions will be loaded from the saved tool options. If no plugin has registered an action
|
||||
* for the current tool session, then the an unregistered action descriptor will get created when
|
||||
* editing key and mouse bindings via the options UI.
|
||||
*/
|
||||
public interface ActionBindingsDescriptor {
|
||||
|
||||
/**
|
||||
* {@return the action name without the owner}
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* {@return the full action name in the format: 'Name (Owner)'}
|
||||
*/
|
||||
public String getFullName();
|
||||
|
||||
/**
|
||||
* {@return the owner name(s) of the action}
|
||||
*/
|
||||
public String getOwnerDescription();
|
||||
|
||||
/**
|
||||
* {@return the action description or a blank string}
|
||||
*/
|
||||
public String getDescription();
|
||||
|
||||
/**
|
||||
* {@return a string that shows all key and mouse binding info for the action}
|
||||
*/
|
||||
public String getBindingText();
|
||||
|
||||
/**
|
||||
* The action for the binding. This will be an arbitrary action for shared bindings.
|
||||
* This will be null if this class represents an unregistered action.
|
||||
*
|
||||
* @return an action or null
|
||||
*/
|
||||
public DockingActionIf getRepresentativeAction();
|
||||
|
||||
/**
|
||||
* {@return true if a plugin has registered an action for this descriptor; false is no action
|
||||
* has been registered}
|
||||
*/
|
||||
public boolean isRegistered();
|
||||
}
|
||||
+243
-74
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,7 @@ import resources.Icons;
|
||||
*/
|
||||
public class KeyEntryDialog extends DialogComponentProvider {
|
||||
|
||||
private KeyBindings keyBindings;
|
||||
private KeyBindingsModel keyBindings;
|
||||
private ToolActions toolActions;
|
||||
private DockingActionIf action;
|
||||
|
||||
@@ -54,7 +54,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
||||
this.action = action;
|
||||
this.toolActions = (ToolActions) tool.getToolActions();
|
||||
|
||||
this.keyBindings = new KeyBindings(tool);
|
||||
this.keyBindings = new KeyBindingsModel(tool);
|
||||
|
||||
setUpAttributes();
|
||||
createPanel();
|
||||
|
||||
@@ -8,6 +8,9 @@ color.fg.extensionpanel.path = color.palette.blue
|
||||
color.fg.extensionpanel.details.title = color.palette.maroon
|
||||
color.fg.extensionpanel.details.version = color.palette.blue
|
||||
|
||||
color.fg.options.keybindings.table.unregistered = color.palette.lightgray
|
||||
color.fg.options.keybindings.table.unregistered.selected.unfocused = color.palette.gray
|
||||
|
||||
color.fg.pluginpanel.name = color.fg
|
||||
color.fg.pluginpanel.description = color.palette.gray
|
||||
|
||||
|
||||
+61
-41
@@ -35,6 +35,7 @@ import docking.tool.util.DockingToolConstants;
|
||||
import docking.widgets.*;
|
||||
import docking.widgets.MultiLineLabel.VerticalAlignment;
|
||||
import docking.widgets.table.*;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@@ -66,10 +67,10 @@ public class KeyBindingsPanel extends JPanel {
|
||||
private KeyBindingsTableModel tableModel;
|
||||
private ActionBindingListener actionBindingListener = new ActionBindingListener();
|
||||
private ActionBindingPanel actionBindingPanel;
|
||||
private GTableFilterPanel<DockingActionIf> tableFilterPanel;
|
||||
private GTableFilterPanel<ActionBindingsDescriptor> tableFilterPanel;
|
||||
private EmptyBorderButton helpButton;
|
||||
|
||||
private KeyBindings keyBindings;
|
||||
private KeyBindingsModel keyBindings;
|
||||
private boolean unappliedChanges;
|
||||
|
||||
private PluginTool tool;
|
||||
@@ -82,7 +83,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||
public KeyBindingsPanel(PluginTool tool) {
|
||||
this.tool = tool;
|
||||
|
||||
this.keyBindings = new KeyBindings(tool);
|
||||
this.keyBindings = new KeyBindingsModel(tool);
|
||||
|
||||
createPanelComponents();
|
||||
|
||||
@@ -147,7 +148,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||
gettingStartedPanel = new JPanel();
|
||||
activeActionPanel = createActiveActionPanel();
|
||||
|
||||
tableModel = new KeyBindingsTableModel(new ArrayList<>(keyBindings.getUniqueActions()));
|
||||
tableModel = new KeyBindingsTableModel(new ArrayList<>(keyBindings.getActionBindings()));
|
||||
actionTable = new GTable(tableModel);
|
||||
|
||||
JScrollPane actionsScroller = new JScrollPane(actionTable);
|
||||
@@ -156,6 +157,8 @@ public class KeyBindingsPanel extends JPanel {
|
||||
actionTable.setHTMLRenderingEnabled(true);
|
||||
actionTable.getSelectionModel().addListSelectionListener(new TableSelectionListener());
|
||||
|
||||
actionTable.setDefaultRenderer(String.class, new KeyBindingsRenderer());
|
||||
|
||||
adjustTableColumns();
|
||||
|
||||
// middle panel - filter field and import/export buttons
|
||||
@@ -206,9 +209,9 @@ public class KeyBindingsPanel extends JPanel {
|
||||
helpButton = new EmptyBorderButton(Icons.HELP_ICON);
|
||||
helpButton.setEnabled(false);
|
||||
helpButton.addActionListener(e -> {
|
||||
DockingActionIf action = getSelectedAction();
|
||||
ActionBindingsDescriptor binding = getSelectedBinding();
|
||||
HelpService hs = Help.getHelpService();
|
||||
hs.showHelp(action, false, KeyBindingsPanel.this);
|
||||
hs.showHelp(binding, false, KeyBindingsPanel.this);
|
||||
});
|
||||
|
||||
JPanel statusPanel = new JPanel();
|
||||
@@ -372,7 +375,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||
unappliedChanges = changes;
|
||||
}
|
||||
|
||||
private DockingActionIf getSelectedAction() {
|
||||
private ActionBindingsDescriptor getSelectedBinding() {
|
||||
if (actionTable.getSelectedRowCount() == 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -381,7 +384,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||
}
|
||||
|
||||
private String getSelectedActionName() {
|
||||
DockingActionIf action = getSelectedAction();
|
||||
ActionBindingsDescriptor action = getSelectedBinding();
|
||||
if (action == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -457,21 +460,22 @@ public class KeyBindingsPanel extends JPanel {
|
||||
private void updateKeyStroke(KeyStroke ks) {
|
||||
clearInfoPanel();
|
||||
|
||||
DockingActionIf action = getSelectedAction();
|
||||
if (action == null) {
|
||||
ActionBindingsDescriptor binding = getSelectedBinding();
|
||||
if (binding == null) {
|
||||
statusLabel.setText(GETTING_STARTED_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
DockingActionIf dockingAction = binding.getRepresentativeAction();
|
||||
ToolActions toolActions = (ToolActions) tool.getToolActions();
|
||||
String errorMessage = toolActions.validateActionKeyBinding(action, ks);
|
||||
String errorMessage = toolActions.validateActionKeyBinding(dockingAction, ks);
|
||||
if (errorMessage != null) {
|
||||
actionBindingPanel.clearKeyStroke();
|
||||
statusLabel.setText(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
String selectedActionName = action.getFullName();
|
||||
String selectedActionName = binding.getFullName();
|
||||
if (setActionKeyStroke(selectedActionName, ks)) {
|
||||
showActionsMappedToKeyStroke(ks);
|
||||
fireRowChanged();
|
||||
@@ -483,13 +487,13 @@ public class KeyBindingsPanel extends JPanel {
|
||||
|
||||
clearInfoPanel();
|
||||
|
||||
DockingActionIf action = getSelectedAction();
|
||||
if (action == null) {
|
||||
ActionBindingsDescriptor binding = getSelectedBinding();
|
||||
if (binding == null) {
|
||||
statusLabel.setText(GETTING_STARTED_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
String selectedActionName = action.getFullName();
|
||||
String selectedActionName = binding.getFullName();
|
||||
if (setMouseBinding(selectedActionName, mb)) {
|
||||
fireRowChanged();
|
||||
changesMade(true);
|
||||
@@ -573,8 +577,8 @@ public class KeyBindingsPanel extends JPanel {
|
||||
|
||||
helpButton.setEnabled(false);
|
||||
|
||||
DockingActionIf action = getSelectedAction();
|
||||
if (action == null) {
|
||||
ActionBindingsDescriptor binding = getSelectedBinding();
|
||||
if (binding == null) {
|
||||
swapView(gettingStartedPanel);
|
||||
|
||||
statusLabel.setText(GETTING_STARTED_MESSAGE);
|
||||
@@ -599,48 +603,64 @@ public class KeyBindingsPanel extends JPanel {
|
||||
MouseBinding mb = keyBindings.getMouseBinding(fullActionName);
|
||||
actionBindingPanel.setKeyBindingData(ks, mb);
|
||||
|
||||
String description = action.getDescription();
|
||||
String description = binding.getDescription();
|
||||
if (StringUtils.isBlank(description)) {
|
||||
description = action.getName();
|
||||
description = binding.getName();
|
||||
}
|
||||
|
||||
// Not sure why we escape the html here. Probably just to be safe.
|
||||
statusLabel.setText("<html>" + description);
|
||||
helpButton.setToolTipText("Help for " + action.getName());
|
||||
helpButton.setToolTipText("Help for " + binding.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static GColor COLOR_FG_UNREGISTERED =
|
||||
new GColor("color.fg.options.keybindings.table.unregistered");
|
||||
private static GColor COLOR_FG_UNREGISTERED_SELECTED_UNFOCUSED =
|
||||
new GColor("color.fg.options.keybindings.table.unregistered.selected.unfocused");
|
||||
|
||||
private class KeyBindingsRenderer extends GTableCellRenderer {
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
Component renderer = super.getTableCellRendererComponent(data);
|
||||
ActionBindingsDescriptor action = (ActionBindingsDescriptor) data.getRowObject();
|
||||
if (!action.isRegistered()) {
|
||||
|
||||
boolean selected = data.isSelected();
|
||||
boolean focused = actionTable.isFocusOwner();
|
||||
setForeground(COLOR_FG_UNREGISTERED);
|
||||
|
||||
if (!focused && selected) {
|
||||
// Selected and not focused; light gray background on some LaFs. Update the
|
||||
// foreground to stand out against that color.
|
||||
setForeground(COLOR_FG_UNREGISTERED_SELECTED_UNFOCUSED);
|
||||
}
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
|
||||
private class KeyBindingsTableModel
|
||||
extends GDynamicColumnTableModel<DockingActionIf, Object> {
|
||||
extends GDynamicColumnTableModel<ActionBindingsDescriptor, Object> {
|
||||
|
||||
private List<DockingActionIf> actions;
|
||||
private List<ActionBindingsDescriptor> actions;
|
||||
|
||||
public KeyBindingsTableModel(List<DockingActionIf> actions) {
|
||||
public KeyBindingsTableModel(List<ActionBindingsDescriptor> actions) {
|
||||
super(new ServiceProviderStub());
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<DockingActionIf> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<DockingActionIf> descriptor = new TableColumnDescriptor<>();
|
||||
protected TableColumnDescriptor<ActionBindingsDescriptor> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<ActionBindingsDescriptor> descriptor =
|
||||
new TableColumnDescriptor<>();
|
||||
descriptor.addVisibleColumn("Action Name", String.class, a -> a.getName(), 1, true);
|
||||
descriptor.addVisibleColumn("Key Binding", String.class, a -> {
|
||||
String text = "";
|
||||
String fullName = a.getFullName();
|
||||
KeyStroke ks = keyBindings.getKeyStroke(fullName);
|
||||
if (ks != null) {
|
||||
text += KeyBindingUtils.parseKeyStroke(ks);
|
||||
}
|
||||
|
||||
MouseBinding mb = keyBindings.getMouseBinding(fullName);
|
||||
if (mb != null) {
|
||||
text += " (" + mb.getDisplayText() + ")";
|
||||
}
|
||||
|
||||
return text.trim();
|
||||
});
|
||||
descriptor.addVisibleColumn("Key Binding", String.class, a -> a.getBindingText());
|
||||
descriptor.addVisibleColumn("Owner", String.class, a -> a.getOwnerDescription());
|
||||
descriptor.addHiddenColumn("Description", String.class, a -> a.getDescription());
|
||||
descriptor.addHiddenColumn("Registered?", Boolean.class, a -> a.isRegistered());
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@@ -650,7 +670,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getModelData() {
|
||||
public List<ActionBindingsDescriptor> getModelData() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user