diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerBlockChooserDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerBlockChooserDialog.java index a2d2505663..f90c0cd5dd 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerBlockChooserDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerBlockChooserDialog.java @@ -25,7 +25,6 @@ import javax.swing.*; import docking.ReusableDialogComponentProvider; import docking.widgets.table.*; import docking.widgets.table.ColumnSortState.SortDirection; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.app.services.DebuggerStaticMappingService; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java index efffa1a80a..a4d22ca548 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java @@ -29,7 +29,6 @@ import docking.action.*; import docking.action.builder.ActionBuilder; import docking.menu.MultiActionDockingAction; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.app.context.ProgramLocationActionContext; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerResources; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/console/DebuggerConsoleProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/console/DebuggerConsoleProvider.java index 6b95c8e616..2915c0afa7 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/console/DebuggerConsoleProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/console/DebuggerConsoleProvider.java @@ -36,7 +36,7 @@ import docking.action.*; import docking.actions.PopupActionProvider; import docking.widgets.table.ColumnSortState.SortDirection; import docking.widgets.table.CustomToStringCellRenderer; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; +import docking.widgets.table.EnumeratedTableColumn; import generic.theme.GIcon; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerResources; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java index 85ccaa818c..2787694d29 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java @@ -29,7 +29,6 @@ import javax.swing.table.TableCellEditor; import db.Transaction; import docking.ReusableDialogComponentProvider; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyPlan.Copier; import ghidra.app.services.*; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionMapProposalDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionMapProposalDialog.java index bc3261b275..2ec8b398dc 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionMapProposalDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionMapProposalDialog.java @@ -23,7 +23,6 @@ import java.util.function.Function; import javax.swing.table.TableCellEditor; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.app.plugin.core.debug.gui.AbstractDebuggerMapProposalDialog; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.debug.api.modules.RegionMapProposal.RegionMapEntry; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java index 2ca9aba852..155058c496 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java @@ -22,7 +22,6 @@ import java.util.function.Function; import javax.swing.table.TableCellEditor; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.app.plugin.core.debug.gui.AbstractDebuggerMapProposalDialog; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionMapProposalDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionMapProposalDialog.java index 178113c8bb..25d5b81540 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionMapProposalDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionMapProposalDialog.java @@ -23,7 +23,6 @@ import java.util.function.Function; import javax.swing.table.*; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.app.plugin.core.debug.gui.AbstractDebuggerMapProposalDialog; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.debug.api.modules.SectionMapProposal.SectionMapEntry; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProvider.java index 1cb8a8e264..42e413dd81 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProvider.java @@ -30,7 +30,6 @@ import docking.ActionContext; import docking.action.DockingAction; import docking.action.DockingActionIf; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerProvider; import ghidra.app.plugin.core.debug.gui.DebuggerResources; @@ -132,7 +131,7 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter } } - protected static class MappingTableModel extends DebouncedRowWrappedEnumeratedColumnTableModel< // + protected static class MappingTableModel extends DebouncedRowWrappedEnumeratedColumnTableModel< StaticMappingTableColumns, ObjectKey, StaticMappingRow, TraceStaticMapping> { public MappingTableModel(PluginTool tool) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java index 39f00df9c0..1eec5e5128 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java @@ -31,7 +31,6 @@ import javax.swing.table.*; import db.Transaction; import docking.action.DockingAction; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import generic.theme.GColor; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerResources; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerSelectPlatformOfferDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerSelectPlatformOfferDialog.java index 046cbe6d0f..602d348185 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerSelectPlatformOfferDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerSelectPlatformOfferDialog.java @@ -26,7 +26,6 @@ import javax.swing.*; import docking.DialogComponentProvider; import docking.widgets.table.*; import docking.widgets.table.ColumnSortState.SortDirection; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOffer; import ghidra.framework.plugintool.PluginTool; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerAvailableRegistersDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerAvailableRegistersDialog.java index cfdd05e30a..ceac467269 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerAvailableRegistersDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerAvailableRegistersDialog.java @@ -26,9 +26,7 @@ import javax.swing.*; import docking.ActionContext; import docking.ReusableDialogComponentProvider; import docking.action.DockingAction; -import docking.widgets.table.DefaultEnumeratedColumnTableModel; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; -import docking.widgets.table.GTable; +import docking.widgets.table.*; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.lang.Language; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java index 6c3b5ecf0b..2c6588a29c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java @@ -38,7 +38,6 @@ import docking.actions.PopupActionProvider; import docking.widgets.AbstractGCellRenderer; import docking.widgets.table.*; import docking.widgets.table.ColumnSortState.SortDirection; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import generic.theme.GColor; import ghidra.app.plugin.core.data.DataSettingsDialog; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerSnapshotTablePanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerSnapshotTablePanel.java index bbf4a1c9ae..be4f8f8a04 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerSnapshotTablePanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerSnapshotTablePanel.java @@ -16,31 +16,24 @@ package ghidra.app.plugin.core.debug.gui.time; import java.awt.*; -import java.util.*; -import java.util.List; +import java.util.Date; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Function; -import java.util.stream.Collectors; import javax.swing.*; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; +import docking.widgets.table.threaded.GThreadedTablePanel; import generic.theme.GColor; import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.docking.settings.Settings; -import ghidra.framework.model.DomainObjectEvent; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; import ghidra.trace.model.Trace; -import ghidra.trace.model.TraceDomainObjectListener; -import ghidra.trace.model.target.TraceObjectValue; -import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.time.TraceSnapshot; -import ghidra.trace.model.time.TraceTimeManager; import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule.TimeRadix; -import ghidra.trace.util.TraceEvents; import ghidra.util.DateUtils; import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.table.column.AbstractGColumnRenderer; @@ -178,77 +171,6 @@ public class DebuggerSnapshotTablePanel extends JPanel { } } - protected class SnapshotTableModel - extends DefaultEnumeratedColumnTableModel { - public SnapshotTableModel(PluginTool tool) { - super(tool, "Snapshots", SnapshotTableColumns.class); - } - - @Override - public List defaultSortOrder() { - return List.of(SnapshotTableColumns.TIME); - } - - Trace getTrace() { - return currentTrace; - } - - DebuggerCoordinates getCurrent() { - return current; - } - } - - private class SnapshotListener extends TraceDomainObjectListener { - public SnapshotListener() { - listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored()); - - listenFor(TraceEvents.SNAPSHOT_ADDED, this::snapAdded); - listenFor(TraceEvents.SNAPSHOT_CHANGED, this::snapChanged); - listenFor(TraceEvents.SNAPSHOT_DELETED, this::snapDeleted); - - listenFor(TraceEvents.VALUE_CREATED, this::valueCreated); - listenFor(TraceEvents.VALUE_DELETED, this::valueDeleted); - } - - private void objectRestored() { - loadSnapshots(); - } - - private void snapAdded(TraceSnapshot snapshot) { - if (snapshot.getKey() < 0 && hideScratch) { - return; - } - SnapshotRow row = new SnapshotRow(snapshot, tool); - snapshotTableModel.add(row); - } - - private void snapChanged(TraceSnapshot snapshot) { - if (snapshot.getKey() < 0 && hideScratch) { - return; - } - snapshotTableModel.notifyUpdatedWith(row -> row.getSnapshot() == snapshot); - } - - private void snapDeleted(TraceSnapshot snapshot) { - if (snapshot.getKey() < 0 && hideScratch) { - return; - } - snapshotTableModel.deleteWith(row -> row.getSnapshot() == snapshot); - } - - private void valueCreated(TraceObjectValue value) { - if (value.getCanonicalPath().equals(KeyPath.of(TraceTimeManager.KEY_TIME_RADIX))) { - snapshotTableModel.fireTableDataChanged(); - } - } - - private void valueDeleted(TraceObjectValue value) { - if (value.getCanonicalPath().equals(KeyPath.of(TraceTimeManager.KEY_TIME_RADIX))) { - snapshotTableModel.fireTableDataChanged(); - } - } - } - static class StyleCurrentSnapRenderer extends AbstractGColumnRenderer implements GTableAccess { @@ -363,102 +285,34 @@ public class DebuggerSnapshotTablePanel extends JPanel { protected final PluginTool tool; protected final SnapshotTableModel snapshotTableModel; + protected final GThreadedTablePanel snapshotTablePanel; protected final GTable snapshotTable; protected final GhidraTableFilterPanel snapshotFilterPanel; - protected boolean hideScratch = false; - - private Trace currentTrace; - private volatile DebuggerCoordinates current = DebuggerCoordinates.NOWHERE; - - protected final SnapshotListener listener = new SnapshotListener(); public DebuggerSnapshotTablePanel(PluginTool tool) { super(new BorderLayout()); this.tool = tool; snapshotTableModel = new SnapshotTableModel(tool); - snapshotTable = new GTable(snapshotTableModel); + snapshotTablePanel = new GThreadedTablePanel<>(snapshotTableModel); + snapshotTable = snapshotTablePanel.getTable(); + snapshotTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - add(new JScrollPane(snapshotTable)); + add(snapshotTablePanel); snapshotFilterPanel = new GhidraTableFilterPanel<>(snapshotTable, snapshotTableModel); add(snapshotFilterPanel, BorderLayout.SOUTH); } - private void addNewListeners() { - if (currentTrace == null) { - return; - } - currentTrace.addListener(listener); - } - - private void removeOldListeners() { - if (currentTrace == null) { - return; - } - currentTrace.removeListener(listener); - } - public void setTrace(Trace trace) { - if (currentTrace == trace) { - return; - } - removeOldListeners(); - currentTrace = trace; - addNewListeners(); - loadSnapshots(); + snapshotTableModel.setTrace(trace); } public Trace getTrace() { - return currentTrace; + return snapshotTableModel.getTrace(); } public void setHideScratchSnapshots(boolean hideScratch) { - if (this.hideScratch == hideScratch) { - return; - } - this.hideScratch = hideScratch; - if (hideScratch) { - deleteScratchSnapshots(); - } - else { - loadScratchSnapshots(); - } - } - - protected void loadSnapshots() { - snapshotTableModel.clear(); - if (currentTrace == null) { - return; - } - TraceTimeManager manager = currentTrace.getTimeManager(); - - List toAdd = new ArrayList<>(); - for (TraceSnapshot snapshot : hideScratch - ? manager.getSnapshots(0, true, Long.MAX_VALUE, true) - : manager.getAllSnapshots()) { - SnapshotRow row = new SnapshotRow(snapshot, tool); - toAdd.add(row); - if (current != DebuggerCoordinates.NOWHERE && - snapshot.getKey() == current.getViewSnap()) { - } - } - snapshotTableModel.addAll(toAdd); - } - - protected void deleteScratchSnapshots() { - snapshotTableModel.deleteWith(s -> s.getSnap() < 0); - } - - protected void loadScratchSnapshots() { - if (currentTrace == null) { - return; - } - TraceTimeManager manager = currentTrace.getTimeManager(); - Collection sratch = - manager.getSnapshots(Long.MIN_VALUE, true, 0, false); - snapshotTableModel.addAll(sratch.stream() - .map(s -> new SnapshotRow(s, tool)) - .collect(Collectors.toList())); + snapshotTableModel.setHideScratch(hideScratch); } public ListSelectionModel getSelectionModel() { @@ -471,9 +325,8 @@ public class DebuggerSnapshotTablePanel extends JPanel { } public void setCurrent(DebuggerCoordinates coords) { - assert coords.getTrace() == currentTrace; - boolean fire = coords.getViewSnap() != current.getViewSnap(); - current = coords; + boolean fire = coords.getViewSnap() != snapshotTableModel.getCurrent().getViewSnap(); + snapshotTableModel.setCurrent(coords); if (fire) { snapshotTable.repaint(); } @@ -490,7 +343,13 @@ public class DebuggerSnapshotTablePanel extends JPanel { if (Objects.equals(curSnap, snap)) { return; } - SnapshotRow row = snapshotTableModel.findFirst(r -> r.getSnap() == snap); + + if (snapshotTableModel.getTrace() == null) { + return; + } + TraceSnapshot snapshot = + snapshotTableModel.getTrace().getTimeManager().getSnapshot(snap, false); + SnapshotRow row = snapshotTableModel.rowMap.get(snapshot); if (row == null) { snapshotTable.clearSelection(); return; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/SnapshotTableModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/SnapshotTableModel.java new file mode 100644 index 0000000000..734f216078 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/SnapshotTableModel.java @@ -0,0 +1,193 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.gui.time; + +import java.util.*; + +import docking.widgets.table.ThreadedEnumeratedColumnTableModel; +import ghidra.app.plugin.core.debug.gui.time.DebuggerSnapshotTablePanel.SnapshotTableColumns; +import ghidra.debug.api.tracemgr.DebuggerCoordinates; +import ghidra.framework.model.DomainObjectEvent; +import ghidra.framework.plugintool.PluginTool; +import ghidra.trace.model.Trace; +import ghidra.trace.model.TraceDomainObjectListener; +import ghidra.trace.model.target.TraceObjectValue; +import ghidra.trace.model.target.path.KeyPath; +import ghidra.trace.model.time.TraceSnapshot; +import ghidra.trace.model.time.TraceTimeManager; +import ghidra.trace.util.TraceEvents; +import ghidra.util.datastruct.Accumulator; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +class SnapshotTableModel + extends ThreadedEnumeratedColumnTableModel { + + private class SnapshotListener extends TraceDomainObjectListener { + public SnapshotListener() { + listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored()); + + listenFor(TraceEvents.SNAPSHOT_ADDED, this::snapAdded); + listenFor(TraceEvents.SNAPSHOT_CHANGED, this::snapChanged); + listenFor(TraceEvents.SNAPSHOT_DELETED, this::snapDeleted); + + listenFor(TraceEvents.VALUE_CREATED, this::valueCreated); + listenFor(TraceEvents.VALUE_DELETED, this::valueDeleted); + } + + private void objectRestored() { + reload(); + } + + private void snapAdded(TraceSnapshot snapshot) { + addSnapshot(snapshot); + } + + private void snapChanged(TraceSnapshot snapshot) { + updateSnapshot(snapshot); + } + + private void snapDeleted(TraceSnapshot snapshot) { + removeSnapshot(snapshot); + } + + private void valueCreated(TraceObjectValue value) { + if (value.getCanonicalPath().equals(KeyPath.of(TraceTimeManager.KEY_TIME_RADIX))) { + fireTableDataChanged(); + } + } + + private void valueDeleted(TraceObjectValue value) { + if (value.getCanonicalPath().equals(KeyPath.of(TraceTimeManager.KEY_TIME_RADIX))) { + fireTableDataChanged(); + } + } + } + + protected final SnapshotListener listener = new SnapshotListener(); + protected final Map rowMap = new HashMap<>(); + + private volatile Trace currentTrace; // Because it gets set before current + private volatile DebuggerCoordinates current = DebuggerCoordinates.NOWHERE; + private boolean hideScratch; + + public SnapshotTableModel(PluginTool tool) { + super(tool, "Snapshots", SnapshotTableColumns.class, null, true); + } + + private void addNewListeners() { + if (currentTrace == null) { + return; + } + currentTrace.addListener(listener); + } + + private void removeOldListeners() { + if (currentTrace == null) { + return; + } + currentTrace.removeListener(listener); + } + + public void setTrace(Trace trace) { + if (this.currentTrace == trace) { + return; + } + removeOldListeners(); + this.currentTrace = trace; + addNewListeners(); + + reload(); + } + + public Trace getTrace() { + return currentTrace; + } + + public void setCurrent(DebuggerCoordinates coords) { + assert coords.getTrace() == currentTrace; + this.current = coords; + } + + public DebuggerCoordinates getCurrent() { + return current; + } + + public void setHideScratch(boolean hideScratch) { + if (this.hideScratch == hideScratch) { + return; + } + this.hideScratch = hideScratch; + reload(); + } + + @Override + public List defaultSortOrder() { + return List.of(SnapshotTableColumns.TIME); + } + + @Override + protected void doLoad(Accumulator accumulator, TaskMonitor monitor) + throws CancelledException { + rowMap.clear(); + if (currentTrace == null) { + return; + } + TraceTimeManager manager = currentTrace.getTimeManager(); + Long maxSnap = manager.getMaxSnap(); + monitor.initialize(maxSnap == null ? 0 : maxSnap.longValue(), "Reading Snapshots"); + for (TraceSnapshot snapshot : hideScratch + ? manager.getSnapshots(0, true, Long.MAX_VALUE, true) + : manager.getAllSnapshots()) { + SnapshotRow row = new SnapshotRow(snapshot, serviceProvider); + rowMap.put(snapshot, row); + accumulator.add(row); + monitor.setProgress(Math.max(0, snapshot.getKey())); + monitor.checkCancelled(); + } + } + + public void addSnapshot(TraceSnapshot snapshot) { + if (snapshot.getKey() < 0 && hideScratch) { + return; + } + SnapshotRow row = + rowMap.computeIfAbsent(snapshot, s -> new SnapshotRow(s, serviceProvider)); + addObject(row); + } + + public void updateSnapshot(TraceSnapshot snapshot) { + if (snapshot.getKey() < 0 && hideScratch) { + return; + } + SnapshotRow row = rowMap.get(snapshot); + if (row == null) { + return; + } + updateObject(row); + } + + public void removeSnapshot(TraceSnapshot snapshot) { + if (snapshot.getKey() < 0 && hideScratch) { + return; + } + SnapshotRow row = rowMap.remove(snapshot); + if (row == null) { + return; + } + removeObject(row); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java index eddf31fa22..7417bbf7b7 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java @@ -38,7 +38,6 @@ import docking.action.DockingAction; import docking.action.ToggleDockingAction; import docking.action.builder.ActionBuilder; import docking.widgets.table.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import generic.theme.GColor; import ghidra.app.context.ListingActionContext; import ghidra.app.context.ProgramLocationActionContext; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DebouncedRowWrappedEnumeratedColumnTableModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DebouncedRowWrappedEnumeratedColumnTableModel.java index 8c5f32f6ed..9b8cb78b60 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DebouncedRowWrappedEnumeratedColumnTableModel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DebouncedRowWrappedEnumeratedColumnTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,14 +17,15 @@ package ghidra.app.plugin.core.debug.utils; import java.util.function.Function; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; +import docking.widgets.table.EnumeratedTableColumn; import docking.widgets.table.RowWrappedEnumeratedColumnTableModel; import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncTimer; import ghidra.framework.plugintool.PluginTool; import ghidra.util.Swing; -public class DebouncedRowWrappedEnumeratedColumnTableModel & EnumeratedTableColumn, K, R, T> +public class DebouncedRowWrappedEnumeratedColumnTableModel< + C extends Enum & EnumeratedTableColumn, K, R, T> extends RowWrappedEnumeratedColumnTableModel { AsyncDebouncer debouncer = new AsyncDebouncer(AsyncTimer.DEFAULT_TIMER, 100); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProviderTest.java index 2430318163..4f9acf4e5d 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProviderTest.java @@ -102,25 +102,26 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { createSnaplessTrace(); addSnapshots(); + waitForBusyTool(tool); assertDisabled(listingProvider, timePlugin.actionRenameSnapshot); traceManager.openTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertDisabled(listingProvider, timePlugin.actionRenameSnapshot); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertEnabled(listingProvider, timePlugin.actionRenameSnapshot); traceManager.activateSnap(10); - waitForSwing(); + waitForBusyTool(tool); performEnabledAction(listingProvider, timePlugin.actionRenameSnapshot, false); InputDialog dialog = waitForDialogComponent(InputDialog.class); assertEquals("Snap 10", dialog.getValue()); dialog.setValue("My Snapshot"); dialog.close(); // isCancelled (private) defaults to false - waitForSwing(); + waitForBusyTool(tool); DBTraceSnapshot snapshot = tb.trace.getTimeManager().getSnapshot(10, false); assertEquals("My Snapshot", snapshot.getDescription()); @@ -130,6 +131,7 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { @Test public void testEmpty() { + waitForBusyTool(tool); assertProviderEmpty(); } @@ -138,12 +140,13 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { createSnaplessTrace(); traceManager.openTrace(tb.trace); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertProviderEmpty(); addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderPopulated(); } @@ -158,12 +161,13 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { } traceManager.openTrace(tb.trace); traceManager.activateThread(thread); - waitForSwing(); + waitForBusyTool(tool); assertProviderEmpty(); addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderPopulated(); } @@ -174,11 +178,12 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { traceManager.openTrace(tb.trace); addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderEmpty(); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertProviderPopulated(); } @@ -189,11 +194,12 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { traceManager.openTrace(tb.trace); addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderEmpty(); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertProviderPopulated(); @@ -201,6 +207,7 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { tb.trace.getTimeManager().getSnapshot(10, false).delete(); } waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertEquals(1, timeProvider.mainPanel.snapshotTableModel.getModelData().size()); } @@ -210,20 +217,23 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { createSnaplessTrace(); traceManager.openTrace(tb.trace); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertProviderEmpty(); addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderPopulated(); undo(tb.trace); + waitForBusyTool(tool); assertProviderEmpty(); redo(tb.trace); + waitForBusyTool(tool); assertProviderPopulated(); } @@ -233,19 +243,21 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { createSnaplessTrace(); traceManager.openTrace(tb.trace); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertProviderEmpty(); try (Transaction tx = tb.startTransaction()) { addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderPopulated(); tx.abort(); } waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderEmpty(); } @@ -260,7 +272,7 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { assertProviderEmpty(); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertProviderPopulated(); @@ -276,15 +288,17 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { traceManager.openTrace(tb.trace); addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderEmpty(); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); SnapshotRow row = timeProvider.mainPanel.snapshotTableModel.getModelData().get(0); runSwing(() -> row.setDescription("Custom Description")); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertEquals("Custom Description", tb.trace.getTimeManager().getSnapshot(0, false).getDescription()); @@ -298,11 +312,12 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { traceManager.openTrace(tb.trace); addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertProviderEmpty(); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); clickTableCell(timeProvider.mainPanel.snapshotTable, 0, 0, 2); assertEquals(0, traceManager.getCurrentSnap()); @@ -318,9 +333,10 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { addSnapshots(); addScratchSnapshot(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertEquals(3, timeProvider.mainPanel.snapshotTableModel.getModelData().size()); } @@ -331,14 +347,16 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { traceManager.openTrace(tb.trace); addSnapshots(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertEquals(2, timeProvider.mainPanel.snapshotTableModel.getModelData().size()); addScratchSnapshot(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); assertEquals(3, timeProvider.mainPanel.snapshotTableModel.getModelData().size()); } @@ -350,19 +368,22 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { addSnapshots(); addScratchSnapshot(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertEquals(false, timeProvider.hideScratch); assertEquals(3, timeProvider.mainPanel.snapshotTableModel.getModelData().size()); performAction(timeProvider.actionHideScratch); + waitForBusyTool(tool); assertEquals(true, timeProvider.hideScratch); assertEquals(2, timeProvider.mainPanel.snapshotTableModel.getModelData().size()); performAction(timeProvider.actionHideScratch); + waitForBusyTool(tool); assertEquals(false, timeProvider.hideScratch); assertEquals(3, timeProvider.mainPanel.snapshotTableModel.getModelData().size()); @@ -377,9 +398,10 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerTest { addSnapshots(); addScratchSnapshot(); waitForDomainObject(tb.trace); + waitForBusyTool(tool); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForBusyTool(tool); assertEquals(true, timeProvider.hideScratch); List data = timeProvider.mainPanel.snapshotTableModel.getModelData(); diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnProgramTableModel.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnProgramTableModel.java index 3bb112c38c..d29ca38351 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnProgramTableModel.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnProgramTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,7 +15,6 @@ */ package docking.widgets.table; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnTableModel.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnTableModel.java index 40c605f3bd..0b217ab49a 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnTableModel.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnTableModel.java @@ -18,15 +18,7 @@ package docking.widgets.table; import java.util.*; import java.util.function.Predicate; -import javax.swing.table.TableCellEditor; - -import docking.widgets.table.ColumnSortState.SortDirection; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; -import ghidra.docking.settings.Settings; -import ghidra.docking.settings.SettingsDefinition; -import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.ServiceProvider; -import ghidra.util.table.column.GColumnRenderer; /** * A table model whose columns are described using an {@link Enum}. @@ -39,128 +31,16 @@ import ghidra.util.table.column.GColumnRenderer; */ public class DefaultEnumeratedColumnTableModel & EnumeratedTableColumn, R> extends GDynamicColumnTableModel implements EnumeratedColumnTableModel { - // NOTE: If I need to track indices, addSortListener - /** - * An interface on enums used to describe table columns - * - * @param the type of the enum - * @param the type of rows - */ - public static interface EnumeratedTableColumn, R> { - /** - * Get the value class of cells in this column - * - * @return the class - */ - public Class getValueClass(); - - /** - * Get the value of this column for the given row - * - * @param row the row - * @return the value - */ - public Object getValueOf(R row); - - /** - * Get the name of this column - * - * @return the name - */ - public String getHeader(); - - /** - * Get the value of this column for the given row - * - * @param row the row - * @param value the new value - */ - default public void setValueOf(R row, Object value) { - throw new UnsupportedOperationException("Cell is not editable"); - } - - /** - * Check if this column can be modified for the given row - * - * @param row the row - * @return true if editable - */ - default public boolean isEditable(R row) { - return false; - } - - /** - * Check if this column can be sorted - * - *

- * TODO: Either this should be implemented as ported to {@link GDynamicColumnTableModel}, or - * removed. - * - * @return true if sortable - */ - default public boolean isSortable() { - return true; - } - - /** - * Check if this column should be visible by default - * - * @return true if visible - */ - default public boolean isVisible() { - return true; - } - - /** - * Get the default sort direction for this column - * - * @return the sort direction - */ - default public SortDirection defaultSortDirection() { - return SortDirection.ASCENDING; - } - - default public int getPreferredWidth() { - return AbstractGTableModel.WIDTH_UNDEFINED; - } - - default public int getMinWidth() { - return AbstractGTableModel.WIDTH_UNDEFINED; - } - - default public int getMaxWidth() { - return AbstractGTableModel.WIDTH_UNDEFINED; - } - - /** - * Because of limitations with Java generics and Enumerations, type checking cannot be - * guaranteed here. The user must ensure that any returned by {@link #getValueOf(Object)} - * can be accepted by the renderer returned here. The framework will perform an unchecked - * cast of the renderer. - * - * @return the renderer - */ - default public GColumnRenderer getRenderer() { - return null; - } - - default public TableCellEditor getEditor() { - return null; - } - - default public SettingsDefinition[] getSettingsDefinitions() { - return null; - } - } private final List modelData = new ArrayList<>(); private final String name; - private final C[] cols; + private final List cols; - public DefaultEnumeratedColumnTableModel(PluginTool tool, String name, Class colType) { - super(tool); + public DefaultEnumeratedColumnTableModel(ServiceProvider serviceProvider, String name, + Class colType) { + super(serviceProvider); this.name = name; - this.cols = colType.getEnumConstants(); + this.cols = List.of(colType.getEnumConstants()); reloadColumns(); // Smell } @@ -181,98 +61,9 @@ public class DefaultEnumeratedColumnTableModel & EnumeratedTab } } - static class EnumeratedDynamicTableColumn - extends AbstractDynamicTableColumn - implements EditableDynamicTableColumn { - private final EnumeratedTableColumn col; - - public EnumeratedDynamicTableColumn(EnumeratedTableColumn col) { - this.col = col; - } - - @Override - public String getColumnName() { - return col.getHeader(); - } - - @Override - public Object getValue(R rowObject, Settings settings, Void data, - ServiceProvider serviceProvider) throws IllegalArgumentException { - return col.getValueOf(rowObject); - } - - @Override - @SuppressWarnings("unchecked") - public Class getColumnClass() { - return (Class) col.getValueClass(); - } - - @Override - public boolean isEditable(R row, Settings settings, Void dataSource, - ServiceProvider serviceProvider) { - return col.isEditable(row); - } - - @Override - public void setValueOf(R row, Object value, Settings settings, Void dataSource, - ServiceProvider serviceProvider) { - col.setValueOf(row, value); - } - - @Override - @SuppressWarnings("unchecked") - public GColumnRenderer getColumnRenderer() { - return (GColumnRenderer) col.getRenderer(); - } - - @Override - public TableCellEditor getColumnEditor() { - return col.getEditor(); - } - - @Override - public int getColumnPreferredWidth() { - return col.getPreferredWidth(); - } - - @Override - public int getColumnMaxWidth() { - return col.getMaxWidth(); - } - - @Override - public int getColumnMinWidth() { - return col.getMinWidth(); - } - - @Override - public SettingsDefinition[] getSettingsDefinitions() { - SettingsDefinition[] defs = col.getSettingsDefinitions(); - if (defs != null) { - return defs; - } - return super.getSettingsDefinitions(); - } - } - @Override protected TableColumnDescriptor createTableColumnDescriptor() { - TableColumnDescriptor descriptor = new TableColumnDescriptor<>(); - if (cols != null) { // Smells - List defaultOrder = defaultSortOrder(); - for (C col : cols) { - EnumeratedDynamicTableColumn ecol = new EnumeratedDynamicTableColumn(col); - if (col.isVisible()) { - descriptor.addVisibleColumn(ecol, - defaultOrder.indexOf(col), // -1 means not found, not sorted - col.defaultSortDirection().isAscending()); - } - else { - descriptor.addHiddenColumn(ecol); - } - } - } - return descriptor; + return EnumeratedColumnTableModel.createTableColumnDescriptor(cols, defaultSortOrder()); } @Override diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedColumnTableModel.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedColumnTableModel.java index caf18883b4..98d608cce9 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedColumnTableModel.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedColumnTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -54,4 +54,23 @@ public interface EnumeratedColumnTableModel extends RowObjectTableModel { R findFirst(Predicate predicate); public void clear(); + + static & EnumeratedTableColumn, R> TableColumnDescriptor + createTableColumnDescriptor(Collection cols, List defaultOrder) { + TableColumnDescriptor descriptor = new TableColumnDescriptor<>(); + if (cols != null) { // Smells + for (C col : cols) { + EnumeratedDynamicTableColumn ecol = new EnumeratedDynamicTableColumn(col); + if (col.isVisible()) { + descriptor.addVisibleColumn(ecol, + defaultOrder.indexOf(col), // -1 means not found, not sorted + col.defaultSortDirection().isAscending()); + } + else { + descriptor.addHiddenColumn(ecol); + } + } + } + return descriptor; + } } diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedDynamicTableColumn.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedDynamicTableColumn.java new file mode 100644 index 0000000000..939e6dda74 --- /dev/null +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedDynamicTableColumn.java @@ -0,0 +1,98 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking.widgets.table; + +import javax.swing.table.TableCellEditor; + +import docking.widgets.table.EnumeratedColumnTableModel.EditableDynamicTableColumn; +import ghidra.docking.settings.Settings; +import ghidra.docking.settings.SettingsDefinition; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.util.table.column.GColumnRenderer; + +class EnumeratedDynamicTableColumn + extends AbstractDynamicTableColumn + implements EditableDynamicTableColumn { + private final EnumeratedTableColumn col; + + public EnumeratedDynamicTableColumn(EnumeratedTableColumn col) { + this.col = col; + } + + @Override + public String getColumnName() { + return col.getHeader(); + } + + @Override + public Object getValue(R rowObject, Settings settings, Void data, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return col.getValueOf(rowObject); + } + + @Override + @SuppressWarnings("unchecked") + public Class getColumnClass() { + return (Class) col.getValueClass(); + } + + @Override + public boolean isEditable(R row, Settings settings, Void dataSource, + ServiceProvider serviceProvider) { + return col.isEditable(row); + } + + @Override + public void setValueOf(R row, Object value, Settings settings, Void dataSource, + ServiceProvider serviceProvider) { + col.setValueOf(row, value); + } + + @Override + @SuppressWarnings("unchecked") + public GColumnRenderer getColumnRenderer() { + return (GColumnRenderer) col.getRenderer(); + } + + @Override + public TableCellEditor getColumnEditor() { + return col.getEditor(); + } + + @Override + public int getColumnPreferredWidth() { + return col.getPreferredWidth(); + } + + @Override + public int getColumnMaxWidth() { + return col.getMaxWidth(); + } + + @Override + public int getColumnMinWidth() { + return col.getMinWidth(); + } + + @Override + public SettingsDefinition[] getSettingsDefinitions() { + SettingsDefinition[] defs = col.getSettingsDefinitions(); + if (defs != null) { + return defs; + } + return super.getSettingsDefinitions(); + } +} diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedTableColumn.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedTableColumn.java new file mode 100644 index 0000000000..fe503ff80c --- /dev/null +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedTableColumn.java @@ -0,0 +1,136 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking.widgets.table; + +import javax.swing.table.TableCellEditor; + +import docking.widgets.table.ColumnSortState.SortDirection; +import ghidra.docking.settings.SettingsDefinition; +import ghidra.util.table.column.GColumnRenderer; + +// NOTE: If I need to track indices, addSortListener +/** + * An interface on enums used to describe table columns + * + * @param the type of the enum + * @param the type of rows + */ +public interface EnumeratedTableColumn, R> { + /** + * Get the value class of cells in this column + * + * @return the class + */ + public Class getValueClass(); + + /** + * Get the value of this column for the given row + * + * @param row the row + * @return the value + */ + public Object getValueOf(R row); + + /** + * Get the name of this column + * + * @return the name + */ + public String getHeader(); + + /** + * Get the value of this column for the given row + * + * @param row the row + * @param value the new value + */ + default public void setValueOf(R row, Object value) { + throw new UnsupportedOperationException("Cell is not editable"); + } + + /** + * Check if this column can be modified for the given row + * + * @param row the row + * @return true if editable + */ + default public boolean isEditable(R row) { + return false; + } + + /** + * Check if this column can be sorted + * + *

+ * TODO: Either this should be implemented as ported to {@link GDynamicColumnTableModel}, or + * removed. + * + * @return true if sortable + */ + default public boolean isSortable() { + return true; + } + + /** + * Check if this column should be visible by default + * + * @return true if visible + */ + default public boolean isVisible() { + return true; + } + + /** + * Get the default sort direction for this column + * + * @return the sort direction + */ + default public SortDirection defaultSortDirection() { + return SortDirection.ASCENDING; + } + + default public int getPreferredWidth() { + return AbstractGTableModel.WIDTH_UNDEFINED; + } + + default public int getMinWidth() { + return AbstractGTableModel.WIDTH_UNDEFINED; + } + + default public int getMaxWidth() { + return AbstractGTableModel.WIDTH_UNDEFINED; + } + + /** + * Because of limitations with Java generics and Enumerations, type checking cannot be + * guaranteed here. The user must ensure that any returned by {@link #getValueOf(Object)} can be + * accepted by the renderer returned here. The framework will perform an unchecked cast of the + * renderer. + * + * @return the renderer + */ + default public GColumnRenderer getRenderer() { + return null; + } + + default public TableCellEditor getEditor() { + return null; + } + + default public SettingsDefinition[] getSettingsDefinitions() { + return null; + } +} diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/RowWrappedEnumeratedColumnTableModel.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/RowWrappedEnumeratedColumnTableModel.java index c335224f37..dfdc59969f 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/RowWrappedEnumeratedColumnTableModel.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/RowWrappedEnumeratedColumnTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,7 +21,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import ghidra.framework.plugintool.PluginTool; import ghidra.util.Msg; diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/ThreadedEnumeratedColumnTableModel.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/ThreadedEnumeratedColumnTableModel.java new file mode 100644 index 0000000000..91f372e078 --- /dev/null +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/ThreadedEnumeratedColumnTableModel.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking.widgets.table; + +import java.util.Collections; +import java.util.List; + +import docking.widgets.table.threaded.ThreadedTableModel; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.util.task.TaskMonitor; + +public abstract class ThreadedEnumeratedColumnTableModel< + C extends Enum & EnumeratedTableColumn, R> extends ThreadedTableModel { + + private final List cols; + + protected ThreadedEnumeratedColumnTableModel(ServiceProvider serviceProvider, String name, + Class colType, TaskMonitor monitor, boolean loadIncrementally) { + super(name, serviceProvider, monitor, loadIncrementally); + this.cols = List.of(colType.getEnumConstants()); + + reloadColumns(); + } + + /** + * Get the default sort order of the table + * + * @return the list of columns in order of descending priority + */ + public List defaultSortOrder() { + return Collections.emptyList(); + } + + @Override + protected TableColumnDescriptor createTableColumnDescriptor() { + return EnumeratedColumnTableModel.createTableColumnDescriptor(cols, defaultSortOrder()); + } + + @Override + public Void getDataSource() { + return null; + } +} diff --git a/Ghidra/Debug/ProposedUtils/src/test/java/docking/widgets/table/DemoSpanCellRendererTest.java b/Ghidra/Debug/ProposedUtils/src/test/java/docking/widgets/table/DemoSpanCellRendererTest.java index f062ac51fa..f0399fede7 100644 --- a/Ghidra/Debug/ProposedUtils/src/test/java/docking/widgets/table/DemoSpanCellRendererTest.java +++ b/Ghidra/Debug/ProposedUtils/src/test/java/docking/widgets/table/DemoSpanCellRendererTest.java @@ -29,7 +29,6 @@ import javax.swing.table.TableColumn; import org.junit.*; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import docking.widgets.table.RangeCursorTableHeaderRenderer.SeekListener; import generic.Span; import ghidra.test.AbstractGhidraHeadedIntegrationTest; diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryInformationPanel.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryInformationPanel.java index 10473d0c04..dd00c185d6 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryInformationPanel.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryInformationPanel.java @@ -24,9 +24,7 @@ import java.util.stream.Stream; import javax.swing.JPanel; import javax.swing.JScrollPane; -import docking.widgets.table.DefaultEnumeratedColumnTableModel; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; -import docking.widgets.table.GTable; +import docking.widgets.table.*; import ghidra.framework.plugintool.PluginTool; import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.table.column.GColumnRenderer; diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryInstructionLogPanel.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryInstructionLogPanel.java index 54600ec717..9f62b059e4 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryInstructionLogPanel.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryInstructionLogPanel.java @@ -23,9 +23,7 @@ import java.util.function.Function; import javax.swing.*; -import docking.widgets.table.DefaultEnumeratedColumnProgramTableModel; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; -import docking.widgets.table.GTable; +import docking.widgets.table.*; import generic.theme.GColor; import ghidra.framework.plugintool.PluginTool; import ghidra.pcode.emu.symz3.SymZ3RecordsExecution.RecInstruction; diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryPcodeLogPanel.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryPcodeLogPanel.java index bb3056d4c9..857c8f1d6d 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryPcodeLogPanel.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/symz3/gui/Z3SummaryPcodeLogPanel.java @@ -23,9 +23,7 @@ import java.util.function.Function; import javax.swing.*; -import docking.widgets.table.DefaultEnumeratedColumnProgramTableModel; -import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; -import docking.widgets.table.GTable; +import docking.widgets.table.*; import generic.theme.GColor; import ghidra.app.plugin.processors.sleigh.template.OpTpl; import ghidra.app.util.pcode.*;