diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorDialog.java index a8d85b1a9e..79e622a132 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorDialog.java @@ -27,6 +27,8 @@ import javax.swing.border.CompoundBorder; import javax.swing.event.*; import javax.swing.table.TableCellEditor; +import org.apache.commons.lang3.StringUtils; + import docking.*; import docking.widgets.OptionDialog; import docking.widgets.checkbox.GCheckBox; @@ -248,7 +250,22 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod signatureTextField.setFont(font.deriveFont(18.0f)); panel.add(signatureTextField); - signatureTextField.setEscapeListener(e -> model.resetSignatureTextField()); + signatureTextField.setEscapeListener(e -> { + + if (!model.hasChanges()) { + // no changes; user wish to close the dialog + cancelCallback(); + return; + } + + // editor has changes, see if they wish to cancel editing + if (!promptToAbortChanges()) { + return; // keep editing + } + + // abort changes confirmed + model.resetSignatureTextField(); + }); signatureTextField.setActionListener(e -> { try { @@ -297,18 +314,31 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod message = details; } - message = HTMLUtilities.wrapAsHTML( - message + "

Do you want to continue editing or " + - "abort your changes?
"); - int result = OptionDialog.showOptionNoCancelDialog(rootPanel, "Invalid Function Signature", - message, "Continue Editing", "Abort Changes", OptionDialog.ERROR_MESSAGE); - if (result == OptionDialog.OPTION_TWO) { + if (doPromptToAbortChanges(message)) { model.resetSignatureTextField(); return true; } return false; } + private boolean promptToAbortChanges() { + return doPromptToAbortChanges(""); + } + + private boolean doPromptToAbortChanges(String message) { + + if (!StringUtils.isBlank(message)) { + message += "

"; + } + + message = HTMLUtilities.wrapAsHTML( + message + "
Do you want to continue editing or " + + "abort your changes?
"); + int result = OptionDialog.showOptionNoCancelDialog(rootPanel, "Invalid Function Signature", + message, "Continue Editing", "Abort Changes", OptionDialog.ERROR_MESSAGE); + return result == OptionDialog.OPTION_TWO; // Option 2 is to abort + } + private Component buildAttributePanel() { JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 15, 15)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java index dc46d07bd9..4bb56cd1cd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java @@ -17,8 +17,6 @@ package ghidra.app.plugin.core.function.editor; import java.util.*; -import javax.swing.SwingUtilities; - import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.cparser.C.ParseException; import ghidra.app.util.parser.FunctionSignatureParser; @@ -30,8 +28,7 @@ import ghidra.program.model.listing.Function.FunctionUpdateType; import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SymbolUtilities; -import ghidra.util.Msg; -import ghidra.util.SystemUtilities; +import ghidra.util.*; import ghidra.util.exception.*; public class FunctionEditorModel { @@ -85,14 +82,6 @@ public class FunctionEditorModel { this.listener = listener; } -// public FunctionEditorModel(DataTypeManagerService service, Function function, -// ModelChangeListener listener, FunctionDefinitionDataType functionDef, -// String callingConv) { -// this(service, function, listener); -// callingConventionName = callingConv; -// setFunctionData(functionDef); -// } - // Returns the current calling convention or the default calling convention if current unknown private PrototypeModel getEffectiveCallingConvention() { PrototypeModel effectiveCallingConvention = @@ -193,19 +182,10 @@ public class FunctionEditorModel { this.modelChanged |= functionDataChanged; validate(); if (listener != null) { - SwingUtilities.invokeLater(() -> listener.dataChanged()); + Swing.runLater(() -> listener.dataChanged()); } } -// private void notifyParsingModeChanged() { -// SwingUtilities.invokeLater(new Runnable() { -// @Override -// public void run() { -// listener.parsingModeChanged(); -// } -// }); -// } - private void validate() { statusText = ""; if (signatureTransformed) { @@ -237,7 +217,7 @@ public class FunctionEditorModel { returnType = ((TypeDef) returnType).getBaseDataType(); } if (storageSize > 0 && (returnType instanceof AbstractFloatDataType)) { - return true; // dont constrain float storage size + return true; // don't constrain float storage size } int returnDataTypeSize = returnType.getLength(); @@ -406,7 +386,7 @@ public class FunctionEditorModel { } public String getFunctionSignatureTextFromModel() { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); buf.append(returnInfo.getFormalDataType().getName()).append(" "); buf.append(getNameString()); buf.append(" ("); @@ -495,7 +475,6 @@ public class FunctionEditorModel { * Get the effective function to which changes will be made. This * will be the same as function unless it is a thunk in which case * the returned function will be the ultimate non-thunk function. - * @param function * @return non-thunk function */ private Function getAffectiveFunction() { @@ -663,7 +642,8 @@ public class FunctionEditorModel { try { if (autoParamCount < oldAutoCount) { if (oldParams.get( - autoParamCount).getStorage().getAutoParameterType() != storage.getAutoParameterType()) { + autoParamCount).getStorage().getAutoParameterType() != storage + .getAutoParameterType()) { adjustSelectionForRowRemoved(i); } } @@ -1180,6 +1160,10 @@ public class FunctionEditorModel { setSignatureFieldText(getFunctionSignatureTextFromModel()); } + public boolean hasChanges() { + return !Objects.equals(getFunctionSignatureTextFromModel(), signatureFieldText); + } + public void parseSignatureFieldText() throws ParseException, CancelledException { FunctionSignatureParser parser = new FunctionSignatureParser(program.getDataTypeManager(), dataTypeManagerService); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bean/SetEquateDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bean/SetEquateDialog.java index b42a1e404d..02b1a0e326 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bean/SetEquateDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bean/SetEquateDialog.java @@ -16,8 +16,7 @@ package ghidra.app.util.bean; import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.util.*; import java.util.List; import java.util.stream.Collectors; @@ -28,6 +27,7 @@ import javax.swing.table.TableColumnModel; import org.apache.commons.lang3.StringUtils; import docking.DialogComponentProvider; +import docking.actions.KeyBindingUtils; import docking.widgets.button.GRadioButton; import docking.widgets.checkbox.GCheckBox; import docking.widgets.filter.FilterListener; @@ -220,7 +220,7 @@ public class SetEquateDialog extends DialogComponentProvider { return entries; } - /** + /* * Builds the main panel of the dialog and returns it. */ protected JPanel buildMainPanel() { @@ -274,10 +274,18 @@ public class SetEquateDialog extends DialogComponentProvider { suggestedEquatesTable = new GhidraTable(model); suggestedEquatesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - JPanel tablePanel = new JPanel(new BorderLayout()); - JScrollPane scrollPane = new JScrollPane(suggestedEquatesTable); - tablePanel.add(scrollPane); + // allows users to press enter in the table to accept the current selection + KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); + AbstractAction action = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + okCallback(); + } + }; + KeyBindingUtils.registerAction(suggestedEquatesTable, enter, action, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + // allows users to double-click a row to accept the item suggestedEquatesTable.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { @@ -289,6 +297,10 @@ public class SetEquateDialog extends DialogComponentProvider { } } }); + + JPanel tablePanel = new JPanel(new BorderLayout()); + JScrollPane scrollPane = new JScrollPane(suggestedEquatesTable); + tablePanel.add(scrollPane); tablePanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 5, 5)); filterPanel = @@ -378,7 +390,7 @@ public class SetEquateDialog extends DialogComponentProvider { return result; } - /** + /* * Get the Equate Name entered or chosen by the user. */ public String getEquateName() { @@ -441,7 +453,7 @@ public class SetEquateDialog extends DialogComponentProvider { /** * Returns the type of selection the user has chosen. * - * @return + * @return the selection type */ public SelectionType getSelectionType() { if (applyToAll.isSelected()) { @@ -458,7 +470,7 @@ public class SetEquateDialog extends DialogComponentProvider { /** * Returns true if the user has chosen to overwrite any existing equate rules. * - * @return + * @return true if the user has chosen to overwrite any existing equate rules. */ public boolean getOverwriteExisting() { return overwriteExistingEquates.isSelected(); @@ -492,6 +504,7 @@ public class SetEquateDialog extends DialogComponentProvider { /** * Sets the dialogs status display to the given message. + * @param text the text */ void setStatus(String text) { this.setStatusText(text);