mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-3755 - Added filter to the Memory Map provider
This commit is contained in:
parent
b261137b40
commit
6afed33ce0
7 changed files with 276 additions and 281 deletions
|
@ -15,30 +15,33 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.memory;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.dialogs.NumberInputDialog;
|
||||
import docking.widgets.table.AbstractSortedTableModel;
|
||||
|
||||
/**
|
||||
* Table Model for a Table where each entry represents a MemoryBlock
|
||||
* from a Program's Memory.
|
||||
*/
|
||||
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.table.ProgramTableModel;
|
||||
|
||||
class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
||||
/**
|
||||
* Table Model for a Table where each entry represents a MemoryBlock from a Program's Memory.
|
||||
*/
|
||||
class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> implements ProgramTableModel {
|
||||
|
||||
final static byte NAME = 0;
|
||||
final static byte START = 1;
|
||||
|
@ -70,9 +73,11 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
final static String SOURCE_COL = "Source";
|
||||
final static String COMMENT_COL = "Comment";
|
||||
|
||||
private Program program;
|
||||
private final static Cursor WAIT_CURSOR = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
|
||||
private final static Cursor DEFAULT_CURSOR = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
|
||||
|
||||
private ArrayList<MemoryBlock> memList;
|
||||
private Program program;
|
||||
private List<MemoryBlock> memList;
|
||||
private MemoryMapProvider provider;
|
||||
|
||||
private final static String COLUMN_NAMES[] =
|
||||
|
@ -81,9 +86,12 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
|
||||
MemoryMapModel(MemoryMapProvider provider, Program program) {
|
||||
super(START);
|
||||
this.program = program;
|
||||
this.provider = provider;
|
||||
setProgram(program);
|
||||
}
|
||||
|
||||
void setProgram(Program program) {
|
||||
this.program = program;
|
||||
populateMap();
|
||||
}
|
||||
|
||||
|
@ -91,6 +99,7 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
memList = new ArrayList<>();
|
||||
|
||||
if (program == null) {
|
||||
fireTableDataChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -141,12 +150,6 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
return COLUMN_NAMES[column];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for locating columns by name.
|
||||
* Implementation is naive so this should be overridden if
|
||||
* this method is to be called often. This method is not
|
||||
* in the TableModel interface and is not used by the JTable.
|
||||
*/
|
||||
@Override
|
||||
public int findColumn(String columnName) {
|
||||
for (int i = 0; i < COLUMN_NAMES.length; i++) {
|
||||
|
@ -157,9 +160,6 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Object.class by default
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
if (columnIndex == READ || columnIndex == WRITE || columnIndex == EXECUTE ||
|
||||
|
@ -169,9 +169,6 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
return String.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this column is editable.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
|
||||
|
@ -195,15 +192,6 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of records managed by the data source object. A
|
||||
* <B>JTable</B> uses this method to determine how many rows it
|
||||
* should create and display. This method should be quick, as it
|
||||
* is call by <B>JTable</B> quite frequently.
|
||||
*
|
||||
* @return the number or rows in the model
|
||||
* @see #getColumnCount
|
||||
*/
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return memList.size();
|
||||
|
@ -226,157 +214,134 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
if (rowIndex < 0 || rowIndex >= memList.size()) {
|
||||
return null;
|
||||
}
|
||||
MemoryBlock block = memList.get(rowIndex);
|
||||
try {
|
||||
// make sure block is still valid
|
||||
block.getStart();
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
update();
|
||||
}
|
||||
return memList.get(rowIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This empty implementation is provided so users don't have to implement
|
||||
* this method if their data model is not editable.
|
||||
*/
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
provider.setCursor(MemoryMapPlugin.WAIT_CURSOR);
|
||||
public void setValueAt(Object aValue, int row, int column) {
|
||||
provider.setCursor(WAIT_CURSOR);
|
||||
try {
|
||||
|
||||
MemoryBlock block = getBlockAt(rowIndex);
|
||||
MemoryBlock block = getBlockAt(row);
|
||||
if (block == null) {
|
||||
// this can happen when the tool is closing while an edit is open
|
||||
return;
|
||||
}
|
||||
|
||||
switch (columnIndex) {
|
||||
case NAME:
|
||||
String name = ((String) aValue).trim();
|
||||
if (!verifyRenameAllowed(block, name)) {
|
||||
return;
|
||||
}
|
||||
if (name.length() == 0) {
|
||||
Msg.showError(this, provider.getComponent(), "Enter Block Label",
|
||||
"Please enter a label name.");
|
||||
break;
|
||||
}
|
||||
if (name.equals(block.getName())) {
|
||||
break;
|
||||
}
|
||||
if (!Memory.isValidMemoryBlockName(name)) {
|
||||
Msg.showError(this, provider.getComponent(), "Invalid Name",
|
||||
"Invalid Memory Block Name: " + name);
|
||||
break;
|
||||
}
|
||||
if (!name.equals(block.getName())) {
|
||||
|
||||
program.withTransaction("Rename Memory Block", () -> {
|
||||
try {
|
||||
block.setName(name);
|
||||
}
|
||||
catch (LockException e) {
|
||||
provider.setStatusText(e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
break;
|
||||
case READ: {
|
||||
|
||||
program.withTransaction("Set Read State", () -> {
|
||||
boolean value = ((Boolean) aValue).booleanValue();
|
||||
block.setRead(value);
|
||||
provider.setStatusText("");
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case WRITE: {
|
||||
|
||||
program.withTransaction("Set Write State", () -> {
|
||||
boolean value = ((Boolean) aValue).booleanValue();
|
||||
block.setWrite(value);
|
||||
provider.setStatusText("");
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case EXECUTE: {
|
||||
int id = program.startTransaction("Set Execute State");
|
||||
try {
|
||||
boolean value = ((Boolean) aValue).booleanValue();
|
||||
block.setExecute(value);
|
||||
provider.setStatusText("");
|
||||
program.endTransaction(id, true);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
program.endTransaction(id, false);
|
||||
throw e;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VOLATILE: {
|
||||
int id = program.startTransaction("Set Volatile State");
|
||||
try {
|
||||
boolean value = ((Boolean) aValue).booleanValue();
|
||||
block.setVolatile(value);
|
||||
provider.setStatusText("");
|
||||
program.endTransaction(id, true);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
program.endTransaction(id, false);
|
||||
throw e;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INIT:
|
||||
MemoryBlockType blockType = block.getType();
|
||||
if (blockType == MemoryBlockType.BIT_MAPPED ||
|
||||
blockType == MemoryBlockType.BYTE_MAPPED) {
|
||||
|
||||
showMessage("Cannot change intialized memory state of a mapped Block");
|
||||
return;
|
||||
}
|
||||
provider.setStatusText("");
|
||||
boolean booleanValue = ((Boolean) aValue).booleanValue();
|
||||
if (booleanValue) {
|
||||
initializeBlock(block);
|
||||
}
|
||||
else {
|
||||
revertBlockToUninitialized(block);
|
||||
}
|
||||
return;
|
||||
|
||||
case SOURCE:
|
||||
break;
|
||||
case COMMENT:
|
||||
String cmt = block.getComment();
|
||||
if (cmt == null || !cmt.equals(aValue)) {
|
||||
String value = (String) aValue;
|
||||
if (value.length() == 0) {
|
||||
value = null;
|
||||
}
|
||||
int id = program.startTransaction("Set Comment State");
|
||||
try {
|
||||
block.setComment(value);
|
||||
program.endTransaction(id, true);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
program.endTransaction(id, false);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fireTableRowsUpdated(rowIndex, rowIndex);
|
||||
doSetValueAt(aValue, row, column, block);
|
||||
}
|
||||
finally {
|
||||
provider.setCursor(MemoryMapPlugin.NORM_CURSOR);
|
||||
provider.setCursor(DEFAULT_CURSOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void doSetValueAt(Object aValue, int row, int column, MemoryBlock block) {
|
||||
|
||||
switch (column) {
|
||||
case NAME:
|
||||
setName(block, (String) aValue);
|
||||
break;
|
||||
case READ: {
|
||||
program.withTransaction("Set Read State", () -> {
|
||||
boolean value = ((Boolean) aValue).booleanValue();
|
||||
block.setRead(value);
|
||||
provider.setStatusText("");
|
||||
});
|
||||
break;
|
||||
}
|
||||
case WRITE: {
|
||||
program.withTransaction("Set Write State", () -> {
|
||||
boolean value = ((Boolean) aValue).booleanValue();
|
||||
block.setWrite(value);
|
||||
provider.setStatusText("");
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EXECUTE: {
|
||||
program.withTransaction("Set Execute State", () -> {
|
||||
boolean value = ((Boolean) aValue).booleanValue();
|
||||
block.setExecute(value);
|
||||
provider.setStatusText("");
|
||||
});
|
||||
break;
|
||||
}
|
||||
case VOLATILE: {
|
||||
program.withTransaction("Set Volatile State", () -> {
|
||||
boolean value = ((Boolean) aValue).booleanValue();
|
||||
block.setVolatile(value);
|
||||
provider.setStatusText("");
|
||||
});
|
||||
break;
|
||||
}
|
||||
case INIT:
|
||||
MemoryBlockType blockType = block.getType();
|
||||
if (blockType == MemoryBlockType.BIT_MAPPED ||
|
||||
blockType == MemoryBlockType.BYTE_MAPPED) {
|
||||
showMessage("Cannot change intialized memory state of a mapped Block");
|
||||
break;
|
||||
}
|
||||
provider.setStatusText("");
|
||||
boolean booleanValue = ((Boolean) aValue).booleanValue();
|
||||
if (booleanValue) {
|
||||
initializeBlock(block);
|
||||
}
|
||||
else {
|
||||
revertBlockToUninitialized(block);
|
||||
}
|
||||
break;
|
||||
case SOURCE:
|
||||
break;
|
||||
case COMMENT:
|
||||
setComment(block, (String) aValue);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fireTableRowsUpdated(row, column);
|
||||
}
|
||||
|
||||
private void setComment(MemoryBlock block, String aValue) {
|
||||
|
||||
String cmt = block.getComment();
|
||||
if (cmt == null || !cmt.equals(aValue)) {
|
||||
if (aValue.length() == 0) {
|
||||
aValue = null;
|
||||
}
|
||||
|
||||
String newValue = aValue;
|
||||
program.withTransaction("Set Comment", () -> {
|
||||
block.setComment(newValue);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setName(MemoryBlock block, String name) {
|
||||
name = name.trim();
|
||||
if (!verifyRenameAllowed(block, name)) {
|
||||
return;
|
||||
}
|
||||
if (name.length() == 0) {
|
||||
Msg.showError(this, provider.getComponent(), "Enter Block Label",
|
||||
"Please enter a label name.");
|
||||
return;
|
||||
}
|
||||
if (name.equals(block.getName())) {
|
||||
return;
|
||||
}
|
||||
if (!Memory.isValidMemoryBlockName(name)) {
|
||||
Msg.showError(this, provider.getComponent(), "Invalid Name",
|
||||
"Invalid Memory Block Name: " + name);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String newName = name;
|
||||
program.withTransaction("Rename Memory Block", () -> {
|
||||
block.setName(newName);
|
||||
});
|
||||
}
|
||||
catch (LockException e) {
|
||||
this.provider.setStatusText(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,15 +403,14 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
}
|
||||
catch (Throwable t) {
|
||||
program.endTransaction(id, false);
|
||||
String msg = t.getMessage();
|
||||
msg = msg == null ? t.toString() : msg;
|
||||
String msg = ExceptionUtils.getMessage(t);
|
||||
Msg.showError(this, provider.getComponent(), "Block Initialization Failed", msg, t);
|
||||
}
|
||||
}
|
||||
|
||||
private void showMessage(final String msg) {
|
||||
private void showMessage(String msg) {
|
||||
// mouse listeners wipe out the message so show it later...
|
||||
SwingUtilities.invokeLater(() -> provider.setStatusText(msg));
|
||||
Swing.runLater(() -> provider.setStatusText(msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -530,6 +494,44 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
return new MemoryMapComparator(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(int row, int column) {
|
||||
|
||||
MemoryBlock block = getRowObject(row);
|
||||
Address address = block.getStart();
|
||||
if (column == END) {
|
||||
address = block.getEnd();
|
||||
}
|
||||
|
||||
return new ProgramLocation(program, address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getProgramSelection(int[] rows) {
|
||||
|
||||
if (rows.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AddressSet addressSet = new AddressSet();
|
||||
for (int row : rows) {
|
||||
|
||||
MemoryBlock block = getRowObject(row);
|
||||
Address start = block.getStart();
|
||||
Address end = block.getEnd();
|
||||
|
||||
if (start.isMemoryAddress() && end.isMemoryAddress()) {
|
||||
addressSet.addRange(start, end);
|
||||
}
|
||||
}
|
||||
return new ProgramSelection(addressSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
private class MemoryMapComparator implements Comparator<MemoryBlock> {
|
||||
private final int sortColumn;
|
||||
|
||||
|
@ -607,4 +609,5 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.memory;
|
||||
|
||||
import java.awt.Cursor;
|
||||
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
|
@ -51,9 +49,6 @@ import ghidra.program.util.ProgramLocation;
|
|||
//@formatter:on
|
||||
public class MemoryMapPlugin extends ProgramPlugin implements DomainObjectListener {
|
||||
|
||||
final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR);
|
||||
final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
|
||||
|
||||
private MemoryMapProvider provider;
|
||||
private GoToService goToService;
|
||||
private MemoryMapManager memManager;
|
||||
|
|
|
@ -26,7 +26,6 @@ import javax.swing.table.TableModel;
|
|||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.textfield.GValidatedTextField.MaxLengthField;
|
||||
import generic.theme.GIcon;
|
||||
|
@ -41,18 +40,19 @@ import ghidra.program.model.mem.MemoryBlockType;
|
|||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import ghidra.util.table.GhidraTableFilterPanel;
|
||||
import ghidra.util.table.actions.MakeProgramSelectionAction;
|
||||
|
||||
/**
|
||||
* Provider for the memory map Component.
|
||||
*
|
||||
*/
|
||||
class MemoryMapProvider extends ComponentProviderAdapter {
|
||||
private final static int MAX_SIZE = 256;
|
||||
|
||||
private JPanel mainPanel;
|
||||
private GTable memTable;
|
||||
private JScrollPane memPane;
|
||||
private MemoryMapModel mapModel;
|
||||
private MemoryMapModel tableModel;
|
||||
private GhidraTable table;
|
||||
private GTableFilterPanel<MemoryBlock> filterPanel;
|
||||
|
||||
private DockingAction addAction;
|
||||
private DockingAction moveAction;
|
||||
|
@ -84,6 +84,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
@Override
|
||||
public void componentShown() {
|
||||
updateMap();
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,7 +97,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
return new ProgramActionContext(this, program, table);
|
||||
}
|
||||
|
||||
void setStatusText(String msg) {
|
||||
|
@ -105,7 +106,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
|
||||
void dispose() {
|
||||
removeFromTool();
|
||||
memTable.dispose();
|
||||
filterPanel.dispose();
|
||||
plugin = null;
|
||||
program = null;
|
||||
tool = null;
|
||||
|
@ -113,7 +114,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
|
||||
void setProgram(Program program) {
|
||||
this.program = program;
|
||||
updateMap(program);
|
||||
updateProgram(program);
|
||||
arrangeTable();
|
||||
}
|
||||
|
||||
|
@ -121,69 +122,67 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
return memManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Main Panel for the Memory Map Dialog
|
||||
*/
|
||||
private JPanel buildMainPanel() {
|
||||
JPanel memPanel = new JPanel(new BorderLayout());
|
||||
mapModel = new MemoryMapModel(this, null);
|
||||
memTable = new MemoryMapTable(mapModel);
|
||||
tableModel = new MemoryMapModel(this, null);
|
||||
table = new MemoryMapTable(tableModel);
|
||||
filterPanel = new GhidraTableFilterPanel<>(table, tableModel);
|
||||
|
||||
memTable.setAutoCreateColumnsFromModel(false);
|
||||
table.setAutoCreateColumnsFromModel(false);
|
||||
|
||||
GTableCellRenderer monoRenderer = new GTableCellRenderer();
|
||||
monoRenderer.setFont(monoRenderer.getFixedWidthFont());
|
||||
|
||||
TableColumn column = memTable.getColumn(MemoryMapModel.START_COL);
|
||||
TableColumn column = table.getColumn(MemoryMapModel.START_COL);
|
||||
column.setCellRenderer(monoRenderer);
|
||||
column = memTable.getColumn(MemoryMapModel.END_COL);
|
||||
column = table.getColumn(MemoryMapModel.END_COL);
|
||||
column.setCellRenderer(monoRenderer);
|
||||
column = memTable.getColumn(MemoryMapModel.LENGTH_COL);
|
||||
column = table.getColumn(MemoryMapModel.LENGTH_COL);
|
||||
column.setCellRenderer(monoRenderer);
|
||||
|
||||
column = memTable.getColumn(MemoryMapModel.READ_COL);
|
||||
column = table.getColumn(MemoryMapModel.READ_COL);
|
||||
column.setCellRenderer(new GBooleanCellRenderer());
|
||||
column = memTable.getColumn(MemoryMapModel.WRITE_COL);
|
||||
column = table.getColumn(MemoryMapModel.WRITE_COL);
|
||||
column.setCellRenderer(new GBooleanCellRenderer());
|
||||
column = memTable.getColumn(MemoryMapModel.EXECUTE_COL);
|
||||
column = table.getColumn(MemoryMapModel.EXECUTE_COL);
|
||||
column.setCellRenderer(new GBooleanCellRenderer());
|
||||
column = memTable.getColumn(MemoryMapModel.VOLATILE_COL);
|
||||
column = table.getColumn(MemoryMapModel.VOLATILE_COL);
|
||||
column.setCellRenderer(new GBooleanCellRenderer());
|
||||
column = memTable.getColumn(MemoryMapModel.OVERLAY_COL);
|
||||
column = table.getColumn(MemoryMapModel.OVERLAY_COL);
|
||||
column.setCellRenderer(new GBooleanCellRenderer());
|
||||
column = memTable.getColumn(MemoryMapModel.INIT_COL);
|
||||
column = table.getColumn(MemoryMapModel.INIT_COL);
|
||||
column.setCellRenderer(new GBooleanCellRenderer());
|
||||
|
||||
memTable.setDefaultEditor(String.class,
|
||||
table.setDefaultEditor(String.class,
|
||||
new GTableTextCellEditor(new MaxLengthField(MAX_SIZE)));
|
||||
|
||||
memPane = new JScrollPane(memTable);
|
||||
memTable.setPreferredScrollableViewportSize(new Dimension(700, 105));
|
||||
table.setPreferredScrollableViewportSize(new Dimension(700, 105));
|
||||
|
||||
memTable.addMouseListener(new MouseHandler());
|
||||
table.addMouseListener(new MouseHandler());
|
||||
|
||||
memTable.addKeyListener(new KeyHandler());
|
||||
table.addKeyListener(new KeyHandler());
|
||||
|
||||
memTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
ListSelectionModel lsm = memTable.getSelectionModel();
|
||||
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
ListSelectionModel lsm = table.getSelectionModel();
|
||||
|
||||
lsm.addListSelectionListener(e -> {
|
||||
// Ignore extra messages.
|
||||
if (e.getValueIsAdjusting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ListSelectionModel model = (ListSelectionModel) e.getSource();
|
||||
enableOptions(model);
|
||||
contextChanged();
|
||||
});
|
||||
|
||||
memPanel.add(new GLabel("Memory Blocks", SwingConstants.CENTER), BorderLayout.NORTH);
|
||||
memPanel.add(memPane, BorderLayout.CENTER);
|
||||
memPanel.add(new JScrollPane(table), BorderLayout.CENTER);
|
||||
memPanel.add(filterPanel, BorderLayout.SOUTH);
|
||||
|
||||
return memPanel;
|
||||
}
|
||||
|
||||
private void addLocalActions() {
|
||||
|
||||
Icon addImage = new GIcon("icon.plugin.memorymap.add");
|
||||
|
||||
addAction = new MemoryMapAction("Add Block", addImage) {
|
||||
|
@ -294,6 +293,11 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
|
||||
setBaseAction.setDescription("Set Image Base");
|
||||
tool.addLocalAction(this, setBaseAction);
|
||||
|
||||
MakeProgramSelectionAction action =
|
||||
new MakeProgramSelectionAction(plugin, table);
|
||||
action.getToolBarData().setToolBarGroup("B"); // the other actions are in group 'A'
|
||||
tool.addLocalAction(this, action);
|
||||
}
|
||||
|
||||
private void setBase() {
|
||||
|
@ -332,12 +336,6 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable the expand up/down actions according to the selected
|
||||
* block.
|
||||
*
|
||||
* @param numSelected number of blocks selected
|
||||
*/
|
||||
private void enableExpandActions(int numSelected) {
|
||||
if (numSelected != 1) {
|
||||
expandUpAction.setEnabled(false);
|
||||
|
@ -368,7 +366,11 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
JTable getTable() {
|
||||
return memTable;
|
||||
return table;
|
||||
}
|
||||
|
||||
MemoryMapModel getModel() {
|
||||
return tableModel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -376,7 +378,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
*/
|
||||
void updateMap() {
|
||||
if (isVisible()) {
|
||||
mapModel.update();
|
||||
tableModel.update();
|
||||
arrangeTable();
|
||||
updateTitle();
|
||||
}
|
||||
|
@ -385,7 +387,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
void updateData() {
|
||||
if (isVisible()) {
|
||||
updateTitle();
|
||||
memTable.repaint();
|
||||
table.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,9 +400,9 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
/**
|
||||
* Update the memory map with the new program's memory
|
||||
*/
|
||||
private void updateMap(Program updateProgram) {
|
||||
private void updateProgram(Program updatedProgram) {
|
||||
enableOptions(null);
|
||||
if (updateProgram == null) {
|
||||
if (updatedProgram == null) {
|
||||
addAction.setEnabled(false);
|
||||
setBaseAction.setEnabled(false);
|
||||
}
|
||||
|
@ -408,8 +410,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
setBaseAction.setEnabled(true);
|
||||
}
|
||||
|
||||
mapModel = new MemoryMapModel(this, updateProgram);
|
||||
memTable.setModel(mapModel);
|
||||
tableModel.setProgram(updatedProgram);
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
|
@ -420,48 +421,48 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
// memTable.setRowHeight(20);
|
||||
TableColumn column;
|
||||
|
||||
column = memTable.getColumn(MemoryMapModel.READ_COL);
|
||||
column = table.getColumn(MemoryMapModel.READ_COL);
|
||||
if (column != null) {
|
||||
column.setMaxWidth(25);
|
||||
column.setMinWidth(25);
|
||||
column.setResizable(false);
|
||||
}
|
||||
|
||||
column = memTable.getColumn(MemoryMapModel.WRITE_COL);
|
||||
column = table.getColumn(MemoryMapModel.WRITE_COL);
|
||||
if (column != null) {
|
||||
column.setMaxWidth(25);
|
||||
column.setMinWidth(25);
|
||||
column.setResizable(false);
|
||||
}
|
||||
|
||||
column = memTable.getColumn(MemoryMapModel.EXECUTE_COL);
|
||||
column = table.getColumn(MemoryMapModel.EXECUTE_COL);
|
||||
if (column != null) {
|
||||
column.setMaxWidth(25);
|
||||
column.setMinWidth(25);
|
||||
column.setResizable(false);
|
||||
}
|
||||
|
||||
column = memTable.getColumn(MemoryMapModel.VOLATILE_COL);
|
||||
column = table.getColumn(MemoryMapModel.VOLATILE_COL);
|
||||
if (column != null) {
|
||||
column.setMaxWidth(65);
|
||||
column.setMinWidth(65);
|
||||
column.setResizable(false);
|
||||
}
|
||||
|
||||
column = memTable.getColumn(MemoryMapModel.OVERLAY_COL);
|
||||
column = table.getColumn(MemoryMapModel.OVERLAY_COL);
|
||||
if (column != null) {
|
||||
column.setMaxWidth(65);
|
||||
column.setMinWidth(65);
|
||||
column.setResizable(false);
|
||||
}
|
||||
|
||||
column = memTable.getColumn(MemoryMapModel.BLOCK_TYPE_COL);
|
||||
column = table.getColumn(MemoryMapModel.BLOCK_TYPE_COL);
|
||||
if (column != null) {
|
||||
column.setMinWidth(60);
|
||||
// column.setResizable(true);
|
||||
}
|
||||
|
||||
column = memTable.getColumn(MemoryMapModel.INIT_COL);
|
||||
column = table.getColumn(MemoryMapModel.INIT_COL);
|
||||
if (column != null) {
|
||||
column.setMaxWidth(80);
|
||||
column.setMinWidth(80);
|
||||
|
@ -484,7 +485,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
if (!e.isPopupTrigger()) {
|
||||
if ((e.getModifiersEx() &
|
||||
(InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)) == 0) {
|
||||
selectAddress();
|
||||
navigateToAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -499,30 +500,30 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
selectAddress();
|
||||
navigateToAddress();
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void selectAddress() {
|
||||
int row = memTable.getSelectedRow();
|
||||
int viewColumn = memTable.getSelectedColumn();
|
||||
int col = memTable.convertColumnIndexToModel(viewColumn);
|
||||
MemoryBlock block = mapModel.getBlockAt(row);
|
||||
if (block != null && (col == 1 || col == 2)) {
|
||||
Address addr = (col == 1 ? block.getStart() : block.getEnd());
|
||||
private void navigateToAddress() {
|
||||
int viewRow = table.getSelectedRow();
|
||||
int viewColumn = table.getSelectedColumn();
|
||||
int modelColumn = table.convertColumnIndexToModel(viewColumn);
|
||||
MemoryBlock block = tableModel.getBlockAt(viewRow);
|
||||
if (block != null && (modelColumn == 1 || modelColumn == 2)) {
|
||||
Address addr = (modelColumn == 1 ? block.getStart() : block.getEnd());
|
||||
plugin.blockSelected(block, addr);
|
||||
memTable.setRowSelectionInterval(row, row);
|
||||
table.setRowSelectionInterval(viewRow, viewRow);
|
||||
}
|
||||
}
|
||||
|
||||
private MemoryBlock getSelectedBlock() {
|
||||
int row = memTable.getSelectedRow();
|
||||
int row = table.getSelectedRow();
|
||||
if (row < 0) {
|
||||
return null;
|
||||
}
|
||||
return mapModel.getBlockAt(row);
|
||||
return tableModel.getBlockAt(row);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -534,12 +535,12 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
return;
|
||||
}
|
||||
ArrayList<MemoryBlock> delBlocks = new ArrayList<>();
|
||||
int delRows[] = memTable.getSelectedRows();
|
||||
int delRows[] = table.getSelectedRows();
|
||||
for (int element : delRows) {
|
||||
MemoryBlock block = mapModel.getBlockAt(element);
|
||||
MemoryBlock block = tableModel.getBlockAt(element);
|
||||
delBlocks.add(block);
|
||||
}
|
||||
memTable.clearSelection();
|
||||
table.clearSelection();
|
||||
deleteBlock(delBlocks);
|
||||
}
|
||||
|
||||
|
@ -642,12 +643,12 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
*/
|
||||
private void mergeBlocks() {
|
||||
ArrayList<MemoryBlock> blocks = new ArrayList<>();
|
||||
int rows[] = memTable.getSelectedRows();
|
||||
int rows[] = table.getSelectedRows();
|
||||
for (int element : rows) {
|
||||
MemoryBlock block = mapModel.getBlockAt(element);
|
||||
MemoryBlock block = tableModel.getBlockAt(element);
|
||||
blocks.add(block);
|
||||
}
|
||||
memTable.clearSelection();
|
||||
table.clearSelection();
|
||||
memManager.mergeBlocks(blocks);
|
||||
}
|
||||
|
||||
|
@ -670,9 +671,9 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
return plugin.getTool();
|
||||
}
|
||||
|
||||
// ==================================================================================================
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
// ==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
private class MemoryMapTable extends GhidraTable {
|
||||
MemoryMapTable(TableModel model) {
|
||||
|
@ -691,7 +692,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
private abstract class MemoryMapAction extends DockingAction {
|
||||
MemoryMapAction(String name, Icon icon) {
|
||||
super(name, plugin.getName());
|
||||
this.setToolBarData(new ToolBarData(icon, null));
|
||||
this.setToolBarData(new ToolBarData(icon, "A"));
|
||||
}
|
||||
|
||||
public boolean checkExclusiveAccess() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -21,31 +20,28 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
/**
|
||||
* An interface for translating table rows and columns
|
||||
* into program locations and selections.
|
||||
* An interface for translating table rows and columns into program locations and selections.
|
||||
*/
|
||||
public interface ProgramTableModel {
|
||||
/**
|
||||
* Returns a program location corresponding the given row and column.
|
||||
* Motivation:
|
||||
* Given a table that has a column that contains addresses.
|
||||
* If the user clicks on this column, then it would be nice
|
||||
* to have the CodeBrowser navigate to this address.
|
||||
* @param row the row
|
||||
* @param column the column
|
||||
* @return a program location corresponding the given row and column
|
||||
*/
|
||||
public ProgramLocation getProgramLocation(int row, int column);
|
||||
/**
|
||||
* Returns a program location corresponding the given row and column.
|
||||
* <p>
|
||||
* Motivation: Given a table that has a column that contains addresses. If the user clicks on
|
||||
* this column, then it would be nice to have the CodeBrowser navigate to this address.
|
||||
* @param row the row
|
||||
* @param modelColumn the column in the model's index
|
||||
* @return a program location corresponding the given row and column
|
||||
*/
|
||||
public ProgramLocation getProgramLocation(int row, int modelColumn);
|
||||
|
||||
/**
|
||||
* Returns a program selection corresponding to the
|
||||
* specified row index array. This array will contain
|
||||
* the currently selected rows.
|
||||
* Returns a program selection corresponding to the specified row index array. This array will
|
||||
* contain the currently selected rows.
|
||||
* @param rows the currently selected rows.
|
||||
* @return a program selection
|
||||
*/
|
||||
public ProgramSelection getProgramSelection(int[] rows);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the program associated with this ProgramTableModel.
|
||||
* @return the program associated with this ProgramTableModel.
|
||||
|
|
|
@ -673,8 +673,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
|
|||
waitForSwing();
|
||||
provider = plugin.getMemoryMapProvider();
|
||||
table = provider.getTable();
|
||||
model = (MemoryMapModel) table.getModel();
|
||||
|
||||
model = provider.getModel();
|
||||
}
|
||||
|
||||
private Address getAddr(long offset) {
|
||||
|
|
|
@ -109,7 +109,8 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
|||
private static final Icon ICON_HOME = new GIcon("icon.filechooser.places.home");
|
||||
private static final Icon ICON_RECENT = new GIcon("icon.filechooser.places.recent");
|
||||
|
||||
// base and overlay?
|
||||
private final static Cursor WAIT_CURSOR = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
|
||||
private final static Cursor DEFAULT_CURSOR = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
|
||||
|
||||
/** Instruction to display only files. */
|
||||
public static final int FILES_ONLY = 0;
|
||||
|
@ -464,13 +465,13 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
|||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
waitPanel.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
waitPanel.setCursor(WAIT_CURSOR);
|
||||
e.consume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
waitPanel.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
|
||||
waitPanel.setCursor(DEFAULT_CURSOR);
|
||||
}
|
||||
});
|
||||
waitPanel.addMouseMotionListener(new MouseMotionAdapter() {
|
||||
|
|
|
@ -32,8 +32,8 @@ import ghidra.util.task.TaskMonitor;
|
|||
|
||||
public class DataTypeArchiveTransformerPanel extends JPanel {
|
||||
|
||||
private final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR);
|
||||
private final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
|
||||
private final static Cursor WAIT_CURSOR = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
|
||||
private final static Cursor DEFAULT_CURSOR = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
|
||||
|
||||
private JPanel filePanel;
|
||||
private JTextField oldFileTextField;
|
||||
|
@ -85,7 +85,7 @@ public class DataTypeArchiveTransformerPanel extends JPanel {
|
|||
oldBrowseButton.addActionListener(e -> {
|
||||
setCursor(WAIT_CURSOR);
|
||||
File file = chooseFile("Choose old data type archive");
|
||||
setCursor(NORM_CURSOR);
|
||||
setCursor(DEFAULT_CURSOR);
|
||||
if (file != null) {
|
||||
oldFileTextField.setText(file.getAbsolutePath());
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ public class DataTypeArchiveTransformerPanel extends JPanel {
|
|||
newBrowseButton.addActionListener(e -> {
|
||||
setCursor(WAIT_CURSOR);
|
||||
File file = chooseFile("Choose new data type archive");
|
||||
setCursor(NORM_CURSOR);
|
||||
setCursor(DEFAULT_CURSOR);
|
||||
if (file != null) {
|
||||
newFileTextField.setText(file.getAbsolutePath());
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ public class DataTypeArchiveTransformerPanel extends JPanel {
|
|||
destinationBrowseButton.addActionListener(e -> {
|
||||
setCursor(WAIT_CURSOR);
|
||||
File file = chooseFile("Choose destination file");
|
||||
setCursor(NORM_CURSOR);
|
||||
setCursor(DEFAULT_CURSOR);
|
||||
if (file != null) {
|
||||
destinationFileTextField.setText(file.getAbsolutePath());
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue