diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/AbstractDebuggerMapProposalDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/AbstractDebuggerMapProposalDialog.java index 9eeaf700b6..06be2f1a6c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/AbstractDebuggerMapProposalDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/AbstractDebuggerMapProposalDialog.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. @@ -27,6 +27,7 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.util.table.GhidraTableFilterPanel; public abstract class AbstractDebuggerMapProposalDialog extends ReusableDialogComponentProvider { + protected static final int BUTTON_SIZE = 32; protected final EnumeratedColumnTableModel tableModel; protected GTable table; 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 aa16e1be3e..a2d2505663 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 @@ -21,8 +21,6 @@ import java.util.Map.Entry; import java.util.function.Function; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; import docking.ReusableDialogComponentProvider; import docking.widgets.table.*; @@ -37,6 +35,7 @@ import ghidra.program.util.ProgramLocation; import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.modules.TraceSection; import ghidra.util.table.GhidraTableFilterPanel; +import ghidra.util.table.column.GColumnRenderer; public class DebuggerBlockChooserDialog extends ReusableDialogComponentProvider { public static class MemoryBlockRow { @@ -103,12 +102,29 @@ public class DebuggerBlockChooserDialog extends ReusableDialogComponentProvider enum MemoryBlockTableColumns implements EnumeratedTableColumn { + // LATER: Adjust column widths? SCORE("Score", Double.class, MemoryBlockRow::getScore, SortDirection.DESCENDING), PROGRAM("Program", String.class, MemoryBlockRow::getProgramName, SortDirection.ASCENDING), BLOCK("Block", String.class, MemoryBlockRow::getBlockName, SortDirection.ASCENDING), - START("Start Address", Address.class, MemoryBlockRow::getMinAddress, SortDirection.ASCENDING), - END("End Address", Address.class, MemoryBlockRow::getMaxAddress, SortDirection.ASCENDING), - LENGTH("Length", Long.class, MemoryBlockRow::getLength, SortDirection.ASCENDING); + START("Start Address", Address.class, MemoryBlockRow::getMinAddress, + SortDirection.ASCENDING) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + END("End Address", Address.class, MemoryBlockRow::getMaxAddress, SortDirection.ASCENDING) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + LENGTH("Length", Long.class, MemoryBlockRow::getLength, SortDirection.ASCENDING) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_ULONG_HEX; + } + }; MemoryBlockTableColumns(String header, Class cls, Function getter, SortDirection dir) { @@ -188,18 +204,6 @@ public class DebuggerBlockChooserDialog extends ReusableDialogComponentProvider okButton.setEnabled(filterPanel.getSelectedItems().size() == 1); // Prevent empty selection }); - - // TODO: Adjust column widths? - TableColumnModel columnModel = table.getColumnModel(); - - TableColumn startCol = columnModel.getColumn(MemoryBlockTableColumns.START.ordinal()); - startCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - - TableColumn endCol = columnModel.getColumn(MemoryBlockTableColumns.END.ordinal()); - endCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - - TableColumn lenCol = columnModel.getColumn(MemoryBlockTableColumns.LENGTH.ordinal()); - lenCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX); } public Map.Entry chooseBlock(PluginTool tool, TraceSection section, diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java index bda58a96ce..841c0a84ba 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.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. @@ -23,20 +23,21 @@ import javax.swing.*; import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.table.TableCellEditor; +import docking.widgets.table.GTable; import docking.widgets.table.GTableFilterPanel; import ghidra.debug.api.breakpoint.LogicalBreakpoint.State; public abstract class DebuggerBreakpointStateTableCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener { - private final GTableFilterPanel filterPanel; protected final JButton button = new JButton(); private State value = State.NONE; private T row; - public DebuggerBreakpointStateTableCellEditor( - GTableFilterPanel filterPanel) { - this.filterPanel = filterPanel; + private Class cls; + + public DebuggerBreakpointStateTableCellEditor(Class cls) { + this.cls = cls; button.setHorizontalAlignment(SwingConstants.CENTER); button.setOpaque(true); @@ -54,6 +55,10 @@ public abstract class DebuggerBreakpointStateTableCellEditor extends Abstract @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + if (!(table instanceof GTable gtable)) { + return null; + } + GTableFilterPanel filterPanel = gtable.getTableFilterPanel(); if (isSelected) { button.setBackground(table.getSelectionBackground()); } @@ -61,7 +66,12 @@ public abstract class DebuggerBreakpointStateTableCellEditor extends Abstract // TODO: Alternating colors? Can't inherit GTableCellRenderer.... button.setBackground(table.getBackground()); } - this.row = filterPanel.getRowObject(row); + Object rowObj = filterPanel.getRowObject(row); + if (!(cls.isInstance(rowObj))) { + return null; + } + + this.row = cls.cast(rowObj); this.value = (State) value; button.setIcon(this.value.icon); button.setHorizontalAlignment(SwingConstants.CENTER); 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 88d460684b..efffa1a80a 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 @@ -22,11 +22,9 @@ import java.util.function.*; import java.util.stream.Collectors; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.TableCellEditor; -import docking.ActionContext; -import docking.WindowPosition; +import docking.*; import docking.action.*; import docking.action.builder.ActionBuilder; import docking.menu.MultiActionDockingAction; @@ -64,52 +62,150 @@ import ghidra.util.*; import ghidra.util.database.ObjectKey; import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTableFilterPanel; +import ghidra.util.table.column.GColumnRenderer; public class DebuggerBreakpointsProvider extends ComponentProviderAdapter implements LogicalBreakpointsChangeListener, ControlModeChangeListener { + final static DebuggerBreakpointStateTableCellRenderer STATE_RENDERER = + new DebuggerBreakpointStateTableCellRenderer(); + final static DebuggerBreakpointStateTableCellEditor LOGICAL_STATE_EDITOR = + new DebuggerBreakpointStateTableCellEditor<>(LogicalBreakpointRow.class) { + @Override + protected State getToggledState(LogicalBreakpointRow row, State current) { + boolean mapped = row.isMapped(); + if (!mapped) { + Tool tool = row.getTool(); + tool.setStatusInfo("Breakpoint has no locations. Only toggling its bookmark.", + true); + } + return current.getToggled(mapped); + } + }; + + final static DebuggerBreakpointStateTableCellEditor LOC_STATE_EDITOR = + new DebuggerBreakpointStateTableCellEditor<>(BreakpointLocationRow.class) { + @Override + protected State getToggledState(BreakpointLocationRow row, State current) { + return current.getToggled(false); + } + }; + protected enum LogicalBreakpointTableColumns implements EnumeratedTableColumn { - STATE("State", State.class, LogicalBreakpointRow::getState, LogicalBreakpointRow::setState, - true), + STATE("State", State.class, LogicalBreakpointRow::getState, + LogicalBreakpointRow::setState) { + @Override + public GColumnRenderer getRenderer() { + return STATE_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return LOGICAL_STATE_EDITOR; + } + + @Override + public int getMaxWidth() { + return 24; + } + + @Override + public int getMinWidth() { + return 24; + } + }, NAME("Name", String.class, LogicalBreakpointRow::getName, LogicalBreakpointRow::setName, - LogicalBreakpointRow::isNamable, true), - ADDRESS("Address", Address.class, LogicalBreakpointRow::getAddress, true), - IMAGE("Image", String.class, LogicalBreakpointRow::getImageName, true), - LENGTH("Length", Long.class, LogicalBreakpointRow::getLength, true), - KINDS("Kinds", String.class, LogicalBreakpointRow::getKinds, true), - LOCATIONS("Locations", Integer.class, LogicalBreakpointRow::getLocationCount, true), - SLEIGH("Sleigh", Boolean.class, LogicalBreakpointRow::hasSleigh, true); + LogicalBreakpointRow::isNamable) { + @Override + public int getPreferredWidth() { + return 150; + } + }, + ADDRESS("Address", Address.class, LogicalBreakpointRow::getAddress) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + + @Override + public int getPreferredWidth() { + return 150; + } + }, + IMAGE("Image", String.class, LogicalBreakpointRow::getImageName) { + @Override + public int getPreferredWidth() { + return 100; + } + }, + LENGTH("Length", Long.class, LogicalBreakpointRow::getLength) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_ULONG_HEX; + } + + @Override + public int getPreferredWidth() { + return 60; + } + }, + KINDS("Kinds", String.class, LogicalBreakpointRow::getKinds) { + @Override + public int getPreferredWidth() { + return 150; + } + }, + LOCATIONS("Locations", Integer.class, LogicalBreakpointRow::getLocationCount) { + @Override + public int getPreferredWidth() { + return 20; + } + }, + SLEIGH("Sleigh", Boolean.class, LogicalBreakpointRow::hasSleigh) { + @Override + public int getMaxWidth() { + return 30; + } + + @Override + public int getMinWidth() { + return 30; + } + }; private final String header; private final Class cls; private final Function getter; private final BiConsumer setter; private final Predicate editable; - private final boolean sortable; LogicalBreakpointTableColumns(String header, Class cls, - Function getter, boolean sortable) { - this(header, cls, getter, null, null, sortable); + Function getter) { + this(header, cls, getter, null, null); } LogicalBreakpointTableColumns(String header, Class cls, Function getter, - BiConsumer setter, boolean sortable) { - this(header, cls, getter, setter, null, sortable); + BiConsumer setter) { + this(header, cls, getter, setter, null); } @SuppressWarnings("unchecked") LogicalBreakpointTableColumns(String header, Class cls, Function getter, BiConsumer setter, - Predicate editable, boolean sortable) { + Predicate editable) { this.header = header; this.cls = cls; this.getter = getter; this.setter = (BiConsumer) setter; this.editable = editable; - this.sortable = sortable; + } + + @Override + public String getHeader() { + return header; } @Override @@ -122,21 +218,11 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter return getter.apply(row); } - @Override - public String getHeader() { - return header; - } - @Override public boolean isEditable(LogicalBreakpointRow row) { return setter != null && (editable == null || editable.test(row)); } - @Override - public boolean isSortable() { - return sortable; - } - @Override public void setValueOf(LogicalBreakpointRow row, Object value) { setter.accept(row, value); @@ -162,39 +248,84 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter protected enum BreakpointLocationTableColumns implements EnumeratedTableColumn { STATE("State", State.class, BreakpointLocationRow::getState, - BreakpointLocationRow::setState, true, true), - NAME("Name", String.class, BreakpointLocationRow::getName, BreakpointLocationRow::setName, - true, true), - ADDRESS("Address", Address.class, BreakpointLocationRow::getAddress, true, true), - TRACE("Trace", String.class, BreakpointLocationRow::getTraceName, true, true), - THREADS("Threads", String.class, BreakpointLocationRow::getThreads, true, false), + BreakpointLocationRow::setState) { + @Override + public GColumnRenderer getRenderer() { + return STATE_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return LOC_STATE_EDITOR; + } + + @Override + public int getMinWidth() { + return 24; + } + + @Override + public int getMaxWidth() { + return 24; + } + }, + NAME("Name", String.class, BreakpointLocationRow::getName, BreakpointLocationRow::setName), + ADDRESS("Address", Address.class, BreakpointLocationRow::getAddress) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + TRACE("Trace", String.class, BreakpointLocationRow::getTraceName), + THREADS("Threads", String.class, BreakpointLocationRow::getThreads) { + @Override + public boolean isVisible() { + return false; + } + }, COMMENT("Comment", String.class, BreakpointLocationRow::getComment, - BreakpointLocationRow::setComment, true, true), - EXPRESSION("Expression", String.class, BreakpointLocationRow::getExpression, true, true), - SLEIGH("Sleigh", Boolean.class, BreakpointLocationRow::hasSleigh, true, true); + BreakpointLocationRow::setComment), + EXPRESSION("Expression", String.class, BreakpointLocationRow::getExpression), + SLEIGH("Sleigh", Boolean.class, BreakpointLocationRow::hasSleigh) { + @Override + public int getMaxWidth() { + return 30; + } + + @Override + public int getMinWidth() { + return 30; + } + + @Override + public boolean isVisible() { + return false; + } + }; private final String header; + private final Class cls; private final Function getter; private final BiConsumer setter; - private final boolean sortable; - private final boolean visible; - private final Class cls; BreakpointLocationTableColumns(String header, Class cls, - Function getter, boolean sortable, boolean visible) { - this(header, cls, getter, null, sortable, visible); + Function getter) { + this(header, cls, getter, null); } @SuppressWarnings("unchecked") BreakpointLocationTableColumns(String header, Class cls, Function getter, - BiConsumer setter, boolean sortable, boolean visible) { + BiConsumer setter) { this.header = header; this.cls = cls; this.getter = getter; this.setter = (BiConsumer) setter; - this.sortable = sortable; - this.visible = visible; + } + + @Override + public String getHeader() { + return header; } @Override @@ -207,26 +338,11 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter return getter.apply(row); } - @Override - public String getHeader() { - return header; - } - @Override public boolean isEditable(BreakpointLocationRow row) { return setter != null; } - @Override - public boolean isSortable() { - return sortable; - } - - @Override - public boolean isVisible() { - return visible; - } - @Override public void setValueOf(BreakpointLocationRow row, Object value) { setter.accept(row, value); @@ -1131,71 +1247,6 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter navigateToSelectedLocation(); } }); - - TableColumnModel bptColModel = breakpointTable.getColumnModel(); - TableColumn bptEnCol = bptColModel.getColumn(LogicalBreakpointTableColumns.STATE.ordinal()); - bptEnCol.setCellRenderer(new DebuggerBreakpointStateTableCellRenderer()); - bptEnCol.setCellEditor(new DebuggerBreakpointStateTableCellEditor<>(breakpointFilterPanel) { - @Override - protected State getToggledState(LogicalBreakpointRow row, State current) { - boolean mapped = row.isMapped(); - if (!mapped) { - tool.setStatusInfo("Breakpoint has no locations. Only toggling its bookmark.", - true); - } - return current.getToggled(mapped); - } - }); - bptEnCol.setMaxWidth(24); - bptEnCol.setMinWidth(24); - TableColumn bptNameCol = - bptColModel.getColumn(LogicalBreakpointTableColumns.NAME.ordinal()); - bptNameCol.setPreferredWidth(150); - TableColumn bptAddrCol = - bptColModel.getColumn(LogicalBreakpointTableColumns.ADDRESS.ordinal()); - bptAddrCol.setPreferredWidth(150); - bptAddrCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - TableColumn bptImgCol = - bptColModel.getColumn(LogicalBreakpointTableColumns.IMAGE.ordinal()); - bptImgCol.setPreferredWidth(100); - TableColumn lenCol = bptColModel.getColumn(LogicalBreakpointTableColumns.LENGTH.ordinal()); - lenCol.setPreferredWidth(60); - lenCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX); - TableColumn kindCol = bptColModel.getColumn(LogicalBreakpointTableColumns.KINDS.ordinal()); - kindCol.setPreferredWidth(150); - TableColumn locsCol = - bptColModel.getColumn(LogicalBreakpointTableColumns.LOCATIONS.ordinal()); - locsCol.setPreferredWidth(20); - TableColumn bptSleighCol = - bptColModel.getColumn(LogicalBreakpointTableColumns.SLEIGH.ordinal()); - bptSleighCol.setMaxWidth(30); - bptSleighCol.setMinWidth(30); - - GTableColumnModel locColModel = (GTableColumnModel) locationTable.getColumnModel(); - TableColumn locEnCol = - locColModel.getColumn(BreakpointLocationTableColumns.STATE.ordinal()); - locEnCol.setCellRenderer(new DebuggerBreakpointStateTableCellRenderer()); - locEnCol.setCellEditor(new DebuggerBreakpointStateTableCellEditor<>(locationFilterPanel) { - @Override - protected State getToggledState(BreakpointLocationRow row, State current) { - return current.getToggled(false); - } - }); - locEnCol.setMaxWidth(24); - locEnCol.setMinWidth(24); - TableColumn locAddrCol = - locColModel.getColumn(BreakpointLocationTableColumns.ADDRESS.ordinal()); - locAddrCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - TableColumn locThreadsCol = - locColModel.getColumn(BreakpointLocationTableColumns.THREADS.ordinal()); - TableColumn locSleighCol = - locColModel.getColumn(BreakpointLocationTableColumns.SLEIGH.ordinal()); - locSleighCol.setMaxWidth(30); - locSleighCol.setMinWidth(30); - - locColModel.setVisible(locThreadsCol, false); - locColModel.setVisible(locSleighCol, false); - } protected void navigateToSelectedBreakpoint() { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java index cc8fe0d9b6..edede8cb39 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.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,6 +17,7 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.util.concurrent.CompletableFuture; +import docking.Tool; import ghidra.debug.api.breakpoint.LogicalBreakpoint; import ghidra.debug.api.breakpoint.LogicalBreakpoint.Mode; import ghidra.debug.api.breakpoint.LogicalBreakpoint.State; @@ -159,4 +160,8 @@ public class LogicalBreakpointRow { } return !lb.getMappedTraces().isEmpty(); } + + public Tool getTool() { + return provider.getTool(); + } } 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 a174b75f85..6b95c8e616 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 @@ -63,43 +63,77 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter new Dimension(ACTION_BUTTON_SIZE, ACTION_BUTTON_SIZE); static final int MIN_ROW_HEIGHT = 16; + private static final ConsoleActionsCellRenderer ACTIONS_RENDERER = + new ConsoleActionsCellRenderer(); + private static final ConsoleActionsCellEditor ACTIONS_EDITOR = new ConsoleActionsCellEditor(); + protected enum LogTableColumns implements EnumeratedTableColumn> { - ICON("Icon", Icon.class, LogRow::icon, SortDirection.ASCENDING, false), - MESSAGE("Message", Object.class, LogRow::message, SortDirection.ASCENDING, false) { + ICON("Icon", Icon.class, LogRow::icon, SortDirection.ASCENDING) { + @Override + public int getMaxWidth() { + return 24; + } + + @Override + public int getMinWidth() { + return 24; + } + }, + MESSAGE("Message", Object.class, LogRow::message, SortDirection.ASCENDING) { @Override public GColumnRenderer getRenderer() { return HtmlOrProgressCellRenderer.INSTANCE; } - }, - ACTIONS("Actions", ActionList.class, LogRow::actions, SortDirection.DESCENDING, true) { - private static final ConsoleActionsCellRenderer RENDERER = - new ConsoleActionsCellRenderer(); @Override - public GColumnRenderer getRenderer() { - return RENDERER; + public int getPreferredWidth() { + return 150; } }, - TIME("Time", Date.class, LogRow::date, SortDirection.DESCENDING, false) { + ACTIONS("Actions", ActionList.class, LogRow::actions, SortDirection.DESCENDING) { + @Override + public GColumnRenderer getRenderer() { + return ACTIONS_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return ACTIONS_EDITOR; + } + + @Override + public boolean isEditable(LogRow row) { + return true; + } + + @Override + public int getPreferredWidth() { + return 50; + } + }, + TIME("Time", Date.class, LogRow::date, SortDirection.DESCENDING) { @Override public GColumnRenderer getRenderer() { return CustomToStringCellRenderer.TIME_24HMSms; } + + @Override + public int getPreferredWidth() { + return 15; + }; }; private final String header; private final Function, ?> getter; private final Class cls; private final SortDirection defaultSortDirection; - private final boolean editable; LogTableColumns(String header, Class cls, Function, T> getter, - SortDirection defaultSortDirection, boolean editable) { + SortDirection defaultSortDirection) { this.header = header; this.cls = cls; this.getter = getter; this.defaultSortDirection = defaultSortDirection; - this.editable = editable; } @Override @@ -117,11 +151,6 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter return getter.apply(row); } - @Override - public boolean isEditable(LogRow row) { - return editable; - } - @Override public void setValueOf(LogRow row, Object value) { } @@ -325,8 +354,8 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter } } - protected static class LogTableModel extends DebouncedRowWrappedEnumeratedColumnTableModel< // - LogTableColumns, ActionContext, LogRow, LogRow> { + protected static class LogTableModel extends DebouncedRowWrappedEnumeratedColumnTableModel< + LogTableColumns, ActionContext, LogRow, LogRow> { public LogTableModel(PluginTool tool) { super(tool, "Log", LogTableColumns.class, r -> r == null ? null : r.actionContext(), @@ -483,21 +512,6 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter }); logTable.setRowHeight(ACTION_BUTTON_SIZE + 2); - TableColumnModel columnModel = logTable.getColumnModel(); - - TableColumn iconCol = columnModel.getColumn(LogTableColumns.ICON.ordinal()); - iconCol.setMaxWidth(24); - iconCol.setMinWidth(24); - - TableColumn msgCol = columnModel.getColumn(LogTableColumns.MESSAGE.ordinal()); - msgCol.setPreferredWidth(150); - - TableColumn actCol = columnModel.getColumn(LogTableColumns.ACTIONS.ordinal()); - actCol.setPreferredWidth(50); - actCol.setCellEditor(new ConsoleActionsCellEditor()); - - TableColumn timeCol = columnModel.getColumn(LogTableColumns.TIME.ordinal()); - timeCol.setPreferredWidth(15); } protected boolean activateSelectedRow() { 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 b1d13fd887..85ccaa818c 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 @@ -24,8 +24,7 @@ import java.util.function.*; import java.util.stream.Collectors; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.TableCellEditor; import db.Transaction; import docking.ReusableDialogComponentProvider; @@ -51,6 +50,7 @@ import ghidra.trace.model.program.TraceProgramView; import ghidra.util.*; import ghidra.util.exception.CancelledException; import ghidra.util.table.GhidraTableFilterPanel; +import ghidra.util.table.column.GColumnRenderer; import ghidra.util.task.*; public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvider { @@ -136,15 +136,48 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid } } + private static final IconButtonTableCellRenderer REMOVE_BUTTON_RENDERER = + new IconButtonTableCellRenderer(DebuggerResources.ICON_DELETE, BUTTON_SIZE); + private static final IconButtonTableCellEditor REMOVE_BUTTON_EDITOR = + new IconButtonTableCellEditor<>(RangeEntry.class, DebuggerResources.ICON_DELETE) { + @Override + protected void clicked() { + if (!(model instanceof RangeTableModel mapModel)) { + return; + } + mapModel.dialog.removeEntry(row); + } + }; + protected enum RangeTableColumns implements EnumeratedTableColumn { - REMOVE("Remove", String.class, e -> "Remove Range", (e, v) -> nop(), null), + REMOVE("Remove", String.class, e -> "Remove Range", (e, v) -> nop(), null) { + @Override + public GColumnRenderer getRenderer() { + return REMOVE_BUTTON_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return REMOVE_BUTTON_EDITOR; + } + + @Override + public int getMaxWidth() { + return BUTTON_SIZE; + } + + @Override + public int getMinWidth() { + return BUTTON_SIZE; + } + }, REGION("Region", String.class, RangeEntry::getRegionName), MODULES("Modules", String.class, RangeEntry::getModuleNames), SECTIONS("Sections", String.class, RangeEntry::getSectionNames), SRC_MIN("SrcMin", Address.class, RangeEntry::getSrcMinAddress), SRC_MAX("SrcMax", Address.class, RangeEntry::getSrcMaxAddress), - BLOCK("Block", String.class, RangeEntry::getBlockName, RangeEntry::setBlockName, // + BLOCK("Block", String.class, RangeEntry::getBlockName, RangeEntry::setBlockName, RangeEntry::isCreate), OVERLAY("Overlay", Boolean.class, RangeEntry::isOverlay), DST_MIN("DstMin", Address.class, RangeEntry::getDstMinAddress), @@ -201,8 +234,11 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid protected static class RangeTableModel extends DefaultEnumeratedColumnTableModel { - public RangeTableModel(PluginTool tool) { + private final DebuggerCopyIntoProgramDialog dialog; + + public RangeTableModel(PluginTool tool, DebuggerCopyIntoProgramDialog dialog) { super(tool, "Ranges", RangeTableColumns.class); + this.dialog = dialog; } @Override @@ -311,7 +347,7 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid public DebuggerCopyIntoProgramDialog(PluginTool tool) { super("Copy Into Program", true, true, true, true); - tableModel = new RangeTableModel(tool); + tableModel = new RangeTableModel(tool, this); populateComponents(); } @@ -435,11 +471,7 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid addCancelButton(); addResetButton(); - TableColumnModel columnModel = table.getColumnModel(); - - TableColumn removeCol = columnModel.getColumn(RangeTableColumns.REMOVE.ordinal()); - CellEditorUtils.installButton(table, filterPanel, removeCol, DebuggerResources.ICON_DELETE, - BUTTON_SIZE, this::removeEntry); + table.setRowHeight(BUTTON_SIZE); } protected void addResetButton() { 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 d6f54e4883..bc3261b275 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 @@ -20,8 +20,7 @@ import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Function; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.TableCellEditor; import docking.widgets.table.*; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; @@ -33,22 +32,102 @@ import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemoryBlock; import ghidra.util.Swing; +import ghidra.util.table.column.GColumnRenderer; public class DebuggerRegionMapProposalDialog extends AbstractDebuggerMapProposalDialog { - static final int BUTTON_SIZE = 32; + private static final IconButtonTableCellRenderer REMOVE_BUTTON_RENDERER = + new IconButtonTableCellRenderer(DebuggerResources.ICON_DELETE, BUTTON_SIZE); + private static final IconButtonTableCellEditor REMOVE_BUTTON_EDITOR = + new IconButtonTableCellEditor<>(RegionMapEntry.class, DebuggerResources.ICON_DELETE) { + @Override + protected void clicked() { + if (!(model instanceof RegionMapPropsalTableModel mapModel)) { + return; + } + mapModel.dialog.removeEntry(row); + } + }; + + private static final IconButtonTableCellRenderer CHOOSE_BUTTON_RENDERER = + new IconButtonTableCellRenderer(DebuggerResources.ICON_PROGRAM, BUTTON_SIZE); + private static final IconButtonTableCellEditor CHOOSE_BUTTON_EDITOR = + new IconButtonTableCellEditor<>(RegionMapEntry.class, DebuggerResources.ICON_PROGRAM) { + @Override + protected void clicked() { + if (!(model instanceof RegionMapPropsalTableModel mapModel)) { + return; + } + mapModel.dialog.chooseAndSetBlock(row); + } + }; protected enum RegionMapTableColumns implements EnumeratedTableColumn { - REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()), + REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()) { + @Override + public GColumnRenderer getRenderer() { + return REMOVE_BUTTON_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return REMOVE_BUTTON_EDITOR; + } + + @Override + public int getMaxWidth() { + return BUTTON_SIZE; + } + + @Override + public int getMinWidth() { + return BUTTON_SIZE; + } + }, REGION_NAME("Region", String.class, e -> e.getRegionName()), - DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getRegionMinAddress()), - CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()), + DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getRegionMinAddress()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()) { + @Override + public GColumnRenderer getRenderer() { + return CHOOSE_BUTTON_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return CHOOSE_BUTTON_EDITOR; + } + + @Override + public int getMaxWidth() { + return BUTTON_SIZE; + } + + @Override + public int getMinWidth() { + return BUTTON_SIZE; + } + }, PROGRAM_NAME("Program", String.class, e -> e.getToProgram().getName()), BLOCK_NAME("Block", String.class, e -> e.getBlock().getName()), - STATIC_BASE("Static Base", Address.class, e -> e.getBlock().getStart()), - SIZE("Size", Long.class, e -> e.getMappingLength()); + STATIC_BASE("Static Base", Address.class, e -> e.getBlock().getStart()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + SIZE("Size", Long.class, e -> e.getMappingLength()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_ULONG_HEX; + } + }; private final String header; private final Class cls; @@ -100,9 +179,11 @@ public class DebuggerRegionMapProposalDialog protected static class RegionMapPropsalTableModel extends DefaultEnumeratedColumnTableModel { + protected final DebuggerRegionMapProposalDialog dialog; - public RegionMapPropsalTableModel(PluginTool tool) { + public RegionMapPropsalTableModel(PluginTool tool, DebuggerRegionMapProposalDialog dialog) { super(tool, "Region Map", RegionMapTableColumns.class); + this.dialog = dialog; } @Override @@ -120,33 +201,14 @@ public class DebuggerRegionMapProposalDialog @Override protected RegionMapPropsalTableModel createTableModel(PluginTool tool) { - return new RegionMapPropsalTableModel(tool); + return new RegionMapPropsalTableModel(tool, this); } @Override protected void populateComponents() { super.populateComponents(); setPreferredSize(600, 300); - - TableColumnModel columnModel = table.getColumnModel(); - - TableColumn removeCol = columnModel.getColumn(RegionMapTableColumns.REMOVE.ordinal()); - CellEditorUtils.installButton(table, filterPanel, removeCol, - DebuggerResources.ICON_DELETE, BUTTON_SIZE, this::removeEntry); - - TableColumn dynBaseCol = - columnModel.getColumn(RegionMapTableColumns.DYNAMIC_BASE.ordinal()); - dynBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - - TableColumn chooseCol = columnModel.getColumn(RegionMapTableColumns.CHOOSE.ordinal()); - CellEditorUtils.installButton(table, filterPanel, chooseCol, DebuggerResources.ICON_PROGRAM, - BUTTON_SIZE, this::chooseAndSetBlock); - - TableColumn stBaseCol = columnModel.getColumn(RegionMapTableColumns.STATIC_BASE.ordinal()); - stBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - - TableColumn sizeCol = columnModel.getColumn(RegionMapTableColumns.SIZE.ordinal()); - sizeCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX); + table.setRowHeight(BUTTON_SIZE); } private void chooseAndSetBlock(RegionMapEntry entry) { 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 ab49a239a0..2ca9aba852 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 @@ -19,8 +19,7 @@ import java.util.List; import java.util.function.BiConsumer; import java.util.function.Function; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.TableCellEditor; import docking.widgets.table.*; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; @@ -32,24 +31,105 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.util.Swing; +import ghidra.util.table.column.GColumnRenderer; public class DebuggerModuleMapProposalDialog extends AbstractDebuggerMapProposalDialog { - static final int BUTTON_SIZE = 32; + private static final IconButtonTableCellRenderer REMOVE_BUTTON_RENDERER = + new IconButtonTableCellRenderer(DebuggerResources.ICON_DELETE, BUTTON_SIZE); + private static final IconButtonTableCellEditor REMOVE_BUTTON_EDITOR = + new IconButtonTableCellEditor<>(ModuleMapEntry.class, DebuggerResources.ICON_DELETE) { + @Override + protected void clicked() { + if (!(model instanceof ModuleMapPropsalTableModel mapModel)) { + return; + } + mapModel.dialog.removeEntry(row); + } + }; + + private static final IconButtonTableCellRenderer CHOOSE_BUTTON_RENDERER = + new IconButtonTableCellRenderer(DebuggerResources.ICON_PROGRAM, BUTTON_SIZE); + private static final IconButtonTableCellEditor CHOOSE_BUTTON_EDITOR = + new IconButtonTableCellEditor<>(ModuleMapEntry.class, DebuggerResources.ICON_PROGRAM) { + @Override + protected void clicked() { + if (!(model instanceof ModuleMapPropsalTableModel mapModel)) { + return; + } + mapModel.dialog.chooseAndSetProgram(row); + } + }; protected enum ModuleMapTableColumns implements EnumeratedTableColumn { - REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()), + REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()) { + @Override + public GColumnRenderer getRenderer() { + return REMOVE_BUTTON_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return REMOVE_BUTTON_EDITOR; + } + + @Override + public int getMaxWidth() { + return BUTTON_SIZE; + } + + @Override + public int getMinWidth() { + return BUTTON_SIZE; + } + }, MODULE_NAME("Module", String.class, e -> e.getModuleName()), - DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getFromRange().getMinAddress()), - CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()), + DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getFromRange().getMinAddress()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()) { + @Override + public GColumnRenderer getRenderer() { + return CHOOSE_BUTTON_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return CHOOSE_BUTTON_EDITOR; + } + + @Override + public int getMaxWidth() { + return BUTTON_SIZE; + } + + @Override + public int getMinWidth() { + return BUTTON_SIZE; + } + }, PROGRAM_NAME("Program", String.class, e -> (e.getToProgram().getDomainFile() == null ? e.getToProgram().getName() : e.getToProgram().getDomainFile().getName())), - STATIC_BASE("Static Base", Address.class, e -> e.getToRange().getMinAddress()), - SIZE("Size", Long.class, e -> e.getFromRange().getLength()), - MEMORIZE("Memorize", Boolean.class, ModuleMapEntry::isMemorize, ModuleMapEntry::setMemorize); + STATIC_BASE("Static Base", Address.class, e -> e.getToRange().getMinAddress()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + SIZE("Size", Long.class, e -> e.getFromRange().getLength()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_ULONG_HEX; + } + }, + MEMORIZE("Memorize", Boolean.class, ModuleMapEntry::isMemorize, + ModuleMapEntry::setMemorize); private final String header; private final Class cls; @@ -100,9 +180,11 @@ public class DebuggerModuleMapProposalDialog protected static class ModuleMapPropsalTableModel extends DefaultEnumeratedColumnTableModel { + protected final DebuggerModuleMapProposalDialog dialog; - public ModuleMapPropsalTableModel(PluginTool tool) { + public ModuleMapPropsalTableModel(PluginTool tool, DebuggerModuleMapProposalDialog dialog) { super(tool, "Module Map", ModuleMapTableColumns.class); + this.dialog = dialog; } @Override @@ -120,33 +202,14 @@ public class DebuggerModuleMapProposalDialog @Override protected ModuleMapPropsalTableModel createTableModel(PluginTool tool) { - return new ModuleMapPropsalTableModel(tool); + return new ModuleMapPropsalTableModel(tool, this); } @Override protected void populateComponents() { super.populateComponents(); setPreferredSize(600, 300); - - TableColumnModel columnModel = table.getColumnModel(); - - TableColumn removeCol = columnModel.getColumn(ModuleMapTableColumns.REMOVE.ordinal()); - CellEditorUtils.installButton(table, filterPanel, removeCol, - DebuggerResources.ICON_DELETE, BUTTON_SIZE, this::removeEntry); - - TableColumn dynBaseCol = - columnModel.getColumn(ModuleMapTableColumns.DYNAMIC_BASE.ordinal()); - dynBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - - TableColumn chooseCol = columnModel.getColumn(ModuleMapTableColumns.CHOOSE.ordinal()); - CellEditorUtils.installButton(table, filterPanel, chooseCol, - DebuggerResources.ICON_PROGRAM, BUTTON_SIZE, this::chooseAndSetProgram); - - TableColumn stBaseCol = columnModel.getColumn(ModuleMapTableColumns.STATIC_BASE.ordinal()); - stBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - - TableColumn sizeCol = columnModel.getColumn(ModuleMapTableColumns.SIZE.ordinal()); - sizeCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX); + table.setRowHeight(BUTTON_SIZE); } private void chooseAndSetProgram(ModuleMapEntry entry) { 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 f5c4b38006..178113c8bb 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 @@ -20,8 +20,7 @@ import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Function; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.*; import docking.widgets.table.*; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; @@ -33,23 +32,103 @@ import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemoryBlock; import ghidra.util.Swing; +import ghidra.util.table.column.GColumnRenderer; public class DebuggerSectionMapProposalDialog extends AbstractDebuggerMapProposalDialog { - static final int BUTTON_SIZE = 32; + private static final IconButtonTableCellRenderer REMOVE_BUTTON_RENDERER = + new IconButtonTableCellRenderer(DebuggerResources.ICON_DELETE, BUTTON_SIZE); + private static final IconButtonTableCellEditor REMOVE_BUTTON_EDITOR = + new IconButtonTableCellEditor<>(SectionMapEntry.class, DebuggerResources.ICON_DELETE) { + @Override + protected void clicked() { + if (!(model instanceof SectionMapPropsalTableModel mapModel)) { + return; + } + mapModel.dialog.removeEntry(row); + } + }; + + private static final IconButtonTableCellRenderer CHOOSE_BUTTON_RENDERER = + new IconButtonTableCellRenderer(DebuggerResources.ICON_PROGRAM, BUTTON_SIZE); + private static final IconButtonTableCellEditor CHOOSE_BUTTON_EDITOR = + new IconButtonTableCellEditor<>(SectionMapEntry.class, DebuggerResources.ICON_PROGRAM) { + @Override + protected void clicked() { + if (!(model instanceof SectionMapPropsalTableModel mapModel)) { + return; + } + mapModel.dialog.chooseAndSetBlock(row); + } + }; protected enum SectionMapTableColumns implements EnumeratedTableColumn { - REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()), + REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()) { + @Override + public GColumnRenderer getRenderer() { + return REMOVE_BUTTON_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return REMOVE_BUTTON_EDITOR; + } + + @Override + public int getMaxWidth() { + return BUTTON_SIZE; + } + + @Override + public int getMinWidth() { + return BUTTON_SIZE; + } + }, MODULE_NAME("Module", String.class, e -> e.getModuleName()), SECTION_NAME("Section", String.class, e -> e.getSectionName()), - DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getSectionStart()), - CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()), + DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getSectionStart()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()) { + @Override + public GColumnRenderer getRenderer() { + return CHOOSE_BUTTON_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return CHOOSE_BUTTON_EDITOR; + } + + @Override + public int getMaxWidth() { + return BUTTON_SIZE; + } + + @Override + public int getMinWidth() { + return BUTTON_SIZE; + } + }, PROGRAM_NAME("Program", String.class, e -> e.getToProgram().getName()), BLOCK_NAME("Block", String.class, e -> e.getBlock().getName()), - STATIC_BASE("Static Base", Address.class, e -> e.getBlock().getStart()), - SIZE("Size", Long.class, e -> e.getMappingLength()); + STATIC_BASE("Static Base", Address.class, e -> e.getBlock().getStart()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + SIZE("Size", Long.class, e -> e.getMappingLength()) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_ULONG_HEX; + } + }; private final String header; private final Class cls; @@ -101,9 +180,12 @@ public class DebuggerSectionMapProposalDialog protected static class SectionMapPropsalTableModel extends DefaultEnumeratedColumnTableModel { + protected final DebuggerSectionMapProposalDialog dialog; - public SectionMapPropsalTableModel(PluginTool tool) { + public SectionMapPropsalTableModel(PluginTool tool, + DebuggerSectionMapProposalDialog dialog) { super(tool, "Section Map", SectionMapTableColumns.class); + this.dialog = dialog; } @Override @@ -121,33 +203,14 @@ public class DebuggerSectionMapProposalDialog @Override protected SectionMapPropsalTableModel createTableModel(PluginTool tool) { - return new SectionMapPropsalTableModel(tool); + return new SectionMapPropsalTableModel(tool, this); } @Override protected void populateComponents() { super.populateComponents(); setPreferredSize(600, 300); - - TableColumnModel columnModel = table.getColumnModel(); - - TableColumn removeCol = columnModel.getColumn(SectionMapTableColumns.REMOVE.ordinal()); - CellEditorUtils.installButton(table, filterPanel, removeCol, - DebuggerResources.ICON_DELETE, BUTTON_SIZE, this::removeEntry); - - TableColumn dynBaseCol = - columnModel.getColumn(SectionMapTableColumns.DYNAMIC_BASE.ordinal()); - dynBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - - TableColumn chooseCol = columnModel.getColumn(SectionMapTableColumns.CHOOSE.ordinal()); - CellEditorUtils.installButton(table, filterPanel, chooseCol, DebuggerResources.ICON_PROGRAM, - BUTTON_SIZE, this::chooseAndSetBlock); - - TableColumn stBaseCol = columnModel.getColumn(SectionMapTableColumns.STATIC_BASE.ordinal()); - stBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - - TableColumn sizeCol = columnModel.getColumn(SectionMapTableColumns.SIZE.ordinal()); - sizeCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX); + table.setRowHeight(BUTTON_SIZE); } private void chooseAndSetBlock(SectionMapEntry entry) { 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 cf6b8a52bb..1cb8a8e264 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 @@ -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. @@ -24,22 +24,21 @@ import java.util.Set; import java.util.function.Function; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; import db.Transaction; import docking.ActionContext; import docking.action.DockingAction; import docking.action.DockingActionIf; -import docking.widgets.table.CustomToStringCellRenderer; +import docking.widgets.table.*; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; -import docking.widgets.table.GTable; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerProvider; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.plugin.core.debug.utils.DebouncedRowWrappedEnumeratedColumnTableModel; import ghidra.app.services.*; +import ghidra.docking.settings.FormatSettingsDefinition; +import ghidra.docking.settings.SettingsDefinition; import ghidra.framework.model.DomainObjectEvent; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.annotation.AutoServiceConsumed; @@ -55,16 +54,55 @@ import ghidra.util.MathUtilities; import ghidra.util.Msg; import ghidra.util.database.ObjectKey; import ghidra.util.table.GhidraTableFilterPanel; +import ghidra.util.table.column.GColumnRenderer; public class DebuggerStaticMappingProvider extends ComponentProviderAdapter implements DebuggerProvider { + + protected static final HexDefaultGColumnRenderer LENGTH_RENDERER = + new HexDefaultGColumnRenderer<>(); + protected static final HexDefaultGColumnRenderer SHIFT_RENDERER = + new HexDefaultGColumnRenderer<>(); + protected static final SettingsDefinition[] SETTINGS_DEFS = + new SettingsDefinition[] { FormatSettingsDefinition.DEF_HEX, }; + protected enum StaticMappingTableColumns implements EnumeratedTableColumn { - DYNAMIC_ADDRESS("Dynamic Address", Address.class, StaticMappingRow::getTraceAddress), + DYNAMIC_ADDRESS("Dynamic Address", Address.class, StaticMappingRow::getTraceAddress) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, STATIC_URL("Static Program", URL.class, StaticMappingRow::getStaticProgramURL), - STATIC_ADDRESS("Static Address", String.class, StaticMappingRow::getStaticAddress), - LENGTH("Length", BigInteger.class, StaticMappingRow::getBigLength), - SHIFT("Shift", Long.class, StaticMappingRow::getShift), + STATIC_ADDRESS("Static Address", String.class, StaticMappingRow::getStaticAddress) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + LENGTH("Length", BigInteger.class, StaticMappingRow::getBigLength) { + @Override + public GColumnRenderer getRenderer() { + return LENGTH_RENDERER; + } + + @Override + public SettingsDefinition[] getSettingsDefinitions() { + return SETTINGS_DEFS; + } + }, + SHIFT("Shift", Long.class, StaticMappingRow::getShift) { + @Override + public GColumnRenderer getRenderer() { + return SHIFT_RENDERER; + } + + @Override + public SettingsDefinition[] getSettingsDefinitions() { + return SETTINGS_DEFS; + } + }, LIFESPAN("Lifespan", Lifespan.class, StaticMappingRow::getLifespan); private final String header; @@ -78,6 +116,11 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter this.getter = getter; } + @Override + public String getHeader() { + return header; + } + @Override public Class getValueClass() { return cls; @@ -87,15 +130,10 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter public Object getValueOf(StaticMappingRow row) { return getter.apply(row); } - - @Override - public String getHeader() { - return header; - } } protected static class MappingTableModel extends DebouncedRowWrappedEnumeratedColumnTableModel< // - StaticMappingTableColumns, ObjectKey, StaticMappingRow, TraceStaticMapping> { + StaticMappingTableColumns, ObjectKey, StaticMappingRow, TraceStaticMapping> { public MappingTableModel(PluginTool tool) { super(tool, "Mappings", StaticMappingTableColumns.class, @@ -224,19 +262,6 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter String namePrefix = "Static Mappings"; mappingTable.setAccessibleNamePrefix(namePrefix); mappingFilterPanel.setAccessibleNamePrefix(namePrefix); - - TableColumnModel columnModel = mappingTable.getColumnModel(); - TableColumn dynAddrCol = - columnModel.getColumn(StaticMappingTableColumns.DYNAMIC_ADDRESS.ordinal()); - dynAddrCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - TableColumn statAddrCol = - columnModel.getColumn(StaticMappingTableColumns.STATIC_ADDRESS.ordinal()); - statAddrCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - TableColumn lengthCol = columnModel.getColumn(StaticMappingTableColumns.LENGTH.ordinal()); - // TODO: Get user column settings working. Still, should default to Hex - lengthCol.setCellRenderer(CustomToStringCellRenderer.MONO_BIG_HEX); - TableColumn shiftCol = columnModel.getColumn(StaticMappingTableColumns.SHIFT.ordinal()); - shiftCol.setCellRenderer(CustomToStringCellRenderer.MONO_LONG_HEX); } protected void createActions() { 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 c43953ccee..39f00df9c0 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 @@ -26,8 +26,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.*; import db.Transaction; import docking.action.DockingAction; @@ -38,12 +37,11 @@ import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.pcode.UniqueRow.RefType; import ghidra.app.plugin.processors.sleigh.template.OpTpl; -import ghidra.app.services.DebuggerEmulationService; -import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.app.services.*; import ghidra.app.util.pcode.AbstractAppender; import ghidra.app.util.pcode.AbstractPcodeFormatter; import ghidra.async.SwingExecutorService; -import ghidra.base.widgets.table.DataTypeTableCellEditor; +import ghidra.base.widgets.table.AbstractDataTypeTableCellEditor; import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.docking.settings.Settings; import ghidra.framework.plugintool.*; @@ -70,6 +68,7 @@ import ghidra.util.*; import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.table.column.AbstractGColumnRenderer; +import ghidra.util.table.column.GColumnRenderer; public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { private static final FontRenderContext METRIC_FRC = @@ -102,10 +101,65 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { return u1.getAddress().compareTo(u2.getAddress()); }; + private static final CounterBackgroundCellRenderer SEQ_COL_RENDERER = + new CounterBackgroundCellRenderer(); + private static final PcodeCellRenderer CODE_COL_RENDERER = new PcodeCellRenderer(); + + // No filter panel on p-code protected enum PcodeTableColumns implements EnumeratedTableColumn { - SEQUENCE("Sequence", Integer.class, PcodeRow::getSequence), - LABEL("Label", String.class, PcodeRow::getLabel), - CODE("Code", String.class, PcodeRow::getCode); + SEQUENCE("Sequence", Integer.class, PcodeRow::getSequence) { + @Override + public GColumnRenderer getRenderer() { + return SEQ_COL_RENDERER; + } + + @Override + public int getMaxWidth() { + return SEQ_COL_RENDERER.measureColWidth("00"); + } + + @Override + public int getMinWidth() { + return SEQ_COL_RENDERER.measureColWidth("00"); + } + + @Override + public boolean isSortable() { + return true; + } + }, + LABEL("Label", String.class, PcodeRow::getLabel) { + @Override + public GColumnRenderer getRenderer() { + return CODE_COL_RENDERER; + } + + @Override + public int getMaxWidth() { + return CODE_COL_RENDERER.measureColWidth("<00>"); + } + + @Override + public int getMinWidth() { + return CODE_COL_RENDERER.measureColWidth("<00>"); + } + + @Override + public boolean isSortable() { + return false; + } + }, + CODE("Code", String.class, PcodeRow::getCode) { + @Override + public GColumnRenderer getRenderer() { + return CODE_COL_RENDERER; + } + + @Override + public boolean isSortable() { + return false; + } + }; private final String header; private final Function getter; @@ -131,11 +185,6 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { public Object getValueOf(PcodeRow row) { return getter.apply(row); } - - @Override - public boolean isSortable() { - return this == SEQUENCE; // HACK - } } protected static class PcodeTableModel @@ -150,14 +199,73 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { } } + private static final UniqueRefCellRenderer UNIQUE_REF_RENDERER = new UniqueRefCellRenderer(); + private static final UniqueDataTypeEditor UNIQUE_DT_EDITOR = new UniqueDataTypeEditor(); + protected enum UniqueTableColumns implements EnumeratedTableColumn { - REF("Ref", RefType.class, UniqueRow::getRefType), - UNIQUE("Unique", String.class, UniqueRow::getName), - BYTES("Bytes", String.class, UniqueRow::getBytes), - VALUE("Value", BigInteger.class, UniqueRow::getValue), - TYPE("Type", DataType.class, UniqueRow::getDataType, UniqueRow::setDataType), - REPR("Repr", String.class, UniqueRow::getValueRepresentation); + REF("Ref", RefType.class, UniqueRow::getRefType) { + @Override + public GColumnRenderer getRenderer() { + return UNIQUE_REF_RENDERER; + } + + @Override + public int getMaxWidth() { + return 24; + } + + @Override + public int getMinWidth() { + return 24; + } + }, + UNIQUE("Unique", String.class, UniqueRow::getName) { + @Override + public int getPreferredWidth() { + return 45; + } + }, + BYTES("Bytes", String.class, UniqueRow::getBytes) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + + @Override + public int getPreferredWidth() { + return 65; + } + }, + // LATER: Changed coloring? + VALUE("Value", BigInteger.class, UniqueRow::getValue) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_BIG_HEX; + } + + @Override + public int getPreferredWidth() { + return 45; + } + }, + TYPE("Type", DataType.class, UniqueRow::getDataType, UniqueRow::setDataType) { + @Override + public TableCellEditor getEditor() { + return UNIQUE_DT_EDITOR; + } + + @Override + public int getPreferredWidth() { + return 45; + } + }, + REPR("Repr", String.class, UniqueRow::getValueRepresentation) { + @Override + public int getPreferredWidth() { + return 45; + } + }; private final String header; private final Function getter; @@ -203,7 +311,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { } } - protected static class UniqueTableModel + protected class UniqueTableModel extends DefaultEnumeratedColumnTableModel { public UniqueTableModel(PluginTool tool) { super(tool, "Unique", UniqueTableColumns.class); @@ -213,31 +321,60 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { public List defaultSortOrder() { return List.of(UniqueTableColumns.UNIQUE); } + + ServiceProvider getServiceProvider() { + return serviceProvider; + } + + Trace getTrace() { + return current.getTrace(); + } } - class UniqueDataTypeEditor extends DataTypeTableCellEditor { - public UniqueDataTypeEditor() { - super(plugin.getTool()); + static class UniqueDataTypeEditor extends AbstractDataTypeTableCellEditor { + @Override + protected DataTypeManagerService getService(TableModel model) { + if (!(model instanceof UniqueTableModel uModel)) { + return null; + } + return uModel.getServiceProvider().getService(DataTypeManagerService.class); } @Override - protected DataType resolveSelection(DataType dataType) { - if (dataType == null) { + protected DataType resolveSelection(DataType dataType, TableModel model) { + if (dataType == null || !(model instanceof UniqueTableModel uModel)) { return null; } - try (Transaction tid = current.getTrace().openTransaction("Resolve DataType")) { - return current.getTrace().getDataTypeManager().resolve(dataType, null); + Trace trace = uModel.getTrace(); + if (trace == null) { + return null; + } + try (Transaction tid = trace.openTransaction("Resolve DataType")) { + return trace.getDataTypeManager().resolve(dataType, null); } } } - class CounterBackgroundCellRenderer extends AbstractGColumnRenderer { + static class CounterBackgroundCellRenderer extends AbstractGColumnRenderer { Color foregroundColor = getForeground(); + protected int measureColWidth(String sample) { + Font font = getFont(); + Insets insets = getBorder().getBorderInsets(this); + return (int) font.getStringBounds(sample, METRIC_FRC).getWidth() + insets.left + + insets.right; + } + + protected int measureWidthHtml(String sampleHtml) { + String sampleText = HTMLUtilities.fromHTML(sampleHtml); + return measureColWidth(sampleText); + } + @Override public Component getTableCellRendererComponent(GTableCellRenderingData data) { super.getTableCellRendererComponent(data); - setForeground(pcodeTable.getForeground()); + JTable table = data.getTable(); + setForeground(table.getForeground()); PcodeRow row = (PcodeRow) data.getRowObject(); if (data.isSelected()) { if (row.isNext()) { @@ -253,7 +390,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { setBackground(COLOR_BACKGROUND_COUNTER); } else { - setBackground(pcodeTable.getBackground()); + setBackground(table.getBackground()); setOpaque(true); } setBorder(noFocusBorder); @@ -266,7 +403,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { } } - class PcodeCellRenderer extends CounterBackgroundCellRenderer { + static class PcodeCellRenderer extends CounterBackgroundCellRenderer { { setHTMLRenderingEnabled(true); } @@ -286,7 +423,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { } } - class UniqueRefCellRenderer extends AbstractGColumnRenderer { + static class UniqueRefCellRenderer extends AbstractGColumnRenderer { @Override public Component getTableCellRendererComponent(GTableCellRenderingData data) { super.getTableCellRendererComponent(data); @@ -573,8 +710,6 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { final PcodeTableModel pcodeTableModel; GhidraTable pcodeTable; JLabel instructionLabel; - // No filter panel on p-code - PcodeCellRenderer codeColRenderer; DockingAction actionStepBackward; DockingAction actionStepForward; @@ -600,18 +735,6 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { contextChanged(); } - protected int measureColWidth(JLabel renderer, String sample) { - Font font = renderer.getFont(); - Insets insets = renderer.getBorder().getBorderInsets(renderer); - return (int) font.getStringBounds(sample, METRIC_FRC).getWidth() + insets.left + - insets.right; - } - - protected int measureWidthHtml(JLabel renderer, String sampleHtml) { - String sampleText = HTMLUtilities.fromHTML(sampleHtml); - return measureColWidth(renderer, sampleText); - } - protected void buildMainPanel() { // An intervening panel to interrupt swings table-viewport nonsense // This will allow the viewport to properly fit the table's contents @@ -648,41 +771,6 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { } uniqueTableModel.fireTableDataChanged(); }); - - TableColumnModel pcodeColModel = pcodeTable.getColumnModel(); - TableColumn seqCol = pcodeColModel.getColumn(PcodeTableColumns.SEQUENCE.ordinal()); - CounterBackgroundCellRenderer seqColRenderer = new CounterBackgroundCellRenderer(); - seqCol.setCellRenderer(seqColRenderer); - int seqColWidth = measureColWidth(seqColRenderer, "00"); - seqCol.setMinWidth(seqColWidth); - seqCol.setMaxWidth(seqColWidth); - TableColumn labelCol = pcodeColModel.getColumn(PcodeTableColumns.LABEL.ordinal()); - codeColRenderer = new PcodeCellRenderer(); - labelCol.setCellRenderer(codeColRenderer); - int labelColWidth = measureColWidth(codeColRenderer, "<00>"); - labelCol.setMinWidth(labelColWidth); - labelCol.setMaxWidth(labelColWidth); - TableColumn codeCol = pcodeColModel.getColumn(PcodeTableColumns.CODE.ordinal()); - codeCol.setCellRenderer(codeColRenderer); - - TableColumnModel uniqueColModel = uniqueTable.getColumnModel(); - TableColumn refCol = uniqueColModel.getColumn(UniqueTableColumns.REF.ordinal()); - refCol.setCellRenderer(new UniqueRefCellRenderer()); - refCol.setMinWidth(24); - refCol.setMaxWidth(24); - TableColumn uniqCol = uniqueColModel.getColumn(UniqueTableColumns.UNIQUE.ordinal()); - uniqCol.setPreferredWidth(45); - TableColumn bytesCol = uniqueColModel.getColumn(UniqueTableColumns.BYTES.ordinal()); - bytesCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); - bytesCol.setPreferredWidth(65); - TableColumn valCol = uniqueColModel.getColumn(UniqueTableColumns.VALUE.ordinal()); - valCol.setCellRenderer(CustomToStringCellRenderer.MONO_BIG_HEX); // TODO: Changed coloring - valCol.setPreferredWidth(45); - TableColumn typeCol = uniqueColModel.getColumn(UniqueTableColumns.TYPE.ordinal()); - typeCol.setCellEditor(new UniqueDataTypeEditor()); - typeCol.setPreferredWidth(45); - TableColumn reprCol = uniqueColModel.getColumn(UniqueTableColumns.REPR.ordinal()); - reprCol.setPreferredWidth(45); } protected void createActions() { @@ -766,10 +854,11 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { protected int computeCodeColWidth(List rows) { return rows.stream() - .map(r -> measureWidthHtml(codeColRenderer, r.getCode())) + .map(r -> CODE_COL_RENDERER.measureWidthHtml(r.getCode())) .reduce(0, Integer::max); } + // LATER: Just hard-code a reasonable minimum? protected void adjustCodeColWidth(List rows) { TableColumn codeCol = pcodeTable.getColumnModel().getColumn(PcodeTableColumns.CODE.ordinal()); 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 06aa029723..046cbe6d0f 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 @@ -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. @@ -39,7 +39,8 @@ public class DebuggerSelectPlatformOfferDialog extends DialogComponentProvider { protected enum OfferTableColumns implements EnumeratedTableColumn { - CONFIDENCE("Confidence", Integer.class, DebuggerPlatformOffer::getConfidence, SortDirection.DESCENDING), + CONFIDENCE("Confidence", Integer.class, DebuggerPlatformOffer::getConfidence, + SortDirection.DESCENDING), PROCESSOR("Processor", String.class, OfferTableColumns::getProcessor), VARIANT("Variant", String.class, OfferTableColumns::getVariant), SIZE("Size", Integer.class, OfferTableColumns::getSize), @@ -111,6 +112,11 @@ public class DebuggerSelectPlatformOfferDialog extends DialogComponentProvider { this(header, cls, getter, SortDirection.ASCENDING); } + @Override + public String getHeader() { + return header; + } + @Override public Class getValueClass() { return cls; @@ -121,11 +127,6 @@ public class DebuggerSelectPlatformOfferDialog extends DialogComponentProvider { return getter.apply(row); } - @Override - public String getHeader() { - return header; - } - @Override public SortDirection defaultSortDirection() { return sortDir; 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 6c7d014f80..cfdd05e30a 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 @@ -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. @@ -22,8 +22,6 @@ import java.util.function.BiConsumer; import java.util.function.Function; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; import docking.ActionContext; import docking.ReusableDialogComponentProvider; @@ -41,35 +39,84 @@ public class DebuggerAvailableRegistersDialog extends ReusableDialogComponentPro protected enum AvailableRegisterTableColumns implements EnumeratedTableColumn { - SELECTED("", Boolean.class, AvailableRegisterRow::isSelected, AvailableRegisterRow::setSelected, true), - NUMBER("#", Integer.class, AvailableRegisterRow::getNumber, true), - NAME("Name", String.class, AvailableRegisterRow::getName, true), - BITS("Bits", Integer.class, AvailableRegisterRow::getBits, true), - KNOWN("Known", Boolean.class, AvailableRegisterRow::isKnown, true), - GROUP("Group", String.class, AvailableRegisterRow::getGroup, true), - CONTAINS("Contains", String.class, AvailableRegisterRow::getContains, true), - PARENT("Parent", String.class, AvailableRegisterRow::getParentName, true); + SELECTED("", Boolean.class, AvailableRegisterRow::isSelected, + AvailableRegisterRow::setSelected) { + @Override + public int getMaxWidth() { + return 30; + } + + @Override + public int getMinWidth() { + return 30; + } + }, + NUMBER("#", Integer.class, AvailableRegisterRow::getNumber) { + @Override + public int getPreferredWidth() { + return 1; + } + }, + NAME("Name", String.class, AvailableRegisterRow::getName) { + @Override + public int getPreferredWidth() { + return 40; + } + }, + BITS("Bits", Integer.class, AvailableRegisterRow::getBits) { + @Override + public int getPreferredWidth() { + return 30; + } + }, + KNOWN("Known", Boolean.class, AvailableRegisterRow::isKnown) { + @Override + public int getPreferredWidth() { + return 20; + } + }, + GROUP("Group", String.class, AvailableRegisterRow::getGroup) { + @Override + public int getPreferredWidth() { + return 40; + } + }, + CONTAINS("Contains", String.class, AvailableRegisterRow::getContains) { + @Override + public int getPreferredWidth() { + return 20; + } + }, + PARENT("Parent", String.class, AvailableRegisterRow::getParentName) { + @Override + public int getPreferredWidth() { + return 30; + } + }; private final String header; + private final Class cls; private final Function getter; private final BiConsumer setter; - private final boolean sortable; - private final Class cls; AvailableRegisterTableColumns(String header, Class cls, - Function getter, boolean sortable) { - this(header, cls, getter, null, sortable); + Function getter) { + this(header, cls, getter, null); } @SuppressWarnings("unchecked") AvailableRegisterTableColumns(String header, Class cls, Function getter, - BiConsumer setter, boolean sortable) { + BiConsumer setter) { this.header = header; this.cls = cls; this.getter = getter; this.setter = (BiConsumer) setter; - this.sortable = sortable; + } + + @Override + public String getHeader() { + return header; } @Override @@ -82,21 +129,11 @@ public class DebuggerAvailableRegistersDialog extends ReusableDialogComponentPro return getter.apply(row); } - @Override - public String getHeader() { - return header; - } - @Override public boolean isEditable(AvailableRegisterRow row) { return setter != null; } - @Override - public boolean isSortable() { - return sortable; - } - @Override public void setValueOf(AvailableRegisterRow row, Object value) { setter.accept(row, value); @@ -154,27 +191,6 @@ public class DebuggerAvailableRegistersDialog extends ReusableDialogComponentPro panel.getAccessibleContext().setAccessibleName("Available Debugger Registers"); addWorkPanel(panel); - TableColumnModel columnModel = availableTable.getColumnModel(); - TableColumn numCol = columnModel.getColumn(AvailableRegisterTableColumns.NUMBER.ordinal()); - numCol.setPreferredWidth(1); - TableColumn selCol = - columnModel.getColumn(AvailableRegisterTableColumns.SELECTED.ordinal()); - selCol.setPreferredWidth(20); - TableColumn nameCol = columnModel.getColumn(AvailableRegisterTableColumns.NAME.ordinal()); - nameCol.setPreferredWidth(40); - TableColumn bitsCol = columnModel.getColumn(AvailableRegisterTableColumns.BITS.ordinal()); - bitsCol.setPreferredWidth(30); - TableColumn knownCol = columnModel.getColumn(AvailableRegisterTableColumns.KNOWN.ordinal()); - knownCol.setPreferredWidth(20); - TableColumn groupCol = columnModel.getColumn(AvailableRegisterTableColumns.GROUP.ordinal()); - groupCol.setPreferredWidth(40); - TableColumn containsCol = - columnModel.getColumn(AvailableRegisterTableColumns.CONTAINS.ordinal()); - containsCol.setPreferredWidth(20); - TableColumn parentCol = - columnModel.getColumn(AvailableRegisterTableColumns.PARENT.ordinal()); - parentCol.setPreferredWidth(30); - addOKButton(); addCancelButton(); 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 7a2d656f8c..cd93156f71 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 @@ -25,8 +25,8 @@ import java.util.concurrent.CompletableFuture; import java.util.function.*; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableModel; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -49,7 +49,7 @@ import ghidra.app.services.*; import ghidra.app.services.DebuggerControlService.StateEditor; import ghidra.async.AsyncLazyValue; import ghidra.async.AsyncUtils; -import ghidra.base.widgets.table.DataTypeTableCellEditor; +import ghidra.base.widgets.table.AbstractDataTypeTableCellEditor; import ghidra.debug.api.target.Target; import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.docking.settings.*; @@ -140,6 +140,14 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter } } + protected static final RegisterValueCellRenderer VALUE_RENDERER = + new RegisterValueCellRenderer(); + protected static final HexBigIntegerTableCellEditor VALUE_EDITOR = + new HexBigIntegerTableCellEditor(); + protected static final SettingsDefinition[] VALUE_DEFS = + new SettingsDefinition[] { FormatSettingsDefinition.DEF_HEX, }; + protected static final RegisterDataTypeEditor TYPE_EDITOR = new RegisterDataTypeEditor(); + protected enum RegisterTableColumns implements EnumeratedTableColumn { FAV("Fav", 1, Boolean.class, RegisterRow::isFavorite, RegisterRow::setFavorite, r -> true, @@ -148,23 +156,29 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter NAME("Name", 40, String.class, RegisterRow::getName), VALUE("Value", 100, BigInteger.class, RegisterRow::getValue, RegisterRow::setValue, RegisterRow::isValueEditable, SortDirection.ASCENDING) { - private static final RegisterValueCellRenderer RENDERER = - new RegisterValueCellRenderer(); - private static final SettingsDefinition[] DEFS = - new SettingsDefinition[] { FormatSettingsDefinition.DEF_HEX, }; @Override public GColumnRenderer getRenderer() { - return RENDERER; + return VALUE_RENDERER; + } + + @Override + public TableCellEditor getEditor() { + return VALUE_EDITOR; } @Override public SettingsDefinition[] getSettingsDefinitions() { - return DEFS; + return VALUE_DEFS; } }, TYPE("Type", 40, DataType.class, RegisterRow::getDataType, RegisterRow::setDataType, - r -> true, SortDirection.ASCENDING), + r -> true, SortDirection.ASCENDING) { + @Override + public TableCellEditor getEditor() { + return TYPE_EDITOR; + } + }, REPR("Repr", 100, String.class, RegisterRow::getRepresentation, RegisterRow::setRepresentation, RegisterRow::isRepresentationEditable, SortDirection.ASCENDING); @@ -195,6 +209,11 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter this.direction = direction; } + @Override + public String getHeader() { + return header; + } + @Override public Class getValueClass() { return cls; @@ -205,11 +224,6 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter return getter.apply(row); } - @Override - public String getHeader() { - return header; - } - @Override public boolean isEditable(RegisterRow row) { return editable != null && editable.test(row); @@ -231,7 +245,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter } } - protected static class RegistersTableModel + protected class RegistersTableModel extends DefaultEnumeratedColumnTableModel { public RegistersTableModel(PluginTool tool) { super(tool, "Registers", RegisterTableColumns.class); @@ -251,6 +265,14 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter } return descriptor; } + + ServiceProvider getServiceProvider() { + return serviceProvider; + } + + Trace getTrace() { + return currentTrace; + } } protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) { @@ -438,23 +460,23 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter public final Component getTableCellRendererComponent(GTableCellRenderingData data) { super.getTableCellRendererComponent(data); applyStateColors(this, data, RegisterRow::isChanged); + setFont(fixedWidthFont); return this; } } - class RegisterDataTypeEditor extends DataTypeTableCellEditor { - public RegisterDataTypeEditor() { - super(plugin.getTool()); - } - + static class RegisterDataTypeEditor extends AbstractDataTypeTableCellEditor { @Override protected AllowedDataTypes getAllowed(int row, int column) { return AllowedDataTypes.FIXED_LENGTH; } @Override - protected boolean validateSelection(DataType dataType) { - RegisterRow row = regsTableModel.getModelData().get(regsTable.getEditingRow()); + protected boolean validateSelection(DataType dataType, TableModel model) { + if (!(model instanceof RegistersTableModel rModel)) { + return false; + } + RegisterRow row = rModel.getModelData().get(table.getEditingRow()); if (row == null) { return false; } @@ -462,12 +484,21 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter } @Override - protected DataType resolveSelection(DataType dataType) { - if (dataType == null) { + protected DataTypeManagerService getService(TableModel model) { + if (!(model instanceof RegistersTableModel rModel)) { return null; } - try (Transaction tx = currentTrace.openTransaction("Resolve DataType")) { - return currentTrace.getDataTypeManager().resolve(dataType, null); + return rModel.getServiceProvider().getService(DataTypeManagerService.class); + } + + @Override + protected DataType resolveSelection(DataType dataType, TableModel model) { + if (dataType == null || !(model instanceof RegistersTableModel rModel)) { + return null; + } + Trace trace = rModel.getTrace(); + try (Transaction tx = trace.openTransaction("Resolve DataType")) { + return trace.getDataTypeManager().resolve(dataType, null); } } } @@ -575,7 +606,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter protected void buildMainPanel() { regsTable = new GhidraTable(regsTableModel); - // TODO: Allow multiple selection for copy, etc.? + // LATER: Allow multiple selection for copy, etc.? mainPanel.add(new JScrollPane(regsTable)); regsFilterPanel = new GhidraTableFilterPanel<>(regsTable, regsTableModel); mainPanel.add(regsFilterPanel, BorderLayout.SOUTH); @@ -608,12 +639,6 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter } } }); - - TableColumnModel columnModel = regsTable.getColumnModel(); - TableColumn valCol = columnModel.getColumn(RegisterTableColumns.VALUE.ordinal()); - valCol.setCellEditor(new HexBigIntegerTableCellEditor()); - TableColumn typeCol = columnModel.getColumn(RegisterTableColumns.TYPE.ordinal()); - typeCol.setCellEditor(new RegisterDataTypeEditor()); } @Override 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 4547c352d7..939b5b391d 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 @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.stream.Collectors; import javax.swing.*; -import javax.swing.table.*; import docking.widgets.table.*; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; @@ -45,6 +44,7 @@ import ghidra.trace.util.TraceEvents; import ghidra.util.DateUtils; import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.table.column.AbstractGColumnRenderer; +import ghidra.util.table.column.GColumnRenderer; public class DebuggerSnapshotTablePanel extends JPanel { private static final Color COLOR_FOREGROUND_STALE = @@ -52,39 +52,104 @@ public class DebuggerSnapshotTablePanel extends JPanel { private static final Color COLOR_FOREGROUND_STALE_SEL = new GColor("color.debugger.plugin.resources.register.stale.selected"); + static final StyleCurrentSnapRenderer STYLE_CURRENT_SNAP_RENDERER = + new StyleCurrentSnapRenderer(); + protected enum SnapshotTableColumns implements EnumeratedTableColumn { - SNAP("Snap", Long.class, SnapshotRow::getSnap, false), - TIME("Time", TraceSchedule.class, SnapshotRow::getTime, true), - EVENT_THREAD("Event Thread", String.class, SnapshotRow::getEventThreadName, true), - PC("PC", Address.class, SnapshotRow::getProgramCounter, true), - MODULE("Module", String.class, SnapshotRow::getModuleName, true), - FUNCTION("Function", ghidra.program.model.listing.Function.class, SnapshotRow::getFunction, - true), - TIMESTAMP("Timestamp", Date.class, SnapshotRow::getTimeStamp, false), - SCHEDULE("Schedule", TraceSchedule.class, SnapshotRow::getSchedule, false), + SNAP("Snap", Long.class, SnapshotRow::getSnap) { + @Override + public int getPreferredWidth() { + return 20; + } + + @Override + public boolean isVisible() { + return false; + } + }, + TIME("Time", TraceSchedule.class, SnapshotRow::getTime) { + @Override + public int getPreferredWidth() { + return 20; + } + }, + EVENT_THREAD("Event Thread", String.class, SnapshotRow::getEventThreadName) { + @Override + public int getPreferredWidth() { + return 20; + } + }, + PC("PC", Address.class, SnapshotRow::getProgramCounter) { + @Override + public int getPreferredWidth() { + return 40; + } + }, + MODULE("Module", String.class, SnapshotRow::getModuleName) { + @Override + public int getPreferredWidth() { + return 40; + } + }, + FUNCTION("Function", ghidra.program.model.listing.Function.class, + SnapshotRow::getFunction) { + @Override + public int getPreferredWidth() { + return 40; + } + }, + TIMESTAMP("Timestamp", Date.class, SnapshotRow::getTimeStamp) { + @Override + public int getPreferredWidth() { + return 200; + } + + @Override + public boolean isVisible() { + return false; + } + }, + SCHEDULE("Schedule", TraceSchedule.class, SnapshotRow::getSchedule) { + @Override + public int getPreferredWidth() { + return 60; + } + + @Override + public boolean isVisible() { + return false; + } + }, DESCRIPTION("Description", String.class, SnapshotRow::getDescription, - SnapshotRow::setDescription, true); + SnapshotRow::setDescription) { + @Override + public int getPreferredWidth() { + return 20; + } + }; private final String header; private final Function getter; private final BiConsumer setter; private final Class cls; - private final boolean visible; - SnapshotTableColumns(String header, Class cls, Function getter, - boolean visible) { - this(header, cls, getter, null, visible); + SnapshotTableColumns(String header, Class cls, Function getter) { + this(header, cls, getter, null); } @SuppressWarnings("unchecked") SnapshotTableColumns(String header, Class cls, Function getter, - BiConsumer setter, boolean visible) { + BiConsumer setter) { this.header = header; this.cls = cls; this.getter = getter; this.setter = (BiConsumer) setter; - this.visible = visible; + } + + @Override + public String getHeader() { + return header; } @Override @@ -97,28 +162,23 @@ public class DebuggerSnapshotTablePanel extends JPanel { return getter.apply(row); } - @Override - public String getHeader() { - return header; - } - @Override public boolean isEditable(SnapshotRow row) { return setter != null; } - @Override - public boolean isVisible() { - return visible; - } - @Override public void setValueOf(SnapshotRow row, Object value) { setter.accept(row, value); } + + @Override + public GColumnRenderer getRenderer() { + return STYLE_CURRENT_SNAP_RENDERER; + } } - protected static class SnapshotTableModel + protected class SnapshotTableModel extends DefaultEnumeratedColumnTableModel { public SnapshotTableModel(PluginTool tool) { super(tool, "Snapshots", SnapshotTableColumns.class); @@ -128,6 +188,14 @@ public class DebuggerSnapshotTablePanel extends JPanel { public List defaultSortOrder() { return List.of(SnapshotTableColumns.TIME); } + + Trace getTrace() { + return currentTrace; + } + + DebuggerCoordinates getCurrent() { + return current; + } } private class SnapshotListener extends TraceDomainObjectListener { @@ -181,7 +249,16 @@ public class DebuggerSnapshotTablePanel extends JPanel { } } - final TableCellRenderer styleCurrentRenderer = new AbstractGColumnRenderer() { + static class StyleCurrentSnapRenderer extends AbstractGColumnRenderer + implements GTableAccess { + + SnapshotTableModel model; + + protected TimeRadix getTimeRadix() { + Trace trace = model.getTrace(); + return trace == null ? TimeRadix.DEFAULT : trace.getTimeManager().getTimeRadix(); + } + @Override protected String formatNumber(Number value, Settings settings) { return switch (value) { @@ -248,8 +325,13 @@ public class DebuggerSnapshotTablePanel extends JPanel { @Override public Component getTableCellRendererComponent(GTableCellRenderingData data) { + if (!(getUnwrappedModel(data.getTable()) instanceof SnapshotTableModel model)) { + return null; + } + this.model = model; super.getTableCellRendererComponent(data); SnapshotRow row = (SnapshotRow) data.getRowObject(); + DebuggerCoordinates current = model.getCurrent(); if (row == null || current == DebuggerCoordinates.NOWHERE) { // When used in a dialog, only currentTrace is set return this; @@ -277,7 +359,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { return this; } - }; + } protected final PluginTool tool; protected final SnapshotTableModel snapshotTableModel; @@ -300,40 +382,6 @@ public class DebuggerSnapshotTablePanel extends JPanel { snapshotFilterPanel = new GhidraTableFilterPanel<>(snapshotTable, snapshotTableModel); add(snapshotFilterPanel, BorderLayout.SOUTH); - - TableColumnModel columnModel = snapshotTable.getColumnModel(); - TableColumn snapCol = columnModel.getColumn(SnapshotTableColumns.SNAP.ordinal()); - snapCol.setPreferredWidth(20); - snapCol.setCellRenderer(styleCurrentRenderer); - TableColumn timeCol = columnModel.getColumn(SnapshotTableColumns.TIME.ordinal()); - timeCol.setPreferredWidth(20); - timeCol.setCellRenderer(styleCurrentRenderer); - TableColumn etCol = columnModel.getColumn(SnapshotTableColumns.EVENT_THREAD.ordinal()); - etCol.setPreferredWidth(20); - etCol.setCellRenderer(styleCurrentRenderer); - TableColumn pcCol = columnModel.getColumn(SnapshotTableColumns.PC.ordinal()); - pcCol.setPreferredWidth(40); - pcCol.setCellRenderer(styleCurrentRenderer); - TableColumn moduleCol = columnModel.getColumn(SnapshotTableColumns.MODULE.ordinal()); - moduleCol.setPreferredWidth(40); - moduleCol.setCellRenderer(styleCurrentRenderer); - TableColumn functionCol = columnModel.getColumn(SnapshotTableColumns.FUNCTION.ordinal()); - functionCol.setPreferredWidth(40); - functionCol.setCellRenderer(styleCurrentRenderer); - TableColumn timeStampCol = columnModel.getColumn(SnapshotTableColumns.TIMESTAMP.ordinal()); - timeStampCol.setPreferredWidth(200); - timeStampCol.setCellRenderer(styleCurrentRenderer); - TableColumn schdCol = columnModel.getColumn(SnapshotTableColumns.SCHEDULE.ordinal()); - schdCol.setPreferredWidth(60); - schdCol.setCellRenderer(styleCurrentRenderer); - TableColumn descCol = columnModel.getColumn(SnapshotTableColumns.DESCRIPTION.ordinal()); - descCol.setPreferredWidth(20); - descCol.setCellRenderer(styleCurrentRenderer); - } - - protected TimeRadix getTimeRadix() { - return currentTrace == null ? TimeRadix.DEFAULT - : currentTrace.getTimeManager().getTimeRadix(); } private void addNewListeners() { 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 f3727f9f75..eddf31fa22 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 @@ -26,8 +26,8 @@ import java.util.function.Function; import java.util.function.Predicate; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableModel; import org.jdom2.Element; @@ -52,7 +52,7 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.services.*; import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncTimer; -import ghidra.base.widgets.table.DataTypeTableCellEditor; +import ghidra.base.widgets.table.AbstractDataTypeTableCellEditor; import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.watch.WatchRow; import ghidra.docking.settings.*; @@ -148,6 +148,9 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter } } + protected static final WatchValueCellRenderer VALUE_RENDERER = new WatchValueCellRenderer(); + protected static final WatchDataTypeEditor TYPE_EDITOR = new WatchDataTypeEditor(); + protected enum WatchTableColumns implements EnumeratedTableColumn { EXPRESSION("Expression", String.class, WatchRow::getExpression, WatchRow::setExpression), @@ -161,14 +164,18 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter SYMBOL("Symbol", Symbol.class, WatchRow::getSymbol), VALUE("Value", String.class, WatchRow::getRawValueString, WatchRow::setRawValueString, WatchRow::isRawValueEditable) { - private static final WatchValueCellRenderer RENDERER = new WatchValueCellRenderer(); @Override public GColumnRenderer getRenderer() { - return RENDERER; + return VALUE_RENDERER; + } + }, + TYPE("Type", DataType.class, WatchRow::getDataType, WatchRow::setDataType) { + @Override + public TableCellEditor getEditor() { + return TYPE_EDITOR; } }, - TYPE("Type", DataType.class, WatchRow::getDataType, WatchRow::setDataType), REPR("Repr", String.class, WatchRow::getValueString, WatchRow::setValueString, WatchRow::isValueEditable), ERROR("Error", String.class, WatchRow::getErrorMessage); @@ -224,11 +231,19 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter } } - protected static class WatchTableModel + protected class WatchTableModel extends DefaultEnumeratedColumnTableModel { public WatchTableModel(PluginTool tool) { super(tool, "Watches", WatchTableColumns.class); } + + ServiceProvider getServiceProvider() { + return serviceProvider; + } + + Trace getTrace() { + return currentTrace; + } } protected static void copySettings(Settings src, Settings dst, SettingsDefinition[] defs) { @@ -286,21 +301,26 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter } } - class WatchDataTypeEditor extends DataTypeTableCellEditor { - public WatchDataTypeEditor() { - super(plugin.getTool()); + static class WatchDataTypeEditor extends AbstractDataTypeTableCellEditor { + @Override + protected DataTypeManagerService getService(TableModel model) { + if (!(model instanceof WatchTableModel wModel)) { + return null; + } + return wModel.getServiceProvider().getService(DataTypeManagerService.class); } @Override - protected DataType resolveSelection(DataType dataType) { - if (dataType == null) { + protected DataType resolveSelection(DataType dataType, TableModel model) { + if (dataType == null || !(model instanceof WatchTableModel wModel)) { return null; } - if (currentTrace == null) { + Trace trace = wModel.getTrace(); + if (trace == null) { return dataType; } - try (Transaction tx = currentTrace.openTransaction("Resolve DataType")) { - return currentTrace.getDataTypeManager().resolve(dataType, null); + try (Transaction tx = trace.openTransaction("Resolve DataType")) { + return trace.getDataTypeManager().resolve(dataType, null); } } } @@ -465,10 +485,6 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter } } }); - - TableColumnModel columnModel = watchTable.getColumnModel(); - TableColumn typeCol = columnModel.getColumn(WatchTableColumns.TYPE.ordinal()); - typeCol.setCellEditor(new WatchDataTypeEditor()); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DefaultModuleMapProposal.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DefaultModuleMapProposal.java index 0f17a1da83..560b9be694 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DefaultModuleMapProposal.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DefaultModuleMapProposal.java @@ -29,9 +29,8 @@ import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.modules.TraceModule; import ghidra.util.MathUtilities; -public class DefaultModuleMapProposal - extends AbstractMapProposal - implements ModuleMapProposal { +public class DefaultModuleMapProposal extends + AbstractMapProposal implements ModuleMapProposal { protected static final int BLOCK_BITS = 12; protected static final int BLOCK_SIZE = 1 << BLOCK_BITS; protected static final long BLOCK_MASK = -1L << BLOCK_BITS; diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/CellEditorUtils.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/CellEditorUtils.java index 89892792b5..e40ae405f3 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/CellEditorUtils.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/CellEditorUtils.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. @@ -34,13 +34,4 @@ public enum CellEditorUtils { }; editorComponent.addFocusListener(l); } - - public static void installButton(JTable table, GTableFilterPanel filterPanel, - TableColumn column, Icon icon, int size, Consumer action) { - table.setRowHeight(size); - column.setMaxWidth(size); - column.setMinWidth(size); - column.setCellRenderer(new IconButtonTableCellRenderer(icon, size)); - column.setCellEditor(new IconButtonTableCellEditor<>(filterPanel, icon, action)); - } } 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 19bb424538..40c605f3bd 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,6 +18,8 @@ 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; @@ -119,7 +121,15 @@ public class DefaultEnumeratedColumnTableModel & EnumeratedTab } default public int getPreferredWidth() { - return -1; + return AbstractGTableModel.WIDTH_UNDEFINED; + } + + default public int getMinWidth() { + return AbstractGTableModel.WIDTH_UNDEFINED; + } + + default public int getMaxWidth() { + return AbstractGTableModel.WIDTH_UNDEFINED; } /** @@ -134,6 +144,10 @@ public class DefaultEnumeratedColumnTableModel & EnumeratedTab return null; } + default public TableCellEditor getEditor() { + return null; + } + default public SettingsDefinition[] getSettingsDefinitions() { return null; } @@ -211,11 +225,26 @@ public class DefaultEnumeratedColumnTableModel & EnumeratedTab 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(); diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/GTableAccess.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/GTableAccess.java new file mode 100644 index 0000000000..e13c301fe9 --- /dev/null +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/GTableAccess.java @@ -0,0 +1,39 @@ +/* ### + * 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.JTable; +import javax.swing.table.TableModel; + +/** + * Provides public access to the unwrapped table model of a {@link GTable} + */ +public interface GTableAccess { + /** + * {@return the unwrapped table model of the given table} + *

+ * If the given table is a plain {@link JTable}, then this merely delegates to + * {@link JTable#getModel()}. However, if the table happens to be a {@link GTable}, then this + * will invoke the otherwise-protected {@link GTable#getUnwrappedTableModel()}. This is both a + * utility and a means to access a protected method of {@link GTable}. + * + * @param table the table whose model to get + */ + default TableModel getUnwrappedModel(JTable table) { + return (table instanceof GTable gtable) ? gtable.getUnwrappedTableModel() + : table.getModel(); + } +} diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/IconButtonTableCellEditor.java b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/IconButtonTableCellEditor.java index 983f753c6e..7ec371b92a 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/IconButtonTableCellEditor.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/IconButtonTableCellEditor.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,28 +17,25 @@ package docking.widgets.table; import java.awt.Component; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.function.Consumer; import javax.swing.*; import javax.swing.table.TableCellEditor; +import javax.swing.table.TableModel; -public class IconButtonTableCellEditor extends AbstractCellEditor - implements TableCellEditor, ActionListener { +public abstract class IconButtonTableCellEditor extends AbstractCellEditor + implements TableCellEditor { protected static final String BLANK = ""; protected JButton button = new JButton(BLANK); - private final GTableFilterPanel filterPanel; - private final Consumer action; + private final Class cls; protected R row; + protected TableModel model; - public IconButtonTableCellEditor(GTableFilterPanel filterPanel, Icon icon, - Consumer action) { - this.filterPanel = filterPanel; + public IconButtonTableCellEditor(Class cls, Icon icon) { + this.cls = cls; button.setIcon(icon); - this.action = action; - button.addActionListener(this); + button.addActionListener(this::doClicked); } @Override @@ -50,14 +47,29 @@ public class IconButtonTableCellEditor extends AbstractCellEditor @SuppressWarnings("hiding") public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { - this.row = filterPanel.getRowObject(row); + if (!(table instanceof GTable gtable)) { + return null; + } + GTableFilterPanel filterPanel = gtable.getTableFilterPanel(); + if (filterPanel == null) { + // There has to be some other way to get a "row object", no? + return null; + } + Object rowObj = filterPanel.getRowObject(row); + if (!cls.isInstance(rowObj)) { + return null; + } + this.row = cls.cast(rowObj); button.setToolTipText(value.toString()); + this.model = gtable.getUnwrappedTableModel(); + return button; } - @Override - public void actionPerformed(ActionEvent e) { + private void doClicked(ActionEvent e) { fireEditingStopped(); - action.accept(row); + clicked(); } + + protected abstract void clicked(); } diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/base/widgets/table/DataTypeTableCellEditor.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/base/widgets/table/AbstractDataTypeTableCellEditor.java similarity index 80% rename from Ghidra/Debug/ProposedUtils/src/main/java/ghidra/base/widgets/table/DataTypeTableCellEditor.java rename to Ghidra/Debug/ProposedUtils/src/main/java/ghidra/base/widgets/table/AbstractDataTypeTableCellEditor.java index 49ff37113f..13a8ab6ad6 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/base/widgets/table/DataTypeTableCellEditor.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/base/widgets/table/AbstractDataTypeTableCellEditor.java @@ -23,22 +23,21 @@ import javax.swing.*; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.table.TableCellEditor; +import javax.swing.table.TableModel; import docking.widgets.DropDownSelectionTextField; -import docking.widgets.table.CellEditorUtils; -import docking.widgets.table.FocusableEditor; +import docking.widgets.table.*; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.datatype.DataTypeSelectionEditor; -import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; import ghidra.util.Swing; import ghidra.util.data.DataTypeParser.AllowedDataTypes; -public class DataTypeTableCellEditor extends AbstractCellEditor - implements TableCellEditor, FocusableEditor { - private final PluginTool tool; +public abstract class AbstractDataTypeTableCellEditor extends AbstractCellEditor + implements TableCellEditor, FocusableEditor, GTableAccess { + private DataTypeManagerService service; - private JTable table; + protected JTable table; private JPanel editorPanel; private DataTypeSelectionEditor editor; @@ -71,26 +70,7 @@ public class DataTypeTableCellEditor extends AbstractCellEditor dataTypeChooserButton.addActionListener(e -> Swing.runLater(() -> stopEdit())); } - protected DataTypeTableCellEditor(PluginTool tool, DataTypeManagerService service) { - this.tool = tool; - this.service = service; - } - - public DataTypeTableCellEditor(DataTypeManagerService service) { - this(null, service); - } - - public DataTypeTableCellEditor(PluginTool tool) { - // NOTE: Service will be updated on request - this(tool, null); - } - - private DataTypeManagerService updateService() { - if (tool != null) { - service = tool.getService(DataTypeManagerService.class); - } - return service; - } + protected abstract DataTypeManagerService getService(TableModel model); protected AllowedDataTypes getAllowed(int row, int column) { return AllowedDataTypes.ALL; @@ -104,9 +84,13 @@ public class DataTypeTableCellEditor extends AbstractCellEditor public Component getTableCellEditorComponent(JTable newTable, Object value, boolean isSelected, int row, int column) { this.table = newTable; + this.service = getService(getUnwrappedModel(newTable)); + if (service == null) { + return null; + } init(row, column); - // TODO: Use this to verify lengths if variable-length is to be permitted. + // LATER: Use this to verify lengths if variable-length is to be permitted. /*DataTypeInstance dti = (DataTypeInstance) value; if (dti != null) { dt = dti.getDataType(); @@ -127,7 +111,6 @@ public class DataTypeTableCellEditor extends AbstractCellEditor } protected void init(int row, int column) { - updateService(); editor = new DataTypeSelectionEditor(getPreferredDataTypeManager(row, column), service, getAllowed(row, column)); editor.setTabCommitsEdit(true); @@ -144,8 +127,7 @@ public class DataTypeTableCellEditor extends AbstractCellEditor } protected void stopEdit() { - updateService(); - DataType dataType = service.getDataType((String) null); // Why? + DataType dataType = service.promptForDataType((String) null); if (dataType != null) { editor.setCellEditorValue(dataType); editor.stopCellEditing(); @@ -160,11 +142,11 @@ public class DataTypeTableCellEditor extends AbstractCellEditor return dt; } - protected boolean validateSelection(DataType dataType) { + protected boolean validateSelection(DataType dataType, TableModel model) { return true; } - protected DataType resolveSelection(DataType dataType) { + protected DataType resolveSelection(DataType dataType, TableModel model) { return dataType; } @@ -187,8 +169,10 @@ public class DataTypeTableCellEditor extends AbstractCellEditor catch (InvalidDataTypeException e) { return false; } - DataType dataType = resolveSelection(editor.getCellEditorValueAsDataType()); - if (!isEmptyEditorCell() && !validateSelection(dataType)) { + + TableModel model = getUnwrappedModel(table); + DataType dataType = resolveSelection(editor.getCellEditorValueAsDataType(), model); + if (!isEmptyEditorCell() && !validateSelection(dataType, model)) { return false; } if (dataType != null) { 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 3858b19e95..10473d0c04 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 @@ -23,16 +23,13 @@ import java.util.stream.Stream; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; import docking.widgets.table.DefaultEnumeratedColumnTableModel; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import docking.widgets.table.GTable; import ghidra.framework.plugintool.PluginTool; -import ghidra.pcode.emu.symz3.SymZ3RecordsExecution.RecInstruction; -import ghidra.symz3.gui.Z3SummaryInstructionLogPanel.InstructionHtmlFormatter; import ghidra.util.table.GhidraTableFilterPanel; +import ghidra.util.table.column.GColumnRenderer; public class Z3SummaryInformationPanel extends JPanel { @@ -51,32 +48,60 @@ public class Z3SummaryInformationPanel extends JPanel { } } - record InformationRow(InfoKind kind, String variable, String value) { + record InformationRow(InfoKind kind, String variable, String value) {} - } + private static final MonospaceCellRenderer MONO_RENDERER = new MonospaceCellRenderer(); protected enum InformationTableColumns implements EnumeratedTableColumn { - KIND("Kind", InfoKind.class, InformationRow::kind, true), - VARIABLE("Variable", String.class, InformationRow::variable, true), - VALUE("Value", String.class, InformationRow::value, true); + KIND("Kind", InfoKind.class, InformationRow::kind) { + @Override + public int getMaxWidth() { + return 40; + } + + @Override + public int getMinWidth() { + return 40; + } + }, + VARIABLE("Variable", String.class, InformationRow::variable) { + @Override + public GColumnRenderer getRenderer() { + return MONO_RENDERER; + } + + @Override + public int getPreferredWidth() { + return 20; + } + }, + VALUE("Value", String.class, InformationRow::value) { + @Override + public GColumnRenderer getRenderer() { + return MONO_RENDERER; + } + + @Override + public int getPreferredWidth() { + return 60; + } + }; private final String header; private final Class cls; private final Function getter; - private final boolean visible; InformationTableColumns(String header, Class cls, - Function getter, boolean visible) { + Function getter) { this.header = header; this.cls = cls; this.getter = getter; - this.visible = visible; } - static String getInstructionHtml(RecInstruction op) { - InstructionHtmlFormatter formatter = new InstructionHtmlFormatter(); - return formatter.formatInstruction(op.instruction()); + @Override + public String getHeader() { + return header; } @Override @@ -88,16 +113,6 @@ public class Z3SummaryInformationPanel extends JPanel { public Object getValueOf(InformationRow row) { return getter.apply(row); } - - @Override - public String getHeader() { - return header; - } - - @Override - public boolean isVisible() { - return visible; - } } protected static class InformationTableModel extends @@ -125,17 +140,6 @@ public class Z3SummaryInformationPanel extends JPanel { filterPanel = new GhidraTableFilterPanel<>(table, model); add(filterPanel, BorderLayout.SOUTH); - - TableColumnModel columnModel = table.getColumnModel(); - TableColumn kindCol = columnModel.getColumn(InformationTableColumns.KIND.ordinal()); - kindCol.setMaxWidth(40); - kindCol.setMinWidth(40); - TableColumn varCol = columnModel.getColumn(InformationTableColumns.VARIABLE.ordinal()); - varCol.setCellRenderer(new MonospaceCellRenderer()); - varCol.setPreferredWidth(20); - TableColumn valCol = columnModel.getColumn(InformationTableColumns.VALUE.ordinal()); - valCol.setCellRenderer(new MonospaceCellRenderer()); - valCol.setPreferredWidth(60); } public void setInformation(Stream> valuations, 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 34acf959b5..54600ec717 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 @@ -22,8 +22,6 @@ import java.util.List; import java.util.function.Function; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; import docking.widgets.table.DefaultEnumeratedColumnProgramTableModel; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; @@ -39,6 +37,7 @@ import ghidra.program.model.symbol.Equate; import ghidra.util.HTMLUtilities; import ghidra.util.WebColors; import ghidra.util.table.GhidraTableFilterPanel; +import ghidra.util.table.column.GColumnRenderer; public class Z3SummaryInstructionLogPanel extends JPanel { private static final Color COLOR_FOREGROUND_ADDRESS = new GColor("color.fg.listing.address"); @@ -51,6 +50,9 @@ public class Z3SummaryInstructionLogPanel extends JPanel { private static final Color COLOR_FOREGROUND_VARIABLE = new GColor("color.fg.listing.function.variable"); + private static final MonospaceCellRenderer MONO_RENDERER = new MonospaceCellRenderer(); + private static final HtmlCellRenderer HTML_RENDERER = new HtmlCellRenderer(); + protected static String htmlColor(Color color, String display) { return String.format("%s", WebColors.toString(color, false), HTMLUtilities.escapeHTML(display)); @@ -58,22 +60,60 @@ public class Z3SummaryInstructionLogPanel extends JPanel { protected enum InstructionLogTableColumns implements EnumeratedTableColumn { - INDEX("Index", Integer.class, RecInstruction::index, true), - THREAD("Thread", String.class, RecInstruction::getThreadName, true), - ADDRESS("Address", Address.class, RecInstruction::getAddress, true), - CODE("Instruction", String.class, InstructionLogTableColumns::getInstructionHtml, true),; + INDEX("Index", Integer.class, RecInstruction::index) { + @Override + public int getMaxWidth() { + return 30; + } + + @Override + public int getMinWidth() { + return 30; + } + }, + THREAD("Thread", String.class, RecInstruction::getThreadName) { + @Override + public int getMaxWidth() { + return 30; + } + + @Override + public int getMinWidth() { + return 30; + } + }, + ADDRESS("Address", Address.class, RecInstruction::getAddress) { + @Override + public GColumnRenderer getRenderer() { + return MONO_RENDERER; + } + + @Override + public int getPreferredWidth() { + return 20; + } + }, + CODE("Instruction", String.class, InstructionLogTableColumns::getInstructionHtml) { + @Override + public GColumnRenderer getRenderer() { + return HTML_RENDERER; + } + + @Override + public int getPreferredWidth() { + return 40; + } + }; private final String header; private final Class cls; private final Function getter; - private final boolean visible; InstructionLogTableColumns(String header, Class cls, - Function getter, boolean visible) { + Function getter) { this.header = header; this.cls = cls; this.getter = getter; - this.visible = visible; } static String getInstructionHtml(RecInstruction op) { @@ -81,6 +121,11 @@ public class Z3SummaryInstructionLogPanel extends JPanel { return formatter.formatInstruction(op.instruction()); } + @Override + public String getHeader() { + return header; + } + @Override public Class getValueClass() { return cls; @@ -90,16 +135,6 @@ public class Z3SummaryInstructionLogPanel extends JPanel { public Object getValueOf(RecInstruction row) { return getter.apply(row); } - - @Override - public String getHeader() { - return header; - } - - @Override - public boolean isVisible() { - return visible; - } } protected static class InstructionLogTableModel extends @@ -280,20 +315,6 @@ public class Z3SummaryInstructionLogPanel extends JPanel { filterPanel = new GhidraTableFilterPanel<>(table, model); add(filterPanel, BorderLayout.SOUTH); - TableColumnModel columnModel = table.getColumnModel(); - TableColumn indexCol = columnModel.getColumn(InstructionLogTableColumns.INDEX.ordinal()); - indexCol.setMaxWidth(30); - indexCol.setMinWidth(30); - TableColumn threadCol = columnModel.getColumn(InstructionLogTableColumns.THREAD.ordinal()); - threadCol.setMaxWidth(30); - threadCol.setMinWidth(30); - TableColumn addrCol = columnModel.getColumn(InstructionLogTableColumns.ADDRESS.ordinal()); - addrCol.setCellRenderer(new MonospaceCellRenderer()); - addrCol.setPreferredWidth(20); - TableColumn codeCol = columnModel.getColumn(InstructionLogTableColumns.CODE.ordinal()); - codeCol.setCellRenderer(new HtmlCellRenderer()); - codeCol.setPreferredWidth(40); - table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); table.addMouseListener(new MouseAdapter() { @Override 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 9d4f84ac53..bb3056d4c9 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 @@ -22,8 +22,6 @@ import java.util.List; import java.util.function.Function; import javax.swing.*; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; import docking.widgets.table.DefaultEnumeratedColumnProgramTableModel; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; @@ -44,6 +42,7 @@ import ghidra.program.model.pcode.PcodeOp; import ghidra.util.HTMLUtilities; import ghidra.util.WebColors; import ghidra.util.table.GhidraTableFilterPanel; +import ghidra.util.table.column.GColumnRenderer; public class Z3SummaryPcodeLogPanel extends JPanel { private static final Color COLOR_FOREGROUND_ADDRESS = new GColor("color.fg.listing.address"); @@ -63,6 +62,9 @@ public class Z3SummaryPcodeLogPanel extends JPanel { private static final Color COLOR_FOREGROUND_USEROP = new GColor("color.fg.listing.pcode.userop"); + private static final MonospaceCellRenderer MONO_RENDERER = new MonospaceCellRenderer(); + private static final HtmlCellRenderer HTML_RENDERER = new HtmlCellRenderer(); + protected static String htmlColor(Color color, String display) { return String.format("%s", WebColors.toString(color, false), HTMLUtilities.escapeHTML(display)); @@ -70,11 +72,50 @@ public class Z3SummaryPcodeLogPanel extends JPanel { protected enum PcodeLogTableColumns implements EnumeratedTableColumn { - INDEX("Index", Integer.class, RecOp::index, true), - THREAD("Thread", String.class, RecOp::getThreadName, true), - ADDRESS("Address", Address.class, RecOp::getAddress, false), - CODE("P-code", String.class, PcodeLogTableColumns::getPcodeHtml, true), - ; + INDEX("Index", Integer.class, RecOp::index, true) { + @Override + public int getMaxWidth() { + return 30; + } + + @Override + public int getMinWidth() { + return 30; + } + }, + THREAD("Thread", String.class, RecOp::getThreadName, true) { + @Override + public int getMaxWidth() { + return 30; + } + + @Override + public int getMinWidth() { + return 30; + } + }, + ADDRESS("Address", Address.class, RecOp::getAddress, false) { + @Override + public GColumnRenderer getRenderer() { + return MONO_RENDERER; + } + + @Override + public int getPreferredWidth() { + return 20; + } + }, + CODE("P-code", String.class, PcodeLogTableColumns::getPcodeHtml, true) { + @Override + public GColumnRenderer getRenderer() { + return HTML_RENDERER; + } + + @Override + public int getPreferredWidth() { + return 40; + } + }; private final String header; private final Class cls; @@ -94,6 +135,11 @@ public class Z3SummaryPcodeLogPanel extends JPanel { return formatter.formatOp(op.op()); } + @Override + public String getHeader() { + return header; + } + @Override public Class getValueClass() { return cls; @@ -104,11 +150,6 @@ public class Z3SummaryPcodeLogPanel extends JPanel { return getter.apply(row); } - @Override - public String getHeader() { - return header; - } - @Override public boolean isVisible() { return visible; @@ -276,20 +317,6 @@ public class Z3SummaryPcodeLogPanel extends JPanel { filterPanel = new GhidraTableFilterPanel<>(table, model); add(filterPanel, BorderLayout.SOUTH); - TableColumnModel columnModel = table.getColumnModel(); - TableColumn indexCol = columnModel.getColumn(PcodeLogTableColumns.INDEX.ordinal()); - indexCol.setMaxWidth(30); - indexCol.setMinWidth(30); - TableColumn threadCol = columnModel.getColumn(PcodeLogTableColumns.THREAD.ordinal()); - threadCol.setMaxWidth(30); - threadCol.setMinWidth(30); - TableColumn addrCol = columnModel.getColumn(PcodeLogTableColumns.ADDRESS.ordinal()); - addrCol.setCellRenderer(new MonospaceCellRenderer()); - addrCol.setPreferredWidth(20); - TableColumn codeCol = columnModel.getColumn(PcodeLogTableColumns.CODE.ordinal()); - codeCol.setCellRenderer(new HtmlCellRenderer()); - codeCol.setPreferredWidth(40); - table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); table.addMouseListener(new MouseAdapter() { @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractDynamicTableColumn.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractDynamicTableColumn.java index e3db148d4c..ea10504a97 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractDynamicTableColumn.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractDynamicTableColumn.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,6 +17,8 @@ package docking.widgets.table; import java.util.*; +import javax.swing.table.TableCellEditor; + import ghidra.docking.settings.*; import ghidra.framework.plugintool.ServiceProvider; import ghidra.util.NumericUtilities; @@ -74,7 +76,17 @@ public abstract class AbstractDynamicTableColumn getComparator() { @@ -118,6 +130,11 @@ public abstract class AbstractDynamicTableColumn extends AbstractTableModel return WIDTH_UNDEFINED; } + public int getMaxColumnWidth(int columnIndex) { + return WIDTH_UNDEFINED; + } + + public int getMinColumnWidth(int columnIndex) { + return WIDTH_UNDEFINED; + } + /** * The default implementation of {@link TableModel#getValueAt(int, int)} that calls the * abstract {@link #getColumnValueForRow(Object, int)}. diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/ConfigurableColumnTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/ConfigurableColumnTableModel.java index 204a3ed2b0..05d166709a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/ConfigurableColumnTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/ConfigurableColumnTableModel.java @@ -15,62 +15,74 @@ */ package docking.widgets.table; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableModel; +import javax.swing.table.*; import ghidra.docking.settings.Settings; import ghidra.docking.settings.SettingsDefinition; /** - * A model that provides access to table columns that are "configurable," whether by way of - * {@link Settings} object, or by the implementations and how they were written (like supplying + * A model that provides access to table columns that are "configurable," whether by way of + * {@link Settings} object, or by the implementations and how they were written (like supplying * custom renderers and such). */ public interface ConfigurableColumnTableModel extends TableModel { /** - * Returns settings for the specified column index + * {@return settings for the specified column index} + * * @param index column index - * @return column settings. */ public Settings getColumnSettings(int index); /** - * Returns settings definitions for the specified column index + * {@return settings definitions for the specified column index} + * * @param index column index - * @return column settings definitions. */ public SettingsDefinition[] getColumnSettingsDefinitions(int index); /** - * Allows for the bulk setting of Settings. This prevents excessive event - * notification when all settings need to be changed. + * Allows for the bulk setting of Settings. + *

+ * This prevents excessive event notification when all settings need to be changed. * - * @param settings An array of Settings that contains Settings for each column - * where the index of the Settings in the array is the index of the column - * in the model + * @param settings An array of Settings that contains Settings for each column where the index + * of the Settings in the array is the index of the column in the model */ public void setAllColumnSettings(Settings[] settings); /** - * Gets the maximum number of text display lines needed for any given cell within the - * specified column + * {@return the maximum number of text display lines needed for any given cell within the + * specified column} + * * @param index column field index - * @return maximum number of lines needed for specified column */ public int getMaxLines(int index); /** - * Returns the table cell renderer for the given column + * {@return the table cell renderer for the given column} + *

+ * A null value indicates this column uses the default cell renderer. + * * @param columnIndex the index of the column - * @return the renderer */ public TableCellRenderer getRenderer(int columnIndex); /** - * Returns the header cell renderer for the given column + * {@return the table cell editor for the given column} + *

+ * A null value indicates this column uses the default cell editor. + * + * @param columnIndex the index of the column + */ + public TableCellEditor getEditor(int columnIndex); + + /** + * {@return the header cell renderer for the given column} + *

+ * A null value indicates this column uses the default header renderer. + * * @param columnIndex the index of the column - * @return the renderer */ public TableCellRenderer getHeaderRenderer(int columnIndex); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/DynamicTableColumn.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/DynamicTableColumn.java index b98876d8cc..5c6b16fb8b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/DynamicTableColumn.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/DynamicTableColumn.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. @@ -18,6 +18,8 @@ package docking.widgets.table; import java.util.Comparator; import java.util.Date; +import javax.swing.table.TableCellEditor; + import ghidra.docking.settings.Settings; import ghidra.docking.settings.SettingsDefinition; import ghidra.framework.plugintool.ServiceProvider; @@ -35,8 +37,9 @@ import ghidra.util.table.column.GColumnRenderer; public interface DynamicTableColumn { /** - * Determines the unique column heading that may be used to identify a column instance. This - * name must be non-changing and is used to save/restore state information. + * Determines the unique column heading that may be used to identify a column instance. + *

+ * This name must be non-changing and is used to save/restore state information. * * @return the field instance name. */ @@ -50,19 +53,29 @@ public interface DynamicTableColumn { public Class getColumnClass(); /** - * Returns the preferred width for this column. Column should either return a valid positive - * preferred size or -1. - * - * @return the preferred width for this column. + * {@return the preferred width for this column} + *

+ * Column should either return a valid positive preferred size or -1 */ public int getColumnPreferredWidth(); /** - * Returns the single class type of the data that this table field can use to generate columnar - * data. - * - * @return the single class type of the data that this table field can use to generate columnar - * data. + * {@return the maximum width for this column} + *

+ * Column should either return a valid positive maximum size or -1 + */ + public int getColumnMaxWidth(); + + /** + * {@return the minimum width for this column} + *

+ * Column should either return a valid positive minimum size or -1 + */ + public int getColumnMinWidth(); + + /** + * {@return the single class type of the data that this table field can use to generate columnar + * data.} */ public Class getSupportedRowType(); @@ -81,45 +94,40 @@ public interface DynamicTableColumn { ServiceProvider serviceProvider) throws IllegalArgumentException; /** - * Returns the optional cell renderer for this column; null if no renderer is used. - * + * {@return the optional cell renderer for this column; null if no renderer is used} *

* This method allows columns to define custom rendering. The interface returned here ensures * that the text used for filtering matches what the users sees (via the * {@link GColumnRenderer#getFilterString(Object, Settings)} method). - * *

* Note: some types should not make use of the aforementioned filter string. These types include * the {@link Number} wrapper types, {@link Date} and {@link Enum}s. (This is because the * filtering system works naturally with these types.) See {@link GColumnRenderer}. - * - * @return the renderer */ public GColumnRenderer getColumnRenderer(); /** - * Returns the optional header renderer for this column; null if no renderer is used. - * + * {@return the optional cell editor for this column; null if no custom editor is used} + */ + public TableCellEditor getColumnEditor(); + + /** + * {@return the optional header renderer for this column; null if no renderer is used} *

* This method allows columns to define custom header rendering. - * - * @return the renderer */ public GTableHeaderRenderer getHeaderRenderer(); /** - * Returns a list of settings definitions for this field. - * - * @return list of settings definitions for this field. + * {@return a list of settings definitions for this field} */ public SettingsDefinition[] getSettingsDefinitions(); /** - * Gets the maximum number of text display lines needed for any given cell with the specified - * settings. + * {@return the maximum number of text display lines needed for any given cell with the + * specified settings} * * @param settings field settings - * @return maximum number of lines needed */ public int getMaxLines(Settings settings); @@ -132,24 +140,23 @@ public interface DynamicTableColumn { public String getColumnDisplayName(Settings settings); /** - * Returns a description of this column. This may be used as a tooltip for the column header - * - * @return a description of this column. This may be used as a tooltip for the column header. + * {@return a description of this column. This may be used as a tooltip for the column header} */ public String getColumnDescription(); /** - * Returns a value that is unique for this table column. This is different than getting the - * display name, which may be shared by different columns. - * - * @return the identifier + * {@return a value that is unique for this table column} + *

+ * This is different than getting the display name, which may be shared by different columns. */ public String getUniqueIdentifier(); /** * If implemented, will return a comparator that knows how to sort values for this column. - * Implementors should return null if they do not wish to provider a comparator + * Implementors should return {@code null} if they do not wish to provider a comparator * + * @param model the table model + * @param columnIndex the model column index * @return the comparator */ public Comparator getComparator(DynamicColumnTableModel model, diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GDynamicColumnTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GDynamicColumnTableModel.java index c852424007..5e995b235f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GDynamicColumnTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GDynamicColumnTableModel.java @@ -20,6 +20,7 @@ import java.util.stream.Collectors; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import docking.widgets.table.sort.*; @@ -125,9 +126,10 @@ public abstract class GDynamicColumnTableModel /** * Allows clients to defer column creation until after this parent class's constructor has been - * called. This method will not restore any column settings that have been changed after - * construction. Thus, this method is intended only to be called during the construction - * process. + * called. + *

+ * This method will not restore any column settings that have been changed after construction. + * Thus, this method is intended only to be called during the construction process. */ protected void reloadColumns() { @@ -158,13 +160,9 @@ public abstract class GDynamicColumnTableModel // note: we may have multiple columns with the same class. It is not the normal case, // but it can happen for re-usable column classes. - //@formatter:off - List> matching = - tableColumns.stream() - .filter(c -> isColumnClassMatch(c, clazz)) - .collect(Collectors.toList()) - ; - //@formatter:on + List> matching = tableColumns.stream() + .filter(c -> isColumnClassMatch(c, clazz)) + .collect(Collectors.toList()); if (matching.size() > 1) { Msg.warn(this, "More than one column found matching class '" + clazz + "'"); @@ -179,8 +177,7 @@ public abstract class GDynamicColumnTableModel return true; } - if (column instanceof MappedTableColumn) { - MappedTableColumn mappedColumn = (MappedTableColumn) column; + if (column instanceof MappedTableColumn mappedColumn) { Class columnClass = mappedColumn.getMappedColumnClass(); if (clazz.equals(columnClass)) { return true; @@ -209,7 +206,6 @@ public abstract class GDynamicColumnTableModel * @param columnIndex the column index * @return a comparator for the specific column values */ - @SuppressWarnings("unchecked") // the column provides the values itself; safe cast protected Comparator createSortComparatorForColumn(int columnIndex) { if (columnIndex < 0 || columnIndex >= tableColumns.size()) { // We have seen this sporadically. Assume for now there is some sort of timing issue. @@ -219,6 +215,7 @@ public abstract class GDynamicColumnTableModel return null; } DynamicTableColumn column = getColumn(columnIndex); + @SuppressWarnings("unchecked") // the column provides the values itself; safe cast Comparator comparator = (Comparator) column.getComparator(this, columnIndex); return comparator; @@ -261,10 +258,10 @@ public abstract class GDynamicColumnTableModel } /** - * Adds the given column at the end of the list of columns. This method is intended for - * implementations to add custom column objects, rather than relying on generic, discovered - * DynamicTableColumn implementations. - * + * Adds the given column at the end of the list of columns. + *

+ * This method is intended for implementations to add custom column objects, rather than relying + * on generic, discovered DynamicTableColumn implementations. *

* Note: this method assumes that the columns have already been sorted * @@ -275,10 +272,10 @@ public abstract class GDynamicColumnTableModel } /** - * Adds the given columns to the end of the list of columns. This method is intended for - * implementations to add custom column objects, rather than relying on generic, discovered - * DynamicTableColumn implementations. - * + * Adds the given columns to the end of the list of columns. + *

+ * This method is intended for implementations to add custom column objects, rather than relying + * on generic, discovered DynamicTableColumn implementations. *

* Note: this method assumes that the columns have already been sorted. * @@ -289,10 +286,10 @@ public abstract class GDynamicColumnTableModel } /** - * Adds the given columns to the end of the list of columns. This method is intended for - * implementations to add custom column objects, rather than relying on generic, discovered - * DynamicTableColumn implementations. - * + * Adds the given columns to the end of the list of columns. + *

+ * This method is intended for implementations to add custom column objects, rather than relying + * on generic, discovered DynamicTableColumn implementations. *

* Note: this method assumes that the columns have already been sorted. * @@ -308,9 +305,10 @@ public abstract class GDynamicColumnTableModel } /** - * Adds the given field at the given index to the list of fields in this class. This method is - * intended for implementations to add custom column objects, rather than relying on generic, - * discovered DynamicTableColumn implementations. + * Adds the given field at the given index to the list of fields in this class. + *

+ * This method is intended for implementations to add custom column objects, rather than relying + * on generic, discovered DynamicTableColumn implementations. *

* Note: this method assumes that the columns have already been sorted. * @@ -367,8 +365,10 @@ public abstract class GDynamicColumnTableModel } /** - * Removes the given columns from this model. This method allows the client to remove multiple - * columns at once, firing only one event when the work is finished. + * Removes the given columns from this model. + *

+ * This method allows the client to remove multiple columns at once, firing only one event when + * the work is finished. * * @param columns the columns to remove */ @@ -403,13 +403,6 @@ public abstract class GDynamicColumnTableModel return defaultColumns.contains(column); } - /** - * Returns true if the column indicated by the index in the model is a default column (meaning - * that it was specified by the model and not discovered). - * - * @param modelIndex the index of the column in the model. - * @return true if the column is a default. - */ @Override public boolean isDefaultColumn(int modelIndex) { if (modelIndex < 0 || modelIndex >= tableColumns.size()) { @@ -463,6 +456,30 @@ public abstract class GDynamicColumnTableModel return tableColumns.get(column).getColumnPreferredWidth(); } + @Override + public int getMaxColumnWidth(int column) { + if (column < 0 || column >= tableColumns.size()) { + + // hacky: this can happen when we are in the process of rebuilding our column structure, + // where the client calling us has an old index value (such as when we are + // adding/removing columns). + return -1; // default + } + return tableColumns.get(column).getColumnMaxWidth(); + } + + @Override + public int getMinColumnWidth(int column) { + if (column < 0 || column >= tableColumns.size()) { + + // hacky: this can happen when we are in the process of rebuilding our column structure, + // where the client calling us has an old index value (such as when we are + // adding/removing columns). + return -1; // default + } + return tableColumns.get(column).getColumnMinWidth(); + } + @Override public String getColumnDisplayName(int columnIndex) { DynamicTableColumn column = tableColumns.get(columnIndex); @@ -485,35 +502,32 @@ public abstract class GDynamicColumnTableModel return null; } + if (t == null) { + // sometimes happens if we are painting while being disposed + return null; + } + DATA_SOURCE dataSource = getDataSource(); @SuppressWarnings("unchecked") - // Note: We are casting now, as in practice the type should never be different that + // Note: We are casting now, as in practice the type should never be different than // the declared type. We want to remove entirely the 'dataSource' value and then // the templating will be simpler. DynamicTableColumn column = (DynamicTableColumn) tableColumns.get(columnIndex); - if (t == null) { - // sometimes happen if we are painting while being disposed - return null; - } - return column.getValue(t, columnSettings.get(column), dataSource, serviceProvider); } /** - * Returns the table's context for the data. - * - * @return the table's context for the data. + * {@return the table's context for the data} */ public abstract DATA_SOURCE getDataSource(); /** - * Returns the column index of the given column class + * {@return the column index of the given column class, or -1 if not found} * * @param columnClass the class for the type of DynamicTableColumn you want to find. - * @return the column index for the specified DynamicTableColumn. -1 if not found. */ public int getColumnIndex(Class columnClass) { DynamicTableColumn column = @@ -568,43 +582,24 @@ public abstract class GDynamicColumnTableModel stateChanged(new ChangeEvent(this)); } - /** - * Gets the special table cell renderer for the specified table field column. A null value - * indicates that this field uses a default cell renderer. - * - * @param index the model column index - * @return a table cell renderer for this field. Otherwise, null if a default renderer should be - * used. - */ @Override public TableCellRenderer getRenderer(int index) { return tableColumns.get(index).getColumnRenderer(); } - /** - * Gets the special header cell renderer for the specified table field column. A null value - * indicates that this column uses a default header renderer. - * - * @param index the model column index - * @return a table cell renderer for this field's header. Otherwise, null if a default renderer - * should be used. - */ + @Override + public TableCellEditor getEditor(int index) { + return tableColumns.get(index).getColumnEditor(); + } + @Override public TableCellRenderer getHeaderRenderer(int index) { return tableColumns.get(index).getHeaderRenderer(); } - /** - * Gets the maximum number of text display lines needed for any given cell within the specified - * column. - * - * @param index column field index - * @return maximum number of lines needed for specified column - */ @Override public int getMaxLines(int index) { if (index < 0 || index >= tableColumns.size()) { - // hacky: this can happen when we are in the process of rebuilding our column structure, // where the client calling us has an old index value (such as when we are // adding/removing columns). diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java index bec36dcdb4..79d850ce8f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java @@ -15,10 +15,10 @@ */ package docking.widgets.table; -import static docking.DockingUtils.*; -import static docking.action.MenuData.*; -import static java.awt.event.InputEvent.*; -import static javax.swing.ListSelectionModel.*; +import static docking.DockingUtils.CONTROL_KEY_MODIFIER_MASK; +import static docking.action.MenuData.NO_MNEMONIC; +import static java.awt.event.InputEvent.SHIFT_DOWN_MASK; +import static javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION; import java.awt.*; import java.awt.event.*; @@ -50,29 +50,27 @@ import ghidra.util.exception.AssertException; import resources.Icons; /** - * A sub-class of JTable that provides navigation and auto-lookup. - * By default, both of these features are disabled. + * A sub-class of JTable that provides navigation and auto-lookup. By default, both of + * these features are disabled. *

- * Auto-lookup is only supported on one column and must be specified - * using the setAutoLookupColumn() method. + * Auto-lookup is only supported on one column and must be specified using the + * setAutoLookupColumn() method. *

- * Auto-lookup allows a user to begin typing the first few letters - * of a desired row. The table will attempt to locate the first row - * that contains the letters typed up to that point. There is an - * 800ms timeout between typed letters, at which point the list of - * typed letters will be flushed. + * Auto-lookup allows a user to begin typing the first few letters of a desired row. The table will + * attempt to locate the first row that contains the letters typed up to that point. There is an + * 800ms timeout between typed letters, at which point the list of typed letters will be flushed. *

* Auto-lookup is much faster if the underlying table model implements - * SortedTableModel, because a binary search can used - * to locate the desired row. A linear search is used if the model is not sorted. + * SortedTableModel, because a binary search can used to locate the desired row. A + * linear search is used if the model is not sorted. *

* Other features provided: *

    - *
  • Column hiding/showing
  • - *
  • Multi-column sorting
  • - *
  • Column settings
  • - *
  • Column state saving (visibility, size, positioning, sort values)
  • - *
  • Selection management (saving/restoring selection when used with a filter panel)
  • + *
  • Column hiding/showing
  • + *
  • Multi-column sorting
  • + *
  • Column settings
  • + *
  • Column state saving (visibility, size, positioning, sort values)
  • + *
  • Selection management (saving/restoring selection when used with a filter panel)
  • *
* * @see GTableFilterPanel @@ -143,6 +141,7 @@ public class GTable extends JTable { /** * Constructs a new GTable using the specified table model. + * * @param dm the table model */ public GTable(TableModel dm) { @@ -179,8 +178,9 @@ public class GTable extends JTable { } /** - * Selects the given row. This is a convenience method for + * Selects the given row. This is a convenience method for * {@link #setRowSelectionInterval(int, int)}. + * * @param row The row to select */ public void selectRow(int row) { @@ -188,10 +188,11 @@ public class GTable extends JTable { } /** - * Selects the row under the given mouse point. This method is useful when the user - * triggers a popup mouse action and you would like to have the table select that row if it - * is not already selected. This allows you to guarantee that there is always a selection - * when the user triggers a popup menu. + * Selects the row under the given mouse point. + *

+ * This method is useful when the user triggers a popup mouse action and you would like to have + * the table select that row if it is not already selected. This allows you to guarantee that + * there is always a selection when the user triggers a popup menu. * * @param event The event that triggered the popup menu * @return true if the row is selected or was already selected. @@ -217,6 +218,7 @@ public class GTable extends JTable { /** * Allows subclasses to change the type of {@link AutoLookup} created by this table + * * @return the auto lookup */ protected AutoLookup createAutoLookup() { @@ -231,8 +233,12 @@ public class GTable extends JTable { initializeHeader(header); } + /** + * {@inheritDoc} + * + * @implNote overridden to cleanup our SelectionManager + */ @Override - // overridden to cleanup our SelectionManager public void setSelectionModel(ListSelectionModel newModel) { if (selectionManager != null) { selectionManager.dispose(); @@ -242,8 +248,12 @@ public class GTable extends JTable { super.setSelectionModel(newModel); } + /** + * {@inheritDoc} + * + * @implNote overridden to install our SelectionManager + */ @Override - // overridden to install our SelectionManager public void setModel(TableModel dataModel) { // we are going to create a new selection model, save off the old selectionMode and // restore it at the end. @@ -269,35 +279,39 @@ public class GTable extends JTable { return null; } + /** + * @param the type of the row object + * @return the model + * @implNoteThe The cast to {@code RowObjectTableModel} is safe, since we are create a new + * {@link SelectionManager} of an arbitrary type T defined here. So, T doesn't + * really exist and therefore the cast isn't really casting to anything. The + * {@link SelectionManager} will take on the type of the given model. The T is just + * there on the {@link SelectionManager} to make its internal methods consistent. + */ @SuppressWarnings("unchecked") - // The (RowObjectTableModel) is safe, since we are create a new SelectionManager of - // an arbitrary type T defined here. So, T doesn't really exist and therefore the cast isn't - // really casting to anything. The SelectionManager will take on the type of the given model. - // The T is just there on the SelectionManager to make its internal methods consistent. private RowObjectTableModel getRowObjectTableModel() { - TableModel model = getModel(); - if (model instanceof RowObjectTableModel) { - return (RowObjectTableModel) model; + if (getModel() instanceof RowObjectTableModel model) { + return model; } return null; } /** - * Returns the {@link SelectionManager} in use by this GTable. null is returned - * if the user has installed their own {@link ListSelectionModel}. - * - * @return the selection manager + * {@return the {@link SelectionManager} in use by this {@code GTable}, or {@code null} if the + * user has installed their own {@link ListSelectionModel}}. */ public SelectionManager getSelectionManager() { return selectionManager; } /** - * A method that allows clients to signal to this GTable and its internals that the table - * model has changed. Usually, {@link #tableChanged(TableModelEvent)} is called, but clients - * alter the table, but do not do so through the model. In this case, they need a way to - * signal to the table that the model has been updated. + * A method that allows clients to signal to this {@code GTable} and its internals that the + * table model has changed. + *

+ * Usually, {@link #tableChanged(TableModelEvent)} is called, but clients alter the table, but + * do not do so through the model. In this case, they need a way to signal to the table that the + * model has been updated. * * @param event the event for the change */ @@ -334,6 +348,7 @@ public class GTable extends JTable { /** * Sets the delay between keystrokes after which each keystroke is considered a new lookup + * * @param timeout the timeout * @see #setAutoLookupColumn(int) * @see AutoLookup#KEY_TYPING_TIMEOUT @@ -348,10 +363,10 @@ public class GTable extends JTable { /** * Sets the column in which auto-lookup will be enabled. - * - *

Note: calling this method with a valid column index will disable key binding support - * of actions. See {@link #setActionsEnabled(boolean)}. Passing an invalid column index - * will disable the auto-lookup feature. + *

+ * Note: calling this method with a valid column index will disable key binding support of + * actions. See {@link #setActionsEnabled(boolean)}. Passing an invalid column index will + * disable the auto-lookup feature. * * @param lookupColumn the column in which auto-lookup will be enabled */ @@ -385,13 +400,13 @@ public class GTable extends JTable { /** * Enables the keyboard actions to pass through this table and up the component hierarchy. - * Specifically, passing true to this method allows unmodified keystrokes to work - * in the tool when this table is focused. Modified keystrokes, like - * Ctrl-C, will work at all times. Finally, if true is passed to this - * method, then the {@link #setAutoLookupColumn(int) auto lookup} feature is - * disabled. - * - *

The default state is for actions to be disabled. + *

+ * Specifically, passing true to this method allows unmodified keystrokes to work in the tool + * when this table is focused. Modified keystrokes, like {@code Ctrl-C}, will work at all + * times. Finally, if true is passed to this method, then the + * {@linkplain #setAutoLookupColumn(int) auto lookup} feature is disabled. * + *

+ * The default state is for actions to be disabled. * * @param b true allows keyboard actions to pass up the component hierarchy. */ @@ -402,9 +417,10 @@ public class GTable extends JTable { /** * Returns true if key strokes are used to trigger actions. * - *

This method has a relationship with {@link #setAutoLookupColumn(int)}. If this method - * returns true, then the auto-lookup feature is disabled. If this method - * returns false, then the auto-lookup may or may not be enabled. + *

+ * This method has a relationship with {@link #setAutoLookupColumn(int)}. If this method returns + * true, then the auto-lookup feature is disabled. If this method returns false, then the + * auto-lookup may or may not be enabled. * * @return true if key strokes are used to trigger actions * @see #setActionsEnabled(boolean) @@ -416,12 +432,12 @@ public class GTable extends JTable { /** * Sets an accessible name on the GTable such that screen readers will properly describe them. - *

- * This prefix should be the base name that describes the type of items in the table. - * This method will then append the necessary information to property name the table. + *

+ * This prefix should be the base name that describes the type of items in the table. This + * method will then append the necessary information to property name the table. * - * @param namePrefix the accessible name prefix to assign to the filter component. For - * example if the table contains fruits, then "Fruits" would be an appropriate prefix name. + * @param namePrefix the accessible name prefix to assign to the filter component. For example + * if the table contains fruits, then "Fruits" would be an appropriate prefix name. */ public void setAccessibleNamePrefix(String namePrefix) { // set the component name as general good practice @@ -433,8 +449,9 @@ public class GTable extends JTable { } /** - * Enables or disables auto-edit. When enabled, the user can start typing to trigger an - * edit of an editable table cell. + * Enables or disables auto-edit. + *

+ * When enabled, the user can start typing to trigger an edit of an editable table cell. * * @param allowAutoEdit true for auto-editing */ @@ -496,6 +513,7 @@ public class GTable extends JTable { } private void removeActionKeyStrokes() { + // // We remove these keybindings as we have replaced Java's version with our own. To be // thorough, we should really clear all table keybindings, which would ensure that any @@ -505,6 +523,7 @@ public class GTable extends JTable { // wish to override. For now, just clear these. We can clear others if they become // a problem. // + KeyBindingUtils.clearKeyBinding(this, COPY_KEY_STROKE); KeyBindingUtils.clearKeyBinding(this, COPY_COLUMN_KEY_STROKE); KeyBindingUtils.clearKeyBinding(this, SELECT_ALL_KEY_STROKE); @@ -521,7 +540,6 @@ public class GTable extends JTable { } private void initializeRowHeight() { - // Note: this method gets called indirectly from the parent constructor, so we cannot // initialize this field at declaration time or in our constructor, as this call will have // happened at that point. @@ -538,7 +556,6 @@ public class GTable extends JTable { } private void adjustRowHeight() { - if (!isInitialized) { return; // must be initializing } @@ -619,8 +636,7 @@ public class GTable extends JTable { } /** - * Returns the underlying ConfigurableColumnTableModel if one is in-use - * @return the underlying ConfigurableColumnTableModel if one is in-use + * {@return the underlying ConfigurableColumnTableModel if one is in-use} */ public ConfigurableColumnTableModel getConfigurableColumnTableModel() { TableModel model = getUnwrappedTableModel(); @@ -633,6 +649,7 @@ public class GTable extends JTable { /** * Unrolls the current model by checking if the current model is inside of a wrapper table * model. + * * @return this class's table model, unwrapped as needed */ protected TableModel getUnwrappedTableModel() { @@ -687,6 +704,7 @@ public class GTable extends JTable { /** * Installs the default {@link TableCellRenderer}s for known Ghidra table cell data classes. + *

* Subclasses can override this method to add additional types or to change the default * associations. */ @@ -773,9 +791,17 @@ public class GTable extends JTable { return; } AbstractGTableModel gTableModel = (AbstractGTableModel) wrappedModel; - int width = gTableModel.getPreferredColumnWidth(columnIndex); - if (width != AbstractGTableModel.WIDTH_UNDEFINED) { - column.setPreferredWidth(width); + int preferredWidth = gTableModel.getPreferredColumnWidth(columnIndex); + if (preferredWidth != AbstractGTableModel.WIDTH_UNDEFINED) { + column.setPreferredWidth(preferredWidth); + } + int maxWidth = gTableModel.getMaxColumnWidth(columnIndex); + if (maxWidth != AbstractGTableModel.WIDTH_UNDEFINED) { + column.setMaxWidth(maxWidth); + } + int minWidth = gTableModel.getMinColumnWidth(columnIndex); + if (minWidth != AbstractGTableModel.WIDTH_UNDEFINED) { + column.setMinWidth(minWidth); } } @@ -822,13 +848,13 @@ public class GTable extends JTable { } /** - * Enables and disables the rendering of HTML content in this table. If enabled, this table + * Enables and disables the rendering of HTML content in this table. If enabled, this table * will: *

    - *
  • Wrap tooltip text content with an <html> tag so that it is possible for - * the content to be formatted in a manner that is easier for the user read, and
  • - *
  • Enable any default {@link GTableCellRenderer} instances to render - * HTML content, which they do not do by default.
  • + *
  • Wrap tooltip text content with an <html> tag so that it is possible for the content + * to be formatted in a manner that is easier for the user read, and
  • + *
  • Enable any default {@link GTableCellRenderer} instances to render HTML + * content, which they do not do by default.
  • *
*

* HTML rendering is disabled by default. @@ -848,6 +874,7 @@ public class GTable extends JTable { /** * Sets the table filter panel being used for this table. + * * @param filterPanel the filter panel */ public void setTableFilterPanel(GTableFilterPanel filterPanel) { @@ -855,16 +882,17 @@ public class GTable extends JTable { } /** - * Returns the filter panel being used by this table or null. - * @return the filter panel or null + * {@return the filter panel being used by this table or null} */ public GTableFilterPanel getTableFilterPanel() { return tableFilterPanel; } /** - * Sets the key for saving and restoring column configuration state. Use this if you have - * multiple instances of a table and you want different column settings for each instance. + * Sets the key for saving and restoring column configuration state. + *

+ * Use this if you have multiple instances of a table and you want different column settings for + * each instance. * * @param preferenceKey the unique string to use a key for this instance. */ @@ -879,8 +907,9 @@ public class GTable extends JTable { } /** + * {@return the preference key} + * * @see #setPreferenceKey(String) - * @return the preference key */ public String getPreferenceKey() { return preferenceKey; @@ -888,10 +917,12 @@ public class GTable extends JTable { /** * Signals that the preferences of this table (visible columns, sort order, etc.) should be - * saved. Most clients never need to call this method, as changes are saved for free when - * the user manipulates columns. However, sometimes the client can change the state of the - * columns programmatically, which is not guaranteed to get saved; for example, setting - * the sort state of a sorted table model programmatically will not get saved. + * saved. + *

+ * Most clients never need to call this method, as changes are saved for free when the user + * manipulates columns. However, sometimes the client can change the state of the columns + * programmatically, which is not guaranteed to get saved; for example, setting the sort state + * of a sorted table model programmatically will not get saved. */ public void savePreferences() { if (!(columnModel instanceof GTableColumnModel)) { @@ -903,8 +934,9 @@ public class GTable extends JTable { /** * Allows for the disabling of the user's ability to sort an instance of - * {@link AbstractSortedTableModel} by clicking the table's headers. The default setting is - * enabled. + * {@link AbstractSortedTableModel} by clicking the table's headers. + *

+ * The default setting is enabled. * * @param enabled true to enable; false to disable */ @@ -936,10 +968,11 @@ public class GTable extends JTable { } /** - * Performs custom work to locate renderers for special table model types. This method allows - * clients to bypass the {@link #getCellRenderer(int, int)}, which is sometimes overridden by - * subclasses to return a hard-coded renderer. In that case, some clients still want a way to - * perform normal cell renderer lookup. + * Performs custom work to locate renderers for special table model types. + *

+ * This method allows clients to bypass the {@link #getCellRenderer(int, int)}, which is + * sometimes overridden by subclasses to return a hard-coded renderer. In that case, some + * clients still want a way to perform normal cell renderer lookup. * * @param row the row * @param col the column @@ -947,19 +980,50 @@ public class GTable extends JTable { */ public final TableCellRenderer getCellRendererOverride(int row, int col) { ConfigurableColumnTableModel configurableModel = getConfigurableColumnTableModel(); - if (configurableModel != null) { - int modelIndex = convertColumnIndexToModel(col); - TableCellRenderer renderer = configurableModel.getRenderer(modelIndex); - if (renderer != null) { - return renderer; - } + if (configurableModel == null) { + return super.getCellRenderer(row, col); } - return super.getCellRenderer(row, col); + int modelIndex = convertColumnIndexToModel(col); + TableCellRenderer renderer = configurableModel.getRenderer(modelIndex); + if (renderer == null) { + return super.getCellRenderer(row, col); + } + return renderer; + } + + @Override + public TableCellEditor getCellEditor(int row, int column) { + return getCellEditorOverride(row, column); } /** - * Performs custom work to locate header renderers for special table model types. The headers - * are located and installed at the time the table's model is set. + * Performs custom work to locate editors for special table model types. + *

+ * This method allows clients to bypass the {@link #getCellEditor(int, int)}, which is sometimes + * overridden by subclasses to return a hard-coded editor. In that case, some clients still want + * a way to perform normal cell editor lookup. + * + * @param row the row + * @param col the column + * @return the cell editor + */ + public final TableCellEditor getCellEditorOverride(int row, int col) { + ConfigurableColumnTableModel configurableModel = getConfigurableColumnTableModel(); + if (configurableModel == null) { + return super.getCellEditor(row, col); + } + int modelIndex = convertColumnIndexToModel(col); + TableCellEditor editor = configurableModel.getEditor(modelIndex); + if (editor == null) { + return super.getCellEditor(row, col); + } + return editor; + } + + /** + * Performs custom work to locate header renderers for special table model types. + *

+ * The headers are located and installed at the time the table's model is set. * * @param col the column * @return the header cell renderer @@ -977,6 +1041,8 @@ public class GTable extends JTable { } /** + * {@inheritDoc} + *

* If you just begin typing into an editable cell in a JTable, then the cell editor will be * displayed. However, the editor component will not have a focus. This method has been * overridden to request focus on the editor component. @@ -1012,8 +1078,9 @@ public class GTable extends JTable { } /** - * Scrolls the selected row into the view center. This call will not scroll if the selected row - * is already in the view. + * Scrolls the selected row into the view center. + *

+ * This call will not scroll if the selected row is already in the view. */ public void scrollToSelectedRow() { Container parent = getParent(); @@ -1136,10 +1203,12 @@ public class GTable extends JTable { return item; } - /* - * Note: overridden to allow the Copy actions to record the text data of each cell - * *without* using HTML. When users copy the table data, having HTML markup makes the - * data almost unreadable/unusable. + /** + * {@inheritDoc} + * + * @implNote overridden to allow the Copy actions to record the text data of each cell + * without using HTML. When users copy the table data, having HTML markup + * makes the data almost unreadable/unusable. */ @Override public Object getValueAt(int row, int column) { @@ -1177,13 +1246,14 @@ public class GTable extends JTable { /** * Maintain a {@link docking.widgets.table.GTableCellRenderingData} object associated with each - * column that maintains some state and references to useful data. These objects are created as - * needed, stored by the table for convenient re-use and to prevent per-cell creation, and - * cleared when columns are removed from the table. + * column that maintains some state and references to useful data. *

- * Row and cell state is cleared before returning to the caller to ensure consistent state; - * when the client is done rendering a cell, row and cell state should also be cleared to - * minimize references. + * These objects are created as needed, stored by the table for convenient re-use and to prevent + * per-cell creation, and cleared when columns are removed from the table. + *

+ * Row and cell state is cleared before returning to the caller to ensure consistent state; when + * the client is done rendering a cell, row and cell state should also be cleared to minimize + * references. * * @param viewColumn the columns' view index * @return Data specific to the column. Row state is cleared before returning. @@ -1216,7 +1286,9 @@ public class GTable extends JTable { /** * A method that subclasses can override to signal that they wish not to have this table's - * built-in popup actions. Subclasses will almost never need to override this method. + * built-in popup actions. + *

+ * Subclasses will almost never need to override this method. * * @return true if popup actions are supported */ @@ -1225,13 +1297,10 @@ public class GTable extends JTable { } private void copyColumns(int... copyColumns) { - - // // We have to change the column model's selection settings to ensure that the copy works // correctly. For example, if the model only allows single column selection, then we have // to change that to allow for multiple column selection. We will put the original state // back when finished. - // ColumnSelectionState originalState = ColumnSelectionState.copy(this); ColumnSelectionState newState = ColumnSelectionState.withColumns(this, copyColumns); newState.apply(); @@ -1363,7 +1432,6 @@ public class GTable extends JTable { } public static void createSharedActions(Tool tool, ToolActions toolActions, String owner) { - String actionMenuGroup = "zzzTableGroup"; tool.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1"); tool.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2"); @@ -1377,17 +1445,13 @@ public class GTable extends JTable { gTable.doCopy(); } }; - //@formatter:off copyAction.setPopupMenuData(new MenuData( - new String[] { "Copy", "Copy" }, - Icons.COPY_ICON, - actionMenuGroup, NO_MNEMONIC, - Integer.toString(subGroupIndex++) - ) - ); + new String[] { "Copy", "Copy" }, + Icons.COPY_ICON, + actionMenuGroup, NO_MNEMONIC, + Integer.toString(subGroupIndex++))); copyAction.setKeyBindingData(new KeyBindingData(COPY_KEY_STROKE)); copyAction.setHelpLocation(new HelpLocation("Tables", "Copy")); - //@formatter:on GTableAction copyCurrentColumnAction = new GTableAction("Table Data Copy Current Column", owner) { @@ -1397,19 +1461,15 @@ public class GTable extends JTable { gTable.doCopyCurrentColumn(context.getMouseEvent()); } }; - //@formatter:off copyCurrentColumnAction.setPopupMenuData(new MenuData( - new String[] { "Copy", + new String[] { "Copy", "Copy Current Column" }, - Icons.COPY_ICON, - actionMenuGroup, - NO_MNEMONIC, - Integer.toString(subGroupIndex++) - ) - ); + Icons.COPY_ICON, + actionMenuGroup, + NO_MNEMONIC, + Integer.toString(subGroupIndex++))); copyCurrentColumnAction.setKeyBindingData(new KeyBindingData(COPY_COLUMN_KEY_STROKE)); copyCurrentColumnAction.setHelpLocation(new HelpLocation("Tables", "Copy_Current_Column")); - //@formatter:on GTableAction copyColumnsAction = new GTableAction("Table Data Copy by Columns", owner) { @Override @@ -1418,17 +1478,13 @@ public class GTable extends JTable { gTable.doCopyColumns(); } }; - //@formatter:off copyColumnsAction.setPopupMenuData(new MenuData( - new String[] { "Copy", "Copy Columns..." }, - Icons.COPY_ICON, - actionMenuGroup, - NO_MNEMONIC, - Integer.toString(subGroupIndex++) - ) - ); + new String[] { "Copy", "Copy Columns..." }, + Icons.COPY_ICON, + actionMenuGroup, + NO_MNEMONIC, + Integer.toString(subGroupIndex++))); copyColumnsAction.setHelpLocation(new HelpLocation("Tables", "Copy_Columns")); - //@formatter:on GTableAction exportAction = new GTableAction("Table Data CSV Export", owner) { @Override @@ -1437,17 +1493,13 @@ public class GTable extends JTable { gTable.doExport(); } }; - //@formatter:off exportAction.setPopupMenuData(new MenuData( - new String[] { "Export", GTableToCSV.TITLE + "..." }, - ICON_SPREADSHEET, - actionMenuGroup, - NO_MNEMONIC, - Integer.toString(subGroupIndex++) - ) - ); + new String[] { "Export", GTableToCSV.TITLE + "..." }, + ICON_SPREADSHEET, + actionMenuGroup, + NO_MNEMONIC, + Integer.toString(subGroupIndex++))); exportAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV")); - //@formatter:on GTableAction exportColumnsAction = new GTableAction("Table Data CSV Export (by Columns)", owner) { @@ -1457,17 +1509,13 @@ public class GTable extends JTable { gTable.doExportColumns(); } }; - //@formatter:off exportColumnsAction.setPopupMenuData(new MenuData( - new String[] { "Export", "Export Columns to CSV..." }, - ICON_SPREADSHEET, - actionMenuGroup, - NO_MNEMONIC, - Integer.toString(subGroupIndex++) - ) - ); + new String[] { "Export", "Export Columns to CSV..." }, + ICON_SPREADSHEET, + actionMenuGroup, + NO_MNEMONIC, + Integer.toString(subGroupIndex++))); exportColumnsAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV_Columns")); - //@formatter:on GTableAction selectAllAction = new GTableAction("Table Select All", owner) { @Override @@ -1486,18 +1534,14 @@ public class GTable extends JTable { return mode != ListSelectionModel.SINGLE_SELECTION; } }; - //@formatter:off selectAllAction.setPopupMenuData(new MenuData( - new String[] { "Select All" }, - null /*icon*/, - actionMenuGroup, - NO_MNEMONIC, - Integer.toString(subGroupIndex++) - ) - ); + new String[] { "Select All" }, + null /*icon*/, + actionMenuGroup, + NO_MNEMONIC, + Integer.toString(subGroupIndex++))); selectAllAction.setKeyBindingData(new KeyBindingData(SELECT_ALL_KEY_STROKE)); selectAllAction.setHelpLocation(new HelpLocation("Tables", "SelectAll")); - //@formatter:on GTableAction activateFilterAction = new GTableAction("Table/Tree Activate Filter", owner) { @@ -1519,18 +1563,14 @@ public class GTable extends JTable { filterPanel.activate(); } }; - //@formatter:off activateFilterAction.setPopupMenuData(new MenuData( - new String[] { "Activate Filter" }, - null /*icon*/, - actionMenuGroup, - NO_MNEMONIC, - Integer.toString(subGroupIndex++) - ) - ); + new String[] { "Activate Filter" }, + null /*icon*/, + actionMenuGroup, + NO_MNEMONIC, + Integer.toString(subGroupIndex++))); activateFilterAction.setKeyBindingData(new KeyBindingData(ACTIVATE_FILTER_KEY_STROKE)); activateFilterAction.setHelpLocation(new HelpLocation("Trees", "Activate_Filter")); - //@formatter:on GTableAction hideFilterAction = new GTableAction("Table/Tree Hide Filter", owner) { @@ -1573,17 +1613,13 @@ public class GTable extends JTable { return filterPanel.isShowing(); } }; - //@formatter:off hideFilterAction.setPopupMenuData(new MenuData( - new String[] { "Hide Filter" }, - null /*icon*/, - actionMenuGroup, - NO_MNEMONIC, - Integer.toString(subGroupIndex++) - ) - ); + new String[] { "Hide Filter" }, + null /*icon*/, + actionMenuGroup, + NO_MNEMONIC, + Integer.toString(subGroupIndex++))); hideFilterAction.setHelpLocation(new HelpLocation("Trees", "Hide_Filter")); - //@formatter:on toolActions.addGlobalAction(copyAction); toolActions.addGlobalAction(copyColumnsAction); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/MappedTableColumn.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/MappedTableColumn.java index 2a07d7db64..4a8d5fe3ed 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/MappedTableColumn.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/MappedTableColumn.java @@ -17,6 +17,8 @@ package docking.widgets.table; import java.util.Comparator; +import javax.swing.table.TableCellEditor; + import ghidra.docking.settings.Settings; import ghidra.docking.settings.SettingsDefinition; import ghidra.framework.plugintool.ServiceProvider; @@ -99,6 +101,11 @@ public class MappedTableColumn getComparator(DynamicColumnTableModel model, int columnIndex) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/TableColumnModelState.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/TableColumnModelState.java index 5bed5cde1e..c8ab4685ea 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/TableColumnModelState.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/TableColumnModelState.java @@ -34,40 +34,38 @@ import ghidra.util.task.SwingUpdateManager; * A class to keep track of and persist state for column models, including size, ordering and * visibility. *

- * This class performs a bit of magic to accomplish its goals. Resultingly, some of the code - * herein may seem a bit odd or of poor quality. These rough spots are documented as best as - * possible. + * This class performs a bit of magic to accomplish its goals. Resultingly, some of the code herein + * may seem a bit odd or of poor quality. These rough spots are documented as best as possible. *

* The basic outline of how this class works:
* - * This class loads and save table column state via requests made by clients like the {@link GTable} or - * the {@link GTableColumnModel}. These requests are in response to direct users actions (like - * showing a new column) or to table changes (like column resizing). There are few things that - * make this code tricky. Namely, when a change notification comes from the subsystem and not - * direct user intervention, we do not know if the change was motived by the user directly or - * by programmatic table configuration. We would prefer to only save data when the user makes - * changes, but we can not always know the source of the change. For example, column resizing - * can happen due to user dragging or due to the table subsystem performing a column layout. + * This class loads and save table column state via requests made by clients like the {@link GTable} + * or the {@link GTableColumnModel}. These requests are in response to direct users actions (like + * showing a new column) or to table changes (like column resizing). There are few things that make + * this code tricky. Namely, when a change notification comes from the subsystem and not direct user + * intervention, we do not know if the change was motived by the user directly or by programmatic + * table configuration. We would prefer to only save data when the user makes changes, but we can + * not always know the source of the change. For example, column resizing can happen due to user + * dragging or due to the table subsystem performing a column layout. *

* To facilitate this magic, we listen to all changes, attempting to: 1) ignore those that we know * are not from the user, and 2) buffer the changes so that they are not excessive and so they * happen in the correct order. *

- * For 1, we ignore all changes until the table has been shown for the first time. For 2, we use + * For 1, we ignore all changes until the table has been shown for the first time. For 2, we use * SwingUpdate managers. *

- * The complicated part is that we allow clients to add columns at any time. If they do so - * after the table has been made visible, then we cannot ignore the event like we do when the - * table has not yet been realized. In our world view, the uniqueness of a table is based upon - * it's class and its columns. Thus, when a column is added or removed, it becomes a different - * table and thus, saved settings must be applied. + * The complicated part is that we allow clients to add columns at any time. If they do so after the + * table has been made visible, then we cannot ignore the event like we do when the table has not + * yet been realized. In our world view, the uniqueness of a table is based upon it's class and its + * columns. Thus, when a column is added or removed, it becomes a different table and thus, saved + * settings must be applied. */ public class TableColumnModelState implements SortListener { /** - * A width that is large enough to consume the extra space when columns are getting - * resized. This value is meant to be used when a column does not specify it's - * preferred width. + * A width that is large enough to consume the extra space when columns are getting resized. + * This value is meant to be used when a column does not specify it's preferred width. */ private static final int LARGE_DEFAULT_COL_WIDTH = 500; @@ -461,9 +459,9 @@ public class TableColumnModelState implements SortListener { } /** - * This method will return a string key that uniquely describes a table model and its - * *default* columns (those initially added by the model) so that settings for column state - * can be persisted and retrieved. + * This method will return a string key that uniquely describes a table model and its *default* + * columns (those initially added by the model) so that settings for column state can be + * persisted and retrieved. */ private String getPreferenceKey() { String preferenceKey = table.getPreferenceKey(); @@ -547,11 +545,10 @@ public class TableColumnModelState implements SortListener { // TableModel model = table.getUnwrappedTableModel(); - if (!(model instanceof AbstractGTableModel)) { + if (!(model instanceof AbstractGTableModel gModel)) { return; } - AbstractGTableModel gModel = (AbstractGTableModel) model; List columnList = columnModel.getAllColumns(); for (TableColumn col : columnList) { int defaultPreferred = col.getPreferredWidth(); @@ -562,13 +559,23 @@ public class TableColumnModelState implements SortListener { continue; } - int preferred = gModel.getPreferredColumnWidth(col.getModelIndex()); + int modelIndex = col.getModelIndex(); + int preferred = gModel.getPreferredColumnWidth(modelIndex); if (preferred < 15) { preferred = LARGE_DEFAULT_COL_WIDTH; } int size = preferred; col.setWidth(size); col.setPreferredWidth(size); + + int max = gModel.getMaxColumnWidth(modelIndex); + if (max != AbstractGTableModel.WIDTH_UNDEFINED) { + col.setMaxWidth(max); + } + int min = gModel.getMinColumnWidth(modelIndex); + if (min != AbstractGTableModel.WIDTH_UNDEFINED) { + col.setMinWidth(min); + } } }