From bca2fe139afa6fafc2a29c12b493b70cbe4d42fd Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Mon, 18 Dec 2023 16:32:25 -0500 Subject: [PATCH] GP-4156: Add toggles for Model panels and Modules/Sections panel. --- Ghidra/Debug/Debugger/certification.manifest | 6 + .../Debugger/data/debugger.theme.properties | 6 + .../DebuggerModulesPlugin.html | 6 + .../images/DebuggerModulesPlugin.png | Bin 27341 -> 27784 bytes .../core/debug/gui/DebuggerResources.java | 114 ------ .../gui/model/DebuggerModelProvider.java | 377 ++++++++++++++++-- .../debug/gui/model/ListenerSuppressor.java | 21 + .../debug/gui/model/ObjectsTreePanel.java | 111 +++++- .../gui/modules/DebuggerModulesProvider.java | 111 +++++- .../src/main/resources/images/table-a.png | Bin 0 -> 696 bytes .../src/main/resources/images/table-e.png | Bin 0 -> 668 bytes .../src/main/resources/images/table-s.png | Bin 0 -> 727 bytes .../Debug/Debugger/src/main/svg/table-a.svg | 56 +++ .../Debug/Debugger/src/main/svg/table-e.svg | 56 +++ .../Debug/Debugger/src/main/svg/table-s.svg | 56 +++ .../gui/model/DebuggerModelProviderTest.java | 5 +- 16 files changed, 766 insertions(+), 159 deletions(-) create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ListenerSuppressor.java create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/table-a.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/table-e.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/table-s.png create mode 100644 Ghidra/Debug/Debugger/src/main/svg/table-a.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/table-e.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/table-s.svg diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest index e813f80d23..659589c6fa 100644 --- a/Ghidra/Debug/Debugger/certification.manifest +++ b/Ghidra/Debug/Debugger/certification.manifest @@ -139,6 +139,9 @@ src/main/resources/images/steplast.png||GHIDRA||||END| src/main/resources/images/stepout.png||GHIDRA||||END| src/main/resources/images/stepover.png||GHIDRA||||END| src/main/resources/images/system-switch-user.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| +src/main/resources/images/table-a.png||FAMFAMFAM Icons - CC 2.5||||END| +src/main/resources/images/table-e.png||FAMFAMFAM Icons - CC 2.5||||END| +src/main/resources/images/table-s.png||FAMFAMFAM Icons - CC 2.5||||END| src/main/resources/images/thread.png||GHIDRA||||END| src/main/resources/images/time.png||FAMFAMFAM Icons - CC 2.5||||END| src/main/resources/images/write-emulator.png||GHIDRA||||END| @@ -189,6 +192,9 @@ src/main/svg/stepinto.svg||GHIDRA||||END| src/main/svg/steplast.svg||GHIDRA||||END| src/main/svg/stepout.svg||GHIDRA||||END| src/main/svg/stepover.svg||GHIDRA||||END| +src/main/svg/table-a.svg||FAMFAMFAM Icons - CC 2.5||||END| +src/main/svg/table-e.svg||FAMFAMFAM Icons - CC 2.5||||END| +src/main/svg/table-s.svg||FAMFAMFAM Icons - CC 2.5||||END| src/main/svg/thread.svg||GHIDRA||||END| src/main/svg/write-disabled.svg||GHIDRA||||END| src/main/svg/write-emulator.svg||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger/data/debugger.theme.properties b/Ghidra/Debug/Debugger/data/debugger.theme.properties index 412c00bf7a..dd36191376 100644 --- a/Ghidra/Debug/Debugger/data/debugger.theme.properties +++ b/Ghidra/Debug/Debugger/data/debugger.theme.properties @@ -63,6 +63,12 @@ color.debugger.plugin.resources.breakpoint.marker.enabled.ineffective = color.pa color.debugger.plugin.resources.breakpoint.marker.disabled.ineffective = color.debugger.plugin.resources.breakpoint.marker.enabled.ineffective +icon.debugger.model.tree.objects = function_graph.png +icon.debugger.model.table.elements = table-e.png +icon.debugger.model.table.attributes = table-a.png + +icon.debugger.modules.table.sections = table-s.png + icon.debugger.object.populated = object-populated.png icon.debugger.object.unpopulated = object-unpopulated.png diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html index 742a5bfecd..4cfb3fbb6e 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html @@ -167,6 +167,12 @@

This action is offered to resolve a "missing module" console message. It is equivalent to Map Module To on the missing module.

+

Show Sections Table

+ +

This actions is always available. By default the sections table (bottom) is showing. Some + debuggers do not offer section information, and even for those that do, it can be expensive to + retrieve it. The visibility of the section table is controlled by toggling this action.

+

Filter Sections by Module

