mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-24 21:21:56 +00:00
Merge remote-tracking branch 'origin/GP-4363_Dan_fixElementsTableColumns--SQUASHED'
This commit is contained in:
commit
6fdb63a2cc
@ -209,7 +209,7 @@ def start_trace(name):
|
||||
with open(schema_fn, 'r') as schema_file:
|
||||
schema_xml = schema_file.read()
|
||||
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')
|
||||
util.set_convenience_variable('_ghidra_tracing', "true")
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<context>
|
||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
||||
<schema name="DbgengSession" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Access" />
|
||||
<interface name="Attacher" />
|
||||
<interface name="Interpreter" />
|
||||
|
@ -275,7 +275,7 @@ def start_trace(name):
|
||||
with open(schema_fn, 'r') as schema_file:
|
||||
schema_xml = schema_file.read()
|
||||
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)
|
||||
STATE.trace.create_object(AVAILABLES_PATH).insert()
|
||||
STATE.trace.create_object(BREAKPOINTS_PATH).insert()
|
||||
|
@ -1,5 +1,5 @@
|
||||
<context>
|
||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
||||
<schema name="GdbSession" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Access" />
|
||||
<interface name="Attacher" />
|
||||
<interface name="Interpreter" />
|
||||
@ -257,7 +257,6 @@
|
||||
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||
<attribute name="Advance" schema="Method" required="yes" fixed="yes" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Module" elementResync="NEVER" attributeResync="NEVER">
|
||||
|
@ -261,7 +261,7 @@ def start_trace(name):
|
||||
with open(schema_fn, 'r') as schema_file:
|
||||
schema_xml = schema_file.read()
|
||||
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)
|
||||
util.set_convenience_variable('_ghidra_tracing', "true")
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<context>
|
||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
||||
<schema name="LldbSession" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Access" />
|
||||
<interface name="Attacher" />
|
||||
<interface name="Interpreter" />
|
||||
|
@ -82,6 +82,10 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||
|
||||
protected abstract M createModel(Plugin plugin);
|
||||
|
||||
protected void coordinatesChanged() {
|
||||
// Extension point
|
||||
}
|
||||
|
||||
public void goToCoordinates(DebuggerCoordinates coords) {
|
||||
if (DebuggerCoordinates.equalsIgnoreRecorderAndView(current, coords)) {
|
||||
return;
|
||||
@ -102,14 +106,20 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||
if (limitToSnap) {
|
||||
tableModel.setSpan(Lifespan.at(current.getSnap()));
|
||||
}
|
||||
coordinatesChanged();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
tableModel.reload();
|
||||
}
|
||||
|
||||
protected void queryChanged() {
|
||||
// Extension point
|
||||
}
|
||||
|
||||
public void setQuery(ModelQuery query) {
|
||||
tableModel.setQuery(query);
|
||||
queryChanged();
|
||||
}
|
||||
|
||||
public ModelQuery getQuery() {
|
||||
@ -128,12 +138,16 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||
return limitToSnap;
|
||||
}
|
||||
|
||||
protected void showHiddenChanged() {
|
||||
tableModel.setShowHidden(showHidden);
|
||||
}
|
||||
|
||||
public void setShowHidden(boolean showHidden) {
|
||||
if (this.showHidden == showHidden) {
|
||||
return;
|
||||
}
|
||||
this.showHidden = showHidden;
|
||||
tableModel.setShowHidden(showHidden);
|
||||
showHiddenChanged();
|
||||
}
|
||||
|
||||
public boolean isShowHidden() {
|
||||
|
@ -115,6 +115,9 @@ public class ModelQuery {
|
||||
|
||||
public List<TargetObjectSchema> computeSchemas(Trace trace) {
|
||||
TargetObjectSchema rootSchema = trace.getObjectManager().getRootSchema();
|
||||
if (rootSchema == null) {
|
||||
return List.of();
|
||||
}
|
||||
return predicates.getPatterns()
|
||||
.stream()
|
||||
.map(p -> rootSchema.getSuccessorSchema(p.asPath()))
|
||||
|
@ -21,8 +21,9 @@ import java.util.stream.*;
|
||||
|
||||
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.TableColumnDescriptor;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
|
||||
import ghidra.app.plugin.core.debug.gui.model.columns.*;
|
||||
import ghidra.dbg.target.schema.SchemaContext;
|
||||
@ -39,6 +40,7 @@ import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.datastruct.ListenerSet;
|
||||
|
||||
public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||
|
||||
@ -442,59 +444,85 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||
AttributeSchema attributeSchema) {
|
||||
String name = attributeSchema.getName();
|
||||
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);
|
||||
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<>();
|
||||
|
||||
protected ObjectTableModel(Plugin 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
|
||||
protected void traceChanged() {
|
||||
reloadAttributeColumns();
|
||||
reloadColumns();
|
||||
fireTableStructureChanged();
|
||||
updateTimelineMax();
|
||||
super.traceChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void queryChanged() {
|
||||
reloadAttributeColumns();
|
||||
reloadColumns();
|
||||
fireTableStructureChanged();
|
||||
super.queryChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showHiddenChanged() {
|
||||
reloadAttributeColumns();
|
||||
super.showHiddenChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void maxSnapChanged() {
|
||||
updateTimelineMax();
|
||||
refresh();
|
||||
}
|
||||
|
||||
protected void updateTimelineMax() {
|
||||
protected Lifespan computeFullRange() {
|
||||
Long max = getTrace() == null ? null : getTrace().getTimeManager().getMaxSnap();
|
||||
Lifespan fullRange = 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);
|
||||
}
|
||||
}
|
||||
return Lifespan.span(0L, max == null ? 1 : max + 1);
|
||||
}
|
||||
|
||||
protected List<AttributeSchema> computeAttributeSchemas() {
|
||||
protected void updateTimelineMax() {
|
||||
Lifespan fullRange = computeFullRange();
|
||||
getPlotColumn().setFullRange(fullRange);
|
||||
}
|
||||
|
||||
protected List<AttributeSchema> computeAttributes() {
|
||||
Trace trace = getTrace();
|
||||
ModelQuery query = getQuery();
|
||||
if (trace == null || query == null) {
|
||||
@ -506,91 +534,10 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||
}
|
||||
SchemaContext ctx = rootSchema.getContext();
|
||||
return query.computeAttributes(trace)
|
||||
.filter(a -> isShowHidden() || !a.isHidden())
|
||||
.filter(a -> !ctx.getSchema(a.getSchema()).isCanonicalContainer())
|
||||
.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
|
||||
protected Stream<ValueRow> streamRows(Trace trace, ModelQuery query, Lifespan 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 TraceValueValColumn());
|
||||
descriptor.addVisibleColumn(new TraceValueLifeColumn(), 2, true);
|
||||
descriptor.addHiddenColumn(new TraceValueLifePlotColumn());
|
||||
descriptor.addHiddenColumn(getPlotColumn());
|
||||
|
||||
appendAttributeColumns(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
|
||||
public ValueRow findTraceObject(TraceObject object) {
|
||||
for (ValueRow row : getModelData()) {
|
||||
@ -675,25 +649,12 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||
@Override
|
||||
protected void snapChanged() {
|
||||
super.snapChanged();
|
||||
long snap = getSnap();
|
||||
int count = getColumnCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
DynamicTableColumn<ValueRow, ?, ?> column = getColumn(i);
|
||||
if (column instanceof TraceValueLifePlotColumn plotCol) {
|
||||
plotCol.setSnap(snap);
|
||||
}
|
||||
}
|
||||
getPlotColumn().setSnap(getSnap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSeekListener(SeekListener listener) {
|
||||
int count = getColumnCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
DynamicTableColumn<ValueRow, ?, ?> column = getColumn(i);
|
||||
if (column instanceof TraceValueLifePlotColumn plotCol) {
|
||||
plotCol.addSeekListener(listener);
|
||||
}
|
||||
}
|
||||
getSeekListeners().add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -765,4 +726,13 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
||||
ServiceProvider 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;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.table.TableColumn;
|
||||
|
||||
import docking.widgets.table.GTableColumnModel;
|
||||
import docking.widgets.table.GTableTextCellEditor;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
public class ObjectsTablePanel extends AbstractQueryTablePanel<ValueRow, ObjectTableModel> {
|
||||
|
||||
private static final String DEFAULT_PREF_KEY = "DEFAULT";
|
||||
|
||||
private static class PropertyEditor extends GTableTextCellEditor {
|
||||
private final JTextField textField;
|
||||
|
||||
@ -73,4 +81,71 @@ public class ObjectsTablePanel extends AbstractQueryTablePanel<ValueRow, ObjectT
|
||||
setSelectedItem(row);
|
||||
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 RangeCursorTableHeaderRenderer<Long> headerRenderer =
|
||||
new RangeCursorTableHeaderRenderer<>(0L);
|
||||
new RangeCursorTableHeaderRenderer<>(0L, this);
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
|
@ -32,7 +32,7 @@ public class TraceValueLifePlotColumn
|
||||
|
||||
private final SpanSetTableCellRenderer<Long> cellRenderer = new SpanSetTableCellRenderer<>();
|
||||
private final RangeCursorTableHeaderRenderer<Long> headerRenderer =
|
||||
new RangeCursorTableHeaderRenderer<>(0L);
|
||||
new RangeCursorTableHeaderRenderer<>(0L, this);
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
|
@ -189,8 +189,7 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
|
||||
|
||||
/* package access for testing */
|
||||
final SpanTableCellRenderer<Long> spanRenderer = new SpanTableCellRenderer<>();
|
||||
final RangeCursorTableHeaderRenderer<Long> headerRenderer =
|
||||
new RangeCursorTableHeaderRenderer<>(0L);
|
||||
final RangeCursorTableHeaderRenderer<Long> headerRenderer;
|
||||
|
||||
final TableCellRenderer boldCurrentRenderer = new AbstractGColumnRenderer<Object>() {
|
||||
@Override
|
||||
@ -226,6 +225,8 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
|
||||
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
|
||||
|
||||
threadTableModel = new ThreadTableModel(provider);
|
||||
headerRenderer = new RangeCursorTableHeaderRenderer<>(0L,
|
||||
threadTableModel.getColumn(ThreadTableColumns.PLOT.ordinal()));
|
||||
threadTable = new GhidraTable(threadTableModel);
|
||||
threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
add(new JScrollPane(threadTable));
|
||||
|
@ -195,6 +195,11 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceValueLifePlotColumn newPlotColumn() {
|
||||
return new ThreadPlotColumn();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<ValueRow> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<ValueRow> descriptor = new TableColumnDescriptor<>();
|
||||
@ -206,7 +211,7 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
|
||||
descriptor.addHiddenColumn(new ThreadSpColumn());
|
||||
descriptor.addVisibleColumn(new ThreadStateColumn());
|
||||
descriptor.addHiddenColumn(new ThreadCommentColumn());
|
||||
descriptor.addVisibleColumn(new ThreadPlotColumn());
|
||||
descriptor.addVisibleColumn(getPlotColumn());
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
@ -1185,23 +1185,24 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
protected TraceValueLifePlotColumn getPlotColumn() {
|
||||
return findColumnOfType(modelProvider.elementsTablePanel.tableModel,
|
||||
TraceValueLifePlotColumn.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifePlotColumnFitsSnapshotsOnActivate() throws Throwable {
|
||||
TraceValueLifePlotColumn plotCol = findColumnOfType(
|
||||
modelProvider.elementsTablePanel.tableModel, TraceValueLifePlotColumn.class);
|
||||
createTraceAndPopulateObjects();
|
||||
|
||||
traceManager.activateTrace(tb.trace);
|
||||
waitForSwing();
|
||||
|
||||
// NB. The plot adds a margin of 1
|
||||
assertEquals(Lifespan.span(0, 21), plotCol.getFullRange());
|
||||
assertEquals(Lifespan.span(0, 21), getPlotColumn().getFullRange());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifePlotColumnFitsSnapshotsOnAddSnapshot() throws Throwable {
|
||||
TraceValueLifePlotColumn plotCol = findColumnOfType(
|
||||
modelProvider.elementsTablePanel.tableModel, TraceValueLifePlotColumn.class);
|
||||
createTraceAndPopulateObjects();
|
||||
|
||||
traceManager.activateTrace(tb.trace);
|
||||
@ -1213,20 +1214,18 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
// 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()) {
|
||||
tb.trace.getTimeManager().getSnapshot(31, true);
|
||||
}
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
assertEquals(Lifespan.span(0, 32), plotCol.getFullRange());
|
||||
assertEquals(Lifespan.span(0, 32), getPlotColumn().getFullRange());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifePlotColumnFitsSnapshotsOnAddSnapshotSupressEvents() throws Throwable {
|
||||
TraceValueLifePlotColumn plotCol = findColumnOfType(
|
||||
modelProvider.elementsTablePanel.tableModel, TraceValueLifePlotColumn.class);
|
||||
createTraceAndPopulateObjects();
|
||||
|
||||
traceManager.activateTrace(tb.trace);
|
||||
@ -1240,6 +1239,6 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
// 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;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JTable;
|
||||
@ -34,26 +33,49 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||
|
||||
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
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (checkRemove()) {
|
||||
return;
|
||||
}
|
||||
if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
|
||||
return;
|
||||
}
|
||||
if ((e.getButton() != MouseEvent.BUTTON1)) {
|
||||
return;
|
||||
}
|
||||
e.consume();
|
||||
doSeek(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
if (checkRemove()) {
|
||||
return;
|
||||
}
|
||||
int onmask = MouseEvent.BUTTON1_DOWN_MASK;
|
||||
int offmask = MouseEvent.SHIFT_DOWN_MASK;
|
||||
if ((e.getModifiersEx() & (onmask | offmask)) != onmask) {
|
||||
return;
|
||||
}
|
||||
e.consume();
|
||||
doSeek(e);
|
||||
}
|
||||
|
||||
@ -84,6 +106,7 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||
|
||||
double pos =
|
||||
span * (e.getX() - colX) / myViewCol.getWidth() + fullRangeDouble.min();
|
||||
e.consume();
|
||||
listeners.invoke().accept(pos);
|
||||
}
|
||||
}
|
||||
@ -99,6 +122,7 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||
protected Span<N, ?> fullRange;
|
||||
|
||||
protected N pos;
|
||||
protected final DynamicTableColumn<?, ?, ?> col;
|
||||
protected double doublePos;
|
||||
|
||||
private JTable savedTable;
|
||||
@ -107,8 +131,9 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||
private final ForSeekMouseListener forSeekMouseListener = new ForSeekMouseListener();
|
||||
private final ListenerSet<SeekListener> listeners = new ListenerSet<>(SeekListener.class, true);
|
||||
|
||||
public RangeCursorTableHeaderRenderer(N pos) {
|
||||
public RangeCursorTableHeaderRenderer(N pos, DynamicTableColumn<?, ?, ?> col) {
|
||||
this.pos = pos;
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,6 +148,9 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||
}
|
||||
|
||||
protected void setSavedTable(JTable table) {
|
||||
if (savedTable == table) {
|
||||
return;
|
||||
}
|
||||
if (savedTable != null) {
|
||||
JTableHeader header = savedTable.getTableHeader();
|
||||
header.removeMouseListener(forSeekMouseListener);
|
||||
@ -131,8 +159,23 @@ public class RangeCursorTableHeaderRenderer<N extends Number & Comparable<N>>
|
||||
savedTable = table;
|
||||
if (savedTable != null) {
|
||||
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.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());
|
||||
SpanTableCellRenderer<Integer> rangeRenderer = new SpanTableCellRenderer<>();
|
||||
DynamicTableColumn<MyRow, ?, ?> col = model.getColumn(MyColumns.LIFESPAN.ordinal());
|
||||
RangeCursorTableHeaderRenderer<Integer> headerRenderer =
|
||||
new RangeCursorTableHeaderRenderer<>(0);
|
||||
new RangeCursorTableHeaderRenderer<>(0, col);
|
||||
column.setCellRenderer(rangeRenderer);
|
||||
column.setHeaderRenderer(headerRenderer);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user