GP-1913 - Updated data type synchronization workflow by adding a new action and a home button to the structure editor. Added action to the composite editors and enum editor to show the type being edited in the Data Type Manager's tree.
|
@ -15,8 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package agent.frida.model.invm;
|
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 {
|
public class InVmModelForFridaFactoryTest extends AbstractModelForFridaFactoryTest {
|
||||||
@Override
|
@Override
|
||||||
public ModelHost modelHost() throws Throwable {
|
public ModelHost modelHost() throws Throwable {
|
||||||
|
|
|
@ -17,10 +17,13 @@ package agent.frida.model.invm;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
import agent.frida.model.AbstractModelForFridaInterpreterTest;
|
import agent.frida.model.AbstractModelForFridaInterpreterTest;
|
||||||
|
import generic.test.category.NightlyCategory;
|
||||||
import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen;
|
import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen;
|
||||||
|
|
||||||
|
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||||
public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest
|
public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest
|
||||||
implements ProvidesTargetViaLaunchSpecimen {
|
implements ProvidesTargetViaLaunchSpecimen {
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,4 +64,3 @@ public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package agent.frida.model.invm;
|
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 {
|
public class InVmModelForFridaMethodsTest extends AbstractModelForFridaMethodsTest {
|
||||||
@Override
|
@Override
|
||||||
public ModelHost modelHost() throws Throwable {
|
public ModelHost modelHost() throws Throwable {
|
||||||
|
|
|
@ -17,9 +17,12 @@ package agent.frida.model.invm;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
import agent.frida.model.AbstractModelForFridaRootAttacherTest;
|
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 {
|
public class InVmModelForFridaRootAttacherTest extends AbstractModelForFridaRootAttacherTest {
|
||||||
@Override
|
@Override
|
||||||
public ModelHost modelHost() throws Throwable {
|
public ModelHost modelHost() throws Throwable {
|
||||||
|
|
|
@ -15,8 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package agent.frida.model.invm;
|
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 {
|
public class InVmModelForFridaRootLauncherTest extends AbstractModelForFridaRootLauncherTest {
|
||||||
@Override
|
@Override
|
||||||
public ModelHost modelHost() throws Throwable {
|
public ModelHost modelHost() throws Throwable {
|
||||||
|
|
|
@ -15,8 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package agent.frida.model.invm;
|
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 {
|
public class InVmModelForFridaScenarioStackTest extends AbstractModelForFridaScenarioStackTest {
|
||||||
@Override
|
@Override
|
||||||
public ModelHost modelHost() throws Throwable {
|
public ModelHost modelHost() throws Throwable {
|
||||||
|
|
|
@ -15,8 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package agent.frida.model.invm;
|
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 {
|
public class InVmModelForFridaX64RegistersTest extends AbstractModelForFridaX64RegistersTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -98,6 +98,17 @@
|
||||||
the <i>Data Type Manager</i> display is updated to reflect the new name in the tree.
|
the <i>Data Type Manager</i> display is updated to reflect the new name in the tree.
|
||||||
</p>
|
</p>
|
||||||
</blockquote>
|
</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>
|
<h2>Change the Sort Order</h2>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>As with most tables in Ghidra, you can change the sort order of a column by
|
<p>As with most tables in Ghidra, you can change the sort order of a column by
|
||||||
|
|
|
@ -102,6 +102,16 @@
|
||||||
data items in the program will have changed due to the apply.</P>
|
data items in the program will have changed due to the apply.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
|
<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>
|
<H2>Closing the Editor</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
|
|
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 |
|
@ -48,11 +48,8 @@ public class ApplyAction extends CompositeEditorTableAction {
|
||||||
try {
|
try {
|
||||||
model.apply();
|
model.apply();
|
||||||
}
|
}
|
||||||
catch (EmptyCompositeException e1) {
|
catch (EmptyCompositeException | InvalidDataTypeException e) {
|
||||||
model.setStatus(e1.getMessage(), true);
|
model.setStatus(e.getMessage(), true);
|
||||||
}
|
|
||||||
catch (InvalidDataTypeException e1) {
|
|
||||||
model.setStatus(e1.getMessage(), true);
|
|
||||||
}
|
}
|
||||||
requestTableFocus();
|
requestTableFocus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
|
||||||
public static final String EDIT_ACTION_PREFIX = "Editor: ";
|
public static final String EDIT_ACTION_PREFIX = "Editor: ";
|
||||||
|
|
||||||
public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group,
|
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);
|
super(name, provider.plugin.getName(), KeyBindingType.SHARED);
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
model = provider.getModel();
|
model = provider.getModel();
|
||||||
|
|
|
@ -15,53 +15,55 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.compositeeditor;
|
package ghidra.app.plugin.core.compositeeditor;
|
||||||
|
|
||||||
import ghidra.program.model.data.*;
|
|
||||||
import docking.ComponentProvider;
|
import docking.ComponentProvider;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface implemented by data type editors.
|
* Interface implemented by data type editors.
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public interface EditorProvider {
|
public interface EditorProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of this editor.
|
* Get the name of this editor.
|
||||||
|
* @return the name of this editor
|
||||||
*/
|
*/
|
||||||
public String getName();
|
public String getName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the pathname of the data type being edited.
|
* Get the pathname of the data type being edited.
|
||||||
|
* @return the pathname of the data type being edited
|
||||||
*/
|
*/
|
||||||
public DataTypePath getDtPath();
|
public DataTypePath getDtPath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the component provider for this editor.
|
* Get the component provider for this editor.
|
||||||
|
* @return the component provider for this editor
|
||||||
*/
|
*/
|
||||||
public ComponentProvider getComponentProvider();
|
public ComponentProvider getComponentProvider();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the datatype manager associated with this editor.
|
* Get the datatype manager associated with this editor.
|
||||||
|
* @return the datatype manager associated with this editor
|
||||||
*/
|
*/
|
||||||
public DataTypeManager getDataTypeManager();
|
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.
|
* @param domainObject the program or data type archive that was restored.
|
||||||
*/
|
*/
|
||||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject);
|
public void domainObjectRestored(DataTypeManagerDomainObject domainObject);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether this editor is editing the data type with the given
|
* Return whether this editor is editing the data type with the given path.
|
||||||
* path.
|
|
||||||
* @param dtPath path of a data type
|
* @param dtPath path of a data type
|
||||||
* @return true if the data type for the pathname is being edited
|
* @return true if the data type for the pathname is being edited
|
||||||
*/
|
*/
|
||||||
public boolean isEditing(DataTypePath dtPath);
|
public boolean isEditing(DataTypePath dtPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an editor listener that will be notified when the edit window is
|
* Add an editor listener that will be notified when the edit window is closed.
|
||||||
* closed.
|
* @param listener the listener
|
||||||
*/
|
*/
|
||||||
public void addEditorListener(EditorListener listener);
|
public void addEditorListener(EditorListener listener);
|
||||||
|
|
||||||
|
@ -72,6 +74,7 @@ public interface EditorProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether changes need to be saved.
|
* Returns whether changes need to be saved.
|
||||||
|
* @return whether changes need to be saved
|
||||||
*/
|
*/
|
||||||
public boolean needsSave();
|
public boolean needsSave();
|
||||||
|
|
||||||
|
|
|
@ -15,16 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.compositeeditor;
|
package ghidra.app.plugin.core.compositeeditor;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||||
import ghidra.app.util.HelpTopics;
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.program.model.data.Composite;
|
import ghidra.program.model.data.Composite;
|
||||||
import ghidra.program.model.data.DataTypeComponent;
|
import ghidra.program.model.data.DataTypeComponent;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.*;
|
||||||
import ghidra.util.Msg;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An action to show references to the field in the currently selected editor row
|
* 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();
|
String fieldName = getFieldName();
|
||||||
Composite composite = model.getOriginalComposite();
|
Composite composite = model.getOriginalComposite();
|
||||||
SwingUtilities.invokeLater(
|
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName));
|
||||||
() -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFieldName() {
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,6 +73,8 @@ public class StructureEditorProvider extends CompositeEditorProvider {
|
||||||
new ShowComponentPathAction(this),
|
new ShowComponentPathAction(this),
|
||||||
new AddBitFieldAction(this),
|
new AddBitFieldAction(this),
|
||||||
new EditBitFieldAction(this),
|
new EditBitFieldAction(this),
|
||||||
|
new ShowDataTypeInTreeAction(this),
|
||||||
|
|
||||||
// new ViewBitFieldAction(this)
|
// new ViewBitFieldAction(this)
|
||||||
};
|
};
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
|
@ -65,7 +65,8 @@ public class UnionEditorProvider extends CompositeEditorProvider {
|
||||||
new EditFieldAction(this),
|
new EditFieldAction(this),
|
||||||
new HexNumbersAction(this),
|
new HexNumbersAction(this),
|
||||||
new AddBitFieldAction(this),
|
new AddBitFieldAction(this),
|
||||||
new EditBitFieldAction(this)
|
new EditBitFieldAction(this),
|
||||||
|
new ShowDataTypeInTreeAction(this)
|
||||||
};
|
};
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,9 @@ import generic.util.Path;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.plugin.ProgramPlugin;
|
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.archive.*;
|
||||||
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
|
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||||
|
@ -60,8 +62,7 @@ import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.DataTypeArchive;
|
import ghidra.program.model.listing.DataTypeArchive;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.*;
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.datastruct.LRUMap;
|
import ghidra.util.datastruct.LRUMap;
|
||||||
import ghidra.util.task.TaskLauncher;
|
import ghidra.util.task.TaskLauncher;
|
||||||
|
|
||||||
|
@ -570,6 +571,10 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||||
return dataTypeManagerHandler.openArchive(archiveName);
|
return dataTypeManagerHandler.openArchive(archiveName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Archive> getAllArchives() {
|
||||||
|
return dataTypeManagerHandler.getAllArchives();
|
||||||
|
}
|
||||||
|
|
||||||
public void openProjectDataTypeArchive() {
|
public void openProjectDataTypeArchive() {
|
||||||
if (openDialog == null) {
|
if (openDialog == null) {
|
||||||
ActionListener listener = ev -> {
|
ActionListener listener = ev -> {
|
||||||
|
@ -613,7 +618,9 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||||
@Override
|
@Override
|
||||||
public void setDataTypeSelected(DataType dataType) {
|
public void setDataTypeSelected(DataType dataType) {
|
||||||
if (provider.isVisible()) {
|
if (provider.isVisible()) {
|
||||||
provider.setDataTypeSelected(dataType);
|
// this is a service method, ensure it is on the Swing thread, since it interacts with
|
||||||
|
// Swing components
|
||||||
|
Swing.runIfSwingOrRunLater(() -> provider.setDataTypeSelected(dataType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,7 +732,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
DataTypesActionContext dtContext = (DataTypesActionContext) context;
|
DataTypesActionContext dtContext = (DataTypesActionContext) context;
|
||||||
GTreeNode selectedNode = dtContext.getSelectedNode();
|
GTreeNode selectedNode = dtContext.getClickedNode();
|
||||||
if (!(selectedNode instanceof ArchiveNode)) {
|
if (!(selectedNode instanceof ArchiveNode)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,35 +142,38 @@ public class DataTypeSynchronizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commits a single program data type's changes to the associated source data type in the archive.
|
* Commits a single program data type's changes to the associated source data type in the
|
||||||
* @param refDT the program data type
|
* archive.
|
||||||
* @return true if the commit succeeds.
|
* @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) {
|
public static boolean commit(DataTypeManagerHandler dtmHandler, DataType dt) {
|
||||||
SourceArchive sourceArchive = refDT.getSourceArchive();
|
SourceArchive sourceArchive = dt.getSourceArchive();
|
||||||
DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive);
|
DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive);
|
||||||
if (sourceDTM == null) {
|
if (sourceDTM == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
commit(sourceDTM, refDT);
|
commit(sourceDTM, dt);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a single data type in the program to match the associated source data type from the
|
* Updates a single data type in the program to match the associated source data type from the
|
||||||
* archive.
|
* archive.
|
||||||
* @param dataType the program data type
|
* @param dtmHandler the handler that manages data types
|
||||||
* @return true if the update succeeds.
|
* @param dt the data type
|
||||||
|
* @return true if the update succeeds
|
||||||
*/
|
*/
|
||||||
public static boolean update(DataTypeManagerHandler dtmHandler, DataType refDT) {
|
public static boolean update(DataTypeManagerHandler dtmHandler, DataType dt) {
|
||||||
DataTypeManager dataTypeManager = refDT.getDataTypeManager();
|
DataTypeManager dataTypeManager = dt.getDataTypeManager();
|
||||||
SourceArchive sourceArchive = refDT.getSourceArchive();
|
SourceArchive sourceArchive = dt.getSourceArchive();
|
||||||
DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive);
|
DataTypeManager sourceDtm = dtmHandler.getDataTypeManager(sourceArchive);
|
||||||
if (dataTypeManager == null || sourceDTM == null) {
|
if (dataTypeManager == null || sourceDtm == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DataType sourceDT = sourceDTM.getDataType(sourceArchive, refDT.getUniversalID());
|
DataType sourceDt = sourceDtm.getDataType(sourceArchive, dt.getUniversalID());
|
||||||
update(dataTypeManager, sourceDT);
|
update(dataTypeManager, sourceDt);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,8 +358,9 @@ public class DataTypeSynchronizer {
|
||||||
|
|
||||||
buffy.append("<TR BORDER=LEFT>");
|
buffy.append("<TR BORDER=LEFT>");
|
||||||
buffy.append("<TD VALIGN=\"TOP\">");
|
buffy.append("<TD VALIGN=\"TOP\">");
|
||||||
buffy.append("<B>").append(HTMLUtilities.escapeHTML(dataTypeManager.getName())).append(
|
buffy.append("<B>")
|
||||||
"</B><HR NOSHADE>");
|
.append(HTMLUtilities.escapeHTML(dataTypeManager.getName()))
|
||||||
|
.append("</B><HR NOSHADE>");
|
||||||
buffy.append(htmlContent);
|
buffy.append(htmlContent);
|
||||||
|
|
||||||
// horizontal spacer below the inner table in order to force a minimum width
|
// 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>");
|
||||||
|
|
||||||
buffy.append("<TD VALIGN=\"TOP\">");
|
buffy.append("<TD VALIGN=\"TOP\">");
|
||||||
buffy.append("<B>").append(HTMLUtilities.escapeHTML(sourceArchive.getName())).append(
|
buffy.append("<B>")
|
||||||
"</B><HR NOSHADE>");
|
.append(HTMLUtilities.escapeHTML(sourceArchive.getName()))
|
||||||
|
.append("</B><HR NOSHADE>");
|
||||||
|
|
||||||
buffy.append(otherContent);
|
buffy.append(otherContent);
|
||||||
|
|
||||||
|
@ -458,8 +463,8 @@ public class DataTypeSynchronizer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int transactionID = dataTypeManager.startTransaction(
|
int transactionID = dataTypeManager
|
||||||
"re-sync '" + sourceArchive.getName() + "' data types");
|
.startTransaction("re-sync '" + sourceArchive.getName() + "' data types");
|
||||||
try {
|
try {
|
||||||
reSyncOutOfSyncInTimeOnlyDataTypes();
|
reSyncOutOfSyncInTimeOnlyDataTypes();
|
||||||
fixSyncForDifferingDataTypes();
|
fixSyncForDifferingDataTypes();
|
||||||
|
|
|
@ -20,13 +20,15 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
|
import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.context.ProgramActionContext;
|
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.archive.ProjectArchive;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ProjectArchiveNode;
|
|
||||||
import ghidra.framework.main.datatable.DomainFileContext;
|
import ghidra.framework.main.datatable.DomainFileContext;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
public class DataTypesActionContext extends ProgramActionContext implements DomainFileContext {
|
public class DataTypesActionContext extends ProgramActionContext implements DomainFileContext {
|
||||||
|
@ -53,7 +55,7 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma
|
||||||
return isToolbarAction;
|
return isToolbarAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GTreeNode getSelectedNode() {
|
public GTreeNode getClickedNode() {
|
||||||
return clickedNode;
|
return clickedNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,4 +87,49 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma
|
||||||
return true;
|
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.*;
|
||||||
import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin;
|
import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin;
|
||||||
import ghidra.app.plugin.core.datamgr.actions.*;
|
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.archive.*;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
|
@ -220,6 +221,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
// key binding only
|
// key binding only
|
||||||
addLocalAction(new ClearCutAction(plugin)); // Common
|
addLocalAction(new ClearCutAction(plugin)); // Common
|
||||||
|
|
||||||
|
addLocalAction(new AssociateDataTypeAction(plugin));
|
||||||
addLocalAction(new CommitSingleDataTypeAction(plugin));
|
addLocalAction(new CommitSingleDataTypeAction(plugin));
|
||||||
addLocalAction(new UpdateSingleDataTypeAction(plugin));
|
addLocalAction(new UpdateSingleDataTypeAction(plugin));
|
||||||
addLocalAction(new RevertDataTypeAction(plugin));
|
addLocalAction(new RevertDataTypeAction(plugin));
|
||||||
|
|
|
@ -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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
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.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import docking.action.MenuData;
|
|
||||||
|
|
||||||
public class CommitAction extends SyncAction {
|
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,
|
public CommitAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
|
||||||
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm,
|
DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
|
||||||
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) {
|
boolean isEnabled) {
|
||||||
|
|
||||||
super("Commit Changes To Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
super("Commit Changes To Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
||||||
sourceArchive, isEnabled);
|
sourceArchive, isEnabled);
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.ImageIcon;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.*;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.*;
|
import ghidra.util.task.*;
|
||||||
|
|
||||||
public class DisassociateAction extends DockingAction {
|
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 SourceArchive sourceArchive;
|
||||||
private final DataTypeManager dtm;
|
private final DataTypeManager dtm;
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
@ -28,7 +28,8 @@ import docking.action.MenuData;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.*;
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
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.DataTypeArchiveGTree;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
|
@ -56,10 +57,8 @@ public class DisassociateDataTypeAction extends DockingAction {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object contextObject = context.getContextObject();
|
DataTypesActionContext dtContext = (DataTypesActionContext) context;
|
||||||
GTree gTree = (GTree) contextObject;
|
List<DataTypeNode> nodes = dtContext.getDisassociatableNodes();
|
||||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
|
||||||
List<DataTypeNode> nodes = getDisassociatableNodes(selectionPaths);
|
|
||||||
return !nodes.isEmpty();
|
return !nodes.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,11 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
|
import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
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.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import docking.action.MenuData;
|
|
||||||
|
|
||||||
public class RevertAction extends SyncAction {
|
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,
|
public RevertAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
|
||||||
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm,
|
DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
|
||||||
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) {
|
boolean isEnabled) {
|
||||||
|
|
||||||
super("Revert Datatype Changes", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
super("Revert Data Type Changes", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
||||||
sourceArchive, isEnabled);
|
sourceArchive, isEnabled);
|
||||||
setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() }));
|
setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() }));
|
||||||
setHelpLocation(new HelpLocation(plugin.getName(), getHelpTopic()));
|
setHelpLocation(new HelpLocation(plugin.getName(), getHelpTopic()));
|
|
@ -13,14 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
|
@ -29,6 +22,12 @@ import docking.action.DockingAction;
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
import docking.widgets.tree.GTree;
|
import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
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 {
|
public class RevertDataTypeAction extends DockingAction {
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@ public class RevertDataTypeAction extends DockingAction {
|
||||||
public RevertDataTypeAction(DataTypeManagerPlugin plugin) {
|
public RevertDataTypeAction(DataTypeManagerPlugin plugin) {
|
||||||
super("Revert Data Type", plugin.getName());
|
super("Revert Data Type", plugin.getName());
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
setPopupMenuData(new MenuData(new String[] { "Revert" }, "Sync"));
|
setPopupMenuData(new MenuData(new String[] { "Revert Changes" }, "Sync"));
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +67,8 @@ public class RevertDataTypeAction extends DockingAction {
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
return false;
|
return false;
|
||||||
case COMMIT:
|
case COMMIT:
|
||||||
return true;
|
|
||||||
case CONFLICT:
|
case CONFLICT:
|
||||||
|
return true;
|
||||||
case IN_SYNC:
|
case IN_SYNC:
|
||||||
case ORPHAN:
|
case ORPHAN:
|
||||||
case UPDATE:
|
case UPDATE:
|
||||||
|
@ -88,7 +87,6 @@ public class RevertDataTypeAction extends DockingAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
|
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
|
||||||
if (node instanceof DataTypeNode) {
|
|
||||||
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
||||||
DataType dataType = dataTypeNode.getDataType();
|
DataType dataType = dataTypeNode.getDataType();
|
||||||
DataTypeManager dtm = dataType.getDataTypeManager();
|
DataTypeManager dtm = dataType.getDataTypeManager();
|
||||||
|
@ -100,17 +98,15 @@ public class RevertDataTypeAction extends DockingAction {
|
||||||
}
|
}
|
||||||
DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive);
|
DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive);
|
||||||
if (sourceDTM == null) {
|
if (sourceDTM == null) {
|
||||||
Msg.showInfo(getClass(), gTree, "Revert Failed", "Source Archive not open: " +
|
Msg.showInfo(getClass(), gTree, "Revert Failed",
|
||||||
sourceArchive.getName());
|
"Source Archive not open: " + sourceArchive.getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
plugin.revert(dataType);
|
plugin.revert(dataType);
|
||||||
|
|
||||||
// Source archive data type manager was already checked for null above.
|
// Source archive data type manager was already checked for null above.
|
||||||
DataTypeSynchronizer synchronizer =
|
DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
|
||||||
new DataTypeSynchronizer(handler, dtm, sourceArchive);
|
|
||||||
synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes();
|
synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.*;
|
||||||
|
|
||||||
|
@ -83,7 +83,6 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
|
|
||||||
|
|
||||||
if (!dtm.isUpdatable()) {
|
if (!dtm.isUpdatable()) {
|
||||||
showRequiresArchiveOpenMessage(dtm.getName());
|
showRequiresArchiveOpenMessage(dtm.getName());
|
||||||
|
@ -103,6 +102,8 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
TaskBuilder.withTask(new SyncTask(synchronizer))
|
TaskBuilder.withTask(new SyncTask(synchronizer))
|
||||||
.setStatusTextAlignment(SwingConstants.LEADING)
|
.setStatusTextAlignment(SwingConstants.LEADING)
|
||||||
|
@ -248,7 +249,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||||
Set<DataTypeSyncInfo> outOfSyncInfos) {
|
Set<DataTypeSyncInfo> outOfSyncInfos) {
|
||||||
String status = getStatusMessage(outOfSyncInfos);
|
String status = getStatusMessage(outOfSyncInfos);
|
||||||
Msg.showInfo(getClass(), plugin.getTool().getToolFrame(), "No Data Type Changes",
|
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);
|
"\".\n\n" + status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +280,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuilder buf = new StringBuilder();
|
||||||
if (updateCount > 0) {
|
if (updateCount > 0) {
|
||||||
buf.append("\nNumber of UPDATES remaining: " + updateCount);
|
buf.append("\nNumber of UPDATES remaining: " + updateCount);
|
||||||
}
|
}
|
||||||
|
@ -298,7 +299,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||||
|
|
||||||
private void showNoDataTypesToSyncMessage() {
|
private void showNoDataTypesToSyncMessage() {
|
||||||
Msg.showInfo(getClass(), plugin.getTool().getToolFrame(), "No Data Type Changes",
|
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
|
@Override
|
||||||
|
@ -340,7 +341,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||||
private void autoUpdateDataTypesThatHaveNoRealChanges(DataTypeSynchronizer synchronizer,
|
private void autoUpdateDataTypesThatHaveNoRealChanges(DataTypeSynchronizer synchronizer,
|
||||||
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
|
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
|
||||||
|
|
||||||
int transactionID = dtm.startTransaction("auto sync datatypes");
|
int transactionID = dtm.startTransaction("Auto-sync data types");
|
||||||
try {
|
try {
|
||||||
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
|
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
|
||||||
dataTypeSyncInfo.syncTimes();
|
dataTypeSyncInfo.syncTimes();
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions.associate;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
|
@ -13,8 +13,11 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
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.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import docking.action.MenuData;
|
|
||||||
|
|
||||||
public class UpdateAction extends SyncAction {
|
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,
|
public UpdateAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
|
||||||
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm,
|
DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
|
||||||
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) {
|
boolean isEnabled) {
|
||||||
|
|
||||||
super("Update Datatypes From Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
super("Update Data Types From Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
|
||||||
sourceArchive, isEnabled);
|
sourceArchive, isEnabled);
|
||||||
|
|
||||||
setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() }));
|
setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() }));
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.ImageIcon;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
|
@ -34,6 +34,7 @@ import docking.widgets.OptionDialog;
|
||||||
import ghidra.app.plugin.core.compositeeditor.EditorListener;
|
import ghidra.app.plugin.core.compositeeditor.EditorListener;
|
||||||
import ghidra.app.plugin.core.compositeeditor.EditorProvider;
|
import ghidra.app.plugin.core.compositeeditor.EditorProvider;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
import ghidra.program.model.data.Enum;
|
||||||
|
@ -285,19 +286,28 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||||
deleteAction =
|
deleteAction =
|
||||||
new EnumPluginAction("Delete Enum Value", e -> editorPanel.deleteSelectedEntries());
|
new EnumPluginAction("Delete Enum Value", e -> editorPanel.deleteSelectedEntries());
|
||||||
deleteAction.setEnabled(false);
|
deleteAction.setEnabled(false);
|
||||||
deleteAction.setPopupMenuData(
|
deleteAction
|
||||||
new MenuData(new String[] { "Delete" }, DELETE_ICON, editGroup));
|
.setPopupMenuData(new MenuData(new String[] { "Delete" }, DELETE_ICON, editGroup));
|
||||||
deleteAction.setToolBarData(new ToolBarData(DELETE_ICON, editGroup));
|
deleteAction.setToolBarData(new ToolBarData(DELETE_ICON, editGroup));
|
||||||
deleteAction.setDescription("Delete the selected enum entries");
|
deleteAction.setDescription("Delete the selected enum entries");
|
||||||
|
|
||||||
applyAction = new EnumPluginAction("Apply Enum Changes", e -> applyChanges());
|
applyAction = new EnumPluginAction("Apply Enum Changes", e -> applyChanges());
|
||||||
applyAction.setEnabled(false);
|
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");
|
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, applyAction);
|
||||||
tool.addLocalAction(this, addAction);
|
tool.addLocalAction(this, addAction);
|
||||||
tool.addLocalAction(this, deleteAction);
|
tool.addLocalAction(this, deleteAction);
|
||||||
|
tool.addLocalAction(this, showEnumAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean applyChanges() {
|
private boolean applyChanges() {
|
||||||
|
@ -338,6 +348,11 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||||
return true;
|
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.
|
* 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
|
* @param editedEnum the enum to check for conflicts with
|
||||||
|
|
|
@ -27,12 +27,13 @@ import ghidra.app.plugin.core.datamgr.archive.ProgramArchive;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.*;
|
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.Task;
|
||||||
import ghidra.util.task.TaskMonitor;
|
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 {
|
public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
|
|
||||||
|
@ -48,11 +49,11 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataTypeArchiveGTree gTree;
|
private DataTypeArchiveGTree gTree;
|
||||||
private CategoryNode destinationNode;
|
private Category destinationCategory;
|
||||||
private List<GTreeNode> droppedNodes;
|
private List<GTreeNode> copyMoveNodes;
|
||||||
private Archive sourceArchive;
|
private Archive sourceArchive;
|
||||||
private Archive destinationArchive;
|
private Archive destinationArchive;
|
||||||
|
private boolean promptToAssociateTypes = true;
|
||||||
private ActionType actionType;
|
private ActionType actionType;
|
||||||
private DataTypeConflictHandler conflictHandler;
|
private DataTypeConflictHandler conflictHandler;
|
||||||
private List<String> errors = new ArrayList<>();
|
private List<String> errors = new ArrayList<>();
|
||||||
|
@ -65,19 +66,26 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List<GTreeNode> droppedNodeList,
|
public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List<GTreeNode> droppedNodeList,
|
||||||
ActionType actionType, DataTypeArchiveGTree gTree,
|
ActionType actionType, DataTypeArchiveGTree gTree,
|
||||||
DataTypeConflictHandler conflictHandler) {
|
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);
|
super("Drag/Drop", true, true, true);
|
||||||
this.destinationNode = destinationNode;
|
this.destinationCategory = destinationCategory;
|
||||||
this.droppedNodes = droppedNodeList;
|
this.copyMoveNodes = droppedNodeList;
|
||||||
this.actionType = actionType;
|
this.actionType = actionType;
|
||||||
this.gTree = gTree;
|
this.gTree = gTree;
|
||||||
this.conflictHandler = conflictHandler;
|
this.conflictHandler = conflictHandler;
|
||||||
this.destinationArchive = findArchive(destinationNode);
|
this.destinationArchive = destinationArchive;
|
||||||
|
|
||||||
GTreeNode firstNode = droppedNodes.get(0);
|
GTreeNode firstNode = copyMoveNodes.get(0);
|
||||||
this.sourceArchive = findArchive(firstNode);
|
this.sourceArchive = findArchive(firstNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Archive findArchive(GTreeNode node) {
|
private static Archive findArchive(GTreeNode node) {
|
||||||
while (node != null) {
|
while (node != null) {
|
||||||
if (node instanceof ArchiveNode) {
|
if (node instanceof ArchiveNode) {
|
||||||
return ((ArchiveNode) node).getArchive();
|
return ((ArchiveNode) node).getArchive();
|
||||||
|
@ -87,10 +95,22 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
return null;
|
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
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
int nodeCount = droppedNodes.size();
|
int nodeCount = copyMoveNodes.size();
|
||||||
filterRedundantNodes();
|
filterRedundantNodes();
|
||||||
|
|
||||||
if (checkForDifferentSourceArchives()) {
|
if (checkForDifferentSourceArchives()) {
|
||||||
|
@ -143,10 +163,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
|
|
||||||
private boolean checkForDifferentSourceArchives() {
|
private boolean checkForDifferentSourceArchives() {
|
||||||
|
|
||||||
for (GTreeNode node : droppedNodes) {
|
for (GTreeNode node : copyMoveNodes) {
|
||||||
if (sourceArchive != findArchive(node)) {
|
if (sourceArchive != findArchive(node)) {
|
||||||
Msg.showError(this, gTree, "Copy Failed",
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +178,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
DataTypeManager dtm = destinationArchive.getDataTypeManager();
|
DataTypeManager dtm = destinationArchive.getDataTypeManager();
|
||||||
int txId = dtm.startTransaction("Copy/Move Category/DataType");
|
int txId = dtm.startTransaction("Copy/Move Category/DataType");
|
||||||
try {
|
try {
|
||||||
dragNodesToCategory(monitor);
|
copyOrMoveNodesToCategory(monitor);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
dtm.endTransaction(txId, true);
|
dtm.endTransaction(txId, true);
|
||||||
|
@ -187,13 +207,13 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor.initialize(droppedNodes.size());
|
monitor.initialize(copyMoveNodes.size());
|
||||||
|
|
||||||
SourceArchive destination = destinationArchive.getDataTypeManager().getLocalSourceArchive();
|
SourceArchive destination = destinationArchive.getDataTypeManager().getLocalSourceArchive();
|
||||||
DataTypeManager dtm = sourceArchive.getDataTypeManager();
|
DataTypeManager dtm = sourceArchive.getDataTypeManager();
|
||||||
int txId = dtm.startTransaction("Associate DataTypes");
|
int txId = dtm.startTransaction("Associate Data Types");
|
||||||
try {
|
try {
|
||||||
for (GTreeNode node : droppedNodes) {
|
for (GTreeNode node : copyMoveNodes) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
if (node instanceof DataTypeNode) {
|
if (node instanceof DataTypeNode) {
|
||||||
|
@ -215,6 +235,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
|
|
||||||
private boolean promptToAssociateTypes(TaskMonitor monitor) throws CancelledException {
|
private boolean promptToAssociateTypes(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
|
if (!promptToAssociateTypes) {
|
||||||
|
return true; // do not prompt; always associate
|
||||||
|
}
|
||||||
|
|
||||||
if (!containsUnassociatedTypes(monitor)) {
|
if (!containsUnassociatedTypes(monitor)) {
|
||||||
return false; // nothing to associate
|
return false; // nothing to associate
|
||||||
}
|
}
|
||||||
|
@ -230,8 +254,8 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
private boolean containsUnassociatedTypes(TaskMonitor monitor) throws CancelledException {
|
private boolean containsUnassociatedTypes(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
monitor.setMessage("Checking for types to associate");
|
monitor.setMessage("Checking for types to associate");
|
||||||
monitor.initialize(droppedNodes.size());
|
monitor.initialize(copyMoveNodes.size());
|
||||||
for (GTreeNode node : droppedNodes) {
|
for (GTreeNode node : copyMoveNodes) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
if (node instanceof DataTypeNode) {
|
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.setMessage("Drag/Drop Categories/Data Types");
|
||||||
monitor.initialize(droppedNodes.size());
|
monitor.initialize(copyMoveNodes.size());
|
||||||
|
|
||||||
Category toCategory = getCategory(destinationNode);
|
Category toCategory = destinationCategory;
|
||||||
for (GTreeNode node : droppedNodes) {
|
for (GTreeNode node : copyMoveNodes) {
|
||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
break;
|
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 dtName = dataType.getName();
|
||||||
String baseName = getBaseName(dtName);
|
String baseName = getBaseName(dtName);
|
||||||
String copyName = getNextCopyName(destinationCategory, baseName);
|
String copyName = getNextCopyName(toCategory, baseName);
|
||||||
try {
|
try {
|
||||||
dataType.setName(copyName);
|
dataType.setName(copyName);
|
||||||
}
|
}
|
||||||
|
@ -386,12 +410,12 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
return baseName;
|
return baseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getNextCopyName(Category destinationCategory, String baseName) {
|
String getNextCopyName(Category toCategory, String baseName) {
|
||||||
|
|
||||||
String format = "Copy_%d_of_" + baseName;
|
String format = "Copy_%d_of_" + baseName;
|
||||||
for (int i = 1; i < 100; i++) {
|
for (int i = 1; i < 100; i++) {
|
||||||
String copyName = String.format(format, i);
|
String copyName = String.format(format, i);
|
||||||
if (destinationCategory.getDataType(copyName) == null) {
|
if (toCategory.getDataType(copyName) == null) {
|
||||||
return copyName;
|
return copyName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,14 +424,14 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
return String.format(format, System.currentTimeMillis());
|
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) {
|
if (node instanceof DataTypeNode) {
|
||||||
DataType dataType = ((DataTypeNode) node).getDataType();
|
DataType dataType = ((DataTypeNode) node).getDataType();
|
||||||
moveDataType(destinationCategory, dataType);
|
moveDataType(toCategory, dataType);
|
||||||
}
|
}
|
||||||
else if (node instanceof CategoryNode) {
|
else if (node instanceof CategoryNode) {
|
||||||
Category category = ((CategoryNode) node).getCategory();
|
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);
|
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
|
* 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
|
* 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() {
|
private int askToAssociateDataTypes() {
|
||||||
return OptionDialog.showYesNoCancelDialog(gTree, "Associate DataTypes?",
|
return OptionDialog.showYesNoCancelDialog(gTree, "Associate Data Types?",
|
||||||
"Do you want to associate local datatypes with the target archive?");
|
"Do you want to associate local data types with the target archive?");
|
||||||
}
|
}
|
||||||
|
|
||||||
// filters out nodes with categories in their path
|
// filters out nodes with categories in their path
|
||||||
private void filterRedundantNodes() {
|
private void filterRedundantNodes() {
|
||||||
|
|
||||||
Set<GTreeNode> nodeSet = new HashSet<>(droppedNodes);
|
Set<GTreeNode> nodeSet = new HashSet<>(copyMoveNodes);
|
||||||
List<GTreeNode> filteredList = new ArrayList<>();
|
List<GTreeNode> filteredList = new ArrayList<>();
|
||||||
|
|
||||||
for (GTreeNode node : nodeSet) {
|
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) {
|
private boolean containsAncestor(Set<GTreeNode> nodeSet, GTreeNode node) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
|
||||||
import ghidra.app.services.DataTypeQueryService;
|
import ghidra.app.services.DataTypeQueryService;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
import ghidra.program.model.data.Enum;
|
||||||
|
@ -150,6 +149,7 @@ public class DataTypeUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the root folder icon.
|
* Returns the root folder icon.
|
||||||
|
* @param expanded true to use the expanded icon; false to use the collapsed icon.
|
||||||
* @return the root folder icon.
|
* @return the root folder icon.
|
||||||
*/
|
*/
|
||||||
public static Icon getRootIcon(boolean expanded) {
|
public static Icon getRootIcon(boolean expanded) {
|
||||||
|
@ -217,36 +217,6 @@ public class DataTypeUtils {
|
||||||
return closedArchiveFolderIcon;
|
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.
|
* Returns the BuiltIn icon.
|
||||||
*
|
*
|
||||||
|
@ -320,10 +290,10 @@ public class DataTypeUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a sorted list of {@link DataType}s that have names which start with the given
|
* Returns a sorted list of {@link DataType}s that have names which start with the given search
|
||||||
* search string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}.
|
* string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}.
|
||||||
*
|
*
|
||||||
@param searchString The name of the DataTypes to match.
|
* @param searchString The name of the DataTypes to match.
|
||||||
* @param dataService The service from which the data types will be taken.
|
* @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.
|
* string.
|
||||||
|
@ -350,10 +320,13 @@ public class DataTypeUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the give text to prepare it or use in searching for data types. Clients should
|
* 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
|
* call this method to make sure that the given text is suitable for use when searching the
|
||||||
* the data type values returned by {@link #getExactMatchingDataTypes(String, DataTypeManagerService)}
|
* data type values returned by
|
||||||
* and {@link #getStartsWithMatchingDataTypes(String, DataTypeManagerService)}.
|
* {@link #getExactMatchingDataTypes(String, DataTypeQueryService)} and
|
||||||
|
* {@link #getStartsWithMatchingDataTypes(String, DataTypeQueryService)}.
|
||||||
|
* @param searchText the search text
|
||||||
|
* @return the updated text
|
||||||
*/
|
*/
|
||||||
public static String prepareSearchText(String searchText) {
|
public static String prepareSearchText(String searchText) {
|
||||||
return searchText.replaceAll(" ", "");
|
return searchText.replaceAll(" ", "");
|
||||||
|
@ -376,12 +349,14 @@ public class DataTypeUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base data type for the specified data type.
|
* 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
|
* <p>For example, the base data type for Word*[5] is Word. For a pointer, the base data type
|
||||||
* or the pointer itself if it is pointing at nothing.
|
* 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]
|
* <p>If "INT" is a typedef on a "dword" then INT[7][3] would have a base data type of dword.
|
||||||
* you should call getNamedBasedDataType(DataType) instead.
|
* 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.
|
* @param dt the data type whose base data type is to be determined.
|
||||||
* @return the base data type.
|
* @return the base data type.
|
||||||
*/
|
*/
|
||||||
|
@ -409,15 +384,17 @@ public class DataTypeUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the named base data type for the specified data type.
|
* Get the named base data type for the specified data type. This method intentionally does
|
||||||
* This method intentionally does not drill down into typedefs.
|
* 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
|
* <p>For example, the named base data type for Word*[5] is Word. For a pointer, the named
|
||||||
* or the pointer itself if it is pointing at nothing.
|
* base data type is the type being pointed to or the pointer itself if it is pointing at
|
||||||
* <br>If "INT" is a typedef on a "dword", then INT[7][3] would
|
* nothing.
|
||||||
* have a named base data type of INT.
|
*
|
||||||
* If you wanted to get the dword from INT[7][3]
|
* <p>If "INT" is a typedef on a "dword", then INT[7][3] would have a named base data type of
|
||||||
* you should call getBasedDataType(DataType) instead.
|
* 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.
|
* @param dt the data type whose named base data type is to be determined.
|
||||||
* @return the base data type.
|
* @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
|
* Create a copy of the chain of data types that eventually lead to a named
|
||||||
* data type.
|
* data type.
|
||||||
* <p>
|
* <p>
|
||||||
* Returns a {@link DataType#copy(DataTypeManager) copy()} of the first named data
|
* Returns a {@link DataType#copy(DataTypeManager) copy()} of the first named data type found
|
||||||
* type found in the pointer / array type chain, and returns an identical chain of
|
* in the pointer / array type chain, and returns an identical chain of pointer / arrays up to
|
||||||
* pointer / arrays up to the copied named type.
|
* the copied named type.
|
||||||
* <p>
|
*
|
||||||
* @param dataType data type to be copied
|
* @param dataType data type to be copied
|
||||||
* @param dtm data type manager
|
* @param dtm data type manager
|
||||||
* @return deep copy of dataType
|
* @return deep copy of dataType
|
||||||
|
@ -475,39 +452,16 @@ public class DataTypeUtils {
|
||||||
msg = "The Program is not modifiable!\n";
|
msg = "The Program is not modifiable!\n";
|
||||||
}
|
}
|
||||||
else if (dtm instanceof FileArchiveBasedDataTypeManager) {
|
else if (dtm instanceof FileArchiveBasedDataTypeManager) {
|
||||||
msg =
|
msg = "The archive file is not modifiable!\nYou must open the archive for editing\n" +
|
||||||
"The archive file is not modifiable!\nYou must open the archive for editing\n before performing this operation.";
|
"before performing this operation.\n" + dtm.getName();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
msg =
|
msg = "The project archive is not modifiable!\nYou must check out the archive\n" +
|
||||||
"The project archive is not modifiable!\nYou must check out the archive\n before performing this operation.";
|
"before performing this operation.\n" + dtm.getName();
|
||||||
}
|
}
|
||||||
Msg.showInfo(DataTypeUtils.class, parent, title, msg);
|
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;
|
package ghidra.app.plugin.core.compositeeditor;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class StructureEditorLockedEnablementTest extends AbstractStructureEditorTest {
|
public class StructureEditorLockedEnablementTest extends AbstractStructureEditorTest {
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||||
try {
|
try {
|
||||||
DataTypeManager dataTypeManager = cat.getDataTypeManager();
|
DataTypeManager dataTypeManager = cat.getDataTypeManager();
|
||||||
if (dt.getDataTypeManager() != dataTypeManager) {
|
if (dt.getDataTypeManager() != dataTypeManager) {
|
||||||
dt = (Structure) dt.clone(dataTypeManager);
|
dt = dt.clone(dataTypeManager);
|
||||||
}
|
}
|
||||||
CategoryPath categoryPath = cat.getCategoryPath();
|
CategoryPath categoryPath = cat.getCategoryPath();
|
||||||
if (!dt.getCategoryPath().equals(categoryPath)) {
|
if (!dt.getCategoryPath().equals(categoryPath)) {
|
||||||
|
@ -69,7 +68,7 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||||
Structure desiredEmptyStructure = emptyStructure;
|
Structure desiredEmptyStructure = emptyStructure;
|
||||||
int txID = program.startTransaction("Removing emptyStruct from DTM.");
|
int txID = program.startTransaction("Removing emptyStruct from DTM.");
|
||||||
try {
|
try {
|
||||||
programDTM.remove(emptyStructure, TaskMonitorAdapter.DUMMY_MONITOR);
|
programDTM.remove(emptyStructure, TaskMonitor.DUMMY);
|
||||||
if (emptyStructure.getDataTypeManager() != catDTM) {
|
if (emptyStructure.getDataTypeManager() != catDTM) {
|
||||||
desiredEmptyStructure = (Structure) emptyStructure.copy(catDTM);
|
desiredEmptyStructure = (Structure) emptyStructure.copy(catDTM);
|
||||||
desiredEmptyStructure.setCategoryPath(pgmTestCat.getCategoryPath());
|
desiredEmptyStructure.setCategoryPath(pgmTestCat.getCategoryPath());
|
||||||
|
@ -154,7 +153,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||||
for (CompositeEditorTableAction action : actions) {
|
for (CompositeEditorTableAction action : actions) {
|
||||||
if ((action instanceof EditFieldAction) || (action instanceof AddBitFieldAction) ||
|
if ((action instanceof EditFieldAction) || (action instanceof AddBitFieldAction) ||
|
||||||
(action instanceof InsertUndefinedAction) || (action instanceof PointerAction) ||
|
(action instanceof InsertUndefinedAction) || (action instanceof PointerAction) ||
|
||||||
(action instanceof HexNumbersAction)) {
|
(action instanceof HexNumbersAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else if (action instanceof FavoritesAction) {
|
else if (action instanceof FavoritesAction) {
|
||||||
|
@ -203,7 +203,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||||
(action instanceof DuplicateMultipleAction) || (action instanceof ClearAction) ||
|
(action instanceof DuplicateMultipleAction) || (action instanceof ClearAction) ||
|
||||||
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
||||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||||
(action instanceof CreateInternalStructureAction)) {
|
(action instanceof CreateInternalStructureAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -232,7 +233,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||||
(action instanceof MoveDownAction) || (action instanceof ClearAction) ||
|
(action instanceof MoveDownAction) || (action instanceof ClearAction) ||
|
||||||
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
||||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||||
(action instanceof CreateInternalStructureAction)) {
|
(action instanceof CreateInternalStructureAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -261,7 +263,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||||
(action instanceof DuplicateAction) ||
|
(action instanceof DuplicateAction) ||
|
||||||
(action instanceof DuplicateMultipleAction) || (action instanceof ArrayAction) ||
|
(action instanceof DuplicateMultipleAction) || (action instanceof ArrayAction) ||
|
||||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||||
(action instanceof CreateInternalStructureAction)) {
|
(action instanceof CreateInternalStructureAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -289,7 +292,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||||
(action instanceof MoveDownAction) || (action instanceof ClearAction) ||
|
(action instanceof MoveDownAction) || (action instanceof ClearAction) ||
|
||||||
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
||||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||||
(action instanceof CreateInternalStructureAction)) {
|
(action instanceof CreateInternalStructureAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -313,7 +317,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
|
||||||
checkEnablement(action, len <= numBytes);
|
checkEnablement(action, len <= numBytes);
|
||||||
}
|
}
|
||||||
else if ((action instanceof CycleGroupAction) || (action instanceof ClearAction) ||
|
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);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -118,7 +118,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||||
if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) ||
|
if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) ||
|
||||||
(action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) ||
|
(action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) ||
|
||||||
(action instanceof AddBitFieldAction) || (action instanceof PointerAction) ||
|
(action instanceof AddBitFieldAction) || (action instanceof PointerAction) ||
|
||||||
(action instanceof HexNumbersAction)) {
|
(action instanceof HexNumbersAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -151,7 +152,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||||
(action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) ||
|
(action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) ||
|
||||||
(action instanceof ArrayAction) || (action instanceof PointerAction) ||
|
(action instanceof ArrayAction) || (action instanceof PointerAction) ||
|
||||||
(action instanceof HexNumbersAction) ||
|
(action instanceof HexNumbersAction) ||
|
||||||
(action instanceof CreateInternalStructureAction)) {
|
(action instanceof CreateInternalStructureAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else if (action instanceof FavoritesAction) {
|
else if (action instanceof FavoritesAction) {
|
||||||
|
@ -190,7 +192,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||||
(action instanceof MoveUpAction) || (action instanceof ClearAction) ||
|
(action instanceof MoveUpAction) || (action instanceof ClearAction) ||
|
||||||
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
(action instanceof DeleteAction) || (action instanceof ArrayAction) ||
|
||||||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
(action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
|
||||||
(action instanceof CreateInternalStructureAction)) {
|
(action instanceof CreateInternalStructureAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else if (action instanceof FavoritesAction) {
|
else if (action instanceof FavoritesAction) {
|
||||||
|
@ -230,7 +233,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||||
(action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) ||
|
(action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) ||
|
||||||
(action instanceof ArrayAction) || (action instanceof PointerAction) ||
|
(action instanceof ArrayAction) || (action instanceof PointerAction) ||
|
||||||
(action instanceof HexNumbersAction) ||
|
(action instanceof HexNumbersAction) ||
|
||||||
(action instanceof CreateInternalStructureAction)) {
|
(action instanceof CreateInternalStructureAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else if (action instanceof FavoritesAction) {
|
else if (action instanceof FavoritesAction) {
|
||||||
|
@ -269,7 +273,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||||
if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) ||
|
if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) ||
|
||||||
(action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) ||
|
(action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) ||
|
||||||
(action instanceof AddBitFieldAction) || (action instanceof PointerAction) ||
|
(action instanceof AddBitFieldAction) || (action instanceof PointerAction) ||
|
||||||
(action instanceof HexNumbersAction)) {
|
(action instanceof HexNumbersAction) ||
|
||||||
|
(action instanceof ShowDataTypeInTreeAction)) {
|
||||||
checkEnablement(action, true);
|
checkEnablement(action, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -110,9 +110,9 @@ public class DataTypeTestUtils {
|
||||||
|
|
||||||
public static ArchiveNode openArchive(String archiveName, boolean checkout,
|
public static ArchiveNode openArchive(String archiveName, boolean checkout,
|
||||||
DataTypeManagerPlugin plugin) throws Exception {
|
DataTypeManagerPlugin plugin) throws Exception {
|
||||||
ArchiveNode openArchive = openArchive(archiveName, checkout, false, plugin);
|
ArchiveNode archiveNode = openArchive(archiveName, checkout, false, plugin);
|
||||||
waitForTree(plugin);
|
waitForTree(plugin);
|
||||||
return openArchive;
|
return archiveNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void waitForTree(DataTypeManagerPlugin plugin) {
|
private static void waitForTree(DataTypeManagerPlugin plugin) {
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class DeveloperDumpAllTypesScript extends GhidraScript {
|
||||||
}
|
}
|
||||||
String userChoice =
|
String userChoice =
|
||||||
OptionDialog.showInputChoiceDialog(null, "Choose a Data Type Manager or Cancel",
|
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) {
|
if (userChoice == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,8 @@ import docking.widgets.GComponent;
|
||||||
* @param <E> the item type
|
* @param <E> the item type
|
||||||
*/
|
*/
|
||||||
public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||||
private ArrayList<ActionListener> listeners = new ArrayList<>();
|
private List<ActionListener> listeners = new ArrayList<>();
|
||||||
private ArrayList<DocumentListener> docListeners = new ArrayList<>();
|
private List<DocumentListener> docListeners = new ArrayList<>();
|
||||||
private boolean setSelectedFlag = false;
|
private boolean setSelectedFlag = false;
|
||||||
|
|
||||||
private boolean forwardEnter;
|
private boolean forwardEnter;
|
||||||
|
@ -70,7 +70,6 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
*/
|
*/
|
||||||
public GhidraComboBox() {
|
public GhidraComboBox() {
|
||||||
super();
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,6 +255,13 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||||
model.addElement(obj);
|
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) {
|
public boolean containsItem(E obj) {
|
||||||
DefaultComboBoxModel<E> model = (DefaultComboBoxModel<E>) getModel();
|
DefaultComboBoxModel<E> model = (DefaultComboBoxModel<E>) getModel();
|
||||||
return model.getIndexOf(obj) != -1;
|
return model.getIndexOf(obj) != -1;
|
||||||
|
|
|
@ -28,7 +28,6 @@ import docking.widgets.label.GHtmlLabel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dialog that has text fields to get user input.
|
* A dialog that has text fields to get user input.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class InputWithChoicesDialog extends DialogComponentProvider {
|
public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
|
@ -37,19 +36,17 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
private boolean allowEdits;
|
private boolean allowEdits;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a provider for a generic input dialog with the specified title,
|
* Creates a provider for a generic input dialog with the specified title, a label and a
|
||||||
* a label and a editable comboBox pre-populated with selectable values. The user
|
* editable comboBox pre-populated with selectable values. The user can check the value of
|
||||||
* can check the value of {@link #isCanceled()} to know whether or not
|
* {@link #isCanceled()} to know whether or not the user canceled the operation. To get the
|
||||||
* the user canceled the operation. To get the user selected value use the
|
* user selected value use the {@link #getValue()} value(s) entered by the user. If the user
|
||||||
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
|
* cancelled the operation, then null will be returned from {@link #getValue()}.
|
||||||
* null will be returned from <code>getValue()</code>.
|
|
||||||
* <P>
|
|
||||||
*
|
*
|
||||||
* @param dialogTitle used as the name of the dialog's title bar
|
* @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 label value to use for the label of the text field
|
||||||
* @param optionValues values to populate the combo box
|
* @param optionValues values to populate the combo box
|
||||||
* @param initialValue the initial value - can be null
|
* @param initialValue the initial value; may be null
|
||||||
* @param messageIcon the icon to display on the dialog--can be null
|
* @param messageIcon the icon to display on the dialog; may be null
|
||||||
*/
|
*/
|
||||||
public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues,
|
public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues,
|
||||||
String initialValue, Icon messageIcon) {
|
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,
|
* Creates a provider for a generic input dialog with the specified title, a label and a
|
||||||
* a label and a editable comboBox pre-populated with selectable values. The user
|
* editable comboBox pre-populated with selectable values. The user can check the value of
|
||||||
* can check the value of {@link #isCanceled()} to know whether or not
|
* {@link #isCanceled()} to know whether or not the user canceled the operation. To get the
|
||||||
* the user canceled the operation. To get the user selected value use the
|
* user selected value use the {@link #getValue()} value(s) entered by the user. If the user
|
||||||
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
|
* cancelled the operation, then null will be returned from {@link #getValue()}.
|
||||||
* null will be returned from <code>getValue()</code>.
|
|
||||||
* <P>
|
|
||||||
*
|
*
|
||||||
* @param dialogTitle used as the name of the dialog's title bar
|
* @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 label value to use for the label of the text field
|
||||||
* @param optionValues values to populate the combo box
|
* @param optionValues values to populate the combo box
|
||||||
* @param initialValue the initial value - can be null
|
* @param initialValue the initial value; may be null
|
||||||
* @param allowEdits true allows the user to add custom entries to the combo box by entering text
|
* @param allowEdits true allows the user to add custom entries by entering text
|
||||||
* @param messageIcon the icon to display on the dialog--can be null
|
* @param messageIcon the icon to display on the dialog; may be null
|
||||||
*/
|
*/
|
||||||
public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues,
|
public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues,
|
||||||
String initialValue, boolean allowEdits, Icon messageIcon) {
|
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,
|
private void buildMainPanel(String labelText, String[] optionValues, String initialValue,
|
||||||
Icon messageIcon) {
|
Icon messageIcon) {
|
||||||
|
@ -168,13 +163,15 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if this dialog is canceled.
|
* Returns if this dialog is canceled.
|
||||||
|
* @return true if canceled
|
||||||
*/
|
*/
|
||||||
public boolean isCanceled() {
|
public boolean isCanceled() {
|
||||||
return 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() {
|
public String getValue() {
|
||||||
if (isCanceled) {
|
if (isCanceled) {
|
||||||
|
@ -192,8 +189,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
/**
|
/**
|
||||||
* Set the current choice to value.
|
* Set the current choice to value.
|
||||||
* @param value updated choice
|
* @param value updated choice
|
||||||
* @throws NoSuchElementException if choice does not permit edits and value is
|
* @throws NoSuchElementException if edits not permitted and value is not a valid choice
|
||||||
* not a valid choice.
|
|
||||||
*/
|
*/
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
combo.setSelectedItem(value);
|
combo.setSelectedItem(value);
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
|
@ -916,6 +917,13 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
return paths;
|
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) {
|
public boolean isExpanded(TreePath treePath) {
|
||||||
return tree.isExpanded(treePath);
|
return tree.isExpanded(treePath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package generic.test;
|
package generic.test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
import static org.hamcrest.core.StringContains.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
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,
|
private static String printListFailureMessage(String message, List<?> expected,
|
||||||
List<?> actual) {
|
List<?> actual) {
|
||||||
|
|
||||||
|
|
|
@ -416,6 +416,14 @@ public interface DataTypeManager {
|
||||||
*/
|
*/
|
||||||
public void findEnumValueNames(long value, Set<String> enumValueNames);
|
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);
|
public DataType getDataType(SourceArchive sourceArchive, UniversalID datatypeID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -529,6 +537,7 @@ public interface DataTypeManager {
|
||||||
* @deprecated the method {@link DataType#getParents()} should be used instead.
|
* @deprecated the method {@link DataType#getParents()} should be used instead.
|
||||||
* Use of {@link Set} implementations for containing DataTypes is also inefficient.
|
* Use of {@link Set} implementations for containing DataTypes is also inefficient.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Set<DataType> getDataTypesContaining(DataType dataType);
|
public Set<DataType> getDataTypesContaining(DataType dataType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -227,7 +227,7 @@ public class StructureFactory {
|
||||||
return newStructure;
|
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
|
// dataLength number of components
|
||||||
private static void initializeStructureFromContext(Structure structure,
|
private static void initializeStructureFromContext(Structure structure,
|
||||||
DataTypeProviderContext context, int dataLength) {
|
DataTypeProviderContext context, int dataLength) {
|
||||||
|
@ -249,8 +249,8 @@ public class StructureFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (DataTypeComponent dataComp : dataComps) {
|
for (DataTypeComponent dataComp : dataComps) {
|
||||||
structure.add(dataComp.getDataType(), dataComp.getLength(),
|
structure.add(dataComp.getDataType(), dataComp.getLength(), dataComp.getFieldName(),
|
||||||
dataComp.getFieldName(), dataComp.getComment());
|
dataComp.getComment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|