mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-3226 - Symbol Table - Performance improvements - disable filtering on
some columns; turn on 'Name Only' filtering by default; disable sorting by default; remove use of column mapping where not needed; fixed caching for name column
This commit is contained in:
parent
665a83d6c0
commit
fc67c6aaeb
26 changed files with 687 additions and 403 deletions
Binary file not shown.
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 58 KiB |
|
@ -31,7 +31,7 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<P>The columns in the table are:</P>
|
||||
<P>Some of the columns in the table are:</P>
|
||||
|
||||
<CENTER>
|
||||
<TABLE border="1" width="73%" height="159">
|
||||
|
@ -54,13 +54,6 @@
|
|||
<TD width="89%" height="22">Symbol type (Function, External, Class, etc).</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="11%" height="15"><B>Datatype</B></TD>
|
||||
|
||||
<TD width="89%" height="15">Datatype (i.e., byte, float, etc.) applied at symbol
|
||||
address.</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="11%" height="21"><B>Namespace</B></TD>
|
||||
|
||||
|
@ -89,7 +82,15 @@
|
|||
<P><I><IMG src="../../shared/note.png"> You can sort the table on any column by clicking on
|
||||
the column header. The column can be sorted in ascending or descending order.</I></P>
|
||||
|
||||
<P><I><IMG src="../../shared/tip.png"></I> The colors for <B><FONT color="#ff0000">bad
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="../../shared/warning.png">
|
||||
Sorting a column in the symbol table when the program has a large number of symbols
|
||||
can be slow. If you do not need sorting, then you can control-click the sorted column
|
||||
to remove the sort.
|
||||
</p>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P><IMG src="../../shared/tip.png"> The colors for <B><FONT color="#ff0000">bad
|
||||
references</FONT></B>, <B><FONT color="#ff00ff">entry points</FONT></B>, <B>dead code</B>,
|
||||
<B><FONT color="#c0c0c0">offcut code</FONT></B>, <B><FONT color="#0000ff">function
|
||||
names</FONT></B>, <B><FONT color="#008040">local symbols</FONT></B>, <B><FONT color=
|
||||
|
@ -113,6 +114,15 @@
|
|||
<P>The <I>Name Only</I> checkbox allows you to toggle whether to filter on only the name
|
||||
column or all the columns in the table.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="../../shared/tip.png">
|
||||
Filtering the symbol table when the program has a large number of symbols can be slow.
|
||||
When only filtering on the symbol name, via the checkbox above, the overall filtering
|
||||
is considerably faster.
|
||||
</p>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<P>The filter text field will accept basic globbing characters such as '<B>*</B>' and
|
||||
'<B>?</B>' within the filter text unless the "Regular Expression" filter strategy is
|
||||
selected, in which case you should use standard regular expression syntax.</P>
|
||||
|
|
|
@ -83,7 +83,8 @@
|
|||
<p><img border="0" src="../../shared/tip.png" alt="">
|
||||
To remove a sort column from a multiple column sort, Ctrl-left-click that column.
|
||||
This will even work when only one column is sorted, <b><i>thus effectively disabling
|
||||
sorting for the table</i></b>.
|
||||
sorting for the table</i></b>. <u>Disabling sorting can greatly increase the
|
||||
table's performance when the number of rows is large</u>
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
|
@ -212,5 +213,11 @@
|
|||
<br>
|
||||
<br>
|
||||
|
||||
<P class="relatedtopic">Related Topics</P>
|
||||
|
||||
<UL>
|
||||
<LI><A href="help/topics/Trees/GhidraTreeFilter.html">Table Filtering</A></LI>
|
||||
</UL>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -15,7 +15,11 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<P>Most trees and tables in Ghidra support filtering and have a text filter located at the
|
||||
bottom of the tree or table. Below is an example from the Data Type Manager. The filter is
|
||||
bottom of the tree or table. Tables also support the concept of
|
||||
<A HREF="#Column_Filters">Column Filters</A> described below.
|
||||
</P>
|
||||
|
||||
<P>Below is an example from the Data Type Manager. The filter is
|
||||
currently set to "Starts With", but you can select a different filter strategy.</P>
|
||||
|
||||
<CENTER>
|
||||
|
@ -270,6 +274,17 @@
|
|||
<BLOCKQUOTE>
|
||||
<P>Most filterable tables in Ghidra support advanced filtering based on column values. This
|
||||
allows for complex filtering where you can logically combine column specific clauses.</P>
|
||||
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="../../shared/warning.png">
|
||||
Some columns in tables are not filterable via the filter text field below the table.
|
||||
For example, many numeric columns are ignored by the text filter because they can be
|
||||
slow to calculate and they are better filtered by using a range filter, as is available
|
||||
using column filters.
|
||||
</p>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BR>
|
||||
|
||||
|
||||
|
@ -458,5 +473,20 @@
|
|||
filter.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<!-- extra space at the bottom of the page (for readability) -->
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<P class="relatedtopic">Related Topics</P>
|
||||
|
||||
<UL>
|
||||
<LI><A href="help/topics/Tables/GhidraTableHeaders.html">Table Sorting</A></LI>
|
||||
</UL>
|
||||
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
|
|
@ -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.
|
||||
|
@ -16,12 +15,20 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
||||
class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||
|
||||
private final Function function;
|
||||
private final long key;
|
||||
|
||||
FunctionRowObject(long key) {
|
||||
this.key = key;
|
||||
FunctionRowObject(Function function) {
|
||||
this.function = function;
|
||||
this.key = function.getID();
|
||||
}
|
||||
|
||||
Function getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,15 +41,19 @@ class FunctionRowObject implements Comparable<FunctionRowObject> {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
if (this == obj) {
|
||||
return true;
|
||||
if (obj == null)
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
FunctionRowObject other = (FunctionRowObject) obj;
|
||||
if (key != other.key)
|
||||
if (key != other.key) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,16 +17,17 @@ package ghidra.app.plugin.core.functionwindow;
|
|||
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class FunctionRowObjectToAddressTableRowMapper extends
|
||||
ProgramLocationTableRowMapper<FunctionRowObject, Address> {
|
||||
public class FunctionRowObjectToAddressTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<FunctionRowObject, Address> {
|
||||
|
||||
@Override
|
||||
public Address map(FunctionRowObject rowObject, Program program, ServiceProvider serviceProvider) {
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function function = functionManager.getFunction(rowObject.getKey());
|
||||
public Address map(FunctionRowObject rowObject, Program program,
|
||||
ServiceProvider serviceProvider) {
|
||||
Function function = rowObject.getFunction();
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -16,16 +16,18 @@
|
|||
package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class FunctionRowObjectToFunctionTableRowMapper extends
|
||||
ProgramLocationTableRowMapper<FunctionRowObject, Function> {
|
||||
public class FunctionRowObjectToFunctionTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<FunctionRowObject, Function> {
|
||||
|
||||
@Override
|
||||
public Function map(FunctionRowObject rowObject, Program program, ServiceProvider serviceProvider) {
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
return functionManager.getFunction(rowObject.getKey());
|
||||
public Function map(FunctionRowObject rowObject, Program program,
|
||||
ServiceProvider serviceProvider) {
|
||||
Function function = rowObject.getFunction();
|
||||
return function;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,19 +16,19 @@
|
|||
package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.FunctionSignatureFieldLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class FunctionRowObjectToProgramLocationTableRowMapper extends
|
||||
ProgramLocationTableRowMapper<FunctionRowObject, ProgramLocation> {
|
||||
public class FunctionRowObjectToProgramLocationTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<FunctionRowObject, ProgramLocation> {
|
||||
|
||||
@Override
|
||||
public ProgramLocation map(FunctionRowObject rowObject, Program program,
|
||||
ServiceProvider serviceProvider) {
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function function = functionManager.getFunction(rowObject.getKey());
|
||||
Function function = rowObject.getFunction();
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@ package ghidra.app.plugin.core.functionwindow;
|
|||
|
||||
import docking.widgets.table.DiscoverableTableUtils;
|
||||
import docking.widgets.table.TableColumnDescriptor;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.LongIterator;
|
||||
|
@ -43,20 +45,19 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
|||
|
||||
@Override
|
||||
protected TableColumnDescriptor<FunctionRowObject> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<FunctionRowObject> descriptor =
|
||||
new TableColumnDescriptor<>();
|
||||
TableColumnDescriptor<FunctionRowObject> descriptor = new TableColumnDescriptor<>();
|
||||
|
||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
||||
new LabelTableColumn()));
|
||||
descriptor.addVisibleColumn(new NameTableColumn());
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new AddressTableColumn()), 1, true);
|
||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
||||
new FunctionSignatureTableColumn()));
|
||||
//make function size a default column so that a user who wants to know the function size
|
||||
//won't add the "Byte Count" column (which only display the number of bytes in the code
|
||||
//unit at the function's entry point).
|
||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
||||
new FunctionBodySizeTableColumn()));
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new FunctionSignatureTableColumn()));
|
||||
|
||||
// Make function size a default column so that a user who wants to know the function size
|
||||
// won't add the "Byte Count" column (which only display the number of bytes in the code
|
||||
// unit at the function's entry point).
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new FunctionBodySizeTableColumn()));
|
||||
|
||||
// Function tag column is not something widely used, so make hidden by default
|
||||
descriptor.addHiddenColumn(
|
||||
|
@ -91,13 +92,15 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
|||
if (functionMgr != null) {
|
||||
it = new FunctionKeyIterator(functionMgr);
|
||||
}
|
||||
|
||||
monitor.initialize(getKeyCount());
|
||||
int progress = 0;
|
||||
while (it.hasNext()) {
|
||||
monitor.setProgress(progress++);
|
||||
monitor.checkCanceled();
|
||||
long key = it.next();
|
||||
accumulator.add(new FunctionRowObject(key));
|
||||
Function f = functionMgr.getFunction(key);
|
||||
accumulator.add(new FunctionRowObject(f));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,16 +136,16 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
|||
}
|
||||
}
|
||||
|
||||
void functionAdded(Function function) {
|
||||
addObject(new FunctionRowObject(function.getID()));
|
||||
void functionAdded(Function f) {
|
||||
addObject(new FunctionRowObject(f));
|
||||
}
|
||||
|
||||
void functionRemoved(Function function) {
|
||||
removeObject(new FunctionRowObject(function.getID()));
|
||||
void functionRemoved(Function f) {
|
||||
removeObject(new FunctionRowObject(f));
|
||||
}
|
||||
|
||||
void update(Function function) {
|
||||
updateObject(new FunctionRowObject(function.getID()));
|
||||
void update(Function f) {
|
||||
updateObject(new FunctionRowObject(f));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,4 +155,25 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
|||
Function function = functionManager.getFunction(rowObject.getKey());
|
||||
return function != null ? function.getEntryPoint() : null;
|
||||
}
|
||||
|
||||
private class NameTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<FunctionRowObject, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Name";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(FunctionRowObject rowObject, Settings settings, Program data,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
|
||||
Function function = rowObject.getFunction();
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
return function.getName();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -16,19 +15,19 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.symtable;
|
||||
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
class SymbolEditor extends DefaultCellEditor {
|
||||
|
||||
private JTextField symbolField = null;
|
||||
|
||||
SymbolEditor() {
|
||||
super(new JTextField());
|
||||
symbolField = (JTextField)super.getComponent();
|
||||
symbolField = (JTextField) super.getComponent();
|
||||
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
||||
}
|
||||
|
||||
|
@ -38,9 +37,11 @@ class SymbolEditor extends DefaultCellEditor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
|
||||
if (value instanceof Symbol) {
|
||||
Symbol symbol = (Symbol) value;
|
||||
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
|
||||
int row, int column) {
|
||||
if (value instanceof SymbolTableNameValue) {
|
||||
SymbolTableNameValue cellValue = (SymbolTableNameValue) value;
|
||||
Symbol symbol = cellValue.getSymbol();
|
||||
symbolField.setText(symbol.getName());
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -37,7 +37,7 @@ import ghidra.util.table.*;
|
|||
|
||||
class SymbolPanel extends JPanel {
|
||||
|
||||
private static final boolean FILTER_NAME_ONLY_DEFAULT = false;
|
||||
private static final boolean FILTER_NAME_ONLY_DEFAULT = true;
|
||||
|
||||
private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS";
|
||||
|
||||
|
@ -46,7 +46,6 @@ class SymbolPanel extends JPanel {
|
|||
private GhidraTable symTable;
|
||||
private TableModelListener listener;
|
||||
private FilterDialog filterDialog;
|
||||
private PluginTool tool;
|
||||
private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel;
|
||||
private GhidraTableFilterPanel<SymbolRowObject> tableFilterPanel;
|
||||
|
||||
|
@ -54,7 +53,7 @@ class SymbolPanel extends JPanel {
|
|||
final PluginTool tool, GoToService gotoService) {
|
||||
|
||||
super(new BorderLayout());
|
||||
this.tool = tool;
|
||||
|
||||
this.symProvider = provider;
|
||||
this.tableModel = model;
|
||||
|
||||
|
@ -174,15 +173,13 @@ class SymbolPanel extends JPanel {
|
|||
|
||||
if (selectedRowCount == 1) {
|
||||
int selectedRow = symTable.getSelectedRow();
|
||||
Object obj = symTable.getValueAt(selectedRow,
|
||||
symTable.convertColumnIndexToView(SymbolTableModel.LABEL_COL));
|
||||
if (obj instanceof Symbol) {
|
||||
symProvider.setCurrentSymbol((Symbol) obj);
|
||||
return;
|
||||
}
|
||||
Symbol symbol = symProvider.getSymbolForRow(selectedRow);
|
||||
symProvider.setCurrentSymbol(symbol);
|
||||
}
|
||||
else {
|
||||
symProvider.setCurrentSymbol(null);
|
||||
}
|
||||
}
|
||||
|
||||
int getActualSymbolCount() {
|
||||
return symTable.getRowCount();
|
||||
|
@ -212,9 +209,10 @@ class SymbolPanel extends JPanel {
|
|||
@Override
|
||||
public List<String> transform(SymbolRowObject rowObject) {
|
||||
list.clear();
|
||||
Symbol symbol = model.getSymbolForRowObject(rowObject);
|
||||
if (symbol != null) {
|
||||
list.add(symbol.getName());
|
||||
Object value = model.getColumnValueForRow(rowObject, SymbolTableModel.LABEL_COL);
|
||||
if (value != null) {
|
||||
// the toString() returns the value for the symbol, which may be cached
|
||||
list.add(value.toString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
|
|
@ -100,6 +100,10 @@ class SymbolProvider extends ComponentProviderAdapter {
|
|||
return null;
|
||||
}
|
||||
|
||||
Symbol getSymbolForRow(int row) {
|
||||
return symbolKeyModel.getRowObject(row).getSymbol();
|
||||
}
|
||||
|
||||
void setCurrentSymbol(Symbol symbol) {
|
||||
plugin.getReferenceProvider().setCurrentSymbol(symbol);
|
||||
}
|
||||
|
@ -126,9 +130,15 @@ class SymbolProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(long symbolID) {
|
||||
void symbolRemoved(Symbol s) {
|
||||
if (isVisible()) {
|
||||
symbolKeyModel.symbolRemoved(symbolID);
|
||||
symbolKeyModel.symbolRemoved(s);
|
||||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(long symbolId) {
|
||||
if (isVisible()) {
|
||||
symbolKeyModel.symbolRemoved(symbolId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,10 @@ class SymbolRenderer extends GhidraTableCellRenderer {
|
|||
if (value == null && column == SymbolTableModel.LABEL_COL) {
|
||||
setText("<< REMOVED >>");
|
||||
}
|
||||
else if (value instanceof SymbolTableNameValue) {
|
||||
Symbol symbol = ((SymbolTableNameValue) value).getSymbol();
|
||||
handleSymbol(symbol, isSelected);
|
||||
}
|
||||
else if (value instanceof Symbol) {
|
||||
handleSymbol(value, isSelected);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -16,12 +15,28 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.symtable;
|
||||
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
class SymbolRowObject implements Comparable<SymbolRowObject> {
|
||||
|
||||
// symbol can be null after it is deleted
|
||||
private final Symbol symbol;
|
||||
private final long key;
|
||||
|
||||
SymbolRowObject(long key) {
|
||||
this.key = key;
|
||||
SymbolRowObject(Symbol s) {
|
||||
this.symbol = s;
|
||||
this.key = s.getID();
|
||||
}
|
||||
|
||||
// this constructor is used to create a row object to serve as a key for deleting items
|
||||
// in the model after a symbol has been deleted
|
||||
SymbolRowObject(long symbolId) {
|
||||
this.symbol = null;
|
||||
this.key = symbolId;
|
||||
}
|
||||
|
||||
Symbol getSymbol() {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
long getKey() {
|
||||
|
@ -38,15 +53,19 @@ class SymbolRowObject implements Comparable<SymbolRowObject> {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
if (this == obj) {
|
||||
return true;
|
||||
if (obj == null)
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SymbolRowObject other = (SymbolRowObject) obj;
|
||||
if (key != other.key)
|
||||
if (key != other.key) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.symtable;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
|
@ -35,7 +35,6 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.datastruct.LongArrayList;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.table.AddressBasedTableModel;
|
||||
import ghidra.util.table.column.*;
|
||||
|
@ -46,19 +45,11 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
static final int LABEL_COL = 0;
|
||||
static final int LOCATION_COL = 1;
|
||||
static final int TYPE_COL = 2;
|
||||
static final int DATATYPE_COL = 3;
|
||||
static final int DATA_TYPE_COL = 3;
|
||||
static final int NAMESPACE_COL = 4;
|
||||
static final int SOURCE_COL = 5;
|
||||
static final int REFS_COL = 6;
|
||||
|
||||
static final String LABEL_COL_NAME = "Labels";
|
||||
static final String LOCATION_COL_NAME = "Location";
|
||||
static final String TYPE_COL_NAME = "Type";
|
||||
static final String DATATYPE_COL_NAME = "Datatype";
|
||||
static final String REFS_COL_NAME = "# Refs";
|
||||
static final String NAMESPACE_COL_NAME = "Namespace";
|
||||
static final String SOURCE_COL_NAME = "Source";
|
||||
|
||||
private SymbolProvider provider;
|
||||
private PluginTool tool;
|
||||
private SymbolTable symbolTable;
|
||||
|
@ -71,6 +62,9 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
this.provider = provider;
|
||||
this.tool = tool;
|
||||
this.filter = new NewSymbolFilter();
|
||||
|
||||
// leave off default sorting, as this can be slow; the user can sort as desired
|
||||
setDefaultTableSortState(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,9 +73,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
|
||||
descriptor.addVisibleColumn(new NameTableColumn(), 1, true);
|
||||
descriptor.addVisibleColumn(new LocationTableColumn());
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new SymbolTypeTableColumn()));
|
||||
descriptor.addVisibleColumn(new DataTypeTableColumn());
|
||||
descriptor.addVisibleColumn(new SymbolTypeTableColumn());
|
||||
descriptor.addHiddenColumn(new DataTypeTableColumn());
|
||||
descriptor.addVisibleColumn(new NamespaceTableColumn());
|
||||
descriptor.addVisibleColumn(new SourceTableColumn());
|
||||
descriptor.addVisibleColumn(new ReferenceCountTableColumn());
|
||||
|
@ -163,7 +156,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
monitor.checkCanceled();
|
||||
Symbol s = it.next();
|
||||
if (filter.accepts(s, getProgram())) {
|
||||
accumulator.add(new SymbolRowObject(s.getID()));
|
||||
accumulator.add(new SymbolRowObject(s));
|
||||
}
|
||||
}
|
||||
if (filter.acceptsDefaultLabelSymbols()) {
|
||||
|
@ -175,7 +168,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
Address a = addrIt.next();
|
||||
Symbol s = symbolTable.getPrimarySymbol(a);
|
||||
if (s.isDynamic() && filter.accepts(s, getProgram())) {
|
||||
accumulator.add(new SymbolRowObject(s.getID()));
|
||||
accumulator.add(new SymbolRowObject(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,34 +199,25 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
return;
|
||||
}
|
||||
|
||||
switch (columnIndex) {
|
||||
case LABEL_COL:
|
||||
try {
|
||||
if (columnIndex == LABEL_COL) {
|
||||
String newName = aValue.toString();
|
||||
if (!symbol.getName().equals(newName)) {
|
||||
Command renameCmd =
|
||||
new RenameLabelCmd(symbol.getAddress(), symbol.getName(), newName,
|
||||
symbol.getParentNamespace(), SourceType.USER_DEFINED);
|
||||
Command renameCmd = new RenameLabelCmd(symbol.getAddress(), symbol.getName(),
|
||||
newName, symbol.getParentNamespace(), SourceType.USER_DEFINED);
|
||||
|
||||
if (!tool.execute(renameCmd, getProgram())) {
|
||||
Msg.showError(getClass(), provider.getComponent(),
|
||||
"Error Renaming Symbol", renameCmd.getStatusMsg());
|
||||
Msg.showError(getClass(), provider.getComponent(), "Error Renaming Symbol",
|
||||
renameCmd.getStatusMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ConcurrentModificationException exc) {
|
||||
Msg.showError(getClass(), provider.getComponent(), "Invalid Symbol",
|
||||
"Symbol no longer valid.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(int row, int column) {
|
||||
Symbol s = (Symbol) getValueAt(row, LABEL_COL);
|
||||
SymbolTableNameValue s = (SymbolTableNameValue) getValueAt(row, LABEL_COL);
|
||||
if (s != null) {
|
||||
return s.getProgramLocation();
|
||||
return s.getSymbol().getProgramLocation();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -262,20 +246,27 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
|
||||
void symbolAdded(Symbol s) {
|
||||
if (filter.accepts(s, getProgram())) {
|
||||
addObject(new SymbolRowObject(s.getID()));
|
||||
addObject(new SymbolRowObject(s));
|
||||
lastSymbol = s;
|
||||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(long symbolID) {
|
||||
if (lastSymbol != null && lastSymbol.getID() == symbolID) {
|
||||
void symbolRemoved(Symbol s) {
|
||||
if (lastSymbol != null && lastSymbol.getID() == s.getID()) {
|
||||
lastSymbol = null;
|
||||
}
|
||||
removeObject(new SymbolRowObject(symbolID));
|
||||
removeObject(new SymbolRowObject(s));
|
||||
}
|
||||
|
||||
void symbolRemoved(long symbolId) {
|
||||
if (lastSymbol != null && lastSymbol.getID() == symbolId) {
|
||||
lastSymbol = null;
|
||||
}
|
||||
removeObject(new SymbolRowObject(symbolId));
|
||||
}
|
||||
|
||||
void symbolChanged(Symbol s) {
|
||||
SymbolRowObject symbolRowObject = new SymbolRowObject(s.getID());
|
||||
SymbolRowObject symbolRowObject = new SymbolRowObject(s);
|
||||
if (filter.accepts(s, getProgram())) {
|
||||
updateObject(symbolRowObject);
|
||||
}
|
||||
|
@ -288,8 +279,9 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
if (rowObjects == null || rowObjects.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tool.setStatusInfo("");
|
||||
LongArrayList deleteList = new LongArrayList();
|
||||
List<Symbol> deleteList = new LinkedList<>();
|
||||
CompoundCmd cmd = new CompoundCmd("Delete symbol(s)");
|
||||
for (int i = 0; i < rowObjects.size(); i++) {
|
||||
Symbol symbol = symbolTable.getSymbol(rowObjects.get(i).getKey());
|
||||
|
@ -303,7 +295,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
continue;//can't delete dynamic symbols...
|
||||
}
|
||||
}
|
||||
deleteList.add(rowObjects.get(i).getKey());
|
||||
|
||||
deleteList.add(rowObjects.get(i).getSymbol());
|
||||
String label = symbol.getName();
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
Function function = (Function) symbol.getObject();
|
||||
|
@ -339,32 +332,6 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
return filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getSortedColumnClass(int columnIndex) {
|
||||
if (columnIndex == LOCATION_COL) {
|
||||
return Address.class;
|
||||
}
|
||||
return super.getSortedColumnClass(columnIndex);
|
||||
}
|
||||
|
||||
public static int getPreferredWidth(int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case LABEL_COL:
|
||||
return 140;
|
||||
case LOCATION_COL:
|
||||
return 40;
|
||||
case DATATYPE_COL:
|
||||
case TYPE_COL:
|
||||
case SOURCE_COL:
|
||||
return 30;
|
||||
case NAMESPACE_COL:
|
||||
return 80;
|
||||
case REFS_COL:
|
||||
return 20;
|
||||
}
|
||||
return 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress(int row) {
|
||||
Symbol symbol = symbolTable.getSymbol(getRowObject(row).getKey());
|
||||
|
@ -374,21 +341,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
return symbol.getAddress();
|
||||
}
|
||||
|
||||
Symbol getSymbolForRowObject(SymbolRowObject storageObject) {
|
||||
if (symbolTable == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long key = storageObject.getKey();
|
||||
Symbol localSymbol = lastSymbol;
|
||||
if (localSymbol == null || localSymbol.getID() != key) {
|
||||
localSymbol = lastSymbol = symbolTable.getSymbol(key);
|
||||
}
|
||||
return localSymbol;
|
||||
}
|
||||
|
||||
AddressBasedLocation getSymbolLocation(SymbolRowObject rowObject) {
|
||||
Symbol s = getSymbolForRowObject(rowObject);
|
||||
private AddressBasedLocation getSymbolLocation(SymbolRowObject rowObject) {
|
||||
Symbol s = rowObject.getSymbol();
|
||||
if (s == null) {
|
||||
return new AddressBasedLocation();
|
||||
}
|
||||
|
@ -406,7 +360,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
//==================================================================================================
|
||||
|
||||
private class NameTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Symbol> {
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, SymbolTableNameValue> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
|
@ -414,9 +368,20 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Symbol getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
return getSymbolForRowObject(rowObject);
|
||||
public SymbolTableNameValue getValue(SymbolRowObject rowObject, Settings settings,
|
||||
Program p, ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol s = rowObject.getSymbol();
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: this call is slow, especially for dynamic symbols. Caching the dynamic
|
||||
// symbols in the SymbolRowObject *greatly* increases sorting and filtering performance.
|
||||
// For now we assume that most users are not loading dynamic labels. If we add
|
||||
// caching, then we have to deal with the stickiness of when to clear/update the cache
|
||||
String name = s.toString();
|
||||
return new SymbolTableNameValue(s, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,7 +398,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
@Override
|
||||
public Boolean getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -468,7 +433,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
@Override
|
||||
public ProgramLocation getProgramLocation(SymbolRowObject rowObject, Settings settings,
|
||||
Program p, ServiceProvider svcProvider) {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -476,6 +441,29 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
}
|
||||
}
|
||||
|
||||
private class SymbolTypeTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol s = rowObject.getSymbol();
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: this call is slow. If we decide that filtering/sorting on this value is
|
||||
// important, then this should be cached
|
||||
return SymbolUtilities.getSymbolTypeDisplayName(s);
|
||||
}
|
||||
}
|
||||
|
||||
private class VariableSymbolLocation extends AddressBasedLocation {
|
||||
|
||||
VariableSymbolLocation(Variable variable) {
|
||||
|
@ -495,7 +483,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -532,7 +520,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
@Override
|
||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -572,7 +560,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
@Override
|
||||
public SourceType getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -584,6 +572,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
private class ReferenceCountTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Integer> {
|
||||
|
||||
private ReferenceCountRenderer renderer = new ReferenceCountRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Reference Count";
|
||||
|
@ -592,18 +582,32 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
@Override
|
||||
public Integer getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Integer.valueOf(symbol.getReferenceCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// this renderer disables the default text filtering; this column is only filterable
|
||||
// via the column constraint filtering
|
||||
private class ReferenceCountRenderer extends GTableCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
||||
private class OffcutReferenceCountTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Integer> {
|
||||
|
||||
private OffcutReferenceCountRenderer renderer = new OffcutReferenceCountRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Offcut Ref Count";
|
||||
|
@ -613,7 +617,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
public Integer getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -637,6 +641,18 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
}
|
||||
return Integer.valueOf(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// this renderer disables the default text filtering; this column is only filterable
|
||||
// via the column constraint filtering
|
||||
private class OffcutReferenceCountRenderer extends GTableCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
||||
private class UserTableColumn
|
||||
|
@ -656,7 +672,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -694,7 +710,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
|||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null || !symbol.isExternal()) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/* ###
|
||||
* 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.symtable;
|
||||
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
/**
|
||||
* A simple data object for the Name column table cell. This class allows us to control
|
||||
* how sorting is performed by caching the slow (potentially) to calculate symbol name.
|
||||
*/
|
||||
class SymbolTableNameValue implements Comparable<SymbolTableNameValue> {
|
||||
|
||||
private Symbol symbol;
|
||||
private String name;
|
||||
|
||||
SymbolTableNameValue(Symbol symbol, String name) {
|
||||
this.symbol = symbol;
|
||||
this.name = name;
|
||||
|
||||
// name will be non-null when cached by the table model
|
||||
if (name == null) {
|
||||
name = symbol.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Symbol getSymbol() {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(SymbolTableNameValue o) {
|
||||
return name.compareToIgnoreCase(o.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -190,7 +190,10 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
if (!symProvider.isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
|
||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED) ||
|
||||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_ADDED) ||
|
||||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED)) {
|
||||
|
||||
symProvider.reload();
|
||||
refProvider.reload();
|
||||
return;
|
||||
|
@ -223,7 +226,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
Address addAddr = rec.getStart();
|
||||
Symbol primaryAtAdd = currentProgram.getSymbolTable().getPrimarySymbol(addAddr);
|
||||
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
|
||||
symProvider.symbolRemoved(primaryAtAdd.getID());
|
||||
symProvider.symbolRemoved(primaryAtAdd);
|
||||
}
|
||||
symbol = (Symbol) rec.getNewValue();
|
||||
symProvider.symbolAdded(symbol);
|
||||
|
@ -301,13 +304,6 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
refProvider.symbolChanged(element);
|
||||
}
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_MEMORY_BLOCK_ADDED:
|
||||
case ChangeManager.DOCR_MEMORY_BLOCK_REMOVED:
|
||||
symProvider.reload();
|
||||
refProvider.reload();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,14 +15,19 @@
|
|||
*/
|
||||
package ghidra.util.table.field;
|
||||
|
||||
import docking.widgets.table.GTableCellRenderer;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.table.column.AbstractWrapperTypeColumnRenderer;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
|
||||
public class FunctionBodySizeTableColumn
|
||||
extends ProgramBasedDynamicTableColumnExtensionPoint<Function, Integer> {
|
||||
|
||||
private FunctionBodySizeRenderer renderer = new FunctionBodySizeRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Function Size";
|
||||
|
@ -34,4 +39,15 @@ public class FunctionBodySizeTableColumn
|
|||
return (int) rowObject.getBody().getNumAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// this renderer disables the default text filtering; this column is only filterable
|
||||
// via the column constraint filtering
|
||||
private class FunctionBodySizeRenderer extends GTableCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ package ghidra.app.plugin.core.symtable;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -26,7 +27,8 @@ import java.util.function.BiConsumer;
|
|||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.table.*;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import org.jdom.Element;
|
||||
import org.junit.*;
|
||||
|
@ -130,40 +132,49 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testSortingLabelColumn() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LABEL_COL);
|
||||
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
TableColumn column =
|
||||
symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.LABEL_COL);
|
||||
GTableHeaderRenderer renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
||||
assertTrue(renderer.isSortedAscending());
|
||||
sortAscending(SymbolTableModel.LABEL_COL);
|
||||
|
||||
TableModel model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
Symbol sym1 = (Symbol) model.getValueAt(i + 0, SymbolTableModel.LABEL_COL);
|
||||
Symbol sym2 = (Symbol) model.getValueAt(i + 1, SymbolTableModel.LABEL_COL);
|
||||
Symbol sym1 = getSymbol(i);
|
||||
Symbol sym2 = getSymbol(i + 1);
|
||||
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
||||
assertTrue("row " + i + " not sorted correctly", (compare < 0 || compare == 0));
|
||||
}
|
||||
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
assertTrue(!renderer.isSortedAscending());
|
||||
sortDescending(SymbolTableModel.LABEL_COL);
|
||||
|
||||
model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
Symbol sym1 = (Symbol) model.getValueAt(i + 0, SymbolTableModel.LABEL_COL);
|
||||
Symbol sym2 = (Symbol) model.getValueAt(i + 1, SymbolTableModel.LABEL_COL);
|
||||
Symbol sym1 = getSymbol(i);
|
||||
Symbol sym2 = getSymbol(i + 1);
|
||||
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
||||
assertTrue("row " + i + " not sorted correctly", (compare > 0 || compare == 0));
|
||||
}
|
||||
}
|
||||
|
||||
private void sortAscending(int column) {
|
||||
runSwing(() -> symbolModel.setTableSortState(
|
||||
TableSortState.createDefaultSortState(column, true)));
|
||||
waitForTableModel(symbolModel);
|
||||
|
||||
waitForCondition(() -> {
|
||||
TableSortState sort = runSwing(() -> symbolModel.getTableSortState());
|
||||
return sort.getColumnSortState(column).isAscending();
|
||||
});
|
||||
}
|
||||
|
||||
private void sortDescending(int column) {
|
||||
runSwing(() -> symbolModel.setTableSortState(
|
||||
TableSortState.createDefaultSortState(column, false)));
|
||||
waitForTableModel(symbolModel);
|
||||
|
||||
waitForCondition(() -> {
|
||||
TableSortState sort = runSwing(() -> symbolModel.getTableSortState());
|
||||
return !sort.getColumnSortState(column).isAscending();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColumnDiscovery() throws Exception {
|
||||
//
|
||||
|
@ -205,35 +216,21 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testSortingAddressColumn() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LOCATION_COL);
|
||||
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
TableColumn column =
|
||||
symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.LOCATION_COL);
|
||||
GTableHeaderRenderer renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
||||
assertTrue(renderer.isSortedAscending());
|
||||
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||
|
||||
SymbolTableModel model = (SymbolTableModel) symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
AddressBasedLocation refs1 =
|
||||
(AddressBasedLocation) model.getValueAt(i + 0, SymbolTableModel.LOCATION_COL);
|
||||
AddressBasedLocation refs2 =
|
||||
(AddressBasedLocation) model.getValueAt(i + 1, SymbolTableModel.LOCATION_COL);
|
||||
assertTrue(refs1.compareTo(refs2) <= 0);
|
||||
AddressBasedLocation loc1 = getLocation(i);
|
||||
AddressBasedLocation loc2 = getLocation(i + 0);
|
||||
assertTrue(loc1.compareTo(loc2) <= 0);
|
||||
}
|
||||
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
assertTrue(!renderer.isSortedAscending());
|
||||
sortDescending(SymbolTableModel.LOCATION_COL);
|
||||
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
AddressBasedLocation refs1 =
|
||||
(AddressBasedLocation) model.getValueAt(i + 0, SymbolTableModel.LOCATION_COL);
|
||||
AddressBasedLocation refs2 =
|
||||
(AddressBasedLocation) model.getValueAt(i + 1, SymbolTableModel.LOCATION_COL);
|
||||
assertTrue(refs1.compareTo(refs2) >= 0);
|
||||
AddressBasedLocation loc1 = getLocation(i);
|
||||
AddressBasedLocation loc2 = getLocation(i + 0);
|
||||
assertTrue(loc1.compareTo(loc2) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,26 +238,20 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testSortingReferenceColumn() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
sortOnColumn(SymbolTableModel.REFS_COL);
|
||||
|
||||
TableColumn column =
|
||||
symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.REFS_COL);
|
||||
GTableHeaderRenderer renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
||||
assertTrue(renderer.isSortedAscending());
|
||||
sortAscending(SymbolTableModel.REFS_COL);
|
||||
|
||||
TableModel model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
Integer refs1 = (Integer) model.getValueAt(i + 0, SymbolTableModel.REFS_COL);
|
||||
Integer refs2 = (Integer) model.getValueAt(i + 1, SymbolTableModel.REFS_COL);
|
||||
Integer refs1 = getRefCount(i);
|
||||
Integer refs2 = getRefCount(i + 1);
|
||||
assertTrue(refs1.compareTo(refs2) <= 0);
|
||||
}
|
||||
|
||||
sortOnColumn(SymbolTableModel.REFS_COL);
|
||||
assertTrue(!renderer.isSortedAscending());
|
||||
sortDescending(SymbolTableModel.REFS_COL);
|
||||
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
Integer refs1 = (Integer) model.getValueAt(i + 0, SymbolTableModel.REFS_COL);
|
||||
Integer refs2 = (Integer) model.getValueAt(i + 1, SymbolTableModel.REFS_COL);
|
||||
Integer refs1 = getRefCount(i);
|
||||
Integer refs2 = getRefCount(i + 1);
|
||||
assertTrue(refs1.compareTo(refs2) >= 0);
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +350,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
int row = findRow("ghidra", "Global");
|
||||
String symbolName = "ghidra";
|
||||
int row = findRow(symbolName, "Global");
|
||||
|
||||
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL);
|
||||
waitForSwing();
|
||||
|
@ -368,6 +360,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
Component editor = symbolTable.getEditorComponent();
|
||||
assertNotNull(editor);
|
||||
JTextField textField = (JTextField) editor;
|
||||
String currentText = getText(textField);
|
||||
assertEquals(symbolName, currentText);
|
||||
|
||||
triggerActionKey(textField, 0, KeyEvent.VK_END);
|
||||
myTypeText(editor, ".Is.Cool");
|
||||
runSwing(() -> symbolTable.editingStopped(new ChangeEvent(symbolTable)));
|
||||
|
@ -376,7 +371,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
assertTrue(!symbolTable.isEditing());
|
||||
|
||||
Symbol s = (Symbol) symbolTable.getValueAt(row, SymbolTableModel.LABEL_COL);
|
||||
Symbol s = getSymbol(row);
|
||||
assertEquals("ghidra.Is.Cool", s.getName());
|
||||
}
|
||||
|
||||
|
@ -567,7 +562,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
int[] selectedRows = symbolTable.getSelectedRows();
|
||||
assertEquals(3, selectedRows.length);
|
||||
for (int selectedRow : selectedRows) {
|
||||
Symbol symbol = (Symbol) symbolTable.getValueAt(selectedRow, 0);
|
||||
Symbol symbol = getSymbol(selectedRow);
|
||||
assertTrue(!symbol.isPinned());
|
||||
}
|
||||
assertTrue(setPinnedAction.isEnabledForContext(actionContext));
|
||||
|
@ -576,14 +571,14 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
performAction(setPinnedAction, actionContext, true);
|
||||
waitForSwing();
|
||||
for (int selectedRow : selectedRows) {
|
||||
Symbol symbol = (Symbol) symbolTable.getValueAt(selectedRow, 0);
|
||||
Symbol symbol = getSymbol(selectedRow);
|
||||
assertTrue(symbol.isPinned());
|
||||
}
|
||||
|
||||
performAction(clearPinnedAction, actionContext, true);
|
||||
waitForSwing();
|
||||
for (int selectedRow : selectedRows) {
|
||||
Symbol symbol = (Symbol) symbolTable.getValueAt(selectedRow, 0);
|
||||
Symbol symbol = getSymbol(selectedRow);
|
||||
assertTrue(!symbol.isPinned());
|
||||
}
|
||||
|
||||
|
@ -600,11 +595,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
int[] selectedRows = symbolTable.getSelectedRows();
|
||||
|
||||
for (int selectedRow : selectedRows) {
|
||||
Symbol symbol = (Symbol) symbolTable.getValueAt(selectedRow, 0);
|
||||
assertTrue(!symbol.isPinned());
|
||||
Symbol symbol = getSymbol(selectedRow);
|
||||
assertFalse(symbol.isPinned());
|
||||
}
|
||||
assertTrue(!setPinnedAction.isEnabledForContext(actionContext));
|
||||
assertTrue(!clearPinnedAction.isEnabledForContext(actionContext));
|
||||
assertFalse(setPinnedAction.isEnabledForContext(actionContext));
|
||||
assertFalse(clearPinnedAction.isEnabledForContext(actionContext));
|
||||
|
||||
}
|
||||
|
||||
|
@ -620,13 +615,13 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
sym = st.createLabel(sample.getNewAddress(0x01007000), "Zeus", SourceType.USER_DEFINED);
|
||||
waitForNotBusy(symbolTable);
|
||||
assertEquals(rowCount + 1, symbolTable.getRowCount());
|
||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym.getID())) >= 0);
|
||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym)) >= 0);
|
||||
|
||||
sym =
|
||||
st.createLabel(sample.getNewAddress(0x01007100), "Athena", SourceType.USER_DEFINED);
|
||||
waitForNotBusy(symbolTable);
|
||||
assertEquals(rowCount + 2, symbolTable.getRowCount());
|
||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym.getID())) >= 0);
|
||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym)) >= 0);
|
||||
}
|
||||
finally {
|
||||
prog.endTransaction(id, true);
|
||||
|
@ -659,7 +654,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
sym =
|
||||
st.createLabel(sample.getNewAddress(0x01007000), "saaaa", SourceType.USER_DEFINED);
|
||||
waitForNotBusy(symbolTable);
|
||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym.getID())) >= 0);
|
||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym)) >= 0);
|
||||
}
|
||||
finally {
|
||||
prog.endTransaction(id, true);
|
||||
|
@ -722,7 +717,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// entry symbol replaced by dynamic External Entry symbol
|
||||
assertNull(getUniqueSymbol(prog, "entry"));
|
||||
assertNotNull(getUniqueSymbol(prog, "EXT_00000051"));
|
||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym.getID())) == -1);
|
||||
assertTrue("Deleted symbol not removed from table",
|
||||
symbolModel.getRowIndex(new SymbolRowObject(sym)) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -732,8 +728,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Symbol s = getUniqueSymbol(prog, "entry");
|
||||
|
||||
int row = symbolModel.getRowIndex(new SymbolRowObject(s.getID()));
|
||||
Integer refCount = (Integer) symbolTable.getValueAt(row, SymbolTableModel.REFS_COL);
|
||||
int row = symbolModel.getRowIndex(new SymbolRowObject(s));
|
||||
Integer refCount = getRefCount(row);
|
||||
assertNotNull(refCount);
|
||||
assertEquals(3, refCount.intValue());
|
||||
|
||||
|
@ -749,9 +745,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
row = symbolModel.getRowIndex(new SymbolRowObject(s.getID()));
|
||||
row = symbolModel.getRowIndex(new SymbolRowObject(s));
|
||||
|
||||
refCount = (Integer) symbolTable.getValueAt(row, SymbolTableModel.REFS_COL);
|
||||
refCount = getRefCount(row);
|
||||
assertNotNull(refCount);
|
||||
assertEquals(4, refCount.intValue());
|
||||
}
|
||||
|
@ -763,9 +759,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Symbol s = getUniqueSymbol(prog, "doStuff");
|
||||
|
||||
int row = symbolModel.getRowIndex(new SymbolRowObject(s.getID()));
|
||||
int row = symbolModel.getRowIndex(new SymbolRowObject(s));
|
||||
|
||||
Integer refCount = (Integer) symbolTable.getValueAt(row, SymbolTableModel.REFS_COL);
|
||||
Integer refCount = getRefCount(row);
|
||||
assertNotNull(refCount);
|
||||
assertEquals(4, refCount.intValue());
|
||||
|
||||
|
@ -791,7 +787,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
refCount = (Integer) symbolTable.getValueAt(row, SymbolTableModel.REFS_COL);
|
||||
refCount = getRefCount(row);
|
||||
assertNotNull(refCount);
|
||||
assertEquals(3, refCount.intValue());
|
||||
}
|
||||
|
@ -853,23 +849,12 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
/************** LABEL **********************/
|
||||
|
||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LABEL_COL);
|
||||
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
TableColumn column =
|
||||
symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.LABEL_COL);
|
||||
GTableHeaderRenderer renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
||||
assertTrue(renderer.isSortedAscending());
|
||||
sortAscending(SymbolTableModel.LABEL_COL);
|
||||
|
||||
TableModel model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
Symbol sym1 = (Symbol) model.getValueAt(i + 0, SymbolTableModel.LABEL_COL);
|
||||
Symbol sym2 = (Symbol) model.getValueAt(i + 1, SymbolTableModel.LABEL_COL);
|
||||
Symbol sym1 = getSymbol(i);
|
||||
Symbol sym2 = getSymbol(i + 1);
|
||||
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
||||
assertTrue("Symbol \"" + sym1 + "\" is not sorted as less than symbol \"" + sym2 + "\"",
|
||||
compare <= 0);
|
||||
|
@ -877,21 +862,12 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
/************** ADDRESS **********************/
|
||||
|
||||
rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LOCATION_COL);
|
||||
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
column = symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.LOCATION_COL);
|
||||
renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
||||
assertTrue(renderer.isSortedAscending());
|
||||
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||
|
||||
model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
AddressBasedLocation loc1 =
|
||||
(AddressBasedLocation) model.getValueAt(i, SymbolTableModel.LOCATION_COL);
|
||||
AddressBasedLocation loc2 =
|
||||
(AddressBasedLocation) model.getValueAt(i + 1, SymbolTableModel.LOCATION_COL);
|
||||
AddressBasedLocation loc1 = getLocation(i);
|
||||
AddressBasedLocation loc2 = getLocation(i + 1);
|
||||
int compare = SystemUtilities.compareTo(loc1, loc2);
|
||||
assertTrue(
|
||||
"Location1 \"" + loc1 + "\"is not sorted as less than location2 \"" + loc2 + "\"",
|
||||
|
@ -900,18 +876,12 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
/************** REFERENCES **********************/
|
||||
|
||||
rect = symbolTableHeader.getHeaderRect(SymbolTableModel.REFS_COL);
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
|
||||
column = symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.REFS_COL);
|
||||
renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
||||
assertTrue(renderer.isSortedAscending());
|
||||
sortAscending(SymbolTableModel.REFS_COL);
|
||||
|
||||
model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
Integer refs1 = (Integer) model.getValueAt(i + 0, SymbolTableModel.REFS_COL);
|
||||
Integer refs2 = (Integer) model.getValueAt(i + 1, SymbolTableModel.REFS_COL);
|
||||
Integer refs1 = getRefCount(i);
|
||||
Integer refs2 = getRefCount(i + 1);
|
||||
assertTrue(
|
||||
"The number of references (\"" + refs1 + "\") for row did not " +
|
||||
"compare as less than the number for the following row (\"" + refs2 + "\")",
|
||||
|
@ -1035,7 +1005,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
deleteText(textField);
|
||||
|
||||
// sort on a different column to trigger the other kind of filtering
|
||||
sortOnColumn(SymbolTableModel.REFS_COL);
|
||||
sortAscending(SymbolTableModel.REFS_COL);
|
||||
|
||||
text = "_";
|
||||
myTypeText(textField, text);
|
||||
|
@ -1106,7 +1076,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
deleteText(textField);
|
||||
|
||||
// sort on a different column to trigger the other kind of filtering
|
||||
sortOnColumn(SymbolTableModel.LOCATION_COL);
|
||||
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||
|
||||
text = "_";
|
||||
myTypeText(textField, text);
|
||||
|
@ -1285,6 +1255,24 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
}
|
||||
|
||||
private Symbol getSymbol(int row) {
|
||||
SymbolRowObject rowObject = symbolModel.getRowObject(row);
|
||||
return rowObject.getSymbol();
|
||||
}
|
||||
|
||||
private Integer getRefCount(int row) {
|
||||
Integer count =
|
||||
runSwing(() -> (Integer) symbolModel.getValueAt(row, SymbolTableModel.REFS_COL));
|
||||
return count;
|
||||
}
|
||||
|
||||
private AddressBasedLocation getLocation(int row) {
|
||||
AddressBasedLocation location =
|
||||
runSwing(() -> (AddressBasedLocation) symbolModel.getValueAt(row,
|
||||
SymbolTableModel.LOCATION_COL));
|
||||
return location;
|
||||
}
|
||||
|
||||
private void assertReferencesAddressColumnValue(int row, long value) {
|
||||
Address addr =
|
||||
(Address) referenceTable.getValueAt(row, SymbolReferenceModel.ADDRESS_COLUMN);
|
||||
|
@ -1292,12 +1280,6 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
addr.getOffset());
|
||||
}
|
||||
|
||||
private void sortOnColumn(int column) throws Exception {
|
||||
Rectangle rect = symbolTableHeader.getHeaderRect(column);
|
||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
||||
waitForNotBusy(symbolTable);
|
||||
}
|
||||
|
||||
private void deleteText(JTextField field) throws Exception {
|
||||
deleteText(field, false, false);
|
||||
}
|
||||
|
@ -1328,14 +1310,10 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void setFilterOptions(TextFilterStrategy filterStrategy, boolean caseSensitive) {
|
||||
private void setFilterOptions(TextFilterStrategy filterStrategy, boolean caseSensitive)
|
||||
throws Exception {
|
||||
filterPanel.setFilterOptions(new FilterOptions(filterStrategy, true, caseSensitive, false));
|
||||
waitForTable();
|
||||
|
||||
}
|
||||
|
||||
private void waitForTable() {
|
||||
waitForSwing();
|
||||
waitForNotBusy(symbolTable);
|
||||
}
|
||||
|
||||
private JTextField getFilterTextField() {
|
||||
|
@ -1376,14 +1354,14 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
private int getRowForSymbol(Symbol symbol) {
|
||||
for (int i = 0; i < symbolTable.getRowCount(); i++) {
|
||||
Object name = symbolTable.getValueAt(i, 0);
|
||||
if (name.toString().equals(symbol.getName())) {
|
||||
Object namespace = symbolTable.getValueAt(i, 4);
|
||||
if (namespace.toString().equals(symbol.getParentNamespace().getName())) {
|
||||
Symbol rowSymbol = getSymbol(i);
|
||||
if (rowSymbol.equals(symbol)) {
|
||||
// if (rowSymbol.getParentNamespace().equals(symbol.getParentNamespace())) {
|
||||
// return i;
|
||||
// }
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.fail("Didn't find symbol in symbol table: " + symbol.getName());
|
||||
return -1;
|
||||
}
|
||||
|
@ -1478,7 +1456,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
"There are no filtered matches as expected from filter string: " + filterString,
|
||||
rowCount > 0);
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
Symbol symbol = (Symbol) symbolModel.getValueAt(i, SymbolTableModel.LABEL_COL);
|
||||
Symbol symbol = getSymbol(i);
|
||||
assertTrue(
|
||||
"Found an entry in the symbol table model that " + "does not match the given " +
|
||||
"filter: " + filterString + " and symbol: " + symbol.getName(),
|
||||
|
@ -1495,7 +1473,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
String filterText = string;
|
||||
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
Symbol symbol = (Symbol) symbolModel.getValueAt(i, SymbolTableModel.LABEL_COL);
|
||||
Symbol symbol = getSymbol(i);
|
||||
assertTrue(
|
||||
"Found an entry in the symbol table model that does not match the given " +
|
||||
"filter: " + string + " and symbol: " + symbol.getName(),
|
||||
|
@ -1504,8 +1482,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void waitForNotBusy(GTable table) throws Exception {
|
||||
prog.flushEvents();
|
||||
|
||||
waitForProgram(prog);
|
||||
ThreadedTableModel<?, ?> model = (ThreadedTableModel<?, ?>) table.getModel();
|
||||
waitForTableModel(model);
|
||||
}
|
||||
|
@ -1632,6 +1609,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
waitForNotBusy(symbolTable);
|
||||
|
||||
symbolTableHeader = symbolTable.getTableHeader();
|
||||
|
||||
sortAscending(SymbolTableModel.LABEL_COL);
|
||||
}
|
||||
|
||||
private void showReferencesTable() {
|
||||
|
@ -1679,7 +1658,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private int findRow(String symbolName, String namespace) {
|
||||
int max = symbolTable.getRowCount();
|
||||
for (int i = 0; i < max; i++) {
|
||||
Symbol s = (Symbol) symbolTable.getValueAt(i, SymbolTableModel.LABEL_COL);
|
||||
SymbolTableNameValue symbolNameValue =
|
||||
(SymbolTableNameValue) symbolTable.getValueAt(i, SymbolTableModel.LABEL_COL);
|
||||
Symbol s = symbolNameValue.getSymbol();
|
||||
if (symbolName.equals(s.getName()) &&
|
||||
namespace.equals(s.getParentNamespace().getName())) {
|
||||
return i;
|
||||
|
|
|
@ -92,8 +92,8 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the given row object in this model; -1 if the model does not contain
|
||||
* the given object.
|
||||
* Returns the index of the given row object in this model; a negative value if the model
|
||||
* does not contain the given object.
|
||||
*
|
||||
* <p>Warning: if the this model has no sort applied, then performance will be O(n). If
|
||||
* sorted, then performance is O(log n). You can call {@link #isSorted()} to know when
|
||||
|
@ -132,6 +132,9 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||
|
||||
@Override
|
||||
public int getPrimarySortColumnIndex() {
|
||||
if (sortState.isUnsorted()) {
|
||||
return -1;
|
||||
}
|
||||
return sortState.iterator().next().getColumnModelIndex();
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,13 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
|||
}
|
||||
}
|
||||
|
||||
if (columnUsesConstraintFilteringOnly(column)) {
|
||||
// This allows columns to be ignored for default text filtering while still being
|
||||
// filterable through the column constraints API
|
||||
return null;
|
||||
}
|
||||
|
||||
// note: this call can be slow when columns dynamically calculate values from the database
|
||||
Object value = model.getColumnValueForRow(rowObject, column);
|
||||
if (value == null) {
|
||||
return null;
|
||||
|
@ -96,7 +103,22 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
|||
return value.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean columnUsesConstraintFilteringOnly(int column) {
|
||||
if (!(model instanceof DynamicColumnTableModel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DynamicColumnTableModel<ROW_OBJECT> columnBasedModel =
|
||||
(DynamicColumnTableModel<ROW_OBJECT>) model;
|
||||
GColumnRenderer<Object> renderer = getColumnRenderer(columnBasedModel, column);
|
||||
if (renderer == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode();
|
||||
return mode == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY;
|
||||
}
|
||||
|
||||
private String getRenderedColumnValue(Object columnValue, int columnIndex) {
|
||||
|
||||
if (!(model instanceof DynamicColumnTableModel)) {
|
||||
|
@ -110,11 +132,6 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
|||
return null;
|
||||
}
|
||||
|
||||
ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode();
|
||||
if (mode == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY) {
|
||||
return null; // this renderer does not support text
|
||||
}
|
||||
|
||||
Settings settings = columnBasedModel.getColumnSettings(columnIndex);
|
||||
String s = renderer.getFilterString(columnValue, settings);
|
||||
return s;
|
||||
|
|
|
@ -33,7 +33,7 @@ import ghidra.util.datastruct.WeakSet;
|
|||
public class GTableColumnModel
|
||||
implements TableColumnModel, PropertyChangeListener, ListSelectionListener {
|
||||
|
||||
private List<TableColumn> visibleList = new ArrayList<>();
|
||||
private VisibleColumns visibleColumns = new VisibleColumns();
|
||||
private List<TableColumn> completeList = new ArrayList<>();
|
||||
private int totalColumnWidth;
|
||||
private int columnMargin;
|
||||
|
@ -67,18 +67,8 @@ public class GTableColumnModel
|
|||
|
||||
void removeAllColumns() {
|
||||
|
||||
TableColumn[] asArray = visibleList.toArray(new TableColumn[visibleList.size()]);
|
||||
for (int i = 0; i < asArray.length; i++) {
|
||||
TableColumn column = asArray[i];
|
||||
visibleList.remove(column);
|
||||
fireColumnRemoved(new TableColumnModelEvent(this, i, i));
|
||||
}
|
||||
|
||||
/*
|
||||
TODO replace the above snippet with this code, after the upcoming release
|
||||
fireColumnRemoved(new TableColumnModelEvent(this, 0, visibleList.size() - 1));
|
||||
visibleList.clear();
|
||||
*/
|
||||
fireColumnRemoved(new TableColumnModelEvent(this, 0, visibleColumns.size() - 1));
|
||||
visibleColumns.clear();
|
||||
|
||||
// no need to fire the removed event for items in the complete list, as the clients
|
||||
// only know about the visible columns
|
||||
|
@ -90,7 +80,7 @@ public class GTableColumnModel
|
|||
|
||||
void dispose() {
|
||||
listeners.clear();
|
||||
visibleList.clear();
|
||||
visibleColumns.clear();
|
||||
completeList.clear();
|
||||
columnModelState.dispose();
|
||||
}
|
||||
|
@ -101,7 +91,7 @@ public class GTableColumnModel
|
|||
* @return true if the given column is visible.
|
||||
*/
|
||||
public boolean isVisible(TableColumn column) {
|
||||
return visibleList.contains(column);
|
||||
return visibleColumns.contains(column);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,7 +109,7 @@ public class GTableColumnModel
|
|||
}
|
||||
|
||||
public void setVisible(TableColumn column, boolean visible) {
|
||||
boolean isVisible = visibleList.contains(column);
|
||||
boolean isVisible = visibleColumns.contains(column);
|
||||
|
||||
if (visible == isVisible) {
|
||||
return;
|
||||
|
@ -127,12 +117,12 @@ public class GTableColumnModel
|
|||
|
||||
if (visible) {
|
||||
int insertIndex = findVisibleInsertionIndex(column);
|
||||
visibleList.add(insertIndex, column);
|
||||
visibleColumns.add(insertIndex, column);
|
||||
fireColumnAdded(new TableColumnModelEvent(this, insertIndex, insertIndex));
|
||||
}
|
||||
else {
|
||||
int columnIndex = visibleList.indexOf(column);
|
||||
visibleList.remove(columnIndex);
|
||||
int columnIndex = visibleColumns.indexOf(column);
|
||||
visibleColumns.remove(columnIndex);
|
||||
// Adjust for the selection
|
||||
if (selectionModel != null) {
|
||||
selectionModel.removeIndexInterval(columnIndex, columnIndex);
|
||||
|
@ -146,12 +136,12 @@ public class GTableColumnModel
|
|||
}
|
||||
|
||||
private int findVisibleInsertionIndex(TableColumn column) {
|
||||
int completeIndex = completeList.indexOf(column);
|
||||
int completeIndex = visibleColumns.indexOf(column);
|
||||
|
||||
int size = visibleList.size();
|
||||
int size = visibleColumns.size();
|
||||
for (int i = completeIndex + 1; i < size; i++) {
|
||||
TableColumn nextColumn = completeList.get(i);
|
||||
int visibleIndex = visibleList.indexOf(nextColumn);
|
||||
int visibleIndex = visibleColumns.indexOf(nextColumn);
|
||||
if (visibleIndex != -1) {
|
||||
return visibleIndex;
|
||||
}
|
||||
|
@ -169,7 +159,7 @@ public class GTableColumnModel
|
|||
removeColumnWithModelIndex(aColumn.getModelIndex()); // dedup
|
||||
|
||||
completeList.add(aColumn);
|
||||
visibleList.add(aColumn);
|
||||
visibleColumns.add(aColumn);
|
||||
|
||||
aColumn.addPropertyChangeListener(this);
|
||||
|
||||
|
@ -205,7 +195,7 @@ public class GTableColumnModel
|
|||
}
|
||||
|
||||
completeList.remove(tableColumn);
|
||||
visibleList.remove(tableColumn);
|
||||
visibleColumns.remove(tableColumn);
|
||||
tableColumn.removePropertyChangeListener(this);
|
||||
}
|
||||
|
||||
|
@ -216,15 +206,15 @@ public class GTableColumnModel
|
|||
|
||||
@Override
|
||||
public TableColumn getColumn(int columnIndex) {
|
||||
if ((columnIndex < 0) || (columnIndex >= visibleList.size())) {
|
||||
if ((columnIndex < 0) || (columnIndex >= visibleColumns.size())) {
|
||||
return null;
|
||||
}
|
||||
return visibleList.get(columnIndex);
|
||||
return visibleColumns.get(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return visibleList.size();
|
||||
return visibleColumns.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -232,8 +222,8 @@ public class GTableColumnModel
|
|||
if (columnIdentifier == null) {
|
||||
throw new IllegalArgumentException("Identifier is null");
|
||||
}
|
||||
for (int i = 0; i < visibleList.size(); i++) {
|
||||
TableColumn tableColumn = visibleList.get(i);
|
||||
for (int i = 0; i < visibleColumns.size(); i++) {
|
||||
TableColumn tableColumn = visibleColumns.get(i);
|
||||
if (columnIdentifier.equals(tableColumn.getIdentifier())) {
|
||||
return i;
|
||||
}
|
||||
|
@ -271,7 +261,7 @@ public class GTableColumnModel
|
|||
|
||||
@Override
|
||||
public Enumeration<TableColumn> getColumns() {
|
||||
return Collections.enumeration(visibleList);
|
||||
return visibleColumns.toEnumeration();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -355,8 +345,8 @@ public class GTableColumnModel
|
|||
}
|
||||
|
||||
// update the visible list
|
||||
TableColumn movedColumn = visibleList.remove(columnIndex);
|
||||
visibleList.add(newIndex, movedColumn);
|
||||
TableColumn movedColumn = visibleColumns.remove(columnIndex);
|
||||
visibleColumns.add(newIndex, movedColumn);
|
||||
|
||||
// update the complete list
|
||||
completeList.remove(movedColumn);
|
||||
|
@ -364,7 +354,7 @@ public class GTableColumnModel
|
|||
|
||||
// get the item at the index after the new index (since we are moving up, we know
|
||||
// that there are columns below the new index)
|
||||
TableColumn column = visibleList.get(newIndex + 1);
|
||||
TableColumn column = visibleColumns.get(newIndex + 1);
|
||||
|
||||
// find this column in the complete list and then place the moved column before that
|
||||
// position in the complete list
|
||||
|
@ -375,7 +365,7 @@ public class GTableColumnModel
|
|||
|
||||
// get the item at the index before the new index (since we are moving down, we know
|
||||
// that there are columns above the new index)
|
||||
TableColumn column = visibleList.get(newIndex - 1);
|
||||
TableColumn column = visibleColumns.get(newIndex - 1);
|
||||
|
||||
// find this column in the complete list and then place the moved column after that
|
||||
// position in the complete list
|
||||
|
@ -402,9 +392,9 @@ public class GTableColumnModel
|
|||
public void removeColumn(TableColumn column) {
|
||||
completeList.remove(column);
|
||||
|
||||
int index = visibleList.indexOf(column);
|
||||
int index = visibleColumns.indexOf(column);
|
||||
if (index >= 0) {
|
||||
visibleList.remove(index);
|
||||
visibleColumns.remove(index);
|
||||
// Adjust for the selection
|
||||
if (selectionModel != null) {
|
||||
selectionModel.removeIndexInterval(index, index);
|
||||
|
@ -459,7 +449,7 @@ public class GTableColumnModel
|
|||
*/
|
||||
private void recalcWidthCache() {
|
||||
totalColumnWidth = 0;
|
||||
for (TableColumn tableColumn : visibleList) {
|
||||
for (TableColumn tableColumn : visibleColumns.getColumns()) {
|
||||
totalColumnWidth += tableColumn.getWidth();
|
||||
}
|
||||
}
|
||||
|
@ -471,7 +461,7 @@ public class GTableColumnModel
|
|||
void restoreState(List<TableColumn> newCompleteList, List<Settings> newSettingsList,
|
||||
List<TableColumn> newVisibleList) {
|
||||
this.completeList = newCompleteList;
|
||||
this.visibleList = newVisibleList;
|
||||
this.visibleColumns = new VisibleColumns(newVisibleList);
|
||||
|
||||
TableModel model = table.getModel();
|
||||
if (model instanceof ConfigurableColumnTableModel) {
|
||||
|
@ -484,10 +474,6 @@ public class GTableColumnModel
|
|||
configurableModel.setAllColumnSettings(columnIndexAndSettings);
|
||||
}
|
||||
|
||||
// TODO: at some point in the future (like a year or more) we can remove this, when
|
||||
// we know the new code below it works
|
||||
// fireColumnMarginChanged(); // let the system know to rebuild the GUI (Java HACK!)
|
||||
|
||||
// signal a change; we've added/removed columns, but we don't need to be specific
|
||||
TableColumnModelEvent e = new TableColumnModelEvent(this, 0, getColumnCount() - 1);
|
||||
fireColumnAdded(e);
|
||||
|
@ -515,6 +501,74 @@ public class GTableColumnModel
|
|||
return oldValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* A small class to provide a method to quickly see if a column is visible by calling contains
|
||||
* on a hash set
|
||||
*/
|
||||
private class VisibleColumns {
|
||||
private Set<TableColumn> visibleSet = new HashSet<>();
|
||||
private List<TableColumn> visibleList = new ArrayList<>();
|
||||
|
||||
public VisibleColumns() {
|
||||
}
|
||||
|
||||
public VisibleColumns(List<TableColumn> newVisibleList) {
|
||||
this.visibleList = newVisibleList;
|
||||
visibleSet.addAll(visibleList);
|
||||
}
|
||||
|
||||
List<TableColumn> getColumns() {
|
||||
return visibleList;
|
||||
}
|
||||
|
||||
int size() {
|
||||
return visibleList.size();
|
||||
}
|
||||
|
||||
public void remove(TableColumn column) {
|
||||
visibleList.remove(column);
|
||||
visibleSet.remove(column);
|
||||
}
|
||||
|
||||
public void add(TableColumn column) {
|
||||
visibleList.add(column);
|
||||
visibleSet.add(column);
|
||||
}
|
||||
|
||||
public Enumeration<TableColumn> toEnumeration() {
|
||||
return Collections.enumeration(visibleList);
|
||||
}
|
||||
|
||||
public TableColumn get(int index) {
|
||||
return visibleList.get(index);
|
||||
}
|
||||
|
||||
public int indexOf(TableColumn column) {
|
||||
return visibleList.indexOf(column);
|
||||
}
|
||||
|
||||
public TableColumn remove(int index) {
|
||||
|
||||
TableColumn column = visibleList.remove(index);
|
||||
visibleSet.remove(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
public void add(int insertIndex, TableColumn column) {
|
||||
visibleList.add(insertIndex, column);
|
||||
visibleSet.add(column);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
visibleList.clear();
|
||||
visibleSet.clear();
|
||||
}
|
||||
|
||||
boolean contains(TableColumn c) {
|
||||
return visibleSet.contains(c);
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Listener and event methods
|
||||
//==================================================================================================
|
||||
|
|
|
@ -40,7 +40,7 @@ public interface SortedTableModel extends TableModel {
|
|||
public boolean isSortable(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns the column index that is the primary sorted column
|
||||
* Returns the column index that is the primary sorted column; -1 if no column is sorted
|
||||
*
|
||||
* @return the index
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Comparator;
|
|||
import docking.widgets.table.*;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
|
||||
|
||||
/**
|
||||
* A special version of the backup comparator that uses the column's rendered value for
|
||||
|
@ -33,10 +34,25 @@ public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T>
|
|||
protected int sortColumn;
|
||||
protected DynamicColumnTableModel<T> model;
|
||||
|
||||
// columns do not support sorting via their rendered value if the are marked
|
||||
// for column filtering only
|
||||
private boolean supportsColumnSorting = true;
|
||||
|
||||
public ColumnRenderedValueBackupRowComparator(DynamicColumnTableModel<T> model,
|
||||
int sortColumn) {
|
||||
this.model = model;
|
||||
this.sortColumn = sortColumn;
|
||||
|
||||
DynamicTableColumn<T, ?, ?> column = model.getColumn(sortColumn);
|
||||
@SuppressWarnings("unchecked")
|
||||
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
|
||||
if (renderer != null) {
|
||||
if (renderer.getColumnConstraintFilterMode() == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY) {
|
||||
// this implies that the column has signaled that it does not support
|
||||
// filtering/sorting using its rendered value
|
||||
supportsColumnSorting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,9 +77,13 @@ public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T>
|
|||
@SuppressWarnings("unchecked")
|
||||
private String getRenderedColumnStringValue(T t) {
|
||||
|
||||
if (!supportsColumnSorting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DynamicTableColumn<T, ?, ?> column = model.getColumn(sortColumn);
|
||||
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
|
||||
Object o = model.getColumnValueForRow(t, sortColumn);
|
||||
Object o = getColumnValue(t);
|
||||
if (renderer == null) {
|
||||
return o == null ? null : o.toString();
|
||||
}
|
||||
|
@ -72,6 +92,7 @@ public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T>
|
|||
return renderer.getFilterString(o, settings);
|
||||
}
|
||||
|
||||
// this may be overridden to use caching
|
||||
protected Object getColumnValue(T t) {
|
||||
return model.getColumnValueForRow(t, sortColumn);
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
|||
}
|
||||
|
||||
void initializeCache() {
|
||||
threadLocalColumnCache.set(new LRUMap<ROW_OBJECT, Map<Integer, Object>>(20000));
|
||||
threadLocalColumnCache.set(new LRUMap<ROW_OBJECT, Map<Integer, Object>>(1000000));
|
||||
}
|
||||
|
||||
void clearCache() {
|
||||
|
@ -747,10 +747,6 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
|||
return maxUpdateDelayMillis;
|
||||
}
|
||||
|
||||
protected Class<?> getSortedColumnClass(int columnIndex) {
|
||||
return getColumnClass(columnIndex);
|
||||
}
|
||||
|
||||
ThreadedTableModelUpdateMgr<ROW_OBJECT> getUpdateManager() {
|
||||
return updateManager;
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
|
|||
ComponentProvider provider = getProvider("Symbol Table");
|
||||
tool.showComponentProvider(provider, true);
|
||||
|
||||
moveProviderToItsOwnWindow(provider, 950, 750);
|
||||
moveProviderToItsOwnWindow(provider, 950, 400);
|
||||
GTable table = getTable(provider);
|
||||
setColumnSizes(table);
|
||||
|
||||
|
@ -172,13 +172,28 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
|
|||
TableColumn column = columnModel.getColumn(i);
|
||||
Object headerValue = column.getHeaderValue();
|
||||
if ("Name".equals(headerValue)) {
|
||||
column.setPreferredWidth(175);
|
||||
column.setPreferredWidth(300);
|
||||
}
|
||||
else if ("Reference Count".equals(headerValue)) {
|
||||
column.setPreferredWidth(15);
|
||||
column.setPreferredWidth(25);
|
||||
}
|
||||
else if ("Offcut Ref Count".equals(headerValue)) {
|
||||
column.setPreferredWidth(15);
|
||||
column.setPreferredWidth(25);
|
||||
}
|
||||
else if ("Namespace".equals(headerValue)) {
|
||||
column.setPreferredWidth(160);
|
||||
}
|
||||
else if ("Location".equals(headerValue)) {
|
||||
column.setPreferredWidth(170);
|
||||
}
|
||||
else if ("Namespace".equals(headerValue)) {
|
||||
column.setPreferredWidth(170);
|
||||
}
|
||||
else if ("Source".equals(headerValue)) {
|
||||
column.setPreferredWidth(170);
|
||||
}
|
||||
else if ("Type".equals(headerValue)) {
|
||||
column.setPreferredWidth(170);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue