GP-2140 - Xrefs - Added an action to delete xrefs from the xrefs table

This commit is contained in:
dragonmacher 2025-06-16 13:46:34 -04:00
parent e804e1a01d
commit 1fc7356080
35 changed files with 1032 additions and 278 deletions

View file

@ -133,6 +133,19 @@
and a preview of the instruction at that address. Clicking on any row in the table will cause and a preview of the instruction at that address. Clicking on any row in the table will cause
the browser to navigate to that address</P> the browser to navigate to that address</P>
<H3><A NAME="Refresh"></A>Refresh
<IMG border="0" src="Icons.REFRESH_ICON" alt=""></H3>
<BLOCKQUOTE>
<P>
This action will refresh the table of references. This button will appear disabled
when the data is not stale. However, if Ghidra detects that the data <I>may</I> be
stale, then the button will become color filled, as it is here. You may push the
button for a refresh in either state.
</P>
</BLOCKQUOTE>
<H3><A NAME="Show_Thunk_Xrefs"></A>Show Thunk Xrefs <H3><A NAME="Show_Thunk_Xrefs"></A>Show Thunk Xrefs
<IMG border="0" src="images/ThunkFunction.gif" alt=""></H3> <IMG border="0" src="images/ThunkFunction.gif" alt=""></H3>
@ -148,6 +161,17 @@
</P> </P>
</BLOCKQUOTE> </BLOCKQUOTE>
<H3><A NAME="Delete_Reference"></A>Delete Reference
<IMG border="0" src="Icons.DELETE_ICON" alt=""></H3>
<BLOCKQUOTE>
<P>
This action will delete all selected references from the database. This differs
from the <A HREF="help/topics/Search/Query_Results_Dialog.htm#Remove_Items">Remove Items
</A> action, which will simply remove items from the table.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A NAME="Keyboard_Controls">Keyboard Controls</H2> <H2><A NAME="Keyboard_Controls">Keyboard Controls</H2>

View file

@ -126,12 +126,6 @@
table. You may also access this feature by right-clicking an item in the table and table. You may also access this feature by right-clicking an item in the table and
selecting <B>Make Selection</B>.</P> selecting <B>Make Selection</B>.</P>
<P align="left"><A name="Highlight"></A>The <IMG alt="" src="images/tag_yellow.png">
button toggles the highlighting of the matching references. In this case, the
term highlight refers to the background color of the item in the Listing and not
a <A HREF="help/topics/SetHighlightPlugin/Highlighting.htm">Program Selection
Highlight</A>.
</P>
<BLOCKQUOTE> <BLOCKQUOTE>
<P> <P>
<IMG alt="" src="help/shared/tip.png"> <IMG alt="" src="help/shared/tip.png">
@ -145,6 +139,26 @@
</P> </P>
</BLOCKQUOTE> </BLOCKQUOTE>
<P align="left"><A name="Highlight"></A>The <IMG alt="" src="images/tag_yellow.png">
button toggles the highlighting of the matching references. In this case, the
term highlight refers to the background color of the item in the Listing and not
a <A HREF="help/topics/SetHighlightPlugin/Highlighting.htm">Program Selection
Highlight</A>.
</P>
<P align="left"><A name="Delete_Reference"></A>The <IMG alt="" src="Icons.DELETE_ICON">
button deletes the selected reference(s). This will delete the reference from the database.
</P>
<BLOCKQUOTE>
<P>
<IMG alt="" src="help/shared/note.yellow.png">Not all table rows can be deleted. This is
because the table shows not just items that have a database reference, but also items
that are dynamic references, as well as general uses of an item, such as a data access
generated by the Decompiler. The delete action will only allow you to delete references
that are in the database.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE> </BLOCKQUOTE>
<H3>Search Results</H3> <H3>Search Results</H3>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

View file

@ -164,10 +164,22 @@
</BLOCKQUOTE> </BLOCKQUOTE>
</BLOCKQUOTE> </BLOCKQUOTE>
<H4><B>Provided by:</B> <I>Symbol Table</I> plugin</H4>
<P><BR> <H2><A name="Delete_Reference"></A>Delete Reference<IMG src="Icons.DELETE_ICON"></H2>
<BLOCKQUOTE>
<P>This action will delete all selected references from the database.
</P> </P>
</BLOCKQUOTE>
<P>
<BR> <BR>
<BR>
<P class="providedbyplugin">Provided by: <I>Symbol Table Plugin</I></P>
<P class="relatedtopic">Related Topics</P>
<UL>
<LI><A href="help/topics/SymbolTablePlugin/symbol_table.htm">Symbol Table</A></LI>
</UL>
</BODY> </BODY>
</HTML> </HTML>

View file

@ -164,6 +164,41 @@
</BLOCKQUOTE> </BLOCKQUOTE>
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A name="Make_Selection"></A>Making a Selection <IMG src="Icons.MAKE_SELECTION_ICON"></H2>
<BLOCKQUOTE>
<P>You can make a selection that corresponds to the symbol addresses that are selected in the
<I>Symbol Table</I>.</P>
<P>To make a selection:</P>
<OL>
<LI>Select the symbols in the Symbol Table (hold the &lt;Ctrl&gt; key down to add to the
selection) to be added to the selection.</LI>
<LI>
Right-mouse-click and select "Make Selection" from the popup menu.
<UL>
<LI>Or, click the <IMG src="Icons.MAKE_SELECTION_ICON"> button in the <I>Symbol
Table</I> toolbar.</LI>
</UL>
</LI>
</OL>
</BLOCKQUOTE>
<H2><A name="Navigate_on_Incoming_Location_Changes"></A>
Making a Selection <IMG src="Icons.MAKE_SELECTION_ICON"></H2>
<BLOCKQUOTE>
<P>When selected, the Symbol Table will select the row in the table that corresponds to the
symbol selected in the <A href="help/topics/CodeBrowserPlugin/CodeBrowser.htm">Listing</A>.
</P>
</BLOCKQUOTE>
<H2>Renaming a Symbol</H2> <H2>Renaming a Symbol</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
@ -199,29 +234,6 @@
for more discussion on the use of the edit dialog). for more discussion on the use of the edit dialog).
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A name="Make_Selection"></A>Making a Selection <IMG src="Icons.MAKE_SELECTION_ICON"></H2>
<BLOCKQUOTE>
<P>You can make a selection that corresponds to the symbol addresses that are selected in the
<I>Symbol Table</I>.</P>
<P>To make a selection:</P>
<OL>
<LI>Select the symbols in the Symbol Table (hold the &lt;Ctrl&gt; key down to add to the
selection) to be added to the selection.</LI>
<LI>
Right-mouse-click and select "Make Selection" from the popup menu.
<UL>
<LI>Or, click the <IMG src="Icons.MAKE_SELECTION_ICON"> button in the <I>Symbol
Table</I> toolbar.</LI>
</UL>
</LI>
</OL>
</BLOCKQUOTE>
<H2><A name="Pinning a Symbol"></A>Pinning a Symbol</H2> <H2><A name="Pinning a Symbol"></A>Pinning a Symbol</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
@ -250,18 +262,16 @@
</LI> </LI>
</OL> </OL>
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A name="Set_Filter"></A>Filtering <IMG src="images/view-filter.png"></H2> <H2><A name="Set_Filter"></A>Filtering</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
<P>The list of displayed symbols is determined by the current symbol table settings. These <P>The list of displayed symbols is determined by the current symbol table settings. These
settings can be adjusted by clicking the <I>Filter</I> <IMG src="images/view-filter.png"> settings can be adjusted by clicking the <I>Filter</I> <IMG src="Icons.CONFIGURE_FILTER_ICON">
button in the toolbar of the <I>Symbol Table</I> window or from the right-mouse popup menu.. button in the toolbar of the <I>Symbol Table</I> window or from the right-mouse popup menu..
The displayed symbols will correspond to the selected checkboxes in the <I>Symbol Table The displayed symbols will correspond to the selected checkboxes in the <I>Symbol Table
Filter</I> dialog.</P> Filter</I> dialog.</P>
</BLOCKQUOTE> </BLOCKQUOTE>
<P align="center"><IMG src="images/view-filter.png"></P>
<P align="center"><I>Symbol Table Filter</I> Dialog</P> <P align="center"><I>Symbol Table Filter</I> Dialog</P>
<P align="center"><IMG src="images/Filter.png"></P> <P align="center"><IMG src="images/Filter.png"></P>
@ -606,7 +616,6 @@
</BLOCKQUOTE> </BLOCKQUOTE>
<P class="providedbyplugin">Provided by: <I>Symbol Table Plugin</I></P> <P class="providedbyplugin">Provided by: <I>Symbol Table Plugin</I></P>
<P class="relatedtopic">Related Topics</P> <P class="relatedtopic">Related Topics</P>
<UL> <UL>

View file

