mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +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;
|
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.List;
|
||||||
import java.util.function.Consumer;
|
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.DebuggerPluginPackage;
|
||||||
import ghidra.app.plugin.core.debug.event.*;
|
import ghidra.app.plugin.core.debug.event.*;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractNewListingAction;
|
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.services.*;
|
||||||
import ghidra.app.util.viewer.format.FormatManager;
|
import ghidra.app.util.viewer.format.FormatManager;
|
||||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||||
|
@ -81,7 +82,8 @@ import ghidra.trace.model.program.TraceProgramView;
|
||||||
},
|
},
|
||||||
servicesProvided = {
|
servicesProvided = {
|
||||||
DebuggerListingService.class,
|
DebuggerListingService.class,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerListingProvider>
|
public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerListingProvider>
|
||||||
implements DebuggerListingService {
|
implements DebuggerListingService {
|
||||||
private static final String KEY_CONNECTED_PROVIDER = "connectedProvider";
|
private static final String KEY_CONNECTED_PROVIDER = "connectedProvider";
|
||||||
|
@ -450,6 +452,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeConfigState(SaveState saveState) {
|
public void writeConfigState(SaveState saveState) {
|
||||||
|
super.writeConfigState(saveState);
|
||||||
SaveState connectedProviderState = new SaveState();
|
SaveState connectedProviderState = new SaveState();
|
||||||
connectedProvider.writeConfigState(connectedProviderState);
|
connectedProvider.writeConfigState(connectedProviderState);
|
||||||
saveState.putXmlElement(KEY_CONNECTED_PROVIDER, connectedProviderState.saveToXml());
|
saveState.putXmlElement(KEY_CONNECTED_PROVIDER, connectedProviderState.saveToXml());
|
||||||
|
@ -470,6 +473,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readConfigState(SaveState saveState) {
|
public void readConfigState(SaveState saveState) {
|
||||||
|
super.readConfigState(saveState);
|
||||||
Element connectedProviderElement = saveState.getXmlElement(KEY_CONNECTED_PROVIDER);
|
Element connectedProviderElement = saveState.getXmlElement(KEY_CONNECTED_PROVIDER);
|
||||||
if (connectedProviderElement != null) {
|
if (connectedProviderElement != null) {
|
||||||
SaveState connectedProviderState = new SaveState(connectedProviderElement);
|
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.21.0.json||GHIDRA||||END|
|
||||||
data/typeinfo/golang/go1.22.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.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.24.0.json||GHIDRA||||END|
|
||||||
data/typeinfo/golang/go1.25.0.json||GHIDRA||||END|
|
data/typeinfo/golang/go1.25.0.json||GHIDRA||||END|
|
||||||
data/typeinfo/golang/golang_1.15_anybit_any.gdt||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
|
color.bg.listing.error = color.palette.lightcoral
|
||||||
|
|
||||||
font.listing.base = font.monospaced
|
font.listing.base = font.monospaced
|
||||||
|
font.listing.base.hidden.field = font.listing.base[italic]
|
||||||
font.listing.header = SansSerif-PLAIN-11
|
font.listing.header = SansSerif-PLAIN-11
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -655,6 +655,29 @@
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
</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>
|
<H2><A name="cursorTextHighlight"></A> Cursor Text Highlight</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
|
|
|
@ -181,6 +181,7 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||||
public P createNewDisconnectedProvider() {
|
public P createNewDisconnectedProvider() {
|
||||||
P newProvider = createProvider(formatMgr.createClone(), false);
|
P newProvider = createProvider(formatMgr.createClone(), false);
|
||||||
newProvider.setClipboardService(tool.getService(ClipboardService.class));
|
newProvider.setClipboardService(tool.getService(ClipboardService.class));
|
||||||
|
|
||||||
disconnectedProviders.add(newProvider);
|
disconnectedProviders.add(newProvider);
|
||||||
if (dndProvider != null) {
|
if (dndProvider != null) {
|
||||||
newProvider.addProgramDropProvider(dndProvider);
|
newProvider.addProgramDropProvider(dndProvider);
|
||||||
|
|
|
@ -274,12 +274,14 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeConfigState(SaveState saveState) {
|
public void writeConfigState(SaveState saveState) {
|
||||||
|
super.writeConfigState(saveState);
|
||||||
formatMgr.saveState(saveState);
|
formatMgr.saveState(saveState);
|
||||||
connectedProvider.saveState(saveState);
|
connectedProvider.saveState(saveState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readConfigState(SaveState saveState) {
|
public void readConfigState(SaveState saveState) {
|
||||||
|
super.readConfigState(saveState);
|
||||||
formatMgr.readState(saveState);
|
formatMgr.readState(saveState);
|
||||||
connectedProvider.readState(saveState);
|
connectedProvider.readState(saveState);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ import java.awt.Point;
|
||||||
import java.awt.datatransfer.DataFlavor;
|
import java.awt.datatransfer.DataFlavor;
|
||||||
import java.awt.datatransfer.Transferable;
|
import java.awt.datatransfer.Transferable;
|
||||||
import java.awt.dnd.*;
|
import java.awt.dnd.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
@ -31,6 +32,7 @@ import javax.swing.event.ChangeListener;
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.action.builder.ActionBuilder;
|
import docking.action.builder.ActionBuilder;
|
||||||
|
import docking.action.builder.ToggleActionBuilder;
|
||||||
import docking.actions.PopupActionProvider;
|
import docking.actions.PopupActionProvider;
|
||||||
import docking.dnd.*;
|
import docking.dnd.*;
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
|
@ -41,6 +43,7 @@ import docking.widgets.fieldpanel.support.*;
|
||||||
import docking.widgets.tab.GTabPanel;
|
import docking.widgets.tab.GTabPanel;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
import ghidra.app.context.ListingActionContext;
|
import ghidra.app.context.ListingActionContext;
|
||||||
|
import ghidra.app.context.ProgramLocationActionContext;
|
||||||
import ghidra.app.nav.ListingPanelContainer;
|
import ghidra.app.nav.ListingPanelContainer;
|
||||||
import ghidra.app.nav.LocationMemento;
|
import ghidra.app.nav.LocationMemento;
|
||||||
import ghidra.app.plugin.core.clipboard.CodeBrowserClipboardProvider;
|
import ghidra.app.plugin.core.clipboard.CodeBrowserClipboardProvider;
|
||||||
|
@ -68,6 +71,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
implements ProgramLocationListener, ProgramSelectionListener, Draggable, Droppable,
|
implements ProgramLocationListener, ProgramSelectionListener, Draggable, Droppable,
|
||||||
ChangeListener, StringSelectionListener, PopupActionProvider {
|
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 OLD_NAME = "CodeBrowserPlugin";
|
||||||
private static final String NAME = "Listing";
|
private static final String NAME = "Listing";
|
||||||
private static final String TITLE = NAME + ": ";
|
private static final String TITLE = NAME + ": ";
|
||||||
|
@ -126,6 +130,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
private FieldNavigator fieldNavigator;
|
private FieldNavigator fieldNavigator;
|
||||||
|
|
||||||
private MultiListingLayoutModel multiModel;
|
private MultiListingLayoutModel multiModel;
|
||||||
|
private ToggleDockingAction toggleVariablesAction;
|
||||||
|
|
||||||
public CodeViewerProvider(CodeBrowserPluginInterface plugin, FormatManager formatMgr,
|
public CodeViewerProvider(CodeBrowserPluginInterface plugin, FormatManager formatMgr,
|
||||||
boolean isConnected) {
|
boolean isConnected) {
|
||||||
|
@ -151,6 +156,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
|
|
||||||
listingPanel = new ListingPanel(formatMgr);
|
listingPanel = new ListingPanel(formatMgr);
|
||||||
listingPanel.enablePropertyBasedColorModel(true);
|
listingPanel.enablePropertyBasedColorModel(true);
|
||||||
|
|
||||||
decorationPanel = new ListingPanelContainer(listingPanel, isConnected);
|
decorationPanel = new ListingPanelContainer(listingPanel, isConnected);
|
||||||
ListingMiddleMouseHighlightProvider listingHighlighter =
|
ListingMiddleMouseHighlightProvider listingHighlighter =
|
||||||
createListingHighlighter(listingPanel, tool, decorationPanel);
|
createListingHighlighter(listingPanel, tool, decorationPanel);
|
||||||
|
@ -429,6 +435,11 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
listingPanel.setProgram(program);
|
listingPanel.setProgram(program);
|
||||||
|
ListingModel listingModel = listingPanel.getListingModel();
|
||||||
|
if (listingModel != null) {
|
||||||
|
boolean shouldShowVariables = toggleVariablesAction.isSelected();
|
||||||
|
listingModel.setAllFunctionVariablesOpen(shouldShowVariables);
|
||||||
|
}
|
||||||
codeViewerClipboardProvider.setProgram(program);
|
codeViewerClipboardProvider.setProgram(program);
|
||||||
codeViewerClipboardProvider.setListingLayoutModel(listingPanel.getListingModel());
|
codeViewerClipboardProvider.setListingLayoutModel(listingPanel.getListingModel());
|
||||||
if (coordinatedListingPanelListener != null) {
|
if (coordinatedListingPanelListener != null) {
|
||||||
|
@ -470,17 +481,67 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
action = new GotoNextFunctionAction(tool, plugin.getName());
|
action = new GotoNextFunctionAction(tool, plugin.getName());
|
||||||
tool.addAction(action);
|
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();
|
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() {
|
private void buildQuickTogleFieldActions() {
|
||||||
List<String> quickToggleFieldNames = formatMgr.getQuickToggleFieldNames();
|
List<String> quickToggleFieldNames = formatMgr.getQuickToggleFieldNames();
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (String fieldName : quickToggleFieldNames) {
|
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)
|
.popupMenuPath("Toggle Field", fieldName)
|
||||||
.popupMenuGroup("Field", "" + count)
|
.popupMenuGroup("Field", "" + count)
|
||||||
|
.keyBinding(keyBinding)
|
||||||
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Toggle_Field"))
|
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Toggle_Field"))
|
||||||
// only show this action when over the listing field header
|
// only show this action when over the listing field header
|
||||||
.popupWhen(c -> c.getContextObject() instanceof FieldHeaderLocation)
|
.popupWhen(c -> c.getContextObject() instanceof FieldHeaderLocation)
|
||||||
|
@ -488,19 +549,10 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
|
|
||||||
// automatically assign keybindings to the first 5 toggle fields.
|
// 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++;
|
count++;
|
||||||
}
|
}
|
||||||
tool.setMenuGroup(new String[] { "Toggle Field" }, "Disassembly");
|
tool.setMenuGroup(new String[] { "Toggle Field" }, "Disassembly");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListingPanel getListingPanel() {
|
public ListingPanel getListingPanel() {
|
||||||
|
@ -820,12 +872,19 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
void saveState(SaveState saveState) {
|
void saveState(SaveState saveState) {
|
||||||
saveState.putInt(DIVIDER_LOCATION, getListingPanel().getDividerLocation());
|
saveState.putInt(DIVIDER_LOCATION, getListingPanel().getDividerLocation());
|
||||||
saveState.putBoolean(HOVER_MODE, toggleHoverAction.isSelected());
|
saveState.putBoolean(HOVER_MODE, toggleHoverAction.isSelected());
|
||||||
|
saveState.putBoolean(SHOW_FUNCITON_VARS_OPTIONS_NAME, toggleVariablesAction.isSelected());
|
||||||
}
|
}
|
||||||
|
|
||||||
void readState(SaveState saveState) {
|
void readState(SaveState saveState) {
|
||||||
getListingPanel().setDividerLocation(
|
getListingPanel().setDividerLocation(
|
||||||
saveState.getInt(DIVIDER_LOCATION, ListingPanel.DEFAULT_DIVIDER_LOCATION));
|
saveState.getInt(DIVIDER_LOCATION, ListingPanel.DEFAULT_DIVIDER_LOCATION));
|
||||||
toggleHoverAction.setSelected(saveState.getBoolean(HOVER_MODE, true));
|
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) {
|
private void setHoverEnabled(boolean enabled) {
|
||||||
|
@ -951,9 +1010,12 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
// (its done in an invoke later)
|
// (its done in an invoke later)
|
||||||
Swing.runLater(() -> {
|
Swing.runLater(() -> {
|
||||||
newProvider.doSetProgram(program);
|
newProvider.doSetProgram(program);
|
||||||
|
SaveState saveState = new SaveState();
|
||||||
|
saveState(saveState);
|
||||||
|
newProvider.readState(saveState);
|
||||||
|
newProvider.setLocation(currentLocation);
|
||||||
newProvider.listingPanel.getFieldPanel()
|
newProvider.listingPanel.getFieldPanel()
|
||||||
.setViewerPosition(vp.getIndex(), vp.getXOffset(), vp.getYOffset());
|
.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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,7 +68,7 @@ public class IndentField implements ListingField {
|
||||||
|
|
||||||
// this class is dependent upon the OpenClosedField in that they work together to perform
|
// this class is dependent upon the OpenClosedField in that they work together to perform
|
||||||
// painting
|
// painting
|
||||||
toggleHandleSize = OpenCloseField.getOpenCloseHandleSize();
|
toggleHandleSize = AbstractOpenCloseField.getOpenCloseHandleSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,41 +15,25 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.viewer.field;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
|
import static ghidra.app.util.viewer.field.AbstractOpenCloseField.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||||
import docking.widgets.fieldpanel.support.*;
|
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||||
import generic.theme.GIcon;
|
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||||
import ghidra.app.util.viewer.proxy.EmptyProxy;
|
|
||||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FactoryField class for displaying the open/close field.
|
* FactoryField class for displaying the open/close field.
|
||||||
*/
|
*/
|
||||||
public class OpenCloseField implements ListingField {
|
public class OpenCloseField extends AbstractOpenCloseField {
|
||||||
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;
|
|
||||||
private int indentLevel;
|
private int indentLevel;
|
||||||
private boolean isLast;
|
private boolean isLast;
|
||||||
|
|
||||||
private int toggleHandleSize;
|
|
||||||
private int insetSpace = 1;
|
private int insetSpace = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,51 +48,10 @@ public class OpenCloseField implements ListingField {
|
||||||
*/
|
*/
|
||||||
public OpenCloseField(FieldFactory factory, ProxyObj<?> proxy, int indentLevel,
|
public OpenCloseField(FieldFactory factory, ProxyObj<?> proxy, int indentLevel,
|
||||||
FontMetrics metrics, int x, int width, boolean isLast) {
|
FontMetrics metrics, int x, int width, boolean isLast) {
|
||||||
this.factory = factory;
|
super(factory, proxy, metrics, x, width);
|
||||||
this.proxy = proxy;
|
|
||||||
this.isOpen = proxy.getListingLayoutModel().isOpen((Data) proxy.getObject());
|
this.isOpen = proxy.getListingLayoutModel().isOpen((Data) proxy.getObject());
|
||||||
this.fieldWidth = width;
|
|
||||||
this.startX = x;
|
|
||||||
this.indentLevel = indentLevel;
|
this.indentLevel = indentLevel;
|
||||||
this.isLast = isLast;
|
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
|
@Override
|
||||||
|
@ -116,37 +59,6 @@ public class OpenCloseField implements ListingField {
|
||||||
return (indentLevel + 1) * fieldWidth;
|
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
|
@Override
|
||||||
public void paint(JComponent c, Graphics g, PaintContext context,
|
public void paint(JComponent c, Graphics g, PaintContext context,
|
||||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
|
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
|
||||||
|
@ -211,132 +123,11 @@ public class OpenCloseField implements ListingField {
|
||||||
paintCursor(g, context.getCursorColor(), cursorLoc);
|
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.
|
* Toggles the open state of this field.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void toggleOpenCloseState() {
|
public void toggleOpenCloseState() {
|
||||||
proxy.getListingLayoutModel().toggleOpen((Data) proxy.getObject());
|
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;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class OpenCloseFieldMouseHandler implements FieldMouseHandlerExtension {
|
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
|
@Override
|
||||||
public boolean fieldElementClicked(Object clickedObject, Navigatable sourceNavigatable,
|
public boolean fieldElementClicked(Object clickedObject, Navigatable sourceNavigatable,
|
||||||
|
@ -32,7 +33,7 @@ public class OpenCloseFieldMouseHandler implements FieldMouseHandlerExtension {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenCloseField field = (OpenCloseField) clickedObject;
|
AbstractOpenCloseField field = (AbstractOpenCloseField) clickedObject;
|
||||||
field.toggleOpenCloseState();
|
field.toggleOpenCloseState();
|
||||||
return true;
|
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 rowElem = new Element("ROW");
|
||||||
|
|
||||||
Element colElem = new Element("FIELD");
|
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");
|
colElem.setAttribute("ENABLED", "true");
|
||||||
rowElem.addContent(colElem);
|
rowElem.addContent(colElem);
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,21 @@ public class EmptyListingModel implements ListingModel {
|
||||||
// stub
|
// 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
|
@Override
|
||||||
public void openAllData(Data data, TaskMonitor monitor) {
|
public void openAllData(Data data, TaskMonitor monitor) {
|
||||||
// stub
|
// stub
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.viewer.listingpanel;
|
package ghidra.app.util.viewer.listingpanel;
|
||||||
|
|
||||||
|
import docking.widgets.fieldpanel.FieldPanel;
|
||||||
import docking.widgets.fieldpanel.Layout;
|
import docking.widgets.fieldpanel.Layout;
|
||||||
import ghidra.app.util.viewer.format.FormatManager;
|
import ghidra.app.util.viewer.format.FormatManager;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
|
@ -23,6 +24,9 @@ import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model displaying program data in a {@link FieldPanel}
|
||||||
|
*/
|
||||||
public interface ListingModel {
|
public interface ListingModel {
|
||||||
|
|
||||||
static final String FUNCTION_POINTER_OPTION_GROUP_NAME = "Function Pointers";
|
static final String FUNCTION_POINTER_OPTION_GROUP_NAME = "Function Pointers";
|
||||||
|
@ -42,6 +46,9 @@ public interface ListingModel {
|
||||||
|
|
||||||
public Layout getLayout(Address address, boolean isGapAddress);
|
public Layout getLayout(Address address, boolean isGapAddress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the width of the longest layout this model can produce.}
|
||||||
|
*/
|
||||||
public int getMaxWidth();
|
public int getMaxWidth();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +66,28 @@ public interface ListingModel {
|
||||||
*/
|
*/
|
||||||
public void toggleOpen(Data data);
|
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.
|
* Opens the given data, but not any sub-components.
|
||||||
*
|
*
|
||||||
|
@ -106,18 +135,45 @@ public interface ListingModel {
|
||||||
*/
|
*/
|
||||||
public void closeAllData(AddressSetView addresses, TaskMonitor monitor);
|
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);
|
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);
|
public void removeListener(ListingModelListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the program being displayed by this model.}
|
||||||
|
*/
|
||||||
public Program getProgram();
|
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();
|
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);
|
public void setFormatManager(FormatManager formatManager);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes this model
|
||||||
|
*/
|
||||||
public void dispose();
|
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);
|
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.field.ListingField;
|
||||||
import ghidra.app.util.viewer.format.*;
|
import ghidra.app.util.viewer.format.*;
|
||||||
import ghidra.app.util.viewer.proxy.*;
|
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.DomainObjectChangedEvent;
|
||||||
import ghidra.framework.model.DomainObjectListener;
|
import ghidra.framework.model.DomainObjectListener;
|
||||||
import ghidra.framework.options.OptionsChangeListener;
|
import ghidra.framework.options.OptionsChangeListener;
|
||||||
|
@ -43,7 +43,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
DomainObjectListener, ChangeListener, OptionsChangeListener {
|
DomainObjectListener, ChangeListener, OptionsChangeListener {
|
||||||
|
|
||||||
protected final Program program;
|
protected final Program program;
|
||||||
private OpenCloseManager openCloseMgr = new OpenCloseManager();
|
private ProgramOpenCloseManager openCloseMgr = new ProgramOpenCloseManager();
|
||||||
private FormatManager formatMgr;
|
private FormatManager formatMgr;
|
||||||
private ToolOptions fieldOptions;
|
private ToolOptions fieldOptions;
|
||||||
private boolean showExternalFunctionPointerFormat;
|
private boolean showExternalFunctionPointerFormat;
|
||||||
|
@ -172,17 +172,17 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
if (function != null) {
|
if (function != null) {
|
||||||
format = formatMgr.getFunctionFormat();
|
format = formatMgr.getFunctionFormat();
|
||||||
format.addLayouts(list, 0, new FunctionProxy(this, program, addr, function));
|
format.addLayouts(list, 0, new FunctionProxy(this, program, addr, function));
|
||||||
Parameter[] params = function.getParameters();
|
|
||||||
format = formatMgr.getFunctionVarFormat();
|
format = formatMgr.getFunctionVarFormat();
|
||||||
format.addLayouts(list, 0,
|
boolean variablesOpen = openCloseMgr.isFunctionVariablesOpen(function.getEntryPoint());
|
||||||
new VariableProxy(this, program, addr, function, function.getReturn()));
|
if (variablesOpen) {
|
||||||
for (Parameter param : params) {
|
addReturn(addr, list, format, function);
|
||||||
format.addLayouts(list, 0, new VariableProxy(this, program, addr, function, param));
|
addParameters(addr, list, format, function);
|
||||||
|
addLocals(addr, list, format, function);
|
||||||
}
|
}
|
||||||
Variable[] vars = function.getLocalVariables();
|
else {
|
||||||
for (Variable var : vars) {
|
format.addLayouts(list, 0, new ClosedVariableProxy(this, program, addr, function));
|
||||||
format.addLayouts(list, 0, new VariableProxy(this, program, addr, function, var));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (cu != null) {
|
if (cu != null) {
|
||||||
format = formatMgr.getCodeUnitFormat();
|
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) {
|
private Function getPointerReferencedFunction(Data data) {
|
||||||
|
|
||||||
Reference ref = data.getPrimaryReference(0);
|
Reference ref = data.getPrimaryReference(0);
|
||||||
|
@ -239,7 +263,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
if (cu instanceof Data) {
|
if (cu instanceof Data) {
|
||||||
Data data = (Data) cu;
|
Data data = (Data) cu;
|
||||||
if (data.getNumComponents() > 0) {
|
if (data.getNumComponents() > 0) {
|
||||||
if (openCloseMgr.isOpen(data.getMinAddress())) {
|
if (openCloseMgr.isDataOpen(data.getMinAddress())) {
|
||||||
Address openAddr = findOpenDataAfter(address, data);
|
Address openAddr = findOpenDataAfter(address, data);
|
||||||
if (openAddr != null) {
|
if (openAddr != null) {
|
||||||
return openAddr;
|
return openAddr;
|
||||||
|
@ -255,8 +279,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
Data data;
|
Data data;
|
||||||
DataType dt = parent.getBaseDataType();
|
DataType dt = parent.getBaseDataType();
|
||||||
if (dt instanceof Union) {
|
if (dt instanceof Union) {
|
||||||
int index =
|
int index = openCloseMgr.getOpenDataIndex(parent);
|
||||||
openCloseMgr.getOpenIndex(parent.getMinAddress(), parent.getComponentPath());
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -288,7 +311,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (data.getNumComponents() > 0) {
|
if (data.getNumComponents() > 0) {
|
||||||
if (openCloseMgr.isOpen(data.getMinAddress(), data.getComponentPath())) {
|
if (openCloseMgr.isDataOpen(data)) {
|
||||||
Address openAddr = findOpenDataAfter(address, data);
|
Address openAddr = findOpenDataAfter(address, data);
|
||||||
if (openAddr != null) {
|
if (openAddr != null) {
|
||||||
return openAddr;
|
return openAddr;
|
||||||
|
@ -344,7 +367,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
if (cu instanceof Data) {
|
if (cu instanceof Data) {
|
||||||
Data data = (Data) cu;
|
Data data = (Data) cu;
|
||||||
if (data.getNumComponents() > 0) {
|
if (data.getNumComponents() > 0) {
|
||||||
if (openCloseMgr.isOpen(data.getMinAddress())) {
|
if (openCloseMgr.isDataOpen(data.getMinAddress())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,7 +382,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
Data data;
|
Data data;
|
||||||
if (parent.getBaseDataType() instanceof Union) {
|
if (parent.getBaseDataType() instanceof Union) {
|
||||||
int index =
|
int index =
|
||||||
openCloseMgr.getOpenIndex(parent.getMinAddress(), parent.getComponentPath());
|
openCloseMgr.getOpenDataIndex(parent);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -376,7 +399,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.getNumComponents() > 0) {
|
if (data.getNumComponents() > 0) {
|
||||||
if (openCloseMgr.isOpen(data.getMinAddress(), data.getComponentPath())) {
|
if (openCloseMgr.isDataOpen(data)) {
|
||||||
Address openAddr = findOpenDataBefore(addr, data);
|
Address openAddr = findOpenDataBefore(addr, data);
|
||||||
if (openAddr != null) {
|
if (openAddr != null) {
|
||||||
return openAddr;
|
return openAddr;
|
||||||
|
@ -396,11 +419,11 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
|
|
||||||
private void addOpenData(List<Data> list, Data data, Address addr) {
|
private void addOpenData(List<Data> list, Data data, Address addr) {
|
||||||
Address dataAddr = data.getMinAddress();
|
Address dataAddr = data.getMinAddress();
|
||||||
if (openCloseMgr.isOpen(dataAddr, data.getComponentPath())) {
|
if (openCloseMgr.isDataOpen(data)) {
|
||||||
DataType dt = data.getBaseDataType();
|
DataType dt = data.getBaseDataType();
|
||||||
if (dt instanceof Union) {
|
if (dt instanceof Union) {
|
||||||
int openIndex =
|
int openIndex =
|
||||||
openCloseMgr.getOpenIndex(data.getMinAddress(), data.getComponentPath());
|
openCloseMgr.getOpenDataIndex(data);
|
||||||
int numComps = ((Union) dt).getNumComponents();
|
int numComps = ((Union) dt).getNumComponents();
|
||||||
if (openIndex < 0) {
|
if (openIndex < 0) {
|
||||||
openIndex = numComps;
|
openIndex = numComps;
|
||||||
|
@ -437,8 +460,8 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
DataType dt = data.getBaseDataType();
|
DataType dt = data.getBaseDataType();
|
||||||
if (dt instanceof Union) {
|
if (dt instanceof Union) {
|
||||||
Address dataAddr = data.getMinAddress();
|
Address dataAddr = data.getMinAddress();
|
||||||
if (openCloseMgr.isOpen(dataAddr, data.getComponentPath())) {
|
if (openCloseMgr.isDataOpen(data)) {
|
||||||
int openIndex = openCloseMgr.getOpenIndex(dataAddr, data.getComponentPath());
|
int openIndex = openCloseMgr.getOpenDataIndex(data);
|
||||||
int i = openIndex;
|
int i = openIndex;
|
||||||
int numComps = ((Union) dt).getNumComponents();
|
int numComps = ((Union) dt).getNumComponents();
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
|
@ -459,22 +482,37 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOpen(Data data) {
|
public boolean isOpen(Data data) {
|
||||||
return openCloseMgr.isOpen(data);
|
return openCloseMgr.isDataOpen(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toggleOpen(Data data) {
|
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
|
@Override
|
||||||
public void openAllData(Data data, TaskMonitor monitor) {
|
public void openAllData(Data data, TaskMonitor monitor) {
|
||||||
openCloseMgr.openAllData(data, monitor);
|
openCloseMgr.openDataRecursively(data, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeAllData(Data data, TaskMonitor monitor) {
|
public void closeAllData(Data data, TaskMonitor monitor) {
|
||||||
openCloseMgr.closeAllData(data, monitor);
|
openCloseMgr.closeDataRecursively(data, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -494,8 +532,8 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean openData(Data data) {
|
public boolean openData(Data data) {
|
||||||
return openCloseMgr.openData(data);
|
openCloseMgr.openData(data);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifyDataChanged(boolean updateImmediately) {
|
protected void notifyDataChanged(boolean updateImmediately) {
|
||||||
|
|
|
@ -177,6 +177,21 @@ public class ListingModelConverter implements ListingModel {
|
||||||
model.toggleOpen(data);
|
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
|
@Override
|
||||||
public AddressSet adjustAddressSetToCodeUnitBoundaries(AddressSet addressSet) {
|
public AddressSet adjustAddressSetToCodeUnitBoundaries(AddressSet addressSet) {
|
||||||
AddressSet compatibleAddressSet = DiffUtility.getCompatibleAddressSet(addressSet, program);
|
AddressSet compatibleAddressSet = DiffUtility.getCompatibleAddressSet(addressSet, program);
|
||||||
|
|
|
@ -271,6 +271,21 @@ public class MultiListingLayoutModel implements ListingModelListener, FormatMode
|
||||||
models[modelID].toggleOpen(data);
|
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
|
@Override
|
||||||
public boolean openData(Data data) {
|
public boolean openData(Data data) {
|
||||||
return models[modelID].openData(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ public class VariableProxy extends ProxyObj<Variable> {
|
||||||
private int firstUseOffset;
|
private int firstUseOffset;
|
||||||
private Variable var;
|
private Variable var;
|
||||||
private int ordinal = -1;
|
private int ordinal = -1;
|
||||||
|
private boolean isFirst;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a proxy for a variable.
|
* 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 locationAddr the listing address at which the function exists or was inferred via reference
|
||||||
* @param fun the function containing the variable.
|
* @param fun the function containing the variable.
|
||||||
* @param var the variable to proxy.
|
* @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,
|
public VariableProxy(ListingModel model, Program program, Address locationAddr, Function fun,
|
||||||
Variable var) {
|
Variable var, boolean isFirst) {
|
||||||
super(model);
|
super(model);
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.locationAddr = locationAddr;
|
this.locationAddr = locationAddr;
|
||||||
this.var = var;
|
this.var = var;
|
||||||
|
this.isFirst = isFirst;
|
||||||
this.functionAddr = fun.getEntryPoint();
|
this.functionAddr = fun.getEntryPoint();
|
||||||
if (var instanceof Parameter) {
|
if (var != null) {
|
||||||
ordinal = ((Parameter) var).getOrdinal();
|
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
|
@Override
|
||||||
|
@ -126,6 +132,10 @@ public class VariableProxy extends ProxyObj<Variable> {
|
||||||
return functionAddr;
|
return functionAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Program getProgram() {
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Address a) {
|
public boolean contains(Address a) {
|
||||||
Variable v = getObject();
|
Variable v = getObject();
|
||||||
|
@ -134,4 +144,8 @@ public class VariableProxy extends ProxyObj<Variable> {
|
||||||
}
|
}
|
||||||
return Objects.equals(v.getMinAddress(), a);
|
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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,9 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.viewer.util;
|
package ghidra.app.util.viewer.util;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.*;
|
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.
|
* 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.
|
* The map stores an int[] for each address that has something open.
|
||||||
* If map.get(address) returns null then outermost level is closed.
|
* If map.get(address) returns null then outermost level is closed.
|
||||||
*/
|
*/
|
||||||
private Map<Address, int[]> map = new HashMap<>();
|
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.
|
* Marks the given data as open. This method notifies listeners of changes.
|
||||||
* @param data The data to open.
|
* @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) {
|
if (data.getComponent(0) == null) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Address addr = data.getMinAddress();
|
Address addr = data.getMinAddress();
|
||||||
int[] path = data.getComponentPath();
|
int[] path = data.getComponentPath();
|
||||||
if (isOpen(addr, path)) {
|
if (!isDataOpen(addr, path)) {
|
||||||
return false;
|
openData(addr, path);
|
||||||
}
|
}
|
||||||
open(addr, path);
|
|
||||||
notifyDataToggled();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,11 +59,10 @@ public class OpenCloseManager {
|
||||||
}
|
}
|
||||||
Address addr = data.getMinAddress();
|
Address addr = data.getMinAddress();
|
||||||
int[] path = data.getComponentPath();
|
int[] path = data.getComponentPath();
|
||||||
if (!isOpen(addr, path)) {
|
if (!isDataOpen(addr, path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
close(addr, path);
|
close(addr, path);
|
||||||
notifyDataToggled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,11 +70,11 @@ public class OpenCloseManager {
|
||||||
* @param address the address to open
|
* @param address the address to open
|
||||||
* @param path the data component path 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 pathSize = path.length;
|
||||||
int[] levels = map.get(address);
|
int[] levels = map.get(address);
|
||||||
if ((levels == null) || (pathSize >= levels.length)) {
|
if ((levels == null) || (pathSize >= levels.length)) {
|
||||||
exactOpen(address, path);
|
exactOpenData(address, path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
levels[0] = 0;
|
levels[0] = 0;
|
||||||
|
@ -90,7 +82,7 @@ public class OpenCloseManager {
|
||||||
for (; i < pathSize; i++) {
|
for (; i < pathSize; i++) {
|
||||||
if (levels[i + 1] != path[i]) {
|
if (levels[i + 1] != path[i]) {
|
||||||
if (levels[i + 1] != -1) {
|
if (levels[i + 1] != -1) {
|
||||||
exactOpen(address, path);
|
exactOpenData(address, path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
levels[i + 1] = path[i];
|
levels[i + 1] = path[i];
|
||||||
|
@ -99,7 +91,7 @@ public class OpenCloseManager {
|
||||||
map.put(address, levels);
|
map.put(address, levels);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exactOpen(Address address, int[] path) {
|
private void exactOpenData(Address address, int[] path) {
|
||||||
int pathSize = path.length;
|
int pathSize = path.length;
|
||||||
int[] newLevels = new int[pathSize + 1];
|
int[] newLevels = new int[pathSize + 1];
|
||||||
newLevels[0] = 0;
|
newLevels[0] = 0;
|
||||||
|
@ -158,20 +150,13 @@ public class OpenCloseManager {
|
||||||
return 0;
|
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
|
* Test is the data at the given address and component path is open
|
||||||
* @param address the address to test
|
* @param address the address to test
|
||||||
* @param path the component path 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);
|
int[] levels = map.get(address);
|
||||||
if (levels == null) {
|
if (levels == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -199,8 +184,9 @@ public class OpenCloseManager {
|
||||||
* Returns the index of the component that is open at the given address.
|
* Returns the index of the component that is open at the given address.
|
||||||
* @param address the address to find the open index.
|
* @param address the address to find the open index.
|
||||||
* @param path the component path.
|
* @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);
|
int[] levels = map.get(address);
|
||||||
if ((levels == null) || (levels.length == 0)) {
|
if ((levels == null) || (levels.length == 0)) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -216,14 +202,8 @@ public class OpenCloseManager {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOpen(Data data) {
|
|
||||||
return isOpen(data.getMinAddress(), data.getComponentPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toggleOpen(Data data) {
|
public void toggleOpen(Data data) {
|
||||||
toggleTopLevelData(data);
|
toggleTopLevelData(data);
|
||||||
|
|
||||||
notifyDataToggled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleTopLevelData(Data data) {
|
private void toggleTopLevelData(Data data) {
|
||||||
|
@ -233,17 +213,11 @@ public class OpenCloseManager {
|
||||||
Address addr = data.getMinAddress();
|
Address addr = data.getMinAddress();
|
||||||
int[] path = data.getComponentPath();
|
int[] path = data.getComponentPath();
|
||||||
|
|
||||||
if (isOpen(addr, path)) {
|
if (isDataOpen(addr, path)) {
|
||||||
close(addr, path);
|
close(addr, path);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
open(addr, path);
|
openData(addr, path);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyDataToggled() {
|
|
||||||
for (ChangeListener l : listeners) {
|
|
||||||
l.stateChanged(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,32 +250,22 @@ public class OpenCloseManager {
|
||||||
long progress = addresses.getNumAddresses() - unprocessed.getNumAddresses();
|
long progress = addresses.getNumAddresses() - unprocessed.getNumAddresses();
|
||||||
monitor.setProgress(progress);
|
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) {
|
public void closeAllData(Program program, AddressSetView addresses, TaskMonitor monitor) {
|
||||||
toggleAllDataInAddresses(false, program, addresses, monitor);
|
toggleAllDataInAddresses(false, program, addresses, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closeAllData(Data data, TaskMonitor monitor) {
|
public boolean isDataOpen(Data data) {
|
||||||
toggleDataRecursively(data, false, monitor);
|
return isDataOpen(data.getMinAddress(), data.getComponentPath());
|
||||||
|
|
||||||
notifyDataToggled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleDataRecursively(Data data, boolean openState, TaskMonitor monitor) {
|
public void toggleDataRecursively(Data data, boolean openState, TaskMonitor monitor) {
|
||||||
if (data == null && !monitor.isCancelled()) {
|
if (data == null && !monitor.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOpen(data) != openState) {
|
if (isDataOpen(data) != openState) {
|
||||||
toggleTopLevelData(data);
|
toggleTopLevelData(data);
|
||||||
}
|
}
|
||||||
int componentCount = data.getNumComponents();
|
int componentCount = data.getNumComponents();
|
||||||
|
@ -326,7 +290,7 @@ public class OpenCloseManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOpen(data) != openState) {
|
if (isDataOpen(data) != openState) {
|
||||||
toggleTopLevelData(data);
|
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
|
// 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