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)) {
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionBindingsDescriptor.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionBindingsDescriptor.java
new file mode 100644
index 0000000000..f763b21248
--- /dev/null
+++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionBindingsDescriptor.java
@@ -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();
+}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindings.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingsModel.java
similarity index 59%
rename from Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindings.java
rename to Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingsModel.java
index 0376fd8cd1..7137f6db12 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindings.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingsModel.java
@@ -17,6 +17,7 @@ package docking.actions;
import java.util.*;
import java.util.Map.Entry;
+import java.util.stream.Collectors;
import javax.swing.KeyStroke;
@@ -30,31 +31,31 @@ import gui.event.MouseBinding;
import util.CollectionUtils;
/**
- * An object that maps actions to key strokes and mouse bindings.
+ * An object that maps actions to key strokes and mouse bindings. This class loads key bindings
+ * from tool options. Clients can use this model to manage the values of each binding until the
+ * user is done editing the bindings. When the client is finished, the bindings changes can be:
+ * applied, ignored or restored to default settings.
*
* This class knows how to load all system actions and how to load any key and mouse bindings for
* those actions from the tool's options. Clients can make changes to the state of this class that
* can then be applied to the system by calling {@link #applyChanges()}.
*/
-public class KeyBindings {
+public class KeyBindingsModel {
private Tool tool;
private ToolOptions keyBindingOptions;
- // allows clients to populate a table of all actions
- private List uniqueActions = new ArrayList<>();
-
// allows clients to know if a given key stroke or mouse binding is in use
private Map> actionNamesByKeyStroke = new HashMap<>();
private Map actionNameByMouseBinding = new HashMap<>();
// tracks all changes to an action's key stroke and mouse bindings, which allows us to apply
// and restore options values
- private Map actionInfoByFullName = new HashMap<>();
+ private Map actionInfoByFullName = new HashMap<>();
private String longestActionName = "";
- public KeyBindings(Tool tool) {
+ public KeyBindingsModel(Tool tool) {
this.tool = tool;
keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
@@ -62,15 +63,19 @@ public class KeyBindings {
init();
}
- public List getUniqueActions() {
- return Collections.unmodifiableList(uniqueActions);
+ public List getActionBindings() {
+
+ return actionInfoByFullName.values()
+ .stream()
+ .map(state -> (ActionBindingsDescriptor) state)
+ .collect(Collectors.toList());
}
/* used for testing */
public Map getKeyStrokesByFullActionName() {
Map result = new HashMap<>();
- Set> entries = actionInfoByFullName.entrySet();
- for (Entry entry : entries) {
+ Set> entries = actionInfoByFullName.entrySet();
+ for (Entry entry : entries) {
String key = entry.getKey();
KeyStroke value = entry.getValue().getCurrentKeyStroke();
result.put(key, value);
@@ -83,12 +88,12 @@ public class KeyBindings {
}
public KeyStroke getKeyStroke(String fullName) {
- ActionKeyBindingState info = actionInfoByFullName.get(fullName);
+ KeyBindingState info = actionInfoByFullName.get(fullName);
return info.getCurrentKeyStroke();
}
public MouseBinding getMouseBinding(String fullName) {
- ActionKeyBindingState info = actionInfoByFullName.get(fullName);
+ KeyBindingState info = actionInfoByFullName.get(fullName);
return info.getCurrentMouseBinding();
}
@@ -114,11 +119,9 @@ public class KeyBindings {
sb.append(" ");
String name = names.get(i);
- ActionKeyBindingState info = actionInfoByFullName.get(name);
- DockingActionIf action = info.getRepresentativeAction();
- String shortName = action.getName();
- sb.append(shortName);
- sb.append(" (").append(action.getOwnerDescription()).append(')');
+ KeyBindingState state = actionInfoByFullName.get(name);
+ String fullName = state.getFullName();
+ sb.append(fullName);
if (i < names.size() - 1) {
sb.append("\n");
}
@@ -155,7 +158,7 @@ public class KeyBindings {
actionNameByMouseBinding.put(newBinding, fullName);
}
- ActionKeyBindingState info = actionInfoByFullName.get(fullName);
+ KeyBindingState info = actionInfoByFullName.get(fullName);
info.setCurrentMouseBinding(newBinding);
return true;
}
@@ -174,14 +177,14 @@ public class KeyBindings {
}
addActionKeyStroke(fullName, newKs);
- ActionKeyBindingState info = actionInfoByFullName.get(fullName);
+ KeyBindingState info = actionInfoByFullName.get(fullName);
info.setCurrentKeyStroke(newKs);
return true;
}
public boolean removeKeyStroke(String fullName) {
- ActionKeyBindingState info = actionInfoByFullName.get(fullName);
+ KeyBindingState info = actionInfoByFullName.get(fullName);
if (info == null) {
return false; // not sure if this can happen
}
@@ -201,7 +204,7 @@ public class KeyBindings {
* system started.
*/
public void restoreOptions() {
- for (ActionKeyBindingState info : actionInfoByFullName.values()) {
+ for (KeyBindingState info : actionInfoByFullName.values()) {
info.restore(keyBindingOptions);
}
}
@@ -210,8 +213,8 @@ public class KeyBindings {
* Cancels any pending changes that have not yet been applied.
*/
public void cancelChanges() {
- for (ActionKeyBindingState info : actionInfoByFullName.values()) {
- info.cancelChanges();
+ for (KeyBindingState info : actionInfoByFullName.values()) {
+ info.clearChanges();
}
}
@@ -219,7 +222,7 @@ public class KeyBindings {
* Applies any pending changes.
*/
public void applyChanges() {
- for (ActionKeyBindingState info : actionInfoByFullName.values()) {
+ for (KeyBindingState info : actionInfoByFullName.values()) {
info.apply(keyBindingOptions);
}
}
@@ -242,6 +245,7 @@ public class KeyBindings {
actionInfoByFullName = new HashMap<>();
+ Set registeredNames = new HashSet<>();
Map> actionsByFullName =
KeyBindingUtils.getAllActionsByFullName(tool);
Set>> entries = actionsByFullName.entrySet();
@@ -252,6 +256,8 @@ public class KeyBindings {
String fullName = entry.getKey();
ActionTrigger trigger = keyBindingOptions.getActionTrigger(fullName, null);
+ registeredNames.add(fullName);
+
KeyStroke ks = null;
MouseBinding mb = null;
@@ -260,18 +266,45 @@ public class KeyBindings {
mb = trigger.getMouseBinding();
}
- ActionKeyBindingState info = new ActionKeyBindingState(actions, ks, mb);
- actionInfoByFullName.put(fullName, info);
-
- uniqueActions.add(info.getRepresentativeAction());
+ ActionKeyBindingState state = new ActionKeyBindingState(actions, ks, mb);
+ actionInfoByFullName.put(fullName, state);
addActionKeyStroke(fullName, ks);
- String shortName = info.getShortName();
+ String shortName = state.getName();
if (shortName.length() > longestActionName.length()) {
longestActionName = shortName;
}
}
+
+ // ask options for unregistered key binding options
+ List allNamesList = keyBindingOptions.getOptionNames();
+ Set unregisteredNames = new HashSet<>(allNamesList);
+ unregisteredNames.removeAll(registeredNames);
+
+ for (String fullName : unregisteredNames) {
+ KeyStroke ks = null;
+ MouseBinding mb = null;
+
+ ActionTrigger trigger = keyBindingOptions.getActionTrigger(fullName, null);
+ if (trigger != null) {
+ ks = trigger.getKeyStroke();
+ mb = trigger.getMouseBinding();
+ }
+
+ UnregisteredActionKeyBindingState state =
+ new UnregisteredActionKeyBindingState(fullName, ks, mb);
+ actionInfoByFullName.put(fullName, state);
+
+ addActionKeyStroke(fullName, ks);
+
+ int description = fullName.indexOf("(");
+ String shortName = fullName.substring(0, description).trim();
+ if (shortName.length() > longestActionName.length()) {
+ longestActionName = shortName;
+ }
+
+ }
}
private void addActionKeyStroke(String actionName, KeyStroke ks) {
@@ -289,62 +322,74 @@ public class KeyBindings {
}
}
- /**
- * A class to store current and original values for key strokes and mouse bindings. This is
- * used to apply changes and restore default values.
- */
- private class ActionKeyBindingState {
+ private abstract class KeyBindingState implements ActionBindingsDescriptor {
- private List actions = new ArrayList<>();
- private KeyStroke originalKeyStroke;
- private KeyStroke currentKeyStroke;
- private MouseBinding originalMouseBinding;
- private MouseBinding currentMouseBinding;
+ KeyStroke originalKeyStroke;
+ KeyStroke currentKeyStroke;
+ MouseBinding originalMouseBinding;
+ MouseBinding currentMouseBinding;
- ActionKeyBindingState(List actions, KeyStroke ks, MouseBinding mb) {
- this.actions.addAll(actions);
+ KeyBindingState(KeyStroke ks, MouseBinding mb) {
this.originalKeyStroke = ks;
this.currentKeyStroke = ks;
this.originalMouseBinding = mb;
this.currentMouseBinding = mb;
}
- public DockingActionIf getRepresentativeAction() {
- // pick one action, they are all conceptually the same
- return actions.get(0);
+ abstract KeyBindingData getCurrentKeyBindingData();
+
+ // restores the key/mouse binding options to their default values
+ abstract void restore(ToolOptions options);
+
+ KeyBindingData getOriginalKeyBindingData() {
+
+ if (originalKeyStroke == null && originalMouseBinding == null) {
+ return null; // the key binding data does not exist or has been cleared
+ }
+
+ ActionTrigger trigger = new ActionTrigger(originalKeyStroke, originalMouseBinding);
+ return new KeyBindingData(trigger);
}
- String getShortName() {
- // pick one action, they are all conceptually the same
- return actions.get(0).getName();
+ @Override
+ public String getBindingText() {
+ String text = "";
+ String fullName = getFullName();
+ KeyStroke ks = getKeyStroke(fullName);
+ if (ks != null) {
+ text += KeyBindingUtils.parseKeyStroke(ks);
+ }
+
+ MouseBinding mb = getMouseBinding(fullName);
+ if (mb != null) {
+ text += " (" + mb.getDisplayText() + ")";
+ }
+
+ return text.trim();
}
- String getFullName() {
- return getRepresentativeAction().getFullName();
- }
-
- public MouseBinding getCurrentMouseBinding() {
+ MouseBinding getCurrentMouseBinding() {
return currentMouseBinding;
}
- public void setCurrentMouseBinding(MouseBinding newMouseBinding) {
+ void setCurrentMouseBinding(MouseBinding newMouseBinding) {
this.currentMouseBinding = newMouseBinding;
}
- public KeyStroke getCurrentKeyStroke() {
+ KeyStroke getCurrentKeyStroke() {
return currentKeyStroke;
}
- public void setCurrentKeyStroke(KeyStroke newKeyStroke) {
+ void setCurrentKeyStroke(KeyStroke newKeyStroke) {
this.currentKeyStroke = newKeyStroke;
}
- public void cancelChanges() {
+ void clearChanges() {
currentKeyStroke = originalKeyStroke;
currentMouseBinding = originalMouseBinding;
}
- public void apply(ToolOptions keyStrokeOptions) {
+ void apply(ToolOptions keyStrokeOptions) {
if (!hasChanged()) {
return;
}
@@ -353,19 +398,17 @@ public class KeyBindings {
apply(keyStrokeOptions, kbd);
}
- private void apply(ToolOptions keyStrokeOptions, KeyBindingData keyBinding) {
-
+ void apply(ToolOptions keyStrokeOptions, KeyBindingData keyBinding) {
+ String fullName = getFullName();
if (keyBinding == null) {
- // no bindings; bindings have been cleared
- for (DockingActionIf action : actions) {
- action.setUnvalidatedKeyBindingData(null);
- }
+ keyStrokeOptions.setActionTrigger(fullName, null);
+ currentKeyStroke = null;
+ currentMouseBinding = null;
return;
}
// 1) update the options with the new value
ActionTrigger newTrigger = keyBinding.getActionTrigger();
- String fullName = getFullName();
keyStrokeOptions.setActionTrigger(fullName, newTrigger);
// 2) update our state so the UI shows the new value
@@ -373,12 +416,7 @@ public class KeyBindings {
currentMouseBinding = newTrigger.getMouseBinding();
}
- private boolean hasChanged() {
- return !Objects.equals(originalKeyStroke, currentKeyStroke) ||
- !Objects.equals(originalMouseBinding, currentMouseBinding);
- }
-
- private boolean matches(KeyBindingData kbData) {
+ boolean matches(KeyBindingData kbData) {
if (CollectionUtils.isAllNull(kbData, currentKeyStroke, currentMouseBinding)) {
return true;
@@ -397,7 +435,137 @@ public class KeyBindings {
return Objects.equals(otherMb, currentMouseBinding);
}
- private KeyBindingData getCurrentKeyBindingData() {
+ boolean hasChanged() {
+ return !Objects.equals(originalKeyStroke, currentKeyStroke) ||
+ !Objects.equals(originalMouseBinding, currentMouseBinding);
+ }
+ }
+
+ /**
+ * Represents info for an action that is known by the options, but has not been registered.
+ */
+ private class UnregisteredActionKeyBindingState extends KeyBindingState {
+
+ private String fullName;
+ private String actionName;
+ private String ownerName;
+
+ UnregisteredActionKeyBindingState(String fullName, KeyStroke ks, MouseBinding mb) {
+ super(ks, mb);
+ this.fullName = fullName;
+
+ int descriptionIndex = fullName.indexOf("(");
+ this.actionName = fullName.substring(0, descriptionIndex).trim();
+
+ int lastParen = fullName.lastIndexOf(")");
+ this.ownerName = fullName.substring(descriptionIndex + 1, lastParen).trim();
+ }
+
+ @Override
+ public String getName() {
+ return actionName;
+ }
+
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
+
+ @Override
+ public String getOwnerDescription() {
+ return ownerName;
+ }
+
+ @Override
+ public String getDescription() {
+ return ""; // no action; no description
+ }
+
+ @Override
+ public DockingActionIf getRepresentativeAction() {
+ return null; // no action
+ }
+
+ @Override
+ public boolean isRegistered() {
+ return false;
+ }
+
+ @Override
+ KeyBindingData getCurrentKeyBindingData() {
+ if (currentKeyStroke == null && currentMouseBinding == null) {
+ return null; // no bindings or the values have been cleared
+ }
+
+ ActionTrigger trigger = new ActionTrigger(currentKeyStroke, currentMouseBinding);
+ return new KeyBindingData(trigger);
+ }
+
+ @Override
+ void restore(ToolOptions options) {
+
+ if (!hasChanged()) {
+ return;
+ }
+
+ KeyBindingData defaultBinding = getOriginalKeyBindingData();
+
+ clearChanges();
+
+ if (!matches(defaultBinding)) {
+ apply(options, defaultBinding);
+ }
+ }
+
+ }
+
+ /**
+ * A class to store current and original values for key strokes and mouse bindings. This is
+ * used to apply changes and restore default values.
+ */
+ private class ActionKeyBindingState extends KeyBindingState {
+
+ private List actions = new ArrayList<>();
+
+ ActionKeyBindingState(List actions, KeyStroke ks, MouseBinding mb) {
+ super(ks, mb);
+ this.actions.addAll(actions);
+ }
+
+ @Override
+ public String getName() {
+ // pick one action, they are all conceptually the same
+ return getRepresentativeAction().getName();
+ }
+
+ @Override
+ public String getFullName() {
+ return getRepresentativeAction().getFullName();
+ }
+
+ @Override
+ public String getOwnerDescription() {
+ return getRepresentativeAction().getOwnerDescription();
+ }
+
+ @Override
+ public String getDescription() {
+ return getRepresentativeAction().getDescription();
+ }
+
+ @Override
+ public DockingActionIf getRepresentativeAction() {
+ // pick one action, they are all conceptually the same
+ return actions.get(0);
+ }
+
+ @Override
+ public boolean isRegistered() {
+ return true;
+ }
+
+ @Override
+ KeyBindingData getCurrentKeyBindingData() {
if (currentKeyStroke == null && currentMouseBinding == null) {
return null; // the key binding data does not exist or has been cleared
@@ -410,11 +578,12 @@ public class KeyBindings {
}
// restores the options to their default values
- public void restore(ToolOptions options) {
+ @Override
+ void restore(ToolOptions options) {
DockingActionIf action = getRepresentativeAction();
KeyBindingData defaultBinding = action.getDefaultKeyBindingData();
- cancelChanges();
+ clearChanges();
if (!matches(defaultBinding)) {
apply(options, defaultBinding);
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyEntryDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyEntryDialog.java
index a349f29d89..f3d1082331 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyEntryDialog.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyEntryDialog.java
@@ -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();
diff --git a/Ghidra/Framework/Project/data/project.theme.properties b/Ghidra/Framework/Project/data/project.theme.properties
index 1fc29cfa51..e2085fae9b 100644
--- a/Ghidra/Framework/Project/data/project.theme.properties
+++ b/Ghidra/Framework/Project/data/project.theme.properties
@@ -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
diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java
index 0a78fe40c6..1ade1749a0 100644
--- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java
+++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java
@@ -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 tableFilterPanel;
+ private GTableFilterPanel 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("" + 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 {
+ extends GDynamicColumnTableModel {
- private List actions;
+ private List actions;
- public KeyBindingsTableModel(List actions) {
+ public KeyBindingsTableModel(List actions) {
super(new ServiceProviderStub());
this.actions = actions;
}
@Override
- protected TableColumnDescriptor createTableColumnDescriptor() {
- TableColumnDescriptor descriptor = new TableColumnDescriptor<>();
+ protected TableColumnDescriptor createTableColumnDescriptor() {
+ TableColumnDescriptor 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 getModelData() {
+ public List getModelData() {
return actions;
}