mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-5886 Added ability to toggle the display of function variables in the listing.
This commit is contained in:
parent
1bc7bbfe8d
commit
8fc93d0d50
25 changed files with 1165 additions and 361 deletions
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.listing;
|
||||
|
||||
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.GROUP_TRANSIENT_VIEWS;
|
||||
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
@ -32,7 +32,8 @@ import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
|||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.event.*;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractNewListingAction;
|
||||
import ghidra.app.plugin.core.debug.gui.action.*;
|
||||
import ghidra.app.plugin.core.debug.gui.action.DebuggerProgramLocationActionContext;
|
||||
import ghidra.app.plugin.core.debug.gui.action.NoneLocationTrackingSpec;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
|
@ -81,7 +82,8 @@ import ghidra.trace.model.program.TraceProgramView;
|
|||
},
|
||||
servicesProvided = {
|
||||
DebuggerListingService.class,
|
||||
})
|
||||
}
|
||||
)
|
||||
public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerListingProvider>
|
||||
implements DebuggerListingService {
|
||||
private static final String KEY_CONNECTED_PROVIDER = "connectedProvider";
|
||||
|
@ -450,6 +452,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
|
|||
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
super.writeConfigState(saveState);
|
||||
SaveState connectedProviderState = new SaveState();
|
||||
connectedProvider.writeConfigState(connectedProviderState);
|
||||
saveState.putXmlElement(KEY_CONNECTED_PROVIDER, connectedProviderState.saveToXml());
|
||||
|
@ -470,6 +473,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
|
|||
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
super.readConfigState(saveState);
|
||||
Element connectedProviderElement = saveState.getXmlElement(KEY_CONNECTED_PROVIDER);
|
||||
if (connectedProviderElement != null) {
|
||||
SaveState connectedProviderState = new SaveState(connectedProviderElement);
|
||||
|
|
|
@ -100,7 +100,6 @@ data/typeinfo/golang/go1.20.0.json||GHIDRA||||END|
|
|||
data/typeinfo/golang/go1.21.0.json||GHIDRA||||END|
|
||||
data/typeinfo/golang/go1.22.0.json||GHIDRA||||END|
|
||||
data/typeinfo/golang/go1.23.0.json||GHIDRA||||END|
|
||||
data/typeinfo/golang/go1.23.2.json||GHIDRA||||END|
|
||||
data/typeinfo/golang/go1.24.0.json||GHIDRA||||END|
|
||||
data/typeinfo/golang/go1.25.0.json||GHIDRA||||END|
|
||||
data/typeinfo/golang/golang_1.15_anybit_any.gdt||GHIDRA||||END|
|
||||
|
|
|
@ -108,6 +108,7 @@ color.bg.listing.comparison.code.units.unmatched = color.palette.lightskyblue
|
|||
color.bg.listing.error = color.palette.lightcoral
|
||||
|
||||
font.listing.base = font.monospaced
|
||||
font.listing.base.hidden.field = font.listing.base[italic]
|
||||
font.listing.header = SansSerif-PLAIN-11
|
||||
|
||||
|
||||
|
|
|
@ -654,7 +654,30 @@
|
|||
of where in the containing structure the popup menu was activated.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<H2>Opening/Closing Function Variables Display</H2>
|
||||
<BLOCKQUOTE>
|
||||
The display of parameters and local variables for a function can be toggled on or off.
|
||||
Normally, these are displayed just below the function signature, but they can be turned off
|
||||
to conserve screen space. There is an open/close control (+/-)
|
||||
on the first parameter/variable line that can be used to toggle them on or off.</P>
|
||||
<H3>Actions for Opening/Closing Function Variables Display</H3>
|
||||
<BLOCKQUOTE>
|
||||
<P>In addition to the control widget in the display, there are also several actions that
|
||||
can be used to control the display of function variables.</P>
|
||||
<UL>
|
||||
<LI><A name="Show_All_Variables"></A><B>Show/Hide All Variables</B> - This toggle
|
||||
action can be used to globally control whether or not function variables are displayed.
|
||||
Individual functions can still be toggled on or off, but this sets the default for all
|
||||
functions.</LI>
|
||||
<LI><A name="Show_Variables"></A><B>Show/Hide Variables</B> - This action toggles
|
||||
the display of the variables. If they are showing, this will turn them off. And if they
|
||||
are not showing, this action will turn them on. The default keybinding is
|
||||
<CODE>Space</CODE> so if you are on a function or variable, pressing the space bar will
|
||||
toggle the display state.</LI>
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
<H2><A name="cursorTextHighlight"></A> Cursor Text Highlight</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
|
@ -181,6 +181,7 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
|||
public P createNewDisconnectedProvider() {
|
||||
P newProvider = createProvider(formatMgr.createClone(), false);
|
||||
newProvider.setClipboardService(tool.getService(ClipboardService.class));
|
||||
|
||||
disconnectedProviders.add(newProvider);
|
||||
if (dndProvider != null) {
|
||||
newProvider.addProgramDropProvider(dndProvider);
|
||||
|
|
|
@ -274,12 +274,14 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
|||
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
super.writeConfigState(saveState);
|
||||
formatMgr.saveState(saveState);
|
||||
connectedProvider.saveState(saveState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
super.readConfigState(saveState);
|
||||
formatMgr.readState(saveState);
|
||||
connectedProvider.readState(saveState);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ import java.awt.Point;
|
|||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
@ -31,6 +32,7 @@ import javax.swing.event.ChangeListener;
|
|||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.action.builder.ToggleActionBuilder;
|
||||
import docking.actions.PopupActionProvider;
|
||||
import docking.dnd.*;
|
||||
import docking.widgets.EventTrigger;
|
||||
|
@ -41,6 +43,7 @@ import docking.widgets.fieldpanel.support.*;
|
|||
import docking.widgets.tab.GTabPanel;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.context.ProgramLocationActionContext;
|
||||
import ghidra.app.nav.ListingPanelContainer;
|
||||
import ghidra.app.nav.LocationMemento;
|
||||
import ghidra.app.plugin.core.clipboard.CodeBrowserClipboardProvider;
|
||||
|
@ -68,6 +71,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
implements ProgramLocationListener, ProgramSelectionListener, Draggable, Droppable,
|
||||
ChangeListener, StringSelectionListener, PopupActionProvider {
|
||||
|
||||
private static final String SHOW_FUNCITON_VARS_OPTIONS_NAME = "SHOW_FUNCITON_VARS";
|
||||
private static final String OLD_NAME = "CodeBrowserPlugin";
|
||||
private static final String NAME = "Listing";
|
||||
private static final String TITLE = NAME + ": ";
|
||||
|
@ -126,6 +130,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
private FieldNavigator fieldNavigator;
|
||||
|
||||
private MultiListingLayoutModel multiModel;
|
||||
private ToggleDockingAction toggleVariablesAction;
|
||||
|
||||
public CodeViewerProvider(CodeBrowserPluginInterface plugin, FormatManager formatMgr,
|
||||
boolean isConnected) {
|
||||
|
@ -151,6 +156,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
|
||||
listingPanel = new ListingPanel(formatMgr);
|
||||
listingPanel.enablePropertyBasedColorModel(true);
|
||||
|
||||
decorationPanel = new ListingPanelContainer(listingPanel, isConnected);
|
||||
ListingMiddleMouseHighlightProvider listingHighlighter =
|
||||
createListingHighlighter(listingPanel, tool, decorationPanel);
|
||||
|
@ -429,6 +435,11 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
updateTitle();
|
||||
|
||||
listingPanel.setProgram(program);
|
||||
ListingModel listingModel = listingPanel.getListingModel();
|
||||
if (listingModel != null) {
|
||||
boolean shouldShowVariables = toggleVariablesAction.isSelected();
|
||||
listingModel.setAllFunctionVariablesOpen(shouldShowVariables);
|
||||
}
|
||||
codeViewerClipboardProvider.setProgram(program);
|
||||
codeViewerClipboardProvider.setListingLayoutModel(listingPanel.getListingModel());
|
||||
if (coordinatedListingPanelListener != null) {
|
||||
|
@ -470,17 +481,67 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
action = new GotoNextFunctionAction(tool, plugin.getName());
|
||||
tool.addAction(action);
|
||||
|
||||
toggleVariablesAction = new ToggleActionBuilder("Show Function Variables", plugin.getName())
|
||||
.popupMenuPath("Show/Hide All Variables")
|
||||
.popupMenuGroup("Variables")
|
||||
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Show_All_Variables"))
|
||||
.selected(true)
|
||||
.withContext(ProgramLocationActionContext.class)
|
||||
.enabledWhen(this::isInFunctionArea)
|
||||
.onAction(c -> showVariablesForAllFunctions(toggleVariablesAction.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
new ActionBuilder("Toggle Show Function Variables", plugin.getName())
|
||||
.popupMenuPath("Show/Hide Variables")
|
||||
.popupMenuGroup("Variables")
|
||||
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Show_Variables"))
|
||||
.keyBinding("SPACE")
|
||||
.withContext(ProgramLocationActionContext.class)
|
||||
.enabledWhen(this::isInFunctionArea)
|
||||
.onAction(c -> toggleShowVariables(c.getAddress()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
buildQuickTogleFieldActions();
|
||||
|
||||
}
|
||||
|
||||
private void toggleShowVariables(Address address) {
|
||||
ListingModel model = listingPanel.getListingModel();
|
||||
boolean open = model.areFunctionVariablesOpen(address);
|
||||
model.setAllFunctionVariablesOpen(!open);
|
||||
setLocation(new VariablesOpenCloseLocation(program, address));
|
||||
}
|
||||
|
||||
private void showVariablesForAllFunctions(boolean selected) {
|
||||
ListingModel model = listingPanel.getListingModel();
|
||||
model.setAllFunctionVariablesOpen(selected);
|
||||
}
|
||||
|
||||
private boolean isInFunctionArea(ProgramLocationActionContext context) {
|
||||
ProgramLocation location = context.getLocation();
|
||||
return location instanceof FunctionLocation ||
|
||||
location instanceof VariablesOpenCloseLocation;
|
||||
}
|
||||
|
||||
private void buildQuickTogleFieldActions() {
|
||||
List<String> quickToggleFieldNames = formatMgr.getQuickToggleFieldNames();
|
||||
int count = 0;
|
||||
for (String fieldName : quickToggleFieldNames) {
|
||||
DockingAction toggleAction = new ActionBuilder("Toggle " + fieldName, plugin.getName())
|
||||
String keyBinding = null;
|
||||
if (count < 5) {
|
||||
char c = (char) ('1' + count);
|
||||
keyBinding = "control shift " + c;
|
||||
}
|
||||
else {
|
||||
Msg.debug(this,
|
||||
"Excessive Field Toggle actions . No keybinding assigned for field: " +
|
||||
fieldName);
|
||||
}
|
||||
|
||||
new ActionBuilder("Toggle " + fieldName, plugin.getName())
|
||||
.popupMenuPath("Toggle Field", fieldName)
|
||||
.popupMenuGroup("Field", "" + count)
|
||||
.keyBinding(keyBinding)
|
||||
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Toggle_Field"))
|
||||
// only show this action when over the listing field header
|
||||
.popupWhen(c -> c.getContextObject() instanceof FieldHeaderLocation)
|
||||
|
@ -488,19 +549,10 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
.buildAndInstallLocal(this);
|
||||
|
||||
// automatically assign keybindings to the first 5 toggle fields.
|
||||
if (count < 5) {
|
||||
char c = (char) ('1' + count);
|
||||
toggleAction.setKeyBindingData(
|
||||
new KeyBindingData(c, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
|
||||
}
|
||||
else {
|
||||
Msg.debug(this,
|
||||
"Excessive Field Toggle actions . No keybinding assigned for field: " +
|
||||
fieldName);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
tool.setMenuGroup(new String[] { "Toggle Field" }, "Disassembly");
|
||||
|
||||
}
|
||||
|
||||
public ListingPanel getListingPanel() {
|
||||
|
@ -820,12 +872,19 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
void saveState(SaveState saveState) {
|
||||
saveState.putInt(DIVIDER_LOCATION, getListingPanel().getDividerLocation());
|
||||
saveState.putBoolean(HOVER_MODE, toggleHoverAction.isSelected());
|
||||
saveState.putBoolean(SHOW_FUNCITON_VARS_OPTIONS_NAME, toggleVariablesAction.isSelected());
|
||||
}
|
||||
|
||||
void readState(SaveState saveState) {
|
||||
getListingPanel().setDividerLocation(
|
||||
saveState.getInt(DIVIDER_LOCATION, ListingPanel.DEFAULT_DIVIDER_LOCATION));
|
||||
toggleHoverAction.setSelected(saveState.getBoolean(HOVER_MODE, true));
|
||||
boolean showVariables = saveState.getBoolean(SHOW_FUNCITON_VARS_OPTIONS_NAME, true);
|
||||
toggleVariablesAction.setSelected(showVariables);
|
||||
ListingModel listingModel = listingPanel.getListingModel();
|
||||
if (listingModel != null) {
|
||||
listingModel.setAllFunctionVariablesOpen(showVariables);
|
||||
}
|
||||
}
|
||||
|
||||
private void setHoverEnabled(boolean enabled) {
|
||||
|
@ -951,9 +1010,12 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
// (its done in an invoke later)
|
||||
Swing.runLater(() -> {
|
||||
newProvider.doSetProgram(program);
|
||||
SaveState saveState = new SaveState();
|
||||
saveState(saveState);
|
||||
newProvider.readState(saveState);
|
||||
newProvider.setLocation(currentLocation);
|
||||
newProvider.listingPanel.getFieldPanel()
|
||||
.setViewerPosition(vp.getIndex(), vp.getXOffset(), vp.getYOffset());
|
||||
newProvider.setLocation(currentLocation);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
/* ###
|
||||
* 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.util.viewer.field;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.util.viewer.proxy.EmptyProxy;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
|
||||
/**
|
||||
* FactoryField class for displaying the open/close field.
|
||||
*/
|
||||
public abstract class AbstractOpenCloseField implements ListingField {
|
||||
protected static final GIcon OPEN_ICON =
|
||||
new GIcon("icon.base.util.viewer.fieldfactory.openclose.open");
|
||||
protected static final GIcon CLOSED_ICON =
|
||||
new GIcon("icon.base.util.viewer.fieldfactory.openclose.closed");
|
||||
|
||||
private FieldFactory factory;
|
||||
protected int startX;
|
||||
protected int startY;
|
||||
protected int fieldWidth;
|
||||
protected int heightAbove;
|
||||
protected int heightBelow;
|
||||
protected ProxyObj<?> proxy;
|
||||
|
||||
protected boolean isOpen;
|
||||
|
||||
protected int toggleHandleSize;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param factory the FieldFactory that created this field.
|
||||
* @param proxy the object associated with this field.
|
||||
* @param metrics the FontMetrics used to render this field.
|
||||
* @param x the starting x position of this field.
|
||||
* @param width the width of this field.
|
||||
*/
|
||||
public AbstractOpenCloseField(FieldFactory factory, ProxyObj<?> proxy,
|
||||
FontMetrics metrics, int x, int width) {
|
||||
this.factory = factory;
|
||||
this.proxy = proxy;
|
||||
this.fieldWidth = width;
|
||||
this.startX = x;
|
||||
|
||||
this.heightAbove = metrics.getAscent();
|
||||
this.heightBelow = metrics.getLeading() + metrics.getDescent();
|
||||
this.toggleHandleSize = AbstractOpenCloseField.getOpenCloseHandleSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldFactory getFieldFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyObj<?> getProxy() {
|
||||
if (proxy == null) {
|
||||
return EmptyProxy.EMPTY_PROXY;
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeightAbove() {
|
||||
return heightAbove;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeightBelow() {
|
||||
return heightBelow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the yPos relative to the overall layout.
|
||||
* @param yPos the starting Y position of the layout row.
|
||||
* @param heightAbove the heightAbove the alignment line in the layout row.
|
||||
* @param heightBelow the heightBelow the alignment line in the layout row.
|
||||
*/
|
||||
public void setYPos(int yPos, int heightAbove, int heightBelow) {
|
||||
this.startY = yPos;
|
||||
this.heightAbove = heightAbove;
|
||||
this.heightBelow = heightBelow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredWidth() {
|
||||
return getWidth(); // does the width of this field vary?
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return heightAbove + heightBelow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartX() {
|
||||
return startX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vertical position of this field.
|
||||
* @return the position
|
||||
*/
|
||||
public int getStartY() {
|
||||
return startY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the starting vertical position of this field.
|
||||
* @param startY the starting vertical position.
|
||||
*/
|
||||
public void setStartY(int startY) {
|
||||
this.startY = startY;
|
||||
}
|
||||
|
||||
protected void paintCursor(Graphics g, Color cursorColor, RowColLocation cursorLoc) {
|
||||
if (cursorLoc != null) {
|
||||
g.setColor(cursorColor);
|
||||
Rectangle cursorBounds = getCursorBounds(cursorLoc.row(), cursorLoc.col());
|
||||
g.fillRect(cursorBounds.x, cursorBounds.y, cursorBounds.width, cursorBounds.height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(int x, int y) {
|
||||
if ((x < startX) || (x >= startX + fieldWidth) || (y < startY) ||
|
||||
(y >= startY + heightAbove + heightBelow)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumDataRows() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumRows() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumCols(int row) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX(int row, int col) {
|
||||
return startX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY(int row) {
|
||||
return startY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRow(int y) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCol(int row, int x) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(int row, int col) {
|
||||
return ((row == 0) && (col == 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getCursorBounds(int row, int col) {
|
||||
if (!isValid(row, col)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Rectangle(startX, -heightAbove, 2, heightAbove + heightBelow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollableUnitIncrement(int topOfScreen, int direction, int max) {
|
||||
if ((topOfScreen < startY) || (topOfScreen > startY + heightAbove + heightBelow)) {
|
||||
return max;
|
||||
}
|
||||
|
||||
if (direction > 0) { // if scrolling down
|
||||
return heightAbove + heightBelow - (topOfScreen - startY);
|
||||
}
|
||||
return startY - topOfScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimary() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rowHeightChanged(int newHeightAbove, int newHeightBelow) {
|
||||
this.heightAbove = newHeightAbove;
|
||||
this.heightBelow = newHeightBelow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextWithLineSeparators() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowColLocation textOffsetToScreenLocation(int textOffset) {
|
||||
return new DefaultRowColLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int screenLocationToTextOffset(int row, int col) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getClickedObject(FieldLocation fieldLocation) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the open state of this field.
|
||||
*/
|
||||
public abstract void toggleOpenCloseState();
|
||||
|
||||
//==================================================================================================
|
||||
// Static Methods
|
||||
//==================================================================================================
|
||||
|
||||
static int getOpenCloseHandleSize() {
|
||||
return OPEN_ICON.getIconWidth();
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -68,7 +68,7 @@ public class IndentField implements ListingField {
|
|||
|
||||
// this class is dependent upon the OpenClosedField in that they work together to perform
|
||||
// painting
|
||||
toggleHandleSize = OpenCloseField.getOpenCloseHandleSize();
|
||||
toggleHandleSize = AbstractOpenCloseField.getOpenCloseHandleSize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,41 +15,25 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import static ghidra.app.util.viewer.field.AbstractOpenCloseField.*;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import generic.theme.GIcon;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.util.viewer.proxy.EmptyProxy;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
import ghidra.program.model.listing.Data;
|
||||
|
||||
/**
|
||||
* FactoryField class for displaying the open/close field.
|
||||
*/
|
||||
public class OpenCloseField implements ListingField {
|
||||
private static final GIcon OPEN_ICON =
|
||||
new GIcon("icon.base.util.viewer.fieldfactory.openclose.open");
|
||||
private static final GIcon CLOSED_ICON =
|
||||
new GIcon("icon.base.util.viewer.fieldfactory.openclose.closed");
|
||||
|
||||
private FieldFactory factory;
|
||||
private int startX;
|
||||
private int startY;
|
||||
private int fieldWidth;
|
||||
private int heightAbove;
|
||||
private int heightBelow;
|
||||
private ProxyObj<?> proxy;
|
||||
|
||||
private boolean isOpen;
|
||||
public class OpenCloseField extends AbstractOpenCloseField {
|
||||
private int indentLevel;
|
||||
private boolean isLast;
|
||||
|
||||
private int toggleHandleSize;
|
||||
private int insetSpace = 1;
|
||||
|
||||
/**
|
||||
|
@ -64,51 +48,10 @@ public class OpenCloseField implements ListingField {
|
|||
*/
|
||||
public OpenCloseField(FieldFactory factory, ProxyObj<?> proxy, int indentLevel,
|
||||
FontMetrics metrics, int x, int width, boolean isLast) {
|
||||
this.factory = factory;
|
||||
this.proxy = proxy;
|
||||
super(factory, proxy, metrics, x, width);
|
||||
this.isOpen = proxy.getListingLayoutModel().isOpen((Data) proxy.getObject());
|
||||
this.fieldWidth = width;
|
||||
this.startX = x;
|
||||
this.indentLevel = indentLevel;
|
||||
this.isLast = isLast;
|
||||
this.heightAbove = metrics.getAscent();
|
||||
this.heightBelow = metrics.getLeading() + metrics.getDescent();
|
||||
this.toggleHandleSize = OpenCloseField.getOpenCloseHandleSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldFactory getFieldFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyObj<?> getProxy() {
|
||||
if (proxy == null) {
|
||||
return EmptyProxy.EMPTY_PROXY;
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeightAbove() {
|
||||
return heightAbove;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeightBelow() {
|
||||
return heightBelow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the yPos relative to the overall layout.
|
||||
* @param yPos the starting Y position of the layout row.
|
||||
* @param heightAbove the heightAbove the alignment line in the layout row.
|
||||
* @param heightBelow the heightBelow the alignment line in the layout row.
|
||||
*/
|
||||
public void setYPos(int yPos, int heightAbove, int heightBelow) {
|
||||
this.startY = yPos;
|
||||
this.heightAbove = heightAbove;
|
||||
this.heightBelow = heightBelow;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,37 +59,6 @@ public class OpenCloseField implements ListingField {
|
|||
return (indentLevel + 1) * fieldWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredWidth() {
|
||||
return getWidth(); // does the width of this field vary?
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return heightAbove + heightBelow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartX() {
|
||||
return startX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vertical position of this field.
|
||||
* @return the position
|
||||
*/
|
||||
public int getStartY() {
|
||||
return startY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the starting vertical position of this field.
|
||||
* @param startY the starting vertical position.
|
||||
*/
|
||||
public void setStartY(int startY) {
|
||||
this.startY = startY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(JComponent c, Graphics g, PaintContext context,
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
|
||||
|
@ -211,132 +123,11 @@ public class OpenCloseField implements ListingField {
|
|||
paintCursor(g, context.getCursorColor(), cursorLoc);
|
||||
}
|
||||
|
||||
private void paintCursor(Graphics g, Color cursorColor, RowColLocation cursorLoc) {
|
||||
if (cursorLoc != null) {
|
||||
g.setColor(cursorColor);
|
||||
Rectangle cursorBounds = getCursorBounds(cursorLoc.row(), cursorLoc.col());
|
||||
g.fillRect(cursorBounds.x, cursorBounds.y, cursorBounds.width, cursorBounds.height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(int x, int y) {
|
||||
if ((x < startX) || (x >= startX + fieldWidth) || (y < startY) ||
|
||||
(y >= startY + heightAbove + heightBelow)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumDataRows() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumRows() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumCols(int row) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX(int row, int col) {
|
||||
return startX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY(int row) {
|
||||
return startY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRow(int y) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCol(int row, int x) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(int row, int col) {
|
||||
return ((row == 0) && (col == 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getCursorBounds(int row, int col) {
|
||||
if (!isValid(row, col)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Rectangle(startX, -heightAbove, 2, heightAbove + heightBelow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollableUnitIncrement(int topOfScreen, int direction, int max) {
|
||||
if ((topOfScreen < startY) || (topOfScreen > startY + heightAbove + heightBelow)) {
|
||||
return max;
|
||||
}
|
||||
|
||||
if (direction > 0) { // if scrolling down
|
||||
return heightAbove + heightBelow - (topOfScreen - startY);
|
||||
}
|
||||
return startY - topOfScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimary() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rowHeightChanged(int newHeightAbove, int newHeightBelow) {
|
||||
this.heightAbove = newHeightAbove;
|
||||
this.heightBelow = newHeightBelow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextWithLineSeparators() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowColLocation textOffsetToScreenLocation(int textOffset) {
|
||||
return new DefaultRowColLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int screenLocationToTextOffset(int row, int col) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getClickedObject(FieldLocation fieldLocation) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the open state of this field.
|
||||
*/
|
||||
@Override
|
||||
public void toggleOpenCloseState() {
|
||||
proxy.getListingLayoutModel().toggleOpen((Data) proxy.getObject());
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Static Methods
|
||||
//==================================================================================================
|
||||
|
||||
static int getOpenCloseHandleSize() {
|
||||
return OPEN_ICON.getIconWidth();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,14 +15,15 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class OpenCloseFieldMouseHandler implements FieldMouseHandlerExtension {
|
||||
private final static Class<?>[] SUPPORTED_CLASSES = new Class[] { OpenCloseField.class };
|
||||
private final static Class<?>[] SUPPORTED_CLASSES =
|
||||
new Class[] { OpenCloseField.class, VariableOpenCloseField.class };
|
||||
|
||||
@Override
|
||||
public boolean fieldElementClicked(Object clickedObject, Navigatable sourceNavigatable,
|
||||
|
@ -32,7 +33,7 @@ public class OpenCloseFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
return false;
|
||||
}
|
||||
|
||||
OpenCloseField field = (OpenCloseField) clickedObject;
|
||||
AbstractOpenCloseField field = (AbstractOpenCloseField) clickedObject;
|
||||
field.toggleOpenCloseState();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/* ###
|
||||
* 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.util.viewer.field;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
import ghidra.app.util.viewer.proxy.VariableProxy;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
* FactoryField class for displaying the open/close field widget for function variables.
|
||||
*/
|
||||
public class VariableOpenCloseField extends AbstractOpenCloseField {
|
||||
private static final Font HIDDEN_FONT = Gui.getFont("font.listing.base.hidden.field");
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param factory the FieldFactory that created this field.
|
||||
* @param proxy the object associated with this field.
|
||||
* @param metrics the FontMetrics used to render this field.
|
||||
* @param x the starting x position of this field.
|
||||
* @param width the width of this field.
|
||||
*/
|
||||
public VariableOpenCloseField(FieldFactory factory, ProxyObj<?> proxy,
|
||||
FontMetrics metrics, int x, int width) {
|
||||
super(factory, proxy, metrics, x, width);
|
||||
if (proxy instanceof VariableProxy variableProxy) {
|
||||
Address functionAddress = variableProxy.getFunctionAddress();
|
||||
this.isOpen = proxy.getListingLayoutModel().areFunctionVariablesOpen(functionAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
if (isOpen) {
|
||||
return fieldWidth;
|
||||
}
|
||||
return fieldWidth + 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(JComponent c, Graphics g, PaintContext context,
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
|
||||
int rowHeight) {
|
||||
|
||||
// center in the heightAbove area (negative, since 0 is the baseline of text, which is at
|
||||
// the bottom of the heightAbove)
|
||||
int toggleHandleStartY = -((heightAbove / 2) + (toggleHandleSize / 2));
|
||||
int toggleHandleStartX = startX;
|
||||
|
||||
// If we're in printing mode, trying to render these open/close images
|
||||
// causes the JVM to bomb. We'd like to eventually figure out why but in
|
||||
// the meantime we can safely comment this out and still generate an acceptable
|
||||
// image.
|
||||
//
|
||||
if (!context.isPrinting()) {
|
||||
if (isOpen) {
|
||||
g.drawImage(OPEN_ICON.getImageIcon().getImage(), toggleHandleStartX,
|
||||
toggleHandleStartY, context.getBackground(), null);
|
||||
}
|
||||
else {
|
||||
g.drawImage(CLOSED_ICON.getImageIcon().getImage(), toggleHandleStartX,
|
||||
toggleHandleStartY, context.getBackground(), null);
|
||||
}
|
||||
}
|
||||
|
||||
g.setColor(Palette.LIGHT_GRAY);
|
||||
|
||||
if (!isOpen) {
|
||||
Font font = g.getFont();
|
||||
g.setFont(HIDDEN_FONT);
|
||||
g.drawString("Variables", startX + fieldWidth + 10, 0);
|
||||
g.setFont(font);
|
||||
}
|
||||
paintCursor(g, context.getCursorColor(), cursorLoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the open state of this field.
|
||||
*/
|
||||
@Override
|
||||
public void toggleOpenCloseState() {
|
||||
if (proxy instanceof VariableProxy variableProxy) {
|
||||
Address functionAddress = variableProxy.getFunctionAddress();
|
||||
proxy.getListingLayoutModel().setFunctionVariablesOpen(functionAddress, !isOpen);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/* ###
|
||||
* 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.util.viewer.field;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
import ghidra.app.util.viewer.proxy.VariableProxy;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.Variable;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.VariablesOpenCloseLocation;
|
||||
|
||||
/**
|
||||
* Generates Open/Close Fields for variables under functions.
|
||||
*/
|
||||
public class VariablesOpenCloseFieldFactory extends FieldFactory {
|
||||
|
||||
public static final String FIELD_NAME = "+";
|
||||
|
||||
public VariablesOpenCloseFieldFactory() {
|
||||
super(FIELD_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param model the model that the field belongs to.
|
||||
* @param hlProvider the HightLightStringProvider.
|
||||
* @param displayOptions the Options for display properties.
|
||||
* @param fieldOptions the Options for field specific properties.
|
||||
*/
|
||||
private VariablesOpenCloseFieldFactory(FieldFormatModel model,
|
||||
ListingHighlightProvider hlProvider,
|
||||
Options displayOptions, Options fieldOptions) {
|
||||
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
|
||||
servicesChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FactoryField for the given object at index index.
|
||||
* @param varWidth the amount of variable width spacing for any fields
|
||||
* before this one.
|
||||
* @param proxy the object whose properties should be displayed.
|
||||
*/
|
||||
@Override
|
||||
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
|
||||
|
||||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
if (proxy instanceof VariableProxy variableProxy) {
|
||||
if (variableProxy.isFirst()) {
|
||||
return new VariableOpenCloseField(this, proxy, getMetrics(), startX + varWidth,
|
||||
width);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(int row, int col, ListingField bf) {
|
||||
ProxyObj<?> proxy = bf.getProxy();
|
||||
if (proxy instanceof VariableProxy variableProxy) {
|
||||
Program program = variableProxy.getProgram();
|
||||
Address functionAddress = variableProxy.getFunctionAddress();
|
||||
return new VariablesOpenCloseLocation(program, functionAddress);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
|
||||
ProgramLocation programLoc) {
|
||||
|
||||
if (!(programLoc instanceof VariablesOpenCloseLocation)) {
|
||||
return null;
|
||||
}
|
||||
return new FieldLocation(index, fieldNum, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsType(int category, Class<?> proxyObjectClass) {
|
||||
if (!Variable.class.isAssignableFrom(proxyObjectClass)) {
|
||||
return false;
|
||||
}
|
||||
return (category == FieldFormatModel.FUNCTION_VARS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldFactory newInstance(FieldFormatModel fieldModel, ListingHighlightProvider provider,
|
||||
ToolOptions displayOptions, ToolOptions fieldOptions) {
|
||||
return new VariablesOpenCloseFieldFactory(fieldModel, provider, displayOptions,
|
||||
fieldOptions);
|
||||
}
|
||||
}
|
|
@ -516,7 +516,13 @@ public class FormatManager implements OptionsChangeListener {
|
|||
Element rowElem = new Element("ROW");
|
||||
|
||||
Element colElem = new Element("FIELD");
|
||||
colElem.setAttribute("WIDTH", "90");
|
||||
colElem.setAttribute("NAME", "+");
|
||||
colElem.setAttribute("WIDTH", "20");
|
||||
colElem.setAttribute("ENABLED", "true");
|
||||
rowElem.addContent(colElem);
|
||||
|
||||
colElem = new Element("FIELD");
|
||||
colElem.setAttribute("WIDTH", "70");
|
||||
colElem.setAttribute("ENABLED", "true");
|
||||
rowElem.addContent(colElem);
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -73,6 +73,21 @@ public class EmptyListingModel implements ListingModel {
|
|||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFunctionVariablesOpen(Address functionAddress, boolean open) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllFunctionVariablesOpen(boolean open) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areFunctionVariablesOpen(Address FunctionAddress) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openAllData(Data data, TaskMonitor monitor) {
|
||||
// stub
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.listingpanel;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.Layout;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.framework.options.Options;
|
||||
|
@ -23,6 +24,9 @@ import ghidra.program.model.listing.Data;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Model displaying program data in a {@link FieldPanel}
|
||||
*/
|
||||
public interface ListingModel {
|
||||
|
||||
static final String FUNCTION_POINTER_OPTION_GROUP_NAME = "Function Pointers";
|
||||
|
@ -42,6 +46,9 @@ public interface ListingModel {
|
|||
|
||||
public Layout getLayout(Address address, boolean isGapAddress);
|
||||
|
||||
/**
|
||||
* {@return the width of the longest layout this model can produce.}
|
||||
*/
|
||||
public int getMaxWidth();
|
||||
|
||||
/**
|
||||
|
@ -59,6 +66,28 @@ public interface ListingModel {
|
|||
*/
|
||||
public void toggleOpen(Data data);
|
||||
|
||||
/**
|
||||
* Sets whether or not to display function variables for the function at the given address.
|
||||
* @param FunctionAddress the address of the function
|
||||
* @param open if true, the variables are displayed, otherwise they are hidden
|
||||
*/
|
||||
public void setFunctionVariablesOpen(Address FunctionAddress, boolean open);
|
||||
|
||||
/**
|
||||
* Checks if the function variables are being displayed at the given address
|
||||
* @param FunctionAddress the address of the function
|
||||
* @return true if the variables are being displayed for the function at the given address
|
||||
*/
|
||||
public boolean areFunctionVariablesOpen(Address FunctionAddress);
|
||||
|
||||
/**
|
||||
* Sets the display of variables for all functions. This basically sets the default state,
|
||||
* but the state can be overridden for individual functions. Changing this value erases all
|
||||
* individually set values.
|
||||
* @param open if true, show function variables
|
||||
*/
|
||||
public void setAllFunctionVariablesOpen(boolean open);
|
||||
|
||||
/**
|
||||
* Opens the given data, but not any sub-components.
|
||||
*
|
||||
|
@ -106,18 +135,45 @@ public interface ListingModel {
|
|||
*/
|
||||
public void closeAllData(AddressSetView addresses, TaskMonitor monitor);
|
||||
|
||||
/**
|
||||
* Adds a listener for changes to this model.
|
||||
* @param listener the listener to be notified
|
||||
*/
|
||||
public void addListener(ListingModelListener listener);
|
||||
|
||||
/**
|
||||
* Removes a listener from those being notified of model changes.
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
public void removeListener(ListingModelListener listener);
|
||||
|
||||
/**
|
||||
* {@return the program being displayed by this model.}
|
||||
*/
|
||||
public Program getProgram();
|
||||
|
||||
/**
|
||||
* {@return true if the program being displayed by this listing has been closed (and therefor
|
||||
* the model is invalid.)}
|
||||
*/
|
||||
public boolean isClosed();
|
||||
|
||||
/**
|
||||
* Sets the {@link FormatManager} for this model which determines the layout of the fields.
|
||||
* @param formatManager the new FormatManager to use
|
||||
*/
|
||||
public void setFormatManager(FormatManager formatManager);
|
||||
|
||||
/**
|
||||
* Disposes this model
|
||||
*/
|
||||
public void dispose();
|
||||
|
||||
/**
|
||||
* Adjusts each range in the given address set to be on code unit boundaries.
|
||||
* @param addressSet the address set to be adjusted
|
||||
* @return a new AddressSet where each range is on a code unit boundary
|
||||
*/
|
||||
public AddressSet adjustAddressSetToCodeUnitBoundaries(AddressSet addressSet);
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,7 +27,7 @@ import ghidra.app.util.viewer.field.DummyFieldFactory;
|
|||
import ghidra.app.util.viewer.field.ListingField;
|
||||
import ghidra.app.util.viewer.format.*;
|
||||
import ghidra.app.util.viewer.proxy.*;
|
||||
import ghidra.app.util.viewer.util.OpenCloseManager;
|
||||
import ghidra.app.util.viewer.util.ProgramOpenCloseManager;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
|
@ -43,7 +43,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
DomainObjectListener, ChangeListener, OptionsChangeListener {
|
||||
|
||||
protected final Program program;
|
||||
private OpenCloseManager openCloseMgr = new OpenCloseManager();
|
||||
private ProgramOpenCloseManager openCloseMgr = new ProgramOpenCloseManager();
|
||||
private FormatManager formatMgr;
|
||||
private ToolOptions fieldOptions;
|
||||
private boolean showExternalFunctionPointerFormat;
|
||||
|
@ -172,17 +172,17 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
if (function != null) {
|
||||
format = formatMgr.getFunctionFormat();
|
||||
format.addLayouts(list, 0, new FunctionProxy(this, program, addr, function));
|
||||
Parameter[] params = function.getParameters();
|
||||
format = formatMgr.getFunctionVarFormat();
|
||||
format.addLayouts(list, 0,
|
||||
new VariableProxy(this, program, addr, function, function.getReturn()));
|
||||
for (Parameter param : params) {
|
||||
format.addLayouts(list, 0, new VariableProxy(this, program, addr, function, param));
|
||||
boolean variablesOpen = openCloseMgr.isFunctionVariablesOpen(function.getEntryPoint());
|
||||
if (variablesOpen) {
|
||||
addReturn(addr, list, format, function);
|
||||
addParameters(addr, list, format, function);
|
||||
addLocals(addr, list, format, function);
|
||||
}
|
||||
Variable[] vars = function.getLocalVariables();
|
||||
for (Variable var : vars) {
|
||||
format.addLayouts(list, 0, new VariableProxy(this, program, addr, function, var));
|
||||
else {
|
||||
format.addLayouts(list, 0, new ClosedVariableProxy(this, program, addr, function));
|
||||
}
|
||||
|
||||
}
|
||||
if (cu != null) {
|
||||
format = formatMgr.getCodeUnitFormat();
|
||||
|
@ -213,6 +213,30 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
|
||||
}
|
||||
|
||||
private void addReturn(Address addr, List<RowLayout> list, FieldFormatModel format,
|
||||
Function function) {
|
||||
format.addLayouts(list, 0,
|
||||
new VariableProxy(this, program, addr, function, function.getReturn(), true));
|
||||
}
|
||||
|
||||
private void addLocals(Address addr, List<RowLayout> list, FieldFormatModel format,
|
||||
Function function) {
|
||||
Variable[] vars = function.getLocalVariables();
|
||||
for (Variable var : vars) {
|
||||
format.addLayouts(list, 0,
|
||||
new VariableProxy(this, program, addr, function, var, false));
|
||||
}
|
||||
}
|
||||
|
||||
private void addParameters(Address addr, List<RowLayout> list, FieldFormatModel format,
|
||||
Function function) {
|
||||
Parameter[] params = function.getParameters();
|
||||
for (Parameter param : params) {
|
||||
format.addLayouts(list, 0,
|
||||
new VariableProxy(this, program, addr, function, param, false));
|
||||
}
|
||||
}
|
||||
|
||||
private Function getPointerReferencedFunction(Data data) {
|
||||
|
||||
Reference ref = data.getPrimaryReference(0);
|
||||
|
@ -239,7 +263,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
if (cu instanceof Data) {
|
||||
Data data = (Data) cu;
|
||||
if (data.getNumComponents() > 0) {
|
||||
if (openCloseMgr.isOpen(data.getMinAddress())) {
|
||||
if (openCloseMgr.isDataOpen(data.getMinAddress())) {
|
||||
Address openAddr = findOpenDataAfter(address, data);
|
||||
if (openAddr != null) {
|
||||
return openAddr;
|
||||
|
@ -255,8 +279,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
Data data;
|
||||
DataType dt = parent.getBaseDataType();
|
||||
if (dt instanceof Union) {
|
||||
int index =
|
||||
openCloseMgr.getOpenIndex(parent.getMinAddress(), parent.getComponentPath());
|
||||
int index = openCloseMgr.getOpenDataIndex(parent);
|
||||
if (index < 0) {
|
||||
return null;
|
||||
}
|
||||
|
@ -288,7 +311,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
return null;
|
||||
}
|
||||
if (data.getNumComponents() > 0) {
|
||||
if (openCloseMgr.isOpen(data.getMinAddress(), data.getComponentPath())) {
|
||||
if (openCloseMgr.isDataOpen(data)) {
|
||||
Address openAddr = findOpenDataAfter(address, data);
|
||||
if (openAddr != null) {
|
||||
return openAddr;
|
||||
|
@ -344,7 +367,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
if (cu instanceof Data) {
|
||||
Data data = (Data) cu;
|
||||
if (data.getNumComponents() > 0) {
|
||||
if (openCloseMgr.isOpen(data.getMinAddress())) {
|
||||
if (openCloseMgr.isDataOpen(data.getMinAddress())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +382,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
Data data;
|
||||
if (parent.getBaseDataType() instanceof Union) {
|
||||
int index =
|
||||
openCloseMgr.getOpenIndex(parent.getMinAddress(), parent.getComponentPath());
|
||||
openCloseMgr.getOpenDataIndex(parent);
|
||||
if (index < 0) {
|
||||
return null;
|
||||
}
|
||||
|
@ -376,7 +399,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
}
|
||||
|
||||
if (data.getNumComponents() > 0) {
|
||||
if (openCloseMgr.isOpen(data.getMinAddress(), data.getComponentPath())) {
|
||||
if (openCloseMgr.isDataOpen(data)) {
|
||||
Address openAddr = findOpenDataBefore(addr, data);
|
||||
if (openAddr != null) {
|
||||
return openAddr;
|
||||
|
@ -396,11 +419,11 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
|
||||
private void addOpenData(List<Data> list, Data data, Address addr) {
|
||||
Address dataAddr = data.getMinAddress();
|
||||
if (openCloseMgr.isOpen(dataAddr, data.getComponentPath())) {
|
||||
if (openCloseMgr.isDataOpen(data)) {
|
||||
DataType dt = data.getBaseDataType();
|
||||
if (dt instanceof Union) {
|
||||
int openIndex =
|
||||
openCloseMgr.getOpenIndex(data.getMinAddress(), data.getComponentPath());
|
||||
openCloseMgr.getOpenDataIndex(data);
|
||||
int numComps = ((Union) dt).getNumComponents();
|
||||
if (openIndex < 0) {
|
||||
openIndex = numComps;
|
||||
|
@ -437,8 +460,8 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
DataType dt = data.getBaseDataType();
|
||||
if (dt instanceof Union) {
|
||||
Address dataAddr = data.getMinAddress();
|
||||
if (openCloseMgr.isOpen(dataAddr, data.getComponentPath())) {
|
||||
int openIndex = openCloseMgr.getOpenIndex(dataAddr, data.getComponentPath());
|
||||
if (openCloseMgr.isDataOpen(data)) {
|
||||
int openIndex = openCloseMgr.getOpenDataIndex(data);
|
||||
int i = openIndex;
|
||||
int numComps = ((Union) dt).getNumComponents();
|
||||
if (i < 0) {
|
||||
|
@ -459,22 +482,37 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
|
||||
@Override
|
||||
public boolean isOpen(Data data) {
|
||||
return openCloseMgr.isOpen(data);
|
||||
return openCloseMgr.isDataOpen(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleOpen(Data data) {
|
||||
openCloseMgr.toggleOpen(data);
|
||||
openCloseMgr.toggleDataOpen(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFunctionVariablesOpen(Address functionAddress, boolean open) {
|
||||
openCloseMgr.setFunctionVariablesOpen(functionAddress, open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllFunctionVariablesOpen(boolean open) {
|
||||
openCloseMgr.setAllFunctionVariablesOpen(open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areFunctionVariablesOpen(Address FunctionAddress) {
|
||||
return openCloseMgr.isFunctionVariablesOpen(FunctionAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openAllData(Data data, TaskMonitor monitor) {
|
||||
openCloseMgr.openAllData(data, monitor);
|
||||
openCloseMgr.openDataRecursively(data, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeAllData(Data data, TaskMonitor monitor) {
|
||||
openCloseMgr.closeAllData(data, monitor);
|
||||
openCloseMgr.closeDataRecursively(data, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -494,8 +532,8 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
|||
|
||||
@Override
|
||||
public boolean openData(Data data) {
|
||||
return openCloseMgr.openData(data);
|
||||
|
||||
openCloseMgr.openData(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void notifyDataChanged(boolean updateImmediately) {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -177,6 +177,21 @@ public class ListingModelConverter implements ListingModel {
|
|||
model.toggleOpen(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFunctionVariablesOpen(Address functionAddress, boolean open) {
|
||||
model.setFunctionVariablesOpen(functionAddress, open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllFunctionVariablesOpen(boolean open) {
|
||||
model.setAllFunctionVariablesOpen(open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areFunctionVariablesOpen(Address FunctionAddress) {
|
||||
return model.areFunctionVariablesOpen(FunctionAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet adjustAddressSetToCodeUnitBoundaries(AddressSet addressSet) {
|
||||
AddressSet compatibleAddressSet = DiffUtility.getCompatibleAddressSet(addressSet, program);
|
||||
|
|
|
@ -271,6 +271,21 @@ public class MultiListingLayoutModel implements ListingModelListener, FormatMode
|
|||
models[modelID].toggleOpen(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFunctionVariablesOpen(Address functionAddress, boolean open) {
|
||||
models[modelID].setFunctionVariablesOpen(functionAddress, open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllFunctionVariablesOpen(boolean open) {
|
||||
models[modelID].setAllFunctionVariablesOpen(open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areFunctionVariablesOpen(Address FunctionAddress) {
|
||||
return models[modelID].areFunctionVariablesOpen(FunctionAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean openData(Data data) {
|
||||
return models[modelID].openData(data);
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/* ###
|
||||
* 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.util.viewer.proxy;
|
||||
|
||||
import ghidra.app.util.viewer.listingpanel.ListingModel;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Variable Proxy for when the function variables aren't being shown. It doesn't include
|
||||
* a variable object which prevents the various variable field factories from triggering.
|
||||
*/
|
||||
public class ClosedVariableProxy extends VariableProxy {
|
||||
|
||||
public ClosedVariableProxy(ListingModel model, Program program, Address locationAddr,
|
||||
Function fun) {
|
||||
super(model, program, locationAddr, fun, null, true);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -37,6 +37,7 @@ public class VariableProxy extends ProxyObj<Variable> {
|
|||
private int firstUseOffset;
|
||||
private Variable var;
|
||||
private int ordinal = -1;
|
||||
private boolean isFirst;
|
||||
|
||||
/**
|
||||
* Constructs a proxy for a variable.
|
||||
|
@ -45,20 +46,25 @@ public class VariableProxy extends ProxyObj<Variable> {
|
|||
* @param locationAddr the listing address at which the function exists or was inferred via reference
|
||||
* @param fun the function containing the variable.
|
||||
* @param var the variable to proxy.
|
||||
* @param isFirst true if this is the first parameter or variable
|
||||
*/
|
||||
public VariableProxy(ListingModel model, Program program, Address locationAddr, Function fun,
|
||||
Variable var) {
|
||||
Variable var, boolean isFirst) {
|
||||
super(model);
|
||||
this.program = program;
|
||||
this.locationAddr = locationAddr;
|
||||
this.var = var;
|
||||
this.isFirst = isFirst;
|
||||
this.functionAddr = fun.getEntryPoint();
|
||||
if (var instanceof Parameter) {
|
||||
ordinal = ((Parameter) var).getOrdinal();
|
||||
if (var != null) {
|
||||
if (var instanceof Parameter) {
|
||||
ordinal = ((Parameter) var).getOrdinal();
|
||||
}
|
||||
|
||||
Varnode firstVarnode = var.getFirstStorageVarnode();
|
||||
storageAddr = firstVarnode != null ? firstVarnode.getAddress() : null;
|
||||
firstUseOffset = var.getFirstUseOffset();
|
||||
}
|
||||
Varnode firstVarnode = var.getFirstStorageVarnode();
|
||||
storageAddr = firstVarnode != null ? firstVarnode.getAddress() : null;
|
||||
firstUseOffset = var.getFirstUseOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -126,6 +132,10 @@ public class VariableProxy extends ProxyObj<Variable> {
|
|||
return functionAddr;
|
||||
}
|
||||
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address a) {
|
||||
Variable v = getObject();
|
||||
|
@ -134,4 +144,8 @@ public class VariableProxy extends ProxyObj<Variable> {
|
|||
}
|
||||
return Objects.equals(v.getMinAddress(), a);
|
||||
}
|
||||
|
||||
public boolean isFirst() {
|
||||
return isFirst;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/* ###
|
||||
* 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.util.viewer.util;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
* Class to maintain a simple open close/state for address locations. The default open/close
|
||||
* state can be set and then a set of address is kept for the locations that are the opposite
|
||||
* of the default.
|
||||
*/
|
||||
public class AddressBasedOpenCloseManager {
|
||||
private boolean openByDefault = true;
|
||||
private Set<Address> addresses = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Checks if the state is "open" for the given address.
|
||||
* @param address the address to test
|
||||
* @return true if the state of the given address is "open"
|
||||
*/
|
||||
public boolean isOpen(Address address) {
|
||||
boolean contains = addresses.contains(address);
|
||||
return openByDefault ? !contains : contains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state at the given address to be "open".
|
||||
* @param address the address to set "open"
|
||||
*/
|
||||
public void open(Address address) {
|
||||
if (openByDefault) {
|
||||
addresses.remove(address);
|
||||
}
|
||||
else {
|
||||
addresses.add(address);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state at the given address to be "closed".
|
||||
* @param address the address to set "closed"
|
||||
*/
|
||||
public void close(Address address) {
|
||||
if (openByDefault) {
|
||||
addresses.add(address);
|
||||
}
|
||||
else {
|
||||
addresses.remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the default state is "open".
|
||||
* @return true if the default state for addresses is "open"
|
||||
*/
|
||||
public boolean isOpenByDefault() {
|
||||
return openByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all address to "open" (Makes "open" the default state and clears all individual
|
||||
* settings.
|
||||
*/
|
||||
public void openAll() {
|
||||
openByDefault = true;
|
||||
addresses.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all address to "closed" (Makes "closed" the default state and clears all individual
|
||||
* settings.
|
||||
*/
|
||||
public void closeAll() {
|
||||
openByDefault = false;
|
||||
addresses.clear();
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,9 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.util;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
|
@ -27,33 +26,27 @@ import ghidra.util.task.TaskMonitorAdapter;
|
|||
/**
|
||||
* Manages the open/close state of structures and arrays at specific addresses.
|
||||
*/
|
||||
public class OpenCloseManager {
|
||||
public class DataOpenCloseManager {
|
||||
/**
|
||||
* The map stores an int[] for each address that has something open.
|
||||
* If map.get(address) returns null then outermost level is closed.
|
||||
*/
|
||||
private Map<Address, int[]> map = new HashMap<>();
|
||||
|
||||
private List<ChangeListener> listeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Marks the given data as open. This method notifies listeners of changes.
|
||||
* @param data The data to open.
|
||||
* @return true if the data location was opened (false if already open or can't be opened)
|
||||
*/
|
||||
public boolean openData(Data data) {
|
||||
public void openData(Data data) {
|
||||
if (data.getComponent(0) == null) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
Address addr = data.getMinAddress();
|
||||
int[] path = data.getComponentPath();
|
||||
if (isOpen(addr, path)) {
|
||||
return false;
|
||||
if (!isDataOpen(addr, path)) {
|
||||
openData(addr, path);
|
||||
}
|
||||
open(addr, path);
|
||||
notifyDataToggled();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,11 +59,10 @@ public class OpenCloseManager {
|
|||
}
|
||||
Address addr = data.getMinAddress();
|
||||
int[] path = data.getComponentPath();
|
||||
if (!isOpen(addr, path)) {
|
||||
if (!isDataOpen(addr, path)) {
|
||||
return;
|
||||
}
|
||||
close(addr, path);
|
||||
notifyDataToggled();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,11 +70,11 @@ public class OpenCloseManager {
|
|||
* @param address the address to open
|
||||
* @param path the data component path to open
|
||||
*/
|
||||
private void open(Address address, int[] path) {
|
||||
private void openData(Address address, int[] path) {
|
||||
int pathSize = path.length;
|
||||
int[] levels = map.get(address);
|
||||
if ((levels == null) || (pathSize >= levels.length)) {
|
||||
exactOpen(address, path);
|
||||
exactOpenData(address, path);
|
||||
return;
|
||||
}
|
||||
levels[0] = 0;
|
||||
|
@ -90,7 +82,7 @@ public class OpenCloseManager {
|
|||
for (; i < pathSize; i++) {
|
||||
if (levels[i + 1] != path[i]) {
|
||||
if (levels[i + 1] != -1) {
|
||||
exactOpen(address, path);
|
||||
exactOpenData(address, path);
|
||||
return;
|
||||
}
|
||||
levels[i + 1] = path[i];
|
||||
|
@ -99,7 +91,7 @@ public class OpenCloseManager {
|
|||
map.put(address, levels);
|
||||
}
|
||||
|
||||
private void exactOpen(Address address, int[] path) {
|
||||
private void exactOpenData(Address address, int[] path) {
|
||||
int pathSize = path.length;
|
||||
int[] newLevels = new int[pathSize + 1];
|
||||
newLevels[0] = 0;
|
||||
|
@ -158,20 +150,13 @@ public class OpenCloseManager {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the data at the given address is open
|
||||
* @param address the address to test if open
|
||||
*/
|
||||
public boolean isOpen(Address address) {
|
||||
return isOpen(address, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test is the data at the given address and component path is open
|
||||
* @param address the address to test
|
||||
* @param path the component path to test.
|
||||
* @return true if the data with the given component path is open
|
||||
*/
|
||||
public boolean isOpen(Address address, int[] path) {
|
||||
public boolean isDataOpen(Address address, int[] path) {
|
||||
int[] levels = map.get(address);
|
||||
if (levels == null) {
|
||||
return false;
|
||||
|
@ -199,8 +184,9 @@ public class OpenCloseManager {
|
|||
* Returns the index of the component that is open at the given address.
|
||||
* @param address the address to find the open index.
|
||||
* @param path the component path.
|
||||
* @return the index of the component that is open at the given address.
|
||||
*/
|
||||
public int getOpenIndex(Address address, int[] path) {
|
||||
public int getOpenDataIndex(Address address, int[] path) {
|
||||
int[] levels = map.get(address);
|
||||
if ((levels == null) || (levels.length == 0)) {
|
||||
return -1;
|
||||
|
@ -216,14 +202,8 @@ public class OpenCloseManager {
|
|||
return -1;
|
||||
}
|
||||
|
||||
public boolean isOpen(Data data) {
|
||||
return isOpen(data.getMinAddress(), data.getComponentPath());
|
||||
}
|
||||
|
||||
public void toggleOpen(Data data) {
|
||||
toggleTopLevelData(data);
|
||||
|
||||
notifyDataToggled();
|
||||
}
|
||||
|
||||
private void toggleTopLevelData(Data data) {
|
||||
|
@ -233,17 +213,11 @@ public class OpenCloseManager {
|
|||
Address addr = data.getMinAddress();
|
||||
int[] path = data.getComponentPath();
|
||||
|
||||
if (isOpen(addr, path)) {
|
||||
if (isDataOpen(addr, path)) {
|
||||
close(addr, path);
|
||||
}
|
||||
else {
|
||||
open(addr, path);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyDataToggled() {
|
||||
for (ChangeListener l : listeners) {
|
||||
l.stateChanged(null);
|
||||
openData(addr, path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,32 +250,22 @@ public class OpenCloseManager {
|
|||
long progress = addresses.getNumAddresses() - unprocessed.getNumAddresses();
|
||||
monitor.setProgress(progress);
|
||||
}
|
||||
|
||||
notifyDataToggled();
|
||||
}
|
||||
|
||||
public void openAllData(Data data, TaskMonitor monitor) {
|
||||
toggleDataRecursively(data, true, monitor);
|
||||
|
||||
notifyDataToggled();
|
||||
}
|
||||
|
||||
public void closeAllData(Program program, AddressSetView addresses, TaskMonitor monitor) {
|
||||
toggleAllDataInAddresses(false, program, addresses, monitor);
|
||||
}
|
||||
|
||||
public void closeAllData(Data data, TaskMonitor monitor) {
|
||||
toggleDataRecursively(data, false, monitor);
|
||||
|
||||
notifyDataToggled();
|
||||
public boolean isDataOpen(Data data) {
|
||||
return isDataOpen(data.getMinAddress(), data.getComponentPath());
|
||||
}
|
||||
|
||||
private void toggleDataRecursively(Data data, boolean openState, TaskMonitor monitor) {
|
||||
public void toggleDataRecursively(Data data, boolean openState, TaskMonitor monitor) {
|
||||
if (data == null && !monitor.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isOpen(data) != openState) {
|
||||
if (isDataOpen(data) != openState) {
|
||||
toggleTopLevelData(data);
|
||||
}
|
||||
int componentCount = data.getNumComponents();
|
||||
|
@ -326,7 +290,7 @@ public class OpenCloseManager {
|
|||
return;
|
||||
}
|
||||
|
||||
if (isOpen(data) != openState) {
|
||||
if (isDataOpen(data) != openState) {
|
||||
toggleTopLevelData(data);
|
||||
}
|
||||
|
||||
|
@ -336,22 +300,6 @@ public class OpenCloseManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a change listener to be notified when a location is open or closed.
|
||||
* @param l the listener to be notified.
|
||||
*/
|
||||
public void addChangeListener(ChangeListener l) {
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener.
|
||||
* @param l the listener to remove.
|
||||
*/
|
||||
public void removeChangeListener(ChangeListener l) {
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
|
@ -0,0 +1,167 @@
|
|||
/* ###
|
||||
* 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.util.viewer.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Manages the open/close state of various listing regions. This includes structures, arrays, and
|
||||
* function variables.
|
||||
*/
|
||||
public class ProgramOpenCloseManager {
|
||||
private DataOpenCloseManager dataOpenCloseManager = new DataOpenCloseManager();
|
||||
private List<ChangeListener> listeners = new ArrayList<>();
|
||||
private AddressBasedOpenCloseManager variablesOpenCloseManager = new AddressBasedOpenCloseManager();
|
||||
|
||||
/**
|
||||
* Sets whether or not to display function variables at the given address.
|
||||
* @param functionAddress the address of the function
|
||||
* @param open true to display function variables, false to to hide them
|
||||
*/
|
||||
public void setFunctionVariablesOpen(Address functionAddress, boolean open) {
|
||||
if (open) {
|
||||
variablesOpenCloseManager.open(functionAddress);
|
||||
}
|
||||
else {
|
||||
variablesOpenCloseManager.close(functionAddress);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the function variables are being shown at the given function address.
|
||||
* @param functionAddress the address of the function to check
|
||||
* @return true if the variables are being displayed
|
||||
*/
|
||||
public boolean isFunctionVariablesOpen(Address functionAddress) {
|
||||
return variablesOpenCloseManager.isOpen(functionAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not function variables are being shown by globally. This essentially sets
|
||||
* the default state, but the state can be overridden at specific functions.
|
||||
* @param open if true, then the function variables are displayed
|
||||
*/
|
||||
public void setAllFunctionVariablesOpen(boolean open) {
|
||||
if (open) {
|
||||
variablesOpenCloseManager.openAll();
|
||||
}
|
||||
else {
|
||||
variablesOpenCloseManager.closeAll();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public boolean isAllFunctionVariablesOpen() {
|
||||
return variablesOpenCloseManager.isOpenByDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the given data as open. This method notifies listeners of changes.
|
||||
* @param data The data to open.
|
||||
*/
|
||||
public void openData(Data data) {
|
||||
dataOpenCloseManager.openData(data);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the given data as open. This method notifies listeners of changes.
|
||||
* @param data The data to open.
|
||||
*/
|
||||
public void closeData(Data data) {
|
||||
dataOpenCloseManager.closeData(data);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the data at the given address is open
|
||||
* @param address the address to test if open
|
||||
* @return true if the data at the address is open.
|
||||
*/
|
||||
public boolean isDataOpen(Address address) {
|
||||
return dataOpenCloseManager.isDataOpen(address, null);
|
||||
}
|
||||
|
||||
public boolean isDataOpen(Data data) {
|
||||
return dataOpenCloseManager.isDataOpen(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the component that is open at the given address.
|
||||
* @param data the data to get the index for
|
||||
* @return the index of the component that is open for the given data
|
||||
*/
|
||||
public int getOpenDataIndex(Data data) {
|
||||
return dataOpenCloseManager.getOpenDataIndex(data.getMinAddress(), data.getComponentPath());
|
||||
}
|
||||
|
||||
public void toggleDataOpen(Data data) {
|
||||
dataOpenCloseManager.toggleOpen(data);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public void openAllData(Program program, AddressSetView addresses, TaskMonitor monitor) {
|
||||
dataOpenCloseManager.openAllData(program, addresses, monitor);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public void closeAllData(Program program, AddressSetView addresses, TaskMonitor monitor) {
|
||||
dataOpenCloseManager.closeAllData(program, addresses, monitor);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public void openDataRecursively(Data data, TaskMonitor monitor) {
|
||||
dataOpenCloseManager.toggleDataRecursively(data, true, monitor);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public void closeDataRecursively(Data data, TaskMonitor monitor) {
|
||||
dataOpenCloseManager.toggleDataRecursively(data, false, monitor);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a change listener to be notified when a location is open or closed.
|
||||
* @param l the listener to be notified.
|
||||
*/
|
||||
public void addChangeListener(ChangeListener l) {
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener.
|
||||
* @param l the listener to remove.
|
||||
*/
|
||||
public void removeChangeListener(ChangeListener l) {
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
private void notifyListeners() {
|
||||
for (ChangeListener l : listeners) {
|
||||
l.stateChanged(null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* ###
|
||||
* 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.program.util;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* ProgramLocation that represents the cursor being on the variables open/close widget
|
||||
*/
|
||||
public class VariablesOpenCloseLocation extends CodeUnitLocation {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param program the program of the location
|
||||
* @param addr address of the location
|
||||
*/
|
||||
public VariablesOpenCloseLocation(Program program, Address addr) {
|
||||
super(program, addr, null, 0, 0, 0);
|
||||
}
|
||||
|
||||
public VariablesOpenCloseLocation() {
|
||||
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue