mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 01:39:21 +02:00
Merge remote-tracking branch
'origin/GP-5963_dragonmacher_PR-7346_fmagin_fmagin_table_accessor' (Closes #7346)
This commit is contained in:
commit
87a32d568c
2 changed files with 192 additions and 10 deletions
|
@ -15,18 +15,18 @@
|
|||
*/
|
||||
package docking.widgets.table;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.table.TableColumn;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.widgets.table.model.DirData;
|
||||
import docking.widgets.table.model.TestGDynamicColumnTableModel;
|
||||
import docking.widgets.table.model.*;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
|
||||
|
@ -73,9 +73,8 @@ public class GTableDynamicColumnModelTest extends AbstractGhidraHeadedIntegratio
|
|||
@Test
|
||||
public void testAddColumn() throws Exception {
|
||||
|
||||
// Grab a column in the middle to remove
|
||||
int count = model.getColumnCount();
|
||||
DynamicTableColumn<DirData, ?, ?> column = model.getColumn(2);
|
||||
DynamicTableColumn<DirData, ?, ?> column = new DirDataSizeColumn();
|
||||
|
||||
int index = 2; // in the middle
|
||||
runSwing(() -> {
|
||||
|
@ -86,6 +85,93 @@ public class GTableDynamicColumnModelTest extends AbstractGhidraHeadedIntegratio
|
|||
assertColumnPresent(column);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTableColumnDescriptor_ShortcutColumnCreation() throws Exception {
|
||||
|
||||
frame.dispose();
|
||||
|
||||
model = new TestGDynamicColumnTableModel() {
|
||||
@Override
|
||||
protected TableColumnDescriptor<DirData> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<DirData> descriptor = new TableColumnDescriptor<>();
|
||||
descriptor.addVisibleColumn("Name", String.class, data -> data.getName());
|
||||
descriptor.addVisibleColumn("Size", Integer.class, data -> data.getSize());
|
||||
descriptor.addHiddenColumn("Date", String.class, data -> data.getTime());
|
||||
return descriptor;
|
||||
}
|
||||
};
|
||||
table = new GhidraTable(model);
|
||||
|
||||
frame = new JFrame("Ghidra Table Test");
|
||||
frame.getContentPane().setLayout(new BorderLayout());
|
||||
frame.getContentPane().add(new JScrollPane(table));
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
|
||||
// Note: when building the column model, all columns are visible by default, including those
|
||||
// created as hidden columns. A swing process will run to update the hidden columns. Thus,
|
||||
// we need to flush the swing processes to get that update to happen.
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(3, model.getColumnCount());
|
||||
|
||||
GTableColumnModel columnModel = (GTableColumnModel) table.getColumnModel();
|
||||
assertEquals(2, columnModel.getColumnCount());
|
||||
|
||||
assertColumns("Name", "Size");
|
||||
|
||||
showHiddenColumn("Date");
|
||||
assertColumns("Name", "Size", "Date");
|
||||
}
|
||||
|
||||
private void showHiddenColumn(String name) {
|
||||
TableColumn tableColumn = getHiddenTableColumn(name);
|
||||
GTableColumnModel columnModel = (GTableColumnModel) table.getColumnModel();
|
||||
runSwing(() -> columnModel.setVisible(tableColumn, true));
|
||||
}
|
||||
|
||||
private TableColumn getHiddenTableColumn(String name) {
|
||||
GTableColumnModel columnModel = (GTableColumnModel) table.getColumnModel();
|
||||
TableColumn tableColumn = runSwing(() -> {
|
||||
List<TableColumn> allColumns = columnModel.getAllColumns();
|
||||
for (TableColumn column : allColumns) {
|
||||
Object headerValue = column.getHeaderValue();
|
||||
if (headerValue.equals(name)) {
|
||||
return column;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
assertNotNull(tableColumn);
|
||||
|
||||
boolean isVisible = runSwing(() -> {
|
||||
int index = tableColumn.getModelIndex();
|
||||
return columnModel.isVisible(index);
|
||||
});
|
||||
assertFalse(isVisible);
|
||||
return tableColumn;
|
||||
}
|
||||
|
||||
private DynamicTableColumn<DirData, ?, ?> getColumn(String name) {
|
||||
int count = model.getColumnCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
DynamicTableColumn<DirData, ?, ?> column = model.getColumn(i);
|
||||
String columnName = column.getColumnName();
|
||||
if (columnName.equals(name)) {
|
||||
return column;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void assertColumns(String... expectedNames) {
|
||||
for (String expectedName : expectedNames) {
|
||||
DynamicTableColumn<DirData, ?, ?> column = getColumn(expectedName);
|
||||
assertNotNull("Column not found in model - " + expectedName, column);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertColumnPresent(DynamicTableColumn<DirData, ?, ?> column) {
|
||||
int count = model.getColumnCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
package docking.widgets.table;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class TableColumnDescriptor<ROW_TYPE> {
|
||||
|
@ -106,6 +109,99 @@ public class TableColumnDescriptor<ROW_TYPE> {
|
|||
columns.add(new TableColumnInfo(column, true, sortOrdinal, ascending));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a column to the descriptor via an anonymous accessor function instead.
|
||||
* <P>
|
||||
* If you would like to control the sorting behavior of your column, then use
|
||||
* {@link #addVisibleColumn(String, Class, Function, int, boolean)}.
|
||||
* <P>
|
||||
* Note: any columns created via this method will not be discoverable by other tables. To use
|
||||
* that feature, you must create a separate column class that extends
|
||||
* {@link DynamicTableColumnExtensionPoint}.
|
||||
*
|
||||
* @param name the column name, visible in the UI
|
||||
* @param columnTypeClass the column class type
|
||||
* @param rowToColumnFunction a function to convert a row object to the column object
|
||||
* @param <COLUMN_TYPE> the column type
|
||||
*/
|
||||
public <COLUMN_TYPE> void addVisibleColumn(String name, Class<COLUMN_TYPE> columnTypeClass,
|
||||
Function<ROW_TYPE, COLUMN_TYPE> rowToColumnFunction) {
|
||||
addVisibleColumn(name, columnTypeClass, rowToColumnFunction, -1, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a column to the descriptor via an anonymous accessor function instead.
|
||||
* <P>
|
||||
* Note: any columns created via this method will not be discoverable by other tables. To use
|
||||
* that feature, you must create a separate column class that extends
|
||||
* {@link DynamicTableColumnExtensionPoint}.
|
||||
*
|
||||
* @param name the column name, visible in the UI
|
||||
* @param columnTypeClass the column class type
|
||||
* @param rowToColumnFunction a function to convert a row object to the column object
|
||||
* @param sortOrdinal the <b>ordinal (i.e., 1, 2, 3...n)</b>, not the index (i.e, 0, 1, 2...n)
|
||||
* @param ascending true for sort ascending; false for descending
|
||||
* @param <COLUMN_TYPE> the column type
|
||||
*/
|
||||
public <COLUMN_TYPE> void addVisibleColumn(String name, Class<COLUMN_TYPE> columnTypeClass,
|
||||
Function<ROW_TYPE, COLUMN_TYPE> rowToColumnFunction, int sortOrdinal,
|
||||
boolean ascending) {
|
||||
|
||||
AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> column =
|
||||
createColumnStub(name, columnTypeClass, rowToColumnFunction);
|
||||
addVisibleColumn(column, sortOrdinal, ascending);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a column to the descriptor via an anonymous accessor function instead. The column added
|
||||
* will not be displayed until enabled by the user.
|
||||
* <P>
|
||||
* Note: any columns created via this method will not be discoverable by other tables. To use
|
||||
* that feature, you must create a separate column class that extends
|
||||
* {@link DynamicTableColumnExtensionPoint}.
|
||||
*
|
||||
* @param name the column name, visible in the UI
|
||||
* @param columnTypeClass the column class type
|
||||
* @param rowToColumnFunction a function to convert a row object to the column object
|
||||
* @param <COLUMN_TYPE> the column type
|
||||
*/
|
||||
public <COLUMN_TYPE> void addHiddenColumn(String name, Class<COLUMN_TYPE> columnTypeClass,
|
||||
Function<ROW_TYPE, COLUMN_TYPE> rowToColumnFunction) {
|
||||
|
||||
AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> column =
|
||||
createColumnStub(name, columnTypeClass, rowToColumnFunction);
|
||||
addHiddenColumn(column);
|
||||
}
|
||||
|
||||
private <COLUMN_TYPE> AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> createColumnStub(
|
||||
String name, Class<COLUMN_TYPE> columnTypeClass,
|
||||
Function<ROW_TYPE, COLUMN_TYPE> rowToColumnFunction) {
|
||||
|
||||
return new AbstractDynamicTableColumn<>() {
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public COLUMN_TYPE getValue(ROW_TYPE rowObject, Settings settings, Object data,
|
||||
ServiceProvider serviceProvider) throws IllegalArgumentException {
|
||||
return rowToColumnFunction.apply(rowObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<COLUMN_TYPE> getColumnClass() {
|
||||
return columnTypeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ROW_TYPE> getSupportedRowType() {
|
||||
// returning null means this column will not be available to use in other tables
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class TableColumnInfo implements Comparable<TableColumnInfo> {
|
||||
private DynamicTableColumn<ROW_TYPE, ?, ?> column;
|
||||
private boolean isVisible = false;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue