mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 05:32:14 +00:00
GP-4716 - Data Type Editors - Fixed the traversal order of the structure
editor
This commit is contained in:
parent
c95c7581d7
commit
6aadccc40a
@ -19,6 +19,7 @@ import java.awt.*;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.dnd.DropTargetDragEvent;
|
||||
import java.awt.event.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.TitledBorder;
|
||||
@ -35,8 +36,8 @@ import generic.theme.GThemeDefaults.Colors.Viewport;
|
||||
import ghidra.app.plugin.core.compositeeditor.BitFieldPlacementComponent.BitAttributes;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
@ -77,12 +78,12 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
private JLabel actualAlignmentLabel;
|
||||
private JTextField actualAlignmentValueTextField;
|
||||
|
||||
private List<Component> focusList;
|
||||
|
||||
private BitFieldPlacementComponent bitViewComponent;
|
||||
|
||||
private DocumentListener fieldDocListener;
|
||||
|
||||
private ActionListener fieldActionListener;
|
||||
|
||||
private FocusListener fieldFocusListener;
|
||||
|
||||
private boolean updatingSize;
|
||||
@ -234,6 +235,30 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
return bitViewPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFocusComponents() {
|
||||
if (focusList == null) {
|
||||
//@formatter:off
|
||||
focusList = List.of(
|
||||
|
||||
table,
|
||||
searchPanel.getTextField(),
|
||||
nameTextField,
|
||||
descriptionTextField,
|
||||
sizeTextField,
|
||||
|
||||
// add the first radio button; the rest are reachable via arrow keys
|
||||
defaultAlignButton,
|
||||
packingEnablementButton,
|
||||
|
||||
// add the first radio button; the rest are reachable via arrow keys
|
||||
defaultPackingButton
|
||||
);
|
||||
//@formatter:on
|
||||
}
|
||||
return focusList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Info Panel that is horizontally resizable. The panel contains
|
||||
* the name, category, data type, size, and edit mode for the current
|
||||
@ -288,10 +313,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
gridBagConstraints.gridwidth = 4;
|
||||
infoPanel.add(nameTextField, gridBagConstraints);
|
||||
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(nameTextField,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Name"));
|
||||
}
|
||||
provider.registerHelp(nameTextField, "Name");
|
||||
}
|
||||
|
||||
private void setupDescription() {
|
||||
@ -318,10 +340,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
gridBagConstraints.gridwidth = 4;
|
||||
infoPanel.add(descriptionTextField, gridBagConstraints);
|
||||
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(descriptionTextField, new HelpLocation(provider.getHelpTopic(),
|
||||
provider.getHelpName() + "_" + "Description"));
|
||||
}
|
||||
provider.registerHelp(descriptionTextField, "Description");
|
||||
}
|
||||
|
||||
private void setupCategory() {
|
||||
@ -387,12 +406,10 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
|
||||
alignPanel = new JPanel(new GridBagLayout());
|
||||
TitledBorder border = BorderFactory.createTitledBorder("align (minimum)");
|
||||
// border.setTitlePosition(TitledBorder.ABOVE_TOP);
|
||||
|
||||
alignPanel.setBorder(border);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(alignPanel,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Align"));
|
||||
}
|
||||
provider.registerHelp(alignPanel, "Align");
|
||||
|
||||
String alignmentToolTip =
|
||||
"<html>The <B>align</B> control allows the overall minimum alignment of this<BR>" +
|
||||
"data type to be specified. The actual computed alignment<BR>" +
|
||||
@ -459,10 +476,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
});
|
||||
|
||||
defaultAlignButton.setToolTipText(alignmentToolTip);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(defaultAlignButton,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Align"));
|
||||
}
|
||||
provider.registerHelp(defaultAlignButton, "Align");
|
||||
}
|
||||
|
||||
private void setupMachineMinAlignButton() {
|
||||
@ -478,10 +492,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
((CompEditorModel) model).setAlignmentType(AlignmentType.MACHINE, -1);
|
||||
});
|
||||
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(machineAlignButton,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Align"));
|
||||
}
|
||||
provider.registerHelp(machineAlignButton, "Align");
|
||||
}
|
||||
|
||||
private void setupExplicitAlignButton() {
|
||||
@ -492,18 +503,22 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
"this composite may be any multiple of this value.</html>";
|
||||
explicitAlignButton.setToolTipText(alignmentToolTip);
|
||||
|
||||
explicitAlignButton.addActionListener(e -> {
|
||||
chooseExplicitAlign();
|
||||
// As a convenience, when this radio button is focused, change focus to the editor field
|
||||
explicitAlignButton.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
explicitAlignTextField.requestFocus();
|
||||
}
|
||||
});
|
||||
explicitAlignButton.addActionListener(e -> chooseExplicitAlign());
|
||||
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(explicitAlignButton,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Align"));
|
||||
}
|
||||
provider.registerHelp(explicitAlignButton, "Align");
|
||||
|
||||
explicitAlignTextField.setName("Explicit Alignment Value");
|
||||
explicitAlignTextField.setEditable(true);
|
||||
explicitAlignTextField.addActionListener(e -> adjustExplicitMinimumAlignmentValue());
|
||||
explicitAlignTextField
|
||||
.addKeyListener(new UpAndDownKeyListener(defaultAlignButton, machineAlignButton));
|
||||
|
||||
explicitAlignTextField.addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
@ -522,10 +537,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
});
|
||||
|
||||
explicitAlignTextField.setToolTipText(alignmentToolTip);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(explicitAlignTextField,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Align"));
|
||||
}
|
||||
provider.registerHelp(explicitAlignTextField, "Align");
|
||||
|
||||
refreshGUIMinimumAlignmentValue(); // Display the initial value.
|
||||
}
|
||||
@ -572,16 +584,16 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
infoPanel.add(actualAlignmentPanel, gridBagConstraints);
|
||||
|
||||
actualAlignmentValueTextField = new JTextField(8);
|
||||
actualAlignmentValueTextField
|
||||
.setText(Integer.toString(((CompEditorModel) model).getActualAlignment()));
|
||||
int actualAlignment = ((CompEditorModel) model).getActualAlignment();
|
||||
actualAlignmentValueTextField.setText(Integer.toString(actualAlignment));
|
||||
actualAlignmentValueTextField.setToolTipText(actualAlignmentToolTip);
|
||||
actualAlignmentValueTextField.setEditable(false);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(actualAlignmentValueTextField, new HelpLocation(
|
||||
provider.getHelpTopic(), provider.getHelpName() + "_" + "ActualAlignment"));
|
||||
}
|
||||
actualAlignmentValueTextField.setEnabled(false);
|
||||
actualAlignmentValueTextField.setBackground(getBackground());
|
||||
actualAlignmentValueTextField.setName("Actual Alignment Value");
|
||||
|
||||
provider.registerHelp(actualAlignmentValueTextField, "ActualAlignment");
|
||||
|
||||
gridBagConstraints.insets = VERTICAL_INSETS;
|
||||
gridBagConstraints.anchor = GridBagConstraints.LINE_START;
|
||||
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
@ -589,7 +601,6 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
gridBagConstraints.gridx = 3;
|
||||
gridBagConstraints.gridy = 3;
|
||||
infoPanel.add(actualAlignmentValueTextField, gridBagConstraints);
|
||||
actualAlignmentValueTextField.setBackground(getBackground());
|
||||
}
|
||||
|
||||
private void setupPacking() {
|
||||
@ -618,10 +629,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
packingGroup.add(defaultPackingButton);
|
||||
packingGroup.add(explicitPackingButton);
|
||||
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(packingPanel,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Pack"));
|
||||
}
|
||||
provider.registerHelp(packingPanel, "Pack");
|
||||
|
||||
addPackingComponents(innerPanel);
|
||||
|
||||
@ -669,6 +677,12 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
// gridPanel.add(disabledPackingButton, gridBagConstraints);
|
||||
}
|
||||
|
||||
protected boolean choosePacking() {
|
||||
int choice = OptionDialog.showYesNoDialog(this, "Use Packing?",
|
||||
"<html>Applying packing may drastically change this structure.<BR><BR>Use Packing?");
|
||||
return choice == OptionDialog.YES_OPTION;
|
||||
}
|
||||
|
||||
private void setupPackingEnablementButton() {
|
||||
packingEnablementButton.setName("Packing Enablement");
|
||||
String packingToolTipText =
|
||||
@ -677,16 +691,24 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
"<font color=\"" + Palette.BLUE.toHexString() +
|
||||
"\" size=\"-2\">(<F1> for help)</font></html>";
|
||||
packingEnablementButton.addActionListener(e -> {
|
||||
|
||||
// When turning this on, warn the use. This prevents accidental enablement
|
||||
// destructively changing the structure.
|
||||
if (packingEnablementButton.isSelected()) {
|
||||
if (!choosePacking()) {
|
||||
Swing.runLater(() -> packingEnablementButton.setSelected(false));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
((CompEditorModel) model).setPackingType(
|
||||
packingEnablementButton.isSelected() ? PackingType.DEFAULT : PackingType.DISABLED,
|
||||
-1);
|
||||
});
|
||||
|
||||
packingEnablementButton.setToolTipText(packingToolTipText);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(packingEnablementButton,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Pack"));
|
||||
}
|
||||
|
||||
provider.registerHelp(packingEnablementButton, "Pack");
|
||||
}
|
||||
|
||||
private void setupDefaultPackingButton() {
|
||||
@ -699,10 +721,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
});
|
||||
|
||||
defaultPackingButton.setToolTipText(packingToolTipText);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(defaultPackingButton,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Pack"));
|
||||
}
|
||||
provider.registerHelp(defaultPackingButton, "Pack");
|
||||
}
|
||||
|
||||
private void setupExplicitPackingButton() {
|
||||
@ -710,16 +729,23 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
String packingToolTipText =
|
||||
"<html>Indicates an explicit pack size should be applied.</html>";
|
||||
|
||||
explicitPackingButton.addActionListener(e -> chooseByValuePacking());
|
||||
explicitPackingButton.setToolTipText(packingToolTipText);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(explicitPackingButton,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Pack"));
|
||||
}
|
||||
|
||||
// As a convenience, when this radio button is focused, change focus to the editor field
|
||||
explicitPackingButton.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
explicitPackingTextField.requestFocus();
|
||||
}
|
||||
});
|
||||
explicitPackingButton.addActionListener(e -> chooseByValuePacking());
|
||||
provider.registerHelp(explicitPackingButton, "Pack");
|
||||
|
||||
explicitPackingTextField.setName("Packing Value");
|
||||
explicitPackingTextField.setEditable(true);
|
||||
explicitPackingTextField.addActionListener(e -> adjustPackingValue());
|
||||
explicitPackingTextField.addKeyListener(
|
||||
new UpAndDownKeyListener(defaultPackingButton, defaultPackingButton));
|
||||
|
||||
explicitPackingTextField.addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
@ -738,10 +764,8 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
});
|
||||
|
||||
explicitPackingTextField.setToolTipText(packingToolTipText);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(explicitPackingTextField,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Pack"));
|
||||
}
|
||||
|
||||
provider.registerHelp(explicitPackingTextField, "Pack");
|
||||
}
|
||||
|
||||
private void chooseByValuePacking() {
|
||||
@ -830,8 +854,9 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
|
||||
protected void setSizeEditable(boolean editable) {
|
||||
sizeTextField.setEditable(editable);
|
||||
sizeTextField.setEnabled(editable);
|
||||
if (editable) {
|
||||
// editable - use same background as category field
|
||||
// editable - use same background as description field
|
||||
sizeTextField.setBackground(descriptionTextField.getBackground());
|
||||
}
|
||||
else {
|
||||
@ -1216,8 +1241,46 @@ public class CompEditorPanel extends CompositeEditorPanel {
|
||||
|
||||
@Override
|
||||
public void showUndefinedStateChanged(boolean showUndefinedBytes) {
|
||||
// TODO Auto-generated method stub
|
||||
// stub
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple class that allows clients to focus other components when the up or down arrows keys
|
||||
* are pressed
|
||||
*/
|
||||
private class UpAndDownKeyListener extends KeyAdapter {
|
||||
|
||||
private JRadioButton previousComponent;
|
||||
private JRadioButton nextComponent;
|
||||
|
||||
UpAndDownKeyListener(JRadioButton previousComponent, JRadioButton nextComponent) {
|
||||
this.previousComponent = previousComponent;
|
||||
this.nextComponent = nextComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
|
||||
if (e.isConsumed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int code = e.getKeyCode();
|
||||
if (code == KeyEvent.VK_UP) {
|
||||
// We need to run later due to focusLost() listener on the text field that will
|
||||
// interfere with the selected state of our newly selected button
|
||||
previousComponent.requestFocusInWindow();
|
||||
Swing.runLater(() -> previousComponent.setSelected(true));
|
||||
e.consume();
|
||||
}
|
||||
else if (code == KeyEvent.VK_DOWN) {
|
||||
// We need to run later due to focusLost() listener on the text field that will
|
||||
// interfere with the selected state of our newly selected button
|
||||
nextComponent.requestFocusInWindow();
|
||||
Swing.runLater(() -> nextComponent.setSelected(true));
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -71,8 +71,6 @@ public abstract class CompositeEditorPanel extends JPanel
|
||||
|
||||
protected static final Border BEVELED_BORDER = BorderFactory.createLoweredBevelBorder();
|
||||
|
||||
protected static final HelpService helpManager = Help.getHelpService();
|
||||
|
||||
protected CompositeEditorProvider provider;
|
||||
protected CompositeEditorModel model;
|
||||
protected GTable table;
|
||||
@ -91,27 +89,47 @@ public abstract class CompositeEditorPanel extends JPanel
|
||||
private DataFlavor[] acceptableFlavors; // data flavors that are valid.
|
||||
protected int lastDndAction = DnDConstants.ACTION_NONE;
|
||||
|
||||
protected SearchControlPanel searchPanel;
|
||||
|
||||
public CompositeEditorPanel(CompositeEditorModel model, CompositeEditorProvider provider) {
|
||||
super(new BorderLayout());
|
||||
JPanel lowerPanel = new JPanel(new VerticalLayout(5));
|
||||
this.provider = provider;
|
||||
this.model = model;
|
||||
|
||||
createTable();
|
||||
|
||||
JPanel lowerPanel = new JPanel(new VerticalLayout(5));
|
||||
JPanel bitViewerPanel = createBitViewerPanel();
|
||||
if (bitViewerPanel != null) {
|
||||
lowerPanel.add(bitViewerPanel);
|
||||
}
|
||||
|
||||
JPanel infoPanel = createInfoPanel();
|
||||
if (infoPanel != null) {
|
||||
adjustCompositeInfo();
|
||||
lowerPanel.add(infoPanel);
|
||||
}
|
||||
|
||||
lowerPanel.add(createStatusPanel());
|
||||
add(lowerPanel, BorderLayout.SOUTH);
|
||||
model.addCompositeEditorModelListener(this);
|
||||
setUpDragDrop();
|
||||
|
||||
// These 2 methods allow us to specify the order of component navigation when Tab and
|
||||
// Shift-Tab are pressed
|
||||
setFocusTraversalPolicy(new CompFocusTraversalPolicy());
|
||||
setFocusTraversalPolicyProvider(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of focus traversal components. This list will be used to navigate forward
|
||||
* and backward when the Tab and Shift-Tab keys are pressed. The components will be traversed
|
||||
* in the order they are contained in the list.
|
||||
*
|
||||
* @return the list
|
||||
*/
|
||||
protected abstract List<Component> getFocusComponents();
|
||||
|
||||
protected Composite getOriginalComposite() {
|
||||
return model.getOriginalComposite();
|
||||
}
|
||||
@ -640,12 +658,11 @@ public abstract class CompositeEditorPanel extends JPanel
|
||||
table.setPreferredScrollableViewportSize(new Dimension(model.getWidth(), 250));
|
||||
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
tablePanel.add(sp, BorderLayout.CENTER);
|
||||
SearchControlPanel searchPanel = new SearchControlPanel(this);
|
||||
searchPanel = new SearchControlPanel(this);
|
||||
|
||||
HelpService help = Help.getHelpService();
|
||||
help.registerHelp(searchPanel, new HelpLocation("DataTypeEditors", "Searching_In_Editor"));
|
||||
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(searchPanel,
|
||||
new HelpLocation("DataTypeEditors", "Searching_In_Editor"));
|
||||
}
|
||||
tablePanel.add(searchPanel, BorderLayout.SOUTH);
|
||||
|
||||
add(tablePanel, BorderLayout.CENTER);
|
||||
@ -757,10 +774,8 @@ public abstract class CompositeEditorPanel extends JPanel
|
||||
panel.add(label);
|
||||
panel.add(Box.createHorizontalStrut(2));
|
||||
panel.add(textField);
|
||||
if (helpManager != null) {
|
||||
helpManager.registerHelp(textField,
|
||||
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + name));
|
||||
}
|
||||
|
||||
provider.registerHelp(textField, name);
|
||||
return panel;
|
||||
}
|
||||
|
||||
@ -1456,4 +1471,140 @@ public abstract class CompositeEditorPanel extends JPanel
|
||||
KeyBindingUtils.clearKeyBinding(this, keyStroke);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple traversal policy that allows this editor panel to control the order that components
|
||||
* get focused when pressing Tab and Ctrl-Tab.
|
||||
* <P>
|
||||
* Note: We typically do not use traversal policies in the application. We do so here due to
|
||||
* the complicated nature of this widget. It seemed easier to specify the policy than to
|
||||
* change the order of the widgets in the UI to get the expected traversal order.
|
||||
* <P>
|
||||
* @see #getFocusComponents()
|
||||
*/
|
||||
private class CompFocusTraversalPolicy extends FocusTraversalPolicy {
|
||||
|
||||
@Override
|
||||
public Component getComponentAfter(Container aContainer, Component aComponent) {
|
||||
|
||||
List<Component> list = getFocusComponents();
|
||||
return getNext(aComponent, list);
|
||||
}
|
||||
|
||||
private Component getNext(Component component, List<Component> list) {
|
||||
int currentIndex = list.indexOf(component);
|
||||
if (currentIndex < 0) {
|
||||
// The given component is not in the list of traversal components. This can happen
|
||||
// when some widget in the panel has focus but is not part of the focus traversal.
|
||||
// Assume the component is part of a group of components that can be traversed. Get
|
||||
// the next component after this group.
|
||||
return getNextGroupComponent(component, list);
|
||||
}
|
||||
|
||||
int nextIndex = currentIndex + 1;
|
||||
if (nextIndex == list.size()) {
|
||||
nextIndex = 0; // wrap
|
||||
}
|
||||
|
||||
Component next = list.get(nextIndex);
|
||||
if (!next.isFocusable() || !next.isEnabled()) {
|
||||
return getNext(next, list);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a sibling of the given component and get the component after the sibling. We can do
|
||||
* this since we have guilty knowledge that the few focusable components not in the
|
||||
* traversal list have siblings that are. In that case, all siblings represent the focused
|
||||
* group. This will move to the next component after that group.
|
||||
*
|
||||
* @param component the component used to find the next component
|
||||
* @param list the list of traversal components
|
||||
* @return the next component
|
||||
*/
|
||||
private Component getNextGroupComponent(Component component, List<Component> list) {
|
||||
Component sibling = findSibling(component, list);
|
||||
if (sibling == null) {
|
||||
return list.get(0);
|
||||
}
|
||||
return getNext(sibling, list);
|
||||
}
|
||||
|
||||
// see the description for getNextGroupComponent()
|
||||
private Component getPreviousGroupComponent(Component component, List<Component> list) {
|
||||
Component sibling = findSibling(component, list);
|
||||
if (sibling == null) {
|
||||
return list.get(0);
|
||||
}
|
||||
return getPrevious(sibling, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first sibling of the given component in the given list.
|
||||
*
|
||||
* @param component the component that is not in the list, but has a sibling in the list
|
||||
* @param list the list of focus traversal components
|
||||
* @return the sibling or null
|
||||
*/
|
||||
private Component findSibling(Component component, List<Component> list) {
|
||||
|
||||
Container parent = component.getParent();
|
||||
Component[] siblings = parent.getComponents();
|
||||
for (Component sibling : siblings) {
|
||||
if (list.contains(sibling)) {
|
||||
return sibling;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponentBefore(Container aContainer, Component aComponent) {
|
||||
|
||||
List<Component> list = getFocusComponents();
|
||||
return getPrevious(aComponent, list);
|
||||
}
|
||||
|
||||
private Component getPrevious(Component aComponent, List<Component> list) {
|
||||
int currentIndex = list.indexOf(aComponent);
|
||||
if (currentIndex < 0) {
|
||||
// The given component is not in the list of traversal components. This can happen
|
||||
// when some widget in the panel has focus but is not part of the focus traversal.
|
||||
// Assume the component is part of a group of components that can be traversed. Get
|
||||
// the previous component before this group.
|
||||
return getPreviousGroupComponent(aComponent, list);
|
||||
}
|
||||
|
||||
int previousIndex = currentIndex - 1;
|
||||
if (previousIndex == -1) {
|
||||
previousIndex = list.size() - 1; // wrap
|
||||
}
|
||||
|
||||
Component previous = list.get(previousIndex);
|
||||
if (!previous.isFocusable() || !previous.isEnabled()) {
|
||||
return getPrevious(previous, list);
|
||||
}
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getFirstComponent(Container aContainer) {
|
||||
List<Component> list = getFocusComponents();
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getLastComponent(Container aContainer) {
|
||||
List<Component> list = getFocusComponents();
|
||||
return list.get(list.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDefaultComponent(Container aContainer) {
|
||||
List<Component> list = getFocusComponents();
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import help.Help;
|
||||
import help.HelpService;
|
||||
|
||||
/**
|
||||
* Editor provider for a Composite Data Type.
|
||||
@ -332,4 +334,9 @@ public abstract class CompositeEditorProvider extends ComponentProviderAdapter
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void registerHelp(Object object, String anchor) {
|
||||
HelpService help = Help.getHelpService();
|
||||
help.registerHelp(object, new HelpLocation(getHelpTopic(), getHelpName() + "_" + anchor));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -107,4 +107,7 @@ public class SearchControlPanel extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
public JTextField getTextField() {
|
||||
return textField;
|
||||
}
|
||||
}
|
||||
|
@ -28,4 +28,8 @@ public class UnionEditorPanel extends CompEditorPanel {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean choosePacking() {
|
||||
return true; // packing is not destructive to unions, so safe to use without prompting
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,9 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.stackeditor;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
@ -36,6 +38,7 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
private JTextField paramSizeField;
|
||||
private JTextField paramOffsetField;
|
||||
private JTextField returnAddrOffsetField;
|
||||
private List<Component> focusList;
|
||||
|
||||
public StackEditorPanel(Program program, StackEditorModel model, StackEditorProvider provider) {
|
||||
super(model, provider);
|
||||
@ -61,10 +64,21 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
return Integer.decode(returnAddrOffsetField.getText()).intValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.compositeeditor.CompositeEditorPanel#createInfoPanel()
|
||||
*/
|
||||
@Override
|
||||
protected List<Component> getFocusComponents() {
|
||||
if (focusList == null) {
|
||||
//@formatter:off
|
||||
focusList = List.of(
|
||||
table,
|
||||
searchPanel.getTextField(),
|
||||
localSizeField,
|
||||
paramSizeField
|
||||
);
|
||||
//@formatter:on
|
||||
}
|
||||
return focusList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JPanel createInfoPanel() {
|
||||
|
||||
@ -83,11 +97,10 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
JPanel returnAddrOffsetPanel =
|
||||
createNamedTextPanel(returnAddrOffsetField, "Return Address Offset");
|
||||
|
||||
JPanel[] hPanels =
|
||||
new JPanel[] {
|
||||
createHorizontalPanel(new JPanel[] { frameSizePanel, returnAddrOffsetPanel,
|
||||
localSizePanel }),
|
||||
createHorizontalPanel(new JPanel[] { paramOffsetPanel, paramSizePanel }) };
|
||||
JPanel[] hPanels = new JPanel[] {
|
||||
createHorizontalPanel(
|
||||
new JPanel[] { frameSizePanel, returnAddrOffsetPanel, localSizePanel }),
|
||||
createHorizontalPanel(new JPanel[] { paramOffsetPanel, paramSizePanel }) };
|
||||
JPanel outerPanel = createVerticalPanel(hPanels);
|
||||
outerPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
@ -98,6 +111,7 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
frameSizeField = new JTextField(20);
|
||||
frameSizeField.setName("Frame Size");
|
||||
frameSizeField.setEditable(false);
|
||||
frameSizeField.setEnabled(false);
|
||||
}
|
||||
|
||||
private void setupLocalSize() {
|
||||
@ -195,12 +209,14 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
paramOffsetField = new JTextField(20);
|
||||
paramOffsetField.setName("Parameter Offset");
|
||||
paramOffsetField.setEditable(false);
|
||||
paramOffsetField.setEnabled(false);
|
||||
}
|
||||
|
||||
private void setupReturnAddrOffset() {
|
||||
returnAddrOffsetField = new JTextField(20);
|
||||
returnAddrOffsetField.setName("Return Address Offset");
|
||||
returnAddrOffsetField.setEditable(false);
|
||||
returnAddrOffsetField.setEnabled(false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
@ -16,6 +16,7 @@
|
||||
package help;
|
||||
|
||||
import docking.DefaultHelpService;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Creates the HelpManager for the application. This is just a glorified global variable for
|
||||
@ -28,7 +29,7 @@ public class Help {
|
||||
/**
|
||||
* Get the help service
|
||||
*
|
||||
* @return null if the call to setMainHelpSetURL() failed
|
||||
* @return a non-null help service
|
||||
*/
|
||||
public static HelpService getHelpService() {
|
||||
return helpService;
|
||||
@ -36,6 +37,10 @@ public class Help {
|
||||
|
||||
// allows help services to install themselves
|
||||
public static void installHelpService(HelpService service) {
|
||||
if (service == null) {
|
||||
Msg.debug(Help.class, "Attempted to install null help service");
|
||||
return;
|
||||
}
|
||||
helpService = service;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user