mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-5967 - Improved Options Key Binding UI
This commit is contained in:
parent
eef9950870
commit
d538513428
12 changed files with 281 additions and 260 deletions
|
@ -70,7 +70,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
setUpDialog();
|
setUpDialog();
|
||||||
|
|
||||||
grabActionsWithoutKeybinding();
|
grabActionsWithoutKeyBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -545,7 +545,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// find 2 actions that do not have key bindings so that we can add and change the values
|
// find 2 actions that do not have key bindings so that we can add and change the values
|
||||||
private void grabActionsWithoutKeybinding() {
|
private void grabActionsWithoutKeyBinding() {
|
||||||
Set<DockingActionIf> list = tool.getAllActions();
|
Set<DockingActionIf> list = tool.getAllActions();
|
||||||
for (DockingActionIf action : list) {
|
for (DockingActionIf action : list) {
|
||||||
if (ignoreAction(action)) {
|
if (ignoreAction(action)) {
|
||||||
|
|
|
@ -123,6 +123,8 @@ icon.widget.imagepanel.zoom.out = icon.zoom.out
|
||||||
icon.widget.filterpanel.filter.off = filter_off.png
|
icon.widget.filterpanel.filter.off = filter_off.png
|
||||||
icon.widget.filterpanel.filter.on = filter_on.png
|
icon.widget.filterpanel.filter.on = filter_on.png
|
||||||
|
|
||||||
|
icon.text.field.clear = icon.delete[size(12,12)]
|
||||||
|
|
||||||
icon.widget.pathmanager.reset = trash-empty.png
|
icon.widget.pathmanager.reset = trash-empty.png
|
||||||
|
|
||||||
icon.widget.table.header.help = info_small.png
|
icon.widget.table.header.help = info_small.png
|
||||||
|
|
|
@ -15,12 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package docking;
|
package docking;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.EmptyBorderButton;
|
||||||
|
import docking.widgets.label.GLabel;
|
||||||
|
import generic.theme.GIcon;
|
||||||
import gui.event.MouseBinding;
|
import gui.event.MouseBinding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,9 +32,7 @@ public class ActionBindingPanel extends JPanel {
|
||||||
private static final String DISABLED_HINT = "Select an action";
|
private static final String DISABLED_HINT = "Select an action";
|
||||||
|
|
||||||
private KeyEntryPanel keyEntryPanel;
|
private KeyEntryPanel keyEntryPanel;
|
||||||
private JCheckBox useMouseBindingCheckBox;
|
|
||||||
private MouseEntryTextField mouseEntryField;
|
private MouseEntryTextField mouseEntryField;
|
||||||
private JPanel textFieldPanel;
|
|
||||||
|
|
||||||
private DockingActionInputBindingListener listener;
|
private DockingActionInputBindingListener listener;
|
||||||
|
|
||||||
|
@ -47,42 +46,31 @@ public class ActionBindingPanel extends JPanel {
|
||||||
|
|
||||||
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
||||||
|
|
||||||
textFieldPanel = new JPanel(new BorderLayout());
|
|
||||||
|
|
||||||
keyEntryPanel = new KeyEntryPanel(20, ks -> listener.keyStrokeChanged(ks));
|
keyEntryPanel = new KeyEntryPanel(20, ks -> listener.keyStrokeChanged(ks));
|
||||||
keyEntryPanel.setDisabledHint(DISABLED_HINT);
|
keyEntryPanel.setDisabledHint(DISABLED_HINT);
|
||||||
keyEntryPanel.setEnabled(false); // enabled on action selection
|
keyEntryPanel.setEnabled(false); // enabled on action selection
|
||||||
|
|
||||||
mouseEntryField = new MouseEntryTextField(20, mb -> listener.mouseBindingChanged(mb));
|
mouseEntryField = new MouseEntryTextField(20, mb -> listener.mouseBindingChanged(mb));
|
||||||
mouseEntryField.setDisabledHint(DISABLED_HINT);
|
mouseEntryField.setDisabledHint(DISABLED_HINT);
|
||||||
mouseEntryField.setEnabled(false); // enabled on action selection
|
|
||||||
|
|
||||||
textFieldPanel.add(keyEntryPanel, BorderLayout.NORTH);
|
JButton clearMouseButton = new EmptyBorderButton(new GIcon("icon.text.field.clear"));
|
||||||
|
clearMouseButton.setName("Clear Mouse Binding");
|
||||||
|
clearMouseButton.addActionListener(e -> mouseEntryField.clearMouseBinding());
|
||||||
|
|
||||||
String checkBoxText = "Enter Mouse Binding";
|
GLabel keyBindingLabel = new GLabel("Key Binding: ");
|
||||||
useMouseBindingCheckBox = new GCheckBox(checkBoxText);
|
JTextField tf = keyEntryPanel.getTextField();
|
||||||
useMouseBindingCheckBox
|
keyBindingLabel.setLabelFor(tf);
|
||||||
.setToolTipText("When checked, the text field accepts mouse buttons");
|
|
||||||
useMouseBindingCheckBox.setName(checkBoxText);
|
|
||||||
useMouseBindingCheckBox.addItemListener(e -> updateTextField());
|
|
||||||
|
|
||||||
add(textFieldPanel);
|
GLabel mouseBindingLabel = new GLabel("Mouse Binding: ");
|
||||||
add(Box.createHorizontalStrut(5));
|
mouseBindingLabel.setLabelFor(mouseBindingLabel);
|
||||||
add(useMouseBindingCheckBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTextField() {
|
add(keyBindingLabel);
|
||||||
|
add(keyEntryPanel);
|
||||||
if (useMouseBindingCheckBox.isSelected()) {
|
add(Box.createHorizontalStrut(30));
|
||||||
textFieldPanel.remove(keyEntryPanel);
|
add(mouseBindingLabel);
|
||||||
textFieldPanel.add(mouseEntryField, BorderLayout.NORTH);
|
add(mouseEntryField);
|
||||||
}
|
add(Box.createHorizontalStrut(2));
|
||||||
else {
|
add(clearMouseButton);
|
||||||
textFieldPanel.remove(mouseEntryField);
|
|
||||||
textFieldPanel.add(keyEntryPanel, BorderLayout.NORTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
validate();
|
|
||||||
repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyBindingData(KeyStroke ks, MouseBinding mb) {
|
public void setKeyBindingData(KeyStroke ks, MouseBinding mb) {
|
||||||
|
@ -113,11 +101,6 @@ public class ActionBindingPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearMouseBinding() {
|
public void clearMouseBinding() {
|
||||||
mouseEntryField.clearField();
|
mouseEntryField.clearMouseBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMouseBinding() {
|
|
||||||
return useMouseBindingCheckBox.isSelected();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ package docking;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.widgets.EmptyBorderButton;
|
import docking.widgets.EmptyBorderButton;
|
||||||
import resources.Icons;
|
import generic.theme.GIcon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A panel that holds a {@link KeyEntryTextField} and a button for clearing the current key binding.
|
* A panel that holds a {@link KeyEntryTextField} and a button for clearing the current key binding.
|
||||||
|
@ -41,7 +41,7 @@ public class KeyEntryPanel extends JPanel {
|
||||||
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
||||||
|
|
||||||
keyEntryField = new KeyEntryTextField(columns, listener);
|
keyEntryField = new KeyEntryTextField(columns, listener);
|
||||||
clearButton = new EmptyBorderButton(Icons.DELETE_ICON);
|
clearButton = new EmptyBorderButton(new GIcon("icon.text.field.clear"));
|
||||||
clearButton.setName("Clear Key Binding");
|
clearButton.setName("Clear Key Binding");
|
||||||
clearButton.addActionListener(e -> keyEntryField.clearKeyStroke());
|
clearButton.addActionListener(e -> keyEntryField.clearKeyStroke());
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package docking;
|
package docking;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.*;
|
||||||
import java.awt.event.KeyListener;
|
import java.awt.event.FocusEvent.Cause;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
@ -47,7 +47,21 @@ public class KeyEntryTextField extends HintTextField {
|
||||||
getAccessibleContext().setAccessibleName(getName());
|
getAccessibleContext().setAccessibleName(getName());
|
||||||
setColumns(columns);
|
setColumns(columns);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
|
||||||
|
// remove the default mouse listeners to prevent pasting
|
||||||
|
MouseListener[] oldListeners1 = getMouseListeners();
|
||||||
|
for (MouseListener l : oldListeners1) {
|
||||||
|
removeMouseListener(l);
|
||||||
|
}
|
||||||
|
|
||||||
addKeyListener(new MyKeyListener());
|
addKeyListener(new MyKeyListener());
|
||||||
|
|
||||||
|
addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
requestFocusInWindow(Cause.MOUSE_EVENT);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package docking;
|
package docking;
|
||||||
|
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
|
import java.awt.event.FocusEvent.Cause;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@ -37,6 +38,12 @@ public class MouseEntryTextField extends HintTextField {
|
||||||
getAccessibleContext().setAccessibleName(getName());
|
getAccessibleContext().setAccessibleName(getName());
|
||||||
this.listener = Objects.requireNonNull(listener);
|
this.listener = Objects.requireNonNull(listener);
|
||||||
|
|
||||||
|
// remove the default mouse listeners to prevent pasting
|
||||||
|
MouseListener[] oldListeners1 = getMouseListeners();
|
||||||
|
for (MouseListener l : oldListeners1) {
|
||||||
|
removeMouseListener(l);
|
||||||
|
}
|
||||||
|
|
||||||
addMouseListener(new MyMouseListener());
|
addMouseListener(new MyMouseListener());
|
||||||
addKeyListener(new MyKeyListener());
|
addKeyListener(new MyKeyListener());
|
||||||
}
|
}
|
||||||
|
@ -63,10 +70,23 @@ public class MouseEntryTextField extends HintTextField {
|
||||||
processMouseBinding(mb, false);
|
processMouseBinding(mb, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the state of this class, but does not notify listeners. This allows clients to
|
||||||
|
* control the state of the field without having a callback change the client state.
|
||||||
|
*/
|
||||||
public void clearField() {
|
public void clearField() {
|
||||||
processMouseBinding(null, false);
|
processMouseBinding(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the state of this class and notifies this client. This effectively allows for the
|
||||||
|
* programmatic setting of the mouse binding in use to be null, or in the 'no mouse binding set'
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
public void clearMouseBinding() {
|
||||||
|
processMouseBinding(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
private void processMouseBinding(MouseBinding mb, boolean notify) {
|
private void processMouseBinding(MouseBinding mb, boolean notify) {
|
||||||
|
|
||||||
this.mouseBinding = mb;
|
this.mouseBinding = mb;
|
||||||
|
@ -90,6 +110,8 @@ public class MouseEntryTextField extends HintTextField {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestFocusInWindow(Cause.MOUSE_EVENT);
|
||||||
|
|
||||||
int modifiersEx = e.getModifiersEx();
|
int modifiersEx = e.getModifiersEx();
|
||||||
int button = e.getButton();
|
int button = e.getButton();
|
||||||
|
|
||||||
|
@ -102,6 +124,7 @@ public class MouseEntryTextField extends HintTextField {
|
||||||
}
|
}
|
||||||
|
|
||||||
processMouseBinding(new MouseBinding(button, modifiersEx), true);
|
processMouseBinding(new MouseBinding(button, modifiersEx), true);
|
||||||
|
|
||||||
e.consume();
|
e.consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ public class KeyBindingUtils {
|
||||||
public static ToolOptions importKeyBindings() {
|
public static ToolOptions importKeyBindings() {
|
||||||
// show a filechooser for the user to choose a location
|
// show a filechooser for the user to choose a location
|
||||||
InputStream inputStream = getInputStreamForFile(getStartingDir());
|
InputStream inputStream = getInputStreamForFile(getStartingDir());
|
||||||
return createOptionsforKeybindings(inputStream);
|
return createOptionsforKeyBindings(inputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,7 +107,7 @@ public class KeyBindingUtils {
|
||||||
* @return An options object that is composed of key binding names and their
|
* @return An options object that is composed of key binding names and their
|
||||||
* associated keystrokes.
|
* associated keystrokes.
|
||||||
*/
|
*/
|
||||||
public static ToolOptions createOptionsforKeybindings(InputStream inputStream) {
|
public static ToolOptions createOptionsforKeyBindings(InputStream inputStream) {
|
||||||
if (inputStream == null) {
|
if (inputStream == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class SetKeyBindingAction extends DockingAction {
|
||||||
|
|
||||||
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
||||||
Component parent = windowManager.getActiveComponent();
|
Component parent = windowManager.getActiveComponent();
|
||||||
Msg.showInfo(getClass(), parent, "Unable to Set Keybinding",
|
Msg.showInfo(getClass(), parent, "Unable to Set Key Binding",
|
||||||
"Action \"" + getActionName(action) + "\" does not support key bindings");
|
"Action \"" + getActionName(action) + "\" does not support key bindings");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,7 +241,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
||||||
private void loadKeyBindingFromOptions(DockingActionIf action, ActionTrigger actionTrigger) {
|
private void loadKeyBindingFromOptions(DockingActionIf action, ActionTrigger actionTrigger) {
|
||||||
|
|
||||||
String fullName = action.getFullName();
|
String fullName = action.getFullName();
|
||||||
String description = "Keybinding for " + fullName;
|
String description = "Key Binding for " + fullName;
|
||||||
options.registerOption(fullName, OptionType.ACTION_TRIGGER, actionTrigger, null,
|
options.registerOption(fullName, OptionType.ACTION_TRIGGER, actionTrigger, null,
|
||||||
description);
|
description);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import utilities.util.reflection.ReflectionUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Class to render a String that has new line characters as a multiline
|
* Class to render a String that has new line characters as a multi-line
|
||||||
* label. Calculates the resizing and centering characteristics.
|
* label. Calculates the resizing and centering characteristics.
|
||||||
* <p>
|
* <p>
|
||||||
* Not affected by HTML formatting.
|
* Not affected by HTML formatting.
|
||||||
|
@ -49,12 +49,19 @@ public class MultiLineLabel extends JPanel {
|
||||||
protected String[] lines; // lines to text to display
|
protected String[] lines; // lines to text to display
|
||||||
protected int num_lines; // number of lines
|
protected int num_lines; // number of lines
|
||||||
protected int margin_width; // left and right margins
|
protected int margin_width; // left and right margins
|
||||||
protected int margin_height; // top and botton margins
|
protected int margin_height; // top and bottom margins
|
||||||
protected int line_height; // total height of font
|
protected int line_height; // total height of font
|
||||||
protected int line_ascent; // font height above baseline
|
protected int line_ascent; // font height above baseline
|
||||||
protected int[] line_widths; // how wide each line is
|
protected int[] line_widths; // how wide each line is
|
||||||
protected int max_width; // width of widest line
|
protected int max_width; // width of widest line
|
||||||
protected int alignment = CENTER; // default alignment of text
|
protected int alignment = CENTER; // default alignment of text
|
||||||
|
private VerticalAlignment verticalAlignment = VerticalAlignment.MIDDLE;
|
||||||
|
|
||||||
|
/** Values for controlling vertical alignment of the text */
|
||||||
|
public enum VerticalAlignment {
|
||||||
|
TOP,
|
||||||
|
MIDDLE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
|
@ -91,9 +98,9 @@ public class MultiLineLabel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* breaks specified label into array of lines.
|
* Breaks specified label into array of lines.
|
||||||
*
|
*
|
||||||
*@param label String to display in canvas.
|
*@param label String to display.
|
||||||
*/
|
*/
|
||||||
protected void newLabel(String label) {
|
protected void newLabel(String label) {
|
||||||
if (label == null) {
|
if (label == null) {
|
||||||
|
@ -119,9 +126,6 @@ public class MultiLineLabel extends JPanel {
|
||||||
protected void measure() {
|
protected void measure() {
|
||||||
|
|
||||||
FontMetrics fm = this.getFontMetrics(this.getFont());
|
FontMetrics fm = this.getFontMetrics(this.getFont());
|
||||||
|
|
||||||
// if no font metrics yet, just return
|
|
||||||
|
|
||||||
if (fm == null) {
|
if (fm == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -139,9 +143,9 @@ public class MultiLineLabel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new label for JPanel
|
* Set a new label to display.
|
||||||
*
|
*
|
||||||
* @param label String to display in canvas
|
* @param label String to display
|
||||||
*/
|
*/
|
||||||
public void setLabel(String label) {
|
public void setLabel(String label) {
|
||||||
|
|
||||||
|
@ -163,7 +167,7 @@ public class MultiLineLabel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the label text.
|
* {@return the label text.}
|
||||||
*/
|
*/
|
||||||
public String getLabel() {
|
public String getLabel() {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
|
@ -189,11 +193,6 @@ public class MultiLineLabel extends JPanel {
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a new color for Canvas
|
|
||||||
*
|
|
||||||
*@param c Color to display in canvas
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setForeground(Color c) {
|
public void setForeground(Color c) {
|
||||||
super.setForeground(c);
|
super.setForeground(c);
|
||||||
|
@ -209,6 +208,15 @@ public class MultiLineLabel extends JPanel {
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the vertical alignment of the text. The default is {@link VerticalAlignment#MIDDLE}.
|
||||||
|
* @param alignment the alignment
|
||||||
|
*/
|
||||||
|
public void setVerticalAlignment(VerticalAlignment alignment) {
|
||||||
|
this.verticalAlignment = alignment;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set margin width.
|
* Set margin width.
|
||||||
* @param mw the new margin width.
|
* @param mw the new margin width.
|
||||||
|
@ -227,29 +235,20 @@ public class MultiLineLabel extends JPanel {
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get alignment for text, LEFT, CENTER, RIGHT.
|
|
||||||
*/
|
|
||||||
public final int getAlignment() {
|
public final int getAlignment() {
|
||||||
return alignment;
|
return alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get margin width.
|
|
||||||
*/
|
|
||||||
public final int getMarginWidth() {
|
public final int getMarginWidth() {
|
||||||
return margin_width;
|
return margin_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*Get margin height.
|
|
||||||
*/
|
|
||||||
public final int getMarginHeight() {
|
public final int getMarginHeight() {
|
||||||
return margin_height;
|
return margin_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is invoked after Canvas is first created
|
* This method is invoked after this class is first created
|
||||||
* but before it can be actually displayed. After we have
|
* but before it can be actually displayed. After we have
|
||||||
* invoked our superclass's addNotify() method, we have font
|
* invoked our superclass's addNotify() method, we have font
|
||||||
* metrics and can successfully call measure() to figure out
|
* metrics and can successfully call measure() to figure out
|
||||||
|
@ -257,48 +256,38 @@ public class MultiLineLabel extends JPanel {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void addNotify() {
|
public void addNotify() {
|
||||||
|
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
measure();
|
measure();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called by a layout manager when it wants
|
|
||||||
* to know how big we'd like to be
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public java.awt.Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
return new Dimension(max_width + 2 * margin_width,
|
return new Dimension(max_width + 2 * margin_width,
|
||||||
num_lines * line_height + 2 * margin_height);
|
num_lines * line_height + 2 * margin_height);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when layout manager wants to
|
|
||||||
* know the bare minimum amount of space we need to get by.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public java.awt.Dimension getMinimumSize() {
|
public Dimension getMinimumSize() {
|
||||||
|
|
||||||
return new Dimension(max_width, num_lines * line_height);
|
return new Dimension(max_width, num_lines * line_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method draws label (applets use same method).
|
|
||||||
* Note that it handles the margins and the alignment, but
|
|
||||||
* that is does not have to worry about the color or font --
|
|
||||||
* the superclass takes care of setting those in the Graphics
|
|
||||||
* object we've passed.
|
|
||||||
* @param g the graphics context to paint with.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void paint(Graphics g) {
|
public void paint(Graphics g) {
|
||||||
|
|
||||||
int x, y;
|
paintBorder(g);
|
||||||
Dimension d = this.getSize();
|
|
||||||
// g.clearRect(0, 0, d.width, d.height);
|
|
||||||
|
|
||||||
|
Dimension d = this.getSize();
|
||||||
|
|
||||||
|
int y;
|
||||||
|
if (verticalAlignment == VerticalAlignment.MIDDLE) {
|
||||||
y = line_ascent + (d.height - num_lines * line_height) / 2;
|
y = line_ascent + (d.height - num_lines * line_height) / 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
y = margin_height + line_ascent;
|
||||||
|
}
|
||||||
|
int x;
|
||||||
for (int i = 0; i < num_lines; i++, y += line_height) {
|
for (int i = 0; i < num_lines; i++, y += line_height) {
|
||||||
switch (alignment) {
|
switch (alignment) {
|
||||||
case LEFT:
|
case LEFT:
|
||||||
|
@ -313,14 +302,11 @@ public class MultiLineLabel extends JPanel {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsUtils.drawString(this, g, lines[i], x, y);
|
GraphicsUtils.drawString(this, g, lines[i], x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple test for the MultiLineLabel class.
|
|
||||||
* @param args not used
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
MultiLineLabel mlab = new MultiLineLabel(
|
MultiLineLabel mlab = new MultiLineLabel(
|
||||||
|
|
|
@ -29,9 +29,8 @@ import org.jdesktop.animation.timing.interpolation.PropertySetter;
|
||||||
|
|
||||||
import docking.util.AnimationUtils;
|
import docking.util.AnimationUtils;
|
||||||
import docking.widgets.label.GIconLabel;
|
import docking.widgets.label.GIconLabel;
|
||||||
|
import generic.theme.GIcon;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
import resources.Icons;
|
|
||||||
import resources.ResourceManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A label that displays an icon that, when clicked, will clear the contents of the
|
* A label that displays an icon that, when clicked, will clear the contents of the
|
||||||
|
@ -39,8 +38,7 @@ import resources.ResourceManager;
|
||||||
*/
|
*/
|
||||||
public class ClearFilterLabel extends GIconLabel {
|
public class ClearFilterLabel extends GIconLabel {
|
||||||
|
|
||||||
private Icon RAW_ICON = Icons.DELETE_ICON;
|
private Icon ICON = new GIcon("icon.text.field.clear");
|
||||||
private Icon ICON = ResourceManager.getScaledIcon(RAW_ICON, 10, 10);
|
|
||||||
|
|
||||||
private static final float FULLY_TRANSPARENT = 0F;
|
private static final float FULLY_TRANSPARENT = 0F;
|
||||||
private static final float FULLY_OPAQUE = .6F;
|
private static final float FULLY_OPAQUE = .6F;
|
||||||
|
|
|
@ -33,14 +33,14 @@ import docking.action.DockingActionIf;
|
||||||
import docking.actions.*;
|
import docking.actions.*;
|
||||||
import docking.tool.util.DockingToolConstants;
|
import docking.tool.util.DockingToolConstants;
|
||||||
import docking.widgets.*;
|
import docking.widgets.*;
|
||||||
import docking.widgets.label.GIconLabel;
|
import docking.widgets.MultiLineLabel.VerticalAlignment;
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import generic.theme.Gui;
|
import generic.theme.Gui;
|
||||||
import ghidra.framework.options.*;
|
import ghidra.framework.options.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.util.*;
|
import ghidra.framework.plugintool.ServiceProviderStub;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.layout.VerticalLayout;
|
import ghidra.util.Swing;
|
||||||
import gui.event.MouseBinding;
|
import gui.event.MouseBinding;
|
||||||
import help.Help;
|
import help.Help;
|
||||||
import help.HelpService;
|
import help.HelpService;
|
||||||
|
@ -51,7 +51,8 @@ import resources.Icons;
|
||||||
*/
|
*/
|
||||||
public class KeyBindingsPanel extends JPanel {
|
public class KeyBindingsPanel extends JPanel {
|
||||||
|
|
||||||
private static final int STATUS_LABEL_HEIGHT = 60;
|
private static final String GETTING_STARTED_MESSAGE =
|
||||||
|
"<html><i>Select an action to change a keybinding";
|
||||||
|
|
||||||
private final static int ACTION_NAME = 0;
|
private final static int ACTION_NAME = 0;
|
||||||
private final static int KEY_BINDING = 1;
|
private final static int KEY_BINDING = 1;
|
||||||
|
@ -61,7 +62,6 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
|
|
||||||
private JTextPane statusLabel;
|
private JTextPane statusLabel;
|
||||||
private GTable actionTable;
|
private GTable actionTable;
|
||||||
private JPanel infoPanel;
|
|
||||||
private MultiLineLabel collisionLabel;
|
private MultiLineLabel collisionLabel;
|
||||||
private KeyBindingsTableModel tableModel;
|
private KeyBindingsTableModel tableModel;
|
||||||
private ActionBindingListener actionBindingListener = new ActionBindingListener();
|
private ActionBindingListener actionBindingListener = new ActionBindingListener();
|
||||||
|
@ -76,6 +76,9 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
private boolean firingTableDataChanged;
|
private boolean firingTableDataChanged;
|
||||||
private PropertyChangeListener propertyChangeListener;
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
|
||||||
|
private JPanel gettingStartedPanel;
|
||||||
|
private JPanel activeActionPanel;
|
||||||
|
|
||||||
public KeyBindingsPanel(PluginTool tool) {
|
public KeyBindingsPanel(PluginTool tool) {
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
|
|
||||||
|
@ -110,7 +113,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
// clear the action to avoid the appearance of editing while restoring
|
// clear the action to avoid the appearance of editing while restoring
|
||||||
actionTable.clearSelection();
|
actionTable.clearSelection();
|
||||||
|
|
||||||
restoreDefaultKeybindings();
|
restoreDefaultKeyBindings();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,50 +138,69 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPanelComponents() {
|
private void createPanelComponents() {
|
||||||
|
|
||||||
setLayout(new BorderLayout(10, 10));
|
setLayout(new BorderLayout(10, 10));
|
||||||
|
|
||||||
|
// A stub panel to take up about the same amount of space as the active panel. This stub
|
||||||
|
// panel will get swapped for the active panel when a selection is made in the table. Using
|
||||||
|
// the stub panel is easier than trying to visually disable the editing widgets.
|
||||||
|
gettingStartedPanel = new JPanel();
|
||||||
|
activeActionPanel = createActiveActionPanel();
|
||||||
|
|
||||||
tableModel = new KeyBindingsTableModel(new ArrayList<>(keyBindings.getUniqueActions()));
|
tableModel = new KeyBindingsTableModel(new ArrayList<>(keyBindings.getUniqueActions()));
|
||||||
actionTable = new GTable(tableModel);
|
actionTable = new GTable(tableModel);
|
||||||
|
|
||||||
JScrollPane sp = new JScrollPane(actionTable);
|
JScrollPane actionsScroller = new JScrollPane(actionTable);
|
||||||
actionTable.setPreferredScrollableViewportSize(new Dimension(400, 100));
|
actionTable.setPreferredScrollableViewportSize(new Dimension(400, 100));
|
||||||
actionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
actionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
actionTable.setHTMLRenderingEnabled(true);
|
actionTable.setHTMLRenderingEnabled(true);
|
||||||
|
actionTable.getSelectionModel().addListSelectionListener(new TableSelectionListener());
|
||||||
|
|
||||||
adjustTableColumns();
|
adjustTableColumns();
|
||||||
|
|
||||||
// middle panel - filter field and import/export buttons
|
// middle panel - filter field and import/export buttons
|
||||||
JPanel importExportPanel = createImportExportPanel();
|
JPanel importExportPanel = createImportExportPanel();
|
||||||
tableFilterPanel = new GTableFilterPanel<>(actionTable, tableModel);
|
tableFilterPanel = new GTableFilterPanel<>(actionTable, tableModel);
|
||||||
JPanel middlePanel = new JPanel(new BorderLayout());
|
JPanel filterAndExportsPanel = new JPanel(new BorderLayout());
|
||||||
middlePanel.add(tableFilterPanel, BorderLayout.NORTH);
|
filterAndExportsPanel.add(tableFilterPanel, BorderLayout.NORTH);
|
||||||
middlePanel.add(importExportPanel, BorderLayout.SOUTH);
|
filterAndExportsPanel.add(importExportPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
// contains the upper panel (table) and the middle panel)
|
// contains the upper panel (table and the middle panel)
|
||||||
JPanel centerPanel = new JPanel(new BorderLayout());
|
JPanel centerPanel = new JPanel(new BorderLayout());
|
||||||
centerPanel.add(sp, BorderLayout.CENTER);
|
centerPanel.add(actionsScroller, BorderLayout.CENTER);
|
||||||
centerPanel.add(middlePanel, BorderLayout.SOUTH);
|
centerPanel.add(filterAndExportsPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
add(centerPanel, BorderLayout.CENTER);
|
||||||
|
add(gettingStartedPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
// make both panels the same size so that as we swap them, the UI doesn't jump
|
||||||
|
Dimension preferredSize = activeActionPanel.getPreferredSize();
|
||||||
|
gettingStartedPanel.setPreferredSize(preferredSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel createActiveActionPanel() {
|
||||||
|
|
||||||
// lower panel - key entry panel and status panel
|
// lower panel - key entry panel and status panel
|
||||||
JPanel keyPanel = createKeyEntryPanel();
|
JPanel keyPanel = createKeyEntryPanel();
|
||||||
JComponent statusPanel = createStatusPanel(keyPanel);
|
JPanel collisionAreaPanel = createCollisionArea();
|
||||||
|
|
||||||
add(centerPanel, BorderLayout.CENTER);
|
JPanel parentPanel = new JPanel(new BorderLayout());
|
||||||
add(statusPanel, BorderLayout.SOUTH);
|
parentPanel.add(keyPanel, BorderLayout.NORTH);
|
||||||
|
parentPanel.add(collisionAreaPanel, BorderLayout.SOUTH);
|
||||||
actionTable.getSelectionModel().addListSelectionListener(new TableSelectionListener());
|
return parentPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel createStatusPanel(JPanel keyPanel) {
|
private JPanel createStatusPanel() {
|
||||||
|
|
||||||
statusLabel = new JTextPane();
|
statusLabel = new JTextPane();
|
||||||
statusLabel.setEnabled(false);
|
statusLabel.setEnabled(false);
|
||||||
DockingUtils.setTransparent(statusLabel);
|
DockingUtils.setTransparent(statusLabel);
|
||||||
statusLabel.setBorder(BorderFactory.createEmptyBorder(5, 10, 0, 5));
|
statusLabel.setBorder(BorderFactory.createEmptyBorder(5, 10, 0, 5));
|
||||||
statusLabel.setContentType("text/html"); // render any HTML we find in descriptions
|
statusLabel.setContentType("text/html"); // render any HTML we find in descriptions
|
||||||
|
statusLabel.setText(GETTING_STARTED_MESSAGE);
|
||||||
|
|
||||||
// make sure the label gets enough space
|
// make the label wide enough to show a line of text, but set a limit to force wrapping
|
||||||
statusLabel.setPreferredSize(new Dimension(0, STATUS_LABEL_HEIGHT));
|
statusLabel.setPreferredSize(new Dimension(300, 30));
|
||||||
statusLabel.setFont(Gui.getFont(FONT_ID));
|
statusLabel.setFont(Gui.getFont(FONT_ID));
|
||||||
|
|
||||||
helpButton = new EmptyBorderButton(Icons.HELP_ICON);
|
helpButton = new EmptyBorderButton(Icons.HELP_ICON);
|
||||||
|
@ -189,70 +211,47 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
hs.showHelp(action, false, KeyBindingsPanel.this);
|
hs.showHelp(action, false, KeyBindingsPanel.this);
|
||||||
});
|
});
|
||||||
|
|
||||||
JPanel helpButtonPanel = new JPanel();
|
JPanel statusPanel = new JPanel();
|
||||||
helpButtonPanel.setLayout(new BoxLayout(helpButtonPanel, BoxLayout.PAGE_AXIS));
|
statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.LINE_AXIS));
|
||||||
helpButtonPanel.add(helpButton);
|
statusPanel.add(helpButton);
|
||||||
helpButtonPanel.add(Box.createVerticalGlue());
|
statusPanel.add(statusLabel);
|
||||||
|
|
||||||
JPanel lowerStatusPanel = new JPanel();
|
statusPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
|
||||||
lowerStatusPanel.setLayout(new BoxLayout(lowerStatusPanel, BoxLayout.X_AXIS));
|
|
||||||
lowerStatusPanel.add(helpButtonPanel);
|
|
||||||
lowerStatusPanel.add(statusLabel);
|
|
||||||
|
|
||||||
JPanel panel = new JPanel(new VerticalLayout(5));
|
return statusPanel;
|
||||||
panel.add(keyPanel);
|
|
||||||
panel.add(lowerStatusPanel);
|
|
||||||
return panel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel createKeyEntryPanel() {
|
private JPanel createKeyEntryPanel() {
|
||||||
actionBindingPanel = new ActionBindingPanel(actionBindingListener);
|
actionBindingPanel = new ActionBindingPanel(actionBindingListener);
|
||||||
|
|
||||||
// this is the lower panel that holds the key entry text field
|
// add some space at the bottom of the input area to separate it from the info area
|
||||||
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
actionBindingPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 20, 0));
|
||||||
p.add(actionBindingPanel);
|
|
||||||
|
|
||||||
JPanel keyPanel = new JPanel(new BorderLayout());
|
JPanel keyPanel = new JPanel(new BorderLayout());
|
||||||
|
keyPanel.add(actionBindingPanel, BorderLayout.NORTH);
|
||||||
JPanel defaultPanel = new JPanel(new BorderLayout());
|
|
||||||
|
|
||||||
// the content of the left-hand side label
|
|
||||||
MultiLineLabel mlabel =
|
|
||||||
new MultiLineLabel("To add or change a key binding, select an action\n" +
|
|
||||||
"and type any key combination.");
|
|
||||||
JPanel labelPanel = new JPanel();
|
|
||||||
labelPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
|
|
||||||
BoxLayout bl = new BoxLayout(labelPanel, BoxLayout.X_AXIS);
|
|
||||||
labelPanel.setLayout(bl);
|
|
||||||
labelPanel.add(Box.createHorizontalStrut(5));
|
|
||||||
labelPanel.add(new GIconLabel(Icons.INFO_ICON));
|
|
||||||
labelPanel.add(Box.createHorizontalStrut(5));
|
|
||||||
labelPanel.add(mlabel);
|
|
||||||
|
|
||||||
// the default panel is the panel that holds left-hand side label
|
|
||||||
defaultPanel.add(labelPanel, BorderLayout.NORTH);
|
|
||||||
defaultPanel.setBorder(BorderFactory.createLoweredBevelBorder());
|
|
||||||
|
|
||||||
// the info panel is the holds the right-hand label and is inside of
|
|
||||||
// a scroll pane
|
|
||||||
infoPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
|
||||||
collisionLabel = new MultiLineLabel(" ");
|
|
||||||
collisionLabel.setName("CollisionLabel");
|
|
||||||
|
|
||||||
infoPanel.add(collisionLabel);
|
|
||||||
JScrollPane sp = new JScrollPane(infoPanel);
|
|
||||||
sp.setPreferredSize(defaultPanel.getPreferredSize());
|
|
||||||
|
|
||||||
// inner panel holds the two label panels
|
|
||||||
JPanel innerPanel = new JPanel(new PairLayout(2, 6));
|
|
||||||
innerPanel.add(defaultPanel);
|
|
||||||
innerPanel.add(sp);
|
|
||||||
|
|
||||||
keyPanel.add(innerPanel, BorderLayout.CENTER);
|
|
||||||
keyPanel.add(p, BorderLayout.SOUTH);
|
|
||||||
return keyPanel;
|
return keyPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JPanel createCollisionArea() {
|
||||||
|
|
||||||
|
collisionLabel = new MultiLineLabel(" ");
|
||||||
|
collisionLabel.setVerticalAlignment(VerticalAlignment.TOP);
|
||||||
|
collisionLabel.setName("CollisionLabel");
|
||||||
|
JScrollPane collisionScroller = new JScrollPane(collisionLabel);
|
||||||
|
int height = 100; // enough to show the typical number of collisions without scrolling
|
||||||
|
collisionScroller.setPreferredSize(new Dimension(400, height));
|
||||||
|
|
||||||
|
// note: we add a strut so that when the scroll pane is hidden, the size does not change
|
||||||
|
JPanel parentPanel = new JPanel(new BorderLayout());
|
||||||
|
parentPanel.add(collisionScroller, BorderLayout.CENTER);
|
||||||
|
parentPanel.add(Box.createVerticalStrut(height), BorderLayout.WEST);
|
||||||
|
|
||||||
|
JPanel alignmentPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
|
alignmentPanel.add(parentPanel);
|
||||||
|
|
||||||
|
return alignmentPanel;
|
||||||
|
}
|
||||||
|
|
||||||
private JPanel createImportExportPanel() {
|
private JPanel createImportExportPanel() {
|
||||||
JButton importButton = new JButton("Import...");
|
JButton importButton = new JButton("Import...");
|
||||||
importButton.setToolTipText("Load key binding settings from a file");
|
importButton.setToolTipText("Load key binding settings from a file");
|
||||||
|
@ -290,11 +289,16 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
JPanel containerPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
JPanel statusPanel = createStatusPanel();
|
||||||
containerPanel.add(importButton);
|
|
||||||
containerPanel.add(exportButton);
|
|
||||||
|
|
||||||
return containerPanel;
|
JPanel buttonPanel = new JPanel();
|
||||||
|
buttonPanel.add(importButton);
|
||||||
|
buttonPanel.add(exportButton);
|
||||||
|
|
||||||
|
JPanel parentPanel = new JPanel(new BorderLayout());
|
||||||
|
parentPanel.add(statusPanel, BorderLayout.WEST);
|
||||||
|
parentPanel.add(buttonPanel, BorderLayout.EAST);
|
||||||
|
return parentPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean showApplyPrompt() {
|
private boolean showApplyPrompt() {
|
||||||
|
@ -354,7 +358,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
column.setPreferredWidth(150);
|
column.setPreferredWidth(150);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restoreDefaultKeybindings() {
|
private void restoreDefaultKeyBindings() {
|
||||||
keyBindings.restoreOptions();
|
keyBindings.restoreOptions();
|
||||||
|
|
||||||
// let the table know that changes may have been made
|
// let the table know that changes may have been made
|
||||||
|
@ -398,13 +402,20 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCollisionPanel(String text) {
|
private void updateCollisionPanel(String text) {
|
||||||
infoPanel.removeAll();
|
|
||||||
infoPanel.repaint();
|
// Hide the scroll pane when there is nothing to show
|
||||||
collisionLabel = new MultiLineLabel(text);
|
Container parent = collisionLabel.getParent().getParent();
|
||||||
collisionLabel.setName("CollisionLabel");
|
if (text.isBlank()) {
|
||||||
infoPanel.add(collisionLabel);
|
parent.setVisible(false);
|
||||||
infoPanel.invalidate();
|
}
|
||||||
|
else {
|
||||||
|
parent.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
collisionLabel.setLabel(text);
|
||||||
|
collisionLabel.invalidate();
|
||||||
validate();
|
validate();
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadKeyBindingsFromImportedOptions(Options keyBindingOptions) {
|
private void loadKeyBindingsFromImportedOptions(Options keyBindingOptions) {
|
||||||
|
@ -448,7 +459,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
|
|
||||||
DockingActionIf action = getSelectedAction();
|
DockingActionIf action = getSelectedAction();
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
statusLabel.setText("No action is selected.");
|
statusLabel.setText(GETTING_STARTED_MESSAGE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +474,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
String selectedActionName = action.getFullName();
|
String selectedActionName = action.getFullName();
|
||||||
if (setActionKeyStroke(selectedActionName, ks)) {
|
if (setActionKeyStroke(selectedActionName, ks)) {
|
||||||
showActionsMappedToKeyStroke(ks);
|
showActionsMappedToKeyStroke(ks);
|
||||||
tableModel.fireTableDataChanged();
|
fireRowChanged();
|
||||||
changesMade(true);
|
changesMade(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,17 +485,23 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
|
|
||||||
DockingActionIf action = getSelectedAction();
|
DockingActionIf action = getSelectedAction();
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
statusLabel.setText("No action is selected.");
|
statusLabel.setText(GETTING_STARTED_MESSAGE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String selectedActionName = action.getFullName();
|
String selectedActionName = action.getFullName();
|
||||||
if (setMouseBinding(selectedActionName, mb)) {
|
if (setMouseBinding(selectedActionName, mb)) {
|
||||||
tableModel.fireTableDataChanged();
|
fireRowChanged();
|
||||||
changesMade(true);
|
changesMade(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fireRowChanged() {
|
||||||
|
int viewRow = actionTable.getSelectedRow();
|
||||||
|
int modelRow = tableFilterPanel.getModelRow(viewRow);
|
||||||
|
tableModel.fireTableRowsUpdated(modelRow, modelRow);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean setMouseBinding(String actionName, MouseBinding mouseBinding) {
|
private boolean setMouseBinding(String actionName, MouseBinding mouseBinding) {
|
||||||
|
|
||||||
if (keyBindings.isMouseBindingInUse(actionName, mouseBinding)) {
|
if (keyBindings.isMouseBindingInUse(actionName, mouseBinding)) {
|
||||||
|
@ -525,9 +542,25 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
return keyBindings.getKeyStrokesByFullActionName();
|
return keyBindings.getKeyStrokesByFullActionName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void swapView(JComponent newView) {
|
||||||
|
|
||||||
|
// the lower panel we want to swap is at index 1 (index 0 is the table area)
|
||||||
|
Component component = getComponent(1);
|
||||||
|
if (component == newView) {
|
||||||
|
return; // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(component);
|
||||||
|
add(newView, BorderLayout.SOUTH);
|
||||||
|
Container parent = getParent();
|
||||||
|
parent.validate();
|
||||||
|
parent.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Inner Classes
|
// Inner Classes
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selection listener class for the table model.
|
* Selection listener class for the table model.
|
||||||
*/
|
*/
|
||||||
|
@ -539,15 +572,22 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
helpButton.setEnabled(false);
|
helpButton.setEnabled(false);
|
||||||
String fullActionName = getSelectedActionName();
|
|
||||||
if (fullActionName == null) {
|
DockingActionIf action = getSelectedAction();
|
||||||
statusLabel.setText("");
|
if (action == null) {
|
||||||
|
swapView(gettingStartedPanel);
|
||||||
|
|
||||||
|
statusLabel.setText(GETTING_STARTED_MESSAGE);
|
||||||
actionBindingPanel.setEnabled(false);
|
actionBindingPanel.setEnabled(false);
|
||||||
|
helpButton.setToolTipText("Select action in table for help");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
actionBindingPanel.setEnabled(true);
|
String fullActionName = getSelectedActionName();
|
||||||
|
|
||||||
|
swapView(activeActionPanel);
|
||||||
|
|
||||||
|
actionBindingPanel.setEnabled(true);
|
||||||
helpButton.setEnabled(true);
|
helpButton.setEnabled(true);
|
||||||
clearInfoPanel();
|
clearInfoPanel();
|
||||||
|
|
||||||
|
@ -559,44 +599,34 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
MouseBinding mb = keyBindings.getMouseBinding(fullActionName);
|
MouseBinding mb = keyBindings.getMouseBinding(fullActionName);
|
||||||
actionBindingPanel.setKeyBindingData(ks, mb);
|
actionBindingPanel.setKeyBindingData(ks, mb);
|
||||||
|
|
||||||
// make sure the label gets enough space
|
|
||||||
statusLabel.setPreferredSize(
|
|
||||||
new Dimension(statusLabel.getPreferredSize().width, STATUS_LABEL_HEIGHT));
|
|
||||||
|
|
||||||
DockingActionIf action = getSelectedAction();
|
|
||||||
String description = action.getDescription();
|
String description = action.getDescription();
|
||||||
if (description == null || description.trim().isEmpty()) {
|
if (StringUtils.isBlank(description)) {
|
||||||
description = action.getName();
|
description = action.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
statusLabel.setText("<html>" + HTMLUtilities.escapeHTML(description));
|
// Not sure why we escape the html here. Probably just to be safe.
|
||||||
|
statusLabel.setText("<html>" + description);
|
||||||
|
helpButton.setToolTipText("Help for " + action.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class KeyBindingsTableModel extends AbstractSortedTableModel<DockingActionIf> {
|
private class KeyBindingsTableModel
|
||||||
private final String[] columnNames = { "Action Name", "KeyBinding", "Plugin Name" };
|
extends GDynamicColumnTableModel<DockingActionIf, Object> {
|
||||||
|
|
||||||
private List<DockingActionIf> actions;
|
private List<DockingActionIf> actions;
|
||||||
|
|
||||||
KeyBindingsTableModel(List<DockingActionIf> actions) {
|
public KeyBindingsTableModel(List<DockingActionIf> actions) {
|
||||||
super(0);
|
super(new ServiceProviderStub());
|
||||||
this.actions = actions;
|
this.actions = actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
protected TableColumnDescriptor<DockingActionIf> createTableColumnDescriptor() {
|
||||||
return "Keybindings";
|
TableColumnDescriptor<DockingActionIf> descriptor = new TableColumnDescriptor<>();
|
||||||
}
|
descriptor.addVisibleColumn("Action Name", String.class, a -> a.getName(), 1, true);
|
||||||
|
descriptor.addVisibleColumn("Key Binding", String.class, a -> {
|
||||||
@Override
|
|
||||||
public Object getColumnValueForRow(DockingActionIf action, int columnIndex) {
|
|
||||||
|
|
||||||
String fullName = action.getFullName();
|
|
||||||
switch (columnIndex) {
|
|
||||||
case ACTION_NAME:
|
|
||||||
return action.getName();
|
|
||||||
case KEY_BINDING:
|
|
||||||
String text = "";
|
String text = "";
|
||||||
|
String fullName = a.getFullName();
|
||||||
KeyStroke ks = keyBindings.getKeyStroke(fullName);
|
KeyStroke ks = keyBindings.getKeyStroke(fullName);
|
||||||
if (ks != null) {
|
if (ks != null) {
|
||||||
text += KeyBindingUtils.parseKeyStroke(ks);
|
text += KeyBindingUtils.parseKeyStroke(ks);
|
||||||
|
@ -608,10 +638,15 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
return text.trim();
|
return text.trim();
|
||||||
case PLUGIN_NAME:
|
});
|
||||||
return action.getOwnerDescription();
|
descriptor.addVisibleColumn("Owner", String.class, a -> a.getOwnerDescription());
|
||||||
|
descriptor.addHiddenColumn("Description", String.class, a -> a.getDescription());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
return "Unknown Column!";
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Key Bindings";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -620,28 +655,8 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSortable(int columnIndex) {
|
public Object getDataSource() {
|
||||||
return true;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getColumnName(int column) {
|
|
||||||
return columnNames[column];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getColumnCount() {
|
|
||||||
return columnNames.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRowCount() {
|
|
||||||
return actions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getColumnClass(int columnIndex) {
|
|
||||||
return String.class;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue