Merge remote-tracking branch 'origin/GP-4363_Dan_fixElementsTableColumns--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-02-27 11:06:02 -05:00
commit 6fdb63a2cc
17 changed files with 258 additions and 148 deletions

View File

@ -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")

View File

@ -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" />

View File

@ -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()

View File

@ -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">

View File

@ -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")

View File

@ -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" />

View File

@ -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() {

View File

@ -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()))

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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() {

View File

@ -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() {

View File

@ -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));

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}
}

View File

@ -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);