mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-02 08:23:50 +00:00
GP-1619 - Fixed bug that caused some MultiStateDockinActions to get called twice when clicked
This commit is contained in:
parent
436bb4873e
commit
6934c33aa6
|
@ -24,7 +24,6 @@ public interface DebuggerAutoReadMemoryAction extends AutoReadMemoryAction {
|
|||
static MultiStateActionBuilder<AutoReadMemorySpec> builder(Plugin owner) {
|
||||
MultiStateActionBuilder<AutoReadMemorySpec> builder = AutoReadMemoryAction.builder(owner);
|
||||
builder.toolBarGroup(NAME);
|
||||
builder.performActionOnButtonClick(true);
|
||||
for (AutoReadMemorySpec spec : AutoReadMemorySpec.allSpecs().values()) {
|
||||
builder.addState(spec.getMenuName(), spec.getMenuIcon(), spec);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ public interface DebuggerTrackLocationAction extends TrackLocationAction {
|
|||
static MultiStateActionBuilder<LocationTrackingSpec> builder(Plugin owner) {
|
||||
MultiStateActionBuilder<LocationTrackingSpec> builder = TrackLocationAction.builder(owner);
|
||||
builder.toolBarGroup(owner.getName());
|
||||
builder.performActionOnButtonClick(true);
|
||||
for (LocationTrackingSpec spec : LocationTrackingSpec.allSpecs().values()) {
|
||||
builder.addState(spec.getMenuName(), spec.getMenuIcon(), spec);
|
||||
}
|
||||
|
|
|
@ -63,22 +63,14 @@ import ghidra.util.datastruct.ListenerSet;
|
|||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@PluginInfo(
|
||||
shortDescription = "Debugger models manager service (proxy to front-end)",
|
||||
description = "Manage debug sessions, connections, and trace recording",
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.RELEASED,
|
||||
eventsConsumed = {
|
||||
ProgramActivatedPluginEvent.class,
|
||||
ProgramClosedPluginEvent.class,
|
||||
},
|
||||
servicesRequired = {
|
||||
DebuggerTraceManagerService.class,
|
||||
},
|
||||
servicesProvided = {
|
||||
DebuggerModelService.class,
|
||||
})
|
||||
@PluginInfo(shortDescription = "Debugger models manager service (proxy to front-end)", description = "Manage debug sessions, connections, and trace recording", category = PluginCategoryNames.DEBUGGER, packageName = DebuggerPluginPackage.NAME, status = PluginStatus.RELEASED, eventsConsumed = {
|
||||
ProgramActivatedPluginEvent.class,
|
||||
ProgramClosedPluginEvent.class,
|
||||
}, servicesRequired = {
|
||||
DebuggerTraceManagerService.class,
|
||||
}, servicesProvided = {
|
||||
DebuggerModelService.class,
|
||||
})
|
||||
public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||
implements DebuggerModelServiceInternal {
|
||||
|
||||
|
@ -250,7 +242,6 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
.enabledWhen(ctx -> currentProgram != null)
|
||||
.onAction(this::debugProgramButtonActivated)
|
||||
.onActionStateChanged(this::debugProgramStateActivated)
|
||||
.performActionOnButtonClick(true)
|
||||
.addState(DUMMY_LAUNCH_STATE)
|
||||
.buildAndInstall(tool);
|
||||
actionDisconnectAll = DisconnectAllAction.builder(this, delegate)
|
||||
|
@ -383,7 +374,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
List<DebuggerProgramLaunchOffer> offers = program == null ? List.of()
|
||||
: getProgramLaunchOffers(program).collect(Collectors.toList());
|
||||
List<ActionState<DebuggerProgramLaunchOffer>> states = offers.stream()
|
||||
.map(o -> new ActionState<DebuggerProgramLaunchOffer>(o.getButtonTitle(),
|
||||
.map(o -> new ActionState<>(o.getButtonTitle(),
|
||||
o.getIcon(), o))
|
||||
.collect(Collectors.toList());
|
||||
if (!states.isEmpty()) {
|
||||
|
|
|
@ -348,7 +348,7 @@ public class SampleGraphProvider extends ComponentProviderAdapter {
|
|||
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) {
|
||||
|
||||
@Override
|
||||
protected void doActionPerformed(ActionContext context) {
|
||||
public void actionPerformed(ActionContext context) {
|
||||
// this callback is when the user clicks the button
|
||||
LayoutProvider<SampleVertex, SampleEdge, SampleGraph> currentUserData =
|
||||
getCurrentUserData();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,17 +15,16 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.program.model.data.DataTypeConflictHandler;
|
||||
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResolutionPolicy;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import resources.ResourceManager;
|
||||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.widgets.EventTrigger;
|
||||
|
||||
public class ConflictHandlerModesAction extends
|
||||
MultiStateDockingAction<DataTypeConflictHandler.ConflictResolutionPolicy> {
|
||||
|
@ -43,33 +41,31 @@ public class ConflictHandlerModesAction extends
|
|||
new HelpLocation(plugin.getName(), "conflict_mode");
|
||||
setHelpLocation(conflictModesHelpLocation);
|
||||
|
||||
setPerformActionOnPrimaryButtonClick(false);
|
||||
|
||||
Icon renameAndAddIcon = ResourceManager.loadImage("images/conflictRename.png");
|
||||
Icon useExistingIcon = ResourceManager.loadImage("images/conflictKeep.png");
|
||||
Icon replaceExistingIcon = ResourceManager.loadImage("images/conflictReplace.png");
|
||||
Icon replaceDefaultIcon = ResourceManager.loadImage("images/conflictReplaceOrRename.png");
|
||||
|
||||
ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> renameAndAddState =
|
||||
new ActionState<DataTypeConflictHandler.ConflictResolutionPolicy>(
|
||||
new ActionState<>(
|
||||
"Rename New or Moved Data Type", renameAndAddIcon,
|
||||
DataTypeConflictHandler.ConflictResolutionPolicy.RENAME_AND_ADD);
|
||||
renameAndAddState.setHelpLocation(conflictModesHelpLocation);
|
||||
|
||||
ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> useExistingState =
|
||||
new ActionState<DataTypeConflictHandler.ConflictResolutionPolicy>(
|
||||
new ActionState<>(
|
||||
"Use Existing Data Type", useExistingIcon,
|
||||
DataTypeConflictHandler.ConflictResolutionPolicy.USE_EXISTING);
|
||||
useExistingState.setHelpLocation(conflictModesHelpLocation);
|
||||
|
||||
ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> replaceExistingState =
|
||||
new ActionState<DataTypeConflictHandler.ConflictResolutionPolicy>(
|
||||
new ActionState<>(
|
||||
"Replace Existing Data Type", replaceExistingIcon,
|
||||
DataTypeConflictHandler.ConflictResolutionPolicy.REPLACE_EXISTING);
|
||||
replaceExistingState.setHelpLocation(conflictModesHelpLocation);
|
||||
|
||||
ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> replaceDefaultState =
|
||||
new ActionState<DataTypeConflictHandler.ConflictResolutionPolicy>(
|
||||
new ActionState<>(
|
||||
"Replace Empty Structures else Rename",
|
||||
replaceDefaultIcon,
|
||||
DataTypeConflictHandler.ConflictResolutionPolicy.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD);
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.navigation;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import java.awt.event.*;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.KeyStroke;
|
||||
|
@ -103,7 +102,7 @@ public class NextPreviousBookmarkAction extends MultiStateDockingAction<String>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doActionPerformed(ActionContext context) {
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (context instanceof NavigatableActionContext) {
|
||||
gotoNextPrevious((NavigatableActionContext) context, this.getCurrentUserData());
|
||||
}
|
||||
|
|
|
@ -1,314 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.navigation;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.MarkerSet;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.BookmarkType;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class NextPreviousMarkerAction extends MultiStateDockingAction<String> {
|
||||
//private static final ImageIcon new ImageIcon(); = null;
|
||||
private boolean isForward = true;
|
||||
private PluginTool tool;
|
||||
|
||||
private static ImageIcon markerIcon = ResourceManager.loadImage("images/M.gif");
|
||||
private static ImageIcon markerAnalysisBookmarkIcon = ResourceManager.loadImage("images/M.gif");
|
||||
private static ImageIcon markerConflictingChangesIcon =
|
||||
ResourceManager.loadImage("images/edit-delete.png");
|
||||
private static ImageIcon markerLatestVersionChangesIcon =
|
||||
ResourceManager.loadImage("images/information.png");
|
||||
private static ImageIcon markerNotCheckedInChangesIcon =
|
||||
ResourceManager.loadImage("images/notes.gif");
|
||||
private static ImageIcon markerUnSavedChangesIcon =
|
||||
ResourceManager.loadImage("images/warning.png");
|
||||
private static ImageIcon markerCursorIcon = ResourceManager.loadImage("images/unknown.gif");
|
||||
private static ImageIcon markerErrorBookmarkIcon =
|
||||
ResourceManager.loadImage("images/unknown.gif");
|
||||
private static ImageIcon markerHighlightIcon = ResourceManager.loadImage("images/unknown.gif");
|
||||
private static ImageIcon markerInfoBookmarkIcon =
|
||||
ResourceManager.loadImage("images/unknown.gif");
|
||||
private static ImageIcon markerNoteBookmarkIcon =
|
||||
ResourceManager.loadImage("images/unknown.gif");
|
||||
private static ImageIcon markerSelectionIcon = ResourceManager.loadImage("images/unknown.gif");
|
||||
private static ImageIcon markerWarningBookmarkIcon =
|
||||
ResourceManager.loadImage("images/unknown.gif");
|
||||
|
||||
public NextPreviousMarkerAction(PluginTool tool, String owner, String subGroup) {
|
||||
super("Next Marker", owner);
|
||||
this.tool = tool;
|
||||
|
||||
ToolBarData toolBarData =
|
||||
new ToolBarData(markerIcon, ToolConstants.TOOLBAR_GROUP_FOUR);
|
||||
toolBarData.setToolBarSubGroup(subGroup);
|
||||
setToolBarData(toolBarData);
|
||||
|
||||
MenuData menuData =
|
||||
new MenuData(new String[] { ToolConstants.MENU_NAVIGATION, getMenuName() }, markerIcon,
|
||||
ToolConstants.MENU_GROUP_NEXT_CODE_UNIT_NAV);
|
||||
menuData.setMenuSubGroup(subGroup);
|
||||
setMenuBarData(menuData);
|
||||
|
||||
setKeyBindingData(new KeyBindingData(getKeyStroke()));
|
||||
addToWindowWhen(CodeViewerActionContext.class);
|
||||
|
||||
setHelpLocation(new HelpLocation(HelpTopics.NAVIGATION, getName()));
|
||||
setDescription("Set marker options");
|
||||
addToWindowWhen(CodeViewerActionContext.class);
|
||||
|
||||
ActionState<String> allMarkers =
|
||||
new ActionState<String>("All Types", markerIcon, "All Types");
|
||||
ActionState<String> analysis =
|
||||
new ActionState<String>("Analysis Marker", markerAnalysisBookmarkIcon,
|
||||
BookmarkType.ANALYSIS);
|
||||
ActionState<String> conflictingChanges =
|
||||
new ActionState<String>("Conflicting Changes", markerAnalysisBookmarkIcon,
|
||||
"Conflicting Changes");
|
||||
ActionState<String> latestVersionChanges =
|
||||
new ActionState<String>("Latest Version Changes", markerAnalysisBookmarkIcon,
|
||||
"Latest Version Changes");
|
||||
ActionState<String> notCheckedInChanges =
|
||||
new ActionState<String>("Not Checked In Changes", markerAnalysisBookmarkIcon,
|
||||
"Not Checked In Changes");
|
||||
ActionState<String> unsavedChanges =
|
||||
new ActionState<String>("Unsaved Changes", markerIcon, "Unsaved Changes");
|
||||
ActionState<String> cursor = new ActionState<String>("Cursor", markerIcon, "Cursor");
|
||||
ActionState<String> error =
|
||||
new ActionState<String>("Error Marker", markerIcon, BookmarkType.ERROR);
|
||||
ActionState<String> highlight =
|
||||
new ActionState<String>("Highlight", markerIcon, "Highlight");
|
||||
ActionState<String> info =
|
||||
new ActionState<String>("Info Marker", markerIcon, BookmarkType.INFO);
|
||||
ActionState<String> note =
|
||||
new ActionState<String>("Note Marker", markerIcon, BookmarkType.NOTE);
|
||||
ActionState<String> selection =
|
||||
new ActionState<String>("Selection", markerIcon, "Selection");
|
||||
ActionState<String> warning =
|
||||
new ActionState<String>("Warning Marker", markerIcon, BookmarkType.WARNING);
|
||||
ActionState<String> custom =
|
||||
new ActionState<String>("Custom Marker", markerIcon, "Custom Marker");
|
||||
|
||||
addActionState(allMarkers);
|
||||
addActionState(analysis);
|
||||
addActionState(conflictingChanges);
|
||||
addActionState(latestVersionChanges);
|
||||
addActionState(notCheckedInChanges);
|
||||
addActionState(unsavedChanges);
|
||||
addActionState(cursor);
|
||||
addActionState(error);
|
||||
addActionState(highlight);
|
||||
addActionState(info);
|
||||
addActionState(note);
|
||||
addActionState(selection);
|
||||
addActionState(warning);
|
||||
addActionState(custom);
|
||||
|
||||
setCurrentActionState(allMarkers); // default
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMenuBarData(MenuData newMenuData) {
|
||||
//
|
||||
// When we are in the menu we will display our default icon, which is the marker icon.
|
||||
//
|
||||
superSetMenuBarData(newMenuData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doActionPerformed(ActionContext context) {
|
||||
gotoNextPrevious((ListingActionContext) context.getContextObject(),
|
||||
this.getCurrentUserData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionStateChanged(ActionState<String> newActionState, EventTrigger trigger) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
// Find the beginning of the next instruction range
|
||||
/*private Address getNextAddress(Program program, Address address, String markerType) {
|
||||
MarkerSet nextMarker = getNextMarker(program, address, true, markerType);
|
||||
return nextMarker == null ? null : nextMarker.getAddressSet().getMinAddress();
|
||||
}
|
||||
*/
|
||||
/*private Address getPreviousAddress(Program program, Address address, String markerType) {
|
||||
MarkerManager markerManager = program.getMarkerManager();
|
||||
Iterator<Marker> markerIterator = markerManager.getMarkersIterator(address, false);
|
||||
if (isMarkerAddressEqualToCurrent(markerIterator.next(), address)) {
|
||||
|
||||
}
|
||||
MarkerSet nextMarker = getNextMarker(program, address, false, markerType);
|
||||
return nextMarker == null ? null : nextMarker.getAddressSet().getMinAddress();
|
||||
}
|
||||
|
||||
private MarkerSet getNextMarker(Program program, Address address, boolean forward,
|
||||
String markerType) {
|
||||
MarkerManager markerManager = program.getMarkerManager();
|
||||
Iterator<MarkerSet> markerIterator = markerManager.getMarkersIterator(address, forward);
|
||||
while (markerIterator.hasNext()) {
|
||||
MarkerSet nextMarker = markerIterator.next();
|
||||
Address nextAddress = nextMarker.getAddressSet().getMinAddress();
|
||||
if (nextAddress.isExternalAddress()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (markerType.equals(MarkerType.ALL_TYPES) && !nextAddress.equals(address)) {
|
||||
return nextMarker;
|
||||
}
|
||||
else if (markerType.equals("Custom")) {
|
||||
if (!nextMarker.getTypeString().equals(MarkerType.ANALYSIS) &&
|
||||
!nextMarker.getTypeString().equals(MarkerType.INFO) &&
|
||||
!nextMarker.getTypeString().equals(MarkerType.NOTE) &&
|
||||
!nextMarker.getTypeString().equals(MarkerType.WARNING) &&
|
||||
!nextMarker.getTypeString().equals(MarkerType.ERROR) &&
|
||||
!nextAddress.equals(address)) {
|
||||
return nextMarker;
|
||||
}
|
||||
|
||||
}
|
||||
else if (nextMarker.getTypeString().equals(markerType) && !nextAddress.equals(address)) {
|
||||
return nextMarker;
|
||||
}
|
||||
}
|
||||
|
||||
if (!markerIterator.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
return markerIterator.next();
|
||||
}
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private boolean isMarkerAddressEqualToCurrent(MarkerSet marker, Address address) {
|
||||
if (marker == null) {
|
||||
return false;
|
||||
}
|
||||
return !address.equals(marker.getMinAddress());
|
||||
}
|
||||
|
||||
private void gotoAddress(GoToService service, Navigatable navigatable, Address address) {
|
||||
service.goTo(navigatable, address);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// AbstractNextPreviousAction Methods
|
||||
//==================================================================================================
|
||||
void gotoNextPrevious(final ListingActionContext context, final String markerType) {
|
||||
/*final Address address =
|
||||
isForward ? getNextAddress(context.getProgram(), context.getAddress(), markerType)
|
||||
: getPreviousAddress(context.getProgram(), context.getAddress(), markerType);
|
||||
*//*
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
gotoAddress(context, address);
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
private void gotoAddress(ListingActionContext listingActionContext, Address address) {
|
||||
if (address == null) {
|
||||
tool.setStatusInfo("Unable to locate another " + getNavigationTypeName() +
|
||||
" past the current range, in the current direction.");
|
||||
return;
|
||||
}
|
||||
tool.clearStatusInfo();
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service != null) {
|
||||
Navigatable navigatable = listingActionContext.getNavigatable();
|
||||
gotoAddress(service, navigatable, address);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setDirection(boolean isForward) {
|
||||
this.isForward = isForward;
|
||||
getMenuBarData().setMenuItemName(getMenuName());
|
||||
setDescription(getDescription());
|
||||
}
|
||||
|
||||
private String getMenuName() {
|
||||
String prefix = isForward ? "Next " : "Previous ";
|
||||
return prefix + getNavigationTypeName();
|
||||
}
|
||||
|
||||
private String getNavigationTypeName() {
|
||||
return "Marker";
|
||||
}
|
||||
|
||||
private KeyStroke getKeyStroke() {
|
||||
return KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK |
|
||||
InputEvent.ALT_DOWN_MASK);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// CodeViewerContextAction Methods
|
||||
//==================================================================================================
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof CodeViewerActionContext)) {
|
||||
return false;
|
||||
}
|
||||
return isEnabledForContext((CodeViewerActionContext) context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidContext(ActionContext context) {
|
||||
if (!(context instanceof CodeViewerActionContext)) {
|
||||
return false;
|
||||
}
|
||||
return isValidContext((CodeViewerActionContext) context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!(context instanceof CodeViewerActionContext)) {
|
||||
return false;
|
||||
}
|
||||
return isAddToPopup((CodeViewerActionContext) context);
|
||||
}
|
||||
|
||||
protected boolean isValidContext(CodeViewerActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isEnabledForContext(CodeViewerActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isAddToPopup(CodeViewerActionContext context) {
|
||||
return isEnabledForContext(context);
|
||||
}
|
||||
|
||||
}
|
|
@ -128,15 +128,8 @@ public class OverviewColorPlugin extends ProgramPlugin {
|
|||
actionMap.put(overviewColorService,
|
||||
new OverviewToggleAction(getName(), overviewColorService));
|
||||
}
|
||||
multiAction = new MultiActionDockingAction("Overview", getName()) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
// do nothing - the following setPerformActionOnButtonClick(false) will ensure
|
||||
// this never gets called.
|
||||
}
|
||||
};
|
||||
multiAction.setPerformActionOnButtonClick(false);
|
||||
multiAction = new MultiActionDockingAction("Overview", getName());
|
||||
multiAction.setActions(new ArrayList<DockingActionIf>(actionMap.values()));
|
||||
multiAction.setToolBarData(
|
||||
new ToolBarData(ResourceManager.loadImage("images/x-office-document-template.png")));
|
||||
|
|
|
@ -668,8 +668,6 @@ public class ListingCodeComparisonPanel
|
|||
setHelpLocation(helpLocation);
|
||||
setDescription("Set Navigate Next/Previous Area Marker options");
|
||||
|
||||
setPerformActionOnPrimaryButtonClick(false);
|
||||
|
||||
ActionState<String> allAreaMarkers =
|
||||
new ActionState<>(ALL_AREA_MARKERS, bothIcon, ALL_AREA_MARKERS);
|
||||
allAreaMarkers.setHelpLocation(helpLocation);
|
||||
|
|
|
@ -177,7 +177,8 @@ public class ClearTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testClearActionEnablement() throws Exception {
|
||||
|
||||
closeProgram();
|
||||
assertTrue(!clearAction.isEnabledForContext(new ActionContext()));
|
||||
ActionContext context = cb.getProvider().getActionContext(null);
|
||||
assertFalse(clearAction.isEnabledForContext(context));
|
||||
|
||||
showTool(tool);
|
||||
loadProgram("notepad");
|
||||
|
@ -185,10 +186,12 @@ public class ClearTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
waitForSwing();
|
||||
assertTrue(cb.goToField(addr("0x10026f0"), "Address", 0, 0));
|
||||
|
||||
assertTrue(clearAction.isEnabled());
|
||||
context = cb.getProvider().getActionContext(null);
|
||||
assertTrue(clearAction.isEnabledForContext(context));
|
||||
closeProgram();
|
||||
|
||||
assertTrue(!clearAction.isEnabledForContext(new ActionContext()));
|
||||
context = cb.getProvider().getActionContext(null);
|
||||
assertFalse(clearAction.isEnabledForContext(context));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -453,7 +456,7 @@ public class ClearTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Symbol[] symbols = program.getSymbolTable().getSymbols(addr("0x01001010"));
|
||||
assertEquals(1, symbols.length);
|
||||
assertTrue(!symbols[0].isDynamic());
|
||||
assertFalse(symbols[0].isDynamic());
|
||||
int id = program.startTransaction("Anchor");
|
||||
symbols[0].setPinned(true);
|
||||
program.endTransaction(id, true);
|
||||
|
@ -467,7 +470,7 @@ public class ClearTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
symbols = program.getSymbolTable().getSymbols(addr("0x01001010"));
|
||||
assertEquals(1, symbols.length);
|
||||
assertTrue(!symbols[0].isDynamic());
|
||||
assertFalse(symbols[0].isDynamic());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -639,7 +642,7 @@ public class ClearTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(l.getNumInstructions() > 0);
|
||||
assertTrue(l.getNumDefinedData() > 0);
|
||||
|
||||
assertTrue(!program.getListing().getFunctions(true).hasNext());
|
||||
assertFalse(program.getListing().getFunctions(true).hasNext());
|
||||
|
||||
assertTrue(program.getSymbolTable().getNumSymbols() > 0);
|
||||
undo(program);
|
||||
|
@ -674,7 +677,7 @@ public class ClearTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
okOnClearDialog();
|
||||
|
||||
assertTrue(!context.hasValueOverRange(ax, BigInteger.valueOf(5),
|
||||
assertFalse(context.hasValueOverRange(ax, BigInteger.valueOf(5),
|
||||
new AddressSet(addr("0x10022cc"))));
|
||||
undo(program);
|
||||
assertTrue(context.hasValueOverRange(ax, BigInteger.valueOf(5),
|
||||
|
|
|
@ -304,13 +304,6 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private ComponentProvider showDecompiler() {
|
||||
ComponentProvider cp = tool.getComponentProvider("Decompiler");
|
||||
tool.showComponentProvider(cp, true);
|
||||
cp.requestFocus();
|
||||
return cp;
|
||||
}
|
||||
|
||||
private void assertCurrentAddress(Address expected) {
|
||||
assertEquals(expected, currentAddress());
|
||||
}
|
||||
|
@ -415,8 +408,7 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
//
|
||||
|
||||
// clear the popup
|
||||
Object mouseAdapter = getInstanceField("popupListener", button);
|
||||
setInstanceField("popupMenu", mouseAdapter, null);
|
||||
setInstanceField("popupMenu", button, null);
|
||||
|
||||
// trigger the popup
|
||||
Shape popupTriggerArea = (Shape) TestUtils.getInstanceField("popupContext", button);
|
||||
|
@ -426,7 +418,7 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
clickMouse(button, MouseEvent.BUTTON1, x, y, 1, 0);
|
||||
|
||||
// get the popup
|
||||
JPopupMenu menu = (JPopupMenu) getInstanceField("popupMenu", mouseAdapter);
|
||||
JPopupMenu menu = (JPopupMenu) getInstanceField("popupMenu", button);
|
||||
assertNotNull(menu);
|
||||
|
||||
// Note: calling clickMouse() seems to work for now. If this is not consistent, then
|
||||
|
|
|
@ -43,18 +43,17 @@ import ghidra.framework.main.datatree.DataTree;
|
|||
import ghidra.framework.main.datatree.ProjectDataTreePanel;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.symbol.LibrarySymbol;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryConflictException;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
|
@ -158,7 +157,7 @@ public abstract class AbstractSymbolTreePluginExternalsTest
|
|||
|
||||
protected ExternalLocation setupExternalLocation(String library, String label, Address address,
|
||||
SourceType sourceType, boolean isFunction)
|
||||
throws InvalidInputException, DuplicateNameException {
|
||||
throws Exception {
|
||||
boolean success = false;
|
||||
int transactionID =
|
||||
program.startTransaction("Setting Up External Location " + library + "::" + label);
|
||||
|
@ -181,12 +180,12 @@ public abstract class AbstractSymbolTreePluginExternalsTest
|
|||
}
|
||||
|
||||
protected ExternalLocation setupExternalLocation(String library, String label, Address address,
|
||||
SourceType sourceType) throws InvalidInputException, DuplicateNameException {
|
||||
SourceType sourceType) throws Exception {
|
||||
return setupExternalLocation(library, label, address, sourceType, false);
|
||||
}
|
||||
|
||||
protected ExternalLocation setupExternalFunction(String library, String label, Address address,
|
||||
SourceType sourceType) throws InvalidInputException, DuplicateNameException {
|
||||
SourceType sourceType) throws Exception {
|
||||
return setupExternalLocation(library, label, address, sourceType, true);
|
||||
}
|
||||
|
||||
|
@ -448,8 +447,7 @@ public abstract class AbstractSymbolTreePluginExternalsTest
|
|||
}
|
||||
|
||||
protected void addOverlayBlock(String name, String startAddress, long length)
|
||||
throws LockException, DuplicateNameException, MemoryConflictException,
|
||||
AddressOverflowException, CancelledException {
|
||||
throws Exception {
|
||||
int transactionID = program.startTransaction("Add Overlay Block to test");
|
||||
Address address = program.getAddressFactory().getAddress(startAddress);
|
||||
Memory memory = program.getMemory();
|
||||
|
|
|
@ -87,7 +87,7 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertNotNull(closeOthersAction);
|
||||
ProgramActionContext context = new ProgramActionContext(null, program1);
|
||||
assertEquals(true, closeOthersAction.isEnabledForContext(context));
|
||||
performAction(closeOthersAction, true);
|
||||
performAction(closeOthersAction, context, true);
|
||||
|
||||
allOpenPrograms = pm.getAllOpenPrograms();
|
||||
assertEquals(1, allOpenPrograms.length);
|
||||
|
|
|
@ -859,10 +859,10 @@ class FGActionManager {
|
|||
HelpLocation layoutHelpLocation =
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
|
||||
|
||||
layoutAction = new MultiStateDockingAction<>("Relayout Graph", plugin.getName(), true) {
|
||||
layoutAction = new MultiStateDockingAction<>("Relayout Graph", plugin.getName()) {
|
||||
|
||||
@Override
|
||||
protected void doActionPerformed(ActionContext context) {
|
||||
public void actionPerformed(ActionContext context) {
|
||||
// this callback is when the user clicks the button
|
||||
FGLayoutProvider currentUserData = getCurrentUserData();
|
||||
changeLayout(currentUserData);
|
||||
|
@ -994,7 +994,6 @@ class FGActionManager {
|
|||
};
|
||||
vertexHoverModeAction.setGroup(group);
|
||||
vertexHoverModeAction.setHelpLocation(pathHelpLocation);
|
||||
vertexHoverModeAction.setPerformActionOnPrimaryButtonClick(false);
|
||||
|
||||
vertexHoverModeAction.addActionState(offState);
|
||||
vertexHoverModeAction.addActionState(pathsForwardScopedFlow);
|
||||
|
@ -1063,7 +1062,6 @@ class FGActionManager {
|
|||
};
|
||||
vertexFocusModeAction.setGroup(group);
|
||||
vertexFocusModeAction.setHelpLocation(pathHelpLocation);
|
||||
vertexFocusModeAction.setPerformActionOnPrimaryButtonClick(false);
|
||||
|
||||
vertexFocusModeAction.addActionState(offState);
|
||||
vertexFocusModeAction.addActionState(pathsForwardScopedFlow);
|
||||
|
|
|
@ -526,7 +526,7 @@ public class FcgProvider
|
|||
RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) {
|
||||
|
||||
@Override
|
||||
protected void doActionPerformed(ActionContext context) {
|
||||
public void actionPerformed(ActionContext context) {
|
||||
// this callback is when the user clicks the button
|
||||
LayoutProvider<FcgVertex, FcgEdge, FunctionCallGraph> currentUserData =
|
||||
getCurrentUserData();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,19 +16,20 @@
|
|||
package ghidra.feature.vt.gui.actions;
|
||||
|
||||
import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.*;
|
||||
import ghidra.feature.vt.gui.plugin.VTPlugin;
|
||||
import ghidra.feature.vt.gui.provider.matchtable.VTMatchTableProvider;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import resources.ResourceManager;
|
||||
import docking.action.ToolBarData;
|
||||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.feature.vt.gui.plugin.VTPlugin;
|
||||
import ghidra.feature.vt.gui.provider.matchtable.VTMatchTableProvider;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class MatchTableSelectionAction extends MultiStateDockingAction<TableSelectionTrackingState> {
|
||||
public class MatchTableSelectionAction
|
||||
extends MultiStateDockingAction<TableSelectionTrackingState> {
|
||||
|
||||
private static final String MENU_GROUP = VTPlugin.VT_SETTINGS_MENU_GROUP;
|
||||
|
||||
|
@ -44,7 +44,6 @@ public class MatchTableSelectionAction extends MultiStateDockingAction<TableSele
|
|||
setToolBarData(new ToolBarData(null, MENU_GROUP));
|
||||
setDescription("Adjust the Apply Mark-up Settings for Applying Matches");
|
||||
setEnabled(true);
|
||||
setPerformActionOnPrimaryButtonClick(false); // pressing button shows drop-down
|
||||
|
||||
HelpLocation helpLocation =
|
||||
new HelpLocation("VersionTrackingPlugin", "Match_Table_Selection");
|
||||
|
@ -55,17 +54,17 @@ public class MatchTableSelectionAction extends MultiStateDockingAction<TableSele
|
|||
Icon trackRowIndexSelectionIcon = ResourceManager.loadImage("images/table_gear.png");
|
||||
|
||||
ActionState<TableSelectionTrackingState> trackSelectedIndexActionState =
|
||||
new ActionState<TableSelectionTrackingState>("Track Selected Index",
|
||||
new ActionState<>("Track Selected Index",
|
||||
trackRowIndexSelectionIcon, MAINTAIN_SELECTED_ROW_INDEX);
|
||||
trackSelectedIndexActionState.setHelpLocation(helpLocation);
|
||||
|
||||
ActionState<TableSelectionTrackingState> trackMatchSelectionActionState =
|
||||
new ActionState<TableSelectionTrackingState>("Track Selected Match",
|
||||
new ActionState<>("Track Selected Match",
|
||||
trackMatchSelectionIcon, MAINTAIN_SELECTED_ROW_VALUE);
|
||||
trackMatchSelectionActionState.setHelpLocation(helpLocation);
|
||||
|
||||
ActionState<TableSelectionTrackingState> noSelectionTrackingActionState =
|
||||
new ActionState<TableSelectionTrackingState>("No Selection Tracking",
|
||||
new ActionState<>("No Selection Tracking",
|
||||
noSelectionTrackingIcon, NO_SELECTION_TRACKING);
|
||||
noSelectionTrackingActionState.setHelpLocation(helpLocation);
|
||||
|
||||
|
|
|
@ -15,12 +15,8 @@
|
|||
*/
|
||||
package ghidra.feature.vt.gui.provider.functionassociation;
|
||||
|
||||
import static ghidra.feature.vt.api.impl.VTChangeManager.DOCR_VT_ASSOCIATION_STATUS_CHANGED;
|
||||
import static ghidra.feature.vt.api.impl.VTChangeManager.DOCR_VT_MATCH_ADDED;
|
||||
import static ghidra.feature.vt.api.impl.VTChangeManager.DOCR_VT_MATCH_DELETED;
|
||||
import static ghidra.feature.vt.gui.provider.functionassociation.FilterSettings.SHOW_ALL;
|
||||
import static ghidra.feature.vt.gui.provider.functionassociation.FilterSettings.SHOW_UNACCEPTED;
|
||||
import static ghidra.feature.vt.gui.provider.functionassociation.FilterSettings.SHOW_UNMATCHED;
|
||||
import static ghidra.feature.vt.api.impl.VTChangeManager.*;
|
||||
import static ghidra.feature.vt.gui.provider.functionassociation.FilterSettings.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
@ -156,6 +152,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
|
|||
destinationFunctionsModel.setFilterSettings(filterSettings);
|
||||
}
|
||||
};
|
||||
|
||||
filterAction.setHelpLocation(new HelpLocation("VersionTrackingPlugin", "Functions_Filter"));
|
||||
|
||||
Icon allFunctionsIcon = ResourceManager.loadImage("images/function.png");
|
||||
|
|
|
@ -31,10 +31,6 @@ public class MultiActionBuilder
|
|||
* List of actions for the the MultActionDockingAction
|
||||
*/
|
||||
private List<DockingActionIf> actionList = Collections.emptyList();
|
||||
/**
|
||||
* determines if the the main action is invokable
|
||||
*/
|
||||
private boolean performActionOnButtonClick = true;
|
||||
|
||||
/**
|
||||
* Builder constructor
|
||||
|
@ -61,7 +57,6 @@ public class MultiActionBuilder
|
|||
};
|
||||
decorateAction(action);
|
||||
action.setActions(actionList);
|
||||
action.setPerformActionOnButtonClick(performActionOnButtonClick);
|
||||
return action;
|
||||
}
|
||||
|
||||
|
@ -81,26 +76,8 @@ public class MultiActionBuilder
|
|||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure whether to perform actions on a button click.
|
||||
* See {@link MultiActionDockingAction#setPerformActionOnButtonClick(boolean)}
|
||||
*
|
||||
* @param b true if the main action is invokable
|
||||
* @return this MultiActionDockingActionBuilder (for chaining)
|
||||
*/
|
||||
public MultiActionBuilder performActionOnButtonClick(boolean b) {
|
||||
this.performActionOnButtonClick = b;
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validate() {
|
||||
// if the MultiAction performs an action when the main button is presseed, make sure that
|
||||
// an action callback has been defined in before building (which is what super validate
|
||||
// does). Otherwise, don't force the client to define an action callback if it won't be used.
|
||||
if (performActionOnButtonClick) {
|
||||
super.validate();
|
||||
}
|
||||
if (actionList == null) {
|
||||
throw new IllegalStateException("No ActionList has been set");
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.util.function.BiConsumer;
|
|||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.menu.*;
|
||||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.widgets.EventTrigger;
|
||||
|
||||
/**
|
||||
|
@ -35,7 +36,6 @@ public class MultiStateActionBuilder<T> extends
|
|||
|
||||
private BiConsumer<ActionState<T>, EventTrigger> actionStateChangedCallback;
|
||||
private boolean useCheckboxForIcons;
|
||||
private boolean performActionOnButtonClick = false;
|
||||
|
||||
private List<ActionState<T>> states = new ArrayList<>();
|
||||
|
||||
|
@ -68,18 +68,6 @@ public class MultiStateActionBuilder<T> extends
|
|||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure whether to perform actions on a button click.
|
||||
* See {@link MultiActionDockingAction#setPerformActionOnButtonClick(boolean)}
|
||||
*
|
||||
* @param b true if the main action is invokable
|
||||
* @return this MultiActionDockingActionBuilder (for chaining)
|
||||
*/
|
||||
public MultiStateActionBuilder<T> performActionOnButtonClick(boolean b) {
|
||||
this.performActionOnButtonClick = b;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default icons for actions shown in popup menu of the multi-state action. By
|
||||
* default, the popup menu items will use the icons as provided by the {@link ActionState}.
|
||||
|
@ -103,7 +91,7 @@ public class MultiStateActionBuilder<T> extends
|
|||
* @return this MultiActionDockingActionBuilder (for chaining)
|
||||
*/
|
||||
public MultiStateActionBuilder<T> addState(String displayName, Icon icon, T userData) {
|
||||
states.add(new ActionState<T>(displayName, icon, userData));
|
||||
states.add(new ActionState<>(displayName, icon, userData));
|
||||
return self();
|
||||
}
|
||||
|
||||
|
@ -133,7 +121,7 @@ public class MultiStateActionBuilder<T> extends
|
|||
public MultiStateDockingAction<T> build() {
|
||||
validate();
|
||||
MultiStateDockingAction<T> action =
|
||||
new MultiStateDockingAction<>(name, owner, isToolbarAction()) {
|
||||
new MultiStateDockingAction<>(name, owner) {
|
||||
|
||||
@Override
|
||||
public void actionStateChanged(ActionState<T> newActionState,
|
||||
|
@ -142,10 +130,13 @@ public class MultiStateActionBuilder<T> extends
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doActionPerformed(ActionContext context) {
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (actionCallback != null) {
|
||||
actionCallback.accept(context);
|
||||
}
|
||||
else {
|
||||
super.actionPerformed(context);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -154,16 +145,12 @@ public class MultiStateActionBuilder<T> extends
|
|||
}
|
||||
|
||||
decorateAction(action);
|
||||
action.setPerformActionOnPrimaryButtonClick(performActionOnButtonClick);
|
||||
action.setUseCheckboxForIcons(useCheckboxForIcons);
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validate() {
|
||||
if (performActionOnButtonClick) {
|
||||
super.validate(); // require an action callback has been defined
|
||||
}
|
||||
if (actionStateChangedCallback == null) {
|
||||
throw new IllegalStateException(
|
||||
"Can't build a MultiStateDockingAction without an action state changed callback");
|
||||
|
|
|
@ -22,13 +22,14 @@ import javax.swing.JButton;
|
|||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
* A class that supports multiple sub-actions, as well as a primary action. This is useful for
|
||||
* actions that perform navigation operations.
|
||||
* <p>
|
||||
* Clients may add actions to this class with the intention that they will be accessible
|
||||
* to the user via a GUI; for example, from a popup menu.
|
||||
* Clients may add actions to this class with the intention that they will be accessible to the
|
||||
* user via a GUI; for example, from a popup menu.
|
||||
* <p>
|
||||
* Actions added must have menu bar data set.
|
||||
*
|
||||
|
@ -36,18 +37,20 @@ import docking.action.*;
|
|||
* the user to execute.
|
||||
*
|
||||
* <p>
|
||||
* If the user executes this action directly, then
|
||||
* {@link #actionPerformed(ActionContext)} will be called. Otherwise, the
|
||||
* {@link DockingAction#actionPerformed(ActionContext)} method of the sub-action
|
||||
* that was executed will be called.
|
||||
* If the user executes this action directly (by clicking the non-popup section of the button),
|
||||
* then {@link #actionPerformed(ActionContext)} will be called. By default, when the button is
|
||||
* clicked, the popup menu is shown. To change this behavior, override
|
||||
* {@link #actionPerformed(ActionContext)}. If an item of the popup menu is clicked, then the
|
||||
* {@link DockingAction#actionPerformed(ActionContext)} method of the sub-action that was executed
|
||||
* will be called.
|
||||
*
|
||||
* @see MultiStateDockingAction
|
||||
*/
|
||||
public abstract class MultiActionDockingAction extends DockingAction
|
||||
public class MultiActionDockingAction extends DockingAction
|
||||
implements MultiActionDockingActionIf {
|
||||
|
||||
private List<DockingActionIf> actionList = Collections.emptyList();
|
||||
private boolean performActionOnButtonClick = true;
|
||||
private MultipleActionDockingToolbarButton multipleButton;
|
||||
|
||||
public MultiActionDockingAction(String name, String owner) {
|
||||
super(name, owner);
|
||||
|
@ -67,24 +70,23 @@ public abstract class MultiActionDockingAction extends DockingAction
|
|||
return actionList;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when the user clicks the button <B>when this action is used as part of
|
||||
* the default {@link DockingAction} framework.</B>
|
||||
*
|
||||
* This is the callback to be overridden when the child wishes to respond to user button
|
||||
* presses that are on the button and not the drop-down. The default behavior is to show the
|
||||
* popup menu when the button is clicked.
|
||||
*/
|
||||
@Override
|
||||
public JButton doCreateButton() {
|
||||
MultipleActionDockingToolbarButton button = new MultipleActionDockingToolbarButton(this);
|
||||
button.setPerformActionOnButtonClick(performActionOnButtonClick);
|
||||
return button;
|
||||
public void actionPerformed(ActionContext context) {
|
||||
Swing.runLater(() -> multipleButton.showPopup());
|
||||
}
|
||||
|
||||
/**
|
||||
* By default a click on this action will trigger <code>actionPerformed()</code> to be called.
|
||||
* You can call this method to disable that feature. When called with <code>false</code>, this
|
||||
* method will effectively let the user click anywhere on the button or its drop-down arrow
|
||||
* to show the popup menu. During normal operation, the user can only show the popup by
|
||||
* clicking the drop-down arrow.
|
||||
* @param performActionOnButtonClick if true, pressing the button calls actionPerformed;
|
||||
* otherwise it pops up the menu.
|
||||
*/
|
||||
public void setPerformActionOnButtonClick(boolean performActionOnButtonClick) {
|
||||
this.performActionOnButtonClick = performActionOnButtonClick;
|
||||
@Override
|
||||
public JButton doCreateButton() {
|
||||
multipleButton = new MultipleActionDockingToolbarButton(this);
|
||||
return multipleButton;
|
||||
}
|
||||
|
||||
public static DockingActionIf createSeparator() {
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package docking.menu;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -23,25 +22,24 @@ import javax.swing.Icon;
|
|||
import javax.swing.JButton;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.action.*;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import resources.icons.EmptyIcon;
|
||||
|
||||
/**
|
||||
* An action that can be in one of multiple states. The button of this action has a
|
||||
* drop-down icon that allows users to change the state of the button. Also, by default, as
|
||||
* the user presses the button, it will execute the action corresponding to the current
|
||||
* state.
|
||||
*
|
||||
* <p>Warning: if you use this action in a toolbar, then be sure to call the
|
||||
* {@link #MultiStateDockingAction(String, String, boolean) correct constructor}. If you call
|
||||
* another constructor, or pass false for this boolean above, your
|
||||
* {@link #doActionPerformed(ActionContext)} method will get called twice.
|
||||
* drop-down icon that allows users to change the state of the button. As the user changes the
|
||||
* state of this action, {@link #actionStateChanged(ActionState, EventTrigger)} will be called.
|
||||
* Clients may also use the button of this action to respond to button presses by overriding
|
||||
* {@link #actionPerformed(ActionContext)}.
|
||||
*
|
||||
* <p>This action is intended primarily for use as toolbar actions. Alternatively, some clients
|
||||
* use this action to add a button to custom widgets. In the custom usage case, clients should use
|
||||
* {@link NonToolbarMultiStateAction}.
|
||||
*
|
||||
* @param <T> the type of the user data
|
||||
* @see MultiActionDockingAction
|
||||
*/
|
||||
|
@ -54,16 +52,9 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||
private MultiActionDockingActionIf multiActionGenerator;
|
||||
private MultipleActionDockingToolbarButton multipleButton;
|
||||
|
||||
private boolean performActionOnPrimaryButtonClick = true;
|
||||
private Icon defaultIcon;
|
||||
private boolean useCheckboxForIcons;
|
||||
|
||||
// A listener that will get called when the button (not the popup) is clicked. Toolbar
|
||||
// actions do not use this listener.
|
||||
private ActionListener clickListener = e -> {
|
||||
// stub for toolbar actions
|
||||
};
|
||||
|
||||
/**
|
||||
* Call this constructor with this action will not be added to a toolbar
|
||||
*
|
||||
|
@ -72,7 +63,11 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||
* @see #MultiStateDockingAction(String, String, boolean)
|
||||
*/
|
||||
public MultiStateDockingAction(String name, String owner) {
|
||||
this(name, owner, false);
|
||||
super(name, owner);
|
||||
multiActionGenerator = context -> getStateActions();
|
||||
|
||||
// set this here so we don't have to check for null elsewhere
|
||||
super.setToolBarData(new ToolBarData(null));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,50 +77,31 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||
* @param name the action name
|
||||
* @param owner the owner
|
||||
* @param isToolbarAction true if this action is a toolbar action
|
||||
* @deprecated use {@link #MultiStateDockingAction(String, String)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "10.2")
|
||||
protected MultiStateDockingAction(String name, String owner, boolean isToolbarAction) {
|
||||
super(name, owner);
|
||||
multiActionGenerator = context -> getStateActions();
|
||||
|
||||
// set this here so we don't have to check for null elsewhere
|
||||
super.setToolBarData(new ToolBarData(null));
|
||||
|
||||
if (!isToolbarAction) {
|
||||
// we need this listener to perform the action when the user click the button;
|
||||
// toolbar actions have their own listener
|
||||
clickListener = e -> {
|
||||
actionPerformed(getActionContext());
|
||||
};
|
||||
}
|
||||
this(name, owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be called as the user changes the selected button state
|
||||
* @param newActionState the newly selected state
|
||||
* @param trigger the source of the event
|
||||
*/
|
||||
public abstract void actionStateChanged(ActionState<T> newActionState, EventTrigger trigger);
|
||||
|
||||
/**
|
||||
* If <code>doPerformAction</code> is <code>true</code>, then, when the user clicks the
|
||||
* button and not the drop-down arrow, the {@link #doActionPerformed(ActionContext)}
|
||||
* method will be called. If <code>doPerformAction</code> is <code>false</code>, then, when
|
||||
* the user clicks the button and not the drop-down arrow, the popup menu will be shown, just
|
||||
* as if the user had clicked the drop-down arrow.
|
||||
* <p>
|
||||
* Also, if the parameter is true, then the button will behave like a button in terms of
|
||||
* mouse feedback. If false, then the button will behave more like a label.
|
||||
*
|
||||
* @param doPerformAction true to call {@link #doActionPerformed(ActionContext)} when the
|
||||
* user presses the button for this action (not the drop-down menu; see above)
|
||||
* This method is called when the user clicks the button <B>when this action is used as part of
|
||||
* the default {@link DockingAction} framework.</B>
|
||||
*
|
||||
* This is the callback to be overridden when the child wishes to respond to user button
|
||||
* presses that are on the button and not the drop-down. The default behavior is to show the
|
||||
* popup menu when the button is clicked.
|
||||
*/
|
||||
public void setPerformActionOnPrimaryButtonClick(boolean doPerformAction) {
|
||||
performActionOnPrimaryButtonClick = doPerformAction;
|
||||
if (multipleButton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
multipleButton.setPerformActionOnButtonClick(performActionOnPrimaryButtonClick);
|
||||
|
||||
multipleButton.removeActionListener(clickListener);
|
||||
if (performActionOnPrimaryButtonClick) {
|
||||
multipleButton.addActionListener(clickListener);
|
||||
}
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
Swing.runLater(() -> multipleButton.showPopup());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,38 +127,6 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||
this.defaultIcon = icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void actionPerformed(ActionContext context) {
|
||||
if (!performActionOnPrimaryButtonClick) {
|
||||
SystemUtilities.runSwingLater(() -> multipleButton.showPopup(null));
|
||||
return;
|
||||
}
|
||||
|
||||
doActionPerformed(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the callback to be overridden when the child wishes to respond to user button
|
||||
* presses that are on the button and not the drop-down. This will only be called if
|
||||
* {@link #performActionOnPrimaryButtonClick} is true.
|
||||
*
|
||||
* @param context the action context
|
||||
*/
|
||||
protected void doActionPerformed(ActionContext context) {
|
||||
// override me to do work
|
||||
}
|
||||
|
||||
private ActionContext getActionContext() {
|
||||
DockingWindowManager manager = DockingWindowManager.getActiveInstance();
|
||||
|
||||
ActionContext context = manager.getActionContext(this);
|
||||
|
||||
if (context == null) {
|
||||
context = new ActionContext();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
protected List<DockingActionIf> getStateActions() {
|
||||
ActionState<T> selectedState = actionStates.get(currentStateIndex);
|
||||
List<DockingActionIf> actions = new ArrayList<>(actionStates.size());
|
||||
|
@ -295,15 +239,6 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||
@Override
|
||||
public JButton doCreateButton() {
|
||||
multipleButton = new MultipleActionDockingToolbarButton(multiActionGenerator);
|
||||
multipleButton.setPerformActionOnButtonClick(performActionOnPrimaryButtonClick);
|
||||
|
||||
if (performActionOnPrimaryButtonClick) {
|
||||
multipleButton.addActionListener(clickListener);
|
||||
}
|
||||
else {
|
||||
multipleButton.removeActionListener(clickListener);
|
||||
}
|
||||
|
||||
if (currentStateIndex >= 0) {
|
||||
ActionState<T> actionState = actionStates.get(currentStateIndex);
|
||||
setButtonState(actionState);
|
||||
|
@ -350,6 +285,10 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||
return getName() + ": " + getCurrentState().getName();
|
||||
}
|
||||
|
||||
protected void showPopup() {
|
||||
multipleButton.showPopup();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
|
|
@ -43,11 +43,11 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
private static int ARROW_PADDING = 4;
|
||||
|
||||
private PopupMouseListener popupListener;
|
||||
private JPopupMenu popupMenu;
|
||||
private Shape popupContext;
|
||||
private long popupLastClosedTime;
|
||||
|
||||
private final MultiActionDockingActionIf multipleAction;
|
||||
private boolean iconBorderEnabled = true;
|
||||
private boolean entireButtonShowsPopupMenu;
|
||||
|
||||
public MultipleActionDockingToolbarButton(MultiActionDockingActionIf action) {
|
||||
multipleAction = action;
|
||||
|
@ -74,21 +74,6 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
return disabledIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default a click on this button will trigger <code>actionPerformed()</code> to be called.
|
||||
* You can call this method to disable that feature. When called with <code>false</code>, this
|
||||
* method will effectively let the user click anywhere on the button or its drop-down arrow
|
||||
* to show the popup menu. During normal operation, the user can only show the popup by
|
||||
* clicking the drop-down arrow.
|
||||
*
|
||||
* @param performActionOnButtonClick true to perform the action when the button is clicked
|
||||
*/
|
||||
public void setPerformActionOnButtonClick(boolean performActionOnButtonClick) {
|
||||
entireButtonShowsPopupMenu = !performActionOnButtonClick;
|
||||
iconBorderEnabled = performActionOnButtonClick;
|
||||
popupContext = createPopupContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintBorder(Graphics g) {
|
||||
Border buttonBorder = getBorder();
|
||||
|
@ -98,10 +83,7 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
|
||||
Insets borderInsets = buttonBorder.getBorderInsets(this);
|
||||
int leftIconWidth = primaryIcon.getIconWidth() + (borderInsets.left + borderInsets.right);
|
||||
if (iconBorderEnabled) {
|
||||
buttonBorder.paintBorder(this, g, 0, 0, leftIconWidth, getHeight());
|
||||
}
|
||||
|
||||
buttonBorder.paintBorder(this, g, 0, 0, leftIconWidth, getHeight());
|
||||
int rightButtonWidth =
|
||||
ARROW_WIDTH + ARROW_PADDING + (borderInsets.left + borderInsets.right);
|
||||
buttonBorder.paintBorder(this, g, leftIconWidth, 0, rightButtonWidth, getHeight());
|
||||
|
@ -132,10 +114,6 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
}
|
||||
|
||||
private Shape createPopupContext() {
|
||||
if (entireButtonShowsPopupMenu) {
|
||||
return new Rectangle(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
Border buttonBorder = getBorder();
|
||||
Insets borderInsets =
|
||||
buttonBorder == null ? new Insets(0, 0, 0, 0) : buttonBorder.getBorderInsets(this);
|
||||
|
@ -163,10 +141,31 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
|
||||
/**
|
||||
* Show a popup containing all the actions below the button
|
||||
* @param listener for the created popup menu
|
||||
* @return the popup menu that was shown
|
||||
*/
|
||||
JPopupMenu showPopup(PopupMenuListener listener) {
|
||||
JPopupMenu showPopup() {
|
||||
|
||||
if (popupIsShowing()) {
|
||||
popupMenu.setVisible(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// showPopup() will handled 2 cases when this action's button is clicked:
|
||||
// 1) show a popup if it was not showing
|
||||
// 2) hide the popup if it was showing
|
||||
//
|
||||
// Case 2 requires timestamps. Java will close the popup as the button is clicked. This
|
||||
// means that when we are told to show the popup as the result of a click, the popup will
|
||||
// never be showing. To work around this, we track the elapsed time since last click. If
|
||||
// the period is too short, then we assume Java closed the popup when the click happened
|
||||
//and thus we should ignore it.
|
||||
//
|
||||
long elapsedTime = System.currentTimeMillis() - popupLastClosedTime;
|
||||
if (elapsedTime < 500) { // somewhat arbitrary time window
|
||||
return null;
|
||||
}
|
||||
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
List<DockingActionIf> actionList = multipleAction.getActionList(getActionContext());
|
||||
for (DockingActionIf dockingAction : actionList) {
|
||||
|
@ -202,9 +201,7 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
menu.add(item);
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
menu.addPopupMenuListener(listener);
|
||||
}
|
||||
menu.addPopupMenuListener(popupListener);
|
||||
Point p = getPopupPoint();
|
||||
menu.show(this, p.x, p.y);
|
||||
return menu;
|
||||
|
@ -215,6 +212,10 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
return new Point(0, bounds.y + bounds.height);
|
||||
}
|
||||
|
||||
private boolean popupIsShowing() {
|
||||
return (popupMenu != null) && popupMenu.isVisible();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
@ -285,8 +286,6 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
|
||||
private class PopupMouseListener extends MouseAdapter implements PopupMenuListener {
|
||||
private final MouseListener[] parentListeners;
|
||||
private JPopupMenu popupMenu;
|
||||
private long actionID = 0; // used to determine when the popup was closed by clicking the button
|
||||
|
||||
public PopupMouseListener(MouseListener[] parentListeners) {
|
||||
this.parentListeners = parentListeners;
|
||||
|
@ -294,16 +293,6 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
// close the popup if the user clicks the button while the popup is visible
|
||||
if (popupIsShowing() && e.getClickCount() == 1) { // ignore double-click when the menu is up
|
||||
popupMenu.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
long eventTime = System.currentTimeMillis();
|
||||
if (actionID == eventTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point clickPoint = e.getPoint();
|
||||
if (isEnabled() && popupContext.contains(clickPoint)) {
|
||||
|
@ -311,8 +300,8 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
// Unusual Code Alert: we need to put this call in an invoke later, since Java
|
||||
// will update the focused window after we click. We need the focus to be
|
||||
// correct before we show, since our menu is built with actions based upon the
|
||||
// focused dude.
|
||||
Swing.runLater(() -> popupMenu = showPopup(PopupMouseListener.this));
|
||||
// focused component.
|
||||
Swing.runLater(() -> popupMenu = showPopup());
|
||||
|
||||
e.consume();
|
||||
model.setPressed(false);
|
||||
|
@ -372,10 +361,6 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean popupIsShowing() {
|
||||
return (popupMenu != null) && popupMenu.isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popupMenuCanceled(PopupMenuEvent e) {
|
||||
// no-op
|
||||
|
@ -383,7 +368,7 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
|||
|
||||
@Override
|
||||
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
|
||||
actionID = System.currentTimeMillis(); // hacktastic!
|
||||
popupLastClosedTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,7 +15,13 @@
|
|||
*/
|
||||
package docking.menu;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.JButton;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
* A class for clients that wish to create a button that has multiple states, controlled by a
|
||||
|
@ -27,12 +33,39 @@ import docking.widgets.EventTrigger;
|
|||
* {@link #actionStateChanged(ActionState, EventTrigger)} callback. Call
|
||||
* {@link #createButton()} and add the return value to your UI.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> the type
|
||||
* @see MultiStateDockingAction
|
||||
*/
|
||||
public abstract class NonToolbarMultiStateAction<T> extends MultiStateDockingAction<T> {
|
||||
|
||||
// A listener that will get called when the button (not the popup) is clicked. Toolbar
|
||||
// actions do not need this functionality, since the toolbar API will call actionPerfomred().
|
||||
private ActionListener clickListener = e -> {
|
||||
actionPerformed();
|
||||
};
|
||||
|
||||
public NonToolbarMultiStateAction(String name, String owner) {
|
||||
super(name, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JButton doCreateButton() {
|
||||
JButton button = super.doCreateButton();
|
||||
button.addActionListener(clickListener);
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when the user clicks the button <B>when this action is used as a
|
||||
* custom button provider and not installed into the default {@link DockingAction} framework.
|
||||
* </B>
|
||||
*
|
||||
* This is the callback to be overridden when the child wishes to respond to user button
|
||||
* presses that are on the button and not the drop-down. The default behavior is to show the
|
||||
* popup menu when the button is clicked.
|
||||
*/
|
||||
protected void actionPerformed() {
|
||||
Swing.runLater(() -> showPopup());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import javax.swing.table.TableModel;
|
|||
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.help.HelpService;
|
||||
import docking.menu.*;
|
||||
|
@ -413,12 +412,12 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doActionPerformed(ActionContext context) {
|
||||
protected void actionPerformed() {
|
||||
showFilterDialog(tableModel);
|
||||
}
|
||||
|
||||
};
|
||||
columnFilterAction.setPerformActionOnPrimaryButtonClick(true);
|
||||
|
||||
HelpLocation helpLocation = new HelpLocation("Trees", "Column_Filters");
|
||||
columnFilterAction.setHelpLocation(helpLocation);
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ public interface DataType {
|
|||
|
||||
/**
|
||||
* WARNING: do not add <code>default</code> method implementations to this interface. Doing so
|
||||
* intereferes with correct initialization of the static instance variables {@link #DEFAULT} and
|
||||
* interferes with correct initialization of the static instance variables {@link #DEFAULT} and
|
||||
* {@link #VOID} below.
|
||||
*/
|
||||
|
||||
|
@ -559,7 +559,7 @@ public interface DataType {
|
|||
* The datatypes must be of the same "type" (i.e. structure can only be replacedWith another
|
||||
* structure.
|
||||
*
|
||||
* @param datatype the datatype that contains the internals to upgrade to.
|
||||
* @param dataType the datatype that contains the internals to upgrade to.
|
||||
* @throws UnsupportedOperationException if the datatype does not support change.
|
||||
* @throws IllegalArgumentException if the given datatype is not the same type as this datatype.
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue