mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-4363: Various fixes and improvements for Elements tables.
This commit is contained in:
parent
5a4315fb39
commit
ab5c800030
17 changed files with 258 additions and 148 deletions
|
@ -209,7 +209,7 @@ def start_trace(name):
|
||||||
with open(schema_fn, 'r') as schema_file:
|
with open(schema_fn, 'r') as schema_file:
|
||||||
schema_xml = schema_file.read()
|
schema_xml = schema_file.read()
|
||||||
with STATE.trace.open_tx("Create Root Object"):
|
with STATE.trace.open_tx("Create Root Object"):
|
||||||
root = STATE.trace.create_root_object(schema_xml, 'Session')
|
root = STATE.trace.create_root_object(schema_xml, 'DbgengSession')
|
||||||
root.set_value('_display', util.DBG_VERSION.full + ' via pybag')
|
root.set_value('_display', util.DBG_VERSION.full + ' via pybag')
|
||||||
util.set_convenience_variable('_ghidra_tracing', "true")
|
util.set_convenience_variable('_ghidra_tracing', "true")
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<context>
|
<context>
|
||||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
<schema name="DbgengSession" elementResync="NEVER" attributeResync="NEVER">
|
||||||
<interface name="Access" />
|
<interface name="Access" />
|
||||||
<interface name="Attacher" />
|
<interface name="Attacher" />
|
||||||
<interface name="Interpreter" />
|
<interface name="Interpreter" />
|
||||||
|
|
|
@ -275,7 +275,7 @@ def start_trace(name):
|
||||||
with open(schema_fn, 'r') as schema_file:
|
with open(schema_fn, 'r') as schema_file:
|
||||||
schema_xml = schema_file.read()
|
schema_xml = schema_file.read()
|
||||||
with STATE.trace.open_tx("Create Root Object"):
|
with STATE.trace.open_tx("Create Root Object"):
|
||||||
root = STATE.trace.create_root_object(schema_xml, 'Session')
|
root = STATE.trace.create_root_object(schema_xml, 'GdbSession')
|
||||||
root.set_value('_display', 'GNU gdb ' + util.GDB_VERSION.full)
|
root.set_value('_display', 'GNU gdb ' + util.GDB_VERSION.full)
|
||||||
STATE.trace.create_object(AVAILABLES_PATH).insert()
|
STATE.trace.create_object(AVAILABLES_PATH).insert()
|
||||||
STATE.trace.create_object(BREAKPOINTS_PATH).insert()
|
STATE.trace.create_object(BREAKPOINTS_PATH).insert()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<context>
|
<context>
|
||||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
<schema name="GdbSession" elementResync="NEVER" attributeResync="NEVER">
|
||||||
<interface name="Access" />
|
<interface name="Access" />
|
||||||
<interface name="Attacher" />
|
<interface name="Attacher" />
|
||||||
<interface name="Interpreter" />
|
<interface name="Interpreter" />
|
||||||
|
@ -257,7 +257,6 @@
|
||||||
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
<attribute name="_order" schema="INT" hidden="yes" />
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
<attribute name="Advance" schema="Method" required="yes" fixed="yes" hidden="yes" />
|
|
||||||
<attribute schema="VOID" />
|
<attribute schema="VOID" />
|
||||||
</schema>
|
</schema>
|
||||||
<schema name="Module" elementResync="NEVER" attributeResync="NEVER">
|
<schema name="Module" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
|
|
@ -261,7 +261,7 @@ def start_trace(name):
|
||||||
with open(schema_fn, 'r') as schema_file:
|
with open(schema_fn, 'r') as schema_file:
|
||||||
schema_xml = schema_file.read()
|
schema_xml = schema_file.read()
|
||||||
with STATE.trace.open_tx("Create Root Object"):
|
with STATE.trace.open_tx("Create Root Object"):
|
||||||
root = STATE.trace.create_root_object(schema_xml, 'Session')
|
root = STATE.trace.create_root_object(schema_xml, 'LldbSession')
|
||||||
root.set_value('_display', 'GNU lldb ' + util.LLDB_VERSION.full)
|
root.set_value('_display', 'GNU lldb ' + util.LLDB_VERSION.full)
|
||||||
util.set_convenience_variable('_ghidra_tracing', "true")
|
util.set_convenience_variable('_ghidra_tracing', "true")
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<context>
|
<context>
|
||||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
<schema name="LldbSession" elementResync="NEVER" attributeResync="NEVER">
|
||||||
<interface name="Access" />
|
<interface name="Access" />
|
||||||
<interface name="Attacher" />
|
<interface name="Attacher" />
|
||||||
<interface name="Interpreter" />
|
<interface name="Interpreter" />
|
||||||
|
|
|
@ -82,6 +82,10 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||||
|
|
||||||
protected abstract M createModel(Plugin plugin);
|
protected abstract M createModel(Plugin plugin);
|
||||||
|
|
||||||
|
protected void coordinatesChanged() {
|
||||||
|
// Extension point
|
||||||
|
}
|
||||||
|
|
||||||
public void goToCoordinates(DebuggerCoordinates coords) {
|
public void goToCoordinates(DebuggerCoordinates coords) {
|
||||||
if (DebuggerCoordinates.equalsIgnoreRecorderAndView(current, coords)) {
|
if (DebuggerCoordinates.equalsIgnoreRecorderAndView(current, coords)) {
|
||||||
return;
|
return;
|
||||||
|
@ -102,14 +106,20 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||||
if (limitToSnap) {
|
if (limitToSnap) {
|
||||||
tableModel.setSpan(Lifespan.at(current.getSnap()));
|
tableModel.setSpan(Lifespan.at(current.getSnap()));
|
||||||
}
|
}
|
||||||
|
coordinatesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reload() {
|
public void reload() {
|
||||||
tableModel.reload();
|
tableModel.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void queryChanged() {
|
||||||
|
// Extension point
|
||||||
|
}
|
||||||
|
|
||||||
public void setQuery(ModelQuery query) {
|
public void setQuery(ModelQuery query) {
|
||||||
tableModel.setQuery(query);
|
tableModel.setQuery(query);
|
||||||
|
queryChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelQuery getQuery() {
|
public ModelQuery getQuery() {
|
||||||
|
@ -128,12 +138,16 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||||
return limitToSnap;
|
return limitToSnap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void showHiddenChanged() {
|
||||||
|
tableModel.setShowHidden(showHidden);
|
||||||
|
}
|
||||||
|
|
||||||
public void setShowHidden(boolean showHidden) {
|
public void setShowHidden(boolean showHidden) {
|
||||||
if (this.showHidden == showHidden) {
|
if (this.showHidden == showHidden) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.showHidden = showHidden;
|
this.showHidden = showHidden;
|
||||||
tableModel.setShowHidden(showHidden);
|
showHiddenChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isShowHidden() {
|
public boolean isShowHidden() {
|
||||||
|
|
|
@ -115,6 +115,9 @@ public class ModelQuery {
|
||||||
|
|
||||||
public List<TargetObjectSchema> computeSchemas(Trace trace) {
|
public List<TargetObjectSchema> computeSchemas(Trace trace) {
|
||||||
TargetObjectSchema rootSchema = trace.getObjectManager().getRootSchema();
|
TargetObjectSchema rootSchema = trace.getObjectManager().getRootSchema();
|
||||||
|
if (rootSchema == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
return predicates.getPatterns()
|
return predicates.getPatterns()
|
||||||
.stream()
|
.stream()
|
||||||
.map(p -> rootSchema.getSuccessorSchema(p.asPath()))
|
.map(p -> rootSchema.getSuccessorSchema(p.asPath()))
|
||||||
|
|
|
@ -21,8 +21,9 @@ import java.util.stream.*;
|
||||||
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.DynamicTableColumn;
|
||||||
import docking.widgets.table.RangeCursorTableHeaderRenderer.SeekListener;
|
import docking.widgets.table.RangeCursorTableHeaderRenderer.SeekListener;
|
||||||
|
import docking.widgets.table.TableColumnDescriptor;
|
||||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
|
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
|
||||||
import ghidra.app.plugin.core.debug.gui.model.columns.*;
|
import ghidra.app.plugin.core.debug.gui.model.columns.*;
|
||||||
import ghidra.dbg.target.schema.SchemaContext;
|
import ghidra.dbg.target.schema.SchemaContext;
|
||||||
|
@ -39,6 +40,7 @@ import ghidra.trace.model.target.TraceObject;
|
||||||
import ghidra.trace.model.target.TraceObjectValue;
|
import ghidra.trace.model.target.TraceObjectValue;
|
||||||
import ghidra.util.HTMLUtilities;
|
import ghidra.util.HTMLUtilities;
|
||||||
import ghidra.util.NumericUtilities;
|
import ghidra.util.NumericUtilities;
|
||||||
|
import ghidra.util.datastruct.ListenerSet;
|
||||||
|
|
||||||
public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||||
|
|
||||||
|
@ -442,59 +444,85 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||||
AttributeSchema attributeSchema) {
|
AttributeSchema attributeSchema) {
|
||||||
String name = attributeSchema.getName();
|
String name = attributeSchema.getName();
|
||||||
Class<?> type = computeAttributeType(ctx, attributeSchema);
|
Class<?> type = computeAttributeType(ctx, attributeSchema);
|
||||||
return new AutoAttributeColumn<>(name, type);
|
return new AutoAttributeColumn<>(name, type, attributeSchema.isHidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
public AutoAttributeColumn(String attributeName, Class<T> attributeType) {
|
final boolean hidden;
|
||||||
|
|
||||||
|
public AutoAttributeColumn(String attributeName, Class<T> attributeType, boolean hidden) {
|
||||||
super(attributeName, attributeType);
|
super(attributeName, attributeType);
|
||||||
|
this.hidden = hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHidden() {
|
||||||
|
return hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Save and restore these between sessions, esp., their settings
|
private TraceValueLifePlotColumn plotColumn;
|
||||||
|
private ListenerSet<SeekListener> seekListeners;
|
||||||
|
private SeekListener mySeekListener;
|
||||||
private Map<ColKey, TraceValueObjectAttributeColumn<?>> columnCache = new HashMap<>();
|
private Map<ColKey, TraceValueObjectAttributeColumn<?>> columnCache = new HashMap<>();
|
||||||
|
|
||||||
protected ObjectTableModel(Plugin plugin) {
|
protected ObjectTableModel(Plugin plugin) {
|
||||||
super("Object Model", plugin);
|
super("Object Model", plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected TraceValueLifePlotColumn newPlotColumn() {
|
||||||
|
return new TraceValueLifePlotColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TraceValueLifePlotColumn getPlotColumn() {
|
||||||
|
if (plotColumn == null) {
|
||||||
|
plotColumn = newPlotColumn();
|
||||||
|
plotColumn.setFullRange(computeFullRange());
|
||||||
|
getSeekListeners();
|
||||||
|
plotColumn.addSeekListener(mySeekListener);
|
||||||
|
}
|
||||||
|
return plotColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ListenerSet<SeekListener> getSeekListeners() {
|
||||||
|
if (seekListeners == null) {
|
||||||
|
seekListeners = new ListenerSet<>(SeekListener.class, true);
|
||||||
|
// Can't use the proxy directly, as Set will invoke hashCode/equals
|
||||||
|
mySeekListener = seekListeners.invoke()::accept;
|
||||||
|
}
|
||||||
|
return seekListeners;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void traceChanged() {
|
protected void traceChanged() {
|
||||||
reloadAttributeColumns();
|
reloadColumns();
|
||||||
|
fireTableStructureChanged();
|
||||||
updateTimelineMax();
|
updateTimelineMax();
|
||||||
super.traceChanged();
|
super.traceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void queryChanged() {
|
protected void queryChanged() {
|
||||||
reloadAttributeColumns();
|
reloadColumns();
|
||||||
|
fireTableStructureChanged();
|
||||||
super.queryChanged();
|
super.queryChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showHiddenChanged() {
|
|
||||||
reloadAttributeColumns();
|
|
||||||
super.showHiddenChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void maxSnapChanged() {
|
protected void maxSnapChanged() {
|
||||||
updateTimelineMax();
|
updateTimelineMax();
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateTimelineMax() {
|
protected Lifespan computeFullRange() {
|
||||||
Long max = getTrace() == null ? null : getTrace().getTimeManager().getMaxSnap();
|
Long max = getTrace() == null ? null : getTrace().getTimeManager().getMaxSnap();
|
||||||
Lifespan fullRange = Lifespan.span(0L, max == null ? 1 : max + 1);
|
return Lifespan.span(0L, max == null ? 1 : max + 1);
|
||||||
int count = getColumnCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
DynamicTableColumn<ValueRow, ?, ?> column = getColumn(i);
|
|
||||||
if (column instanceof TraceValueLifePlotColumn plotCol) {
|
|
||||||
plotCol.setFullRange(fullRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<AttributeSchema> computeAttributeSchemas() {
|
protected void updateTimelineMax() {
|
||||||
|
Lifespan fullRange = computeFullRange();
|
||||||
|
getPlotColumn().setFullRange(fullRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<AttributeSchema> computeAttributes() {
|
||||||
Trace trace = getTrace();
|
Trace trace = getTrace();
|
||||||
ModelQuery query = getQuery();
|
ModelQuery query = getQuery();
|
||||||
if (trace == null || query == null) {
|
if (trace == null || query == null) {
|
||||||
|
@ -506,91 +534,10 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||||
}
|
}
|
||||||
SchemaContext ctx = rootSchema.getContext();
|
SchemaContext ctx = rootSchema.getContext();
|
||||||
return query.computeAttributes(trace)
|
return query.computeAttributes(trace)
|
||||||
.filter(a -> isShowHidden() || !a.isHidden())
|
|
||||||
.filter(a -> !ctx.getSchema(a.getSchema()).isCanonicalContainer())
|
.filter(a -> !ctx.getSchema(a.getSchema()).isCanonicalContainer())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void reloadAttributeColumns() {
|
|
||||||
List<AttributeSchema> attributes;
|
|
||||||
Trace trace = getTrace();
|
|
||||||
ModelQuery query = getQuery();
|
|
||||||
if (trace == null || query == null || trace.getObjectManager().getRootSchema() == null) {
|
|
||||||
attributes = List.of();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SchemaContext ctx = trace.getObjectManager().getRootSchema().getContext();
|
|
||||||
attributes = query.computeAttributes(trace)
|
|
||||||
.filter(a -> !ctx.getSchema(a.getSchema()).isCanonicalContainer())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
resyncAttributeColumns(attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Set<DynamicTableColumn<ValueRow, ?, ?>> computeAttributeColumns(
|
|
||||||
Collection<AttributeSchema> attributes) {
|
|
||||||
if (attributes == null) {
|
|
||||||
return Set.of();
|
|
||||||
}
|
|
||||||
Trace trace = getTrace();
|
|
||||||
if (trace == null) {
|
|
||||||
return Set.of();
|
|
||||||
}
|
|
||||||
TargetObjectSchema rootSchema = trace.getObjectManager().getRootSchema();
|
|
||||||
if (rootSchema == null) {
|
|
||||||
return Set.of();
|
|
||||||
}
|
|
||||||
SchemaContext ctx = rootSchema.getContext();
|
|
||||||
return attributes.stream()
|
|
||||||
.map(as -> columnCache.computeIfAbsent(ColKey.fromSchema(ctx, as),
|
|
||||||
ck -> AutoAttributeColumn.fromSchema(ctx, as)))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void resyncAttributeColumns(Collection<AttributeSchema> attributes) {
|
|
||||||
Map<Boolean, List<AttributeSchema>> byVisible = attributes == null ? Map.of()
|
|
||||||
: attributes.stream()
|
|
||||||
.collect(Collectors.groupingBy(a -> !a.isHidden() || isShowHidden()));
|
|
||||||
Set<DynamicTableColumn<ValueRow, ?, ?>> visibleColumns =
|
|
||||||
new HashSet<>(computeAttributeColumns(byVisible.get(true)));
|
|
||||||
Set<DynamicTableColumn<ValueRow, ?, ?>> hiddenColumns =
|
|
||||||
new HashSet<>(computeAttributeColumns(byVisible.get(false)));
|
|
||||||
Set<DynamicTableColumn<ValueRow, ?, ?>> toRemove = new HashSet<>();
|
|
||||||
boolean[] removedIndices = new boolean[getColumnCount()];
|
|
||||||
for (int i = 0; i < getColumnCount(); i++) {
|
|
||||||
DynamicTableColumn<ValueRow, ?, ?> exists = getColumn(i);
|
|
||||||
if (!(exists instanceof AutoAttributeColumn)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!visibleColumns.remove(exists) && !hiddenColumns.remove(exists)) {
|
|
||||||
toRemove.add(exists);
|
|
||||||
removedIndices[i] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TableSortState curSortState = getTableSortState();
|
|
||||||
TableSortStateEditor newStateEditor = new TableSortStateEditor();
|
|
||||||
for (ColumnSortState css : curSortState.getAllSortStates()) {
|
|
||||||
int index = css.getColumnModelIndex();
|
|
||||||
if (removedIndices[index]) {
|
|
||||||
continue; // Don't add too new
|
|
||||||
}
|
|
||||||
int precedingRemoved = 0;
|
|
||||||
for (int i = 0; i < index; i++) {
|
|
||||||
if (removedIndices[index]) {
|
|
||||||
precedingRemoved++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newStateEditor.addSortedColumn(index - precedingRemoved, css.getSortDirection());
|
|
||||||
}
|
|
||||||
TableSortState newSortState = newStateEditor.createTableSortState();
|
|
||||||
|
|
||||||
setTableSortState(TableSortState.createUnsortedSortState());
|
|
||||||
removeTableColumns(toRemove);
|
|
||||||
addTableColumns(visibleColumns, true);
|
|
||||||
addTableColumns(hiddenColumns, false);
|
|
||||||
setTableSortState(newSortState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Stream<ValueRow> streamRows(Trace trace, ModelQuery query, Lifespan span) {
|
protected Stream<ValueRow> streamRows(Trace trace, ModelQuery query, Lifespan span) {
|
||||||
return distinctCanonical(query.streamValues(trace, span)
|
return distinctCanonical(query.streamValues(trace, span)
|
||||||
|
@ -604,10 +551,37 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||||
descriptor.addVisibleColumn(new TraceValueKeyColumn(), 1, true);
|
descriptor.addVisibleColumn(new TraceValueKeyColumn(), 1, true);
|
||||||
descriptor.addVisibleColumn(new TraceValueValColumn());
|
descriptor.addVisibleColumn(new TraceValueValColumn());
|
||||||
descriptor.addVisibleColumn(new TraceValueLifeColumn(), 2, true);
|
descriptor.addVisibleColumn(new TraceValueLifeColumn(), 2, true);
|
||||||
descriptor.addHiddenColumn(new TraceValueLifePlotColumn());
|
descriptor.addHiddenColumn(getPlotColumn());
|
||||||
|
|
||||||
|
appendAttributeColumns(descriptor);
|
||||||
|
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void appendAttributeColumns(TableColumnDescriptor<ValueRow> descriptor) {
|
||||||
|
Trace trace = getTrace();
|
||||||
|
if (trace == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TargetObjectSchema rootSchema = trace.getObjectManager().getRootSchema();
|
||||||
|
if (rootSchema == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SchemaContext ctx = rootSchema.getContext();
|
||||||
|
List<AttributeSchema> attributes = computeAttributes();
|
||||||
|
for (AttributeSchema as : attributes) {
|
||||||
|
TraceValueObjectAttributeColumn<?> column =
|
||||||
|
columnCache.computeIfAbsent(ColKey.fromSchema(ctx, as),
|
||||||
|
ck -> AutoAttributeColumn.fromSchema(ctx, as));
|
||||||
|
if (as.isHidden()) {
|
||||||
|
descriptor.addHiddenColumn(column);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
descriptor.addVisibleColumn(column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValueRow findTraceObject(TraceObject object) {
|
public ValueRow findTraceObject(TraceObject object) {
|
||||||
for (ValueRow row : getModelData()) {
|
for (ValueRow row : getModelData()) {
|
||||||
|
@ -675,25 +649,12 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||||
@Override
|
@Override
|
||||||
protected void snapChanged() {
|
protected void snapChanged() {
|
||||||
super.snapChanged();
|
super.snapChanged();
|
||||||
long snap = getSnap();
|
getPlotColumn().setSnap(getSnap());
|
||||||
int count = getColumnCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
DynamicTableColumn<ValueRow, ?, ?> column = getColumn(i);
|
|
||||||
if (column instanceof TraceValueLifePlotColumn plotCol) {
|
|
||||||
plotCol.setSnap(snap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addSeekListener(SeekListener listener) {
|
public void addSeekListener(SeekListener listener) {
|
||||||
int count = getColumnCount();
|
getSeekListeners().add(listener);
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
DynamicTableColumn<ValueRow, ?, ?> column = getColumn(i);
|
|
||||||
if (column instanceof TraceValueLifePlotColumn plotCol) {
|
|
||||||
plotCol.addSeekListener(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -765,4 +726,13 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||||
ServiceProvider serviceProvider) {
|
ServiceProvider serviceProvider) {
|
||||||
editable.setValue(t, (COLUMN_TYPE) aValue, settings, dataSource, serviceProvider);
|
editable.setValue(t, (COLUMN_TYPE) aValue, settings, dataSource, serviceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVisibleByDefault(int modelIndex) {
|
||||||
|
DynamicTableColumn<ValueRow, ?, ?> column = tableColumns.get(modelIndex);
|
||||||
|
if (column instanceof AutoAttributeColumn<?> && isShowHidden()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.isVisibleByDefault(modelIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,25 @@
|
||||||
package ghidra.app.plugin.core.debug.gui.model;
|
package ghidra.app.plugin.core.debug.gui.model;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
|
|
||||||
|
import docking.widgets.table.GTableColumnModel;
|
||||||
import docking.widgets.table.GTableTextCellEditor;
|
import docking.widgets.table.GTableTextCellEditor;
|
||||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.*;
|
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.*;
|
||||||
|
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||||
import ghidra.framework.plugintool.Plugin;
|
import ghidra.framework.plugintool.Plugin;
|
||||||
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.target.TraceObject;
|
import ghidra.trace.model.target.TraceObject;
|
||||||
|
|
||||||
public class ObjectsTablePanel extends AbstractQueryTablePanel<ValueRow, ObjectTableModel> {
|
public class ObjectsTablePanel extends AbstractQueryTablePanel<ValueRow, ObjectTableModel> {
|
||||||
|
|
||||||
|
private static final String DEFAULT_PREF_KEY = "DEFAULT";
|
||||||
|
|
||||||
private static class PropertyEditor extends GTableTextCellEditor {
|
private static class PropertyEditor extends GTableTextCellEditor {
|
||||||
private final JTextField textField;
|
private final JTextField textField;
|
||||||
|
|
||||||
|
@ -73,4 +81,71 @@ public class ObjectsTablePanel extends AbstractQueryTablePanel<ValueRow, ObjectT
|
||||||
setSelectedItem(row);
|
setSelectedItem(row);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String computePreferenceKey() {
|
||||||
|
Trace trace = tableModel.getTrace();
|
||||||
|
if (trace == null) {
|
||||||
|
return DEFAULT_PREF_KEY;
|
||||||
|
}
|
||||||
|
ModelQuery query = tableModel.getQuery();
|
||||||
|
if (query == null) {
|
||||||
|
return DEFAULT_PREF_KEY;
|
||||||
|
}
|
||||||
|
List<TargetObjectSchema> schemas = query.computeSchemas(trace);
|
||||||
|
if (schemas.isEmpty()) {
|
||||||
|
return DEFAULT_PREF_KEY;
|
||||||
|
}
|
||||||
|
TargetObjectSchema rootSchema = trace.getObjectManager().getRootSchema();
|
||||||
|
if (rootSchema == null) {
|
||||||
|
return DEFAULT_PREF_KEY;
|
||||||
|
}
|
||||||
|
return rootSchema + ":" + schemas
|
||||||
|
.stream()
|
||||||
|
.map(s -> s.getName().toString())
|
||||||
|
.collect(Collectors.joining(",")) +
|
||||||
|
":" + (isShowHidden() ? "show" : "hide");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showHiddenColumns(boolean show) {
|
||||||
|
if (table.getColumnModel() instanceof GTableColumnModel columnModel) {
|
||||||
|
for (TableColumn tCol : columnModel.getAllColumns()) {
|
||||||
|
int modelIndex = tCol.getModelIndex();
|
||||||
|
if (tableModel
|
||||||
|
.getColumn(modelIndex) instanceof AutoAttributeColumn<?> attrCol) {
|
||||||
|
if (attrCol.isHidden()) {
|
||||||
|
columnModel.setVisible(tCol, show);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void reloadPreferences() {
|
||||||
|
String prefKey = computePreferenceKey();
|
||||||
|
if (!prefKey.equals(table.getPreferenceKey())) {
|
||||||
|
table.setPreferenceKey(prefKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void resyncAttributeVisibility() {
|
||||||
|
showHiddenColumns(isShowHidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void coordinatesChanged() {
|
||||||
|
super.coordinatesChanged();
|
||||||
|
reloadPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void queryChanged() {
|
||||||
|
super.queryChanged();
|
||||||
|
reloadPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showHiddenChanged() {
|
||||||
|
super.showHiddenChanged();
|
||||||
|
resyncAttributeVisibility();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class TracePathLastLifespanPlotColumn
|
||||||
|
|
||||||
private final SpanTableCellRenderer<Long> cellRenderer = new SpanTableCellRenderer<>();
|
private final SpanTableCellRenderer<Long> cellRenderer = new SpanTableCellRenderer<>();
|
||||||
private final RangeCursorTableHeaderRenderer<Long> headerRenderer =
|
private final RangeCursorTableHeaderRenderer<Long> headerRenderer =
|
||||||
new RangeCursorTableHeaderRenderer<>(0L);
|
new RangeCursorTableHeaderRenderer<>(0L, this);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnName() {
|
public String getColumnName() {
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class TraceValueLifePlotColumn
|
||||||
|
|
||||||
private final SpanSetTableCellRenderer<Long> cellRenderer = new SpanSetTableCellRenderer<>();
|
private final SpanSetTableCellRenderer<Long> cellRenderer = new SpanSetTableCellRenderer<>();
|
||||||
private final RangeCursorTableHeaderRenderer<Long> headerRenderer =
|
private final RangeCursorTableHeaderRenderer<Long> headerRenderer =
|
||||||
new RangeCursorTableHeaderRenderer<>(0L);
|
new RangeCursorTableHeaderRenderer<>(0L, this);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnName() {
|
public String getColumnName() {
|
||||||
|
|
|
@ -189,8 +189,7 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
|
||||||
|
|
||||||
/* package access for testing */
|
/* package access for testing */
|
||||||
final SpanTableCellRenderer<Long> spanRenderer = new SpanTableCellRenderer<>();
|
final SpanTableCellRenderer<Long> spanRenderer = new SpanTableCellRenderer<>();
|
||||||
final RangeCursorTableHeaderRenderer<Long> headerRenderer =
|
final RangeCursorTableHeaderRenderer<Long> headerRenderer;
|
||||||
new RangeCursorTableHeaderRenderer<>(0L);
|
|
||||||
|
|
||||||
final TableCellRenderer boldCurrentRenderer = new AbstractGColumnRenderer<Object>() {
|
final TableCellRenderer boldCurrentRenderer = new AbstractGColumnRenderer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -226,6 +225,8 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
|
||||||
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
|
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
|
||||||
|
|
||||||
threadTableModel = new ThreadTableModel(provider);
|
threadTableModel = new ThreadTableModel(provider);
|
||||||
|
headerRenderer = new RangeCursorTableHeaderRenderer<>(0L,
|
||||||
|
threadTableModel.getColumn(ThreadTableColumns.PLOT.ordinal()));
|
||||||
threadTable = new GhidraTable(threadTableModel);
|
threadTable = new GhidraTable(threadTableModel);
|
||||||
threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
add(new JScrollPane(threadTable));
|
add(new JScrollPane(threadTable));
|
||||||
|
|
|
@ -195,6 +195,11 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
|
||||||
super(plugin);
|
super(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TraceValueLifePlotColumn newPlotColumn() {
|
||||||
|
return new ThreadPlotColumn();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TableColumnDescriptor<ValueRow> createTableColumnDescriptor() {
|
protected TableColumnDescriptor<ValueRow> createTableColumnDescriptor() {
|
||||||
TableColumnDescriptor<ValueRow> descriptor = new TableColumnDescriptor<>();
|
TableColumnDescriptor<ValueRow> descriptor = new TableColumnDescriptor<>();
|
||||||
|
@ -206,7 +211,7 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
|
||||||
descriptor.addHiddenColumn(new ThreadSpColumn());
|
descriptor.addHiddenColumn(new ThreadSpColumn());
|
||||||
descriptor.addVisibleColumn(new ThreadStateColumn());
|
descriptor.addVisibleColumn(new ThreadStateColumn());
|
||||||
descriptor.addHiddenColumn(new ThreadCommentColumn());
|
descriptor.addHiddenColumn(new ThreadCommentColumn());
|
||||||
descriptor.addVisibleColumn(new ThreadPlotColumn());
|
descriptor.addVisibleColumn(getPlotColumn());
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1185,23 +1185,24 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected TraceValueLifePlotColumn getPlotColumn() {
|
||||||
|
return findColumnOfType(modelProvider.elementsTablePanel.tableModel,
|
||||||
|
TraceValueLifePlotColumn.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLifePlotColumnFitsSnapshotsOnActivate() throws Throwable {
|
public void testLifePlotColumnFitsSnapshotsOnActivate() throws Throwable {
|
||||||
TraceValueLifePlotColumn plotCol = findColumnOfType(
|
|
||||||
modelProvider.elementsTablePanel.tableModel, TraceValueLifePlotColumn.class);
|
|
||||||
createTraceAndPopulateObjects();
|
createTraceAndPopulateObjects();
|
||||||
|
|
||||||
traceManager.activateTrace(tb.trace);
|
traceManager.activateTrace(tb.trace);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
||||||
// NB. The plot adds a margin of 1
|
// NB. The plot adds a margin of 1
|
||||||
assertEquals(Lifespan.span(0, 21), plotCol.getFullRange());
|
assertEquals(Lifespan.span(0, 21), getPlotColumn().getFullRange());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLifePlotColumnFitsSnapshotsOnAddSnapshot() throws Throwable {
|
public void testLifePlotColumnFitsSnapshotsOnAddSnapshot() throws Throwable {
|
||||||
TraceValueLifePlotColumn plotCol = findColumnOfType(
|
|
||||||
modelProvider.elementsTablePanel.tableModel, TraceValueLifePlotColumn.class);
|
|
||||||
createTraceAndPopulateObjects();
|
createTraceAndPopulateObjects();
|
||||||
|
|
||||||
traceManager.activateTrace(tb.trace);
|
traceManager.activateTrace(tb.trace);
|
||||||
|
@ -1213,20 +1214,18 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest
|
||||||
waitForDomainObject(tb.trace);
|
waitForDomainObject(tb.trace);
|
||||||
|
|
||||||
// NB. The plot adds a margin of 1
|
// NB. The plot adds a margin of 1
|
||||||
assertEquals(Lifespan.span(0, 31), plotCol.getFullRange());
|
assertEquals(Lifespan.span(0, 31), getPlotColumn().getFullRange());
|
||||||
|
|
||||||
try (Transaction tx = tb.startTransaction()) {
|
try (Transaction tx = tb.startTransaction()) {
|
||||||
tb.trace.getTimeManager().getSnapshot(31, true);
|
tb.trace.getTimeManager().getSnapshot(31, true);
|
||||||
}
|
}
|
||||||
waitForDomainObject(tb.trace);
|
waitForDomainObject(tb.trace);
|
||||||
|
|
||||||
assertEquals(Lifespan.span(0, 32), plotCol.getFullRange());
|
assertEquals(Lifespan.span(0, 32), getPlotColumn().getFullRange());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLifePlotColumnFitsSnapshotsOnAddSnapshotSupressEvents() throws Throwable {
|
public void testLifePlotColumnFitsSnapshotsOnAddSnapshotSupressEvents() throws Throwable {
|
||||||
TraceValueLifePlotColumn plotCol = findColumnOfType(
|
|
||||||
modelProvider.elementsTablePanel.tableModel, TraceValueLifePlotColumn.class);
|
|
||||||
createTraceAndPopulateObjects();
|
createTraceAndPopulateObjects();
|
||||||
|
|
||||||
traceManager.activateTrace(tb.trace);
|
traceManager.activateTrace(tb.trace);
|
||||||
|
@ -1240,6 +1239,6 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest
|
||||||
waitForDomainObject(tb.trace);
|
waitForDomainObject(tb.trace);
|
||||||
|
|
||||||
// NB. The plot adds a margin of 1
|
// NB. The plot adds a margin of 1
|
||||||
assertEquals(Lifespan.span(0, 31), plotCol.getFullRange());
|
assertEquals(Lifespan.span(0, 31), getPlotColumn().getFullRange());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
package docking.widgets.table;
|
package docking.widgets.table;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.*;
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
|
@ -34,26 +33,49 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||||
|
|
||||||
protected class ForSeekMouseListener extends MouseAdapter {
|
protected class ForSeekMouseListener extends MouseAdapter {
|
||||||
|
|
||||||
|
private boolean checkRemove() {
|
||||||
|
if (savedTable == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TableModel unwrapped = RowObjectTableModel.unwrap(savedTable.getModel());
|
||||||
|
if (!(unwrapped instanceof DynamicColumnTableModel<?> model)) {
|
||||||
|
setSavedTable(null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int count = model.getColumnCount();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (model.getColumn(i) == col) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSavedTable(null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
if (checkRemove()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
|
if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((e.getButton() != MouseEvent.BUTTON1)) {
|
if ((e.getButton() != MouseEvent.BUTTON1)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.consume();
|
|
||||||
doSeek(e);
|
doSeek(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged(MouseEvent e) {
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
if (checkRemove()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int onmask = MouseEvent.BUTTON1_DOWN_MASK;
|
int onmask = MouseEvent.BUTTON1_DOWN_MASK;
|
||||||
int offmask = MouseEvent.SHIFT_DOWN_MASK;
|
int offmask = MouseEvent.SHIFT_DOWN_MASK;
|
||||||
if ((e.getModifiersEx() & (onmask | offmask)) != onmask) {
|
if ((e.getModifiersEx() & (onmask | offmask)) != onmask) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.consume();
|
|
||||||
doSeek(e);
|
doSeek(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +106,7 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||||
|
|
||||||
double pos =
|
double pos =
|
||||||
span * (e.getX() - colX) / myViewCol.getWidth() + fullRangeDouble.min();
|
span * (e.getX() - colX) / myViewCol.getWidth() + fullRangeDouble.min();
|
||||||
|
e.consume();
|
||||||
listeners.invoke().accept(pos);
|
listeners.invoke().accept(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +122,7 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||||
protected Span<N, ?> fullRange;
|
protected Span<N, ?> fullRange;
|
||||||
|
|
||||||
protected N pos;
|
protected N pos;
|
||||||
|
protected final DynamicTableColumn<?, ?, ?> col;
|
||||||
protected double doublePos;
|
protected double doublePos;
|
||||||
|
|
||||||
private JTable savedTable;
|
private JTable savedTable;
|
||||||
|
@ -107,8 +131,9 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||||
private final ForSeekMouseListener forSeekMouseListener = new ForSeekMouseListener();
|
private final ForSeekMouseListener forSeekMouseListener = new ForSeekMouseListener();
|
||||||
private final ListenerSet<SeekListener> listeners = new ListenerSet<>(SeekListener.class, true);
|
private final ListenerSet<SeekListener> listeners = new ListenerSet<>(SeekListener.class, true);
|
||||||
|
|
||||||
public RangeCursorTableHeaderRenderer(N pos) {
|
public RangeCursorTableHeaderRenderer(N pos, DynamicTableColumn<?, ?, ?> col) {
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
|
this.col = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -123,6 +148,9 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setSavedTable(JTable table) {
|
protected void setSavedTable(JTable table) {
|
||||||
|
if (savedTable == table) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (savedTable != null) {
|
if (savedTable != null) {
|
||||||
JTableHeader header = savedTable.getTableHeader();
|
JTableHeader header = savedTable.getTableHeader();
|
||||||
header.removeMouseListener(forSeekMouseListener);
|
header.removeMouseListener(forSeekMouseListener);
|
||||||
|
@ -131,8 +159,23 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||||
savedTable = table;
|
savedTable = table;
|
||||||
if (savedTable != null) {
|
if (savedTable != null) {
|
||||||
JTableHeader header = savedTable.getTableHeader();
|
JTableHeader header = savedTable.getTableHeader();
|
||||||
|
// I need firstsies. SHIFT key will pass event down the chain.
|
||||||
|
MouseListener[] curMouseListeners = header.getMouseListeners();
|
||||||
|
MouseMotionListener[] curMotionListeners = header.getMouseMotionListeners();
|
||||||
|
for (MouseListener l : curMouseListeners) {
|
||||||
|
header.removeMouseListener(l);
|
||||||
|
}
|
||||||
|
for (MouseMotionListener l : curMotionListeners) {
|
||||||
|
header.removeMouseMotionListener(l);
|
||||||
|
}
|
||||||
header.addMouseListener(forSeekMouseListener);
|
header.addMouseListener(forSeekMouseListener);
|
||||||
header.addMouseMotionListener(forSeekMouseListener);
|
header.addMouseMotionListener(forSeekMouseListener);
|
||||||
|
for (MouseListener l : curMouseListeners) {
|
||||||
|
header.addMouseListener(l);
|
||||||
|
}
|
||||||
|
for (MouseMotionListener l : curMotionListeners) {
|
||||||
|
header.addMouseMotionListener(l);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,8 +214,9 @@ public class DemoSpanCellRendererTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
|
|
||||||
TableColumn column = table.getColumnModel().getColumn(MyColumns.LIFESPAN.ordinal());
|
TableColumn column = table.getColumnModel().getColumn(MyColumns.LIFESPAN.ordinal());
|
||||||
SpanTableCellRenderer<Integer> rangeRenderer = new SpanTableCellRenderer<>();
|
SpanTableCellRenderer<Integer> rangeRenderer = new SpanTableCellRenderer<>();
|
||||||
|
DynamicTableColumn<MyRow, ?, ?> col = model.getColumn(MyColumns.LIFESPAN.ordinal());
|
||||||
RangeCursorTableHeaderRenderer<Integer> headerRenderer =
|
RangeCursorTableHeaderRenderer<Integer> headerRenderer =
|
||||||
new RangeCursorTableHeaderRenderer<>(0);
|
new RangeCursorTableHeaderRenderer<>(0, col);
|
||||||
column.setCellRenderer(rangeRenderer);
|
column.setCellRenderer(rangeRenderer);
|
||||||
column.setHeaderRenderer(headerRenderer);
|
column.setHeaderRenderer(headerRenderer);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue