Merge remote-tracking branch 'origin/GT-3207-dragonmacher-table-header-npe' into Ghidra_9.1

This commit is contained in:
Ryan Kurtz 2019-10-02 13:15:31 -04:00
commit dabaa34e92
3 changed files with 138 additions and 4 deletions

View file

@ -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());
} }

View file

@ -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) {

View file

@ -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();