Merge remote-tracking branch
'origin/GP-1913-dragonmacher-structure-editor-actions--SQUASHED' Conflicts: Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java
@ -15,8 +15,12 @@
|
||||
*/
|
||||
package agent.frida.model.invm;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaFactoryTest;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaFactoryTest;
|
||||
import generic.test.category.NightlyCategory;
|
||||
|
||||
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||
public class InVmModelForFridaFactoryTest extends AbstractModelForFridaFactoryTest {
|
||||
@Override
|
||||
public ModelHost modelHost() throws Throwable {
|
||||
|
@ -17,11 +17,14 @@ package agent.frida.model.invm;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaInterpreterTest;
|
||||
import generic.test.category.NightlyCategory;
|
||||
import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen;
|
||||
|
||||
public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest
|
||||
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||
public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest
|
||||
implements ProvidesTargetViaLaunchSpecimen {
|
||||
@Override
|
||||
public ModelHost modelHost() throws Throwable {
|
||||
@ -61,4 +64,3 @@ public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,12 @@
|
||||
*/
|
||||
package agent.frida.model.invm;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaMethodsTest;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaMethodsTest;
|
||||
import generic.test.category.NightlyCategory;
|
||||
|
||||
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||
public class InVmModelForFridaMethodsTest extends AbstractModelForFridaMethodsTest {
|
||||
@Override
|
||||
public ModelHost modelHost() throws Throwable {
|
||||
|
@ -17,24 +17,27 @@ package agent.frida.model.invm;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaRootAttacherTest;
|
||||
import generic.test.category.NightlyCategory;
|
||||
|
||||
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||
public class InVmModelForFridaRootAttacherTest extends AbstractModelForFridaRootAttacherTest {
|
||||
@Override
|
||||
public ModelHost modelHost() throws Throwable {
|
||||
return new InVmFridaModelHost();
|
||||
}
|
||||
|
||||
|
||||
// NB: These tests need debugger rights, which means either:
|
||||
// (1) on macos, codesigning the executables
|
||||
// (2) on linux, "sudo su; echo 0 > /proc/sys/kernel/yama/ptrace_scope"
|
||||
|
||||
|
||||
@Override
|
||||
@Ignore // test requires ability to attach by object & frida version requires pid
|
||||
@Test
|
||||
public void testAttachByObjBogusThrowsException() throws Throwable {
|
||||
super.testAttachByObjBogusThrowsException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -15,8 +15,12 @@
|
||||
*/
|
||||
package agent.frida.model.invm;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaRootLauncherTest;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaRootLauncherTest;
|
||||
import generic.test.category.NightlyCategory;
|
||||
|
||||
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||
public class InVmModelForFridaRootLauncherTest extends AbstractModelForFridaRootLauncherTest {
|
||||
@Override
|
||||
public ModelHost modelHost() throws Throwable {
|
||||
|
@ -15,8 +15,12 @@
|
||||
*/
|
||||
package agent.frida.model.invm;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaScenarioStackTest;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaScenarioStackTest;
|
||||
import generic.test.category.NightlyCategory;
|
||||
|
||||
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||
public class InVmModelForFridaScenarioStackTest extends AbstractModelForFridaScenarioStackTest {
|
||||
@Override
|
||||
public ModelHost modelHost() throws Throwable {
|
||||
|
@ -15,13 +15,17 @@
|
||||
*/
|
||||
package agent.frida.model.invm;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaX64RegistersTest;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import agent.frida.model.AbstractModelForFridaX64RegistersTest;
|
||||
import generic.test.category.NightlyCategory;
|
||||
|
||||
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||
public class InVmModelForFridaX64RegistersTest extends AbstractModelForFridaX64RegistersTest {
|
||||
|
||||
@Override
|
||||
public ModelHost modelHost() throws Throwable {
|
||||
return new InVmFridaModelHost();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -98,6 +98,17 @@
|
||||
the <i>Data Type Manager</i> display is updated to reflect the new name in the tree.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
<H2>Show In Data Type Manager</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Select the <IMG src="images/go-home.png" alt=""> icon in the toolbar to have the editor's
|
||||
data type be highlighted in the Data Type Manager's tree.
|
||||
</P>
|
||||
|
||||
</BLOCKQUOTE
|
||||
|
||||
<h2>Change the Sort Order</h2>
|
||||
<blockquote>
|
||||
<p>As with most tables in Ghidra, you can change the sort order of a column by
|
||||
|
@ -102,7 +102,17 @@
|
||||
data items in the program will have changed due to the apply.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Closing the Editor</H2>
|
||||
|
||||
<H2><A name="Show_In_Data_Type_Manager"></A><A name="Structure_Editor_Show_In_Data_Type_Manager">Show In Data Type Manager</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Select the <IMG src="images/go-home.png" alt=""> icon in the toolbar to have the editor's
|
||||
data type be highlighted in the Data Type Manager's tree.
|
||||
</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Closing the Editor</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Select the Close dockable component icon <IMG src="../../shared/close16.gif" alt=""> in
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 34 KiB |
@ -23,7 +23,7 @@ import ghidra.program.model.data.InvalidDataTypeException;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* ApplyAction is an action for applying editor changes.
|
||||
* ApplyAction is an action for applying editor changes.
|
||||
*/
|
||||
public class ApplyAction extends CompositeEditorTableAction {
|
||||
|
||||
@ -48,11 +48,8 @@ public class ApplyAction extends CompositeEditorTableAction {
|
||||
try {
|
||||
model.apply();
|
||||
}
|
||||
catch (EmptyCompositeException e1) {
|
||||
model.setStatus(e1.getMessage(), true);
|
||||
}
|
||||
catch (InvalidDataTypeException e1) {
|
||||
model.setStatus(e1.getMessage(), true);
|
||||
catch (EmptyCompositeException | InvalidDataTypeException e) {
|
||||
model.setStatus(e.getMessage(), true);
|
||||
}
|
||||
requestTableFocus();
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
|
||||
public static final String EDIT_ACTION_PREFIX = "Editor: ";
|
||||
|
||||
public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group,
|
||||
String[] popupPath, String[] menuPath, ImageIcon icon) {
|
||||
String[] popupPath, String[] menuPath, Icon icon) {
|
||||
super(name, provider.plugin.getName(), KeyBindingType.SHARED);
|
||||
this.provider = provider;
|
||||
model = provider.getModel();
|
||||
|
@ -15,53 +15,55 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import docking.ComponentProvider;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
/**
|
||||
* Interface implemented by data type editors.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface EditorProvider {
|
||||
|
||||
/**
|
||||
* Get the name of this editor.
|
||||
* @return the name of this editor
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Get the pathname of the data type being edited.
|
||||
* @return the pathname of the data type being edited
|
||||
*/
|
||||
public DataTypePath getDtPath();
|
||||
|
||||
/**
|
||||
* Get the component provider for this editor.
|
||||
* @return the component provider for this editor
|
||||
*/
|
||||
public ComponentProvider getComponentProvider();
|
||||
|
||||
/**
|
||||
* Get the datatype manager associated with this editor.
|
||||
* @return the datatype manager associated with this editor
|
||||
*/
|
||||
public DataTypeManager getDataTypeManager();
|
||||
|
||||
/**
|
||||
* Notification that the data type manager domain object (program or data type archive) was restored.
|
||||
* Notification that the data type manager domain object (program or data type archive) was
|
||||
* restored.
|
||||
* @param domainObject the program or data type archive that was restored.
|
||||
*/
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject);
|
||||
|
||||
/**
|
||||
* Return whether this editor is editing the data type with the given
|
||||
* path.
|
||||
* Return whether this editor is editing the data type with the given path.
|
||||
* @param dtPath path of a data type
|
||||
* @return true if the data type for the pathname is being edited
|
||||
*/
|
||||
public boolean isEditing(DataTypePath dtPath);
|
||||
|
||||
/**
|
||||
* Add an editor listener that will be notified when the edit window is
|
||||
* closed.
|
||||
* Add an editor listener that will be notified when the edit window is closed.
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void addEditorListener(EditorListener listener);
|
||||
|
||||
@ -72,6 +74,7 @@ public interface EditorProvider {
|
||||
|
||||
/**
|
||||
* Returns whether changes need to be saved.
|
||||
* @return whether changes need to be saved
|
||||
*/
|
||||
public boolean needsSave();
|
||||
|
||||
|
@ -15,16 +15,13 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.program.model.data.DataTypeComponent;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.*;
|
||||
|
||||
/**
|
||||
* An action to show references to the field in the currently selected editor row
|
||||
@ -56,8 +53,7 @@ public class FindReferencesToField extends CompositeEditorTableAction {
|
||||
|
||||
String fieldName = getFieldName();
|
||||
Composite composite = model.getOriginalComposite();
|
||||
SwingUtilities.invokeLater(
|
||||
() -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName));
|
||||
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName));
|
||||
}
|
||||
|
||||
private String getFieldName() {
|
||||
|
@ -0,0 +1,60 @@
|
||||
/* ###
|
||||
* 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.compositeeditor;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.ToolBarData;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.program.model.data.*;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Shows the editor's data type in the UI using the {@link DataTypeManagerService}.
|
||||
*/
|
||||
public class ShowDataTypeInTreeAction extends CompositeEditorTableAction {
|
||||
|
||||
// This action should go after the row-based actions, which have this group:
|
||||
// 3_COMPONENT_EDITOR_ACTION
|
||||
private static final String TOOLBAR_GROUP = "4_COMPONENT_EDITOR_ACTION";
|
||||
private static final Icon ICON = ResourceManager.loadImage("images/go-home.png");
|
||||
|
||||
public ShowDataTypeInTreeAction(CompositeEditorProvider provider) {
|
||||
super(provider, "Show In Data Type Manager", TOOLBAR_GROUP, null /*popupPath*/,
|
||||
null /*menuPath*/, ICON);
|
||||
|
||||
setToolBarData(
|
||||
new ToolBarData(ResourceManager.loadImage("images/go-home.png"), TOOLBAR_GROUP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
DataTypeManagerService dtmService = tool.getService(DataTypeManagerService.class);
|
||||
DataTypeManager dtm = provider.getDataTypeManager();
|
||||
DataTypePath path = provider.getDtPath();
|
||||
DataType dt = dtm.getDataType(path);
|
||||
dtmService.setDataTypeSelected(dt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustEnablement() {
|
||||
DataTypeManager dtm = provider.getDataTypeManager();
|
||||
DataTypePath path = provider.getDtPath();
|
||||
DataType dt = dtm.getDataType(path);
|
||||
setEnabled(dt != null);
|
||||
}
|
||||
}
|
@ -55,24 +55,26 @@ public class StructureEditorProvider extends CompositeEditorProvider {
|
||||
return new CompositeEditorTableAction[] {
|
||||
new ApplyAction(this),
|
||||
// new ToggleLockAction(this),
|
||||
new InsertUndefinedAction(this),
|
||||
new MoveUpAction(this),
|
||||
new InsertUndefinedAction(this),
|
||||
new MoveUpAction(this),
|
||||
new MoveDownAction(this),
|
||||
new ClearAction(this),
|
||||
new DuplicateAction(this),
|
||||
new ClearAction(this),
|
||||
new DuplicateAction(this),
|
||||
new DuplicateMultipleAction(this),
|
||||
new DeleteAction(this),
|
||||
new PointerAction(this),
|
||||
new DeleteAction(this),
|
||||
new PointerAction(this),
|
||||
new ArrayAction(this),
|
||||
new FindReferencesToField(this),
|
||||
new UnpackageAction(this),
|
||||
new EditComponentAction(this),
|
||||
new EditFieldAction(this),
|
||||
new EditComponentAction(this),
|
||||
new EditFieldAction(this),
|
||||
new HexNumbersAction(this),
|
||||
new CreateInternalStructureAction(this),
|
||||
new ShowComponentPathAction(this),
|
||||
new AddBitFieldAction(this),
|
||||
new EditBitFieldAction(this),
|
||||
new ShowDataTypeInTreeAction(this),
|
||||
|
||||
// new ViewBitFieldAction(this)
|
||||
};
|
||||
//@formatter:on
|
||||
|
@ -51,21 +51,22 @@ public class UnionEditorProvider extends CompositeEditorProvider {
|
||||
@Override
|
||||
protected CompositeEditorTableAction[] createActions() {
|
||||
//@formatter:off
|
||||
return new CompositeEditorTableAction[] {
|
||||
new ApplyAction(this),
|
||||
return new CompositeEditorTableAction[] {
|
||||
new ApplyAction(this),
|
||||
new MoveUpAction(this),
|
||||
new MoveDownAction(this),
|
||||
new DuplicateAction(this),
|
||||
new MoveDownAction(this),
|
||||
new DuplicateAction(this),
|
||||
new DuplicateMultipleAction(this),
|
||||
new DeleteAction(this),
|
||||
new PointerAction(this),
|
||||
new DeleteAction(this),
|
||||
new PointerAction(this),
|
||||
new ArrayAction(this),
|
||||
new ShowComponentPathAction(this),
|
||||
new ShowComponentPathAction(this),
|
||||
new EditComponentAction(this),
|
||||
new EditFieldAction(this),
|
||||
new EditFieldAction(this),
|
||||
new HexNumbersAction(this),
|
||||
new AddBitFieldAction(this),
|
||||
new EditBitFieldAction(this)
|
||||
new EditBitFieldAction(this),
|
||||
new ShowDataTypeInTreeAction(this)
|
||||
};
|
||||
//@formatter:on
|
||||
}
|
||||
|
@ -38,7 +38,9 @@ import generic.util.Path;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.actions.*;
|
||||
import ghidra.app.plugin.core.datamgr.actions.RecentlyOpenedArchiveAction;
|
||||
import ghidra.app.plugin.core.datamgr.actions.UpdateSourceArchiveNamesAction;
|
||||
import ghidra.app.plugin.core.datamgr.actions.associate.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
|
||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||
@ -569,6 +571,10 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||
return dataTypeManagerHandler.openArchive(archiveName);
|
||||
}
|
||||
|
||||
public List<Archive> getAllArchives() {
|
||||
return dataTypeManagerHandler.getAllArchives();
|
||||
}
|
||||
|
||||
public void openProjectDataTypeArchive() {
|
||||
if (openDialog == null) {
|
||||
ActionListener listener = ev -> {
|
||||
@ -611,11 +617,11 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||
|
||||
@Override
|
||||
public void setDataTypeSelected(DataType dataType) {
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
if (provider.isVisible()) {
|
||||
provider.setDataTypeSelected(dataType);
|
||||
}
|
||||
});
|
||||
if (provider.isVisible()) {
|
||||
// this is a service method, ensure it is on the Swing thread, since it interacts with
|
||||
// Swing components
|
||||
Swing.runIfSwingOrRunLater(() -> provider.setDataTypeSelected(dataType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -726,7 +732,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||
return null;
|
||||
}
|
||||
DataTypesActionContext dtContext = (DataTypesActionContext) context;
|
||||
GTreeNode selectedNode = dtContext.getSelectedNode();
|
||||
GTreeNode selectedNode = dtContext.getClickedNode();
|
||||
if (!(selectedNode instanceof ArchiveNode)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -48,9 +48,9 @@ public class DataTypeSynchronizer {
|
||||
private int localTransactionID;
|
||||
|
||||
/**
|
||||
* Creates a DataTypeSynchronizer to be used for synchronizing data types between a program
|
||||
* Creates a DataTypeSynchronizer to be used for synchronizing data types between a program
|
||||
* and an archive.
|
||||
* @param dataTypeManagerHandler the handler that manages all the open data type managers
|
||||
* @param dataTypeManagerHandler the handler that manages all the open data type managers
|
||||
* whether built-in, program, project data type archive or file data type archive.
|
||||
* @param dataTypeManager the program data type manager.
|
||||
* @param source the data type source archive information indicating the associated archive for
|
||||
@ -142,35 +142,38 @@ public class DataTypeSynchronizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a single program data type's changes to the associated source data type in the archive.
|
||||
* @param refDT the program data type
|
||||
* @return true if the commit succeeds.
|
||||
* Commits a single program data type's changes to the associated source data type in the
|
||||
* archive.
|
||||
* @param dtmHandler the handler that manages data types
|
||||
* @param dt the program data type
|
||||
* @return true if the commit succeeds
|
||||
*/
|
||||
public static boolean commit(DataTypeManagerHandler dtmHandler, DataType refDT) {
|
||||
SourceArchive sourceArchive = refDT.getSourceArchive();
|
||||
public static boolean commit(DataTypeManagerHandler dtmHandler, DataType dt) {
|
||||
SourceArchive sourceArchive = dt.getSourceArchive();
|
||||
DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive);
|
||||
if (sourceDTM == null) {
|
||||
return false;
|
||||
}
|
||||
commit(sourceDTM, refDT);
|
||||
commit(sourceDTM, dt);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a single data type in the program to match the associated source data type from the
|
||||
* archive.
|
||||
* @param dataType the program data type
|
||||
* @return true if the update succeeds.
|
||||
* @param dtmHandler the handler that manages data types
|
||||
* @param dt the data type
|
||||
* @return true if the update succeeds
|
||||
*/
|
||||
public static boolean update(DataTypeManagerHandler dtmHandler, DataType refDT) {
|
||||
DataTypeManager dataTypeManager = refDT.getDataTypeManager();
|
||||
SourceArchive sourceArchive = refDT.getSourceArchive();
|
||||
DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive);
|
||||
if (dataTypeManager == null || sourceDTM == null) {
|
||||
public static boolean update(DataTypeManagerHandler dtmHandler, DataType dt) {
|
||||
DataTypeManager dataTypeManager = dt.getDataTypeManager();
|
||||
SourceArchive sourceArchive = dt.getSourceArchive();
|
||||
DataTypeManager sourceDtm = dtmHandler.getDataTypeManager(sourceArchive);
|
||||
if (dataTypeManager == null || sourceDtm == null) {
|
||||
return false;
|
||||
}
|
||||
DataType sourceDT = sourceDTM.getDataType(sourceArchive, refDT.getUniversalID());
|
||||
update(dataTypeManager, sourceDT);
|
||||
DataType sourceDt = sourceDtm.getDataType(sourceArchive, dt.getUniversalID());
|
||||
update(dataTypeManager, sourceDt);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -216,7 +219,7 @@ public class DataTypeSynchronizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* If the indicated data type is associated with a source archive, this will remove the
|
||||
* If the indicated data type is associated with a source archive, this will remove the
|
||||
* association.
|
||||
* @param dataType the data type to be disassociated from a source archive.
|
||||
*/
|
||||
@ -343,7 +346,7 @@ public class DataTypeSynchronizer {
|
||||
String htmlContent = diffs[0].getHTMLContentString();
|
||||
String otherContent = diffs[1].getHTMLContentString();
|
||||
|
||||
// this string allows us to force both tables to be the same width, which is
|
||||
// this string allows us to force both tables to be the same width, which is
|
||||
// aesthetically pleasing
|
||||
String spacerString = createHTMLSpacerString(htmlContent, otherContent);
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
@ -355,8 +358,9 @@ public class DataTypeSynchronizer {
|
||||
|
||||
buffy.append("<TR BORDER=LEFT>");
|
||||
buffy.append("<TD VALIGN=\"TOP\">");
|
||||
buffy.append("<B>").append(HTMLUtilities.escapeHTML(dataTypeManager.getName())).append(
|
||||
"</B><HR NOSHADE>");
|
||||
buffy.append("<B>")
|
||||
.append(HTMLUtilities.escapeHTML(dataTypeManager.getName()))
|
||||
.append("</B><HR NOSHADE>");
|
||||
buffy.append(htmlContent);
|
||||
|
||||
// horizontal spacer below the inner table in order to force a minimum width
|
||||
@ -368,8 +372,9 @@ public class DataTypeSynchronizer {
|
||||
buffy.append("</TD>");
|
||||
|
||||
buffy.append("<TD VALIGN=\"TOP\">");
|
||||
buffy.append("<B>").append(HTMLUtilities.escapeHTML(sourceArchive.getName())).append(
|
||||
"</B><HR NOSHADE>");
|
||||
buffy.append("<B>")
|
||||
.append(HTMLUtilities.escapeHTML(sourceArchive.getName()))
|
||||
.append("</B><HR NOSHADE>");
|
||||
|
||||
buffy.append(otherContent);
|
||||
|
||||
@ -391,12 +396,12 @@ public class DataTypeSynchronizer {
|
||||
return ToolTipUtils.getHTMLRepresentation(sourceDT);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Compares the two HTML strings to find the widest *rendered* text and then creates
|
||||
* an HTML string of spaces that is wide enough to represent that width.
|
||||
*/
|
||||
private static String createHTMLSpacerString(String htmlContent, String otherHTMLContent) {
|
||||
// unfortunately, to get the displayed widths, we have to have rendered content, which
|
||||
// unfortunately, to get the displayed widths, we have to have rendered content, which
|
||||
// is what the JLabels below are doing for us
|
||||
JLabel label1 = new GDHtmlLabel("<HTML>" + htmlContent);
|
||||
JLabel label2 = new GDHtmlLabel("<HTML>" + otherHTMLContent);
|
||||
@ -446,9 +451,9 @@ public class DataTypeSynchronizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the data type and source archive info for an associated source archive if its sync
|
||||
* state is incorrect. It makes sure that a data type that is the same as the associated
|
||||
* archive one is in-sync. It also makes sure that a data type that differs from the archive
|
||||
* Adjusts the data type and source archive info for an associated source archive if its sync
|
||||
* state is incorrect. It makes sure that a data type that is the same as the associated
|
||||
* archive one is in-sync. It also makes sure that a data type that differs from the archive
|
||||
* one can be committed or updated.
|
||||
*/
|
||||
public void reSyncDataTypes() {
|
||||
@ -458,8 +463,8 @@ public class DataTypeSynchronizer {
|
||||
return;
|
||||
}
|
||||
|
||||
int transactionID = dataTypeManager.startTransaction(
|
||||
"re-sync '" + sourceArchive.getName() + "' data types");
|
||||
int transactionID = dataTypeManager
|
||||
.startTransaction("re-sync '" + sourceArchive.getName() + "' data types");
|
||||
try {
|
||||
reSyncOutOfSyncInTimeOnlyDataTypes();
|
||||
fixSyncForDifferingDataTypes();
|
||||
@ -498,7 +503,7 @@ public class DataTypeSynchronizer {
|
||||
/**
|
||||
* This method is to correct a problem where a data type ends up differing from its associated
|
||||
* data type in the archive, but its timestamp information indicates that it is in sync.
|
||||
* It changes the timestamp info on the data type and the info about the source archive so
|
||||
* It changes the timestamp info on the data type and the info about the source archive so
|
||||
* the user will be able to commit/update the data type to correctly put it back in sync.
|
||||
*/
|
||||
private void fixSyncForDifferingDataTypes() {
|
||||
|
@ -20,13 +20,15 @@ import java.util.List;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.ProjectArchive;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||
import ghidra.app.plugin.core.datamgr.tree.ProjectArchiveNode;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.framework.main.datatable.DomainFileContext;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class DataTypesActionContext extends ProgramActionContext implements DomainFileContext {
|
||||
@ -53,7 +55,7 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma
|
||||
return isToolbarAction;
|
||||
}
|
||||
|
||||
public GTreeNode getSelectedNode() {
|
||||
public GTreeNode getClickedNode() {
|
||||
return clickedNode;
|
||||
}
|
||||
|
||||
@ -85,4 +87,49 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<GTreeNode> getSelectedNodes() {
|
||||
Object contextObject = getContextObject();
|
||||
GTree gTree = (GTree) contextObject;
|
||||
return gTree.getSelectedNodes();
|
||||
}
|
||||
|
||||
public List<DataTypeNode> getDisassociatableNodes() {
|
||||
|
||||
Object contextObject = getContextObject();
|
||||
GTree gTree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
return getDisassociatableNodes(selectionPaths);
|
||||
}
|
||||
|
||||
private List<DataTypeNode> getDisassociatableNodes(TreePath[] paths) {
|
||||
|
||||
List<DataTypeNode> nodes = new ArrayList<>();
|
||||
for (TreePath treePath : paths) {
|
||||
DataTypeNode node = getDisassociatableNode(treePath);
|
||||
if (node != null) {
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private DataTypeNode getDisassociatableNode(TreePath path) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
SourceArchive sourceArchive = dataType.getSourceArchive();
|
||||
if (sourceArchive == null || dataTypeManager == null ||
|
||||
sourceArchive.equals(BuiltInSourceArchive.INSTANCE) ||
|
||||
sourceArchive.getSourceArchiveID().equals(dataTypeManager.getUniversalID())) {
|
||||
|
||||
return null;
|
||||
}
|
||||
return dataTypeNode;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import docking.widgets.textpane.GHtmlTextPane;
|
||||
import docking.widgets.tree.*;
|
||||
import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin;
|
||||
import ghidra.app.plugin.core.datamgr.actions.*;
|
||||
import ghidra.app.plugin.core.datamgr.actions.associate.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||
@ -220,6 +221,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
// key binding only
|
||||
addLocalAction(new ClearCutAction(plugin)); // Common
|
||||
|
||||
addLocalAction(new AssociateDataTypeAction(plugin));
|
||||
addLocalAction(new CommitSingleDataTypeAction(plugin));
|
||||
addLocalAction(new UpdateSingleDataTypeAction(plugin));
|
||||
addLocalAction(new RevertDataTypeAction(plugin));
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -0,0 +1,322 @@
|
||||
/* ###
|
||||
* 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.datamgr.actions.associate;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask.ActionType;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
|
||||
/**
|
||||
* Allows the user to associate the selected action with a source archive. An associate data type
|
||||
* allows users to push changes to the source archive and to pull updates from the source archive.
|
||||
*/
|
||||
public class AssociateDataTypeAction extends DockingAction {
|
||||
|
||||
private DataTypeManagerPlugin plugin;
|
||||
|
||||
public AssociateDataTypeAction(DataTypeManagerPlugin plugin) {
|
||||
super("Associate With Archive", plugin.getName());
|
||||
this.plugin = plugin;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Associate With Archive" }, null, "Sync"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasOnlyDtNodes(((DataTypesActionContext) context).getSelectedNodes());
|
||||
}
|
||||
|
||||
private boolean hasOnlyDtNodes(List<GTreeNode> nodes) {
|
||||
if (nodes.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (GTreeNode node : nodes) {
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isAlreadyAssociated(DataTypesActionContext dtContext) {
|
||||
|
||||
List<DataTypeNode> nodes = dtContext.getDisassociatableNodes();
|
||||
return !nodes.isEmpty();
|
||||
}
|
||||
|
||||
private boolean hasSingleModifiableSourceArchive(List<GTreeNode> nodes) {
|
||||
|
||||
Archive sourceArchive = null;
|
||||
for (GTreeNode node : nodes) {
|
||||
Archive archive = findArchive(node);
|
||||
if (sourceArchive == null) {
|
||||
sourceArchive = archive;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sourceArchive != archive) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceArchive != null && sourceArchive.isModifiable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Archive findArchive(GTreeNode node) {
|
||||
while (node != null) {
|
||||
if (node instanceof ArchiveNode) {
|
||||
return ((ArchiveNode) node).getArchive();
|
||||
}
|
||||
node = node.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Archive> getDestinationArchives() {
|
||||
|
||||
List<Archive> archives = plugin.getAllArchives();
|
||||
List<Archive> sourceArchives = archives.stream()
|
||||
.filter(a -> !(a instanceof ProgramArchive))
|
||||
.filter(a -> !(a instanceof BuiltInArchive))
|
||||
.sorted((a1, a2) -> a1.getName().compareToIgnoreCase(a2.getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return sourceArchives;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
List<GTreeNode> nodes = ((DataTypesActionContext) context).getSelectedNodes();
|
||||
|
||||
if (!hasSingleModifiableSourceArchive(nodes)) {
|
||||
Msg.showInfo(this, getProviderComponent(), "Multiple Source Archives",
|
||||
"The currently selected nodes are from multiple archives.\n" +
|
||||
"Please select only nodes from a single archvie.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAlreadyAssociated((DataTypesActionContext) context)) {
|
||||
Msg.showInfo(this, getProviderComponent(), "Already Associated",
|
||||
"One or more of the currently selected nodes are already associated\n" +
|
||||
"with a source archive.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Archive> archives = getDestinationArchives();
|
||||
if (archives.isEmpty()) {
|
||||
Msg.showInfo(this, getProviderComponent(), "No Source Archives Open",
|
||||
"No source archives open. Please open the desired source archive.");
|
||||
return;
|
||||
}
|
||||
|
||||
ChooseArchiveDialog dialog = new ChooseArchiveDialog(archives);
|
||||
dialog.show();
|
||||
if (dialog.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Archive destinationArchive = dialog.getArchive();
|
||||
Category destinationCategory = dialog.getCategory();
|
||||
|
||||
DataTypeTreeCopyMoveTask task =
|
||||
new DataTypeTreeCopyMoveTask(destinationArchive, destinationCategory, nodes,
|
||||
ActionType.COPY, plugin.getProvider().getGTree(), plugin.getConflictHandler());
|
||||
task.setPromptToAssociateTypes(false); // do not prompt the user; they have already decided
|
||||
TaskLauncher.launch(task);
|
||||
}
|
||||
|
||||
private JComponent getProviderComponent() {
|
||||
return plugin.getProvider().getComponent();
|
||||
}
|
||||
|
||||
private class ChooseArchiveDialog extends DialogComponentProvider {
|
||||
|
||||
private Category category;
|
||||
private Archive archive;
|
||||
|
||||
// default to true to handle the case the user presses Escape or presses the x button
|
||||
private boolean isCancelled = true;
|
||||
|
||||
private GhidraComboBox<Archive> archivesBox = new GhidraComboBox<>();
|
||||
private JTextField categoryField = new JTextField(20);
|
||||
|
||||
ChooseArchiveDialog(List<Archive> archives) {
|
||||
super("Choose New Source Archive", true);
|
||||
|
||||
addWorkPanel(buildWorkPanel());
|
||||
|
||||
archivesBox.addToModel(archives);
|
||||
categoryField.setText("/");
|
||||
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
}
|
||||
|
||||
private JComponent buildWorkPanel() {
|
||||
|
||||
archivesBox.setRenderer(new DefaultListCellRenderer() {
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<?> list, Object value,
|
||||
int index, boolean isSelected, boolean cellHasFocus) {
|
||||
|
||||
JLabel renderer = (JLabel) super.getListCellRendererComponent(list, value,
|
||||
index, isSelected, cellHasFocus);
|
||||
Archive a = (Archive) value;
|
||||
renderer.setText(a.getName());
|
||||
return renderer;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
|
||||
JPanel archivePanel = new JPanel(new PairLayout());
|
||||
archivePanel.add(new GLabel("New Source Archive: "));
|
||||
archivePanel.add(archivesBox);
|
||||
|
||||
JPanel categoryPanel = new JPanel(new PairLayout());
|
||||
categoryPanel.add(new GLabel("Destination Category: "));
|
||||
categoryPanel.add(categoryField);
|
||||
|
||||
panel.add(archivePanel, BorderLayout.NORTH);
|
||||
panel.add(categoryPanel, BorderLayout.SOUTH);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
|
||||
clearStatusText();
|
||||
|
||||
archive = (Archive) archivesBox.getSelectedItem();
|
||||
if (archive == null) {
|
||||
setStatusText("Please choose an archive");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!archive.isModifiable()) {
|
||||
setStatusText(
|
||||
"Archive is not modifiable. You must first open this archive for edit.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!updateCategory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
isCancelled = false;
|
||||
close();
|
||||
}
|
||||
|
||||
private boolean updateCategory() {
|
||||
|
||||
String categoryText = categoryField.getText();
|
||||
if (StringUtils.isBlank(categoryText)) {
|
||||
setStatusText("Category must be specified. Use '/' for the root.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DataTypeManager dtm = archive.getDataTypeManager();
|
||||
CategoryPath categoryPath = new CategoryPath(categoryText);
|
||||
category = dtm.getCategory(categoryPath);
|
||||
if (category != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int choice = OptionDialog.showYesNoDialog(null, "Create Category?",
|
||||
"Category '" + categoryText + "' does not exist. Create it now?");
|
||||
if (choice != OptionDialog.YES_OPTION) {
|
||||
setStatusText("Category does not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean noErrors = false;
|
||||
int tx = dtm.startTransaction("Create Category");
|
||||
try {
|
||||
category = dtm.createCategory(categoryPath);
|
||||
noErrors = true;
|
||||
}
|
||||
finally {
|
||||
dtm.endTransaction(tx, noErrors);
|
||||
}
|
||||
|
||||
if (category == null) {
|
||||
setStatusText("Unable to create category");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
super.cancelCallback();
|
||||
}
|
||||
|
||||
boolean isCancelled() {
|
||||
return isCancelled;
|
||||
}
|
||||
|
||||
void show() {
|
||||
JComponent parent = getProviderComponent();
|
||||
DockingWindowManager.showDialog(parent, this);
|
||||
}
|
||||
|
||||
Archive getArchive() {
|
||||
return archive;
|
||||
}
|
||||
|
||||
Category getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -13,8 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||
@ -22,17 +25,13 @@ import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.SourceArchive;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.MenuData;
|
||||
|
||||
public class CommitAction extends SyncAction {
|
||||
|
||||
public static final String MENU_NAME = "Commit Datatypes To";
|
||||
public static final String MENU_NAME = "Commit Data Types To";
|
||||
|
||||
public CommitAction(DataTypeManagerPlugin plugin,
|
||||
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm,
|
||||
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) {
|
||||
public CommitAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
|
||||
DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
|
||||
boolean isEnabled) {
|
||||
|
||||
super("Commit Changes To Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
||||
sourceArchive, isEnabled);
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.tree.TreePath;
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -35,7 +35,7 @@ import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
public class DisassociateAction extends DockingAction {
|
||||
public static final String MENU_NAME = "Disassociate Datatypes From";
|
||||
public static final String MENU_NAME = "Disassociate Data Types From";
|
||||
|
||||
private final SourceArchive sourceArchive;
|
||||
private final DataTypeManager dtm;
|
||||
@ -96,12 +96,12 @@ public class DisassociateAction extends DockingAction {
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
MonitoredRunnable r =
|
||||
MonitoredRunnable r =
|
||||
monitor -> doDisassociate(synchronizer, typesToDisassociate, allAssociatedTypes, monitor);
|
||||
new TaskBuilder("Disassociate From Archive", r)
|
||||
.setStatusTextAlignment(SwingConstants.LEADING)
|
||||
.launchModal()
|
||||
;
|
||||
;
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@ -110,10 +110,10 @@ public class DisassociateAction extends DockingAction {
|
||||
TaskMonitor monitor) {
|
||||
|
||||
//
|
||||
// Note: we collapse the node before performing this work because there is a
|
||||
// Note: we collapse the node before performing this work because there is a
|
||||
// potential for a large number of events to be generated. Further, if the
|
||||
// given archive node has many children (like 10s of thousands), then the
|
||||
// copious events generated herein could lock the UI. By closing the node,
|
||||
// copious events generated herein could lock the UI. By closing the node,
|
||||
// the tree is not invalidating/validating its cache as a result of these
|
||||
// events.
|
||||
//
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
@ -28,7 +28,8 @@ import docking.action.MenuData;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.tree.*;
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||
@ -56,10 +57,8 @@ public class DisassociateDataTypeAction extends DockingAction {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gTree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
List<DataTypeNode> nodes = getDisassociatableNodes(selectionPaths);
|
||||
DataTypesActionContext dtContext = (DataTypesActionContext) context;
|
||||
List<DataTypeNode> nodes = dtContext.getDisassociatableNodes();
|
||||
return !nodes.isEmpty();
|
||||
}
|
||||
|
||||
@ -127,11 +126,11 @@ public class DisassociateDataTypeAction extends DockingAction {
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
MonitoredRunnable r =
|
||||
MonitoredRunnable r =
|
||||
monitor -> doDisassociate(nodes, monitor);
|
||||
new TaskBuilder("Disassociate From Archive", r)
|
||||
.setStatusTextAlignment(SwingConstants.LEADING)
|
||||
.launchModal();
|
||||
.launchModal();
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@ -159,10 +158,10 @@ public class DisassociateDataTypeAction extends DockingAction {
|
||||
nodes.stream().map(node -> node.getDataType()).collect(Collectors.toList());
|
||||
|
||||
//
|
||||
// Note: we collapse the node before performing this work because there is a
|
||||
// Note: we collapse the node before performing this work because there is a
|
||||
// potential for a large number of events to be generated. Further, if the
|
||||
// given archive node has many children (like 10s of thousands), then the
|
||||
// copious events generated herein could lock the UI. By closing the node,
|
||||
// copious events generated herein could lock the UI. By closing the node,
|
||||
// the tree is not invalidating/validating its cache as a result of these
|
||||
// events.
|
||||
//
|
||||
@ -189,7 +188,7 @@ public class DisassociateDataTypeAction extends DockingAction {
|
||||
monitor.initialize(dataTypes.size());
|
||||
|
||||
//@formatter:off
|
||||
Map<DataTypeManager, List<DataType>> managersToTypes =
|
||||
Map<DataTypeManager, List<DataType>> managersToTypes =
|
||||
dataTypes.stream()
|
||||
.collect(
|
||||
Collectors.groupingBy(dt -> dt.getDataTypeManager()))
|
||||
@ -209,7 +208,7 @@ public class DisassociateDataTypeAction extends DockingAction {
|
||||
// we must process these by their source
|
||||
|
||||
//@formatter:off
|
||||
Map<SourceArchive, List<DataType>> sourceToTypes =
|
||||
Map<SourceArchive, List<DataType>> sourceToTypes =
|
||||
dataTypes.stream()
|
||||
.collect(
|
||||
Collectors.groupingBy(dt -> dt.getSourceArchive()))
|
@ -13,8 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
@ -23,19 +26,15 @@ import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.SourceArchive;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.MenuData;
|
||||
|
||||
public class RevertAction extends SyncAction {
|
||||
|
||||
public static final String MENU_NAME = "Revert Datatypes From";
|
||||
public static final String MENU_NAME = "Revert Data Types From";
|
||||
|
||||
public RevertAction(DataTypeManagerPlugin plugin,
|
||||
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm,
|
||||
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) {
|
||||
public RevertAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
|
||||
DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
|
||||
boolean isEnabled) {
|
||||
|
||||
super("Revert Datatype Changes", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
||||
super("Revert Data Type Changes", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
||||
sourceArchive, isEnabled);
|
||||
setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() }));
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), getHelpTopic()));
|
@ -13,14 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
@ -29,6 +22,12 @@ import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class RevertDataTypeAction extends DockingAction {
|
||||
|
||||
@ -37,7 +36,7 @@ public class RevertDataTypeAction extends DockingAction {
|
||||
public RevertDataTypeAction(DataTypeManagerPlugin plugin) {
|
||||
super("Revert Data Type", plugin.getName());
|
||||
this.plugin = plugin;
|
||||
setPopupMenuData(new MenuData(new String[] { "Revert" }, "Sync"));
|
||||
setPopupMenuData(new MenuData(new String[] { "Revert Changes" }, "Sync"));
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@ -68,8 +67,8 @@ public class RevertDataTypeAction extends DockingAction {
|
||||
case UNKNOWN:
|
||||
return false;
|
||||
case COMMIT:
|
||||
return true;
|
||||
case CONFLICT:
|
||||
return true;
|
||||
case IN_SYNC:
|
||||
case ORPHAN:
|
||||
case UPDATE:
|
||||
@ -88,29 +87,26 @@ public class RevertDataTypeAction extends DockingAction {
|
||||
}
|
||||
|
||||
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
|
||||
if (node instanceof DataTypeNode) {
|
||||
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
DataTypeManager dtm = dataType.getDataTypeManager();
|
||||
DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler();
|
||||
SourceArchive sourceArchive = dataType.getSourceArchive();
|
||||
if (!dtm.isUpdatable()) {
|
||||
DataTypeUtils.showUnmodifiableArchiveErrorMessage(gTree, "Revert Failed", dtm);
|
||||
return;
|
||||
}
|
||||
DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive);
|
||||
if (sourceDTM == null) {
|
||||
Msg.showInfo(getClass(), gTree, "Revert Failed", "Source Archive not open: " +
|
||||
sourceArchive.getName());
|
||||
return;
|
||||
}
|
||||
plugin.revert(dataType);
|
||||
|
||||
// Source archive data type manager was already checked for null above.
|
||||
DataTypeSynchronizer synchronizer =
|
||||
new DataTypeSynchronizer(handler, dtm, sourceArchive);
|
||||
synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes();
|
||||
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
DataTypeManager dtm = dataType.getDataTypeManager();
|
||||
DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler();
|
||||
SourceArchive sourceArchive = dataType.getSourceArchive();
|
||||
if (!dtm.isUpdatable()) {
|
||||
DataTypeUtils.showUnmodifiableArchiveErrorMessage(gTree, "Revert Failed", dtm);
|
||||
return;
|
||||
}
|
||||
DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive);
|
||||
if (sourceDTM == null) {
|
||||
Msg.showInfo(getClass(), gTree, "Revert Failed",
|
||||
"Source Archive not open: " + sourceArchive.getName());
|
||||
return;
|
||||
}
|
||||
plugin.revert(dataType);
|
||||
|
||||
// Source archive data type manager was already checked for null above.
|
||||
DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
|
||||
synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes();
|
||||
}
|
||||
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -83,7 +83,6 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
|
||||
|
||||
if (!dtm.isUpdatable()) {
|
||||
showRequiresArchiveOpenMessage(dtm.getName());
|
||||
@ -103,6 +102,8 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
|
||||
|
||||
//@formatter:off
|
||||
TaskBuilder.withTask(new SyncTask(synchronizer))
|
||||
.setStatusTextAlignment(SwingConstants.LEADING)
|
||||
@ -114,10 +115,10 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
private void doSync(DataTypeSynchronizer synchronizer, TaskMonitor monitor) {
|
||||
|
||||
//
|
||||
// Note: we collapse the node before performing this work because there is a
|
||||
// Note: we collapse the node before performing this work because there is a
|
||||
// potential for a large number of events to be generated. Further, if the
|
||||
// given archive node has many children (like 10s of thousands), then the
|
||||
// copious events generated herein could lock the UI. By closing the node,
|
||||
// copious events generated herein could lock the UI. By closing the node,
|
||||
// the tree is not invalidating/validating its cache as a result of these
|
||||
// events.
|
||||
//
|
||||
@ -248,7 +249,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
Set<DataTypeSyncInfo> outOfSyncInfos) {
|
||||
String status = getStatusMessage(outOfSyncInfos);
|
||||
Msg.showInfo(getClass(), plugin.getTool().getToolFrame(), "No Data Type Changes",
|
||||
"No datatypes found to " + getOperationName() + " for archive \"" + archiveName +
|
||||
"No data types found to " + getOperationName() + " for archive \"" + archiveName +
|
||||
"\".\n\n" + status);
|
||||
}
|
||||
|
||||
@ -279,7 +280,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
case UNKNOWN:
|
||||
}
|
||||
}
|
||||
StringBuffer buf = new StringBuffer();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (updateCount > 0) {
|
||||
buf.append("\nNumber of UPDATES remaining: " + updateCount);
|
||||
}
|
||||
@ -298,7 +299,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
|
||||
private void showNoDataTypesToSyncMessage() {
|
||||
Msg.showInfo(getClass(), plugin.getTool().getToolFrame(), "No Data Type Changes",
|
||||
"No out of sync datatypes found. Updating sync time.");
|
||||
"No out of sync data types found. Updating sync time.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -340,7 +341,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
private void autoUpdateDataTypesThatHaveNoRealChanges(DataTypeSynchronizer synchronizer,
|
||||
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
|
||||
|
||||
int transactionID = dtm.startTransaction("auto sync datatypes");
|
||||
int transactionID = dtm.startTransaction("Auto-sync data types");
|
||||
try {
|
||||
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
|
||||
dataTypeSyncInfo.syncTimes();
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
@ -13,8 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||
@ -22,18 +25,14 @@ import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.SourceArchive;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.MenuData;
|
||||
|
||||
public class UpdateAction extends SyncAction {
|
||||
public static final String MENU_NAME = "Update Datatypes From";
|
||||
public static final String MENU_NAME = "Update Data Types From";
|
||||
|
||||
public UpdateAction(DataTypeManagerPlugin plugin,
|
||||
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm,
|
||||
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) {
|
||||
public UpdateAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
|
||||
DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
|
||||
boolean isEnabled) {
|
||||
|
||||
super("Update Datatypes From Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
||||
super("Update Data Types From Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
||||
sourceArchive, isEnabled);
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() }));
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.tree.TreePath;
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -34,6 +34,7 @@ import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.compositeeditor.EditorListener;
|
||||
import ghidra.app.plugin.core.compositeeditor.EditorProvider;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
@ -81,7 +82,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
private long originalEnumID = -1;
|
||||
|
||||
/**
|
||||
* Construct a new enum editor provider.
|
||||
* Construct a new enum editor provider.
|
||||
* @param plugin owner of this provider
|
||||
* @param enumDT enum data type
|
||||
*/
|
||||
@ -251,7 +252,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
private void updateTitle(DataType dataType) {
|
||||
setTitle(getName() + " - " + getProviderSubTitle(dataType));
|
||||
@ -285,19 +286,28 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
deleteAction =
|
||||
new EnumPluginAction("Delete Enum Value", e -> editorPanel.deleteSelectedEntries());
|
||||
deleteAction.setEnabled(false);
|
||||
deleteAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Delete" }, DELETE_ICON, editGroup));
|
||||
deleteAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Delete" }, DELETE_ICON, editGroup));
|
||||
deleteAction.setToolBarData(new ToolBarData(DELETE_ICON, editGroup));
|
||||
deleteAction.setDescription("Delete the selected enum entries");
|
||||
|
||||
applyAction = new EnumPluginAction("Apply Enum Changes", e -> applyChanges());
|
||||
applyAction.setEnabled(false);
|
||||
applyAction.setToolBarData(new ToolBarData(APPLY_ICON, "ApplyChanges"));
|
||||
String firstGroup = "ApplyChanges";
|
||||
applyAction.setToolBarData(new ToolBarData(APPLY_ICON, firstGroup));
|
||||
applyAction.setDescription("Apply changes to Enum");
|
||||
|
||||
EnumPluginAction showEnumAction =
|
||||
new EnumPluginAction("Show In Data Type Manager", e -> showDataEnumInTree());
|
||||
showEnumAction.setEnabled(true);
|
||||
String thirdGroup = "FThirdGroup";
|
||||
showEnumAction.setToolBarData(
|
||||
new ToolBarData(ResourceManager.loadImage("images/go-home.png"), thirdGroup));
|
||||
|
||||
tool.addLocalAction(this, applyAction);
|
||||
tool.addLocalAction(this, addAction);
|
||||
tool.addLocalAction(this, deleteAction);
|
||||
tool.addLocalAction(this, showEnumAction);
|
||||
}
|
||||
|
||||
private boolean applyChanges() {
|
||||
@ -338,6 +348,11 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showDataEnumInTree() {
|
||||
DataTypeManagerService dtmService = tool.getService(DataTypeManagerService.class);
|
||||
dtmService.setDataTypeSelected(originalEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the new changes to the enum will affect equates based off of it.
|
||||
* @param editedEnum the enum to check for conflicts with
|
||||
@ -467,9 +482,9 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
/**
|
||||
* Prompts the user if the editor has unsaved changes. Saves the changes if
|
||||
* the user indicates to do so.
|
||||
* @return CANCEL (0) if the user canceled;
|
||||
* SAVE (1) if the user saved changes;
|
||||
* NO_SAVE (2) if the user did not save changes or no save was required;
|
||||
* @return CANCEL (0) if the user canceled;
|
||||
* SAVE (1) if the user saved changes;
|
||||
* NO_SAVE (2) if the user did not save changes or no save was required;
|
||||
* ERROR (3) if there was an error when the changes were applied.
|
||||
*/
|
||||
private int saveChangesForCloseEvent(boolean allowCancel) {
|
||||
@ -502,7 +517,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
private class MyDataTypeManagerChangeListener extends DataTypeManagerChangeListenerAdapter {
|
||||
|
||||
@ -547,7 +562,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
|
||||
@Override
|
||||
public void categoryRemoved(DataTypeManager dtm, CategoryPath path) {
|
||||
// should never get this callback, as we should first have gotten a
|
||||
// should never get this callback, as we should first have gotten a
|
||||
// dataTypeRemoved(), which will dispose this editor
|
||||
}
|
||||
|
||||
@ -665,11 +680,11 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
|
||||
DataType dataType = dtm.getDataType(otherPath);
|
||||
if (dataType == null) {
|
||||
//
|
||||
// Unusual Code Alert!:
|
||||
//
|
||||
// Unusual Code Alert!:
|
||||
// Must have been deleted and we have not yet processed the event...return true
|
||||
// here to signal that the types are the same so that clients will continue the
|
||||
// updating process. The types may not really be the same, but the fallout is
|
||||
// updating process. The types may not really be the same, but the fallout is
|
||||
// only that there will be more updating than is necessary.
|
||||
//
|
||||
return true;
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -27,12 +27,13 @@ import ghidra.app.plugin.core.datamgr.archive.ProgramArchive;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Task for handling drop operations.
|
||||
* Task for copying and moving data type nodes within the Data Types tree.
|
||||
*/
|
||||
public class DataTypeTreeCopyMoveTask extends Task {
|
||||
|
||||
@ -48,11 +49,11 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
}
|
||||
|
||||
private DataTypeArchiveGTree gTree;
|
||||
private CategoryNode destinationNode;
|
||||
private List<GTreeNode> droppedNodes;
|
||||
private Category destinationCategory;
|
||||
private List<GTreeNode> copyMoveNodes;
|
||||
private Archive sourceArchive;
|
||||
private Archive destinationArchive;
|
||||
|
||||
private boolean promptToAssociateTypes = true;
|
||||
private ActionType actionType;
|
||||
private DataTypeConflictHandler conflictHandler;
|
||||
private List<String> errors = new ArrayList<>();
|
||||
@ -65,19 +66,26 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List<GTreeNode> droppedNodeList,
|
||||
ActionType actionType, DataTypeArchiveGTree gTree,
|
||||
DataTypeConflictHandler conflictHandler) {
|
||||
this(findArchive(destinationNode), destinationNode.getCategory(), droppedNodeList,
|
||||
actionType, gTree, conflictHandler);
|
||||
}
|
||||
|
||||
public DataTypeTreeCopyMoveTask(Archive destinationArchive, Category destinationCategory,
|
||||
List<GTreeNode> droppedNodeList, ActionType actionType, DataTypeArchiveGTree gTree,
|
||||
DataTypeConflictHandler conflictHandler) {
|
||||
super("Drag/Drop", true, true, true);
|
||||
this.destinationNode = destinationNode;
|
||||
this.droppedNodes = droppedNodeList;
|
||||
this.destinationCategory = destinationCategory;
|
||||
this.copyMoveNodes = droppedNodeList;
|
||||
this.actionType = actionType;
|
||||
this.gTree = gTree;
|
||||
this.conflictHandler = conflictHandler;
|
||||
this.destinationArchive = findArchive(destinationNode);
|
||||
this.destinationArchive = destinationArchive;
|
||||
|
||||
GTreeNode firstNode = droppedNodes.get(0);
|
||||
GTreeNode firstNode = copyMoveNodes.get(0);
|
||||
this.sourceArchive = findArchive(firstNode);
|
||||
}
|
||||
|
||||
private Archive findArchive(GTreeNode node) {
|
||||
private static Archive findArchive(GTreeNode node) {
|
||||
while (node != null) {
|
||||
if (node instanceof ArchiveNode) {
|
||||
return ((ArchiveNode) node).getArchive();
|
||||
@ -87,10 +95,22 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any types being newly copied/moved to a suitable archive are eligible for 'association',
|
||||
* which means changes between the two archives will be tracked. True, the default, signals to
|
||||
* prompt before associating types; false signals not to prompt the user, but to always
|
||||
* associate types.
|
||||
*
|
||||
* @param prompt true to prompt; false to not prompt
|
||||
*/
|
||||
public void setPromptToAssociateTypes(boolean prompt) {
|
||||
this.promptToAssociateTypes = prompt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
int nodeCount = droppedNodes.size();
|
||||
int nodeCount = copyMoveNodes.size();
|
||||
filterRedundantNodes();
|
||||
|
||||
if (checkForDifferentSourceArchives()) {
|
||||
@ -143,10 +163,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
|
||||
private boolean checkForDifferentSourceArchives() {
|
||||
|
||||
for (GTreeNode node : droppedNodes) {
|
||||
for (GTreeNode node : copyMoveNodes) {
|
||||
if (sourceArchive != findArchive(node)) {
|
||||
Msg.showError(this, gTree, "Copy Failed",
|
||||
"All dragged data types must be from the same archive!");
|
||||
"All data types must be from the same archive!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -158,7 +178,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
DataTypeManager dtm = destinationArchive.getDataTypeManager();
|
||||
int txId = dtm.startTransaction("Copy/Move Category/DataType");
|
||||
try {
|
||||
dragNodesToCategory(monitor);
|
||||
copyOrMoveNodesToCategory(monitor);
|
||||
}
|
||||
finally {
|
||||
dtm.endTransaction(txId, true);
|
||||
@ -187,13 +207,13 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
return;
|
||||
}
|
||||
|
||||
monitor.initialize(droppedNodes.size());
|
||||
monitor.initialize(copyMoveNodes.size());
|
||||
|
||||
SourceArchive destination = destinationArchive.getDataTypeManager().getLocalSourceArchive();
|
||||
DataTypeManager dtm = sourceArchive.getDataTypeManager();
|
||||
int txId = dtm.startTransaction("Associate DataTypes");
|
||||
int txId = dtm.startTransaction("Associate Data Types");
|
||||
try {
|
||||
for (GTreeNode node : droppedNodes) {
|
||||
for (GTreeNode node : copyMoveNodes) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
if (node instanceof DataTypeNode) {
|
||||
@ -215,6 +235,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
|
||||
private boolean promptToAssociateTypes(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
if (!promptToAssociateTypes) {
|
||||
return true; // do not prompt; always associate
|
||||
}
|
||||
|
||||
if (!containsUnassociatedTypes(monitor)) {
|
||||
return false; // nothing to associate
|
||||
}
|
||||
@ -230,8 +254,8 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
private boolean containsUnassociatedTypes(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
monitor.setMessage("Checking for types to associate");
|
||||
monitor.initialize(droppedNodes.size());
|
||||
for (GTreeNode node : droppedNodes) {
|
||||
monitor.initialize(copyMoveNodes.size());
|
||||
for (GTreeNode node : copyMoveNodes) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
if (node instanceof DataTypeNode) {
|
||||
@ -296,13 +320,13 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
private void dragNodesToCategory(TaskMonitor monitor) {
|
||||
private void copyOrMoveNodesToCategory(TaskMonitor monitor) {
|
||||
|
||||
monitor.setMessage("Drag/Drop Categories/Data Types");
|
||||
monitor.initialize(droppedNodes.size());
|
||||
monitor.initialize(copyMoveNodes.size());
|
||||
|
||||
Category toCategory = getCategory(destinationNode);
|
||||
for (GTreeNode node : droppedNodes) {
|
||||
Category toCategory = destinationCategory;
|
||||
for (GTreeNode node : copyMoveNodes) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
@ -360,10 +384,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
private void renameAsCopy(Category destinationCategory, DataType dataType) {
|
||||
private void renameAsCopy(Category toCategory, DataType dataType) {
|
||||
String dtName = dataType.getName();
|
||||
String baseName = getBaseName(dtName);
|
||||
String copyName = getNextCopyName(destinationCategory, baseName);
|
||||
String copyName = getNextCopyName(toCategory, baseName);
|
||||
try {
|
||||
dataType.setName(copyName);
|
||||
}
|
||||
@ -386,12 +410,12 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
return baseName;
|
||||
}
|
||||
|
||||
String getNextCopyName(Category destinationCategory, String baseName) {
|
||||
String getNextCopyName(Category toCategory, String baseName) {
|
||||
|
||||
String format = "Copy_%d_of_" + baseName;
|
||||
for (int i = 1; i < 100; i++) {
|
||||
String copyName = String.format(format, i);
|
||||
if (destinationCategory.getDataType(copyName) == null) {
|
||||
if (toCategory.getDataType(copyName) == null) {
|
||||
return copyName;
|
||||
}
|
||||
}
|
||||
@ -400,14 +424,14 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
return String.format(format, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void moveNode(Category destinationCategory, GTreeNode node, TaskMonitor monitor) {
|
||||
private void moveNode(Category toCategory, GTreeNode node, TaskMonitor monitor) {
|
||||
if (node instanceof DataTypeNode) {
|
||||
DataType dataType = ((DataTypeNode) node).getDataType();
|
||||
moveDataType(destinationCategory, dataType);
|
||||
moveDataType(toCategory, dataType);
|
||||
}
|
||||
else if (node instanceof CategoryNode) {
|
||||
Category category = ((CategoryNode) node).getCategory();
|
||||
moveCategory(destinationCategory, category, monitor);
|
||||
moveCategory(toCategory, category, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
@ -457,17 +481,6 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
toCategory.copyCategory(category, conflictHandler, monitor);
|
||||
}
|
||||
|
||||
private Category getCategory(GTreeNode node) {
|
||||
if (node instanceof ArchiveNode) {
|
||||
return ((ArchiveNode) node).getArchive().getDataTypeManager().getRootCategory();
|
||||
}
|
||||
if (node instanceof CategoryNode) {
|
||||
return ((CategoryNode) node).getCategory();
|
||||
}
|
||||
throw new AssertException(
|
||||
"Expected node to be either an ArchiveNode or CategoryNode but was " + node.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given data type's source archive is the same as it's current data
|
||||
* type manager. This is false if copying a new type from the program to an
|
||||
@ -483,14 +496,14 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
}
|
||||
|
||||
private int askToAssociateDataTypes() {
|
||||
return OptionDialog.showYesNoCancelDialog(gTree, "Associate DataTypes?",
|
||||
"Do you want to associate local datatypes with the target archive?");
|
||||
return OptionDialog.showYesNoCancelDialog(gTree, "Associate Data Types?",
|
||||
"Do you want to associate local data types with the target archive?");
|
||||
}
|
||||
|
||||
// filters out nodes with categories in their path
|
||||
private void filterRedundantNodes() {
|
||||
|
||||
Set<GTreeNode> nodeSet = new HashSet<>(droppedNodes);
|
||||
Set<GTreeNode> nodeSet = new HashSet<>(copyMoveNodes);
|
||||
List<GTreeNode> filteredList = new ArrayList<>();
|
||||
|
||||
for (GTreeNode node : nodeSet) {
|
||||
@ -499,7 +512,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
droppedNodes = filteredList;
|
||||
copyMoveNodes = filteredList;
|
||||
}
|
||||
|
||||
private boolean containsAncestor(Set<GTreeNode> nodeSet, GTreeNode node) {
|
||||
|
@ -22,7 +22,6 @@ import java.util.List;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.app.services.DataTypeQueryService;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
@ -150,6 +149,7 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Returns the root folder icon.
|
||||
* @param expanded true to use the expanded icon; false to use the collapsed icon.
|
||||
* @return the root folder icon.
|
||||
*/
|
||||
public static Icon getRootIcon(boolean expanded) {
|
||||
@ -159,7 +159,7 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Returns the open folder icon.
|
||||
*
|
||||
*
|
||||
* @param disabled True returns a disabled icon; false returns the normal icon.
|
||||
* @return the open folder icon.
|
||||
*/
|
||||
@ -174,7 +174,7 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Returns the closed folder icon.
|
||||
*
|
||||
*
|
||||
* @param disabled True returns a disabled icon; false returns the normal icon.
|
||||
* @return the closed folder icon.
|
||||
*/
|
||||
@ -189,7 +189,7 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Returns the open archive folder icon.
|
||||
*
|
||||
*
|
||||
* @param isLocked True means to return the checked-out open archive folder icon
|
||||
* @return the open archive folder icon.
|
||||
*/
|
||||
@ -204,7 +204,7 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Returns the closed folder icon.
|
||||
*
|
||||
*
|
||||
* @param isLocked True means to return the checked-out closed folder icon
|
||||
* @return the closed folder icon.
|
||||
*/
|
||||
@ -217,39 +217,9 @@ public class DataTypeUtils {
|
||||
return closedArchiveFolderIcon;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Returns the open archive folder icon.
|
||||
// *
|
||||
// * @param isLocked True means to return the checked-out open archive folder icon
|
||||
// * @return the open archive folder icon.
|
||||
// */
|
||||
// public static Icon getOpenProjectArchiveFolder( boolean isLocked ) {
|
||||
// loadImages();
|
||||
// if ( isLocked ) {
|
||||
// return lockedOpenProjectArchiveFolderIcon;
|
||||
// }
|
||||
//
|
||||
// return openProjectArchiveFolderIcon;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns the closed folder icon.
|
||||
// *
|
||||
// * @param isLocked True means to return the checked-out closed folder icon
|
||||
// * @return the closed folder icon.
|
||||
// */
|
||||
// public static Icon getClosedProjectArchiveFolder( boolean isLocked ) {
|
||||
// loadImages();
|
||||
// if ( isLocked ) {
|
||||
// return lockedClosedProjectArchiveFolderIcon;
|
||||
// }
|
||||
//
|
||||
// return closedProjectArchiveFolderIcon;
|
||||
// }
|
||||
//
|
||||
/**
|
||||
* Returns the BuiltIn icon.
|
||||
*
|
||||
*
|
||||
* @param disabled True returns a disabled icon; false returns the normal icon.
|
||||
* @return the BuiltIn icon.
|
||||
*/
|
||||
@ -264,7 +234,7 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Returns the favorites icon.
|
||||
*
|
||||
*
|
||||
* @param disabled True returns a disabled icon; false returns the normal icon.
|
||||
* @return the favorites icon.
|
||||
*/
|
||||
@ -279,7 +249,7 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Finds the icon associated with the provided data type.
|
||||
*
|
||||
*
|
||||
* @param dataType The data type for which to find an icon.
|
||||
* @param disabled True returns a disabled icon; false returns the normal icon.
|
||||
* @return the icon associated with the provided data type.
|
||||
@ -302,7 +272,7 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Returns an icon that adds highlighting to the provided icon.
|
||||
*
|
||||
*
|
||||
* @param baseIcon The icon to highlight.
|
||||
* @return the highlighted icon.
|
||||
*/
|
||||
@ -320,12 +290,12 @@ public class DataTypeUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorted list of {@link DataType}s that have names which start with the given
|
||||
* search string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}.
|
||||
*
|
||||
@param searchString The name of the DataTypes to match.
|
||||
* Returns a sorted list of {@link DataType}s that have names which start with the given search
|
||||
* string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}.
|
||||
*
|
||||
* @param searchString The name of the DataTypes to match.
|
||||
* @param dataService The service from which the data types will be taken.
|
||||
* @return A sorted list of {@link DataType}s that have names which start with the given search
|
||||
* @return A sorted list of {@link DataType}s that have names which start with the given search
|
||||
* string.
|
||||
*/
|
||||
public static List<DataType> getStartsWithMatchingDataTypes(String searchString,
|
||||
@ -335,13 +305,13 @@ public class DataTypeUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorted list of {@link DataType}s that have names which match the given search
|
||||
* string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}.
|
||||
*
|
||||
* Returns a sorted list of {@link DataType}s that have names which match the given search
|
||||
* string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}.
|
||||
*
|
||||
* @param searchString The name of the DataTypes to match.
|
||||
* @param dataService The service from which the data types will be taken.
|
||||
* @return A sorted list of {@link DataType}s that have names which match the given search
|
||||
* string.
|
||||
* @return A sorted list of {@link DataType}s that have names which match the given search
|
||||
* string.
|
||||
*/
|
||||
public static List<DataType> getExactMatchingDataTypes(String searchString,
|
||||
DataTypeQueryService dataService) {
|
||||
@ -350,10 +320,13 @@ public class DataTypeUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the give text to prepare it or use in searching for data types. Clients should
|
||||
* call this method to make sure that the given text is suitable for use when searching
|
||||
* the data type values returned by {@link #getExactMatchingDataTypes(String, DataTypeManagerService)}
|
||||
* and {@link #getStartsWithMatchingDataTypes(String, DataTypeManagerService)}.
|
||||
* Changes the given text to prepare it for use in searching for data types. Clients should
|
||||
* call this method to make sure that the given text is suitable for use when searching the
|
||||
* data type values returned by
|
||||
* {@link #getExactMatchingDataTypes(String, DataTypeQueryService)} and
|
||||
* {@link #getStartsWithMatchingDataTypes(String, DataTypeQueryService)}.
|
||||
* @param searchText the search text
|
||||
* @return the updated text
|
||||
*/
|
||||
public static String prepareSearchText(String searchText) {
|
||||
return searchText.replaceAll(" ", "");
|
||||
@ -376,12 +349,14 @@ public class DataTypeUtils {
|
||||
|
||||
/**
|
||||
* Get the base data type for the specified data type.
|
||||
* <br>For example, the base data type for Word*[5] is Word.
|
||||
* For a pointer, the base data type is the type being pointed to
|
||||
* or the pointer itself if it is pointing at nothing.
|
||||
* <br>If "INT" is a typedef on a "dword" then INT[7][3] would have a base data type of dword.
|
||||
* If you wanted to get the INT from INT[7][3]
|
||||
* you should call getNamedBasedDataType(DataType) instead.
|
||||
*
|
||||
* <p>For example, the base data type for Word*[5] is Word. For a pointer, the base data type
|
||||
* is the type being pointed to or the pointer itself if it is pointing at nothing.
|
||||
*
|
||||
* <p>If "INT" is a typedef on a "dword" then INT[7][3] would have a base data type of dword.
|
||||
* If you wanted to get the INT from INT[7][3] you should call getNamedBasedDataType(DataType)
|
||||
* instead.
|
||||
*
|
||||
* @param dt the data type whose base data type is to be determined.
|
||||
* @return the base data type.
|
||||
*/
|
||||
@ -409,15 +384,17 @@ public class DataTypeUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the named base data type for the specified data type.
|
||||
* This method intentionally does not drill down into typedefs.
|
||||
* <br>For example, the named base data type for Word*[5] is Word.
|
||||
* For a pointer, the named base data type is the type being pointed to
|
||||
* or the pointer itself if it is pointing at nothing.
|
||||
* <br>If "INT" is a typedef on a "dword", then INT[7][3] would
|
||||
* have a named base data type of INT.
|
||||
* If you wanted to get the dword from INT[7][3]
|
||||
* you should call getBasedDataType(DataType) instead.
|
||||
* Get the named base data type for the specified data type. This method intentionally does
|
||||
* not drill down into typedefs.
|
||||
*
|
||||
* <p>For example, the named base data type for Word*[5] is Word. For a pointer, the named
|
||||
* base data type is the type being pointed to or the pointer itself if it is pointing at
|
||||
* nothing.
|
||||
*
|
||||
* <p>If "INT" is a typedef on a "dword", then INT[7][3] would have a named base data type of
|
||||
* INT. If you wanted to get the dword from INT[7][3] you should call
|
||||
* getBasedDataType(DataType) instead.
|
||||
*
|
||||
* @param dt the data type whose named base data type is to be determined.
|
||||
* @return the base data type.
|
||||
*/
|
||||
@ -444,10 +421,10 @@ public class DataTypeUtils {
|
||||
* Create a copy of the chain of data types that eventually lead to a named
|
||||
* data type.
|
||||
* <p>
|
||||
* Returns a {@link DataType#copy(DataTypeManager) copy()} of the first named data
|
||||
* type found in the pointer / array type chain, and returns an identical chain of
|
||||
* pointer / arrays up to the copied named type.
|
||||
* <p>
|
||||
* Returns a {@link DataType#copy(DataTypeManager) copy()} of the first named data type found
|
||||
* in the pointer / array type chain, and returns an identical chain of pointer / arrays up to
|
||||
* the copied named type.
|
||||
*
|
||||
* @param dataType data type to be copied
|
||||
* @param dtm data type manager
|
||||
* @return deep copy of dataType
|
||||
@ -475,39 +452,16 @@ public class DataTypeUtils {
|
||||
msg = "The Program is not modifiable!\n";
|
||||
}
|
||||
else if (dtm instanceof FileArchiveBasedDataTypeManager) {
|
||||
msg =
|
||||
"The archive file is not modifiable!\nYou must open the archive for editing\n before performing this operation.";
|
||||
msg = "The archive file is not modifiable!\nYou must open the archive for editing\n" +
|
||||
"before performing this operation.\n" + dtm.getName();
|
||||
}
|
||||
else {
|
||||
msg =
|
||||
"The project archive is not modifiable!\nYou must check out the archive\n before performing this operation.";
|
||||
msg = "The project archive is not modifiable!\nYou must check out the archive\n" +
|
||||
"before performing this operation.\n" + dtm.getName();
|
||||
}
|
||||
Msg.showInfo(DataTypeUtils.class, parent, title, msg);
|
||||
|
||||
}
|
||||
|
||||
// For testing:
|
||||
// public static void main( String[] args ) {
|
||||
// JFrame frame = new JFrame();
|
||||
// JPanel panel = new JPanel();
|
||||
//
|
||||
// JLabel label1 = new GDLabel();
|
||||
// Icon icon = getOpenFolderIcon( false );
|
||||
// label1.setIcon( icon );
|
||||
//
|
||||
// JLabel label2 = new GDLabel();
|
||||
// Icon icon2 = ResourceManager.getDisabledIcon( (ImageIcon) icon );
|
||||
// label2.setIcon( icon2 );
|
||||
//
|
||||
// panel.add( label1 );
|
||||
// panel.add( label2 );
|
||||
//
|
||||
// frame.getContentPane().add( panel );
|
||||
//
|
||||
// frame.pack();
|
||||
// frame.setVisible( true );
|
||||
// frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||
// }
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -15,15 +15,14 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class StructureEditorLockedEnablementTest extends AbstractStructureEditorTest {
|
||||
|
||||
@ -34,7 +33,7 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||
try {
|
||||
DataTypeManager dataTypeManager = cat.getDataTypeManager();
|
||||
if (dt.getDataTypeManager() != dataTypeManager) {
|
||||
dt = (Structure) dt.clone(dataTypeManager);
|
||||
dt = dt.clone(dataTypeManager);
|
||||
}
|
||||
CategoryPath categoryPath = cat.getCategoryPath();
|
||||
if (!dt.getCategoryPath().equals(categoryPath)) {
|
||||
@ -69,7 +68,7 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||
Structure desiredEmptyStructure = emptyStructure;
|
||||
int txID = program.startTransaction("Removing emptyStruct from DTM.");
|
||||
try {
|
||||
programDTM.remove(emptyStructure, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
programDTM.remove(emptyStructure, TaskMonitor.DUMMY);
|
||||
if (emptyStructure.getDataTypeManager() != catDTM) {
|
||||
desiredEmptyStructure = (Structure) emptyStructure.copy(catDTM);
|
||||
desiredEmptyStructure.setCategoryPath(pgmTestCat.getCategoryPath());
|
||||
@ -154,7 +153,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||
for (CompositeEditorTableAction action : actions) {
|
||||
if ((action instanceof EditFieldAction) || (action instanceof AddBitFieldAction) ||
|
||||
(action instanceof InsertUndefinedAction) || (action instanceof PointerAction) ||
|
||||
(action instanceof HexNumbersAction)) {
|
||||
(action instanceof HexNumbersAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else if (action instanceof FavoritesAction) {
|
||||
@ -203,7 +203,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||
(action instanceof DuplicateMultipleAction) || (action instanceof ClearAction) ||
|
||||
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||
(action instanceof CreateInternalStructureAction)) {
|
||||
(action instanceof CreateInternalStructureAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else {
|
||||
@ -232,7 +233,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||
(action instanceof MoveDownAction) || (action instanceof ClearAction) ||
|
||||
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||
(action instanceof CreateInternalStructureAction)) {
|
||||
(action instanceof CreateInternalStructureAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else {
|
||||
@ -261,7 +263,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||
(action instanceof DuplicateAction) ||
|
||||
(action instanceof DuplicateMultipleAction) || (action instanceof ArrayAction) ||
|
||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||
(action instanceof CreateInternalStructureAction)) {
|
||||
(action instanceof CreateInternalStructureAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else {
|
||||
@ -289,7 +292,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||
(action instanceof MoveDownAction) || (action instanceof ClearAction) ||
|
||||
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||
(action instanceof CreateInternalStructureAction)) {
|
||||
(action instanceof CreateInternalStructureAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else {
|
||||
@ -313,7 +317,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||
checkEnablement(action, len <= numBytes);
|
||||
}
|
||||
else if ((action instanceof CycleGroupAction) || (action instanceof ClearAction) ||
|
||||
(action instanceof DeleteAction) || (action instanceof HexNumbersAction)) {
|
||||
(action instanceof DeleteAction) || (action instanceof HexNumbersAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else {
|
||||
|
@ -118,7 +118,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||
if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) ||
|
||||
(action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) ||
|
||||
(action instanceof AddBitFieldAction) || (action instanceof PointerAction) ||
|
||||
(action instanceof HexNumbersAction)) {
|
||||
(action instanceof HexNumbersAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else {
|
||||
@ -151,7 +152,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||
(action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) ||
|
||||
(action instanceof ArrayAction) || (action instanceof PointerAction) ||
|
||||
(action instanceof HexNumbersAction) ||
|
||||
(action instanceof CreateInternalStructureAction)) {
|
||||
(action instanceof CreateInternalStructureAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else if (action instanceof FavoritesAction) {
|
||||
@ -190,7 +192,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||
(action instanceof MoveUpAction) || (action instanceof ClearAction) ||
|
||||
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||
(action instanceof CreateInternalStructureAction)) {
|
||||
(action instanceof CreateInternalStructureAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else if (action instanceof FavoritesAction) {
|
||||
@ -230,7 +233,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||
(action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) ||
|
||||
(action instanceof ArrayAction) || (action instanceof PointerAction) ||
|
||||
(action instanceof HexNumbersAction) ||
|
||||
(action instanceof CreateInternalStructureAction)) {
|
||||
(action instanceof CreateInternalStructureAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else if (action instanceof FavoritesAction) {
|
||||
@ -269,7 +273,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||
if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) ||
|
||||
(action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) ||
|
||||
(action instanceof AddBitFieldAction) || (action instanceof PointerAction) ||
|
||||
(action instanceof HexNumbersAction)) {
|
||||
(action instanceof HexNumbersAction) ||
|
||||
(action instanceof ShowDataTypeInTreeAction)) {
|
||||
checkEnablement(action, true);
|
||||
}
|
||||
else {
|
||||
|
@ -110,9 +110,9 @@ public class DataTypeTestUtils {
|
||||
|
||||
public static ArchiveNode openArchive(String archiveName, boolean checkout,
|
||||
DataTypeManagerPlugin plugin) throws Exception {
|
||||
ArchiveNode openArchive = openArchive(archiveName, checkout, false, plugin);
|
||||
ArchiveNode archiveNode = openArchive(archiveName, checkout, false, plugin);
|
||||
waitForTree(plugin);
|
||||
return openArchive;
|
||||
return archiveNode;
|
||||
}
|
||||
|
||||
private static void waitForTree(DataTypeManagerPlugin plugin) {
|
||||
|
@ -93,7 +93,7 @@ public class DeveloperDumpAllTypesScript extends GhidraScript {
|
||||
}
|
||||
String userChoice =
|
||||
OptionDialog.showInputChoiceDialog(null, "Choose a Data Type Manager or Cancel",
|
||||
"Choose", names, initialDtmChoice, OptionDialog.CANCEL_OPTION);
|
||||
"Choose", names, initialDtmChoice, OptionDialog.PLAIN_MESSAGE);
|
||||
if (userChoice == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -30,24 +30,24 @@ import docking.widgets.GComponent;
|
||||
|
||||
/**
|
||||
* GhidraComboBox adds the following features:
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* 1) ActionListeners are only invoked when the <Enter> key is pressed within the text-field
|
||||
* of the combo-box. In normal JComboBox case, the ActionListeners are notified when an item is
|
||||
* selected from the list.
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* 2) Adds the auto-completion feature. As a user types in the field, the combo box suggest the
|
||||
* nearest matching entry in the combo box model.
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* It also fixes the following bug:
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* A normal JComboBox has a problem (feature?) that if you have a dialog with a button and
|
||||
* JComboBox and you edit the comboText field and then hit the button, the button sometimes does
|
||||
* not work.
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* When the combobox loses focus, and its text has changed, it generates an actionPerformed event
|
||||
* as though the user pressed <Enter> in the combo text field. This has a bizarre effect if
|
||||
@ -55,12 +55,12 @@ import docking.widgets.GComponent;
|
||||
* enablement state of the button that you pressed (which caused the text field to lose focus) in
|
||||
* that you end up changing the button's internal state(by calling setEnabled(true or false)) in
|
||||
* the middle of the button press.
|
||||
*
|
||||
*
|
||||
* @param <E> the item type
|
||||
*/
|
||||
public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||
private ArrayList<ActionListener> listeners = new ArrayList<>();
|
||||
private ArrayList<DocumentListener> docListeners = new ArrayList<>();
|
||||
private List<ActionListener> listeners = new ArrayList<>();
|
||||
private List<DocumentListener> docListeners = new ArrayList<>();
|
||||
private boolean setSelectedFlag = false;
|
||||
|
||||
private boolean forwardEnter;
|
||||
@ -70,7 +70,6 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||
* Default constructor.
|
||||
*/
|
||||
public GhidraComboBox() {
|
||||
super();
|
||||
init();
|
||||
}
|
||||
|
||||
@ -164,7 +163,7 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||
* {@link GhidraComboBox} will add an action listener to handle <Enter> actions.
|
||||
* <p>
|
||||
* To re-enable the default behavior, set the <code>forwardEnter</code> value to true.
|
||||
*
|
||||
*
|
||||
* @param forwardEnter true to enable default <Enter> key handling.
|
||||
*/
|
||||
public void setEnterKeyForwarding(boolean forwardEnter) {
|
||||
@ -198,7 +197,7 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||
* editor used</b>. By default the editor for combo boxes is a text field. This method is
|
||||
* a convenience for the user to set the number of columns on that text field, which updates
|
||||
* the preferred size of the combo box.
|
||||
*
|
||||
*
|
||||
* @param columnCount The number of columns for the text field editor
|
||||
* @see JTextField#setColumns(int)
|
||||
*/
|
||||
@ -216,11 +215,11 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||
* <li>The user deletes the text</li>
|
||||
* <li>setSelectedItem(Object) method is called with the same item</li>
|
||||
* </ol>
|
||||
*
|
||||
*
|
||||
* In that above series of steps, the text will still be empty, as the user deleted it *and*
|
||||
* the call to setSelectedItem(Object) had no effect because the base class assumed that the
|
||||
* item is already selected.
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* This method exists to make sure, in that case, that the text of the field matches the
|
||||
* selected item.
|
||||
@ -256,6 +255,13 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||
model.addElement(obj);
|
||||
}
|
||||
|
||||
public void addToModel(Collection<E> items) {
|
||||
DefaultComboBoxModel<E> model = (DefaultComboBoxModel<E>) getModel();
|
||||
for (E e : items) {
|
||||
model.addElement(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsItem(E obj) {
|
||||
DefaultComboBoxModel<E> model = (DefaultComboBoxModel<E>) getModel();
|
||||
return model.getIndexOf(obj) != -1;
|
||||
|
@ -28,7 +28,6 @@ import docking.widgets.label.GHtmlLabel;
|
||||
|
||||
/**
|
||||
* A dialog that has text fields to get user input.
|
||||
*
|
||||
*/
|
||||
public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||
|
||||
@ -37,19 +36,17 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||
private boolean allowEdits;
|
||||
|
||||
/**
|
||||
* Creates a provider for a generic input dialog with the specified title,
|
||||
* a label and a editable comboBox pre-populated with selectable values. The user
|
||||
* can check the value of {@link #isCanceled()} to know whether or not
|
||||
* the user canceled the operation. To get the user selected value use the
|
||||
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
|
||||
* null will be returned from <code>getValue()</code>.
|
||||
* <P>
|
||||
* Creates a provider for a generic input dialog with the specified title, a label and a
|
||||
* editable comboBox pre-populated with selectable values. The user can check the value of
|
||||
* {@link #isCanceled()} to know whether or not the user canceled the operation. To get the
|
||||
* user selected value use the {@link #getValue()} value(s) entered by the user. If the user
|
||||
* cancelled the operation, then null will be returned from {@link #getValue()}.
|
||||
*
|
||||
* @param dialogTitle used as the name of the dialog's title bar
|
||||
* @param label value to use for the label of the text field
|
||||
* @param optionValues values to populate the combo box
|
||||
* @param initialValue the initial value - can be null
|
||||
* @param messageIcon the icon to display on the dialog--can be null
|
||||
* @param initialValue the initial value; may be null
|
||||
* @param messageIcon the icon to display on the dialog; may be null
|
||||
*/
|
||||
public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues,
|
||||
String initialValue, Icon messageIcon) {
|
||||
@ -67,20 +64,18 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a provider for a generic input dialog with the specified title,
|
||||
* a label and a editable comboBox pre-populated with selectable values. The user
|
||||
* can check the value of {@link #isCanceled()} to know whether or not
|
||||
* the user canceled the operation. To get the user selected value use the
|
||||
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
|
||||
* null will be returned from <code>getValue()</code>.
|
||||
* <P>
|
||||
* Creates a provider for a generic input dialog with the specified title, a label and a
|
||||
* editable comboBox pre-populated with selectable values. The user can check the value of
|
||||
* {@link #isCanceled()} to know whether or not the user canceled the operation. To get the
|
||||
* user selected value use the {@link #getValue()} value(s) entered by the user. If the user
|
||||
* cancelled the operation, then null will be returned from {@link #getValue()}.
|
||||
*
|
||||
* @param dialogTitle used as the name of the dialog's title bar
|
||||
* @param label value to use for the label of the text field
|
||||
* @param optionValues values to populate the combo box
|
||||
* @param initialValue the initial value - can be null
|
||||
* @param allowEdits true allows the user to add custom entries to the combo box by entering text
|
||||
* @param messageIcon the icon to display on the dialog--can be null
|
||||
* @param initialValue the initial value; may be null
|
||||
* @param allowEdits true allows the user to add custom entries by entering text
|
||||
* @param messageIcon the icon to display on the dialog; may be null
|
||||
*/
|
||||
public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues,
|
||||
String initialValue, boolean allowEdits, Icon messageIcon) {
|
||||
@ -103,7 +98,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* completes the construction of the gui for this dialog
|
||||
* Completes the construction of the gui for this dialog
|
||||
*/
|
||||
private void buildMainPanel(String labelText, String[] optionValues, String initialValue,
|
||||
Icon messageIcon) {
|
||||
@ -168,13 +163,15 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||
|
||||
/**
|
||||
* Returns if this dialog is canceled.
|
||||
* @return true if canceled
|
||||
*/
|
||||
public boolean isCanceled() {
|
||||
return isCanceled;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the value of the first combo box
|
||||
* Return the value of the first combo box.
|
||||
* @return the value
|
||||
*/
|
||||
public String getValue() {
|
||||
if (isCanceled) {
|
||||
@ -192,8 +189,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||
/**
|
||||
* Set the current choice to value.
|
||||
* @param value updated choice
|
||||
* @throws NoSuchElementException if choice does not permit edits and value is
|
||||
* not a valid choice.
|
||||
* @throws NoSuchElementException if edits not permitted and value is not a valid choice
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
combo.setSelectedItem(value);
|
||||
|
@ -27,6 +27,7 @@ import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.Timer;
|
||||
@ -334,7 +335,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
* <p>
|
||||
* <b>Note: </b>See the usage note at the header of this class concerning how tree state
|
||||
* is used relative to the <code>equals()</code> method.
|
||||
*
|
||||
*
|
||||
* @param state the state to restore
|
||||
*
|
||||
* @see #getTreeState()
|
||||
@ -582,7 +583,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
* been replaced by a new node that is equal, but a different instance. One way this happens
|
||||
* is if the tree is filtered and therefor the displayed nodes are clones of the model nodes.
|
||||
* This can also happen if the tree nodes are rebuilt for some reason.
|
||||
*
|
||||
*
|
||||
* @param node the node
|
||||
* @return the corresponding model node in the tree. If the tree is filtered the viewed node
|
||||
* will be a clone of the corresponding model node.
|
||||
@ -596,7 +597,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
* been replaced by a new node that is equal, but a different instance. One way this happens
|
||||
* is if the tree is filtered and therefor the displayed nodes are clones of the model nodes.
|
||||
* This can also happen if the tree nodes are rebuilt for some reason.
|
||||
*
|
||||
*
|
||||
* @param path the path of the node
|
||||
* @return the corresponding model node in the tree. If the tree is filtered the viewed node
|
||||
* will be a clone of the corresponding model node.
|
||||
@ -609,7 +610,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
* Gets the view node for the given node. This is useful to translate to a tree path that is
|
||||
* valid for the currently displayed tree. (Remember that if the tree is filtered, then the
|
||||
* displayed nodes are clones of the model nodes.)
|
||||
*
|
||||
*
|
||||
* @param node the node
|
||||
* @return the current node in the displayed (possibly filtered) tree
|
||||
*/
|
||||
@ -621,7 +622,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
* Gets the view node for the given path. This is useful to translate to a tree path that is
|
||||
* valid for the currently displayed tree. (Remember that if the tree is filtered, then the
|
||||
* displayed nodes are clones of the model nodes.)
|
||||
*
|
||||
*
|
||||
* @param path the path of the node
|
||||
* @return the current node in the displayed (possibly filtered) tree
|
||||
*/
|
||||
@ -760,7 +761,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
|
||||
/**
|
||||
* Returns true if the given JTree is the actual JTree used by this GTree.
|
||||
*
|
||||
*
|
||||
* @param jTree the tree to test
|
||||
* @return true if the given JTree is the actual JTree used by this GTree.
|
||||
*/
|
||||
@ -773,7 +774,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
* <P>
|
||||
* NOTE: if this method is not called from the Swing thread, then the root node will be set
|
||||
* later on the Swing thread. That is, this method will return before the work has been done.
|
||||
*
|
||||
*
|
||||
* @param rootNode The node to set as the new root.
|
||||
*/
|
||||
public void setRootNode(GTreeNode rootNode) {
|
||||
@ -916,6 +917,13 @@ public class GTree extends JPanel implements BusyListener {
|
||||
return paths;
|
||||
}
|
||||
|
||||
public List<GTreeNode> getSelectedNodes() {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
return Arrays.stream(paths)
|
||||
.map(tp -> (GTreeNode) tp.getLastPathComponent())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public boolean isExpanded(TreePath treePath) {
|
||||
return tree.isExpanded(treePath);
|
||||
}
|
||||
@ -1023,20 +1031,20 @@ public class GTree extends JPanel implements BusyListener {
|
||||
* becomes available to the model. This method will ensure that the named child passes any
|
||||
* current filter in order for the child to appear in the tree. This effect is temporary and
|
||||
* will be undone when next the filter changes.
|
||||
*
|
||||
*
|
||||
* <p>This method is intended to be used by clients using an asynchronous node model, where
|
||||
* new nodes will get created by application-level events. Such clients may wish to perform
|
||||
* work when newly created nodes become available. This method simplifies the concurrent
|
||||
* nature of the GTree, asynchronous nodes and the processing of asynchronous application-level
|
||||
* events by providing a callback mechanism for clients. <b>This method is non-blocking.</b>
|
||||
*
|
||||
*
|
||||
* <p>Note: this method assumes that the given parent node is in the view and not filtered
|
||||
* out of the view. This method makes no attempt to ensure the given parent node passes any
|
||||
* existing filter.
|
||||
*
|
||||
*
|
||||
* <p>Note: this method will not wait forever for the given node to appear. It will eventually
|
||||
* give up if the node never arrives.
|
||||
*
|
||||
*
|
||||
* @param parent the model's parent node. If the view's parent node is passed, it will
|
||||
* be translated to the model node.
|
||||
* @param childName the name of the desired child
|
||||
@ -1046,17 +1054,17 @@ public class GTree extends JPanel implements BusyListener {
|
||||
Consumer<GTreeNode> consumer) {
|
||||
|
||||
/*
|
||||
|
||||
|
||||
If the GTree were to use Java's CompletableStage API, then the code below
|
||||
could be written thusly:
|
||||
|
||||
|
||||
tree.getNewNode(modelParent, newName)
|
||||
.thenCompose(newModelChild -> {
|
||||
tree.ignoreFilter(newModelChild);
|
||||
return tree.getNewNode(viewParent, newName);
|
||||
))
|
||||
.thenAccept(consumer);
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// ensure we operate on the model node which will always have the given child not the view
|
||||
@ -1075,7 +1083,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
* Requests that the node with the given name, in the given parent, be edited. This operation
|
||||
* is asynchronous. This request will be buffered as needed to wait for the given node to be
|
||||
* added to the parent, up to a timeout period.
|
||||
*
|
||||
*
|
||||
* @param parent the parent node
|
||||
* @param childName the name of the child to edit
|
||||
*/
|
||||
@ -1101,7 +1109,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
|
||||
/**
|
||||
* Requests that the node be edited. This operation is asynchronous.
|
||||
*
|
||||
*
|
||||
* @param node the node to edit
|
||||
*/
|
||||
public void startEditing(GTreeNode node) {
|
||||
@ -1269,7 +1277,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
/**
|
||||
* Used to run tree tasks. This method is not meant for general clients of this tree, but
|
||||
* rather for tasks to tell the tree to perform subtasks.
|
||||
*
|
||||
*
|
||||
* @param task the task to run
|
||||
*/
|
||||
public void runTask(GTreeTask task) {
|
||||
|
@ -15,8 +15,9 @@
|
||||
*/
|
||||
package generic.test;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.StringContains.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
@ -298,6 +299,15 @@ public abstract class AbstractGTest {
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void assertContainsString(String expected, String actual) {
|
||||
assertThat(actual, containsString(expected));
|
||||
}
|
||||
|
||||
public static <T> void assertContainsStringIgnoringCase(String expected, String actual) {
|
||||
// newer hamcrest versions have containsStringIgnoringCase()
|
||||
assertThat(actual.toLowerCase(), containsString(expected.toLowerCase()));
|
||||
}
|
||||
|
||||
private static String printListFailureMessage(String message, List<?> expected,
|
||||
List<?> actual) {
|
||||
|
||||
|
@ -68,7 +68,7 @@ public interface DataTypeManager {
|
||||
/**
|
||||
* Returns a unique name not currently used by any other dataType or category
|
||||
* with the same baseName
|
||||
*
|
||||
*
|
||||
* @param path the path of the name
|
||||
* @param baseName the base name to be made unique
|
||||
* @return a unique name starting with baseName
|
||||
@ -90,7 +90,7 @@ public interface DataTypeManager {
|
||||
* Returns a data type after adding it to this data manager.
|
||||
* The returned dataType will be in a category in this dataTypeManager
|
||||
* that is equivalent to the category of the passed in dataType.
|
||||
*
|
||||
*
|
||||
* @param dataType the dataType to be resolved.
|
||||
* @param handler used to resolve conflicts with existing dataTypes.
|
||||
* @return an equivalent dataType that "belongs" to this dataTypeManager.
|
||||
@ -120,7 +120,7 @@ public interface DataTypeManager {
|
||||
|
||||
/**
|
||||
* Adds all data types to the specified list.]
|
||||
*
|
||||
*
|
||||
* @param list the result list into which the types will be placed
|
||||
*/
|
||||
public void getAllDataTypes(List<DataType> list);
|
||||
@ -181,7 +181,7 @@ public interface DataTypeManager {
|
||||
* there is also a category "b" under category "a". A better solution is to use
|
||||
* the {@link #getDataType(DataTypePath)} method because the DataTypePath keeps the
|
||||
* category and datatype name separate.
|
||||
*
|
||||
*
|
||||
* @param dataTypePath path
|
||||
* @return the dataType or null if it isn't found
|
||||
*/
|
||||
@ -206,7 +206,7 @@ public interface DataTypeManager {
|
||||
/**
|
||||
* Returns the dataTypeId for the given dataType. If the dataType is not
|
||||
* currently in the dataTypeManger, it will be added
|
||||
*
|
||||
*
|
||||
* @param dt the data type
|
||||
* @return the ID of the resolved type
|
||||
*/
|
||||
@ -215,7 +215,7 @@ public interface DataTypeManager {
|
||||
/**
|
||||
* Returns the dataTypeId for the given dataType. If the dataType does not exist,
|
||||
* a -1 will be returned
|
||||
*
|
||||
*
|
||||
* @param dt the datatype to get an id for
|
||||
* @return the ID of the type
|
||||
*/
|
||||
@ -224,7 +224,7 @@ public interface DataTypeManager {
|
||||
/**
|
||||
* Returns the dataType associated with the given dataTypeId or null if the dataTypeId is
|
||||
* not valid
|
||||
*
|
||||
*
|
||||
* @param dataTypeID the ID
|
||||
* @return the type
|
||||
*/
|
||||
@ -232,7 +232,7 @@ public interface DataTypeManager {
|
||||
|
||||
/**
|
||||
* Returns the Category with the given id
|
||||
*
|
||||
*
|
||||
* @param categoryID id of the desired category
|
||||
* @return the category
|
||||
*/
|
||||
@ -240,7 +240,7 @@ public interface DataTypeManager {
|
||||
|
||||
/**
|
||||
* Get the category that has the given path
|
||||
*
|
||||
*
|
||||
* @param path the path
|
||||
* @return the category if defined, otherwise null
|
||||
*/
|
||||
@ -282,7 +282,7 @@ public interface DataTypeManager {
|
||||
|
||||
/**
|
||||
* Return true if the given dataType exists in this data type manager
|
||||
*
|
||||
*
|
||||
* @param dataType the type
|
||||
* @return true if the type is in this manager
|
||||
*/
|
||||
@ -290,7 +290,7 @@ public interface DataTypeManager {
|
||||
|
||||
/**
|
||||
* Create a category for the given path; returns the current category if it already exits
|
||||
*
|
||||
*
|
||||
* @param path the path
|
||||
* @return the category
|
||||
*/
|
||||
@ -351,7 +351,7 @@ public interface DataTypeManager {
|
||||
/**
|
||||
* Returns a default sized pointer to the given datatype. The pointer size is established
|
||||
* dynamically based upon the data organization established by the compiler specification.
|
||||
*
|
||||
*
|
||||
* @param datatype the pointed to data type
|
||||
* @return the pointer
|
||||
*/
|
||||
@ -361,7 +361,7 @@ public interface DataTypeManager {
|
||||
* Returns a pointer of the given size to the given datatype.
|
||||
* Note: It is preferred to use default sized pointers when possible (i.e., size=-1,
|
||||
* see {@link #getPointer(DataType)}) instead of explicitly specifying the size value.
|
||||
*
|
||||
*
|
||||
* @param datatype the pointed to data type
|
||||
* @param size the size of the pointer to be created or -1 for a default sized pointer
|
||||
* @return the pointer
|
||||
@ -416,6 +416,14 @@ public interface DataTypeManager {
|
||||
*/
|
||||
public void findEnumValueNames(long value, Set<String> enumValueNames);
|
||||
|
||||
/**
|
||||
* Finds the data type using the given source archive and id.
|
||||
*
|
||||
* @param sourceArchive the optional source archive; required when the type is associated with
|
||||
* that source archive
|
||||
* @param datatypeID the type's id
|
||||
* @return the type or null
|
||||
*/
|
||||
public DataType getDataType(SourceArchive sourceArchive, UniversalID datatypeID);
|
||||
|
||||
/**
|
||||
@ -433,7 +441,7 @@ public interface DataTypeManager {
|
||||
|
||||
/**
|
||||
* Returns the source archive for the given ID
|
||||
*
|
||||
*
|
||||
* @param sourceID the ID
|
||||
* @return the archive; null if the ID is null; null if the archive does not exist
|
||||
*/
|
||||
@ -447,7 +455,7 @@ public interface DataTypeManager {
|
||||
|
||||
/**
|
||||
* Returns all data types within this manager that have as their source the given archive
|
||||
*
|
||||
*
|
||||
* @param sourceArchive the archive
|
||||
* @return the types
|
||||
*/
|
||||
@ -461,7 +469,7 @@ public interface DataTypeManager {
|
||||
|
||||
/**
|
||||
* Change the given data type so that its source archive is the given archive
|
||||
*
|
||||
*
|
||||
* @param datatype the type
|
||||
* @param archive the archive
|
||||
*/
|
||||
@ -508,7 +516,7 @@ public interface DataTypeManager {
|
||||
/**
|
||||
* Removes the source archive from this manager. This will disassociate all data types in
|
||||
* this manager from the given archive.
|
||||
*
|
||||
*
|
||||
* @param sourceArchive the archive
|
||||
*/
|
||||
public void removeSourceArchive(SourceArchive sourceArchive);
|
||||
@ -529,17 +537,18 @@ public interface DataTypeManager {
|
||||
* @deprecated the method {@link DataType#getParents()} should be used instead.
|
||||
* Use of {@link Set} implementations for containing DataTypes is also inefficient.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<DataType> getDataTypesContaining(DataType dataType);
|
||||
|
||||
/**
|
||||
* Determine if settings are supported for BuiltIn datatypes within this
|
||||
* Determine if settings are supported for BuiltIn datatypes within this
|
||||
* datatype manager.
|
||||
* @return true if BuiltIn Settings are permitted
|
||||
*/
|
||||
public boolean allowsDefaultBuiltInSettings();
|
||||
|
||||
/**
|
||||
* Determine if settings are supported for datatype components within this
|
||||
* Determine if settings are supported for datatype components within this
|
||||
* datatype manager (i.e., for structure and union components).
|
||||
* @return true if BuiltIn Settings are permitted
|
||||
*/
|
||||
|
@ -23,8 +23,8 @@ import ghidra.program.model.listing.*;
|
||||
|
||||
/**
|
||||
* Creates and initializes {@link Structure} objects.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class StructureFactory {
|
||||
public static final String DEFAULT_STRUCTURE_NAME = "struct";
|
||||
@ -33,14 +33,14 @@ public class StructureFactory {
|
||||
* Creates a {@link StructureDataType} instance based upon the information
|
||||
* provided. The instance will not be placed in memory.
|
||||
* <p>
|
||||
* This method is just a pass-through method for
|
||||
* This method is just a pass-through method for
|
||||
* {@link #createStructureDataType(Program,Address,int,String,boolean)}
|
||||
* equivalent to calling:
|
||||
* <pre>
|
||||
* Structure newStructure = StructureFactory.createStructureDataType(
|
||||
* program, address, dataLength, DEFAULT_STRUCTURE_NAME, true );
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @param program The program to which the structure will belong.
|
||||
* @param address The address of the structure.
|
||||
* @param dataLength The number of components to add to the structure.
|
||||
@ -50,7 +50,7 @@ public class StructureFactory {
|
||||
* <li>if <code>dataLength</code> is not greater than zero
|
||||
* <li>if the number of components to add exceeds the available
|
||||
* address space
|
||||
* <li>if there are any instructions in the provided
|
||||
* <li>if there are any instructions in the provided
|
||||
* address space
|
||||
* <li>if there are no data components to add to the structure
|
||||
* </ul>
|
||||
@ -63,7 +63,7 @@ public class StructureFactory {
|
||||
/**
|
||||
* Creates a {@link StructureDataType} instance based upon the information
|
||||
* provided. The instance will not be placed in memory.
|
||||
*
|
||||
*
|
||||
* @param program The program to which the structure will belong.
|
||||
* @param address The address of the structure.
|
||||
* @param dataLength The number of components to add to the structure.
|
||||
@ -77,7 +77,7 @@ public class StructureFactory {
|
||||
* <li>if <code>dataLength</code> is not greater than zero
|
||||
* <li>if the number of components to add exceeds the available
|
||||
* address space
|
||||
* <li>if there are any instructions in the provided
|
||||
* <li>if there are any instructions in the provided
|
||||
* address space
|
||||
* <li>if there are no data components to add to the structure
|
||||
* </ul>
|
||||
@ -125,18 +125,18 @@ public class StructureFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link StructureDataType} instance, which is inside of
|
||||
* another structure, based upon the information provided. The instance
|
||||
* Creates a {@link StructureDataType} instance, which is inside of
|
||||
* another structure, based upon the information provided. The instance
|
||||
* will not be placed in memory.
|
||||
* <p>
|
||||
* This method is just a pass-through method for
|
||||
* This method is just a pass-through method for
|
||||
* {@link #createStructureDataTypeInStrucuture(Program,Address,int[],int[],String,boolean)}
|
||||
* equivalent to calling:
|
||||
* <pre>
|
||||
* Structure newStructure = StructureFactory.createStructureDataTypeInStrucuture(
|
||||
* program, address, fromPath, toPath, DEFAULT_STRUCTURE_NAME, true );
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @param program The program to which the structure will belong.
|
||||
* @param address The address of the structure.
|
||||
* @param fromPath The path to the first element in the parent structure
|
||||
@ -160,10 +160,10 @@ public class StructureFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link StructureDataType} instance, which is inside of
|
||||
* another structure, based upon the information provided. The instance
|
||||
* Creates a {@link StructureDataType} instance, which is inside of
|
||||
* another structure, based upon the information provided. The instance
|
||||
* will not be placed in memory.
|
||||
*
|
||||
*
|
||||
* @param program The program to which the structure will belong.
|
||||
* @param address The address of the structure.
|
||||
* @param fromPath The path to the first element in the parent structure
|
||||
@ -227,7 +227,7 @@ public class StructureFactory {
|
||||
return newStructure;
|
||||
}
|
||||
|
||||
// uses the provided context to initiailze the provided structure with
|
||||
// uses the provided context to initialize the provided structure with
|
||||
// dataLength number of components
|
||||
private static void initializeStructureFromContext(Structure structure,
|
||||
DataTypeProviderContext context, int dataLength) {
|
||||
@ -249,8 +249,8 @@ public class StructureFactory {
|
||||
}
|
||||
|
||||
for (DataTypeComponent dataComp : dataComps) {
|
||||
structure.add(dataComp.getDataType(), dataComp.getLength(),
|
||||
dataComp.getFieldName(), dataComp.getComment());
|
||||
structure.add(dataComp.getDataType(), dataComp.getLength(), dataComp.getFieldName(),
|
||||
dataComp.getComment());
|
||||
}
|
||||
}
|
||||
}
|
||||
|