From b7dec5e36328ecfacfe635139ac1d8f3c530ec1d Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:11:40 -0500 Subject: [PATCH] GP-5097 - Updated the Importer to allow users to double-click a language to pick it --- .../core/processors/SetLanguageDialog.java | 72 ++++++++++------- .../importer/ImporterLanguageDialog.java | 35 +++------ .../plugin/importer/LcsSelectionEvent.java | 39 +++++++--- .../plugin/importer/NewLanguagePanel.java | 77 ++++++++++++------- 4 files changed, 133 insertions(+), 90 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/SetLanguageDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/SetLanguageDialog.java index b7e1bd355c..4fa3ab1567 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/SetLanguageDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/SetLanguageDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,8 +19,8 @@ import javax.swing.BorderFactory; import docking.DialogComponentProvider; import ghidra.framework.plugintool.PluginTool; -import ghidra.plugin.importer.LcsSelectionListener; -import ghidra.plugin.importer.NewLanguagePanel; +import ghidra.plugin.importer.*; +import ghidra.plugin.importer.LcsSelectionEvent.Type; import ghidra.program.model.lang.*; import ghidra.program.util.DefaultLanguageService; import ghidra.util.HelpLocation; @@ -30,33 +30,14 @@ public class SetLanguageDialog extends DialogComponentProvider { private NewLanguagePanel selectLangPanel; private PluginTool tool; - private LanguageCompilerSpecPair currentLCSPair; + private LanguageCompilerSpecPair currentLcsPair; private LanguageID dialogLanguageID; private CompilerSpecID dialogCompilerSpecID; - LcsSelectionListener listener = e -> { - LanguageID langID = null; - CompilerSpecID compilerSpecID = null; - if (e != null && e.selection != null) { - langID = e.selection.languageID; - compilerSpecID = e.selection.compilerSpecID; - } - if ((currentLCSPair != null) && (langID != null) && - (langID.equals(currentLCSPair.getLanguageID()))) { - if (compilerSpecID != null && - compilerSpecID.equals(currentLCSPair.getCompilerSpecID())) { - setStatusText("Please select a different Language or Compiler Spec."); - setOkEnabled(false); - } - else { - setStatusText(null); - setOkEnabled(true); - } - return; - } - setStatusText(null); - setOkEnabled(langID != null); + private LcsSelectionListener listener = e -> { + languageSelected(e.getLcs()); + maybePressOk(e); }; /** @@ -89,7 +70,7 @@ public class SetLanguageDialog extends DialogComponentProvider { */ public SetLanguageDialog(PluginTool tool, LanguageCompilerSpecPair lcsPair, String title) { super(title, true, true, true, false); - currentLCSPair = lcsPair; + currentLcsPair = lcsPair; this.tool = tool; selectLangPanel = new NewLanguagePanel(); @@ -110,7 +91,40 @@ public class SetLanguageDialog extends DialogComponentProvider { setHelpLocation(new HelpLocation("LanguageProviderPlugin", "set language")); selectLangPanel.setShowRecommendedCheckbox(false); - listener.valueChanged(null); // kick to establish initial button enablement + languageSelected(null); // kick to establish initial button enablement + } + + private void languageSelected(LanguageCompilerSpecPair lcs) { + LanguageID langId = null; + CompilerSpecID compilerSpecID = null; + if (lcs != null) { + langId = lcs.languageID; + compilerSpecID = lcs.compilerSpecID; + } + + if ((currentLcsPair != null) && (langId != null) && + (langId.equals(currentLcsPair.getLanguageID()))) { + if (compilerSpecID != null && + compilerSpecID.equals(currentLcsPair.getCompilerSpecID())) { + setStatusText("Please select a different Language or Compiler Spec."); + setOkEnabled(false); + } + else { + setStatusText(null); + setOkEnabled(true); + } + return; + } + + setStatusText(null); + setOkEnabled(langId != null); + } + + private void maybePressOk(LcsSelectionEvent e) { + if (e.getType() == Type.PICKED && isOKEnabled()) { + // the user picked (i.e., double-clicked) a language and it is valid, so use it + okCallback(); + } } private static LanguageCompilerSpecPair getLanguageCompilerSpecPair(String languageIdStr, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/ImporterLanguageDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/ImporterLanguageDialog.java index 0dadbd87ec..c0a56aeb8a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/ImporterLanguageDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/ImporterLanguageDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,14 +19,14 @@ import java.awt.Component; import java.util.*; import javax.swing.BorderFactory; -import javax.swing.SwingUtilities; import docking.DialogComponentProvider; import ghidra.app.util.opinion.LoadSpec; import ghidra.framework.plugintool.PluginTool; +import ghidra.plugin.importer.LcsSelectionEvent.Type; import ghidra.program.model.lang.LanguageCompilerSpecPair; import ghidra.util.HelpLocation; -import ghidra.util.Msg; +import ghidra.util.Swing; public class ImporterLanguageDialog extends DialogComponentProvider { @@ -46,24 +46,10 @@ public class ImporterLanguageDialog extends DialogComponentProvider { } public void show(Component parent) { - if (SwingUtilities.isEventDispatchThread()) { + Swing.runIfSwingOrRunLater(() -> { build(); tool.showDialog(this, parent); - } - else { - try { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - build(); - tool.showDialog(ImporterLanguageDialog.this, parent); - } - }); - } - catch (Exception e) { - Msg.error(this, e); - } - } + }); } private void build() { @@ -72,10 +58,11 @@ public class ImporterLanguageDialog extends DialogComponentProvider { languagePanel.setShowAllLcsPairs(false); languagePanel.setBorder( BorderFactory.createTitledBorder(" Select Language and Compiler Specification ")); - languagePanel.addSelectionListener(new LcsSelectionListener() { - @Override - public void valueChanged(LcsSelectionEvent e) { - validateFormInput(); + languagePanel.addSelectionListener(e -> { + validateFormInput(); + + if (e.getType() == Type.PICKED && isOKEnabled()) { + okCallback(); } }); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/LcsSelectionEvent.java b/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/LcsSelectionEvent.java index 5580606ea6..313ff66116 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/LcsSelectionEvent.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/LcsSelectionEvent.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,14 +18,33 @@ package ghidra.plugin.importer; import ghidra.program.model.lang.LanguageCompilerSpecPair; public class LcsSelectionEvent { - public final LanguageCompilerSpecPair selection; - public LcsSelectionEvent(LanguageCompilerSpecPair selection) { - this.selection = selection; - } + public enum Type { + /** A language was selected in the UI */ + SELECTED, - @Override - public String toString() { - return "LSE{" + selection + "}"; - } + /** A language was picked (e.g., double-clicked) in the UI */ + PICKED + } + + private final LanguageCompilerSpecPair lcs; + private final Type type; + + public LcsSelectionEvent(LanguageCompilerSpecPair selection, Type type) { + this.lcs = selection; + this.type = type; + } + + public LanguageCompilerSpecPair getLcs() { + return lcs; + } + + public Type getType() { + return type; + } + + @Override + public String toString() { + return "LSE{" + lcs + "}"; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/NewLanguagePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/NewLanguagePanel.java index 0a9f2d2b2b..a1c057af47 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/NewLanguagePanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/NewLanguagePanel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,10 +23,12 @@ import java.util.List; import javax.swing.*; import javax.swing.border.Border; +import docking.actions.KeyBindingUtils; import docking.widgets.checkbox.GCheckBox; import docking.widgets.label.GDLabel; import generic.theme.GThemeDefaults.Colors.Messages; import generic.theme.Gui; +import ghidra.plugin.importer.LcsSelectionEvent.Type; import ghidra.program.model.lang.*; import ghidra.program.util.DefaultLanguageService; import ghidra.util.table.*; @@ -41,12 +43,13 @@ public class NewLanguagePanel extends JPanel { private JCheckBox recommendedCheckbox; private JLabel formatLabel; - private void setDescriptionLabelText(String text) { - if (text == null || "".equals(text)) { - text = " "; - } - descriptionLabel.setText(text); - } + private boolean isOnShowAll = true; + + private List allLcsPairsList; + private List recommendedLcsPairsList; + private LanguageCompilerSpecPair recommendedLcsPair; + + private final Set listeners = new HashSet<>(); public NewLanguagePanel() { constructEverything(); @@ -94,7 +97,6 @@ public class NewLanguagePanel extends JPanel { @Override public Dimension getPreferredSize() { // this makes us a bit smaller in height, as the preferred height can be excessive - Dimension preferredSize = super.getPreferredSize(); if (preferredSize.width == 0) { return preferredSize; // no size yet, don't change anything @@ -138,16 +140,37 @@ public class NewLanguagePanel extends JPanel { if (e.getValueIsAdjusting()) { return; } - notifyListeners(); + notifyLanguageSelected(); }); + table.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { if (e.getClickCount() == 2) { - // do the next action thingie + notfiyLanguagePicked(); } } }); + + // Update Enter to allow the user to pick the selected language + KeyStroke enterKs = KeyBindingUtils.parseKeyStroke("Enter"); + Action action = new AbstractAction("Pick Language") { + @Override + public void actionPerformed(ActionEvent e) { + notfiyLanguagePicked(); + } + }; + + // remove the table's enter key binding and then add our own + KeyBindingUtils.clearKeyBinding(table, enterKs); + KeyBindingUtils.registerAction(table, enterKs, action, JComponent.WHEN_FOCUSED); + } + + private void setDescriptionLabelText(String text) { + if (text == null || "".equals(text)) { + text = " "; + } + descriptionLabel.setText(text); } public void setFormatText(String text) { @@ -158,8 +181,6 @@ public class NewLanguagePanel extends JPanel { recommendedCheckbox.setVisible(show); } - private boolean isOnShowAll = true; - private boolean isAllLcsPairsTableShowing() { return isOnShowAll; } @@ -174,7 +195,7 @@ public class NewLanguagePanel extends JPanel { private void setLanguages(List lcsPairList) { tableModel.setLanguages(lcsPairList); - notifyListeners(); + notifyLanguageSelected(); } private void switchToAllList() { @@ -203,13 +224,20 @@ public class NewLanguagePanel extends JPanel { } } - private List allLcsPairsList; - private List recommendedLcsPairsList; + private void notifyLanguageSelected() { + LanguageCompilerSpecPair lcs = getSelectedLcsPair(); + LcsSelectionEvent e = new LcsSelectionEvent(lcs, Type.SELECTED); + doNotify(e); + } - private LanguageCompilerSpecPair recommendedLcsPair; + private void notfiyLanguagePicked() { + LanguageCompilerSpecPair lcs = getSelectedLcsPair(); + LcsSelectionEvent e = new LcsSelectionEvent(lcs, Type.PICKED); + doNotify(e); + } - private void notifyListeners() { - LanguageCompilerSpecPair selectedLcsPair = getSelectedLcsPair(); + private void doNotify(LcsSelectionEvent event) { + LanguageCompilerSpecPair selectedLcsPair = event.getLcs(); if (selectedLcsPair == null) { descriptionLabel.setText(DEFAULT_DESCRIPTION_TEXT); } @@ -221,12 +249,9 @@ public class NewLanguagePanel extends JPanel { descriptionLabel.setText(""); } } -// notifyListenersOfValidityChanged(); - if (!listeners.isEmpty()) { - LcsSelectionEvent e = new LcsSelectionEvent(selectedLcsPair); - for (LcsSelectionListener listener : listeners) { - listener.valueChanged(e); - } + + for (LcsSelectionListener listener : listeners) { + listener.valueChanged(event); } } @@ -314,8 +339,6 @@ public class NewLanguagePanel extends JPanel { return true; } - private final Set listeners = new HashSet<>(); - public void addSelectionListener(LcsSelectionListener listener) { listeners.add(listener); }