@ -454,7 +454,9 @@ public class CallTreeProvider extends ComponentProviderAdapter {
// navigate incoming nodes on selection // navigate incoming nodes on selection
// //
navigateIncomingAction = navigateIncomingAction =
new ToggleDockingAction("Navigation Incoming Location Changes", plugin.getName()) { new ToggleDockingAction("Navigation Incoming Location Changes", plugin.getName(),
KeyBindingType.SHARED) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
// handled later as we receive events // handled later as we receive events

View file

@ -336,9 +336,11 @@ class MemoryMapProvider extends ComponentProviderAdapter {
action.getToolBarData().setToolBarGroup("B"); // the other actions are in group 'A' action.getToolBarData().setToolBarGroup("B"); // the other actions are in group 'A'
tool.addLocalAction(this, action); tool.addLocalAction(this, action);
toggleNavigateAction = new ToggleActionBuilder("Memory Map Navigation", plugin.getName()) toggleNavigateAction =
new ToggleActionBuilder("Navigate on Incoming Location Changes", plugin.getName())
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON) .toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.selected(false) .selected(false)
.sharedKeyBinding()
.helpLocation(new HelpLocation("MemoryMapPlugin", "Navigation")) .helpLocation(new HelpLocation("MemoryMapPlugin", "Navigation"))
.description(HTMLUtilities.toHTML("Toggle <b>on</b> means to select the block" + .description(HTMLUtilities.toHTML("Toggle <b>on</b> means to select the block" +
" that contains the current location")) " that contains the current location"))

View file

@ -20,6 +20,7 @@ import static ghidra.app.plugin.core.navigation.locationreferences.LocationRefer
import java.util.Objects; import java.util.Objects;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.DynamicReference;
import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.Reference;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
@ -36,6 +37,11 @@ public class LocationReference implements Comparable<LocationReference> {
private final LocationReferenceContext context; private final LocationReferenceContext context;
private final ProgramLocation location; private final ProgramLocation location;
/**
* Optional reference object. Some clients do not have actual references.
*/
private Reference reference;
private int hashCode = -1; private int hashCode = -1;
private static String getRefType(Reference r) { private static String getRefType(Reference r) {
@ -57,6 +63,7 @@ public class LocationReference implements Comparable<LocationReference> {
LocationReference(Reference reference, boolean isOffcutReference) { LocationReference(Reference reference, boolean isOffcutReference) {
this(reference.getFromAddress(), null, getRefType(reference), EMPTY_CONTEXT, this(reference.getFromAddress(), null, getRefType(reference), EMPTY_CONTEXT,
isOffcutReference); isOffcutReference);
this.reference = reference;
} }
LocationReference(Address locationOfUseAddress, String refType, boolean isOffcutReference) { LocationReference(Address locationOfUseAddress, String refType, boolean isOffcutReference) {
@ -133,6 +140,28 @@ public class LocationReference implements Comparable<LocationReference> {
return location; return location;
} }
/**
* Returns the reference that this class is using. This may be null if there is no database
* reference associated with this object.
* @return the reference; may be null
*/
public Reference getReference() {
return reference;
}
/**
* Returns true if this class has a {@link Reference} and that reference is not dynamic (i.e.,
* the reference exists in the database).
*
* @return true if this class has a removable reference
*/
public boolean isDeletable() {
if (reference == null) {
return false;
}
return !(reference instanceof DynamicReference);
}
@Override @Override
public int hashCode() { public int hashCode() {
if (hashCode != -1) { if (hashCode != -1) {

View file

@ -18,7 +18,7 @@ package ghidra.app.plugin.core.navigation.locationreferences;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.Collection; import java.util.*;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
@ -88,6 +88,26 @@ public class LocationReferencesPanel extends JPanel {
tableModel.fullReload(); tableModel.fullReload();
} }
boolean isBusy() {
return tableModel.isBusy();
}
void remove(List<LocationReference> refs) {
for (LocationReference lr : refs) {
tableModel.removeObject(lr);
}
}
List<LocationReference> getSelectedReferences() {
List<LocationReference> list = new ArrayList<>();
int[] rows = table.getSelectedRows();
for (int row : rows) {
LocationReference lr = tableModel.getRowObject(row);
list.add(lr);
}
return list;
}
void addTableModelListener(TableModelListener listener) { void addTableModelListener(TableModelListener listener) {
tableModel.addTableModelListener(listener); tableModel.addTableModelListener(listener);
} }

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.navigation.locationreferences; package ghidra.app.plugin.core.navigation.locationreferences;
import java.util.*; import java.util.*;
import java.util.function.Supplier;
import docking.ActionContext; import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;
@ -133,7 +134,7 @@ public class LocationReferencesPlugin extends Plugin
return; // not sure if this can happen return; // not sure if this can happen
} }
Set<Reference> refs = XReferenceUtils.getAllXrefs(location); Supplier<Collection<Reference>> refs = () -> XReferenceUtils.getAllXrefs(location);
XReferenceUtils.showXrefs(lac.getNavigatable(), tool, service, location, refs); XReferenceUtils.showXrefs(lac.getNavigatable(), tool, service, location, refs);
} }

View file

@ -18,18 +18,21 @@ package ghidra.app.plugin.core.navigation.locationreferences;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import docking.ActionContext; import docking.ActionContext;
import docking.DefaultActionContext;
import docking.action.*; import docking.action.*;
import docking.action.builder.ActionBuilder;
import docking.widgets.table.GTable; import docking.widgets.table.GTable;
import generic.theme.GIcon; import generic.theme.GIcon;
import ghidra.app.cmd.refs.RemoveReferenceCmd;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.nav.NavigatableRemovalListener; import ghidra.app.nav.NavigatableRemovalListener;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.framework.cmd.CompoundCmd;
import ghidra.framework.model.DomainObjectChangedEvent; import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectListener; import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
@ -37,6 +40,7 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Reference;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
@ -97,8 +101,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
program.addListener(this); program.addListener(this);
setTitle(TITLE_PREFIX_REFERENCES); setTitle(TITLE_PREFIX_REFERENCES);
setHelpLocation( setHelpLocation(new HelpLocation(getOwner(), "LocationReferencesPlugin"));
new HelpLocation(locationReferencesPlugin.getName(), "LocationReferencesPlugin"));
setWindowMenuGroup("References"); setWindowMenuGroup("References");
setTransient(); setTransient();
createView(); createView();
@ -235,7 +238,8 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
private void createActions() { private void createActions() {
homeAction = new DockingAction("Home", locationReferencesPlugin.getName()) { String toolbarGroup = "A";
homeAction = new DockingAction("Home", getOwner()) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
goTo(currentLocationDescriptor.getHomeLocation(), goTo(currentLocationDescriptor.getHomeLocation(),
@ -243,7 +247,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
} }
}; };
homeAction.setToolBarData(new ToolBarData(HOME_ICON)); homeAction.setToolBarData(new ToolBarData(HOME_ICON, toolbarGroup));
updateHomeActionState(); updateHomeActionState();
selectionAction = selectionAction =
@ -256,17 +260,17 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
} }
}; };
highlightAction.setToolBarData(new ToolBarData(HIGHLIGHT_ICON)); highlightAction.setToolBarData(new ToolBarData(HIGHLIGHT_ICON, toolbarGroup));
highlightAction.setSelected(true); highlightAction.setSelected(true);
highlightAction.setDescription("Highlight matches in tool"); highlightAction.setDescription("Highlight matches in tool");
refreshAction = new DockingAction("Refresh", locationReferencesPlugin.getName()) { refreshAction = new DockingAction("Refresh", getOwner()) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
updateManager.updateNow(); updateManager.updateNow();
} }
}; };
refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON)); refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON, toolbarGroup));
refreshAction.setDescription( refreshAction.setDescription(
"<html>Push at any time to refresh the current table of references.<br>" + "<html>Push at any time to refresh the current table of references.<br>" +
"This button is highlighted when the data <i>may</i> be stale.<br>"); "This button is highlighted when the data <i>may</i> be stale.<br>");
@ -274,6 +278,31 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
SelectionNavigationAction selectionNavigationAction = SelectionNavigationAction selectionNavigationAction =
new SelectionNavigationAction(locationReferencesPlugin, referencesPanel.getTable()); new SelectionNavigationAction(locationReferencesPlugin, referencesPanel.getTable());
//@formatter:off
String actionName = "Delete Reference";
DockingAction deleteRefAction = new ActionBuilder(actionName, getOwner())
.sharedKeyBinding()
.toolBarIcon(Icons.DELETE_ICON)
.toolBarGroup(toolbarGroup)
.enabledWhen(c -> {
if (!(c instanceof LocationReferencesProviderContext lrContext)) {
return false;
}
if (referencesPanel.isBusy()) {
return false;
}
List<LocationReference> refs = lrContext.getDeletableReferences();
return !refs.isEmpty();
})
.onAction(c -> {
LocationReferencesProviderContext lrContext = (LocationReferencesProviderContext) c;
List<LocationReference> refs = lrContext.getDeletableReferences();
deleteRows(refs);
})
.build();
//@formatter:on
GhidraTable table = referencesPanel.getTable(); GhidraTable table = referencesPanel.getTable();
DockingAction removeItemsAction = new DeleteAction(tool, table); DockingAction removeItemsAction = new DeleteAction(tool, table);
removeItemsAction.setEnabled(false); // off by default; updated when the user clicks the table removeItemsAction.setEnabled(false); // off by default; updated when the user clicks the table
@ -281,12 +310,27 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
tool.addLocalAction(this, refreshAction); tool.addLocalAction(this, refreshAction);
tool.addLocalAction(this, selectionAction); tool.addLocalAction(this, selectionAction);
tool.addLocalAction(this, highlightAction); tool.addLocalAction(this, highlightAction);
tool.addLocalAction(this, deleteRefAction);
tool.addLocalAction(this, removeItemsAction); tool.addLocalAction(this, removeItemsAction);
tool.addLocalAction(this, selectionNavigationAction); tool.addLocalAction(this, selectionNavigationAction);
setHelpLocation(); setHelpLocation();
} }
private void deleteRows(List<LocationReference> refs) {
CompoundCmd<Program> compoundCmd = new CompoundCmd<>("Delete References");
for (LocationReference lr : refs) {
Reference ref = lr.getReference();
RemoveReferenceCmd cmd = new RemoveReferenceCmd(ref);
compoundCmd.add(cmd);
}
tool.execute(compoundCmd, program);
// also remove the object from the table, since they have been deleted
referencesPanel.remove(refs);
}
private void updateHomeActionState() { private void updateHomeActionState() {
// we have no home location sometimes, like when launched from the service interface when // we have no home location sometimes, like when launched from the service interface when
// looking for references to datatypes // looking for references to datatypes
@ -300,13 +344,10 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
} }
private void setHelpLocation() { private void setHelpLocation() {
homeAction.setHelpLocation(new HelpLocation(locationReferencesPlugin.getName(), "Home")); homeAction.setHelpLocation(new HelpLocation(getOwner(), "Home"));
refreshAction.setHelpLocation( refreshAction.setHelpLocation(new HelpLocation(getOwner(), "Refresh"));
new HelpLocation(locationReferencesPlugin.getName(), "Refresh")); selectionAction.setHelpLocation(new HelpLocation(getOwner(), "Select"));
selectionAction.setHelpLocation( highlightAction.setHelpLocation(new HelpLocation(getOwner(), "Highlight"));
new HelpLocation(locationReferencesPlugin.getName(), "Select"));
highlightAction.setHelpLocation(
new HelpLocation(locationReferencesPlugin.getName(), "Highlight"));
} }
private void goTo(ProgramLocation loc, Program theProgram) { private void goTo(ProgramLocation loc, Program theProgram) {
@ -318,7 +359,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
final JTable table = referencesPanel.getTable(); final JTable table = referencesPanel.getTable();
table.getSelectionModel().addListSelectionListener(e -> { table.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) { if (!e.getValueIsAdjusting()) {
selectionAction.setEnabled(table.getSelectedRowCount() > 0); contextChanged();
} }
}); });
@ -361,6 +402,15 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
Program getProgram() { Program getProgram() {
return program; return program;
} }
GTable getTable() {
return referencesPanel.getTable();
}
LocationReferencesPanel getPanel() {
return referencesPanel;
}
//================================================================================================== //==================================================================================================
// Interface methods // Interface methods
//================================================================================================== //==================================================================================================
@ -403,7 +453,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
return new DefaultActionContext(this, referencesPanel.getTable()); return new LocationReferencesProviderContext(this);
} }
//================================================================================================== //==================================================================================================
@ -413,7 +463,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
private class DeleteAction extends DeleteTableRowAction { private class DeleteAction extends DeleteTableRowAction {
DeleteAction(PluginTool tool, GTable table) { DeleteAction(PluginTool tool, GTable table) {
super(table, locationReferencesPlugin.getName()); super(table, LocationReferencesProvider.this.getOwner());
} }
@Override @Override

View file

@ -0,0 +1,56 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.navigation.locationreferences;
import java.util.ArrayList;
import java.util.List;
import docking.ActionContext;
import docking.DefaultActionContext;
/**
* An {@link ActionContext} for the {@link LocationReferencesProvider}.
*/
public class LocationReferencesProviderContext extends DefaultActionContext {
private LocationReferencesProvider locationProvider;
private List<LocationReference> selectedReferences;
public LocationReferencesProviderContext(LocationReferencesProvider provider) {
super(provider, provider.getTable());
this.locationProvider = provider;
}
public List<LocationReference> getSelectedReferences() {
if (selectedReferences == null) {
LocationReferencesPanel panel = locationProvider.getPanel();
selectedReferences = panel.getSelectedReferences();
}
return selectedReferences;
}
public List<LocationReference> getDeletableReferences() {
List<LocationReference> results = new ArrayList<>();
List<LocationReference> refs = getSelectedReferences();
for (LocationReference lr : refs) {
if (lr.isDeletable()) {
results.add(lr);
}
}
return results;
}
}

View file

@ -1395,7 +1395,7 @@ public final class ReferenceUtils {
return; return;
} }
Address[] thunkAddrs = func.getFunctionThunkAddresses(); Address[] thunkAddrs = func.getFunctionThunkAddresses(false);
if (thunkAddrs == null) { if (thunkAddrs == null) {
return; return;
} }

View file

@ -16,6 +16,8 @@
package ghidra.app.plugin.core.symtable; package ghidra.app.plugin.core.symtable;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
@ -23,10 +25,13 @@ import javax.swing.JComponent;
import docking.ActionContext; import docking.ActionContext;
import docking.WindowPosition; import docking.WindowPosition;
import generic.theme.GIcon; import generic.theme.GIcon;
import ghidra.app.cmd.refs.RemoveReferenceCmd;
import ghidra.app.context.ProgramActionContext; import ghidra.app.context.ProgramActionContext;
import ghidra.app.util.SymbolInspector; import ghidra.app.util.SymbolInspector;
import ghidra.framework.cmd.CompoundCmd;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.Swing; import ghidra.util.Swing;
@ -72,7 +77,21 @@ class ReferenceProvider extends ComponentProviderAdapter {
if (program == null) { if (program == null) {
return null; return null;
} }
return new ProgramActionContext(this, program);
List<Reference> selectedReferences = getSelectedReferences();
return new ReferenceTableContext(this, selectedReferences);
}
private List<Reference> getSelectedReferences() {
List<Reference> list = new ArrayList<>();
GhidraTable table = getTable();
int[] rows = table.getSelectedRows();
for (int row : rows) {
Reference ref = referenceKeyModel.getRowObject(row);
list.add(ref);
}
return list;
} }
void setCurrentSymbol(Symbol symbol) { void setCurrentSymbol(Symbol symbol) {
@ -104,6 +123,10 @@ class ReferenceProvider extends ComponentProviderAdapter {
} }
} }
Program getProgram() {
return referenceKeyModel.getProgram();
}
void reload() { void reload() {
if (isVisible()) { if (isVisible()) {
referenceKeyModel.reload(); referenceKeyModel.reload();
@ -126,6 +149,17 @@ class ReferenceProvider extends ComponentProviderAdapter {
return referencePanel.getTable(); return referencePanel.getTable();
} }
void deleteRows(List<Reference> refs) {
CompoundCmd<Program> compoundCmd = new CompoundCmd<>("Delete References");
for (Reference ref : refs) {
RemoveReferenceCmd cmd = new RemoveReferenceCmd(ref);
compoundCmd.add(cmd);
referenceKeyModel.removeObject(ref);
}
tool.execute(compoundCmd, getProgram());
}
private String generateSubTitle() { private String generateSubTitle() {
return "(" + referenceKeyModel.getDescription() + ")"; return "(" + referenceKeyModel.getDescription() + ")";
} }

View file

@ -0,0 +1,35 @@
/* ###
* 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.symtable;
import java.util.List;
import ghidra.app.context.ProgramActionContext;
import ghidra.program.model.symbol.Reference;
public class ReferenceTableContext extends ProgramActionContext {
private List<Reference> references;
ReferenceTableContext(ReferenceProvider provider, List<Reference> references) {
super(provider, provider.getProgram(), provider.getTable());
this.references = references;
}
List<Reference> getSelectedReferences() {
return references;
}
}

View file

@ -32,7 +32,11 @@ import docking.widgets.table.RowFilterTransformer;
import ghidra.app.plugin.core.symtable.AbstractSymbolTableModel.OriginalNameColumn; import ghidra.app.plugin.core.symtable.AbstractSymbolTableModel.OriginalNameColumn;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.util.ProgramLocation;
import ghidra.util.table.*; import ghidra.util.table.*;
class SymbolPanel extends JPanel { class SymbolPanel extends JPanel {
@ -41,9 +45,9 @@ class SymbolPanel extends JPanel {
private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS"; private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS";
private SymbolProvider symProvider; private SymbolProvider symbolProvider;
private SymbolTableModel tableModel; private SymbolTableModel symbolModel;
private GhidraTable symTable; private GhidraTable gTable;
private TableModelListener listener; private TableModelListener listener;
private FilterDialog filterDialog; private FilterDialog filterDialog;
private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel; private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel;
@ -51,34 +55,31 @@ class SymbolPanel extends JPanel {
SymbolPanel(SymbolProvider provider, SymbolTableModel model, SymbolRenderer renderer, SymbolPanel(SymbolProvider provider, SymbolTableModel model, SymbolRenderer renderer,
PluginTool tool) { PluginTool tool) {
super(new BorderLayout()); super(new BorderLayout());
this.symProvider = provider; this.symbolProvider = provider;
this.tableModel = model; this.symbolModel = model;
this.threadedTablePanel = new GhidraThreadedTablePanel<>(model);
this.listener = e -> symbolProvider.updateTitle();
threadedTablePanel = new GhidraThreadedTablePanel<>(model); gTable = threadedTablePanel.getTable();
gTable.setAutoLookupColumn(AbstractSymbolTableModel.LABEL_COL);
this.listener = e -> symProvider.updateTitle(); gTable.setRowSelectionAllowed(true);
gTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
symTable = threadedTablePanel.getTable(); gTable.getModel().addTableModelListener(listener);
symTable.setAutoLookupColumn(AbstractSymbolTableModel.LABEL_COL); gTable.getSelectionModel().addListSelectionListener(e -> {
symTable.setRowSelectionAllowed(true);
symTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
symTable.getModel().addTableModelListener(listener);
symTable.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) { if (!e.getValueIsAdjusting()) {
handleTableSelection(); handleTableSelection();
tool.contextChanged(symProvider); tool.contextChanged(symbolProvider);
} }
}); });
symTable.setAccessibleNamePrefix("Symbol"); gTable.setAccessibleNamePrefix("Symbol");
symTable.installNavigation(tool); gTable.installNavigation(tool);
for (int i = 0; i < symTable.getColumnCount(); i++) { for (int i = 0; i < gTable.getColumnCount(); i++) {
TableColumn column = symTable.getColumnModel().getColumn(i); TableColumn column = gTable.getColumnModel().getColumn(i);
column.setCellRenderer(renderer); column.setCellRenderer(renderer);
if (column.getModelIndex() == AbstractSymbolTableModel.LABEL_COL) { if (column.getModelIndex() == AbstractSymbolTableModel.LABEL_COL) {
column.setCellEditor(new SymbolEditor()); column.setCellEditor(new SymbolEditor());
@ -91,11 +92,11 @@ class SymbolPanel extends JPanel {
filterDialog = new FilterDialog(tool); filterDialog = new FilterDialog(tool);
// enable dragging symbols out of the symbol table // enable dragging symbols out of the symbol table
new SymbolTableDragProvider(symTable, model); new SymbolTableDragProvider(gTable, model);
} }
private JPanel createFilterFieldPanel() { private JPanel createFilterFieldPanel() {
tableFilterPanel = new GhidraTableFilterPanel<>(symTable, tableModel); tableFilterPanel = new GhidraTableFilterPanel<>(gTable, symbolModel);
tableFilterPanel.setToolTipText("Filters the contents of the table on symbol " + tableFilterPanel.setToolTipText("Filters the contents of the table on symbol " +
"names that start with the given pattern"); "names that start with the given pattern");
@ -120,17 +121,35 @@ class SymbolPanel extends JPanel {
return tableFilterPanel; return tableFilterPanel;
} }
protected RowFilterTransformer<SymbolRowObject> updateRowDataTransformer(boolean nameOnly) { void locationChanged(ProgramLocation location) {
TableColumnModel columnModel = symTable.getColumnModel();
if (nameOnly) { Program program = location.getProgram();
return new NameOnlyRowTransformer(tableModel, columnModel); SymbolTable symbolTable = program.getSymbolTable();
Address address = location.getAddress();
Symbol primarySymbol = symbolTable.getPrimarySymbol(address);
if (primarySymbol == null) {
return;
} }
return new DefaultRowFilterTransformer<>(tableModel, columnModel);
SymbolRowObject rowObject = new SymbolRowObject(primarySymbol);
int index = symbolModel.getRowIndex(rowObject);
if (index >= 0) {
gTable.selectRow(index);
gTable.scrollToSelectedRow();
}
}
protected RowFilterTransformer<SymbolRowObject> updateRowDataTransformer(boolean nameOnly) {
TableColumnModel columnModel = gTable.getColumnModel();
if (nameOnly) {
return new NameOnlyRowTransformer(symbolModel, columnModel);
}
return new DefaultRowFilterTransformer<>(symbolModel, columnModel);
} }
void dispose() { void dispose() {
symTable.getModel().removeTableModelListener(listener); gTable.getModel().removeTableModelListener(listener);
symTable.dispose(); gTable.dispose();
threadedTablePanel.dispose(); threadedTablePanel.dispose();
tableFilterPanel.dispose(); tableFilterPanel.dispose();
filterDialog.dispose(); filterDialog.dispose();
@ -140,24 +159,24 @@ class SymbolPanel extends JPanel {
if (filterDialog == null) { if (filterDialog == null) {
return; return;
} }
if (symTable.isEditing()) { if (gTable.isEditing()) {
symTable.editingCanceled(null); gTable.editingCanceled(null);
} }
symProvider.setCurrentSymbol(null); symbolProvider.setCurrentSymbol(null);
symTable.clearSelection(); gTable.clearSelection();
filterDialog.adjustFilter(symProvider, tableModel); filterDialog.adjustFilter(symbolProvider, symbolModel);
} }
SymbolFilter getFilter() { SymbolFilter getFilter() {
return tableModel.getFilter(); return symbolModel.getFilter();
} }
void readConfigState(SaveState saveState) { void readConfigState(SaveState saveState) {
Element filterElement = saveState.getXmlElement(FILTER_SETTINGS_ELEMENT_NAME); Element filterElement = saveState.getXmlElement(FILTER_SETTINGS_ELEMENT_NAME);
if (filterElement != null) { if (filterElement != null) {
filterDialog.restoreFilter(filterElement); filterDialog.restoreFilter(filterElement);
tableModel.setFilter(filterDialog.getFilter()); symbolModel.setFilter(filterDialog.getFilter());
} }
} }
@ -167,26 +186,26 @@ class SymbolPanel extends JPanel {
} }
private void handleTableSelection() { private void handleTableSelection() {
int selectedRowCount = symTable.getSelectedRowCount(); int selectedRowCount = gTable.getSelectedRowCount();
if (selectedRowCount == 1) { if (selectedRowCount == 1) {
int selectedRow = symTable.getSelectedRow(); int selectedRow = gTable.getSelectedRow();
Symbol symbol = symProvider.getSymbolForRow(selectedRow); Symbol symbol = symbolProvider.getSymbolForRow(selectedRow);
symProvider.setCurrentSymbol(symbol); // null allowed symbolProvider.setCurrentSymbol(symbol); // null allowed
} }
else { else {
symProvider.setCurrentSymbol(null); symbolProvider.setCurrentSymbol(null);
} }
} }
int getActualSymbolCount() { int getActualSymbolCount() {
return symTable.getRowCount(); return gTable.getRowCount();
} }
List<Symbol> getSelectedSymbols() { List<Symbol> getSelectedSymbols() {
List<Symbol> list = new ArrayList<>(); List<Symbol> list = new ArrayList<>();
int[] rows = symTable.getSelectedRows(); int[] rows = gTable.getSelectedRows();
for (SymbolRowObject rowObject : tableModel.getRowObjects(rows)) { for (SymbolRowObject rowObject : symbolModel.getRowObjects(rows)) {
Symbol s = rowObject.getSymbol(); Symbol s = rowObject.getSymbol();
if (s != null) { if (s != null) {
list.add(s); list.add(s);
@ -196,7 +215,7 @@ class SymbolPanel extends JPanel {
} }
GhidraTable getTable() { GhidraTable getTable() {
return symTable; return gTable;
} }
//================================================================================================== //==================================================================================================

View file

@ -33,6 +33,7 @@ import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
@ -45,6 +46,8 @@ class SymbolProvider extends ComponentProviderAdapter {
private SymbolTableModel symbolKeyModel; private SymbolTableModel symbolKeyModel;
private SymbolPanel symbolPanel; private SymbolPanel symbolPanel;
private boolean followIncomingLocationChanges;
SymbolProvider(SymbolTablePlugin plugin) { SymbolProvider(SymbolTablePlugin plugin) {
super(plugin.getTool(), "Symbol Table", plugin.getName(), ProgramActionContext.class); super(plugin.getTool(), "Symbol Table", plugin.getName(), ProgramActionContext.class);
this.plugin = plugin; this.plugin = plugin;
@ -63,6 +66,19 @@ class SymbolProvider extends ComponentProviderAdapter {
addToTool(); addToTool();
} }
void setFollowIncomingLocationChanges(boolean b) {
followIncomingLocationChanges = b;
}
void locationChanged(ProgramLocation location) {
if (!isVisible()) {
return;
}
if (followIncomingLocationChanges) {
symbolPanel.locationChanged(location);
}
}
void updateTitle() { void updateTitle() {
setSubTitle(generateSubTitle()); setSubTitle(generateSubTitle());
} }

View file

@ -15,8 +15,6 @@
*/ */
package ghidra.app.plugin.core.symtable; package ghidra.app.plugin.core.symtable;
import java.util.Iterator;
import docking.widgets.table.DiscoverableTableUtils; import docking.widgets.table.DiscoverableTableUtils;
import docking.widgets.table.TableColumnDescriptor; import docking.widgets.table.TableColumnDescriptor;
import ghidra.app.services.BlockModelService; import ghidra.app.services.BlockModelService;
@ -53,11 +51,10 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
static final int INSTR_REFS_FROM = 1; static final int INSTR_REFS_FROM = 1;
static final int DATA_REFS_FROM = 2; static final int DATA_REFS_FROM = 2;
private Symbol currentSymbol; private volatile Symbol currentSymbol;
private ReferenceManager refManager; private ReferenceManager refManager;
private int showRefMode = REFS_TO; private int showRefMode = REFS_TO;
private BlockModelService blockModelService; private BlockModelService blockModelService;
private boolean isDisposed;
SymbolReferenceModel(BlockModelService bms, PluginTool tool) { SymbolReferenceModel(BlockModelService bms, PluginTool tool) {
super("Symbol References", tool, null, null); super("Symbol References", tool, null, null);
@ -98,12 +95,6 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
return description; return description;
} }
@Override
public void dispose() {
isDisposed = true;
super.dispose();
}
@Override @Override
public void setProgram(Program prog) { public void setProgram(Program prog) {
if (isDisposed) { if (isDisposed) {
@ -145,9 +136,7 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
} }
private void checkRefs(Symbol symbol) { private void checkRefs(Symbol symbol) {
Iterator<Reference> iter = filteredData.iterator(); for (Reference ref : filteredData) {
while (iter.hasNext()) {
Reference ref = iter.next();
if (ref.getFromAddress().equals(symbol.getAddress())) { if (ref.getFromAddress().equals(symbol.getAddress())) {
reload(); reload();
return; return;
@ -173,6 +162,7 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
@Override @Override
protected void doLoad(Accumulator<Reference> accumulator, TaskMonitor monitor) protected void doLoad(Accumulator<Reference> accumulator, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
if (currentSymbol == null || getProgram() == null) { if (currentSymbol == null || getProgram() == null) {
return; return;
} }

View file

@ -20,11 +20,13 @@ import static ghidra.program.util.ProgramEvent.*;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
import docking.ActionContext; import docking.ActionContext;
import docking.action.*; import docking.action.*;
import docking.action.builder.ActionBuilder;
import generic.theme.GIcon; import generic.theme.GIcon;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.events.ProgramActivatedPluginEvent; import ghidra.app.events.ProgramActivatedPluginEvent;
@ -44,6 +46,7 @@ import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramChangeRecord; import ghidra.program.util.ProgramChangeRecord;
import ghidra.program.util.ProgramLocation;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
import ghidra.util.table.SelectionNavigationAction; import ghidra.util.table.SelectionNavigationAction;
import ghidra.util.table.actions.MakeProgramSelectionAction; import ghidra.util.table.actions.MakeProgramSelectionAction;
@ -70,11 +73,14 @@ import resources.Icons;
"to show subsets of the symbols.", "to show subsets of the symbols.",
servicesRequired = { GoToService.class, BlockModelService.class }, servicesRequired = { GoToService.class, BlockModelService.class },
eventsProduced = { ProgramLocationPluginEvent.class }, eventsProduced = { ProgramLocationPluginEvent.class },
eventsConsumed = { ProgramActivatedPluginEvent.class } eventsConsumed = { ProgramActivatedPluginEvent.class, ProgramLocationPluginEvent.class }
) )
//@formatter:on //@formatter:on
public class SymbolTablePlugin extends Plugin { public class SymbolTablePlugin extends Plugin {
private static final String NAVIGATE_ON_INCOMING_EVENT_KEY = "NAVIGATE_ON_INCOMING_EVENT";
private static final String NAVIGATE_ON_OUTGOING_EVENT_KEY = "NAVIGATE_ON_OUTGOING_EVENT";
final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR); final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR);
final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
@ -85,6 +91,8 @@ public class SymbolTablePlugin extends Plugin {
private ToggleDockingAction referencesToAction; private ToggleDockingAction referencesToAction;
private ToggleDockingAction instructionsFromAction; private ToggleDockingAction instructionsFromAction;
private ToggleDockingAction dataFromAction; private ToggleDockingAction dataFromAction;
private ToggleDockingAction followIncomingAction;
private SelectionNavigationAction selectionNavigationAction;
private SymbolProvider symProvider; private SymbolProvider symProvider;
private ReferenceProvider refProvider; private ReferenceProvider refProvider;
@ -164,11 +172,21 @@ public class SymbolTablePlugin extends Plugin {
@Override @Override
public void readConfigState(SaveState saveState) { public void readConfigState(SaveState saveState) {
symProvider.readConfigState(saveState); symProvider.readConfigState(saveState);
boolean navigateIncoming = saveState.getBoolean(NAVIGATE_ON_INCOMING_EVENT_KEY, false);
boolean navigateOutgoing = saveState.getBoolean(NAVIGATE_ON_OUTGOING_EVENT_KEY, false);
followIncomingAction.setSelected(navigateIncoming);
selectionNavigationAction.setSelected(navigateOutgoing);
} }
@Override @Override
public void writeConfigState(SaveState saveState) { public void writeConfigState(SaveState saveState) {
symProvider.writeConfigState(saveState); symProvider.writeConfigState(saveState);
boolean navigateIncoming = followIncomingAction.isSelected();
boolean navigateOutgoing = selectionNavigationAction.isSelected();
saveState.putBoolean(NAVIGATE_ON_INCOMING_EVENT_KEY, navigateIncoming);
saveState.putBoolean(NAVIGATE_ON_OUTGOING_EVENT_KEY, navigateOutgoing);
} }
@Override @Override
@ -194,6 +212,10 @@ public class SymbolTablePlugin extends Plugin {
tool.contextChanged(symProvider); tool.contextChanged(symProvider);
} }
else if (event instanceof ProgramLocationPluginEvent ple) {
ProgramLocation location = ple.getLocation();
symProvider.locationChanged(location);
}
} }
boolean isBusy() { boolean isBusy() {
@ -391,8 +413,22 @@ public class SymbolTablePlugin extends Plugin {
setFilterAction.setDescription("Configure Symbol Filter"); setFilterAction.setDescription("Configure Symbol Filter");
tool.addLocalAction(symProvider, setFilterAction); tool.addLocalAction(symProvider, setFilterAction);
String navGroup = "2";
followIncomingAction =
new ToggleDockingAction("Navigate on Incoming Location Changes", getName(),
KeyBindingType.SHARED) {
@Override
public void actionPerformed(ActionContext context) {
symProvider.setFollowIncomingLocationChanges(isSelected());
}
};
followIncomingAction
.setToolBarData(new ToolBarData(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON, navGroup));
tool.addLocalAction(symProvider, followIncomingAction);
// override the SelectionNavigationAction to handle both tables that this plugin uses // override the SelectionNavigationAction to handle both tables that this plugin uses
DockingAction selectionNavigationAction = selectionNavigationAction =
new SelectionNavigationAction(this, symProvider.getTable()) { new SelectionNavigationAction(this, symProvider.getTable()) {
@Override @Override
@ -401,6 +437,7 @@ public class SymbolTablePlugin extends Plugin {
refProvider.getTable().setNavigateOnSelectionEnabled(listen); refProvider.getTable().setNavigateOnSelectionEnabled(listen);
} }
}; };
selectionNavigationAction.getToolBarData().setToolBarGroup(navGroup);
tool.addLocalAction(symProvider, selectionNavigationAction); tool.addLocalAction(symProvider, selectionNavigationAction);
String pinnedPopupGroup = "2"; // second group String pinnedPopupGroup = "2"; // second group
@ -416,6 +453,7 @@ public class SymbolTablePlugin extends Plugin {
} }
private void createRefActions() { private void createRefActions() {
String toolbarGroup = "1";
referencesToAction = new ToggleDockingAction("References To", getName()) { referencesToAction = new ToggleDockingAction("References To", getName()) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
@ -435,7 +473,7 @@ public class SymbolTablePlugin extends Plugin {
referencesToAction.setDescription("References To"); referencesToAction.setDescription("References To");
referencesToAction.setSelected(true); referencesToAction.setSelected(true);
referencesToAction.setToolBarData( referencesToAction.setToolBarData(
new ToolBarData(new GIcon("icon.plugin.symboltable.references.to"), null)); new ToolBarData(new GIcon("icon.plugin.symboltable.references.to"), toolbarGroup));
tool.addLocalAction(refProvider, referencesToAction); tool.addLocalAction(refProvider, referencesToAction);
@ -458,7 +496,7 @@ public class SymbolTablePlugin extends Plugin {
instructionsFromAction.setDescription("Instructions From"); instructionsFromAction.setDescription("Instructions From");
instructionsFromAction.setSelected(false); instructionsFromAction.setSelected(false);
instructionsFromAction.setToolBarData( instructionsFromAction.setToolBarData(
new ToolBarData(new GIcon("icon.plugin.symboltable.instructions.from"), null)); new ToolBarData(new GIcon("icon.plugin.symboltable.instructions.from"), toolbarGroup));
tool.addLocalAction(refProvider, instructionsFromAction); tool.addLocalAction(refProvider, instructionsFromAction);
@ -481,9 +519,36 @@ public class SymbolTablePlugin extends Plugin {
dataFromAction.setDescription("Data From"); dataFromAction.setDescription("Data From");
dataFromAction.setSelected(false); dataFromAction.setSelected(false);
dataFromAction.setToolBarData( dataFromAction.setToolBarData(
new ToolBarData(new GIcon("icon.plugin.symboltable.data.from"), null)); new ToolBarData(new GIcon("icon.plugin.symboltable.data.from"), toolbarGroup));
tool.addLocalAction(refProvider, dataFromAction); tool.addLocalAction(refProvider, dataFromAction);
//@formatter:off
toolbarGroup = "2";
String actionName = "Delete Reference";
new ActionBuilder(actionName, getName())
.sharedKeyBinding()
.toolBarIcon(Icons.DELETE_ICON)
.toolBarGroup(toolbarGroup)
.enabledWhen(c -> {
if (!(c instanceof ReferenceTableContext context)) {
return false;
}
if (refProvider.isBusy()) {
return false;
}
List<Reference> refs = context.getSelectedReferences();
return !refs.isEmpty();
})
.onAction(c -> {
ReferenceTableContext context = (ReferenceTableContext) c;
List<Reference> refs = context.getSelectedReferences();
refProvider.deleteRows(refs);
})
.buildAndInstallLocal(refProvider);
//@formatter:on
} }
// a HACK to make the given action the selected action // a HACK to make the given action the selected action

View file

@ -36,6 +36,7 @@ import ghidra.app.nav.Navigatable;
import ghidra.app.nav.NavigatableRemovalListener; import ghidra.app.nav.NavigatableRemovalListener;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@ -64,6 +65,9 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
private List<ComponentProviderActivationListener> activationListenerList = new ArrayList<>(); private List<ComponentProviderActivationListener> activationListenerList = new ArrayList<>();
private Callback closedCallback = Dummy.callback(); private Callback closedCallback = Dummy.callback();
// optional client listener
private DomainObjectListener programListener;
private Navigatable navigatable; private Navigatable navigatable;
private SelectionNavigationAction selectionNavigationAction; private SelectionNavigationAction selectionNavigationAction;
private DockingAction selectAction; private DockingAction selectAction;
@ -422,6 +426,15 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
} }
} }
@Override
public void componentHidden() {
// Note: this method will get called when this provider is closed. Also, the provider will
// be closed if its program is closed.
if (programListener != null) {
program.removeListener(programListener);
}
}
@Override @Override
public String getWindowSubMenuName() { public String getWindowSubMenuName() {
return windowSubMenu; return windowSubMenu;
@ -456,4 +469,22 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
public void setClosedCallback(Callback c) { public void setClosedCallback(Callback c) {
this.closedCallback = Dummy.ifNull(c); this.closedCallback = Dummy.ifNull(c);
} }
/**
* Sets a program listener on this provider. This class will add the listener to the program
* and maintain a reference to the listener for the life of this provider. This prevents the
* listener from getting garbage collected until this provider is disposed.
*
* @param programListener the listener
*/
public void setProgramListener(DomainObjectListener programListener) {
if (this.programListener != null) {
program.removeListener(programListener);
}
this.programListener = programListener;
if (programListener != null) {
program.addListener(programListener);
}
}
} }

View file

@ -17,6 +17,7 @@ package ghidra.app.util;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.Settings;
@ -36,8 +37,8 @@ public class FunctionXrefsTableModel extends ReferencesFromTableModel {
private Function function; private Function function;
private boolean showAllThunkXrefs; private boolean showAllThunkXrefs;
public FunctionXrefsTableModel(Function function, Collection<Reference> directRefs, ServiceProvider sp, public FunctionXrefsTableModel(Function function, Supplier<Collection<Reference>> directRefs,
Program program) { ServiceProvider sp, Program program) {
super(directRefs, sp, program); super(directRefs, sp, program);
this.function = function; this.function = function;

View file

@ -15,12 +15,28 @@
*/ */
package ghidra.app.util; package ghidra.app.util;
import java.util.*; import static ghidra.framework.model.DomainObjectEvent.*;
import static ghidra.program.util.ProgramEvent.*;
import java.util.*;
import java.util.function.Supplier;
import javax.swing.Icon;
import javax.swing.JTable;
import docking.ActionContext;
import docking.action.*;
import docking.action.builder.ToggleActionBuilder; import docking.action.builder.ToggleActionBuilder;
import docking.widgets.OptionDialog;
import docking.widgets.OptionDialogBuilder;
import ghidra.app.cmd.refs.RemoveReferenceCmd;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.table.TableComponentProvider; import ghidra.app.plugin.core.table.TableComponentProvider;
import ghidra.app.util.query.TableService; import ghidra.app.util.query.TableService;
import ghidra.framework.cmd.CompoundCmd;
import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.model.DomainObjectListenerBuilder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider; import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.DataUtilities; import ghidra.program.model.data.DataUtilities;
@ -30,16 +46,18 @@ import ghidra.program.util.*;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.table.ReferencesFromTableModel; import ghidra.util.table.ReferencesFromTableModel;
import ghidra.util.table.field.ReferenceEndpoint; import ghidra.util.table.field.ReferenceEndpoint;
import resources.Icons;
import resources.ResourceManager; import resources.ResourceManager;
public class XReferenceUtils { public class XReferenceUtils {
private static final String X_REFS_TO = "XRefs to "; private static final String X_REFS_TO = "XRefs to ";
// Methods in this class treat -1 as a key to return all references and // Methods in this class treat -1 as a key to return all references and not cap the result set.
// not cap the result set.
private final static int ALL_REFS = -1; private final static int ALL_REFS = -1;
private static OptionDialog promptToDeleteXrefsDialog;
/** /**
* Returns an array containing the first <b><code>max</code></b> * Returns an array containing the first <b><code>max</code></b>
* direct xref references to the specified code unit. * direct xref references to the specified code unit.
@ -69,13 +87,15 @@ public class XReferenceUtils {
} }
// Check for thunk reference // Check for thunk reference
Function func = program.getFunctionManager().getFunctionAt(minAddress); Function function = program.getFunctionManager().getFunctionAt(minAddress);
if (func != null) { if (function == null) {
Address[] thunkAddrs = func.getFunctionThunkAddresses(); return xrefs;
}
Address[] thunkAddrs = function.getFunctionThunkAddresses(false);
if (thunkAddrs != null) { if (thunkAddrs != null) {
for (Address thunkAddr : thunkAddrs) { for (Address thunkAddr : thunkAddrs) {
xrefs.add(new ThunkReference(thunkAddr, func.getEntryPoint())); xrefs.add(new ThunkReference(thunkAddr, function.getEntryPoint()));
}
} }
} }
return xrefs; return xrefs;
@ -203,10 +223,29 @@ public class XReferenceUtils {
* @param service the service needed to show the table * @param service the service needed to show the table
* @param location the location for which to find references * @param location the location for which to find references
* @param xrefs the xrefs to show * @param xrefs the xrefs to show
* @deprecated use {@link #showXrefs(Navigatable, ServiceProvider, TableService,
* ProgramLocation, Supplier)}. That method takes a supplier that can regenerate the current
* xrefs for the table.
*/ */
@Deprecated(since = "11.5", forRemoval = true)
public static void showXrefs(Navigatable navigatable, ServiceProvider serviceProvider, public static void showXrefs(Navigatable navigatable, ServiceProvider serviceProvider,
TableService service, ProgramLocation location, Collection<Reference> xrefs) { TableService service, ProgramLocation location, Collection<Reference> xrefs) {
showXrefs(navigatable, serviceProvider, service, location, () -> xrefs);
}
/**
* Shows all xrefs to the given location in a new table.
*
* @param navigatable the navigatable used for navigation from the table
* @param serviceProvider the service provider needed to wire navigation
* @param service the service needed to show the table
* @param location the location for which to find references
* @param xrefs a supplier of the xrefs to show
*/
public static void showXrefs(Navigatable navigatable, ServiceProvider serviceProvider,
TableService service, ProgramLocation location, Supplier<Collection<Reference>> xrefs) {
Address address = location.getAddress(); Address address = location.getAddress();
Program program = location.getProgram(); Program program = location.getProgram();
FunctionManager fm = program.getFunctionManager(); FunctionManager fm = program.getFunctionManager();
@ -223,9 +262,26 @@ public class XReferenceUtils {
String title = generateXRefTitle(location); String title = generateXRefTitle(location);
TableComponentProvider<ReferenceEndpoint> provider = TableComponentProvider<ReferenceEndpoint> provider =
service.showTable(title, "Xrefs", model, "Xrefs", navigatable); service.showTable(title, "Xrefs", model, "Xrefs", navigatable);
//
// Add Actions
//
provider.installRemoveItemsAction(); provider.installRemoveItemsAction();
installRefreshAction(provider, model);
if (function != null) { if (function != null) {
installShowThunksAction(provider, model);
}
DeleteXrefsAction deleteAction = new DeleteXrefsAction(provider, model, program);
provider.addLocalAction(deleteAction);
}
private static void installShowThunksAction(
TableComponentProvider<ReferenceEndpoint> provider,
ReferencesFromTableModel model) {
//@formatter:off //@formatter:off
String actionName = "Show Thunk Xrefs"; String actionName = "Show Thunk Xrefs";
new ToggleActionBuilder(actionName, provider.getActionOwner()) new ToggleActionBuilder(actionName, provider.getActionOwner())
@ -238,10 +294,112 @@ public class XReferenceUtils {
}) })
.buildAndInstallLocal(provider); .buildAndInstallLocal(provider);
//@formatter:on //@formatter:on
}
private static void installRefreshAction(TableComponentProvider<ReferenceEndpoint> provider,
ReferencesFromTableModel model) {
Icon REFRESH_NOT_NEEDED_ICON = ResourceManager.getDisabledIcon(Icons.REFRESH_ICON, 60);
DockingAction refreshAction = new DockingAction("Refresh", provider.getActionOwner()) {
@Override
public void actionPerformed(ActionContext context) {
getToolBarData().setIcon(REFRESH_NOT_NEEDED_ICON);
model.reload();
}
};
HelpLocation hl = new HelpLocation(HelpTopics.CODE_BROWSER, refreshAction.getName());
refreshAction.setHelpLocation(hl);
refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON, "A"));
refreshAction.setDescription(
"<html>Push at any time to refresh the current table of references.<br>" +
"This button is highlighted when the data <i>may</i> be stale.<br>");
// Add a listener to
DomainObjectListener listener = new DomainObjectListenerBuilder(model)
.any(RESTORED, REFERENCE_ADDED, REFERENCE_REMOVED)
.call(() -> {
// signal that the data in the table does not match the program
refreshAction.getToolBarData().setIcon(Icons.REFRESH_ICON);
})
.build();
provider.setProgramListener(listener);
provider.addLocalAction(refreshAction);
}
// Note: this action lives in this class so that the action can hold a reference to a domain
// object listener. The action will hold a reference so that as long as the the provider is
// around, the action will also be around to hold a reference to the listener.
private static class DeleteXrefsAction extends DockingAction {
private TableComponentProvider<ReferenceEndpoint> provider;
private Program program;
private ReferencesFromTableModel tableModel;
public DeleteXrefsAction(TableComponentProvider<ReferenceEndpoint> provider,
ReferencesFromTableModel tableModel, Program program) {
super("Delete Reference", provider.getActionOwner(), KeyBindingType.SHARED);
this.provider = provider;
this.tableModel = tableModel;
this.program = program;
setToolBarData(new ToolBarData(Icons.DELETE_ICON, "A"));
setHelpLocation(new HelpLocation(HelpTopics.CODE_BROWSER, getName()));
}
@Override
public boolean isEnabledForContext(ActionContext c) {
Object object = c.getContextObject();
if (!(object instanceof JTable table)) {
return false;
}
if (tableModel.isBusy()) {
return false;
}
return table.getSelectedRowCount() > 0;
}
@Override
public void actionPerformed(ActionContext c) {
Object object = c.getContextObject();
JTable table = (JTable) object;
int[] rows = table.getSelectedRows();
PluginTool tool = provider.getTool();
deleteXrefs(tool, program, tableModel, rows);
}
}
private static void deleteXrefs(PluginTool tool, Program program,
ReferencesFromTableModel tableModel, int[] rows) {
if (promptToDeleteXrefsDialog == null) {
promptToDeleteXrefsDialog =
new OptionDialogBuilder("Delete Xrefs?",
"Do you wish to permanently delete the selected xrefs?")
.addOption("Delete")
.addCancel()
.addDontShowAgainOption()
.build();
}
int choice = promptToDeleteXrefsDialog.show();
if (choice != OptionDialog.YES_OPTION) {
return; return;
} }
List<ReferenceEndpoint> deletedRowObjects = new ArrayList<>();
CompoundCmd<Program> compoundCmd = new CompoundCmd<>("Delete References");
for (int row : rows) {
ReferenceEndpoint endpoint = tableModel.getRowObject(row);
deletedRowObjects.add(endpoint);
Reference ref = endpoint.getReference();
RemoveReferenceCmd cmd = new RemoveReferenceCmd(ref);
compoundCmd.add(cmd);
}
tool.execute(compoundCmd, program);
// also remove the object from the table, since they have been deleted
deletedRowObjects.forEach(ro -> tableModel.removeObject(ro));
} }
private static String generateXRefTitle(ProgramLocation location) { private static String generateXRefTitle(ProgramLocation location) {

View file

@ -15,8 +15,8 @@
*/ */
package ghidra.app.util.viewer.field; package ghidra.app.util.viewer.field;
import java.util.HashSet; import java.util.*;
import java.util.Set; import java.util.function.Supplier;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.util.XReferenceUtils; import ghidra.app.util.XReferenceUtils;
@ -39,7 +39,7 @@ public class VariableXRefFieldMouseHandler extends XRefFieldMouseHandler {
@Override @Override
protected Address getFromReferenceAddress(ProgramLocation programLocation) { protected Address getFromReferenceAddress(ProgramLocation programLocation) {
return ((VariableXRefFieldLocation) programLocation).getRefAddress(); return programLocation.getRefAddress();
} }
@Override @Override
@ -63,7 +63,7 @@ public class VariableXRefFieldMouseHandler extends XRefFieldMouseHandler {
VariableLocation variableLocation = (VariableLocation) location; VariableLocation variableLocation = (VariableLocation) location;
Variable variable = variableLocation.getVariable(); Variable variable = variableLocation.getVariable();
Set<Reference> refs = getVariableRefs(variable); Supplier<Collection<Reference>> refs = () -> getVariableRefs(variable);
XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs); XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs);
} }

View file

@ -16,7 +16,8 @@
package ghidra.app.util.viewer.field; package ghidra.app.util.viewer.field;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.Set; import java.util.Collection;
import java.util.function.Supplier;
import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.field.*;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
@ -98,7 +99,7 @@ public class XRefFieldMouseHandler implements FieldMouseHandlerExtension {
} }
protected Address getFromReferenceAddress(ProgramLocation programLocation) { protected Address getFromReferenceAddress(ProgramLocation programLocation) {
return ((XRefFieldLocation) programLocation).getRefAddress(); return programLocation.getRefAddress();
} }
protected void showXRefDialog(Navigatable navigatable, ProgramLocation location, protected void showXRefDialog(Navigatable navigatable, ProgramLocation location,
@ -108,7 +109,7 @@ public class XRefFieldMouseHandler implements FieldMouseHandlerExtension {
return; return;
} }
Set<Reference> refs = XReferenceUtils.getAllXrefs(location); Supplier<Collection<Reference>> refs = () -> XReferenceUtils.getAllXrefs(location);
XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs); XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs);
} }

View file

@ -43,7 +43,7 @@ import resources.Icons;
*/ */
public abstract class AbstractSelectionNavigationAction extends ToggleDockingAction { public abstract class AbstractSelectionNavigationAction extends ToggleDockingAction {
private static final Icon ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON; private static final Icon ICON = Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON;
private static final String SELECTED_STATE = "SELECTION_NAVIGATION_SELECTED_STATE"; private static final String SELECTED_STATE = "SELECTION_NAVIGATION_SELECTED_STATE";
private SelectionListener selectionListener; private SelectionListener selectionListener;
@ -60,7 +60,6 @@ public abstract class AbstractSelectionNavigationAction extends ToggleDockingAct
setDescription(HTMLUtilities.toHTML("Toggle <b>on</b> means to navigate to the location\n" + setDescription(HTMLUtilities.toHTML("Toggle <b>on</b> means to navigate to the location\n" +
"in the program that corresponds to the selected row,\n as the selection changes.")); "in the program that corresponds to the selected row,\n as the selection changes."));
setHelpLocation(new HelpLocation("Search", "Selection_Navigation")); setHelpLocation(new HelpLocation("Search", "Selection_Navigation"));
setEnabled(true);
setSelected(true); // toggle button; enabled by default setSelected(true); // toggle button; enabled by default
initialize(); initialize();

View file

@ -18,8 +18,7 @@ package ghidra.util.table;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -45,16 +44,21 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class ReferencesFromTableModel extends AddressBasedTableModel<ReferenceEndpoint> { public class ReferencesFromTableModel extends AddressBasedTableModel<ReferenceEndpoint> {
private List<IncomingReferenceEndpoint> refs; private Supplier<Collection<Reference>> refsSupplier;
public ReferencesFromTableModel(Collection<Reference> refs, ServiceProvider sp, public ReferencesFromTableModel(Collection<Reference> refs, ServiceProvider sp,
Program program) { Program program) {
super("References", sp, program, null); super("References", sp, program, null);
this.refs = refs.stream().map(r -> { this.refsSupplier = () -> refs;
boolean offcut = ReferenceUtils.isOffcut(program, r.getToAddress());
return new IncomingReferenceEndpoint(r, offcut); addTableColumn(new ReferenceTypeTableColumn());
}).collect(Collectors.toList()); }
public ReferencesFromTableModel(Supplier<Collection<Reference>> refsSupplier,
ServiceProvider sp, Program program) {
super("References", sp, program, null);
this.refsSupplier = refsSupplier;
addTableColumn(new ReferenceTypeTableColumn()); addTableColumn(new ReferenceTypeTableColumn());
} }
@ -62,7 +66,13 @@ public class ReferencesFromTableModel extends AddressBasedTableModel<ReferenceEn
@Override @Override
protected void doLoad(Accumulator<ReferenceEndpoint> accumulator, TaskMonitor monitor) protected void doLoad(Accumulator<ReferenceEndpoint> accumulator, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
refs.forEach(r -> accumulator.add(r));
Collection<Reference> xrefs = refsSupplier.get();
for (Reference xref : xrefs) {
boolean offcut = ReferenceUtils.isOffcut(program, xref.getToAddress());
IncomingReferenceEndpoint endpoint = new IncomingReferenceEndpoint(xref, offcut);
accumulator.add(endpoint);
}
} }
@Override @Override

View file

@ -112,7 +112,7 @@ public class DeleteTableRowAction extends DockingAction {
protected void removeSelectedItems() { protected void removeSelectedItems() {
TableModel model = table.getModel(); TableModel model = table.getModel();
if (!(model instanceof RowObjectTableModel)) { if (!(model instanceof ThreadedTableModel)) {
throw new AssertException("This action cannot delete rows for the given table model." + throw new AssertException("This action cannot delete rows for the given table model." +
"You can override this method to peform the delete action yourself."); "You can override this method to peform the delete action yourself.");
} }
@ -153,7 +153,7 @@ public class DeleteTableRowAction extends DockingAction {
} }
} }
public boolean checkForBusy(TableModel model) { protected boolean checkForBusy(TableModel model) {
if (!(model instanceof ThreadedTableModel)) { if (!(model instanceof ThreadedTableModel)) {
return false; return false;

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.navigation.locationreferences; package ghidra.app.plugin.core.navigation.locationreferences;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.Test; import org.junit.Test;

View file

@ -16,12 +16,17 @@
package ghidra.app.plugin.core.navigation.locationreferences; package ghidra.app.plugin.core.navigation.locationreferences;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.*; import java.util.*;
import javax.swing.ListSelectionModel;
import org.junit.Test; import org.junit.Test;
import docking.action.DockingActionIf;
import docking.widgets.table.GTable;
import ghidra.app.cmd.function.SetReturnDataTypeCmd; import ghidra.app.cmd.function.SetReturnDataTypeCmd;
import ghidra.app.cmd.refs.RemoveReferenceCmd; import ghidra.app.cmd.refs.RemoveReferenceCmd;
import ghidra.app.plugin.core.clear.ClearCmd; import ghidra.app.plugin.core.clear.ClearCmd;
@ -142,8 +147,8 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
// 0100415a - sscanf // 0100415a - sscanf
Address address = addr(0x0100415a); Address address = addr(0x0100415a);
int parameterColumn = 7; int returnTypeColumn = 7;
goTo(address, "Function Signature", parameterColumn); goTo(address, "Function Signature", returnTypeColumn);
search(); search();
@ -172,6 +177,33 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
verifyReferenceAddresses(address, referenceAddresses); verifyReferenceAddresses(address, referenceAddresses);
} }
@Test
public void testDeleteReferencesFromTable() {
// 01002cf5 - ghidra
Address address = addr(0x01002cf5);
int functionNameColumn = 15;
goTo(address, "Function Signature", functionNameColumn);
search();
List<Address> referenceAddresses = getResultAddresses();
int referenceCount = referenceAddresses.size();
DockingActionIf deleteAction =
getAction(tool, locationReferencesPlugin.getName(), "Delete Reference");
LocationReferencesProvider provider = getResultsProvider();
assertFalse(isEnabled(deleteAction, provider));
selectRows(0);
assertTrue(isEnabled(deleteAction, provider));
performAction(deleteAction, provider, true);
referenceAddresses = getResultAddresses();
int updatedReferenceCount = referenceAddresses.size();
assertEquals(referenceCount - 1, updatedReferenceCount);
}
@Test @Test
public void testLabelLocationDescriptor() throws Exception { public void testLabelLocationDescriptor() throws Exception {
@ -369,6 +401,19 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
// Private Methods // Private Methods
//================================================================================================== //==================================================================================================
private void selectRows(int... rows) {
LocationReferencesProvider provider = getResultsProvider();
runSwing(() -> {
GTable gTable = provider.getTable();
ListSelectionModel selectionModel = gTable.getSelectionModel();
for (int row : rows) {
selectionModel.addSelectionInterval(row, row);
}
});
waitForSwing();
}
private void createString_CallStructure(String addressString) throws Exception { private void createString_CallStructure(String addressString) throws Exception {
// String // String
// "call_structure_A: %s\n",00 // "call_structure_A: %s\n",00

View file

@ -122,8 +122,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
int row = findRow("ghidra"); int row = findRow("ghidra");
TableModel model = symbolTable.getModel(); TableModel model = symbolTable.getModel();
doubleClick(symbolTable, row, SymbolTableModel.LOCATION_COL); doubleClick(symbolTable, row, AbstractSymbolTableModel.LOCATION_COL);
ProgramLocation pl = getProgramLocation(row, SymbolTableModel.LOCATION_COL, model); ProgramLocation pl = getProgramLocation(row, AbstractSymbolTableModel.LOCATION_COL, model);
assertEquals(pl.getAddress(), cbPlugin.getCurrentAddress()); assertEquals(pl.getAddress(), cbPlugin.getCurrentAddress());
} }
@ -131,7 +131,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testSortingLabelColumn() throws Exception { public void testSortingLabelColumn() throws Exception {
openProgram("sample"); openProgram("sample");
sortAscending(SymbolTableModel.LABEL_COL); sortAscending(AbstractSymbolTableModel.LABEL_COL);
TableModel model = symbolTable.getModel(); TableModel model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -141,7 +141,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue("row " + i + " not sorted correctly", (compare < 0 || compare == 0)); assertTrue("row " + i + " not sorted correctly", (compare < 0 || compare == 0));
} }
sortDescending(SymbolTableModel.LABEL_COL); sortDescending(AbstractSymbolTableModel.LABEL_COL);
model = symbolTable.getModel(); model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -193,7 +193,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testSortingAddressColumn() throws Exception { public void testSortingAddressColumn() throws Exception {
openProgram("sample"); openProgram("sample");
sortAscending(SymbolTableModel.LOCATION_COL); sortAscending(AbstractSymbolTableModel.LOCATION_COL);
SymbolTableModel model = (SymbolTableModel) symbolTable.getModel(); SymbolTableModel model = (SymbolTableModel) symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -202,7 +202,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(loc1.compareTo(loc2) <= 0); assertTrue(loc1.compareTo(loc2) <= 0);
} }
sortDescending(SymbolTableModel.LOCATION_COL); sortDescending(AbstractSymbolTableModel.LOCATION_COL);
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
AddressBasedLocation loc1 = getLocation(i); AddressBasedLocation loc1 = getLocation(i);
@ -215,7 +215,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testSortingReferenceColumn() throws Exception { public void testSortingReferenceColumn() throws Exception {
openProgram("sample"); openProgram("sample");
sortAscending(SymbolTableModel.REFS_COL); sortAscending(AbstractSymbolTableModel.REFS_COL);
TableModel model = symbolTable.getModel(); TableModel model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -224,7 +224,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(refs1.compareTo(refs2) <= 0); assertTrue(refs1.compareTo(refs2) <= 0);
} }
sortDescending(SymbolTableModel.REFS_COL); sortDescending(AbstractSymbolTableModel.REFS_COL);
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
Integer refs1 = getRefCount(i); Integer refs1 = getRefCount(i);
@ -325,12 +325,10 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testEditing() throws Exception { public void testEditing() throws Exception {
openProgram("sample"); openProgram("sample");
waitForNotBusy();
String symbolName = "ghidra"; String symbolName = "ghidra";
int row = findRow(symbolName); int row = findRow(symbolName);
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL); doubleClick(symbolTable, row, AbstractSymbolTableModel.LABEL_COL);
waitForSwing(); waitForSwing();
assertTrue(symbolTable.isEditing()); assertTrue(symbolTable.isEditing());
@ -361,7 +359,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
String symbolName = "ghidra"; String symbolName = "ghidra";
int row = findRow(symbolName); int row = findRow(symbolName);
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL); doubleClick(symbolTable, row, AbstractSymbolTableModel.LABEL_COL);
waitForSwing(); waitForSwing();
assertTrue(symbolTable.isEditing()); assertTrue(symbolTable.isEditing());
@ -782,6 +780,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
Address from = sample.getNewAddress(0x01004aea); Address from = sample.getNewAddress(0x01004aea);
Address to = sample.getNewAddress(0x50); Address to = sample.getNewAddress(0x50);
Reference ref = getReference(from, to); Reference ref = getReference(from, to);
assertNotNull(ref);
tx(program, () -> { tx(program, () -> {
ReferenceManager manager = program.getReferenceManager(); ReferenceManager manager = program.getReferenceManager();
@ -847,7 +846,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
/************** LABEL **********************/ /************** LABEL **********************/
sortAscending(SymbolTableModel.LABEL_COL); sortAscending(AbstractSymbolTableModel.LABEL_COL);
TableModel model = symbolTable.getModel(); TableModel model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -860,7 +859,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
/************** ADDRESS **********************/ /************** ADDRESS **********************/
sortAscending(SymbolTableModel.LOCATION_COL); sortAscending(AbstractSymbolTableModel.LOCATION_COL);
model = symbolTable.getModel(); model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -874,7 +873,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
/************** REFERENCES **********************/ /************** REFERENCES **********************/
sortAscending(SymbolTableModel.REFS_COL); sortAscending(AbstractSymbolTableModel.REFS_COL);
model = symbolTable.getModel(); model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -893,7 +892,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
showReferencesTable(); showReferencesTable();
singleClick(symbolTable, findRow("ghidra", "Global"), SymbolTableModel.LABEL_COL); singleClick(symbolTable, findRow("ghidra", "Global"), AbstractSymbolTableModel.LABEL_COL);
/*****************************/ /*****************************/
@ -1003,7 +1002,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
deleteText(textField); deleteText(textField);
// sort on a different column to trigger the other kind of filtering // sort on a different column to trigger the other kind of filtering
sortAscending(SymbolTableModel.REFS_COL); sortAscending(AbstractSymbolTableModel.REFS_COL);
text = "_"; text = "_";
myTypeText(textField, text); myTypeText(textField, text);
@ -1074,7 +1073,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
deleteText(textField); deleteText(textField);
// sort on a different column to trigger the other kind of filtering // sort on a different column to trigger the other kind of filtering
sortAscending(SymbolTableModel.LOCATION_COL); sortAscending(AbstractSymbolTableModel.LOCATION_COL);
text = "_"; text = "_";
myTypeText(textField, text); myTypeText(textField, text);
@ -1140,7 +1139,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
@Test @Test
public void testReferenceRemvoed_ReferenceToDynamicSymbol() throws Exception { public void testReferenceRemoved_ReferenceToDynamicSymbol() throws Exception {
openProgram("sample"); openProgram("sample");
@ -1153,10 +1152,63 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertFalse(row > -1); assertFalse(row > -1);
} }
@Test
public void testDeleteReference() throws Exception {
openProgram("sample");
showReferencesTable();
ReferenceProvider referencesProvider = waitForComponentProvider(ReferenceProvider.class);
// pick symbol with refs
selectRow("ghidra");
int refCount = referenceTable.getModel().getRowCount();
assertReference("01004101", "00000052", true);
DockingActionIf deleteRefsAction =
getAction(tool, plugin.getName(), "Delete Reference");
assertFalse(isEnabled(deleteRefsAction, referencesProvider));
selectRef(referenceTable, "01004101", "00000052");
assertTrue(isEnabled(deleteRefsAction, referencesProvider));
performAction(deleteRefsAction, referencesProvider, true);
assertEquals(refCount - 1, referenceTable.getModel().getRowCount());
assertReference("01004101", "00000052", false);
}
//================================================================================================== //==================================================================================================
// Helper methods // Helper methods
//================================================================================================== //==================================================================================================
@SuppressWarnings("unchecked")
private void selectRef(GTable table, String from, String to) throws Exception {
Reference ref = getReference(addr(from), addr(to));
int viewRow = runSwing(() -> {
RowObjectTableModel<Reference> model =
(RowObjectTableModel<Reference>) table.getModel();
return model.getRowIndex(ref);
});
selectRows(table, viewRow);
}
private void assertReference(String fromString, String toString, boolean exists) {
Address from = addr(fromString);
Address to = addr(toString);
Reference ref = getReference(from, to);
if (exists) {
assertNotNull(ref);
}
else {
assertNull(ref);
}
}
private Reference getReference(Address from, Address to) { private Reference getReference(Address from, Address to) {
ReferenceManager rm = program.getReferenceManager(); ReferenceManager rm = program.getReferenceManager();
@ -1167,7 +1219,6 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
} }
fail("Did not find expected mem reference between " + from + " and " + to);
return null; return null;
} }
@ -1234,6 +1285,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
return popup; return popup;
} }
private void selectRow(String symbolName) {
int row = findRow(symbolName);
selectRow(row);
}
private void selectRow(int row) { private void selectRow(int row) {
selectRows(row, row); selectRows(row, row);
@ -1243,18 +1299,22 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
private void selectRows(int... rows) { private void selectRows(int... rows) {
selectRows(symbolTable, rows);
}
private void selectRows(JTable table, int... rows) {
assertNotNull(rows); assertNotNull(rows);
assertTrue("Must have at least one row to select", rows.length > 0); assertTrue("Must have at least one row to select", rows.length > 0);
runSwing(() -> { runSwing(() -> {
symbolTable.clearSelection(); table.clearSelection();
for (int row : rows) { for (int row : rows) {
symbolTable.addRowSelectionInterval(row, row); table.addRowSelectionInterval(row, row);
} }
int end = rows[rows.length - 1]; int end = rows[rows.length - 1];
Rectangle rect = symbolTable.getCellRect(end, 0, true); Rectangle rect = table.getCellRect(end, 0, true);
symbolTable.scrollRectToVisible(rect); table.scrollRectToVisible(rect);
}); });
waitForSwing(); waitForSwing();
} }
@ -1326,14 +1386,15 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
private Integer getRefCount(int row) { private Integer getRefCount(int row) {
Integer count = Integer count =
runSwing(() -> (Integer) symbolModel.getValueAt(row, SymbolTableModel.REFS_COL)); runSwing(
() -> (Integer) symbolModel.getValueAt(row, AbstractSymbolTableModel.REFS_COL));
return count; return count;
} }
private AddressBasedLocation getLocation(int row) { private AddressBasedLocation getLocation(int row) {
AddressBasedLocation location = AddressBasedLocation location =
runSwing(() -> (AddressBasedLocation) symbolModel.getValueAt(row, runSwing(() -> (AddressBasedLocation) symbolModel.getValueAt(row,
SymbolTableModel.LOCATION_COL)); AbstractSymbolTableModel.LOCATION_COL));
return location; return location;
} }
@ -1650,16 +1711,17 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
waitForNotBusy(); waitForNotBusy();
sortAscending(SymbolTableModel.LABEL_COL); sortAscending(AbstractSymbolTableModel.LABEL_COL);
} }
private void showReferencesTable() { private void showReferencesTable() throws Exception {
performAction(viewRefAction, true); performAction(viewRefAction, true);
ReferenceProvider referencesProvider = waitForComponentProvider(ReferenceProvider.class); ReferenceProvider referencesProvider = waitForComponentProvider(ReferenceProvider.class);
referenceTable = referenceTable =
(GTable) findComponentByName(referencesProvider.getComponent(), "Reference Table"); (GTable) findComponentByName(referencesProvider.getComponent(), "Reference Table");
assertNotNull(referenceTable); assertNotNull(referenceTable);
waitForNotBusy();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View file

@ -17,6 +17,9 @@ package ghidra.app.util.viewer.field;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JTextField; import javax.swing.JTextField;
import org.junit.*; import org.junit.*;
@ -43,6 +46,7 @@ import ghidra.program.util.XRefHeaderFieldLocation;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv; import ghidra.test.TestEnv;
import ghidra.util.table.GhidraProgramTableModel; import ghidra.util.table.GhidraProgramTableModel;
import ghidra.util.table.GhidraTable;
/** /**
* Tests that references are displayed correctly when selecting the XRef field in * Tests that references are displayed correctly when selecting the XRef field in
@ -162,6 +166,27 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(3, model.getRowCount()); assertEquals(3, model.getRowCount());
} }
@Test
public void testDeleteReferencesFromTable() {
doubleClickXRef("1001005", "XREF[1]: ");
ComponentProvider comp = waitForComponentProvider(TableComponentProvider.class);
TableComponentProvider<?> table = (TableComponentProvider<?>) comp;
assertEquals(1, table.getModel().getRowCount());
assertReference("01001009", "01001005", true);
DockingActionIf deleteAction = getAction(tool, "TableServicePlugin", "Delete Reference");
assertFalse(runSwing(() -> deleteAction.isEnabled()));
selectRow(table, 0);
assertTrue(runSwing(() -> deleteAction.isEnabled()));
performAction(deleteAction, table, true);
assertEquals(0, table.getModel().getRowCount());
assertReference("01001009", "01001005", false);
}
@Test @Test
public void testViewReferencesShowThunkXrefs_FromThunk() { public void testViewReferencesShowThunkXrefs_FromThunk() {
@ -218,6 +243,42 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
// Private Methods // Private Methods
//================================================================================================== //==================================================================================================
private void assertReference(String fromString, String toString, boolean exists) {
Address from = addr(fromString);
Address to = addr(toString);
List<Reference> refs = getReferences(to);
if (exists) {
assertEquals(1, refs.size());
Reference ref = refs.get(0);
Address refFrom = ref.getFromAddress();
Address refTo = ref.getToAddress();
assertEquals(from, refFrom);
assertEquals(to, refTo);
}
else {
assertEquals(0, refs.size());
}
}
private List<Reference> getReferences(Address to) {
List<Reference> refs = new ArrayList<>();
ReferenceManager rm = program.getReferenceManager();
ReferenceIterator it = rm.getReferencesTo(to);
while (it.hasNext()) {
refs.add(it.next());
}
return refs;
}
private void selectRow(TableComponentProvider<?> provider, int row) {
runSwing(() -> {
GhidraTable gTable = provider.getTable();
gTable.selectRow(row);
});
waitForSwing();
}
private void createThunkFunction(String thunkAddressString, String thunkedAddressString) { private void createThunkFunction(String thunkAddressString, String thunkedAddressString) {
Address thunkAddress = addr(thunkAddressString); Address thunkAddress = addr(thunkAddressString);

View file

@ -86,7 +86,10 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
private GTree inTree; private GTree inTree;
private GTree outTree; private GTree outTree;
private boolean isPrimary; private boolean isPrimary;
private enum Condition {IN, OUT, EITHER}
private enum Condition {
IN, OUT, EITHER
}
private SwingUpdateManager reloadUpdateManager = new SwingUpdateManager(500, () -> doUpdate()); private SwingUpdateManager reloadUpdateManager = new SwingUpdateManager(500, () -> doUpdate());
@ -286,12 +289,13 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
// //
// navigate incoming nodes on selection // navigate incoming nodes on selection
// //
navigationIncomingAction = new ToggleActionBuilder("Navigate Incoming Location Changes", ownerName) navigationIncomingAction = new ToggleActionBuilder("Navigate on Incoming Location Changes", ownerName)
.description(HTMLUtilities.toHTML("Incoming Navigation" + .description(HTMLUtilities.toHTML("Incoming Navigation" +
"<br><br>Toggle <b>On</b> - change the displayed " + "<br><br>Toggle <b>On</b> - change the displayed " +
"function on Listing navigation events" + "function on Listing navigation events" +
"<br>Toggled <b>Off</b> - don't change the displayed function on Listing navigation events")) "<br>Toggled <b>Off</b> - don't change the displayed function on Listing navigation events"))
.helpLocation(new HelpLocation(ownerName, "Call_Tree_Action_Incoming_Navigation")) .helpLocation(new HelpLocation(ownerName, "Call_Tree_Action_Incoming_Navigation"))
.sharedKeyBinding()
.toolBarGroup(navigationOptionsToolbarGroup, "2") .toolBarGroup(navigationOptionsToolbarGroup, "2")
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON) .toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.selected(isPrimary) .selected(isPrimary)
@ -305,7 +309,6 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
//@formatter:on //@formatter:on
} }
private boolean isValidSelection(ActionContext context) { private boolean isValidSelection(ActionContext context) {
Object contextObject = context.getContextObject(); Object contextObject = context.getContextObject();
GTree gTree = (GTree) contextObject; GTree gTree = (GTree) contextObject;
@ -390,7 +393,6 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
return isValidContextMult(context, Condition.OUT); return isValidContextMult(context, Condition.OUT);
} }
private void expandToDepth(ActionContext context) { private void expandToDepth(ActionContext context) {
Object contextObject = context.getContextObject(); Object contextObject = context.getContextObject();
GTree gTree = (GTree) contextObject; GTree gTree = (GTree) contextObject;

View file

@ -112,9 +112,12 @@ public class ComponentInfoDialog extends DialogComponentProvider implements Prop
.build(); .build();
addAction(eventAction); addAction(eventAction);
toggleFollowFocusAction = new ToggleActionBuilder("Follow Focus", ACTION_OWNER) toggleFollowFocusAction =
new ToggleActionBuilder("Navigate on Incoming Location Changes", ACTION_OWNER)
.sharedKeyBinding()
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON) .toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.description("On causes component table to constant repopulate as focus changes") .description(
"On causes component table to constant repopulate as focus changes")
.onAction(c -> toggleFollowFocus()) .onAction(c -> toggleFollowFocus())
.selected(true) .selected(true)
.build(); .build();

View file

@ -36,17 +36,17 @@ public interface Reference extends Comparable<Reference> {
public static final int OTHER = -2; public static final int OTHER = -2;
/** /**
* Get the address of the codeunit that is making the reference. * {@return Get the address of the code unit that is making the reference.}
*/ */
public Address getFromAddress(); public Address getFromAddress();
/** /**
* Get the "to" address for this reference. * {@return Get the "to" address for this reference.}
*/ */
public Address getToAddress(); public Address getToAddress();
/** /**
* Return whether this reference is marked as primary. * {@return Return whether this reference is marked as primary.}
*/ */
public boolean isPrimary(); public boolean isPrimary();
@ -59,7 +59,7 @@ public interface Reference extends Comparable<Reference> {
public long getSymbolID(); public long getSymbolID();
/** /**
* Get the type of reference being made. * {@return Get the type of reference being made.}
*/ */
public RefType getReferenceType(); public RefType getReferenceType();
@ -71,50 +71,50 @@ public interface Reference extends Comparable<Reference> {
public int getOperandIndex(); public int getOperandIndex();
/** /**
* Return true if this reference is on the Mnemonic and not on an operand * {@return Return true if this reference is on the Mnemonic and not on an operand.}
*/ */
public boolean isMnemonicReference(); public boolean isMnemonicReference();
/** /**
* Return true if this reference is on an operand and not on the Mnemonic. * {@return Return true if this reference is on an operand and not on the Mnemonic.}
*/ */
public boolean isOperandReference(); public boolean isOperandReference();
/** /**
* Returns true if this reference is an instance of StackReference and * {@return Returns true if this reference is an instance of StackReference and
* refers to a stack location. * refers to a stack location.}
*/ */
public boolean isStackReference(); public boolean isStackReference();
/** /**
* Returns true if this reference is an instance of ExternalReference. * {@return Returns true if this reference is an instance of ExternalReference.}
*/ */
public boolean isExternalReference(); public boolean isExternalReference();
/** /**
* Returns true if this reference is an instance of EntryReference. * {@return Returns true if this reference is an instance of EntryReference.}
*/ */
public boolean isEntryPointReference(); public boolean isEntryPointReference();
/** /**
* Returns true if this reference to an address in the programs memory * {@return Returns true if this reference to an address in the programs memory
* space. This includes offset and shifted references. * space. This includes offset and shifted references.}
*/ */
public boolean isMemoryReference(); public boolean isMemoryReference();
/** /**
* Returns true if this reference to an address in the programs register * {@return Returns true if this reference to an address in the programs register
* space. * space.}
*/ */
public boolean isRegisterReference(); public boolean isRegisterReference();
/** /**
* Returns true if this reference is an instance of OffsetReference. * {@return Returns true if this reference is an instance of OffsetReference.}
*/ */
public boolean isOffsetReference(); public boolean isOffsetReference();
/** /**
* Returns true if this reference is an instance of ShiftedReference. * {@return Returns true if this reference is an instance of ShiftedReference.}
*/ */
public boolean isShiftedReference(); public boolean isShiftedReference();

View file

@ -189,7 +189,7 @@ public interface ReferenceManager {
* @param source the source of this reference * @param source the source of this reference
* @param type reference type - how the location is being referenced * @param type reference type - how the location is being referenced
* @return external reference * @return external reference
* @throws InvalidInputException * @throws InvalidInputException if the input is invalid
*/ */
public Reference addExternalReference(Address fromAddr, int opIndex, ExternalLocation location, public Reference addExternalReference(Address fromAddr, int opIndex, ExternalLocation location,
SourceType source, RefType type) throws InvalidInputException; SourceType source, RefType type) throws InvalidInputException;
@ -385,11 +385,13 @@ public interface ReferenceManager {
/** /**
* Return the number of references for "to" addresses. * Return the number of references for "to" addresses.
* @return the number of references for "to" addresses.
*/ */
public int getReferenceDestinationCount(); public int getReferenceDestinationCount();
/** /**
* Return the number of references for "from" addresses. * Return the number of references for "from" addresses.
* @return the number of references for "from" addresses.
*/ */
public int getReferenceSourceCount(); public int getReferenceSourceCount();
@ -401,7 +403,7 @@ public interface ReferenceManager {
public boolean hasReferencesTo(Address toAddr); public boolean hasReferencesTo(Address toAddr);
/** /**
* Uodate the reference type on a memory reference. * Update the reference type on a memory reference.
* @param ref reference to be updated * @param ref reference to be updated
* @param refType new reference type * @param refType new reference type
* @return updated reference * @return updated reference