diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest
index 74fa75a598..a9b0a2613b 100644
--- a/Ghidra/Features/Base/certification.manifest
+++ b/Ghidra/Features/Base/certification.manifest
@@ -516,6 +516,7 @@ src/main/help/help/topics/Intro/images/Simple_err_dialog.png||GHIDRA||||END|
src/main/help/help/topics/LabelMgrPlugin/FieldNames.htm||GHIDRA||||END|
src/main/help/help/topics/LabelMgrPlugin/Labels.htm||GHIDRA||||END|
src/main/help/help/topics/LabelMgrPlugin/images/AddLabel.png||GHIDRA||||END|
+src/main/help/help/topics/LabelMgrPlugin/images/ChooseNamespace.png||GHIDRA||||END|
src/main/help/help/topics/LabelMgrPlugin/images/EditFieldNameDialog.png||GHIDRA||||END|
src/main/help/help/topics/LabelMgrPlugin/images/LabelHistoryInputDialog.png||GHIDRA||||END|
src/main/help/help/topics/LabelMgrPlugin/images/SetLabel.png||GHIDRA||||END|
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png
index 66b0cecfdb..4a9fc8d117 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/Labels.htm b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/Labels.htm
index a619434162..afb0bfa9bd 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/Labels.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/Labels.htm
@@ -137,8 +137,6 @@
Enter Label
-
Namespace
-
- - The defining scope of the label. The available namespaces are based upon the current
- address. When editing a label, the available namespaces are not necessarily those in the
- namespace hierarchy in which the label is located, but rather are those based upon the
- address of that label. The Global namespace is always included by default, as well
- as the parent namespace of the current label, if one is being edited.
-
+ The containing namespace for the label or function.
+ The applicable namespaces are based upon the current address and symbol type.
+ The namespace combobox
+ will initially be populated with the most obvious choices for that location, as well
+ any recently chosen namespaces.
+ Next to the combobox, there is a browse button which can be pressed to bring up the
+ Namespace Chooser Dialog. From this chooser dialog,
+ any namespace in the program can be selected. However, not all namespaces are applicable
+ to all locations. For example, you can't put a function into another function's namespace.
+ If the namespace is not applicable to the current location, the selected namespace will
+ still appear in the namespace field, but you will get an error when the OK button is
+ pressed.
-
This field is disabled, if there is a
- function with a default name at this address. The namespace will stay set to the parent
- namespace of the function and the label name you enter will become the new function
- name.
+
This field is disabled when the namespace
+ at that location can't be changed. For example, parameters and local variables can only
+ have the enclosing function as its namespace.
@@ -186,38 +185,66 @@
Entry Point
-
- - Sets the entry point property for address associated with this label. Setting this
+ Sets the entry point property for address associated with this label. Setting this
property on one symbol, changes it for all symbols at the same address.
-
+
Primary
-
- - Sets the primary property for this symbol. If there is only one symbol at this
+ Sets the primary property for this symbol. If there is only one symbol at this
address, the checkbox will be selected and disabled, since it must be primary. Whenever
the checkbox is selected, it will be disabled because the only way to make a symbol
become non-primary is to select another symbol at the same address and make it primary.
This ensures that there will always be one label that is primary. If there is a function
at this address and you add a new label, the checkbox will be enabled such that if you
select the checkbox, the function is renamed to the new label that you entered. The
- function symbol must always be the primary symbol.
-
+ function symbol must always be the primary symbol.
+
Pinned
-
- - Sets the label to pinned. A pinned label will not move if the image base is changed
+ Sets the label to pinned. A pinned label will not move if the image base is changed
or a memory block is moved. A label that is not pinned, will move with the code
or data if a memory block is moved or the image base is changed. Also, a pinned
label will not be removed if the memory block that contains it is removed. Only code,
- data, or function labels may be pinned.
-
+ data, or function labels may be pinned.
+ Namespace Chooser Dialog
+
+
+ The Namespace Chooser Dialog allows you to choose namespaces that have been defined
+ in the current program. It presents a drop-down text field where you can begin typing
+ the name of a namespace and a drop-down window will appear showing a list of
+ all namespaces that match the typed text.
+
+
+ 
+ Namespace Chooser Dialog
+
+ There are several modes that determine how the typed text is used to match against all
+ program namespaces. The mode can be changed by pressing on the symbol(s) shown on the far
+ right of the text field. You can also change the search mode using Ctrl Down and
+ Ctrl Up to change the mode forward and backward, respectively. Regardless of the mode,
+ the searches are not case sensitive.
+ These modes are:
+
+
+ - Starts With ^ - In this mode, the typed text will match all namespaces that
+ have a base name (not path) that starts with the typed text.
+ - Contains () - In this mode, the typed text will match all namespaces that
+ have the typed text anywhere in its full namespace path.
+ - Wildcard *? - In this mode, the typed text may contain wildcard characters
+ which will be used to create a pattern for matching against the full namespace path. The
+ wildcard char '*' will match 0 or more characters, while the wildcard char '?' will match any
+ single character.
+
+
+
+
Set Label Dialog
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/AddLabel.png b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/AddLabel.png
index b95e614926..f3cec4d538 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/AddLabel.png and b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/AddLabel.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/ChooseNamespace.png b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/ChooseNamespace.png
new file mode 100644
index 0000000000..9daf3e746f
Binary files /dev/null and b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/ChooseNamespace.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/EditFieldNameDialog.png b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/EditFieldNameDialog.png
index 8e51de9e6a..7723b0d3ca 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/EditFieldNameDialog.png and b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/EditFieldNameDialog.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/LabelHistoryInputDialog.png b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/LabelHistoryInputDialog.png
index 01f3248f2a..c891ef782b 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/LabelHistoryInputDialog.png and b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/LabelHistoryInputDialog.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/SetLabel.png b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/SetLabel.png
index 5f0a37ab7a..88c2b07ae2 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/SetLabel.png and b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/SetLabel.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/ShowLabelHistory.png b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/ShowLabelHistory.png
index 8c76192c03..dd82d15cdf 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/ShowLabelHistory.png and b/Ghidra/Features/Base/src/main/help/help/topics/LabelMgrPlugin/images/ShowLabelHistory.png differ
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/AddEditDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/AddEditDialog.java
index 108a3b203c..96d4edf86a 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/AddEditDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/AddEditDialog.java
@@ -27,6 +27,7 @@ import org.apache.commons.lang3.StringUtils;
import docking.ComponentProvider;
import docking.ReusableDialogComponentProvider;
import docking.widgets.OptionDialog;
+import docking.widgets.button.BrowseButton;
import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GhidraComboBox;
import ghidra.app.cmd.label.*;
@@ -46,6 +47,7 @@ import ghidra.util.layout.VerticalLayout;
*/
public class AddEditDialog extends ReusableDialogComponentProvider {
private static final int MAX_RETENTION = 10;
+
private PluginTool tool;
private TitledBorder nameBorder;
private GhidraComboBox labelNameChoices;
@@ -337,55 +339,65 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
}
}
- // This method only gets the namespace associated with the current address
- // and it's tree of namespaces. It does not walk the namespace tree of
- // the symbol, which can be different than that of the address.
private void initNamespaces() {
namespaceChoices.removeAllItems();
+ for (Namespace namespace : getSelectableNamespaces()) {
+ namespaceChoices.addItem(new NamespaceWrapper(namespace));
+ }
+ }
+
+ private Collection getSelectableNamespaces() {
if (!namespaceChoices.isEnabled()) {
- namespaceChoices.addItem(new NamespaceWrapper(symbol.getParentNamespace()));
- selectNamespace();
+ return List.of(symbol.getParentNamespace());
+ }
+
+ SequencedSet namespaces = new LinkedHashSet<>();
+ addGlobalNamespace(namespaces);
+ addCurrentNamespace(namespaces);
+ addSuggestedNamespace(namespaces);
+ addRecentNamespaces(namespaces);
+
+ return namespaces;
+ }
+
+ private void addRecentNamespaces(SequencedSet namespaces) {
+ List recentNamespaces = NamespaceCache.get(program);
+ if (recentNamespaces == null) {
return;
}
+ for (Namespace namespace : recentNamespaces) {
+ if (!namespaces.contains(namespace)) {
+ namespaces.add(namespace);
+ }
+ }
+ }
- Collection collection = new HashSet<>();
+ private void addSuggestedNamespace(SequencedSet namespaces) {
+ Namespace namespace = program.getSymbolTable().getNamespace(addr);
+ if (namespace == null) {
+ return;
+ }
+ // Don't include the currently edited symbol as a possible choice
+ if (symbol != null && namespace.equals(symbol.getObject())) {
+ return;
+ }
+ if (!namespaces.contains(namespace)) {
+ namespaces.add(namespace);
+ }
+ }
- // we always add the global namespace
+ private void addGlobalNamespace(SequencedSet namespaces) {
Namespace globalNamespace = program.getGlobalNamespace();
-
- NamespaceWrapper composite = new NamespaceWrapper(globalNamespace);
- namespaceChoices.addItem(composite);
- collection.add(composite);
-
- Namespace currentNamespace = program.getSymbolTable().getNamespace(addr);
-
- // no symbol or not editing a function symbol
- if ((symbol == null) || (symbol != null && symbol.getSymbolType() != SymbolType.FUNCTION)) {
- // walk the tree of namespaces and collect all of the items
- for (; (currentNamespace != globalNamespace); currentNamespace =
- currentNamespace.getParentNamespace()) {
- composite = new NamespaceWrapper(currentNamespace);
-
- if (!collection.contains(composite)) {
- collection.add(composite);
- namespaceChoices.addItem(composite);
- }
- }
+ if (!namespaces.contains(globalNamespace)) {
+ namespaces.add(globalNamespace);
}
+ }
+ private void addCurrentNamespace(SequencedSet namespaces) {
if (symbol != null) {
- // we are adding the current namespace of the symbol if it is not in
- // the namespace tree that belongs to the address
- Namespace symbolNamespace = symbol.getParentNamespace();
- composite = new NamespaceWrapper(symbolNamespace);
- if (!collection.contains(composite)) {
- collection.add(composite);
- namespaceChoices.insertItemAt(composite, 1);
- }
+ namespaces.add(symbol.getParentNamespace());
}
-
- selectNamespace();
}
/**
@@ -478,6 +490,7 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
namespaceChoices.setEnabled(true);
initNamespaces();
+ selectNamespace();
clearStatusText();
}
@@ -538,6 +551,7 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
namespaceChoices.setEnabled(true);
}
initNamespaces();
+ selectNamespace();
clearStatusText();
}
@@ -550,12 +564,11 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
- // change the preferred size to use the width determined by the # of columns in
- // combo box editor instead of the largest item in the combo box data model to
- // prevent the dialog from growing huge when a large label gets added to its recent
- // items
- Dimension editorSize = getEditor().getEditorComponent().getPreferredSize();
- size.width = editorSize.width;
+ // Change the preferred size to use a standard starting width. Previously, it
+ // was sized on the first label edited, but then it just remembered that size.
+ // A width of 500 is a good starting width that will fit most reasonable
+ // namespace names.
+ size.width = 500;
return size;
}
};
@@ -598,7 +611,8 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
mainPanel.add(bottomPanel);
topPanel.add(labelNameChoices, BorderLayout.NORTH);
- midPanel.add(namespaceChoices, BorderLayout.NORTH);
+ midPanel.add(namespaceChoices, BorderLayout.CENTER);
+ midPanel.add(buildBrowsePanel(), BorderLayout.EAST);
bottomPanel.add(entryPointCheckBox);
bottomPanel.add(primaryCheckBox);
bottomPanel.add(pinnedCheckBox);
@@ -610,6 +624,26 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
return mainPanel;
}
+ private Component buildBrowsePanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ JButton browseButton = new BrowseButton();
+ browseButton.setToolTipText("Choose Namespace");
+ browseButton.addActionListener(e -> showNamespaceChooser());
+ panel.add(browseButton, BorderLayout.CENTER);
+ panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
+ return panel;
+ }
+
+ private void showNamespaceChooser() {
+ NamespaceChooserDialog dialog = new NamespaceChooserDialog();
+ Namespace namespace = dialog.getNameSpace(program);
+ if (namespace != null) {
+ NamespaceCache.add(program, namespace);
+ initNamespaces();
+ namespaceChoices.setSelectedItem(new NamespaceWrapper(namespace));
+ }
+ }
+
private void addListeners() {
labelNameChoices.addActionListener(e -> {
if (program != null) {
@@ -636,6 +670,11 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
return text;
}
+ // for testing
+ public JComboBox> getNamespaceComboBox() {
+ return namespaceChoices;
+ }
+
public class NamespaceWrapper {
private Namespace namespace;
@@ -672,4 +711,5 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
return namespace.hashCode();
}
}
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceCache.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceCache.java
new file mode 100644
index 0000000000..3fc485eb75
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceCache.java
@@ -0,0 +1,59 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util;
+
+import java.util.*;
+
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.symbol.Namespace;
+import ghidra.util.datastruct.LRUSet;
+
+/**
+ * Static class for remember the last few namespaces used for a program.
+ */
+public class NamespaceCache {
+ public static final int MAX_RECENTS = 10;
+ private static Map> recentNamespaces = new HashMap<>();
+
+ /**
+ * Returns the list of recently used namespaces for the given program.
+ * @param program the program to get namespaces for
+ * @return the list of recently used namespaces for the given program
+ */
+ public static List get(Program program) {
+ LRUSet recents = recentNamespaces.get(program);
+ return recents != null ? recents.toList() : Collections.emptyList();
+ }
+
+ /**
+ * Adds a recently used namespace for a program.
+ * @param program the program to add a recently namespace
+ * @param namespace the recently used namespace to remember
+ */
+ public static void add(Program program, Namespace namespace) {
+ // no need to cache global namespace, it is always available
+ if (namespace.isGlobal()) {
+ return;
+ }
+ LRUSet recents = recentNamespaces.get(program);
+ if (recents == null) {
+ recents = new LRUSet(MAX_RECENTS);
+ recentNamespaces.put(program, recents);
+ program.addCloseListener(p -> recentNamespaces.remove(p));
+ }
+ recents.add(namespace);
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceChooserDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceChooserDialog.java
new file mode 100644
index 0000000000..43ddafac52
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceChooserDialog.java
@@ -0,0 +1,123 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.*;
+
+import docking.DialogComponentProvider;
+import docking.DockingWindowManager;
+import docking.widgets.DropDownSelectionTextField;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.symbol.Namespace;
+import ghidra.program.model.symbol.Symbol;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.layout.PairLayout;
+import ghidra.util.task.*;
+
+/**
+ * Class for choosing a namespace
+ */
+public class NamespaceChooserDialog extends DialogComponentProvider {
+
+ private DropDownSelectionTextField dropDownField;
+ private NamespaceDropDownModel namespaceModel;
+ private Namespace chosenNamespace;
+
+ public NamespaceChooserDialog() {
+ super("Namespace Chooser");
+ namespaceModel = new NamespaceDropDownModel();
+ addWorkPanel(buildWorkPanel());
+ addOKButton();
+ addCancelButton();
+ }
+
+ public Namespace getNameSpace(Program program) {
+ List namespaces = gatherNamespaces(program);
+ if (namespaces == null) {
+ // user cancelled while gathering namespaces
+ return null;
+ }
+ namespaceModel.setNamespaces(namespaces);
+ DockingWindowManager.showDialog(this);
+ return chosenNamespace;
+ }
+
+ @Override
+ protected void okCallback() {
+ chosenNamespace = dropDownField.getSelectedValue();
+ close();
+ }
+
+ @Override
+ protected void cancelCallback() {
+ chosenNamespace = null;
+ close();
+ }
+
+ private List gatherNamespaces(Program program) {
+ GatherNamespacesTask task = new GatherNamespacesTask(program);
+ TaskLauncher.launch(task);
+ return task.getNamespaces();
+ }
+
+ private JComponent buildWorkPanel() {
+ JPanel panel = new JPanel(new PairLayout());
+ panel.add(new JLabel("Namespace: "));
+
+ dropDownField = new DropDownSelectionTextField<>(namespaceModel);
+ panel.add(dropDownField);
+ panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ return panel;
+ }
+
+ private static class GatherNamespacesTask extends Task {
+ private List namespaces;
+ private Program program;
+
+ GatherNamespacesTask(Program program) {
+ super("Gather Namespaces");
+ this.program = program;
+ }
+
+ @Override
+ public void run(TaskMonitor monitor) throws CancelledException {
+ List list = new ArrayList<>();
+ list.add(program.getGlobalNamespace());
+ for (Symbol symbol : program.getSymbolTable().getDefinedSymbols()) {
+ monitor.checkCancelled();
+ if (!symbol.getSymbolType().isNamespace()) {
+ continue;
+ }
+ Object object = symbol.getObject();
+ if (object instanceof Function f && f.isThunk()) {
+ continue;
+ }
+ list.add((Namespace) object);
+ }
+ namespaces = list;
+ }
+
+ List getNamespaces() {
+ return namespaces;
+ }
+
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceDropDownModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceDropDownModel.java
new file mode 100644
index 0000000000..46ac5f93b7
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/NamespaceDropDownModel.java
@@ -0,0 +1,193 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.help.UnsupportedOperationException;
+import javax.swing.ListCellRenderer;
+
+import org.apache.commons.lang3.StringUtils;
+
+import docking.widgets.DropDownTextFieldDataModel;
+import docking.widgets.list.GListCellRenderer;
+import ghidra.program.model.symbol.Namespace;
+import ghidra.util.datastruct.CaseInsensitiveDuplicateStringComparator;
+
+/**
+ * This is the drop down text field model for namespaces.
+ */
+public class NamespaceDropDownModel implements DropDownTextFieldDataModel {
+ private static final char END_CHAR = '\uffff';
+ private List namespaces;
+ private ListCellRenderer renderer;
+ private Comparator comparator =
+ (n1, n2) -> n1.getName().compareToIgnoreCase(n2.getName());
+ private Comparator stringComparator = new CaseInsensitiveDuplicateStringComparator();
+
+ NamespaceDropDownModel() {
+ renderer = GListCellRenderer.createDefaultTextRenderer(this::getListDisplay);
+ setNamespaces(Collections.emptyList());
+ }
+
+ @Override
+ public List getMatchingData(String searchText) {
+ throw new UnsupportedOperationException(
+ "Method no longer supported. Instead, call getMatchingData(String, SearchMode)");
+ }
+
+ public void setNamespaces(List namespaces) {
+ this.namespaces = namespaces;
+ Collections.sort(namespaces, comparator);
+ }
+
+ private String getListDisplay(Namespace namespace) {
+ StringBuilder buf = new StringBuilder(namespace.getName());
+ Namespace parentNamespace = namespace.getParentNamespace();
+ if (parentNamespace != null) {
+ buf.append(" (");
+ buf.append(parentNamespace.getName(true));
+ buf.append(")");
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public List getMatchingData(String searchText, SearchMode searchMode) {
+ if (StringUtils.isBlank(searchText)) {
+ return new ArrayList<>(namespaces);
+ }
+
+ if (!getSupportedSearchModes().contains(searchMode)) {
+ throw new IllegalArgumentException("Unsupported SearchMode: " + searchMode);
+ }
+
+ if (searchMode == SearchMode.STARTS_WITH) {
+ return getMatchingDataStartsWith(searchText);
+ }
+
+ Pattern p = searchMode.createPattern(searchText);
+ return getMatchingDataRegex(p);
+ }
+
+ private List getMatchingDataRegex(Pattern p) {
+ List results = new ArrayList<>();
+ for (Namespace namespace : namespaces) {
+ String namespacePath = namespace.getName(true);
+ Matcher m = p.matcher(namespacePath);
+ if (m.matches()) {
+ results.add(namespace);
+ }
+ }
+ return results;
+ }
+
+ private List getMatchingDataStartsWith(String searchText) {
+ MappedList list = new MappedList<>(namespaces, n -> n.getName());
+
+ int startIndex = Collections.binarySearch(list, searchText, stringComparator);
+ int endIndex = Collections.binarySearch(list, searchText + END_CHAR, stringComparator);
+
+ // the binary search returns a negative, incremented position if there is no match in the
+ // list for the given search
+ if (startIndex < 0) {
+ startIndex = -startIndex - 1;
+ }
+
+ if (endIndex < 0) {
+ endIndex = -endIndex - 1;
+ }
+
+ return namespaces.subList(startIndex, endIndex);
+ }
+
+ @Override
+ public List getSupportedSearchModes() {
+ return List.of(SearchMode.STARTS_WITH, SearchMode.CONTAINS, SearchMode.WILDCARD);
+ }
+
+ @Override
+ public int getIndexOfFirstMatchingEntry(List data, String text) {
+ // The data are sorted such that lower-case is before upper-case and smaller length
+ // matches come before longer matches. If we ever find a case-sensitive exact match,
+ // use that. Otherwise, keep looking for a case-insensitive exact match. The
+ // case-insensitive match is preferred over a non-matching item. Once we get to a
+ // non-matching item, we can quit.
+ int lastPreferredMatchIndex = -1;
+ for (int i = 0; i < data.size(); i++) {
+ Namespace namespace = data.get(i);
+ String name = namespace.getName();
+ if (name.equals(text)) {
+ // an exact match is the best possible match!
+ return i;
+ }
+
+ if (name.equalsIgnoreCase(text)) {
+ // keep going, but remember this location, in case we don't find any more matches
+ lastPreferredMatchIndex = i;
+ }
+ else {
+ // we've encountered a non-matching entry--nothing left to search
+ return lastPreferredMatchIndex;
+ }
+ }
+
+ return -1; // we only get here when the list is empty
+ }
+
+ @Override
+ public ListCellRenderer getListRenderer() {
+ return renderer;
+ }
+
+ @Override
+ public String getDescription(Namespace value) {
+ return value.getName(true);
+ }
+
+ @Override
+ public String getDisplayText(Namespace value) {
+ return value.getName(false);
+ }
+
+ /**
+ * Provides an read-only mapped view List of type T from a List of type S.
+ * @param The type of elements in the source list
+ * @param The type of elements in the mapped list
+ */
+ private static class MappedList extends AbstractList {
+ private final List sourceList;
+ private final Function transformer;
+
+ public MappedList(List sourceList, Function transformer) {
+ this.sourceList = sourceList;
+ this.transformer = transformer;
+ }
+
+ @Override
+ public T get(int index) {
+ return transformer.apply(sourceList.get(index));
+ }
+
+ @Override
+ public int size() {
+ return sourceList.size();
+ }
+ }
+}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/label/AddEditDialogWithNamespaceChooserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/label/AddEditDialogWithNamespaceChooserTest.java
new file mode 100644
index 0000000000..229900e00c
--- /dev/null
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/label/AddEditDialogWithNamespaceChooserTest.java
@@ -0,0 +1,192 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.plugin.core.label;
+
+import static org.junit.Assert.*;
+
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.*;
+
+import org.junit.*;
+
+import ghidra.app.cmd.function.CreateFunctionCmd;
+import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
+import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin;
+import ghidra.app.util.AddEditDialog;
+import ghidra.app.util.NamespaceChooserDialog;
+import ghidra.app.util.viewer.field.MnemonicFieldFactory;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSet;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.symbol.*;
+import ghidra.test.*;
+
+public class AddEditDialogWithNamespaceChooserTest extends AbstractGhidraHeadedIntegrationTest {
+ private TestEnv env;
+ private Program program;
+ private AddEditDialog dialog;
+ private PluginTool tool;
+ private LabelMgrPlugin plugin;
+ private JComboBox> namespacesComboBox;
+ private Symbol symbol1;
+ private Symbol symbol2;
+
+ @Before
+ public void setUp() throws Exception {
+ env = new TestEnv();
+
+ ClassicSampleX86ProgramBuilder builder = new ClassicSampleX86ProgramBuilder();
+ builder.createNamespace("Foo");
+ builder.createNamespace("Bar");
+ program = builder.getProgram();
+
+ tool = env.showTool(program);
+ tool.addPlugin(CodeBrowserPlugin.class.getName());
+ tool.addPlugin(LabelMgrPlugin.class.getName());
+ tool.addPlugin(GoToAddressLabelPlugin.class.getName());
+
+ plugin = getPlugin(tool, LabelMgrPlugin.class);
+
+ symbol1 = builder.createLabel("0x10065a6", "symbol1");
+ symbol2 = builder.createLabel("0x10065a9", "symbol2");
+ createEntryFunction();
+ builder.dispose();
+
+ dialog = getAddEditDialog();
+
+ namespacesComboBox = dialog.getNamespaceComboBox();
+
+ }
+
+ private AddEditDialog getAddEditDialog() {
+ return runSwing(() -> plugin.getAddEditDialog());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ close(dialog);
+ env.dispose();
+ }
+
+ @Test
+ public void testUsingNamespaceChooser() throws Exception {
+
+ editLabel(symbol1);
+ assertNamespaces("Global", "entry");
+ assertSelected("Global");
+ chooseNamespaceFromChooser("Foo");
+ assertSelected("Foo");
+ pressOk();
+
+ editLabel(symbol2);
+ assertNamespaces("Global", "entry", "Foo");
+ assertSelected("Global");
+ chooseNamespaceFromChooser("Bar");
+ assertSelected("Bar");
+ pressOk();
+
+ editLabel(symbol1);
+ assertNamespaces("Global", "Foo", "entry", "Bar");
+ assertSelected("Foo");
+ pressOk();
+ }
+
+ private void assertSelected(String name) {
+ assertEquals(name, getSelectedNamespace().getName());
+ }
+
+ private void assertNamespaces(String... names) {
+ List comboNamespaces = getComboNamespaces();
+ assertEquals(names.length, comboNamespaces.size());
+ for (int i = 0; i < names.length; i++) {
+ assertEquals(names[i], comboNamespaces.get(i).getName());
+ }
+ }
+
+ private void chooseNamespaceFromChooser(String text) {
+ AbstractButton button = findButtonByName(dialog.getComponent(), "BrowseButton");
+ pressButton(button, false);
+ NamespaceChooserDialog nd = waitForDialogComponent(NamespaceChooserDialog.class);
+ JTextField field = findComponent(nd.getComponent(), JTextField.class);
+ typeText(field, text, true);
+ enter(field);
+ pressButtonByText(nd.getComponent(), "OK");
+ }
+
+ protected void enter(JTextField field) {
+ triggerActionKey(field, 0, KeyEvent.VK_ENTER);
+ waitForSwing();
+ }
+
+ protected void typeText(JTextField textField, String text, boolean expectWindow) {
+ waitForSwing();
+ triggerText(textField, text);
+ }
+
+//==================================================================================================
+// Private Methods
+//==================================================================================================
+
+ private void pressOk() {
+ pressButtonByText(dialog, "OK");
+ }
+
+ private Namespace getSelectedNamespace() {
+ return runSwing(() -> {
+ Object selectedItem = namespacesComboBox.getSelectedItem();
+ return ((AddEditDialog.NamespaceWrapper) selectedItem).getNamespace();
+ });
+ }
+
+ private List getComboNamespaces() {
+ return runSwing(() -> {
+ List namespaces = new ArrayList<>();
+ int count = namespacesComboBox.getItemCount();
+ for (int i = 0; i < count; i++) {
+ Object itemAt = namespacesComboBox.getItemAt(i);
+ namespaces.add(((AddEditDialog.NamespaceWrapper) itemAt).getNamespace());
+ }
+ return namespaces;
+ });
+ }
+
+ private void editLabel(final Symbol symbol) {
+ // this makes debugging easier
+ CodeBrowserPlugin cb = getPlugin(tool, CodeBrowserPlugin.class);
+ cb.goToField(symbol.getAddress(), MnemonicFieldFactory.FIELD_NAME, 0, 0);
+
+ runSwing(() -> dialog.editLabel(symbol, program), false);
+ waitForSwing();
+ }
+
+ private void createEntryFunction() throws Exception {
+ Symbol s = getUniqueSymbol(program, "entry", null);
+ Function f = program.getListing().getFunctionAt(s.getAddress());
+ if (f == null) {
+ Address addr = s.getAddress();
+ AddressSet body = new AddressSet(addr, addr.getNewAddress(0x010065cc));
+ body.addRange(addr.getNewAddress(0x10065a4), addr.getNewAddress(0x010065cc));
+ CreateFunctionCmd cmd =
+ new CreateFunctionCmd(null, addr, body, SourceType.USER_DEFINED);
+ assertTrue(tool.execute(cmd, program));
+ }
+ }
+}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/label/AddEditDialoglTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/label/AddEditDialoglTest.java
index b9a2565240..1f9667792d 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/label/AddEditDialoglTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/label/AddEditDialoglTest.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 static org.junit.Assert.*;
import java.awt.Component;
import java.awt.Window;
+import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
import javax.swing.*;
@@ -86,11 +86,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
}
private AddEditDialog getAddEditDialog() {
- AtomicReference ref = new AtomicReference<>();
- runSwing(() -> {
- ref.set(plugin.getAddEditDialog());
- });
- return ref.get();
+ return runSwing(() -> plugin.getAddEditDialog());
}
@After
@@ -106,7 +102,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals("", getText());
assertTrue(!entryCheckBox.isSelected());
- Namespace scope = getScope();
+ Namespace scope = getSelectedNamespace();
assertEquals(globalScope, scope);
assertTrue(primaryCheckBox.isSelected());
assertTrue(!primaryCheckBox.isEnabled());
@@ -195,42 +191,19 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
Address address = addr(0x10065a6);
Symbol symbol = st.createLabel(address, "sybmol1", scope4, SourceType.USER_DEFINED);
- editLabel(symbol);
-
- JComboBox> namespaceChoices = (JComboBox>) getInstanceField("namespaceChoices", dialog);
-
- // get a list of all namespaces in order to compare with the values
- // from the combo box
- // the first item should always be the global namespace...
- assertEquals("The first item in the list of namespaces is not the global namespace",
- program.getGlobalNamespace(), getScope(0));
-
- assertEquals("The symbol's namespace was not put into the proper place in the list.",
- getScope(2), symbol.getParentNamespace());
-
- // ...then, each namespace should be a child of the following
- // namespace
- int itemCount = namespaceChoices.getItemCount();
- for (int i = 1; i < itemCount - 1; i++) {
- Namespace currentNamespace = getScope(i);
- Namespace nextNamespace = getScope(i + 1);
- Namespace actualParent = currentNamespace.getParentNamespace();
-
- assertTrue("The namespaces are not in order in the " +
- "namespaceChoices combo box. Each item should be a child of " + "following item.",
- nextNamespace.equals(actualParent));
- }
-
- // finally, the last item should be parented on the global namespace
- Namespace currentNamespace = getScope(itemCount - 1);
- Namespace actualParent = currentNamespace.getParentNamespace();
-
- assertTrue(
- "The namespaces are not in order in the " +
- "namespaceChoices combo box. Each item should be a child of " + "following item.",
- actualParent.equals(program.getGlobalNamespace()));
-
program.endTransaction(transactionID, true);
+
+ editLabel(symbol);
+ List comboNamespaces = getComboNamespaces();
+ assertEquals(3, comboNamespaces.size());
+
+ // currently, combo is populated with: global, current symbol namespace, containing function
+ // namespace if in a function body, and then recently used (there are no recently used in
+ // this test)
+ assertEquals(program.getGlobalNamespace(), comboNamespaces.get(0));
+ assertEquals(scope4, comboNamespaces.get(1));
+ assertEquals(f, comboNamespaces.get(2));
+
}
@Test
@@ -892,33 +865,23 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
runSwing(() -> checkbox.setSelected(value));
}
- private Namespace getScope() {
- final AtomicReference ref = new AtomicReference<>();
- runSwing(() -> {
+ private Namespace getSelectedNamespace() {
+ return runSwing(() -> {
Object selectedItem = namespacesComboBox.getSelectedItem();
- Namespace ns = ((AddEditDialog.NamespaceWrapper) selectedItem).getNamespace();
- ref.set(ns);
+ return ((AddEditDialog.NamespaceWrapper) selectedItem).getNamespace();
});
- return ref.get();
}
- private Namespace getScope(final int index) {
- final AtomicReference ref = new AtomicReference<>();
- runSwing(() -> {
+ private List getComboNamespaces() {
+ return runSwing(() -> {
+ List namespaces = new ArrayList<>();
int count = namespacesComboBox.getItemCount();
- if (count <= index) {
- System.err.println("Available namespaces: ");
- for (int i = 0; i < count; i++) {
- System.err.println("\t" + namespacesComboBox.getItemAt(i));
- }
- throw new IllegalArgumentException("No namespace at index: " + index);
+ for (int i = 0; i < count; i++) {
+ Object itemAt = namespacesComboBox.getItemAt(i);
+ namespaces.add(((AddEditDialog.NamespaceWrapper) itemAt).getNamespace());
}
-
- Object selectedItem = namespacesComboBox.getItemAt(index);
- Namespace ns = ((AddEditDialog.NamespaceWrapper) selectedItem).getNamespace();
- ref.set(ns);
+ return namespaces;
});
- return ref.get();
}
private void setScope(final Namespace scope) {
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/NamespaceCacheTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/NamespaceCacheTest.java
new file mode 100644
index 0000000000..c8a1f6d9ed
--- /dev/null
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/NamespaceCacheTest.java
@@ -0,0 +1,126 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import generic.test.AbstractGuiTest;
+import ghidra.program.database.ProgramBuilder;
+import ghidra.program.database.ProgramDB;
+import ghidra.program.model.symbol.Namespace;
+import ghidra.program.model.symbol.SourceType;
+import ghidra.test.ToyProgramBuilder;
+
+public class NamespaceCacheTest extends AbstractGuiTest {
+
+ private Namespace a;
+ private Namespace b;
+ private Namespace c;
+ private Namespace d;
+ private Namespace e;
+ private Namespace f;
+ private Namespace g;
+ private Namespace h;
+ private Namespace i;
+ private Namespace j;
+ private Namespace k;
+ private ProgramDB program;
+
+ @Before
+ public void setup() throws Exception {
+ ProgramBuilder builder = new ToyProgramBuilder("Test", false, this);
+ a = builder.createNamespace("a");
+ b = builder.createNamespace("b", "a", SourceType.USER_DEFINED);
+ c = builder.createNamespace("c", "a::b", SourceType.USER_DEFINED);
+ d = builder.createNamespace("d");
+ e = builder.createNamespace("e");
+ f = builder.createNamespace("f");
+ g = builder.createNamespace("g");
+ h = builder.createNamespace("h");
+ i = builder.createNamespace("i");
+ j = builder.createNamespace("j");
+ k = builder.createNamespace("k");
+ program = builder.getProgram();
+ }
+
+ @Test
+ public void testGetRecent() {
+ NamespaceCache.add(program, a);
+ NamespaceCache.add(program, b);
+ NamespaceCache.add(program, c);
+
+ List recent = NamespaceCache.get(program);
+ assertEquals(c, recent.get(0));
+ assertEquals(b, recent.get(1));
+ assertEquals(a, recent.get(2));
+ }
+
+ @Test
+ public void testMostRecentAtTop() {
+ NamespaceCache.add(program, a);
+ NamespaceCache.add(program, b);
+ NamespaceCache.add(program, c);
+ NamespaceCache.add(program, a);
+
+ List recents = NamespaceCache.get(program);
+ assertEquals(3, recents.size());
+ assertEquals(a, recents.get(0));
+ assertEquals(c, recents.get(1));
+ assertEquals(b, recents.get(2));
+ }
+
+ @Test
+ public void testMaxRecents() {
+ NamespaceCache.add(program, a);
+ NamespaceCache.add(program, b);
+ NamespaceCache.add(program, c);
+ NamespaceCache.add(program, d);
+ NamespaceCache.add(program, e);
+ NamespaceCache.add(program, f);
+ NamespaceCache.add(program, g);
+ NamespaceCache.add(program, h);
+ NamespaceCache.add(program, i);
+ NamespaceCache.add(program, j);
+ NamespaceCache.add(program, k);
+
+ List recents = NamespaceCache.get(program);
+ assertEquals(NamespaceCache.MAX_RECENTS, recents.size());
+ assertEquals(k, recents.get(0));
+ assertEquals(b, recents.get(9));
+ }
+
+ @Test
+ public void testClosingProgramClearsRecents() {
+ NamespaceCache.add(program, a);
+ NamespaceCache.add(program, b);
+ NamespaceCache.add(program, c);
+
+ List recents = NamespaceCache.get(program);
+ assertEquals(3, recents.size());
+
+ program.release(this);
+
+ recents = NamespaceCache.get(program);
+ assertTrue(recents.isEmpty());
+
+ }
+
+}
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/LRUSet.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/LRUSet.java
index c1d8870d57..57445bbba1 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/LRUSet.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/LRUSet.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.
@@ -15,7 +15,7 @@
*/
package ghidra.util.datastruct;
-import java.util.Iterator;
+import java.util.*;
/**
* An ordered set-like data structure.
@@ -51,6 +51,13 @@ public class LRUSet extends LRUMap implements Iterable {
@Override
public String toString() {
- return map.keySet().toString();
+ return keySet().toString();
+ }
+
+ /**
+ * {@return a List of elements in this set}
+ */
+ public List toList() {
+ return new ArrayList<>(keySet());
}
}
diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java
index f1b5457697..b25eff8824 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java
@@ -68,18 +68,6 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator {
positionListingTop(0x40D3B8);
DropDownSelectionTextField> textField = showTypeChooserDialog();
- triggerText(textField, "undefined");
-
- DialogComponentProvider dialog = getDialog();
- JComponent component = dialog.getComponent();
- Window dataTypeDialog = windowForComponent(component);
- Window[] popUpWindows = dataTypeDialog.getOwnedWindows();
-
- List dataTypeWindows = new ArrayList<>(Arrays.asList(popUpWindows));
- dataTypeWindows.add(dataTypeDialog);
-
- captureComponents(dataTypeWindows);
- closeAllWindows();
}
private DropDownSelectionTextField> showTypeChooserDialog() throws Exception {
diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/LabelMgrPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/LabelMgrPluginScreenShots.java
index 5614090ee1..69f5dc025e 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/LabelMgrPluginScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/LabelMgrPluginScreenShots.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,8 +23,7 @@ import org.junit.Test;
import docking.widgets.combobox.GhidraComboBox;
import ghidra.app.plugin.core.label.*;
-import ghidra.app.util.AddEditDialog;
-import ghidra.app.util.EditFieldNameDialog;
+import ghidra.app.util.*;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.LabelHistory;
@@ -76,11 +75,21 @@ public class LabelMgrPluginScreenShots extends GhidraScreenShotGenerator {
captureDialog();
}
+ @Test
+ public void testChooseNamespace() {
+ runSwingLater(() -> {
+ NamespaceChooserDialog dialog = new NamespaceChooserDialog();
+ dialog.getNameSpace(program);
+ });
+ waitForDialogComponent(NamespaceChooserDialog.class);
+ captureDialog();
+ }
+
@Test
public void testSetLabel() {
LabelMgrPlugin plugin = getPlugin(tool, LabelMgrPlugin.class);
final OperandLabelDialog dialog = new OperandLabelDialog(plugin);
- final GhidraComboBox combo = (GhidraComboBox) getInstanceField("myChoice", dialog);
+ final GhidraComboBox> combo = (GhidraComboBox>) getInstanceField("myChoice", dialog);
runSwing(new Runnable() {
@Override
public void run() {