diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java index 9b2f264aa3..ca1fdaa10c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java @@ -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 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 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 = "The align control allows the overall minimum alignment of this
" + "data type to be specified. The actual computed alignment
" + @@ -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."; 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?", + "Applying packing may drastically change this structure.

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 { "(<F1> for help)"; 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 = "Indicates an explicit pack size should be applied."; - 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(); + } + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java index 1c18c828c5..6ce302a2c1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java @@ -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 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. + *

+ * 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. + *

+ * @see #getFocusComponents() + */ + private class CompFocusTraversalPolicy extends FocusTraversalPolicy { + + @Override + public Component getComponentAfter(Container aContainer, Component aComponent) { + + List list = getFocusComponents(); + return getNext(aComponent, list); + } + + private Component getNext(Component component, List 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 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 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 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 list = getFocusComponents(); + return getPrevious(aComponent, list); + } + + private Component getPrevious(Component aComponent, List 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 list = getFocusComponents(); + return list.get(0); + } + + @Override + public Component getLastComponent(Container aContainer) { + List list = getFocusComponents(); + return list.get(list.size() - 1); + } + + @Override + public Component getDefaultComponent(Container aContainer) { + List list = getFocusComponents(); + return list.get(0); + } + + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java index bf86d2146b..3b26fb473b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java @@ -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)); + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/SearchControlPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/SearchControlPanel.java index eadb8d9e84..e4dd8d4986 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/SearchControlPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/SearchControlPanel.java @@ -107,4 +107,7 @@ public class SearchControlPanel extends JPanel { } } + public JTextField getTextField() { + return textField; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorPanel.java index 32c5925f7f..ba01375759 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorPanel.java @@ -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 + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorPanel.java index 14eaf38463..7da4360a69 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorPanel.java @@ -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 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 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) diff --git a/Ghidra/Framework/Help/src/main/java/help/Help.java b/Ghidra/Framework/Help/src/main/java/help/Help.java index 93b3adbdd7..be00c9795c 100644 --- a/Ghidra/Framework/Help/src/main/java/help/Help.java +++ b/Ghidra/Framework/Help/src/main/java/help/Help.java @@ -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; }