This action is always available. By default the bottom table displays all sections in the diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png index 72fe042d4bab18459d330b08778349e1829d82be..2d1b78b3d7a351ff363e5008997daddb10009415 100644 GIT binary patch literal 27784 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 literal 27341 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 XPm_2plL}zl;Jaje^?cdcKOX)Un$-;{ diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java index a06598d846..57bded0203 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java @@ -813,40 +813,6 @@ public interface DebuggerResources { } } - interface ImportMissingModuleAction { - String NAME = "Import Missing Module"; - String DESCRIPTION = "Import the missing module from disk"; - Icon ICON = ICON_IMPORT; - String HELP_ANCHOR = "import_missing_module"; - - static ActionBuilder builder(Plugin owner) { - String ownerName = owner.getName(); - return new ActionBuilder(NAME, ownerName) - .description(DESCRIPTION) - .toolBarIcon(ICON) - .popupMenuIcon(ICON) - .popupMenuPath(NAME) - .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); - } - } - - interface MapMissingModuleAction { - String NAME = "Map Missing Module"; - String DESCRIPTION = "Map the missing module to an existing import"; - Icon ICON = ICON_MAP_MODULES; - String HELP_ANCHOR = "map_missing_module"; - - static ActionBuilder builder(Plugin owner) { - String ownerName = owner.getName(); - return new ActionBuilder(NAME, ownerName) - .description(DESCRIPTION) - .toolBarIcon(ICON) - .popupMenuIcon(ICON) - .popupMenuPath(NAME) - .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); - } - } - interface FollowsCurrentThreadAction { String NAME = "Follows Selected Thread"; String DESCRIPTION = "Register tracking follows selected thread (and contents" + @@ -1864,86 +1830,6 @@ public interface DebuggerResources { } } - interface LimitToCurrentSnapAction { - String NAME = "Limit to Current Snap"; - String DESCRIPTION = "Choose whether displayed objects must be alive at the current snap"; - String GROUP = GROUP_GENERAL; - String HELP_ANCHOR = "limit_to_current_snap"; - - static ToggleActionBuilder builder(Plugin owner) { - String ownerName = owner.getName(); - return new ToggleActionBuilder(NAME, ownerName) - .description(DESCRIPTION) - .menuPath(NAME) - .menuGroup(GROUP) - .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); - } - } - - interface ShowHiddenAction { - String NAME = "Show Hidden"; - String DESCRIPTION = "Choose whether to display hidden children"; - String GROUP = GROUP_GENERAL; - String HELP_ANCHOR = "show_hidden"; - - static ToggleActionBuilder builder(Plugin owner) { - String ownerName = owner.getName(); - return new ToggleActionBuilder(NAME, ownerName) - .description(DESCRIPTION) - .menuPath(NAME) - .menuGroup(GROUP) - .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); - } - } - - interface ShowPrimitivesInTreeAction { - String NAME = "Show Primitives in Tree"; - String DESCRIPTION = "Choose whether to display primitive values in the tree"; - String GROUP = GROUP_GENERAL; - String HELP_ANCHOR = "show_primitives"; - - static ToggleActionBuilder builder(Plugin owner) { - String ownerName = owner.getName(); - return new ToggleActionBuilder(NAME, ownerName) - .description(DESCRIPTION) - .menuPath(NAME) - .menuGroup(GROUP) - .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); - } - } - - interface ShowMethodsInTreeAction { - String NAME = "Show Methods in Tree"; - String DESCRIPTION = "Choose whether to display methods in the tree"; - String GROUP = GROUP_GENERAL; - String HELP_ANCHOR = "show_methods"; - - static ToggleActionBuilder builder(Plugin owner) { - String ownerName = owner.getName(); - return new ToggleActionBuilder(NAME, ownerName) - .description(DESCRIPTION) - .menuPath(NAME) - .menuGroup(GROUP) - .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); - } - } - - interface FollowLinkAction { - String NAME = "Follow Link"; - String DESCRIPTION = "Navigate to the link target"; - String GROUP = GROUP_GENERAL; - String HELP_ANCHOR = "follow_link"; - - static ActionBuilder builder(Plugin owner) { - String ownerName = owner.getName(); - return new ActionBuilder(NAME, ownerName) - .description(DESCRIPTION) - .popupMenuPath(NAME) - .popupMenuGroup(GROUP) - .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); - } - } - public abstract class AbstractDebuggerConnectionsNode extends GTreeNode { @Override public String getName() { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java index 0ff19363ec..d185e040ff 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java @@ -20,6 +20,7 @@ import java.awt.event.*; import java.lang.invoke.MethodHandles; import java.util.List; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Collectors; import javax.swing.*; @@ -27,12 +28,15 @@ import javax.swing.*; import docking.*; import docking.action.DockingAction; import docking.action.ToggleDockingAction; +import docking.action.builder.ActionBuilder; +import docking.action.builder.ToggleActionBuilder; import docking.widgets.table.RangeCursorTableHeaderRenderer.SeekListener; import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin; import generic.theme.GColor; +import generic.theme.GIcon; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerResources; -import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.CloneWindowAction; import ghidra.app.plugin.core.debug.gui.MultiProviderSaveBehavior.SaveableProvider; import ghidra.app.plugin.core.debug.gui.model.AbstractQueryTablePanel.CellActivationListener; import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ObjectRow; @@ -42,13 +46,13 @@ import ghidra.app.plugin.core.debug.gui.model.PathTableModel.PathRow; import ghidra.app.services.DebuggerTraceManagerService; import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.framework.options.SaveState; -import ghidra.framework.plugintool.AutoConfigState; -import ghidra.framework.plugintool.AutoService; +import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.annotation.AutoConfigStateField; import ghidra.framework.plugintool.annotation.AutoServiceConsumed; import ghidra.trace.model.Lifespan; import ghidra.trace.model.Trace; import ghidra.trace.model.target.*; +import ghidra.util.HelpLocation; import ghidra.util.Msg; public class DebuggerModelProvider extends ComponentProvider implements SaveableProvider { @@ -59,10 +63,144 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable private static final String KEY_DEBUGGER_COORDINATES = "DebuggerCoordinates"; private static final String KEY_PATH = "Path"; + interface ShowObjectsTreeAction { + String NAME = "Show Objects Tree"; + Icon ICON = new GIcon("icon.debugger.model.tree.objects"); + String DESCRIPTION = "Toggle display of the Objects Tree pane"; + String GROUP = DebuggerResources.GROUP_VIEWS; + String ORDER = "1"; + String HELP_ANCHOR = "show_objects_tree"; + + static ToggleActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ToggleActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .toolBarIcon(ICON) + .toolBarGroup(GROUP, ORDER) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface ShowElementsTableAction { + String NAME = "Show Elements Table"; + Icon ICON = new GIcon("icon.debugger.model.table.elements"); + String DESCRIPTION = "Toggle display of the Elements Table pane"; + String GROUP = DebuggerResources.GROUP_VIEWS; + String ORDER = "2"; + String HELP_ANCHOR = "show_elements_table"; + + static ToggleActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ToggleActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .toolBarIcon(ICON) + .toolBarGroup(GROUP, ORDER) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface ShowAttributesTableAction { + String NAME = "Show Attributes Table"; + Icon ICON = new GIcon("icon.debugger.model.table.attributes"); + String DESCRIPTION = "Toggle display of the Attributes Table pane"; + String GROUP = DebuggerResources.GROUP_VIEWS; + String ORDER = "3"; + String HELP_ANCHOR = "show_attributes_table"; + + static ToggleActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ToggleActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .toolBarIcon(ICON) + .toolBarGroup(GROUP, ORDER) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface LimitToCurrentSnapAction { + String NAME = "Limit to Current Snap"; + String DESCRIPTION = "Choose whether displayed objects must be alive at the current snap"; + String GROUP = DebuggerResources.GROUP_GENERAL; + String HELP_ANCHOR = "limit_to_current_snap"; + + static ToggleActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ToggleActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .menuPath(NAME) + .menuGroup(GROUP) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface ShowHiddenAction { + String NAME = "Show Hidden"; + String DESCRIPTION = "Choose whether to display hidden children"; + String GROUP = DebuggerResources.GROUP_GENERAL; + String HELP_ANCHOR = "show_hidden"; + + static ToggleActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ToggleActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .menuPath(NAME) + .menuGroup(GROUP) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface ShowPrimitivesInTreeAction { + String NAME = "Show Primitives in Tree"; + String DESCRIPTION = "Choose whether to display primitive values in the tree"; + String GROUP = DebuggerResources.GROUP_GENERAL; + String HELP_ANCHOR = "show_primitives"; + + static ToggleActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ToggleActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .menuPath(NAME) + .menuGroup(GROUP) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface ShowMethodsInTreeAction { + String NAME = "Show Methods in Tree"; + String DESCRIPTION = "Choose whether to display methods in the tree"; + String GROUP = DebuggerResources.GROUP_GENERAL; + String HELP_ANCHOR = "show_methods"; + + static ToggleActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ToggleActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .menuPath(NAME) + .menuGroup(GROUP) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface FollowLinkAction { + String NAME = "Follow Link"; + String DESCRIPTION = "Navigate to the link target"; + String GROUP = DebuggerResources.GROUP_GENERAL; + String HELP_ANCHOR = "follow_link"; + + static ActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .popupMenuPath(NAME) + .popupMenuGroup(GROUP) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + private final DebuggerModelPlugin plugin; private final boolean isClone; - private JPanel mainPanel = new JPanel(new BorderLayout()); + private JPanel mainPanel; static class MyTextField extends JTextField { // This one can be reflected for testing @@ -74,6 +212,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable protected MyTextField pathField; protected JButton goButton; + protected JPanel queryPanel; protected ObjectsTreePanel objectsTreePanel; protected ObjectsTablePanel elementsTablePanel; protected PathsTablePanel attributesTablePanel; @@ -86,6 +225,17 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable @SuppressWarnings("unused") private final AutoService.Wiring autoServiceWiring; + @AutoConfigStateField + private boolean showObjectsTree = true; + @AutoConfigStateField + private boolean showElementsTable = true; + @AutoConfigStateField + private boolean showAttributesTable = true; + @AutoConfigStateField + private double lrResizeWeight = 0.2; + @AutoConfigStateField + private double tbResizeWeight = 0.7; + @AutoConfigStateField private boolean limitToSnap = true; @AutoConfigStateField @@ -96,6 +246,9 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable private boolean showMethodsInTree = false; DockingAction actionCloneWindow; + ToggleDockingAction actionShowObjectsTree; + ToggleDockingAction actionShowElementsTable; + ToggleDockingAction actionShowAttributesTable; ToggleDockingAction actionLimitToCurrentSnap; ToggleDockingAction actionShowHidden; ToggleDockingAction actionShowPrimitivesInTree; @@ -156,7 +309,117 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable super.removeFromTool(); } + protected static double computeResizeWeight(JSplitPane split) { + Function axis = switch (split.getOrientation()) { + case JSplitPane.HORIZONTAL_SPLIT -> (dim -> dim.width); + case JSplitPane.VERTICAL_SPLIT -> (dim -> dim.height); + default -> throw new AssertionError(); + }; + + // This method is off by a little, and I don't know why, but I don't care. + + Component lComp = split.getLeftComponent(); + int lMin = axis.apply(lComp.getMinimumSize()); + int lSize = axis.apply(lComp.getSize()); + Component rComp = split.getRightComponent(); + int rMin = axis.apply(rComp.getMinimumSize()); + int rSize = axis.apply(rComp.getSize()); + + int totalExtra = lSize + rSize - lMin - rMin; + int lExtra = lSize - lMin; + + return (double) lExtra / totalExtra; + } + + protected JSplitPane createLrSplit() { + JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + split.setResizeWeight(lrResizeWeight); + split.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, pce -> { + lrResizeWeight = computeResizeWeight(split); + }); + return split; + } + + protected JSplitPane createTbSplit() { + JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + split.setResizeWeight(tbResizeWeight); + split.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, pce -> { + tbResizeWeight = computeResizeWeight(split); + }); + return split; + } + + protected JPanel createLabeledElementsTable() { + JPanel panel = new JPanel(new BorderLayout()); + panel.add(elementsTablePanel); + panel.add(new JLabel("Elements"), BorderLayout.NORTH); + return panel; + } + + protected JPanel createLabeledAttributesTable() { + JPanel panel = new JPanel(new BorderLayout()); + panel.add(attributesTablePanel); + panel.add(new JLabel("Attributes"), BorderLayout.NORTH); + return panel; + } + + protected void rebuildPanels() { + if (mainPanel == null) { + return; + } + + try (ListenerSuppressor suppressor = objectsTreePanel.suppressShowingListener()) { + mainPanel.removeAll(); + mainPanel.add(queryPanel, BorderLayout.NORTH); + + if (showObjectsTree && showElementsTable && showAttributesTable) { + JSplitPane lrSplit = createLrSplit(); + mainPanel.add(lrSplit, BorderLayout.CENTER); + JSplitPane tbSplit = createTbSplit(); + lrSplit.setRightComponent(tbSplit); + lrSplit.setLeftComponent(objectsTreePanel); + tbSplit.setLeftComponent(createLabeledElementsTable()); + tbSplit.setRightComponent(createLabeledAttributesTable()); + } + else if (showObjectsTree && showElementsTable) { + JSplitPane lrSplit = createLrSplit(); + mainPanel.add(lrSplit, BorderLayout.CENTER); + lrSplit.setLeftComponent(objectsTreePanel); + lrSplit.setRightComponent(elementsTablePanel); + } + else if (showObjectsTree && showAttributesTable) { + JSplitPane lrSplit = createLrSplit(); + mainPanel.add(lrSplit, BorderLayout.CENTER); + lrSplit.setLeftComponent(objectsTreePanel); + lrSplit.setRightComponent(attributesTablePanel); + } + else if (showElementsTable && showAttributesTable) { + JSplitPane tbSplit = createTbSplit(); + tbSplit.setLeftComponent(createLabeledElementsTable()); + tbSplit.setRightComponent(createLabeledAttributesTable()); + mainPanel.add(tbSplit, BorderLayout.CENTER); + } + else if (showObjectsTree) { + mainPanel.add(objectsTreePanel); + } + else if (showElementsTable) { + mainPanel.add(elementsTablePanel); + } + else if (showAttributesTable) { + mainPanel.add(attributesTablePanel); + } + else { + // The actions should not allow this, but in case it happens, help the user out. + mainPanel.add(new JLabel(""" + Well, you should probably enable at least one panel. + Use the local toolbar buttons.""")); + } + } + mainPanel.revalidate(); + } + protected void buildMainPanel() { + mainPanel = new JPanel(new BorderLayout()); pathField = new MyTextField(); pathField.setInputVerifier(new InputVerifier() { @Override @@ -207,27 +470,12 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable tbSplit.setResizeWeight(0.7); lrSplit.setRightComponent(tbSplit); - JPanel queryPanel = new JPanel(new BorderLayout()); + queryPanel = new JPanel(new BorderLayout()); queryPanel.add(new JLabel("Path: "), BorderLayout.WEST); queryPanel.add(pathField, BorderLayout.CENTER); queryPanel.add(goButton, BorderLayout.EAST); - JPanel labeledElementsTablePanel = new JPanel(new BorderLayout()); - labeledElementsTablePanel.add(elementsTablePanel); - labeledElementsTablePanel.add(new JLabel("Elements"), BorderLayout.NORTH); - - JPanel labeledAttributesTablePanel = new JPanel(new BorderLayout()); - labeledAttributesTablePanel.add(attributesTablePanel); - labeledAttributesTablePanel.add(new JLabel("Attributes"), BorderLayout.NORTH); - - lrSplit.setLeftComponent(objectsTreePanel); - tbSplit.setLeftComponent(labeledElementsTablePanel); - tbSplit.setRightComponent(labeledAttributesTablePanel); - - mainPanel.add(queryPanel, BorderLayout.NORTH); - mainPanel.add(lrSplit, BorderLayout.CENTER); - objectsTreePanel.addTreeSelectionListener(evt -> { Trace trace = current.getTrace(); if (trace == null) { @@ -240,6 +488,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable if (!sel.isEmpty()) { myActionContext = new DebuggerObjectActionContext(sel.stream() .map(n -> n.getValue()) + .filter(o -> o != null) // Root for no trace would return null .collect(Collectors.toList()), this, objectsTreePanel); } @@ -251,7 +500,8 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable return; } TraceObjectValue value = sel.get(0).getValue(); - setPath(value.getCanonicalPath(), objectsTreePanel); + setPath(value == null ? TraceObjectKeyPath.of() : value.getCanonicalPath(), + objectsTreePanel); }); objectsTreePanel.tree.addMouseListener(new MouseAdapter() { @Override @@ -320,6 +570,8 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable elementsTablePanel.addSeekListener(seekListener); attributesTablePanel.addSeekListener(seekListener); + + rebuildPanels(); } private void activateObjectSelectedInTree() { @@ -348,6 +600,18 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable .enabledWhen(c -> current.getTrace() != null) .onAction(c -> activatedCloneWindow()) .buildAndInstallLocal(this); + actionShowObjectsTree = ShowObjectsTreeAction.builder(plugin) + .onAction(this::toggledShowObjectsTree) + .selected(showObjectsTree) + .buildAndInstallLocal(this); + actionShowElementsTable = ShowElementsTableAction.builder(plugin) + .onAction(this::toggledShowElementsTable) + .selected(showElementsTable) + .buildAndInstallLocal(this); + actionShowAttributesTable = ShowAttributesTableAction.builder(plugin) + .onAction(this::toggledShowAttributesTable) + .selected(showAttributesTable) + .buildAndInstallLocal(this); actionLimitToCurrentSnap = LimitToCurrentSnapAction.builder(plugin) .onAction(this::toggledLimitToCurrentSnap) .buildAndInstallLocal(this); @@ -404,6 +668,18 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable plugin.getTool().showComponentProvider(clone, true); } + private void toggledShowObjectsTree(ActionContext ctx) { + setShowObjectsTree(actionShowObjectsTree.isSelected()); + } + + private void toggledShowElementsTable(ActionContext ctx) { + setShowElementsTable(actionShowElementsTable.isSelected()); + } + + private void toggledShowAttributesTable(ActionContext ctx) { + setShowAttributesTable(actionShowAttributesTable.isSelected()); + } + private void toggledLimitToCurrentSnap(ActionContext ctx) { setLimitToCurrentSnap(actionLimitToCurrentSnap.isSelected()); } @@ -496,8 +772,6 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable elementsTablePanel.goToCoordinates(coords); attributesTablePanel.goToCoordinates(coords); - checkPath(); - if (isClone) { return; } @@ -549,6 +823,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable traceManager.activateObject(object); return; } + plugin.getTool().setStatusInfo("No such object at path " + path, true); } } @@ -566,27 +841,55 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable objectsTreePanel.repaint(); elementsTablePanel.setQuery(ModelQuery.elementsOf(path)); attributesTablePanel.setQuery(ModelQuery.attributesOf(path)); - - checkPath(); } public void setPath(TraceObjectKeyPath path) { setPath(path, null); } - protected void checkPath() { - if (Trace.isLegacy(current.getTrace())) { - return; - } - if (objectsTreePanel.getNode(path) == null) { - plugin.getTool().setStatusInfo("No such object at path " + path, true); - } - } - public TraceObjectKeyPath getPath() { return path; } + protected void doSetShowObjectsTree(boolean showObjectsTree) { + this.showObjectsTree = showObjectsTree; + actionShowObjectsTree.setSelected(showObjectsTree); + rebuildPanels(); + } + + protected void doSetShowElementsTable(boolean showElementsTable) { + this.showElementsTable = showElementsTable; + actionShowElementsTable.setSelected(showElementsTable); + rebuildPanels(); + } + + protected void doSetShowAttributesTable(boolean showAttributesTable) { + this.showAttributesTable = showAttributesTable; + actionShowAttributesTable.setSelected(showAttributesTable); + rebuildPanels(); + } + + public void setShowObjectsTree(boolean showObjectsTree) { + if (this.showObjectsTree == showObjectsTree) { + return; + } + doSetShowObjectsTree(showObjectsTree); + } + + public void setShowElementsTable(boolean showElementsTable) { + if (this.showElementsTable == showElementsTable) { + return; + } + doSetShowElementsTable(showElementsTable); + } + + public void setShowAttributesTable(boolean showAttributesTable) { + if (this.showAttributesTable == showAttributesTable) { + return; + } + doSetShowAttributesTable(showAttributesTable); + } + protected void doSetLimitToCurrentSnap(boolean limitToSnap) { this.limitToSnap = limitToSnap; actionLimitToCurrentSnap.setSelected(limitToSnap); @@ -680,6 +983,12 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable @Override public void readConfigState(SaveState saveState) { CONFIG_STATE_HANDLER.readConfigState(this, saveState); + + actionShowObjectsTree.setSelected(showObjectsTree); + actionShowElementsTable.setSelected(showElementsTable); + actionShowAttributesTable.setSelected(showAttributesTable); + rebuildPanels(); + doSetLimitToCurrentSnap(limitToSnap); doSetShowHidden(showHidden); doSetShowPrimitivesInTree(showPrimitivesInTree); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ListenerSuppressor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ListenerSuppressor.java new file mode 100644 index 0000000000..8bd713dd6b --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ListenerSuppressor.java @@ -0,0 +1,21 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.gui.model; + +public interface ListenerSuppressor extends AutoCloseable { + @Override + void close(); +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java index a4d82dd1a6..926c7ae10c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java @@ -22,6 +22,8 @@ import java.util.stream.*; import javax.swing.JPanel; import javax.swing.JTree; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; import javax.swing.tree.TreePath; import docking.widgets.tree.GTree; @@ -95,10 +97,55 @@ public class ObjectsTreePanel extends JPanel { } } + protected class ListenerForShowing implements AncestorListener { + boolean showing = false; + + @Override + public void ancestorRemoved(AncestorEvent event) { + updateShowing(); + } + + @Override + public void ancestorMoved(AncestorEvent event) { + updateShowing(); + } + + @Override + public void ancestorAdded(AncestorEvent event) { + updateShowing(); + } + + public void updateShowing() { + setShowing(ObjectsTreePanel.this.isShowing()); + } + + private void setShowing(boolean showing) { + if (this.showing == showing) { + return; + } + this.showing = showing; + showingChanged(showing); + } + } + + protected class ListenerForShowingSuppressor implements ListenerSuppressor { + public ListenerForShowingSuppressor() { + removeAncestorListener(listenerForShowing); + } + + @Override + public void close() { + addAncestorListener(listenerForShowing); + listenerForShowing.updateShowing(); + } + } + protected final ObjectTreeModel treeModel; protected final ObjectGTree tree; + protected boolean showing = false; protected DebuggerCoordinates current = DebuggerCoordinates.NOWHERE; + protected DebuggerCoordinates previous = DebuggerCoordinates.NOWHERE; protected boolean limitToSnap = true; protected boolean showHidden = false; protected boolean showPrimitives = false; @@ -107,8 +154,13 @@ public class ObjectsTreePanel extends JPanel { protected Color diffColor = DebuggerResources.COLOR_VALUE_CHANGED; protected Color diffColorSel = DebuggerResources.COLOR_VALUE_CHANGED_SEL; + protected final ListenerForShowing listenerForShowing = new ListenerForShowing(); + public ObjectsTreePanel() { super(new BorderLayout()); + + addAncestorListener(listenerForShowing); + treeModel = createModel(); tree = new ObjectGTree(treeModel.getRoot()); @@ -116,6 +168,10 @@ public class ObjectsTreePanel extends JPanel { add(tree, BorderLayout.CENTER); } + public ListenerSuppressor suppressShowingListener() { + return new ListenerForShowingSuppressor(); + } + protected ObjectTreeModel createModel() { return new ObjectTreeModel(); } @@ -124,6 +180,20 @@ public class ObjectsTreePanel extends JPanel { return new KeepTreeState(tree); } + protected void showingChanged(boolean showing) { + this.showing = showing; + updateTreeModelForCoordinates(); + updateTreeModelForSpan(); + updateTreeModelForShowHidden(); + updateTreeModelForShowPrimitives(); + updateTreeModelForShowMethods(); + if (showing) { + // Not going to restore the actual selection + selectCurrent(); + } + // Restore expansion? Nah. + } + protected Trace computeDiffTrace(Trace current, Trace previous) { if (current == null) { return null; @@ -138,13 +208,22 @@ public class ObjectsTreePanel extends JPanel { if (DebuggerCoordinates.equalsIgnoreRecorderAndView(current, coords)) { return; } - DebuggerCoordinates previous = current; - this.current = coords; + previous = current; + current = coords; if (previous.getSnap() == current.getSnap() && previous.getTrace() == current.getTrace() && previous.getObject() == current.getObject()) { return; } + updateTreeModelForCoordinates(); + } + + protected void updateTreeModelForCoordinates() { + if (!showing) { + // Clear it out and have it remove its listeners + treeModel.setTrace(null); + return; + } try (KeepTreeState keep = keepTreeState()) { treeModel.setDiffTrace(computeDiffTrace(current.getTrace(), previous.getTrace())); treeModel.setTrace(current.getTrace()); @@ -163,6 +242,13 @@ public class ObjectsTreePanel extends JPanel { return; } this.limitToSnap = limitToSnap; + updateTreeModelForSpan(); + } + + protected void updateTreeModelForSpan() { + if (!showing) { + return; + } try (KeepTreeState keep = keepTreeState()) { treeModel.setSpan(limitToSnap ? Lifespan.at(current.getSnap()) : Lifespan.ALL); } @@ -177,6 +263,13 @@ public class ObjectsTreePanel extends JPanel { return; } this.showHidden = showHidden; + updateTreeModelForShowHidden(); + } + + protected void updateTreeModelForShowHidden() { + if (!showing) { + return; + } try (KeepTreeState keep = keepTreeState()) { treeModel.setShowHidden(showHidden); } @@ -191,6 +284,13 @@ public class ObjectsTreePanel extends JPanel { return; } this.showPrimitives = showPrimitives; + updateTreeModelForShowPrimitives(); + } + + protected void updateTreeModelForShowPrimitives() { + if (!showing) { + return; + } try (KeepTreeState keep = keepTreeState()) { treeModel.setShowPrimitives(showPrimitives); } @@ -205,6 +305,13 @@ public class ObjectsTreePanel extends JPanel { return; } this.showMethods = showMethods; + updateTreeModelForShowMethods(); + } + + protected void updateTreeModelForShowMethods() { + if (!showing) { + return; + } try (KeepTreeState keep = keepTreeState()) { treeModel.setShowMethods(showMethods); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java index aad2a60ee3..ed7ecad5ff 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java @@ -28,12 +28,12 @@ import org.apache.commons.lang3.ArrayUtils; import docking.*; import docking.action.*; -import docking.action.builder.ActionBuilder; -import docking.action.builder.MultiStateActionBuilder; +import docking.action.builder.*; import docking.menu.ActionState; import docking.menu.MultiStateDockingAction; import docking.widgets.EventTrigger; import docking.widgets.filechooser.GhidraFileChooser; +import generic.theme.GIcon; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerBlockChooserDialog; import ghidra.app.plugin.core.debug.gui.DebuggerResources; @@ -217,6 +217,58 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { } } + interface ImportMissingModuleAction { + String NAME = "Import Missing Module"; + String DESCRIPTION = "Import the missing module from disk"; + Icon ICON = DebuggerResources.ICON_IMPORT; + String HELP_ANCHOR = "import_missing_module"; + + static ActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .toolBarIcon(ICON) + .popupMenuIcon(ICON) + .popupMenuPath(NAME) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface MapMissingModuleAction { + String NAME = "Map Missing Module"; + String DESCRIPTION = "Map the missing module to an existing import"; + Icon ICON = DebuggerResources.ICON_MAP_MODULES; + String HELP_ANCHOR = "map_missing_module"; + + static ActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .toolBarIcon(ICON) + .popupMenuIcon(ICON) + .popupMenuPath(NAME) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + + interface ShowSectionsTableAction { + String NAME = "Show Sections Table"; + Icon ICON = new GIcon("icon.debugger.modules.table.sections"); + String DESCRIPTION = "Toggle display fo the Sections Table pane"; + String GROUP = DebuggerResources.FilterAction.GROUP; + String ORDER = "1"; + String HELP_ANCHOR = "show_sections_table"; + + static ToggleActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ToggleActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .toolBarIcon(ICON) + .toolBarGroup(GROUP, ORDER) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + protected class ForMappingTraceListener extends TraceDomainObjectListener { public ForMappingTraceListener(AutoMapSpec spec) { for (TraceChangeType type : spec.getChangeTypes()) { @@ -369,6 +421,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { private final AutoService.Wiring autoServiceWiring; private final JSplitPane mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + private final int defaultDividerSize = mainPanel.getDividerSize(); DebuggerModulesPanel modulesPanel; DebuggerLegacyModulesPanel legacyModulesPanel; @@ -397,8 +450,14 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { MultiStateDockingAction actionAutoMap; private final AutoMapSpec defaultAutoMapSpec = AutoMapSpec.fromConfigName(ByModuleAutoMapSpec.CONFIG_NAME); + @AutoConfigStateField(codec = AutoMapSpecConfigFieldCodec.class) AutoMapSpec autoMapSpec = defaultAutoMapSpec; + @AutoConfigStateField + boolean showSectionsTable = true; + @AutoConfigStateField + boolean filterSectionsByModules = false; + boolean cueAutoMap; private ForMappingTraceListener forMappingListener; @@ -407,6 +466,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { SelectAddressesAction actionSelectAddresses; ImportFromFileSystemAction actionImportFromFileSystem; + ToggleDockingAction actionShowSectionsTable; // TODO: Save the state of this toggle? Not really compelled. ToggleDockingAction actionFilterSectionsByModules; DockingAction actionSelectCurrent; @@ -481,8 +541,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { } protected boolean isFilterSectionsByModules() { - // TODO: Make this a proper field and save it to tool state - return actionFilterSectionsByModules.isSelected(); + return filterSectionsByModules; } void modulesPanelContextChanged() { @@ -576,6 +635,10 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { actionSelectAddresses = new SelectAddressesAction(); actionImportFromFileSystem = new ImportFromFileSystemAction(); + actionShowSectionsTable = ShowSectionsTableAction.builder(plugin) + .onAction(this::toggledShowSectionsTable) + .selected(showSectionsTable) + .buildAndInstallLocal(this); actionFilterSectionsByModules = FilterAction.builder(plugin) .description("Filter sections to those in selected modules") .helpLocation(new HelpLocation(plugin.getName(), "filter_by_module")) @@ -714,10 +777,42 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { mapModuleTo(context.getModule()); } + private void toggledShowSectionsTable(ActionContext ignored) { + setShowSectionsTable(actionShowSectionsTable.isSelected()); + } + + public void setShowSectionsTable(boolean showSectionsTable) { + if (this.showSectionsTable == showSectionsTable) { + return; + } + doSetShowSectionsTable(showSectionsTable); + } + + protected void doSetShowSectionsTable(boolean showSectionsTable) { + this.showSectionsTable = showSectionsTable; + actionShowSectionsTable.setSelected(showSectionsTable); + mainPanel.setDividerSize(showSectionsTable ? defaultDividerSize : 0); + sectionsPanel.setVisible(showSectionsTable); + legacySectionsPanel.setVisible(showSectionsTable); + mainPanel.resetToPreferredSizes(); + } + private void toggledFilter(ActionContext ignored) { - boolean filtered = isFilterSectionsByModules(); - sectionsPanel.setFilteredBySelectedModules(filtered); - legacySectionsPanel.setFilteredBySelectedModules(filtered); + setFilterSectionsByModules(actionFilterSectionsByModules.isSelected()); + } + + public void setFilterSectionsByModules(boolean filterSectionsByModules) { + if (this.filterSectionsByModules == filterSectionsByModules) { + return; + } + doSetFilterSectionsByModules(filterSectionsByModules); + } + + protected void doSetFilterSectionsByModules(boolean filterSectionsByModules) { + this.filterSectionsByModules = filterSectionsByModules; + actionFilterSectionsByModules.setSelected(filterSectionsByModules); + sectionsPanel.setFilteredBySelectedModules(filterSectionsByModules); + legacySectionsPanel.setFilteredBySelectedModules(filterSectionsByModules); } private void activatedSelectCurrent(ActionContext ignored) { @@ -1111,5 +1206,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter { public void readConfigState(SaveState saveState) { CONFIG_STATE_HANDLER.readConfigState(this, saveState); actionAutoMap.setCurrentActionStateByUserData(autoMapSpec); + doSetFilterSectionsByModules(filterSectionsByModules); + doSetShowSectionsTable(showSectionsTable); } } diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/table-a.png b/Ghidra/Debug/Debugger/src/main/resources/images/table-a.png new file mode 100644 index 0000000000000000000000000000000000000000..0847802ca99a2e3e2842c85defd0ea70708ea5b3 GIT binary patch literal 696 zcmV;p0!RIcP)h%!?S{h1U~(#U-Gp3`FDn-Sbd zFI+B{7oL0Wd(MUblml?{@y~Oj9J4BGLLK=xg=6Ew1PfuJO(R>9~0O%f`@}V6aog@+o0NOe_$&_#Ry#rfUkTn(`VEzFB z!aG;f)gLDw9iq9p8OL#W`)Y{Y^+IK3B>>aEX7LIEo)tuuU=j2@pJMo755^eW9s6*A zSS-ewi`O_9K2C8EHdHSm>)Cvwm-`ifG&b2)%9C{P>>Fgg-tC_c?DH$RxYFO<)zq<l*v6$r78UHVAtX{-eDovq8RPuvl!0000){Z=v$znJX=C;~+40mkN@p+upy!WD(9-O8+0Id&`j0N2$u9)JPhQrk5EqR}Xc zL}FdWNF+im76V{4uW*&70?-1$=)?j5UuM#z(`h<`z3i@Y0Lay}l;alybrie+RRRDE z9^J?N(mM8Nn4X><;*%K$LJj3JODp;H4qPpWG1wNw-!GJU182^j$8T8xjNcfdum1{F zHXLl;LBZW{1q0ZYkBdQvJEcsgP9CSRv5~pV8qw27IMH2nG5dW5rDVMWBVhaC^~Xhg zB?qB%SLq6bS^X>g0>%;<1ZZp8hQSL^f-#0F3;GT_43z+H;!pTG5odlmPdL!Q$i3SD zBvV;xYiv&urAV{2Jki zF|Dt!SJ!pbbzKdI!y1i73;smb)1y}tT1;RCfR4Uz0Gw=T;pdN0wl^PU-u!Y>pFZ&D zRg$umd4E?!IXPpC^;~&4tP@#JZ@w7O$;nA|97jW;kPZzEsj6z{>#sUp=}cA+Ug|F< z@T?%J1pLtTHp7~RjlApcr79GnG8CqE{Z_`ujFMdA>((_=?_w|@ + + + + + image/svg+xml + + + + + + + + A + diff --git a/Ghidra/Debug/Debugger/src/main/svg/table-e.svg b/Ghidra/Debug/Debugger/src/main/svg/table-e.svg new file mode 100644 index 0000000000..4798d8ec7c --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/svg/table-e.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + E + diff --git a/Ghidra/Debug/Debugger/src/main/svg/table-s.svg b/Ghidra/Debug/Debugger/src/main/svg/table-s.svg new file mode 100644 index 0000000000..49d8253c33 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/svg/table-s.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + S + diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java index 3bdc1a709a..d30dfa9a9a 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java @@ -117,6 +117,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest public void setUpModelProviderTest() throws Exception { modelPlugin = addPlugin(tool, DebuggerModelPlugin.class); modelProvider = waitForComponentProvider(DebuggerModelProvider.class); + modelProvider.setLimitToCurrentSnap(false); } @After @@ -343,12 +344,12 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest } @Test - public void testSetPathNoExist() throws Throwable { + public void testActivatePathNoExist() throws Throwable { createTraceAndPopulateObjects(); traceManager.activateTrace(tb.trace); waitForSwing(); - modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].NoSuch")); + modelProvider.activatePath(TraceObjectKeyPath.parse("Processes[0].NoSuch")); waitForTasks(); assertEquals("No such object at path Processes[0].NoSuch", tool.getStatusInfo());