mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-3069 - Refactored the 'Create Structure from Selection' action to
work around a focus issue seen by some users
This commit is contained in:
parent
3e03a86117
commit
f58a9035f7
3 changed files with 60 additions and 39 deletions
|
@ -21,10 +21,7 @@ import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.exception.UsrException;
|
import ghidra.util.exception.UsrException;
|
||||||
import ghidra.util.task.TaskLauncher;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,16 +48,20 @@ public class CreateInternalStructureAction extends CompositeEditorTableAction {
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
int[] selectedComponentRows = model.getSelectedComponentRows();
|
int[] selectedComponentRows = model.getSelectedComponentRows();
|
||||||
boolean hasComponentSelection = model.hasComponentSelection();
|
boolean hasComponentSelection = model.hasComponentSelection();
|
||||||
boolean contiguousComponentSelection = model.isContiguousComponentSelection();
|
boolean hasContiguousSelection = model.isContiguousComponentSelection();
|
||||||
if (hasComponentSelection && contiguousComponentSelection &&
|
if (selectedComponentRows.length == 0) {
|
||||||
(selectedComponentRows.length > 0)) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Arrays.sort(selectedComponentRows);
|
if (!hasComponentSelection || !hasContiguousSelection) {
|
||||||
int numComponents = model.getNumComponents();
|
return;
|
||||||
int maxRow = selectedComponentRows[selectedComponentRows.length - 1];
|
}
|
||||||
if (maxRow < numComponents) {
|
|
||||||
TaskLauncher.launchModal(getName(), this::doCreate);
|
Arrays.sort(selectedComponentRows);
|
||||||
}
|
int numComponents = model.getNumComponents();
|
||||||
|
int maxRow = selectedComponentRows[selectedComponentRows.length - 1];
|
||||||
|
if (maxRow < numComponents) {
|
||||||
|
createStructure();
|
||||||
}
|
}
|
||||||
|
|
||||||
requestTableFocus();
|
requestTableFocus();
|
||||||
|
@ -72,12 +73,9 @@ public class CreateInternalStructureAction extends CompositeEditorTableAction {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doCreate(TaskMonitor monitor) {
|
private void createStructure() {
|
||||||
try {
|
try {
|
||||||
((StructureEditorModel) model).createInternalStructure(monitor);
|
((StructureEditorModel) model).createInternalStructure();
|
||||||
}
|
|
||||||
catch (CancelledException e) {
|
|
||||||
// user cancelled
|
|
||||||
}
|
}
|
||||||
catch (UsrException e) {
|
catch (UsrException e) {
|
||||||
model.setStatus(e.getMessage(), true);
|
model.setStatus(e.getMessage(), true);
|
||||||
|
|
|
@ -30,6 +30,7 @@ import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.InsufficientBytesException;
|
import ghidra.program.model.lang.InsufficientBytesException;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
|
import ghidra.util.task.TaskLauncher;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
class StructureEditorModel extends CompEditorModel {
|
class StructureEditorModel extends CompEditorModel {
|
||||||
|
@ -687,7 +688,7 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldRange currentRange = getSelectedRangeContaining(currentIndex);
|
FieldRange currentRange = getSelectedRangeContaining(currentIndex);
|
||||||
// if the index isn't in the selection or is in a range of only
|
// if the index isn't in the selection or is in a range of only
|
||||||
// one row then we want to handle it the same.
|
// one row then we want to handle it the same.
|
||||||
boolean isOneComponent =
|
boolean isOneComponent =
|
||||||
(currentRange == null) || (currentRange.getStart().getIndex().intValue() +
|
(currentRange == null) || (currentRange.getStart().getIndex().intValue() +
|
||||||
|
@ -786,10 +787,10 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the maximum number of bytes available for a data type that is added at the indicated
|
* Gets the maximum number of bytes available for a data type that is added at the indicated
|
||||||
* index. This can vary based on whether or not it is in a selection.
|
* index. This can vary based on whether or not it is in a selection.
|
||||||
* <br>In unlocked mode, the size is unrestricted when no selection or single row selection.
|
* <br>In unlocked mode, the size is unrestricted when no selection or single row selection.
|
||||||
* Multi-row selection always limits the size.
|
* Multi-row selection always limits the size.
|
||||||
* <br>In locked mode, single row selection is limited to selected row plus undefined bytes
|
* <br>In locked mode, single row selection is limited to selected row plus undefined bytes
|
||||||
* following it that can be absorbed.
|
* following it that can be absorbed.
|
||||||
*
|
*
|
||||||
* @param rowIndex index of the row in the editor's composite data type table.
|
* @param rowIndex index of the row in the editor's composite data type table.
|
||||||
|
@ -803,7 +804,7 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
}
|
}
|
||||||
DataTypeComponent comp = getComponent(rowIndex);
|
DataTypeComponent comp = getComponent(rowIndex);
|
||||||
FieldRange currentRange = getSelectedRangeContaining(rowIndex);
|
FieldRange currentRange = getSelectedRangeContaining(rowIndex);
|
||||||
// if the index isn't in the selection or is in a range of only
|
// if the index isn't in the selection or is in a range of only
|
||||||
// one row then we want to handle it the same.
|
// one row then we want to handle it the same.
|
||||||
boolean isOneComponent =
|
boolean isOneComponent =
|
||||||
(currentRange == null) || (currentRange.getStart().getIndex().intValue() +
|
(currentRange == null) || (currentRange.getStart().getIndex().intValue() +
|
||||||
|
@ -825,9 +826,9 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the maximum number of bytes available for a new data type that
|
* Gets the maximum number of bytes available for a new data type that
|
||||||
* will replace the current data type at the indicated index.
|
* will replace the current data type at the indicated index.
|
||||||
* If there isn't a component with the indicated index, the max length
|
* If there isn't a component with the indicated index, the max length
|
||||||
* will be determined by the lock mode.
|
* will be determined by the lock mode.
|
||||||
*
|
*
|
||||||
* @param currentIndex index of the component in the structure.
|
* @param currentIndex index of the component in the structure.
|
||||||
|
@ -1096,12 +1097,12 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
return !viewComposite.isPackingEnabled();
|
return !viewComposite.isPackingEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createInternalStructure(TaskMonitor monitor)
|
void createInternalStructure() throws UsrException {
|
||||||
throws InvalidDataTypeException, UsrException {
|
|
||||||
|
|
||||||
if (selection.getNumRanges() != 1) {
|
if (selection.getNumRanges() != 1) {
|
||||||
throw new UsrException("Can only create structure on a contiguous selection.");
|
throw new UsrException("Can only create structure on a contiguous selection.");
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldRange fieldRange = selection.getFieldRange(0);
|
FieldRange fieldRange = selection.getFieldRange(0);
|
||||||
int minRow = fieldRange.getStart().getIndex().intValue();
|
int minRow = fieldRange.getStart().getIndex().intValue();
|
||||||
int maxRow = fieldRange.getEnd().getIndex().intValue();
|
int maxRow = fieldRange.getEnd().getIndex().intValue();
|
||||||
|
@ -1120,29 +1121,44 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
if (isEditingField()) {
|
if (isEditingField()) {
|
||||||
endFieldEditing();
|
endFieldEditing();
|
||||||
}
|
}
|
||||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
|
||||||
|
DataTypeManager originalDtm = getOriginalDataTypeManager();
|
||||||
String baseName = "struct";
|
String baseName = "struct";
|
||||||
CategoryPath originalCategoryPath = getOriginalCategoryPath();
|
CategoryPath originalCategoryPath = getOriginalCategoryPath();
|
||||||
String uniqueName = viewDTM.getUniqueName(originalCategoryPath, baseName);
|
String uniqueName = viewDTM.getUniqueName(originalCategoryPath, baseName);
|
||||||
DataType conflictingDt = originalDTM.getDataType(originalCategoryPath, uniqueName);
|
DataType conflictingDt = originalDtm.getDataType(originalCategoryPath, uniqueName);
|
||||||
while (conflictingDt != null) {
|
while (conflictingDt != null) {
|
||||||
// pull the data type into the view data type manager with the conflicting name.
|
// pull the data type into the view data type manager with the conflicting name.
|
||||||
viewDTM.resolve(conflictingDt, DataTypeConflictHandler.DEFAULT_HANDLER);
|
viewDTM.resolve(conflictingDt, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||||
// Try to get another unique name.
|
// Try to get another unique name.
|
||||||
uniqueName = viewDTM.getUniqueName(originalCategoryPath, baseName);
|
uniqueName = viewDTM.getUniqueName(originalCategoryPath, baseName);
|
||||||
conflictingDt = originalDTM.getDataType(originalCategoryPath, uniqueName);
|
conflictingDt = originalDtm.getDataType(originalCategoryPath, uniqueName);
|
||||||
}
|
}
|
||||||
|
|
||||||
String specifiedName =
|
String specifiedName =
|
||||||
showNameDialog(uniqueName, originalCategoryPath, viewComposite.getName(), originalDTM);
|
showNameDialog(uniqueName, originalCategoryPath, viewComposite.getName(), originalDtm);
|
||||||
if (specifiedName == null) {
|
if (specifiedName == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uniqueName = specifiedName;
|
|
||||||
|
TaskLauncher.launchModal("Create Structure", monitor -> {
|
||||||
|
try {
|
||||||
|
doCreateInternalStructure(originalDtm, originalCategoryPath, specifiedName,
|
||||||
|
monitor);
|
||||||
|
}
|
||||||
|
catch (UsrException e) {
|
||||||
|
setStatus(e.getMessage(), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doCreateInternalStructure(DataTypeManager dtm, CategoryPath categoryPath,
|
||||||
|
String name, TaskMonitor monitor)
|
||||||
|
throws InvalidDataTypeException, UsrException {
|
||||||
|
|
||||||
int length = 0;
|
int length = 0;
|
||||||
final StructureDataType structureDataType =
|
StructureDataType structureDataType =
|
||||||
new StructureDataType(originalCategoryPath, uniqueName, length, originalDTM);
|
new StructureDataType(categoryPath, name, length, dtm);
|
||||||
|
|
||||||
// adopt pack setting from current structure
|
// adopt pack setting from current structure
|
||||||
structureDataType.setPackingEnabled(isPackingEnabled());
|
structureDataType.setPackingEnabled(isPackingEnabled());
|
||||||
|
@ -1150,6 +1166,10 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
structureDataType.setExplicitPackingValue(getExplicitPackingValue());
|
structureDataType.setExplicitPackingValue(getExplicitPackingValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FieldRange fieldRange = selection.getFieldRange(0);
|
||||||
|
int minRow = fieldRange.getStart().getIndex().intValue();
|
||||||
|
int maxRow = fieldRange.getEnd().getIndex().intValue();
|
||||||
|
|
||||||
// Get data type components to make into structure.
|
// Get data type components to make into structure.
|
||||||
DataTypeComponent firstDtc = null;
|
DataTypeComponent firstDtc = null;
|
||||||
DataTypeComponent lastDtc = null;
|
DataTypeComponent lastDtc = null;
|
||||||
|
@ -1165,7 +1185,6 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
|
|
||||||
DataType dt = component.getDataType();
|
DataType dt = component.getDataType();
|
||||||
int compLength = component.getLength();
|
int compLength = component.getLength();
|
||||||
|
|
||||||
length += compLength;
|
length += compLength;
|
||||||
|
|
||||||
if (!structureDataType.isPackingEnabled() && component.isBitFieldComponent()) {
|
if (!structureDataType.isPackingEnabled() && component.isBitFieldComponent()) {
|
||||||
|
@ -1182,6 +1201,7 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
|
|
||||||
lastDtc = component;
|
lastDtc = component;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataType addedDataType = createDataTypeInOriginalDTM(structureDataType);
|
DataType addedDataType = createDataTypeInOriginalDTM(structureDataType);
|
||||||
if (viewComposite.isPackingEnabled()) {
|
if (viewComposite.isPackingEnabled()) {
|
||||||
deleteSelectedComponents();
|
deleteSelectedComponents();
|
||||||
|
@ -1207,7 +1227,7 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String showNameDialog(final String defaultName, final CategoryPath catPath,
|
private String showNameDialog(final String defaultName, final CategoryPath catPath,
|
||||||
final String parentStructureName, final DataTypeManager applyDTM) {
|
final String parentStructureName, final DataTypeManager applyDTM) {
|
||||||
InputDialogListener listener = dialog -> {
|
InputDialogListener listener = dialog -> {
|
||||||
String name = dialog.getValue();
|
String name = dialog.getValue();
|
||||||
|
@ -1259,7 +1279,7 @@ class StructureEditorModel extends CompEditorModel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpackage the selected component in the structure or array. This means replace the structure
|
* Unpackage the selected component in the structure or array. This means replace the structure
|
||||||
* with the data types for its component parts. For an array replace the array with the data type
|
* with the data types for its component parts. For an array replace the array with the data type
|
||||||
* for each array element.
|
* for each array element.
|
||||||
* If the component isn't a structure or union then returns false.
|
* If the component isn't a structure or union then returns false.
|
||||||
* @param rowIndex the row
|
* @param rowIndex the row
|
||||||
|
|
|
@ -21,9 +21,10 @@ import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An action to trigger a context menu over the focus owner. This allows context menus to be
|
* An action to trigger a context menu over the focus owner. This allows context menus to be
|
||||||
* triggered from the keyboard.
|
* triggered from the keyboard.
|
||||||
*/
|
*/
|
||||||
public class ShowContextMenuAction extends DockingAction {
|
public class ShowContextMenuAction extends DockingAction {
|
||||||
|
@ -42,10 +43,12 @@ public class ShowContextMenuAction extends DockingAction {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// use the focused component to determine what should get the context menu
|
// use the focused component to determine what should get the context menu
|
||||||
Component focusOwner = kfm.getFocusOwner();
|
Component focusOwner = kfm.getFocusOwner();
|
||||||
if (focusOwner != null) {
|
if (focusOwner != null) {
|
||||||
DockingWindowManager.showContextMenu(focusOwner);
|
Swing.runLater(() -> {
|
||||||
|
DockingWindowManager.showContextMenu(focusOwner);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue