mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge remote-tracking branch 'origin/GT-3207-dragonmacher-table-header-npe' into Ghidra_9.1
This commit is contained in:
commit
dabaa34e92
3 changed files with 138 additions and 4 deletions
|
@ -15,11 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functionwindow;
|
package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.table.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.ComponentProvider;
|
import docking.ComponentProvider;
|
||||||
|
import docking.widgets.combobox.GComboBox;
|
||||||
|
import docking.widgets.dialogs.SettingsDialog;
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||||
import ghidra.app.plugin.core.clear.ClearCmd;
|
import ghidra.app.plugin.core.clear.ClearCmd;
|
||||||
|
@ -99,6 +105,84 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
loadProgram("notepad");
|
loadProgram("notepad");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangeSettings() throws Exception {
|
||||||
|
//
|
||||||
|
// This test is for a regression bug. There were multiple exceptions happening when
|
||||||
|
// executing the code paths below.
|
||||||
|
//
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
int column = getColumnIndex("Function Size");
|
||||||
|
String startValue = getRenderedTableCellValue(functionTable, row, column);
|
||||||
|
|
||||||
|
JPopupMenu menu = functionTable.getTableColumnPopupMenu(column);
|
||||||
|
JMenuItem item = (JMenuItem) menu.getComponent(1);
|
||||||
|
assertEquals("Column Settings...", item.getText());
|
||||||
|
|
||||||
|
pressButton(item, false);
|
||||||
|
|
||||||
|
SettingsDialog dialog = waitForDialogComponent(SettingsDialog.class);
|
||||||
|
int editRow = getFormatRow(dialog);
|
||||||
|
//triggerEdit(dialog, editRow, 1);
|
||||||
|
editCell(dialog.getTable(), editRow, 1);
|
||||||
|
setComboValue(dialog, "hex");
|
||||||
|
endEdit(dialog);
|
||||||
|
pressButtonByText(dialog, "Dismiss");
|
||||||
|
|
||||||
|
String endValue = getRenderedTableCellValue(functionTable, row, column);
|
||||||
|
assertNotEquals("Changing the format did not change the view", startValue, endValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getFormatRow(SettingsDialog dialog) {
|
||||||
|
GTable table = dialog.getTable();
|
||||||
|
int column = getColumnIndex(table, "Name");
|
||||||
|
int n = table.getRowCount();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int row = i;
|
||||||
|
Object name = runSwing(() -> table.getValueAt(row, column));
|
||||||
|
if ("Format".equals(name)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Unable to find the 'Format' row in the Settings Dialog");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getColumnIndex(String text) {
|
||||||
|
return getColumnIndex(functionTable, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getColumnIndex(JTable table, String text) {
|
||||||
|
TableColumnModel columnModel = table.getColumnModel();
|
||||||
|
int n = columnModel.getColumnCount();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
TableColumn column = columnModel.getColumn(i);
|
||||||
|
if (text.equals(column.getIdentifier().toString())) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail("Could not find column '" + text + "'");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setComboValue(SettingsDialog d, String string) {
|
||||||
|
GTable table = d.getTable();
|
||||||
|
TableCellEditor activeEditor = runSwing(() -> table.getCellEditor());
|
||||||
|
assertNotNull("Table should be editing, but is not", activeEditor);
|
||||||
|
|
||||||
|
assertTrue(activeEditor.getClass().getSimpleName().contains("SettingsEditor"));
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
GComboBox<String> combo = (GComboBox<String>) getInstanceField("comboBox", activeEditor);
|
||||||
|
setComboBoxSelection(combo, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endEdit(SettingsDialog d) {
|
||||||
|
GTable table = d.getTable();
|
||||||
|
runSwing(() -> table.editingStopped(new ChangeEvent(table)));
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForNotBusy(GTable table) {
|
private void waitForNotBusy(GTable table) {
|
||||||
waitForTableModel((ThreadedTableModel<?, ?>) table.getModel());
|
waitForTableModel((ThreadedTableModel<?, ?>) table.getModel());
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,12 +63,14 @@ public class SettingsDialog extends DialogComponentProvider {
|
||||||
this.settingsDefs = newSettingsDefs;
|
this.settingsDefs = newSettingsDefs;
|
||||||
this.settings = newSettings;
|
this.settings = newSettings;
|
||||||
setTitle(title);
|
setTitle(title);
|
||||||
settingsTableModel.fireTableDataChanged();
|
|
||||||
|
settingsTableModel.setSettingsDefinitions(settingsDefs);
|
||||||
DockingWindowManager.showDialog(parent, this);
|
DockingWindowManager.showDialog(parent, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
settingsTable.editingStopped(null);
|
settingsTable.editingStopped(null);
|
||||||
|
settingsTable.dispose();
|
||||||
|
|
||||||
close();
|
close();
|
||||||
settingsDefs = null;
|
settingsDefs = null;
|
||||||
|
@ -79,7 +81,7 @@ public class SettingsDialog extends DialogComponentProvider {
|
||||||
JPanel workPanel = new JPanel(new BorderLayout());
|
JPanel workPanel = new JPanel(new BorderLayout());
|
||||||
workPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
workPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
settingsTableModel = new SettingsTableModel(settingsDefs);
|
settingsTableModel = new SettingsTableModel();
|
||||||
settingsTable = new GTable(settingsTableModel);
|
settingsTable = new GTable(settingsTableModel);
|
||||||
settingsTable.setAutoscrolls(true);
|
settingsTable.setAutoscrolls(true);
|
||||||
settingsTable.setRowSelectionAllowed(false);
|
settingsTable.setRowSelectionAllowed(false);
|
||||||
|
@ -106,6 +108,10 @@ public class SettingsDialog extends DialogComponentProvider {
|
||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GTable getTable() {
|
||||||
|
return settingsTable;
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -161,10 +167,12 @@ public class SettingsDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
private List<SettingsRowObject> rows = new ArrayList<>();
|
private List<SettingsRowObject> rows = new ArrayList<>();
|
||||||
|
|
||||||
SettingsTableModel(SettingsDefinition[] settingsDefs) {
|
void setSettingsDefinitions(SettingsDefinition[] settingsDefs) {
|
||||||
for (SettingsDefinition sd : settingsDefs) {
|
for (SettingsDefinition sd : settingsDefs) {
|
||||||
rows.add(new SettingsRowObject(sd));
|
rows.add(new SettingsRowObject(sd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsTableModel.fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -203,6 +211,17 @@ public class SettingsDialog extends DialogComponentProvider {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getColumnClass(int col) {
|
||||||
|
switch (col) {
|
||||||
|
case 0:
|
||||||
|
return String.class;
|
||||||
|
case 1:
|
||||||
|
return Settings.class;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getColumnValueForRow(SettingsRowObject t, int columnIndex) {
|
public Object getColumnValueForRow(SettingsRowObject t, int columnIndex) {
|
||||||
switch (columnIndex) {
|
switch (columnIndex) {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.TableCellEditor;
|
import javax.swing.table.TableCellEditor;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import javax.swing.tree.*;
|
import javax.swing.tree.*;
|
||||||
|
|
||||||
|
@ -1307,6 +1308,36 @@ public abstract class AbstractGenericTest extends AbstractGTest {
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the rendered value for the specified table cell. The actual value at the cell may
|
||||||
|
* not be a String. This method will get the String display value, as created by the table.
|
||||||
|
*
|
||||||
|
* @param table the table to query
|
||||||
|
* @param row the row to query
|
||||||
|
* @param column the column to query
|
||||||
|
* @return the String value
|
||||||
|
* @throws IllegalArgumentException if there is no renderer or the rendered component is
|
||||||
|
* something from which this method can get a String (such as a JLabel)
|
||||||
|
*/
|
||||||
|
public static String getRenderedTableCellValue(JTable table, int row, int column) {
|
||||||
|
|
||||||
|
return runSwing(() -> {
|
||||||
|
|
||||||
|
TableCellRenderer renderer = table.getCellRenderer(row, column);
|
||||||
|
if (renderer == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"No renderer registered for row/col: " + row + '/' + column);
|
||||||
|
}
|
||||||
|
Component component = table.prepareRenderer(renderer, row, column);
|
||||||
|
if (!(component instanceof JLabel)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Do not know how to get text from a renderer " + "that is not a JLabel");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((JLabel) component).getText();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> void setComboBoxSelection(final JComboBox<T> comboField, final T selection) {
|
public static <T> void setComboBoxSelection(final JComboBox<T> comboField, final T selection) {
|
||||||
runSwing(() -> comboField.setSelectedItem(selection));
|
runSwing(() -> comboField.setSelectedItem(selection));
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue