listeners = new ArrayList<>(1);
- public CompositeEditorModel(CompositeEditorProvider provider) {
+ /**
+ * Construct abstract composite editor model
+ * @param provider composite editor provider
+ */
+ protected CompositeEditorModel(CompositeEditorProvider provider) {
super(provider);
}
/**
- * Loads the specified composite into the model replacing
- * whatever composite is there.
- *
- * @param dataType the new composite data type.
+ * Reload from view composite and retain current edit state
*/
+ void reloadFromView() {
+
+ if (!isLoaded()) {
+ throw new AssertException();
+ }
+
+ if (isEditingField()) {
+ endFieldEditing();
+ }
+
+ CompositeViewerDataTypeManager oldViewDTM = viewDTM;
+
+ originalComposite = viewDTM.getResolvedViewComposite();
+ originalCompositeId = DataTypeManager.NULL_DATATYPE_ID;
+ originalDataTypePath = originalComposite.getDataTypePath();
+ currentName = originalComposite.getName();
+
+ // Use temporary standalone view datatype manager
+ viewDTM = new CompositeViewerDataTypeManager(viewDTM.getName(),
+ viewDTM.getResolvedViewComposite(), () -> restoreEditor());
+
+ viewComposite = viewDTM.getResolvedViewComposite();
+
+ // Clone all settings some of which do not get resolved.
+
+ // NOTE: It is important to note that the editor will allow modification of component
+ // default settings, however the underlying datatype default settings may not get copied
+ // as they get resolved into the view datatype manager. This may result in the incorrect
+ // underlying datatype default setting value being presented when adjusting component
+ // default settings.
+ cloneAllComponentSettings(originalComposite, viewComposite);
+
+ // Dispose previous view DTM
+ oldViewDTM.close();
+
+ hasChanges = false;
+
+ clearStatus();
+ compositeInfoChanged();
+ fireTableDataChanged();
+ componentDataChanged();
+
+ editorStateChanged(CompositeEditorModelListener.COMPOSITE_LOADED);
+ }
+
@Override
public void load(Composite dataType) {
- if (dataType == null) { // TODO: Why is this needed? Use case?
- return;
- }
- DataTypeManager dataTypeManager = dataType.getDataTypeManager();
- if (dataTypeManager == null) {
+ Objects.requireNonNull(dataType);
+
+ DataTypeManager dtm = dataType.getDataTypeManager();
+ if (dtm == null) {
throw new IllegalArgumentException(
"Datatype " + dataType.getName() + " doesn't have a data type manager specified.");
}
@@ -96,25 +144,21 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
}
// DataType should be a Composite.
+ originalDTM = dtm;
+ originalCompositeId = originalDTM.getID(dataType);
originalComposite = dataType;
originalDataTypePath = originalComposite.getDataTypePath();
currentName = dataType.getName();
- DataTypeManager originalDTM = dataTypeManager;
- viewComposite = createViewCompositeFromOriginalComposite(originalComposite);
- viewDTM = viewComposite.getDataTypeManager();
+ createViewCompositeFromOriginalComposite(originalComposite);
// Listen so we can update editor if name changes for this structure.
- originalCompositeId = DataTypeManager.NULL_DATATYPE_ID;
- if (originalDTM.contains(dataType)) {
- // Get the id if editing an existing data type.
- originalCompositeId = originalDTM.getID(dataType);
- }
originalDTM.addDataTypeManagerListener(this);
- hadChanges = false;
+ hasChanges = false;
- if (originalCompositeId == -1 || lastCompositeId != originalCompositeId) {
+ if (originalCompositeId == DataTypeManager.NULL_DATATYPE_ID ||
+ lastCompositeId != originalCompositeId) {
// only clear the selection if loading a new type
setSelection(new FieldSelection());
}
@@ -127,19 +171,38 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
editorStateChanged(CompositeEditorModelListener.COMPOSITE_LOADED);
}
+ protected void restoreEditor() {
+ if (isEditingField()) {
+ endFieldEditing();
+ }
+
+ currentName = viewComposite.getName();
+ updateAndCheckChangeState();
+
+ clearStatus();
+ compositeInfoChanged();
+ fireTableDataChanged();
+ componentDataChanged();
+ }
+
/**
- * Create view composite with the appropriate datatype manager and
+ * Create {@code viewComposite} and associated view datatype manager ({@code viewDTM}) and
* changes listener(s) if required.
*
* @param original original composite being loaded
- * @return view composite to used by model
*/
- protected Composite createViewCompositeFromOriginalComposite(Composite original) {
+ protected void createViewCompositeFromOriginalComposite(Composite original) {
+
+ if (viewDTM != null) {
+ viewDTM.close();
+ viewDTM = null;
+ }
// Use temporary standalone view datatype manager
- DataTypeManager dtm =
- new CompositeViewerDataTypeManager(original.getDataTypeManager().getName(), original);
- Composite composite = (Composite) dtm.resolve(original, null);
+ viewDTM = new CompositeViewerDataTypeManager(original.getDataTypeManager().getName(),
+ original, () -> restoreEditor());
+
+ viewComposite = viewDTM.getResolvedViewComposite();
// Clone all settings some of which do not get resolved.
@@ -148,39 +211,51 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
// as they get resolved into the view datatype manager. This may result in the incorrect
// underlying datatype default setting value being presented when adjusting component
// default settings.
- cloneAllComponentSettings(original, composite);
-
- dtm.addDataTypeManagerListener(this); // listen to view datatype manager changes
- return composite;
+ cloneAllComponentSettings(original, viewComposite);
}
+ String getOriginType() {
+ if (originalDTM instanceof ProgramBasedDataTypeManager) {
+ return "Program";
+ }
+ return "Archive";
+ }
+
+ /**
+ * Called when the model is no longer needed.
+ * This is where all cleanup code for the model should be placed.
+ */
@Override
- public void dispose() {
+ protected void dispose() {
super.dispose();
}
- @Override
- public CompositeEditorProvider getProvider() {
+ /**
+ * Returns the docking windows component provider associated with this edit model.
+ * @return the component provider
+ */
+ protected CompositeEditorProvider getProvider() {
return provider;
}
- @Override
+ /**
+ * Adds a CompositeEditorModelListener to be notified when changes occur.
+ * @param listener the listener to add.
+ */
public void addCompositeEditorModelListener(CompositeEditorModelListener listener) {
listeners.add(listener);
super.addCompositeViewerModelListener(listener);
}
- @Override
+ /**
+ * Removes a CompositeEditorModelListener that was being notified when changes occur.
+ * @param listener the listener to remove.
+ */
public void removeCompositeEditorModelListener(CompositeEditorModelListener listener) {
listeners.remove(listener);
super.removeCompositeViewerModelListener(listener);
}
- @Override
- public DataType resolve(DataType dt) {
- return viewDTM.resolve(dt, null);
- }
-
/**
* Gets the data type of the appropriate size to be placed at the indicated component index
*
@@ -227,19 +302,6 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
viewComposite.isPackingEnabled());
}
- /**
- * Notification that a field edit has ended.
- */
- @Override
- public void endFieldEditing() {
- if (!isEditingField()) {
- return;
- }
- for (CompositeEditorModelListener listener : listeners) {
- listener.endFieldEditing();
- }
- }
-
/**
* This updates one of the values for a component that is a field of
* this data structure.
@@ -311,30 +373,39 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
}
/**
- * Sets the name for the structure being edited.
+ * Sets the name for the composite data type being edited.
*
* @param name the new name.
- *
+ *
* @throws DuplicateNameException if the name already exists.
+ * @throws InvalidNameException if the name is invalid
*/
- @Override
public void setName(String name) throws DuplicateNameException, InvalidNameException {
if (name.equals(currentName)) {
return;
}
currentName = name;
+
+ if (viewComposite != null) {
+ validName = false;
+ int txId = viewDTM.startTransaction("Set Name");
+ try {
+ viewComposite.setName(name);
+ }
+ finally {
+ viewDTM.endTransaction(txId, true);
+ }
+ checkName(name);
+ validName = true;
+ }
+
boolean nameModified = !currentName.equals(getOriginalDataTypeName());
updateAndCheckChangeState();
+
// Notify any listeners that the name modification state has changed.
int type = (nameModified) ? CompositeEditorModelListener.COMPOSITE_MODIFIED
: CompositeEditorModelListener.COMPOSITE_UNMODIFIED;
editorStateChanged(type);
- if (viewComposite != null) {
- validName = false;
- viewComposite.setName(name);
- checkName(name);
- validName = true;
- }
}
/**
@@ -346,123 +417,169 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
}
/**
- * Sets the description for the composite being edited.
+ * Sets the description for the composite data type being edited.
*
* @param desc the new description.
*/
- @Override
- public void setDescription(String desc) {
- Composite original = this.getOriginalComposite();
- boolean descriptionModified = (original != null) && !desc.equals(original.getDescription());
+ protected void setDescription(String desc) {
+
if (viewComposite != null) {
if (!desc.equals(viewComposite.getDescription())) {
- viewComposite.setDescription(desc);
+ viewDTM.withTransaction("Set Description",
+ () -> viewComposite.setDescription(desc));
}
}
+
updateAndCheckChangeState();
+
// Notify any listeners that the name modification state has changed.
+ Composite original = this.getOriginalComposite();
+ boolean descriptionModified = (original != null) && !desc.equals(original.getDescription());
int type = (descriptionModified) ? CompositeEditorModelListener.COMPOSITE_MODIFIED
: CompositeEditorModelListener.COMPOSITE_UNMODIFIED;
editorStateChanged(type);
}
- @Override
- public boolean setComponentDataType(int rowIndex, Object dataTypeObject) throws UsrException {
- DataType previousDt = null;
- int previousLength = 0;
- String dtName = "";
- DataTypeComponent element = getComponent(rowIndex);
- if (element != null) {
- previousDt = element.getDataType();
- previousLength = element.getLength();
- dtName = previousDt.getDisplayName();
- }
- DataType newDt = null;
- int newLength = -1;
- if (dataTypeObject instanceof DataTypeInstance) {
- DataTypeInstance dti = (DataTypeInstance) dataTypeObject;
- newDt = dti.getDataType();
- newLength = dti.getLength();
- }
- else if (dataTypeObject instanceof DataType) {
- newDt = (DataType) dataTypeObject;
- newLength = newDt.getLength();
- }
- else if (dataTypeObject instanceof String) {
- String dtString = (String) dataTypeObject;
- if (dtString.equals(dtName)) {
+ /**
+ * Sets the data type for the component at the indicated rowIndex.
+ * @param rowIndex the row index of the component
+ * @param dataTypeObject a String or a DataType
+ * @return true if changed
+ * @throws UsrException if the type cannot be used
+ */
+ protected boolean setComponentDataType(int rowIndex, Object dataTypeObject)
+ throws UsrException {
+
+ boolean success = viewDTM.withTransaction("Set Datatype", () -> {
+ DataType previousDt = null;
+ int previousLength = 0;
+ String dtName = "";
+ DataTypeComponent element = getComponent(rowIndex);
+ if (element != null) {
+ previousDt = element.getDataType();
+ previousLength = element.getLength();
+ dtName = previousDt.getDisplayName();
+ }
+ DataType newDt = null;
+ int newLength = -1;
+ if (dataTypeObject instanceof DataTypeInstance dti) {
+ newDt = resolve(dti.getDataType());
+ newLength = dti.getLength();
+ }
+ else if (dataTypeObject instanceof DataType dt) {
+ newDt = resolve(dt);
+ newLength = newDt.getLength();
+ }
+ else if (dataTypeObject instanceof String dtString) {
+ if (dtString.equals(dtName)) {
+ return false;
+ }
+ newDt = DataTypeHelper.parseDataType(rowIndex, dtString, this, originalDTM,
+ provider.dtmService);
+ newLength = newDt.getLength();
+ }
+ if (newDt == null) {
+ return false; // Was nothing and is nothing.
+ }
+
+ if (DataTypeComponent.usesZeroLengthComponent(newDt)) {
+ newLength = 0;
+ }
+
+ checkIsAllowableDataType(newDt);
+
+ if (newLength < 0) {
+ // prefer previous size first
+ int suggestedLength = (previousLength <= 0) ? lastNumBytes : previousLength;
+ DataTypeInstance sizedDataType = DataTypeHelper.getSizedDataType(provider, newDt,
+ suggestedLength, getMaxReplaceLength(rowIndex));
+ if (sizedDataType == null) {
+ return false;
+ }
+ newLength = sizedDataType.getLength();
+ if (newLength <= 0) {
+ throw new UsrException("Can't currently add this data type.");
+ }
+ newDt = sizedDataType.getDataType();
+ }
+
+ if ((previousDt != null) && newDt.isEquivalent(previousDt) &&
+ newLength == previousLength) {
return false;
}
- DataTypeManager originalDTM = getOriginalDataTypeManager();
- newDt = DataTypeHelper.parseDataType(rowIndex, dtString, this, originalDTM,
- provider.dtmService);
- newLength = newDt.getLength();
- }
- if (newDt == null) {
- return false; // Was nothing and is nothing.
- }
- if (DataTypeComponent.usesZeroLengthComponent(newDt)) {
- newLength = 0;
- }
-
- checkIsAllowableDataType(newDt);
-
- newDt = resolveDataType(newDt, viewDTM, DataTypeConflictHandler.DEFAULT_HANDLER);
-
- if (newLength < 0) {
- // prefer previous size first
- int suggestedLength = (previousLength <= 0) ? lastNumBytes : previousLength;
- DataTypeInstance sizedDataType = DataTypeHelper.getSizedDataType(provider, newDt,
- suggestedLength, getMaxReplaceLength(rowIndex));
- if (sizedDataType == null) {
- return false;
+ int maxLength = getMaxReplaceLength(rowIndex);
+ if (maxLength > 0 && newLength > maxLength) {
+ throw new UsrException(newDt.getDisplayName() + " doesn't fit within " + maxLength +
+ " bytes, need " + newLength + " bytes");
}
- newDt = resolveDataType(sizedDataType.getDataType(), viewDTM,
- DataTypeConflictHandler.DEFAULT_HANDLER);
- newLength = sizedDataType.getLength();
- if (newLength <= 0) {
- throw new UsrException("Can't currently add this data type.");
- }
- }
- if ((previousDt != null) && newDt.isEquivalent(previousDt) && newLength == previousLength) {
- return false;
- }
- int maxLength = getMaxReplaceLength(rowIndex);
- if (maxLength > 0 && newLength > maxLength) {
- throw new UsrException(newDt.getDisplayName() + " doesn't fit within " + maxLength +
- " bytes, need " + newLength + " bytes");
+ // Set component datatype and length on view composite
+ DataType dataType = resolve(newDt); // probably already resolved
+ setComponentDataTypeInstance(rowIndex, dataType, newLength);
+ return true;
+ });
+
+ if (success) {
+ notifyCompositeChanged();
}
- setComponentDataTypeInstance(rowIndex, newDt, newLength);
- notifyCompositeChanged();
- return true;
+ return success;
}
/**
- * Resolves the data type against the indicated data type manager using the specified
- * conflictHandler. Transactions should have already been initiated prior to calling this
- * method. If not then override this method to perform the transaction code around the
- * resolve.
- *
- * @param dt the data type to be resolved
- * @param resolveDtm the data type manager to resolve the data type against
- * @param conflictHandler the handler to be used for any conflicts encountered while resolving
- * @return the resolved data type
+ * Sets the data type for the component at the indicated row index with an open
+ * transaction.
+ * @param rowIndex the row index of the component
+ * @param dt component datatype
+ * @param length component length
+ * @throws UsrException if invalid datatype or length specified
*/
- public DataType resolveDataType(DataType dt, DataTypeManager resolveDtm,
- DataTypeConflictHandler conflictHandler) {
- return resolveDtm.resolve(dt, conflictHandler);
+ abstract protected void setComponentDataTypeInstance(int rowIndex, DataType dt, int length)
+ throws UsrException;
+
+ /**
+ * Sets the data type for the component at the indicated index.
+ * @param rowIndex the row index of the component
+ * @param name the name
+ * @return true if a change was made
+ * @throws InvalidNameException if the name is invalid
+ */
+ abstract public boolean setComponentName(int rowIndex, String name) throws InvalidNameException;
+
+ /**
+ * Sets the data type for the component at the indicated index.
+ * @param rowIndex the row index of the component
+ * @param comment the comment
+ * @return true if a change was made
+ */
+ abstract public boolean setComponentComment(int rowIndex, String comment);
+
+ /**
+ * Clears the a defined components at the specified row. Clearing a component within a
+ * non-packed structure causes a defined component to be replaced with a number of
+ * undefined components. This may not the case when clearing a zero-length component or
+ * bit-field which may not result in such undefined components. In the case of a
+ * packed structure clearing is always completed without backfill.
+ * @param rowIndex the composite row to be cleared
+ */
+ protected void clearComponent(int rowIndex) {
+ clearComponents(new int[] { rowIndex });
}
- @SuppressWarnings("unused") // the exception is thrown by subclasses1d
- protected void clearComponents(int[] rows) throws UsrException {
- for (int i = rows.length - 1; i >= 0; i--) {
- clearComponent(rows[i]);
- }
- notifyCompositeChanged();
- }
+ /**
+ * Clears the all defined components at the specified rows. Clearing a component within a
+ * non-packed structure causes a defined component to be replaced with a number of
+ * undefined components. This may not the case when clearing a zero-length component or
+ * bit-field which may not result in such undefined components. In the case of a
+ * packed structure clearing is always completed without backfill.
+ * @param rows composite rows to be cleared
+ */
+ abstract protected void clearComponents(int[] rows);
+ /**
+ * Deletes all components at the specified rows.
+ * @param rows composite rows to be deleted.
+ */
protected void deleteComponents(int[] rows) {
for (int i = rows.length - 1; i >= 0; i--) {
deleteComponent(rows[i]);
@@ -470,15 +587,108 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
notifyCompositeChanged();
}
- protected abstract void deleteComponent(int rowIndex);
+ /**
+ * Deletes the component at the given rowIndex.
+ * @param rowIndex the row of the component to be deleted.
+ */
+ abstract protected void deleteComponent(int rowIndex);
+
+ /**
+ * 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.
+ *
+ * @param rowIndex index of the row in the editor's composite data type.
+ * @return the length
+ */
+ abstract protected int getMaxAddLength(int rowIndex);
+
+ abstract public DataTypeComponent add(DataType dataType) throws UsrException;
+
+ abstract protected DataTypeComponent add(int rowIndex, DataType dataType) throws UsrException;
+
+ abstract protected DataTypeComponent add(int rowIndex, DataType dt, int dtLength)
+ throws UsrException;
+
+ abstract protected DataTypeComponent insert(DataType dataType) throws UsrException;
+
+ abstract protected DataTypeComponent insert(int rowIndex, DataType dataType)
+ throws UsrException;
+
+ abstract protected DataTypeComponent insert(int rowIndex, DataType dt, int dtLength)
+ throws UsrException;
+
+ /**
+ * Gets the maximum number of bytes available for a new data type that
+ * will replace the current data type at the indicated component index.
+ * If there isn't a component with the indicated index, the max length
+ * will be determined by the lock mode.
+ *
+ * @param rowIndex index of the row for the component to replace.
+ * @return the maximum number of bytes that can be replaced.
+ */
+ abstract protected int getMaxReplaceLength(int rowIndex);
+
+ /**
+ * Update the datatype for the component located at the specified rowIndex.
+ * @param rowIndex the index of the row for the component to be updated
+ * @param dt new datatype to be applied
+ * @param dtLength datatype instance length
+ * @return updated component
+ * @throws UsrException if invalid parameters are provided
+ */
+ abstract protected DataTypeComponent replace(int rowIndex, DataType dt, int dtLength)
+ throws UsrException;
+
+ /**
+ * Determine the maximum number of duplicates that can be created for
+ * the component at the indicated index. The duplicates would follow
+ * the component. The number allowed depends on how many fit based on
+ * the current lock/unlock state of the editor.
+ *
Note: This method doesn't care whether there is a selection or not.
+ *
+ * @param rowIndex the index of the row for the component to be duplicated.
+ * @return the maximum number of duplicates.
+ */
+ abstract protected int getMaxDuplicates(int rowIndex);
+
+ /**
+ * Creates multiple duplicates of the indicated component.
+ * The duplicates will be created at the index immediately after the
+ * indicated component.
+ * @param rowIndex the index of the row whose component is to be duplicated.
+ * @param multiple the number of duplicates to create.
+ * @param monitor the task monitor
+ * @throws UsrException if component can't be duplicated the indicated number of times.
+ */
+ abstract protected void duplicateMultiple(int rowIndex, int multiple, TaskMonitor monitor)
+ throws UsrException;
+
+ /**
+ * Apply the changes for the current edited composite back to the
+ * original composite.
+ *
+ * @return true if apply succeeds
+ * @throws EmptyCompositeException if the structure doesn't have any components.
+ * @throws InvalidDataTypeException if this structure has a component that it is part of.
+ */
+ abstract public boolean apply() throws EmptyCompositeException, InvalidDataTypeException;
+
+ /**
+ * Determine the maximum number of array elements that can be created for
+ * the current selection. The array data type is assumed to become the
+ * data type of the first component in the selection. The current selection
+ * must be contiguous or 0 is returned.
+ *
+ * @return the number of array elements that fit in the current selection.
+ */
+ abstract protected int getMaxElements();
/**
* Clear the selected components.
*
* @throws UsrException if the data type isn't allowed to be cleared.
*/
- @Override
- public void createArray() throws UsrException {
+ protected void createArray() throws UsrException {
if (!isArrayAllowed()) {
throw new UsrException("Array not permitted in current context");
}
@@ -542,7 +752,6 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
*
* @throws UsrException if the data type isn't allowed to be cleared.
*/
- @Override
public void clearSelectedComponents() throws UsrException {
if (!isClearAllowed()) {
throw new UsrException("Clearing is not allowed.");
@@ -553,8 +762,12 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
clearComponents(getSelectedComponentRows());
}
- @Override
- public void deleteSelectedComponents() throws UsrException {
+ /**
+ * Delete the selected components.
+ *
+ * @throws UsrException if the data type isn't allowed to be deleted.
+ */
+ protected void deleteSelectedComponents() throws UsrException {
if (!isDeleteAllowed()) {
throw new UsrException("Deleting is not allowed.");
}
@@ -569,9 +782,13 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
return validName;
}
- @Override
+ /**
+ * Returns whether or not the editor has changes that haven't been applied.
+ * Changes can also mean a new data type that hasn't yet been saved.
+ * @return if there are changes
+ */
public boolean hasChanges() {
- return hadChanges;
+ return hasChanges;
}
public boolean updateAndCheckChangeState() {
@@ -580,16 +797,25 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
}
Composite oldComposite = getOriginalComposite();
String oldName = getOriginalDataTypeName();
- String newDesc = viewComposite.getDescription();
- if (newDesc == null) {
- newDesc = "";
- }
String oldDesc = oldComposite != null ? oldComposite.getDescription() : "";
if (oldDesc == null) {
oldDesc = "";
}
+ int oldSize = oldComposite != null ? oldComposite.getLength() : 0;
+
+ String newDesc = viewComposite.getDescription();
+ if (newDesc == null) {
+ newDesc = "";
+ }
+ int newSize = viewComposite.getLength();
+
+ hasChanges = !currentName.equals(oldName) || !newDesc.equals(oldDesc) || oldSize != newSize;
+ if (hasChanges) {
+ return true;
+ }
+
boolean noCompChanges = false;
- if (oldComposite != null) {
+ if (oldComposite != null && !hasChanges) {
noCompChanges = (viewComposite.isEquivalent(oldComposite) &&
hasSameComponentSettings(viewComposite, oldComposite) &&
!hasCompPathNameChanges(viewComposite, oldComposite));
@@ -597,8 +823,8 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
else {
noCompChanges = getNumComponents() == 0;
}
- hadChanges = !(currentName.equals(oldName) && newDesc.equals(oldDesc) && noCompChanges);
- return hadChanges;
+ hasChanges = !noCompChanges;
+ return hasChanges;
}
private boolean hasSameComponentSettings(Composite currentViewComposite,
@@ -702,8 +928,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
// METHODS FOR THE FIELD EDITING
//==================================================================================================
- @Override
- public boolean beginEditingField(int modelRow, int modelColumn) {
+ protected boolean beginEditingField(int modelRow, int modelColumn) {
if (isEditingField()) {
return false;
}
@@ -720,8 +945,11 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
return true;
}
- @Override
- public boolean endEditingField() {
+ /**
+ * Change the edit state to indicate no longer editing a field.
+ * @return the edit state to indicate no longer editing a field.
+ */
+ protected boolean endEditingField() {
if (!isEditingField()) {
return false;
}
@@ -730,11 +958,32 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
return true;
}
- @Override
+ /**
+ * Returns whether the user is currently editing a field's value.
+ * @return whether the user is currently editing a field's value.
+ */
public boolean isEditingField() {
return !settingValueAt && editingField;
}
+ /**
+ * Notification that a field edit has ended.
+ */
+ protected void endFieldEditing() {
+ if (!isEditingField()) {
+ return;
+ }
+ for (CompositeEditorModelListener listener : listeners) {
+ listener.endFieldEditing();
+ }
+ }
+
+ /**
+ * Returns whether or not the editor is showing undefined bytes.
+ * @return true if the editor is showing undefined bytes.
+ */
+ abstract protected boolean isShowingUndefinedBytes();
+
private void notifyEditingChanged() {
for (CompositeEditorModelListener listener : listeners) {
listener.compositeEditStateChanged(
@@ -743,7 +992,6 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
}
}
- @Override
public void cycleDataType(CycleGroup cycleGroup) {
// Only cycle a single component selection.
@@ -887,38 +1135,79 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
return true;
}
- @Override
- public boolean isAddAllowed(int currentIndex, DataType datatype) {
+ /**
+ * Returns whether or not addition of the specified component is allowed
+ * based on the current selection. The addition could be an insert or replace as
+ * determined by the state of the edit model.
+ *
+ * @param datatype the data type to be added.
+ * @return true if add allowed, else false
+ */
+ abstract protected boolean isAddAllowed(DataType datatype);
+
+ /**
+ * Returns whether or not addition of the specified component is allowed
+ * at the specified index. The addition could be an insert or replace as
+ * determined by the state of the edit model.
+ *
+ * @param rowIndex row index of the component in the composite data type.
+ * @param datatype the data type to be inserted.
+ * @return true if add allowed, else false
+ */
+ abstract protected boolean isAddAllowed(int rowIndex, DataType datatype);
+
+ /**
+ * Returns whether or not insertion of the specified data type is allowed
+ * at the specified index.
+ *
+ * @param rowIndex row index of the component in the composite data type.
+ * @param datatype the data type to be inserted.
+ * @return true if insert allowed, else false
+ */
+ protected boolean isInsertAllowed(int rowIndex, DataType datatype) {
return false;
}
- @Override
- public boolean isArrayAllowed() {
+ /**
+ * Returns whether or not the selection is allowed to be changed into an array.
+ * @return true if array conversion allowed, else false
+ */
+ abstract protected boolean isArrayAllowed();
+
+ /**
+ * Returns whether or not a bitfield is allowed at the current location.
+ * @return true if add bitfield, else false
+ */
+ abstract protected boolean isBitFieldAllowed();
+
+ /**
+ * Returns whether or not clearing the selected components is allowed.
+ * @return true if clear allowed, else false
+ */
+ abstract protected boolean isClearAllowed();
+
+ /**
+ * Returns whether or not the selected components can be deleted.
+ * @return true if delete allowed, else false
+ */
+ abstract protected boolean isDeleteAllowed();
+
+ /**
+ * Returns whether or not the component at the selected index is allowed to be duplicated.
+ * @return true if component duplication allowed, else false
+ */
+ protected boolean isDuplicateAllowed() {
return false;
}
- @Override
- public boolean isClearAllowed() {
- return false;
- }
-
- @Override
- public boolean isCycleAllowed(CycleGroup cycleGroup) {
- return false;
- }
-
- @Override
- public boolean isDeleteAllowed() {
- return false;
- }
-
- @Override
- public boolean isDuplicateAllowed() {
- return false;
- }
-
- @Override
- public boolean isEditComponentAllowed() {
+ /**
+ * Returns whether or not the base type of the component at the
+ * selected index is editable. If the base type is a composite
+ * then it is editable.
+ * Also, if there isn't a selection then it isn't allowed.
+ * @return true if edit allowed, else false
+ */
+ protected boolean isEditComponentAllowed() {
if (this.getNumSelectedComponentRows() != 1) {
return false;
}
@@ -935,91 +1224,56 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
(baseDt instanceof Enum) || (baseDt instanceof FunctionDefinition)));
}
- @Override
- public boolean isEditFieldAllowed() {
+ protected boolean isEditFieldAllowed() {
return !isEditingField();
}
- @Override
- public boolean isInsertAllowed(int rowIndex, DataType datatype) {
+ /**
+ * Returns whether the selected component(s) can be moved up (to the next lower index).
+ * @return true if component move-up allowed, else false
+ */
+ protected boolean isMoveUpAllowed() {
return false;
}
- @Override
- public boolean isMoveDownAllowed() {
+ /**
+ * Returns whether the selected component(s) can be moved down (to the next higher index).
+ * @return true if component move-down allowed, else false
+ */
+ protected boolean isMoveDownAllowed() {
return false;
}
- @Override
- public boolean isMoveUpAllowed() {
+ /**
+ * Moves a contiguous selection of components up by a single position. The component that was
+ * immediately above (at the index immediately preceding the selection) the selection will be
+ * moved below the selection (to what was the maximum selected component index).
+ * @return true if selected components were moved up.
+ * @throws UsrException if components can't be moved up.
+ */
+ abstract protected boolean moveUp() throws UsrException;
+
+ /**
+ * Moves a contiguous selection of components down by a single position. The component that was
+ * immediately below (at the index immediately following the selection) the selection will be
+ * moved above the selection (to what was the minimum selected component index).
+ * @return true if selected components were moved down.
+ * @throws UsrException if components can't be moved down.
+ */
+ abstract protected boolean moveDown() throws UsrException;
+
+ protected boolean isReplaceAllowed(int rowIndex, DataType dataType) {
return false;
}
- @Override
- public boolean isReplaceAllowed(int rowIndex, DataType dataType) {
+ /**
+ * Returns whether the selected component can be unpackaged.
+ * @return whether the selected component can be unpackaged.
+ */
+ protected boolean isUnpackageAllowed() {
return false;
}
- @Override
- public boolean isUnpackageAllowed() {
- return false;
- }
-
- @Override
- public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
-
- DataTypeManager originalDTM = getOriginalDataTypeManager();
- if (dtm != originalDTM) {
- return; // Different DTM than the one for this data type.
- }
-
- if (!isLoaded()) {
- return;
- }
-
- if (oldPath.getDataTypeName().equals(newPath.getDataTypeName())) {
- return;
- }
-
- String newName = newPath.getDataTypeName();
- String oldName = oldPath.getDataTypeName();
-
- // Does the old name match our original name.
- // Check originalCompositeId to ensure original type is managed
- if (originalCompositeId != DataTypeManager.NULL_DATATYPE_ID &&
- oldPath.equals(originalDataTypePath)) {
- originalDataTypePath = newPath;
- try {
- if (viewComposite.getName().equals(oldName)) {
- setName(newName);
- compositeInfoChanged();
- }
- }
- catch (DuplicateNameException e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- catch (InvalidNameException e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- }
- else {
- DataType dt = viewDTM.getDataType(oldPath);
- if (dt != null) {
- try {
- dt.setName(newName);
- fireTableDataChanged();
- componentDataChanged();
- }
- catch (InvalidNameException e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- catch (DuplicateNameException e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- }
- }
- }
-
/**
* If the component at the indicated index is a composite data type,
* this gets the number of components that it contains.
@@ -1036,18 +1290,30 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
return 0;
}
- @Override
- public int getLastNumBytes() {
+ /**
+ * Return the last number of bytes the user entered when prompted for
+ * a data type size.
+ * @return the number of bytes
+ */
+ protected int getLastNumBytes() {
return lastNumBytes;
}
- @Override
- public int getLastNumDuplicates() {
+ /**
+ * Return the last number of duplicates the user entered when prompted for
+ * creating duplicates of a component.
+ * @return the number of duplicates
+ */
+ protected int getLastNumDuplicates() {
return lastNumDuplicates;
}
- @Override
- public int getLastNumElements() {
+ /**
+ * Return the last number of elements the user entered when prompted for
+ * creating an array.
+ * @return the number of elements
+ */
+ protected int getLastNumElements() {
return lastNumElements;
}
@@ -1055,7 +1321,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
* Sets the last number of bytes the user entered for a data type
* @param numBytes the last number of bytes entered
*/
- public void setLastNumBytes(int numBytes) {
+ protected void setLastNumBytes(int numBytes) {
lastNumBytes = numBytes;
}
@@ -1063,7 +1329,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
* Sets the last number of bytes the user entered for a data type
* @param numDuplicates the last number of bytes entered
*/
- public void setLastNumDuplicates(int numDuplicates) {
+ protected void setLastNumDuplicates(int numDuplicates) {
lastNumDuplicates = numDuplicates;
}
@@ -1071,14 +1337,10 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
* Sets the last number of bytes the user entered for a data type
* @param numElements the last number of bytes entered
*/
- public void setLastNumElements(int numElements) {
+ protected void setLastNumElements(int numElements) {
lastNumElements = numElements;
}
-//==================================================================================================
-// End of methods for determining if a type of edit action is allowed
-//==================================================================================================
-
/**
* Saves the current selection in the structure components viewing area.
*
@@ -1119,7 +1381,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
* @param selection the new selection
*/
@Override
- public void setSelection(FieldSelection selection) {
+ protected void setSelection(FieldSelection selection) {
if (updatingSelection) {
return;
}
@@ -1139,7 +1401,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
}
@SuppressWarnings("unused") // the exception is thrown by subclasses1
- public void validateComponentOffset(int rowIndex, String offset) throws UsrException {
+ protected void validateComponentOffset(int rowIndex, String offset) throws UsrException {
// If the offset actually needs validating then override this method.
}
@@ -1152,7 +1414,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
* @return a valid data type instance or null if at blank line with no data type name.
* @throws UsrException indicating that the data type is not valid.
*/
- public DataTypeInstance validateComponentDataType(int rowIndex, String dtString)
+ protected DataTypeInstance validateComponentDataType(int rowIndex, String dtString)
throws UsrException {
DataType dt = null;
String dtName = "";
@@ -1168,7 +1430,6 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
}
int newLength = 0;
- DataTypeManager originalDTM = getOriginalDataTypeManager();
DataType newDt = DataTypeHelper.parseDataType(rowIndex, dtString, this, originalDTM,
provider.dtmService);
if (newDt == null) {
@@ -1200,14 +1461,13 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
}
@SuppressWarnings("unused") // the exception is thrown by subclasses
- public void validateComponentName(int rowIndex, String name) throws UsrException {
+ protected void validateComponentName(int rowIndex, String name) throws UsrException {
// If the name actually needs validating then override this method.
}
private void checkName(String name) throws DuplicateNameException {
- DataTypeManager originalDTM = getOriginalDataTypeManager();
DataType dt = originalDTM.getDataType(getOriginalCategoryPath(), name);
- if (dt != null && dt != originalComposite) {
+ if (dt != null && originalDTM.getID(dt) != originalCompositeId) {
throw new DuplicateNameException("Data type named " + name + " already exists");
}
}
@@ -1219,4 +1479,12 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
return (viewComposite instanceof Structure) || (viewComposite instanceof Union);
}
+ /**
+ * Get the composite edtor's datatype manager
+ * @return composite edtor's datatype manager
+ */
+ public CompositeViewerDataTypeManager getViewDataTypeManager() {
+ return viewDTM;
+ }
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModelAdapter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModelAdapter.java
index 80e5b0b786..3b9deca66b 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModelAdapter.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModelAdapter.java
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,51 +18,44 @@ package ghidra.app.plugin.core.compositeeditor;
/**
* Adapter for a composite editor model listener.
*/
-public class CompositeEditorModelAdapter
- implements CompositeEditorModelListener {
+public class CompositeEditorModelAdapter implements CompositeEditorModelListener {
public CompositeEditorModelAdapter() {
}
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeEditorModelListener#compositeEditStateChanged(int)
- */
+ @Override
public void compositeEditStateChanged(int type) {
+ // do nothing by default
}
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeEditorModelListener#endFieldEditing()
- */
+ @Override
public void endFieldEditing() {
+ // do nothing by default
}
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeViewerModelListener#componentDataChanged()
- */
+ @Override
public void componentDataChanged() {
+ // do nothing by default
}
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeViewerModelListener#compositeInfoChanged()
- */
+ @Override
public void compositeInfoChanged() {
+ // do nothing by default
}
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeViewerModelListener#statusChanged(java.lang.String, boolean)
- */
+ @Override
public void statusChanged(String message, boolean beep) {
+ // do nothing by default
}
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeViewerModelListener#selectionChanged()
- */
+ @Override
public void selectionChanged() {
+ // do nothing by default
}
+ @Override
public void showUndefinedStateChanged(boolean showUndefinedBytes) {
- // TODO Auto-generated method stub
-
+ // do nothing by default
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java
index ef4a3f553a..98896f03a4 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java
@@ -30,12 +30,13 @@ import javax.swing.event.ChangeEvent;
import javax.swing.table.*;
import javax.swing.text.JTextComponent;
+import org.apache.commons.lang3.StringUtils;
+
import docking.DockingWindowManager;
import docking.actions.KeyBindingUtils;
import docking.dnd.DropTgtAdapter;
import docking.dnd.Droppable;
import docking.widgets.DropDownSelectionTextField;
-import docking.widgets.OptionDialog;
import docking.widgets.fieldpanel.support.FieldRange;
import docking.widgets.fieldpanel.support.FieldSelection;
import docking.widgets.label.GDLabel;
@@ -50,8 +51,6 @@ import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Composite;
-import ghidra.program.model.listing.DataTypeArchive;
-import ghidra.program.model.listing.Program;
import ghidra.util.*;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
import ghidra.util.exception.UsrException;
@@ -121,6 +120,12 @@ public abstract class CompositeEditorPanel extends JPanel
setFocusTraversalPolicyProvider(true);
}
+ abstract protected boolean hasUncomittedEntry();
+
+ abstract protected boolean hasInvalidEntry();
+
+ abstract protected void comitEntryChanges();
+
/**
* Returns a list of focus traversal components. This list will be used to navigate forward
* and backward when the Tab and Shift-Tab keys are pressed. The components will be traversed
@@ -167,11 +172,10 @@ public abstract class CompositeEditorPanel extends JPanel
DataTypeComponent dtComponent = model.getComponent(modelRow);
if (dtComponent.isBitFieldComponent()) {
table.getCellEditor().cancelCellEditing();
-
+ CompEditorModel editorModel = (CompEditorModel) model;
BitFieldEditorDialog dlg = new BitFieldEditorDialog(model.viewComposite,
- provider.dtmService, modelRow, model.showHexNumbers, ordinal -> {
- model.notifyCompositeChanged();
- });
+ provider.dtmService, modelRow, model.showHexNumbers,
+ ordinal -> refreshTableAndSelection(editorModel, ordinal));
Component c = provider.getComponent();
DockingWindowManager.showDialog(c, dlg);
return true;
@@ -180,6 +184,11 @@ public abstract class CompositeEditorPanel extends JPanel
return false;
}
+ private void refreshTableAndSelection(CompEditorModel editorModel, int ordinal) {
+ editorModel.notifyCompositeChanged();
+ editorModel.setSelection(new int[] { ordinal, ordinal });
+ }
+
private void setupTableCellEditor() {
table.addPropertyChangeListener("tableCellEditor", evt -> {
@@ -546,63 +555,6 @@ public abstract class CompositeEditorPanel extends JPanel
}
}
- protected void dataTypeManagerRestored() {
- DataTypeManager originalDTM = model.getOriginalDataTypeManager();
- if (originalDTM == null) {
- // editor unloaded
- return;
- }
- boolean reload = true;
- String objectType;
- if (originalDTM instanceof ProgramBasedDataTypeManager) {
- objectType = "Program";
- }
- else {
- objectType = "Archive";
- }
- String archiveName = originalDTM.getName();
- DataType dt = originalDTM.getDataType(model.getCompositeID());
- if (dt instanceof Composite) {
- Composite composite = (Composite) dt;
- String origDtPath = composite.getPathName();
- if (!origDtPath.equals(model.getOriginalDataTypePath().getPath())) {
- model.fixupOriginalPath(composite);
- }
- }
- Composite originalDt = model.getOriginalComposite();
- if (originalDt == null) {
- provider.show();
- String info = "The " + objectType + " \"" + archiveName + "\" has been restored.\n" +
- "\"" + model.getCompositeName() + "\" may no longer exist outside the editor.";
- Msg.showWarn(this, this, objectType + " Restored", info);
- return;
- }
- else if (originalDt.isDeleted()) {
- cancelCellEditing(); // Make sure a field isn't being edited.
- provider.dispose(); // Close the editor.
- return;
- }
- else if (model.hasChanges()) {
- provider.show();
- // The user has modified the structure so prompt for whether or
- // not to reload the structure.
- String question =
- "The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
- model.getCompositeName() + "\" may have changed outside the editor.\n" +
- "Discard edits & reload the " + model.getTypeName() + "?";
- String title = "Reload " + model.getTypeName() + " Editor?";
- int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
- if (response != 1) {
- reload = false;
- }
- }
- if (reload) {
- cancelCellEditing(); // Make sure a field isn't being edited.
- model.load(originalDt); // reload the structure
- model.updateAndCheckChangeState();
- }
- }
-
public void dispose() {
if (isVisible()) {
setVisible(false);
@@ -737,7 +689,7 @@ public abstract class CompositeEditorPanel extends JPanel
*/
public void setStatus(String status) {
- if (status == null) {
+ if (StringUtils.isEmpty(status)) {
// Setting the text to null causes the label's preferred height to drop to 0, causing
// the UI to change size, depending on whether there was an existing status or not.
// Using the empty string prevents the UI layout from changing as the status changes.
@@ -906,6 +858,7 @@ public abstract class CompositeEditorPanel extends JPanel
catch (UsrException e) {
model.setStatus(e.getMessage(), true);
}
+ provider.contextChanged();
}
/**
@@ -964,9 +917,11 @@ public abstract class CompositeEditorPanel extends JPanel
switch (type) {
case COMPOSITE_LOADED:
cancelCellEditing(); // Make sure a field isn't being edited.
+ provider.updateTitle();
break;
case NO_COMPOSITE_LOADED:
cancelCellEditing(); // Make sure a field isn't being edited.
+ provider.updateTitle();
break;
case COMPOSITE_MODIFIED:
case COMPOSITE_UNMODIFIED:
@@ -1335,7 +1290,7 @@ public abstract class CompositeEditorPanel extends JPanel
fireEditingCanceled(); // user picked the same datatype
}
else {
- dt = model.resolve(dataType);
+ dt = dataType;
fireEditingStopped();
}
}
@@ -1622,4 +1577,5 @@ public abstract class CompositeEditorPanel extends JPanel
}
}
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java
index 64fac24e0a..a66fdf566f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java
@@ -162,10 +162,14 @@ public abstract class CompositeEditorProvider extends ComponentProviderAdapter
@Override
public void closeComponent() {
+ closeComponent(false);
+ }
+
+ void closeComponent(boolean force) {
if (editorModel != null && editorModel.editingField) {
editorModel.endFieldEditing();
}
- if (saveChanges(true) != 0) {
+ if (force || saveChanges(true) != 0) {
super.closeComponent();
dispose();
}
@@ -262,10 +266,6 @@ public abstract class CompositeEditorProvider extends ComponentProviderAdapter
return editorModel.hasChanges();
}
- public void dataTypeManagerRestored() {
- editorPanel.dataTypeManagerRestored();
- }
-
@Override
public void show() {
tool.showComponentProvider(this, true);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java
index 5be06d1ba6..8088fb2712 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java
@@ -30,7 +30,15 @@ import ghidra.util.HelpLocation;
*
* Note: Any new actions must be registered in the editor manager via the actions's name.
*/
-abstract public class CompositeEditorTableAction extends DockingAction implements EditorAction {
+abstract public class CompositeEditorTableAction extends DockingAction
+ implements CompositeEditorModelListener {
+
+ static final String MAIN_ACTION_GROUP = "0_MAIN_EDITOR_ACTION";
+ static final String UNDOREDO_ACTION_GROUP = "1_UNDOREDO_EDITOR_ACTION";
+ static final String BASIC_ACTION_GROUP = "2_BASIC_EDITOR_ACTION";
+ static final String DATA_ACTION_GROUP = "3_DATA_EDITOR_ACTION";
+ static final String COMPONENT_ACTION_GROUP = "4_COMPONENT_EDITOR_ACTION";
+ static final String BITFIELD_ACTION_GROUP = "5_COMPONENT_EDITOR_ACTION";
protected CompositeEditorProvider provider;
protected CompositeEditorModel model;
@@ -86,46 +94,47 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
tool = null;
}
+ protected boolean hasIncompleteFieldEntry() {
+ return provider.editorPanel.hasInvalidEntry() || provider.editorPanel.hasUncomittedEntry();
+ }
+
protected void requestTableFocus() {
if (provider != null) {
provider.requestTableFocus();
}
}
- @Override
- abstract public void adjustEnablement();
-
public String getHelpName() {
return getName();
}
@Override
public void selectionChanged() {
- adjustEnablement();
+ provider.contextChanged();
}
public void editStateChanged(int i) {
- adjustEnablement();
+ provider.contextChanged();
}
@Override
public void compositeEditStateChanged(int type) {
- adjustEnablement();
+ provider.contextChanged();
}
@Override
public void endFieldEditing() {
- adjustEnablement();
+ provider.contextChanged();
}
@Override
public void componentDataChanged() {
- adjustEnablement();
+ provider.contextChanged();
}
@Override
public void compositeInfoChanged() {
- adjustEnablement();
+ provider.contextChanged();
}
@Override
@@ -135,7 +144,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
@Override
public void showUndefinedStateChanged(boolean showUndefinedBytes) {
- adjustEnablement();
+ provider.contextChanged();
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java
index 72fced345a..52a702bfb0 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,36 +16,109 @@
package ghidra.app.plugin.core.compositeeditor;
import java.io.IOException;
+import java.util.Iterator;
+import java.util.TreeSet;
+import db.util.ErrorHandler;
+import ghidra.program.database.DatabaseObject;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.ProgramArchitecture;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
+import utility.function.Callback;
-public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
+/**
+ * {@link CompositeViewerDataTypeManager} provides a data type manager that the structure editor
+ * will use internally for updating the structure being edited and tracking all directly and
+ * indirectly referenced datatypes. This manager also facilitates undo/redo support within
+ * the editor.
+ */
+public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager
+ implements ErrorHandler {
/**
* The data type manager for original composite data type being edited.
* This is where the edited datatype will be written back to.
*/
private final DataTypeManager originalDTM;
- private final Composite originalComposite;
- private final Composite viewComposite;
- private final int transactionID;
+ private final Composite originalComposite; // may be null if not resolved into this DTM
+ private final Composite viewComposite; // may be null if not resolved into this DTM
+
+ // Database-backed datatype ID map, view to/from original DTM
+ // This is needed to account for datatype use and ID alterations across undo/redo
+ private final IDMapDB dataTypeIDMap;
+
+ // single editor transaction use only - undo/redo not supported when used
+ private Callback restoredCallback;
+ private int transactionId = 0;
+
+ // Modification count used to signal optional clearing of undo/redo stack at the end of a
+ // transaction should any database modifications occur.
+ private long flattenModCount = -1;
+
+ // datatype IDs to be checked as orphaned.
+ // NOTE: Orphan removal can only be done when this DTM actively manages the viewComposite
+ private TreeSet orphanIds = new TreeSet<>();
/**
- * Creates a data type manager that the structure editor will use
- * internally for updating the structure being edited.
+ * Creates a data type manager that the structure editor will use internally for managing
+ * dependencies for an unmanaged structure being edited. A single transaction will be started
+ * with this instantiation and held open until this instance is closed and undo/redo will
+ * not be supported.
* @param rootName the root name for this data type manager (usually the program name).
- * @param originalComposite the original composite data type that is being edited. (cannot be null).
+ * @param originalDTM the original data type manager.
*/
- public CompositeViewerDataTypeManager(String rootName, Composite originalComposite) {
- super(rootName, originalComposite.getDataTypeManager().getDataOrganization());
- this.originalComposite = originalComposite;
- transactionID = super.startTransaction("");
- originalDTM = originalComposite.getDataTypeManager();
+ public CompositeViewerDataTypeManager(String rootName, DataTypeManager originalDTM) {
+ this(rootName, originalDTM, null, null);
+ clearUndo();
+ transactionId = startTransaction("Composite Edit");
+ }
+ /**
+ * Creates a data type manager that the structure editor will use internally for managing a
+ * structure being edited and its dependencies.
+ * @param rootName the root name for this data type manager (usually the program name).
+ * @param originalComposite the original composite data type that is being edited.
+ * @param restoredCallback Callback will be invoked following any undo/redo.
+ */
+ public CompositeViewerDataTypeManager(String rootName, Composite originalComposite,
+ Callback restoredCallback) {
+ this(rootName, originalComposite.getDataTypeManager(), originalComposite, restoredCallback);
+ }
+
+ /**
+ * Constructor
+ * @param rootName the root name for this data type manager (usually the program name).
+ * @param originalDTM the original datatype manager
+ * @param originalComposite the original composite data type that is being edited. (may be null)
+ * @param restoredCallback Callback will be invoked following any undo/redo.
+ */
+ private CompositeViewerDataTypeManager(String rootName, DataTypeManager originalDTM,
+ Composite originalComposite, Callback restoredCallback) {
+ super(rootName, originalDTM.getDataOrganization());
+ this.originalDTM = originalDTM;
+ this.originalComposite = originalComposite;
+ this.restoredCallback = restoredCallback;
+
+ int txId = startTransaction("Setup for Edit");
+ try {
+ initializeArchitecture();
+ dataTypeIDMap = new IDMapDB(dbHandle, this);
+ viewComposite = resolveViewComposite();
+ }
+ finally {
+ endTransaction(txId, true);
+ }
+ clearUndo();
+ }
+
+ private Composite resolveViewComposite() {
+ return originalComposite != null ? (Composite) super.resolve(originalComposite, null)
+ : null;
+ }
+
+ private void initializeArchitecture() {
ProgramArchitecture arch = originalDTM.getProgramArchitecture();
if (arch != null) {
try {
@@ -58,8 +131,48 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
errHandler.dbError(e);
}
}
+ }
- viewComposite = (Composite) super.resolve(originalComposite, null);
+ /**
+ * Provides a means of detecting changes to the underlying database during a transaction.
+ * @return current modification count
+ */
+ public long getModCount() {
+ return dbHandle.getModCount();
+ }
+
+ @Override
+ protected synchronized void clearUndo() {
+ // Exposes method for test use
+ super.clearUndo();
+ }
+
+ @Override
+ public void undo() {
+ dataTypeIDMap.invalidate();
+ super.undo();
+ }
+
+ @Override
+ public void redo() {
+ dataTypeIDMap.invalidate();
+ super.redo();
+ }
+
+ /**
+ * Return the view composite if requested during instantiation.
+ * @return view composite or null if not resolved during instantiation.
+ */
+ public Composite getResolvedViewComposite() {
+ return viewComposite;
+ }
+
+ /**
+ * Determine if undo/redo is allowed.
+ * @return true if undo/redo is allowed with use of individual transactions, else false
+ */
+ public boolean isUndoRedoAllowed() {
+ return restoredCallback != null;
}
@Override
@@ -68,8 +181,10 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
}
@Override
- public void close() {
- super.endTransaction(transactionID, true);
+ public synchronized void close() {
+ if (transactionId != 0) {
+ super.endTransaction(transactionId, true);
+ }
super.close();
}
@@ -100,36 +215,203 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
// DataTypeManager instance.
return viewComposite;
}
- return super.resolve(dataType, handler);
- }
-
- //
- // Transaction support has been disabled since a single open transaction is maintained
- // until this DTM is closed.
- //
-
- @SuppressWarnings("sync-override")
- @Override
- public int startTransaction(String description) {
- // ignore - not yet supported
- return 0;
+ DataType resolvedDt = super.resolve(dataType, handler);
+ if ((dataType instanceof DatabaseObject) && originalDTM.contains(dataType)) {
+ long originalId = originalDTM.getID(dataType);
+ long myId = getID(resolvedDt);
+ dataTypeIDMap.put(myId, originalId);
+ }
+ return resolvedDt;
}
@Override
- public void endTransaction(int txId, boolean commit) {
- // ignore - not yet supported
+ public DataType replaceDataType(DataType existingViewDt, DataType replacementDt,
+ boolean updateCategoryPath) throws DataTypeDependencyException {
+
+ long viewDtId = getID(existingViewDt);
+
+ if (existingViewDt instanceof DatabaseObject) {
+ dataTypeIDMap.remove(viewDtId);
+ }
+
+ DataType newResolvedDt =
+ super.replaceDataType(existingViewDt, replacementDt, updateCategoryPath);
+
+ if (newResolvedDt instanceof DatabaseObject &&
+ replacementDt.getDataTypeManager() == originalDTM) {
+ long originalId = originalDTM.getID(replacementDt);
+ long myId = getID(newResolvedDt);
+ dataTypeIDMap.put(myId, originalId);
+ }
+
+ return newResolvedDt;
}
- @SuppressWarnings("sync-override")
@Override
- public boolean canUndo() {
- return false;
+ public boolean remove(DataType existingViewDt, TaskMonitor monitor) {
+
+ long viewDtId = getID(existingViewDt);
+
+ if (existingViewDt instanceof DatabaseObject) {
+ dataTypeIDMap.remove(viewDtId);
+ }
+
+ return super.remove(existingViewDt, monitor);
+ }
+
+ /**
+ * Refresh all datatypes which originate from the originalDTM.
+ * This methods is intended for use following an undo/redo of the originalDTM only
+ * and will purge the ID mappings for any datatypes which no longer exist or become
+ * orphaned.
+ * @return true if a dependency change is detected, else false
+ */
+ public boolean refreshDBTypesFromOriginal() {
+ synchronized (orphanIds) {
+ return withTransaction("DataTypes Restored", () -> {
+ boolean changed = false;
+ clearUndoOnChange();
+ Iterator allDataTypes = getAllDataTypes();
+ while (allDataTypes.hasNext()) {
+ DataType dt = allDataTypes.next();
+ if (dt == viewComposite || !(dt instanceof DatabaseObject)) {
+ continue;
+ }
+
+ // subject all DB types to orphan check
+ long myId = getID(dt);
+ if (viewComposite != null) {
+ orphanIds.add(myId);
+ }
+
+ Long originalId = dataTypeIDMap.getOriginalIDFromViewID(myId);
+ if (originalId == null) {
+ continue;
+ }
+
+ DataType originalDt = originalDTM.getDataType(originalId);
+ if (originalDt == null) {
+ changed = true;
+ remove(dt, TaskMonitor.DUMMY);
+ continue;
+ }
+
+ if (!originalDt.isEquivalent(dt)) {
+ changed = true;
+ try {
+ originalDt = replaceDataType(dt, originalDt, true);
+ }
+ catch (DataTypeDependencyException e) {
+ throw new AssertException(e); // should not occur
+ }
+ }
+
+ CategoryPath path = dt.getCategoryPath();
+ if (!originalDt.getCategoryPath().equals(path)) {
+ Category newDtCat = createCategory(path);
+ try {
+ newDtCat.moveDataType(dt, null);
+ }
+ catch (DataTypeDependencyException e) {
+ throw new AssertException(e); // should not occur
+ }
+ }
+
+ }
+ checkOrphansForRemoval(true);
+ return changed;
+ });
+ }
}
- @SuppressWarnings("sync-override")
@Override
- public boolean canRedo() {
- return false;
+ public void notifyRestored() {
+ super.notifyRestored();
+ if (restoredCallback != null) {
+ restoredCallback.call();
+ }
+ }
+
+ @Override
+ public synchronized void endTransaction(int transactionID, boolean commit) {
+
+ if (viewComposite != null && getTransactionCount() == 1) {
+ // Perform orphan removal only at the end of the outer-most transaction
+ synchronized (orphanIds) {
+ checkOrphansForRemoval(false);
+ }
+ }
+
+ super.endTransaction(transactionID, commit);
+
+ if (!isTransactionActive() && flattenModCount != -1) {
+ if (flattenModCount != dbHandle.getModCount()) {
+ // Mod count differs from flagged mod count - clean undo/redo
+ clearUndo();
+ }
+ flattenModCount = -1;
+ }
+ }
+
+ private void checkOrphansForRemoval(boolean cleanupIdMaps) {
+ while (!orphanIds.isEmpty()) {
+ long id = orphanIds.removeFirst();
+ if (!hasParent(id)) {
+ DataType dt = getDataType(id);
+ if (dt instanceof DatabaseObject) {
+
+ if (dt == viewComposite) {
+ continue;
+ }
+
+ // check all children of the datatype which may become orphaned
+ orphanIds.addAll(getChildIds(id));
+
+ // Remove orphan DB datatype
+ remove(dt, TaskMonitor.DUMMY);
+
+ if (cleanupIdMaps) {
+ dataTypeIDMap.remove(id);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Flag the next transaction end to check for subsequent database modifications
+ * and clear undo/redo stack if changes are detected. This call is ignored if
+ * there is already a pending check.
+ */
+ public synchronized void clearUndoOnChange() {
+ if (flattenModCount == -1) {
+ flattenModCount = dbHandle.getModCount();
+ }
+ }
+
+ @Override
+ protected void removeParentChildRecord(long parentID, long childID) {
+ // assume lock is in use
+ super.removeParentChildRecord(parentID, childID);
+
+ if (viewComposite != null) {
+ synchronized (orphanIds) {
+ if (!hasParent(childID)) {
+ // assumes if parent is removed it will not be re-added durig same transaction
+ orphanIds.add(childID);
+ }
+ }
+ }
+ }
+
+ public DataType findOriginalDataTypeFromMyID(long myId) {
+ Long originalId = dataTypeIDMap.getOriginalIDFromViewID(myId);
+ return originalId != null ? originalDTM.getDataType(originalId) : null;
+ }
+
+ public DataType findMyDataTypeFromOriginalID(long originalId) {
+ Long myId = dataTypeIDMap.getViewIDFromOriginalID(originalId);
+ return myId != null ? getDataType(myId) : null;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java
index d57ea5dc01..c344963b50 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -24,9 +24,9 @@ import javax.swing.table.TableColumn;
import docking.widgets.fieldpanel.support.FieldRange;
import docking.widgets.fieldpanel.support.FieldSelection;
+import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*;
import ghidra.util.*;
-import ghidra.util.exception.AssertException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
import utility.function.Callback;
@@ -43,9 +43,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
protected Composite originalComposite;
protected DataTypePath originalDataTypePath;
protected long originalCompositeId;
+ protected DataTypeManager originalDTM;
protected Composite viewComposite;
- protected DataTypeManager viewDTM;
+ protected CompositeViewerDataTypeManager viewDTM;
private List modelListeners = new ArrayList<>();
@@ -75,9 +76,9 @@ abstract class CompositeViewerModel extends AbstractTableModel
/** Width of left margin in pixels for the component area. */
protected int leftMargin = 10;
/** the current row for a field edit */
- protected int row = -1;
+ protected int currentEditRow = -1;
/** the current column for a field edit */
- protected int column = -1;
+ protected int currentEditColumn = -1;
protected CompositeEditorProvider provider;
protected boolean showHexNumbers = false;
@@ -134,7 +135,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
/**
* Terminates listening for category change events within the model.
*/
- void dispose() {
+ protected void dispose() {
// Unregister the listeners.
// No longer want to listen for changes to previous category.
unload();
@@ -164,7 +165,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
*
* @param dataType the composite date type to be viewed.
*/
- abstract void load(Composite dataType);
+ protected abstract void load(Composite dataType);
/**
* Unloads the currently loaded composite data type.
@@ -174,14 +175,13 @@ abstract class CompositeViewerModel extends AbstractTableModel
* a new composite data type.
*/
void unload() {
- DataTypeManager originalDTM =
- (originalComposite != null) ? originalComposite.getDataTypeManager() : null;
// Unregister the listeners.
// No longer want to listen for changes to previous category.
if (originalDTM != null) {
originalDTM.removeDataTypeManagerListener(this);
originalDTM = null;
}
+ originalDTM = null;
originalComposite = null;
originalCompositeId = DataTypeManager.NULL_DATATYPE_ID;
viewComposite = null;
@@ -192,16 +192,34 @@ abstract class CompositeViewerModel extends AbstractTableModel
}
}
+ /**
+ * Resolves the data type against the indicated data type manager using the specified
+ * conflictHandler. In general, a transaction should have already been initiated prior to
+ * calling this method so that the true nature of the transaction may be established for
+ * use with undo/redo (e.g., Set Datatype).
+ *
+ * @param dataType the data type to be resolved
+ * @param resolveDtm the data type manager to resolve the data type against
+ * @param conflictHandler the handler to be used for any conflicts encountered while resolving
+ * @return the resolved data type
+ */
+ protected final DataType resolveDataType(DataType dataType, DataTypeManager resolveDtm,
+ DataTypeConflictHandler conflictHandler) {
+ if (resolveDtm == null || dataType == DataType.DEFAULT) {
+ return DataType.DEFAULT;
+ }
+ return resolveDtm.withTransaction("Resolve " + dataType.getPathName(), () -> {
+ return resolveDtm.resolve(dataType, conflictHandler);
+ });
+ }
+
/**
* Resolves the indicated data type against the working copy in the viewer's data type manager.
* @param dataType the data type
* @return the working copy of the data type.
*/
- DataType resolve(DataType dataType) {
- if (viewDTM == null) {
- return DataType.DEFAULT;
- }
- return viewDTM.resolve(dataType, null);
+ public DataType resolve(DataType dataType) {
+ return resolveDataType(dataType, viewDTM, null);
}
/**
@@ -209,7 +227,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* @return the current row
*/
public int getRow() {
- return row;
+ return currentEditRow;
}
/**
@@ -217,7 +235,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* @param row the new row
*/
public void setRow(int row) {
- this.row = row;
+ this.currentEditRow = row;
}
/**
@@ -225,7 +243,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* @return the current column
*/
public int getColumn() {
- return column;
+ return currentEditColumn;
}
/**
@@ -239,7 +257,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
return;
}
- this.column = column;
+ this.currentEditColumn = column;
}
/**
@@ -248,23 +266,43 @@ abstract class CompositeViewerModel extends AbstractTableModel
* @param column the new column
*/
protected void setLocation(int row, int column) {
- this.row = row;
- this.column = column;
+ this.currentEditRow = row;
+ this.currentEditColumn = column;
}
/**
- * Returns the original CompositeDataType that was used to construct this.
- * @return the original composite being viewed or null if it doesn't exist.
+ * Returns the original Composite DataType that currently exists within the original
+ * DataTypeManager, or if not found the instance originally loaded.
+ * @return the original composite being viewed or null if nothing is currently loaded in
+ * the model.
*/
protected Composite getOriginalComposite() {
- DataTypeManager originalDTM = getOriginalDataTypeManager();
- if (originalDataTypePath != null && originalDTM != null) {
- DataType dt = originalDTM.getDataType(originalDataTypePath);
- if (dt instanceof Composite) {
+ Composite existingOriginal = getExistingOriginalComposite();
+ return existingOriginal != null ? existingOriginal : originalComposite;
+ }
+
+ /**
+ * Determine if the original composite exists within the original datatype manager.
+ * NOTE: If this method returns true, the current datatype which exists within the original
+ * datatype manager will be returned by {@link #getOriginalComposite()}, although its name
+ * may differ.
+ * @return true if datatype found else false
+ */
+ protected boolean originalCompositeExists() {
+ return getExistingOriginalComposite() != null;
+ }
+
+ private Composite getExistingOriginalComposite() {
+ long originalId = getCompositeID();
+ if (originalId != DataTypeManager.NULL_DATATYPE_ID && originalDataTypePath != null &&
+ originalDTM != null) {
+ DataType dt = originalDTM.getDataType(originalId);
+ if (dt instanceof Composite &&
+ DataTypeUtilities.isSameKindDataType(originalComposite, dt)) {
return (Composite) dt;
}
}
- return originalComposite;
+ return null;
}
/**
@@ -280,7 +318,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* @return the manager
*/
protected DataTypeManager getOriginalDataTypeManager() {
- return (originalComposite != null) ? originalComposite.getDataTypeManager() : null;
+ return originalDTM;
}
/**
@@ -288,7 +326,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
* @return the category
*/
public final Category getOriginalCategory() {
- DataTypeManager originalDTM = getOriginalDataTypeManager();
if (originalDataTypePath != null && originalDTM != null) {
CategoryPath originalCategoryPath = originalDataTypePath.getCategoryPath();
if (originalDTM.containsCategory(originalCategoryPath)) {
@@ -346,7 +383,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* Return the data type name of the structure being viewed
* @return the name
*/
- public String getCompositeName() {
+ protected String getCompositeName() {
return (viewComposite != null) ? viewComposite.getDisplayName() : "";
}
@@ -587,12 +624,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
}
/**
- * Returns the current dataType name (Structure or Union) as a string.
- * @return the name
+ * Returns the current dataType name (Structure, Union, etc.) as a string.
+ * @return the type of composite being edited
*/
- protected String getTypeName() {
- return "Composite Data Type";
- }
+ public abstract String getTypeName();
/**
* Returns the current status string.
@@ -635,26 +670,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
setStatus(status, false);
}
- /**
- * Fixes up the original name and category because a program restoration may have changed the
- * original composite.
- * @param composite the restored copy of our original composite
- */
- protected void fixupOriginalPath(Composite composite) {
- String newName = composite.getName();
- CategoryPath newCatPath = composite.getCategoryPath();
- CategoryPath oldCatPath = viewComposite.getCategoryPath();
- DataTypePath newDtPath = new DataTypePath(newCatPath, composite.getName());
- DataTypePath oldDtPath = new DataTypePath(oldCatPath, viewComposite.getName());
-
- if (!oldCatPath.equals(newCatPath)) {
- dataTypeMoved(viewDTM, oldDtPath, newDtPath);
- }
- if (!originalDataTypePath.getDataTypeName().equals(newName)) {
- dataTypeRenamed(viewDTM, oldDtPath, newDtPath);
- }
- }
-
/**
* Adds a CompositeViewerModelListener to be notified when model changes occur
* @param listener the listener
@@ -713,7 +728,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
@Override
public void categoryRemoved(DataTypeManager dtm, CategoryPath path) {
- DataTypeManager originalDTM = getOriginalDataTypeManager();
if (dtm != originalDTM) {
return; // Different DTM than the one for this data type.
}
@@ -739,7 +753,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
@Override
public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) {
- DataTypeManager originalDTM = getOriginalDataTypeManager();
if (dtm != originalDTM) {
return; // Different DTM than the one for this data type.
}
@@ -747,24 +760,27 @@ abstract class CompositeViewerModel extends AbstractTableModel
return;
}
Category oldCat = viewDTM.getCategory(oldPath);
- try {
- oldCat.setName(newPath.getName());
- }
- catch (DuplicateNameException e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- catch (InvalidNameException e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- if (originalDataTypePath.isAncestor(oldPath)) {
- changeOriginalDataTypeCategory(oldPath, newPath);
- }
+ viewDTM.withTransaction("Category Renamed", () -> {
+ viewDTM.clearUndoOnChange();
+ try {
+ oldCat.setName(newPath.getName());
+ }
+ catch (DuplicateNameException e) {
+ Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
+ }
+ catch (InvalidNameException e) {
+ Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
+ }
+ if (originalDataTypePath.isAncestor(oldPath)) {
+ changeOriginalDataTypeCategory(oldPath, newPath);
+ }
+ });
+
compositeInfoChanged();
}
@Override
public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) {
- DataTypeManager originalDTM = getOriginalDataTypeManager();
if (dtm != originalDTM) {
return; // Different DTM than the one for this data type.
}
@@ -776,14 +792,17 @@ abstract class CompositeViewerModel extends AbstractTableModel
return;
}
CategoryPath parent = newPath.getParent();
- viewDTM.createCategory(parent);
- Category newCat = viewDTM.getCategory(parent);
- try {
- newCat.moveCategory(oldCat, TaskMonitor.DUMMY);
- }
- catch (DuplicateNameException e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
+ viewDTM.withTransaction("Category Moved", () -> {
+ viewDTM.clearUndoOnChange();
+ viewDTM.createCategory(parent);
+ Category newCat = viewDTM.getCategory(parent);
+ try {
+ newCat.moveCategory(oldCat, TaskMonitor.DUMMY);
+ }
+ catch (DuplicateNameException e) {
+ Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
+ }
+ });
if (originalDataTypePath.isAncestor(oldPath)) {
changeOriginalDataTypeCategory(oldPath, newPath);
}
@@ -795,165 +814,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
// Adding a new data type doesn't affect this one?
}
- @Override
- public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) {
-
- DataTypeManager originalDTM = getOriginalDataTypeManager();
- if (dtm != originalDTM) {
- return; // Different DTM than the one for this data type.
- }
-
- DataType dataType = viewDTM.getDataType(path.getCategoryPath(), path.getDataTypeName());
- if (dataType == null) {
- return;
- }
-
- DataType baseDt = DataTypeHelper.getBaseType(dataType);
- DataTypePath dtPath = new DataTypePath(path.getCategoryPath(), baseDt.getName());
- if (!dtPath.equals(originalDataTypePath)) {
- DataType dt = viewDTM.getDataType(dtPath);
- if (dt != null) {
- if (hasSubDt(viewComposite, dtPath)) {
- String msg = "Removed sub-component data type \"" + dtPath;
- setStatus(msg, true);
- }
- viewDTM.remove(dt, TaskMonitor.DUMMY);
- // If a datatype we are using is removed, change it to undefined data types.
- fireTableDataChanged();
- componentDataChanged();
- }
- }
- else {
- if (!dataType.equals(baseDt)) {
- return; // ignore typedefs, arrays, and pointers of the Datatype being edited.
- }
- String msg = "\"" + dtPath + "\" was removed from the data type manager.";
- setStatus(msg, true);
- }
- }
-
- @Override
- public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
-
- DataTypeManager originalDTM = getOriginalDataTypeManager();
- if (dtm != originalDTM) {
- return; // Different DTM than the one for this data type.
- }
-
- DataType dt = viewDTM.getDataType(oldPath);
- if (dt == null) {
- return;
- }
-
- try {
- dt.setName(newPath.getDataTypeName());
- fireTableDataChanged();
- componentDataChanged();
- }
- catch (InvalidNameException | DuplicateNameException e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- }
-
- @Override
- public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
-
- DataTypeManager originalDTM = getOriginalDataTypeManager();
- if (dtm != originalDTM) {
- return; // Different DTM than the one for this data type.
- }
-
- DataType dt = viewDTM.getDataType(oldPath);
- if (dt == null) {
- return;
- }
-
- Category newDtCat = viewDTM.createCategory(newPath.getCategoryPath());
- try {
- newDtCat.moveDataType(dt, null);
- }
- catch (DataTypeDependencyException e) {
- throw new AssertException(e);
- }
-
- if (originalDataTypePath.getDataTypeName().equals(newPath.getDataTypeName()) &&
- originalDataTypePath.getCategoryPath().equals(oldPath.getCategoryPath())) {
- originalDataTypePath = newPath;
- compositeInfoChanged();
- }
- else {
- fireTableDataChanged();
- componentDataChanged();
- }
- }
-
- @Override
- public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) {
-
- DataTypeManager originalDTM = getOriginalDataTypeManager();
- if (dtm != originalDTM) {
- return; // Different DTM than the one for this data type.
- }
-
- if (isLoaded()) {
- if (originalCompositeId != DataTypeManager.NULL_DATATYPE_ID &&
- path.equals(originalDataTypePath)) {
- compositeInfoChanged();
- }
- else {
- CategoryPath cat = path.getCategoryPath();
- viewDTM.createCategory(cat);
- DataType dt = viewDTM.getDataType(path);
- if (dt == null) {
- return;
- }
- if (originalDTM != viewDTM) {
- // update changed datatype
- DataType dataType = dtm.getDataType(path);
- viewDTM.resolve(dataType, DataTypeConflictHandler.REPLACE_HANDLER);
- }
- fireTableDataChanged();
- componentDataChanged();
- }
- }
- }
-
- @Override
- public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath,
- DataType newDataType) {
-
- DataTypeManager originalDTM = getOriginalDataTypeManager();
- if (dtm != originalDTM) {
- return; // Different DTM than the one for this data type.
- }
-
- if (!isLoaded()) {
- return;
- }
-
- if (!oldPath.equals(originalDataTypePath)) { // am I editing the replaced dataType?
- DataType dt = viewDTM.getDataType(oldPath);
- if (dt != null) {
- if (hasSubDt(viewComposite, oldPath)) {
- String msg = "Replaced sub-component data type \"" + oldPath.getPath();
- setStatus(msg, true);
- }
- try {
-
- viewDTM.replaceDataType(dt, newDataType, true);
- }
- catch (DataTypeDependencyException e) {
- throw new AssertException(e);
- }
- fireTableDataChanged();
- componentDataChanged();
- }
- }
- else {
- load((Composite) newDataType);
- }
- }
-
@Override
public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) {
// Don't care.
@@ -965,9 +825,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
}
@Override
- public void restored(DataTypeManager dataTypeManager) {
- provider.dataTypeManagerRestored();
- }
+ public abstract void restored(DataTypeManager dataTypeManager);
//=================================================================================================
// Helper methods for CategoryChangeListener methods.
@@ -1112,7 +970,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* Returns true if the selection is a single row.
* @return true if the selection is a single row
*/
- public boolean isSingleRowSelection() {
+ protected boolean isSingleRowSelection() {
if (selection.getNumRanges() != 1) {
return false;
}
@@ -1124,7 +982,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* Returns true if the list selection is contiguous and only contains component rows.
* @return true if the list selection is contiguous and only contains component rows
*/
- public boolean isContiguousComponentSelection() {
+ protected boolean isContiguousComponentSelection() {
return ((selection.getNumRanges() == 1) &&
selection.getFieldRange(0).getStart().getIndex().intValue() < getNumComponents());
}
@@ -1133,7 +991,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* Get an array of the indices for all the selected rows.
* @return the selected rows
*/
- public int[] getSelectedRows() {
+ protected int[] getSelectedRows() {
ArrayList list = new ArrayList<>();
for (FieldRange range : this.selection) {
int endIndex = range.getEnd().getIndex().intValue();
@@ -1149,7 +1007,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* Get an array of the row indices for all the selected components.
* @return the selected rows
*/
- public int[] getSelectedComponentRows() {
+ protected int[] getSelectedComponentRows() {
ArrayList list = new ArrayList<>();
int numComponents = getNumComponents();
for (FieldRange range : this.selection) {
@@ -1169,7 +1027,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* @param rowIndex the row index
* @return the range or null
*/
- public FieldRange getSelectedRangeContaining(int rowIndex) {
+ protected FieldRange getSelectedRangeContaining(int rowIndex) {
FieldRange fieldRange = null;
if (selection.containsEntirely(BigInteger.valueOf(rowIndex))) {
// Get the size of the selection range we are in.
@@ -1203,7 +1061,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
*
* @param rows the indices for the selected rows.
*/
- public void setSelection(int[] rows) {
+ protected void setSelection(int[] rows) {
if (updatingSelection) {
return;
@@ -1229,7 +1087,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* it gets adjusted to the empty last line when in unlocked mode.
* @param selection the new selection
*/
- public void setSelection(FieldSelection selection) {
+ protected void setSelection(FieldSelection selection) {
if (updatingSelection) {
return;
}
@@ -1292,7 +1150,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
}
/**
- * Convenience method to run the given task on the swing thread now if swing or later if not.
+ * Convenience method to run the given task on the swing thread.
* @param r the runnable
*/
protected void swing(Runnable r) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CreateInternalStructureAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CreateInternalStructureAction.java
index 16ee7d7808..81dfea1159 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CreateInternalStructureAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CreateInternalStructureAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -40,11 +40,13 @@ public class CreateInternalStructureAction extends CompositeEditorTableAction {
public CreateInternalStructureAction(StructureEditorProvider provider) {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
setDescription(DESCRIPTION);
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
int[] selectedComponentRows = model.getSelectedComponentRows();
boolean hasComponentSelection = model.hasComponentSelection();
boolean hasContiguousSelection = model.isContiguousComponentSelection();
@@ -82,8 +84,8 @@ public class CreateInternalStructureAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(isCreateInternalStructureAllowed());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && isCreateInternalStructureAllowed();
}
private boolean isCreateInternalStructureAllowed() {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CycleGroupAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CycleGroupAction.java
index b44e5fb6f5..28206e6374 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CycleGroupAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CycleGroupAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -57,13 +57,16 @@ public class CycleGroupAction extends CompositeEditorTableAction {
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
model.cycleDataType(cycleGroup);
requestTableFocus();
}
@Override
- public void adjustEnablement() {
- setEnabled(true);
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry();
}
@Override
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeHelper.java
index dc267a1070..74e6c4c0d1 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeHelper.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeHelper.java
@@ -56,19 +56,6 @@ public class DataTypeHelper {
return new String(result, 0, resultIndex);
}
- public static DataType resolveDataType(DataType dt, DataTypeManager resolveDtm,
- DataTypeConflictHandler conflictHandler) {
- int txID = 0;
- try {
- txID = resolveDtm.startTransaction("Apply data type \"" + dt.getName() + "\"");
- dt = resolveDtm.resolve(dt, conflictHandler);
- }
- finally {
- resolveDtm.endTransaction(txID, (dt != null));
- }
- return dt;
- }
-
/**
* Parses a data type that was typed in the composite data type editor.
* It creates a DataTypeInstance that consists of the data type and its size.
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java
index ea9b699e88..0868887482 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -41,11 +41,13 @@ public class DeleteAction extends CompositeEditorTableAction {
setKeyBindingData(new KeyBindingData(KEY_STROKE));
setDescription("Delete the selected components");
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
TaskLauncher.launchModal(getName(), this::doDelete);
requestTableFocus();
}
@@ -63,7 +65,8 @@ public class DeleteAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(model.isDeleteAllowed());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isDeleteAllowed();
}
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateAction.java
index 712843c589..4fb3d12bf7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -41,15 +41,16 @@ public class DuplicateAction extends CompositeEditorTableAction {
KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.ALT_DOWN_MASK);
public DuplicateAction(CompositeEditorProvider provider) {
- super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null,
- ICON);
+ super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
setDescription(DESCRIPTION);
setKeyBindingData(new KeyBindingData(KEY_STROKE));
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
int[] indices = model.getSelectedComponentRows();
if (indices.length != 1) {
return;
@@ -69,7 +70,8 @@ public class DuplicateAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(model.isDuplicateAllowed());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isDuplicateAllowed();
}
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateMultipleAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateMultipleAction.java
index 50d9df73e3..e857455999 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateMultipleAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateMultipleAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -36,8 +36,7 @@ import ghidra.util.task.TaskMonitor;
*/
public class DuplicateMultipleAction extends CompositeEditorTableAction {
- private final static Icon ICON =
- new GIcon("icon.plugin.composite.editor.duplicate.multiple");
+ private final static Icon ICON = new GIcon("icon.plugin.composite.editor.duplicate.multiple");
public final static String ACTION_NAME = "Duplicate Multiple of Component";
private final static String GROUP_NAME = COMPONENT_ACTION_GROUP;
private final static String DESCRIPTION = "Duplicate multiple of the selected component";
@@ -49,11 +48,13 @@ public class DuplicateMultipleAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
setDescription(DESCRIPTION);
setKeyBindingData(new KeyBindingData(keyStroke));
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
int[] indices = model.getSelectedComponentRows();
if (indices.length != 1) {
return;
@@ -96,7 +97,8 @@ public class DuplicateMultipleAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(model.isDuplicateAllowed());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isDuplicateAllowed();
}
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java
index d56f4d43e1..a74e95daef 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java
@@ -31,7 +31,6 @@ public class EditBitFieldAction extends CompositeEditorTableAction {
if (!(model instanceof CompEditorModel)) {
throw new AssertException("unsupported use");
}
- adjustEnablement();
}
@Override
@@ -41,11 +40,9 @@ public class EditBitFieldAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- // Unions do not support non-packed manipulation of bitfields
- boolean enabled = (provider instanceof StructureEditorProvider structProvider) &&
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() &&
+ (provider instanceof StructureEditorProvider structProvider) &&
structProvider.getSelectedNonPackedBitFieldComponent() != null;
- setEnabled(enabled);
}
-
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditComponentAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditComponentAction.java
index 4a3ccf84d5..7b59836e42 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditComponentAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditComponentAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -37,11 +37,13 @@ public class EditComponentAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, MENU_PATH, null);
this.dtmService = provider.dtmService;
setDescription(DESCRIPTION);
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
int row = model.getRow();
if (row >= model.getNumComponents()) {
requestTableFocus();
@@ -79,8 +81,8 @@ public class EditComponentAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(model.isEditComponentAllowed());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isEditComponentAllowed();
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditFieldAction.java
index 8f6273ec5c..f907196753 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditFieldAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditFieldAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -41,35 +41,32 @@ public class EditFieldAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, MENU_PATH, null);
setDescription(DESCRIPTION);
setKeyBindingData(new KeyBindingData(KEY_STROKE));
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
- if (model != null) {
- int row = model.getRow();
- int column = model.getColumn();
- if (model.isCellEditable(row, column)) {
- model.beginEditingField(row, column);
- return;
- }
-
- // just go to the first editable cell, since the current one is not editable
- int firstEditableColumn = provider.getFirstEditableColumn(row);
- JTable table = provider.getTable();
- int modelColumn = table.convertColumnIndexToModel(firstEditableColumn);
- model.beginEditingField(row, modelColumn);
+ if (!isEnabledForContext(context)) {
+ return;
}
+ int row = model.getRow();
+ int column = model.getColumn();
+ if (model.isCellEditable(row, column)) {
+ model.beginEditingField(row, column);
+ return;
+ }
+
+ // just go to the first editable cell, since the current one is not editable
+ int firstEditableColumn = provider.getFirstEditableColumn(row);
+ JTable table = provider.getTable();
+ int modelColumn = table.convertColumnIndexToModel(firstEditableColumn);
+ model.beginEditingField(row, modelColumn);
requestTableFocus();
}
@Override
- public void adjustEnablement() {
- boolean shouldEnableEdit = false;
- if (model.isSingleRowSelection()) {
- shouldEnableEdit = model.isEditFieldAllowed();
- }
- setEnabled(shouldEnableEdit);
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isSingleRowSelection() &&
+ model.isEditFieldAllowed();
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java
deleted file mode 100644
index 8dea69e0ff..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.plugin.core.compositeeditor;
-
-import ghidra.app.util.datatype.EmptyCompositeException;
-import ghidra.program.model.data.*;
-import ghidra.util.InvalidNameException;
-import ghidra.util.exception.DuplicateNameException;
-import ghidra.util.exception.UsrException;
-import ghidra.util.task.TaskMonitor;
-
-public interface EditorModel {
-
- // TODO: This model interface serves no real purpose and could be collapsed into the
- // abstract class CompositeEditorModel implementation.
-
- /**
- * Called when the model is no longer needed.
- * This is where all cleanup code for the model should be placed.
- */
- public void dispose();
-
- /**
- * Returns the docking windows component provider associated with this edit model.
- * @return the component provider
- */
- public CompositeEditorProvider getProvider();
-
- /**
- * Adds a CompositeEditorModelListener to be notified when changes occur.
- * @param listener the listener to add.
- */
- public void addCompositeEditorModelListener(CompositeEditorModelListener listener);
-
- /**
- * Removes a CompositeEditorModelListener that was being notified when changes occur.
- * @param listener the listener to remove.
- */
- public void removeCompositeEditorModelListener(CompositeEditorModelListener listener);
-
- /**
- * Gets a data type within this editor that is equivalent to the indicated data type.
- * @param dt the data type to resolve
- * @return the equivalent data type within this editor.
- */
- public DataType resolve(DataType dt);
-
- /**
- * Returns whether or not addition of the specified component is allowed
- * based on the current selection. the addition could be an insert or replace as
- * determined by the state of the edit model.
- *
- * @param datatype the data type to be added.
- */
- public boolean isAddAllowed(DataType datatype);
-
- /**
- * Returns whether or not addition of the specified component is allowed
- * at the specified index. the addition could be an insert or replace as
- * determined by the state of the edit model.
- *
- * @param rowIndex row index of the component in the composite data type.
- * @param datatype the data type to be inserted.
- */
- public boolean isAddAllowed(int rowIndex, DataType datatype);
-
- /**
- * Returns whether or not the selection is allowed to be changed into an array.
- */
- public boolean isArrayAllowed();
-
- /**
- * Returns whether or not a bitfield is allowed at the current location.
- */
- public boolean isBitFieldAllowed();
-
- /**
- * Returns whether or not clearing the selected components is allowed.
- */
- public boolean isClearAllowed();
-
- /**
- * Returns whether or not the current selection can be cycled using the
- * indicated cycle group.
- * @param cycleGroup the cycle group
- * @return true, so that a message can be written to the user indicating
- * the criteria for cycling.
- */
- public boolean isCycleAllowed(CycleGroup cycleGroup);
-
- /**
- * Returns whether or not the selected components can be deleted.
- */
- public boolean isDeleteAllowed();
-
- /**
- * Returns whether or not the component at the selected index
- * is allowed to be duplicated.
- */
- public boolean isDuplicateAllowed();
-
- /**
- * Returns whether or not the base type of the component at the
- * selected index is editable. If the base type is a composite
- * then it is editable.
- * Also, if there isn't a selection then it isn't allowed.
- */
- public boolean isEditComponentAllowed();
-
- public boolean isEditFieldAllowed();
-
- /**
- * Returns whether or not insertion of the specified data type is allowed
- * at the specified index.
- *
- * @param rowIndex row index of the component in the composite data type.
- * @param datatype the data type to be inserted.
- */
- public boolean isInsertAllowed(int rowIndex, DataType datatype);
-
- /**
- * Returns whether the selected component(s) can be moved down (to the next higher index).
- */
- public boolean isMoveDownAllowed();
-
- /**
- * Returns whether the selected component(s) can be moved up (to the next lower index).
- */
- public boolean isMoveUpAllowed();
-
- public boolean isReplaceAllowed(int rowIndex, DataType dataType);
-
- /**
- * Returns whether the selected component can be unpackaged.
- * @return whether the selected component can be unpackaged.
- */
- public boolean isUnpackageAllowed();
-
- /**
- * Returns whether or not the editor has changes that haven't been applied.
- * Changes can also mean a new data type that hasn't yet been saved.
- * @return if there are changes
- */
- public boolean hasChanges();
-
- /**
- * Sets the name for the composite data type being edited.
- *
- * @param name the new name.
- *
- * @throws DuplicateNameException if the name already exists.
- * @throws InvalidNameException if the name is invalid
- */
- public void setName(String name) throws DuplicateNameException, InvalidNameException;
-
- /**
- * Sets the description for the composite data type being edited.
- *
- * @param desc the new description.
- */
- public void setDescription(String desc);
-
- /**
- * Sets the data type for the component at the indicated rowIndex.
- * @param rowIndex the row index of the component
- * @param dataTypeObject a String or a DataType
- * @return true if changed
- * @throws UsrException if the type cannot be used
- */
- public boolean setComponentDataType(int rowIndex, Object dataTypeObject) throws UsrException;
-
- /**
- * Sets the data type for the component at the indicated row index.
- * @param rowIndex the row index of the component
- * @param dt component datatype
- * @param length component length
- * @throws UsrException if invalid datatype or length specified
- */
- public void setComponentDataTypeInstance(int rowIndex, DataType dt, int length)
- throws UsrException;
-
- /**
- * Sets the data type for the component at the indicated index.
- * @param rowIndex the row index of the component
- * @param name the name
- * @return true if a change was made
- * @throws InvalidNameException if the name is invalid
- */
- public boolean setComponentName(int rowIndex, String name) throws InvalidNameException;
-
- /**
- * Sets the data type for the component at the indicated index.
- * @param rowIndex the row index of the component
- * @param comment the comment
- * @return true if a change was made
- */
- public boolean setComponentComment(int rowIndex, String comment);
-
- /**
- * Returns whether or not the editor is showing undefined bytes.
- * @return true if the editor is showing undefined bytes.
- */
- public boolean isShowingUndefinedBytes();
-
- public boolean beginEditingField(int modelRow, int modelColumn);
-
- /**
- * Change the edit state to indicate no longer editing a field.
- * @return the edit state to indicate no longer editing a field.
- */
- public boolean endEditingField();
-
- /**
- * Returns whether the user is currently editing a field's value.
- * @return whether the user is currently editing a field's value.
- */
- public boolean isEditingField();
-
- public void endFieldEditing();
-
- public DataTypeComponent add(DataType dataType) throws UsrException;
-
- public DataTypeComponent add(int rowIndex, DataType dataType) throws UsrException;
-
- public DataTypeComponent add(int rowIndex, DataType dt, int dtLength) throws UsrException;
-
- /**
- * Apply the changes for the current edited composite back to the
- * original composite.
- *
- * @return true if apply succeeds
- * @throws EmptyCompositeException if the structure doesn't have any components.
- * @throws InvalidDataTypeException if this structure has a component that it is part of.
- */
- public boolean apply() throws EmptyCompositeException, InvalidDataTypeException;
-
- public void clearComponent(int rowIndex);
-
- public void clearSelectedComponents() throws UsrException;
-
- public void cycleDataType(CycleGroup cycleGroup);
-
- public void createArray() throws UsrException;
-
- /**
- * Delete the selected components.
- *
- * @throws UsrException if the data type isn't allowed to be deleted.
- */
- public void deleteSelectedComponents() throws UsrException;
-
- /**
- * Creates multiple duplicates of the indicated component.
- * The duplicates will be created at the index immediately after the
- * indicated component.
- * @param rowIndex the index of the row whose component is to be duplicated.
- * @param multiple the number of duplicates to create.
- * @param monitor the task monitor
- * @throws UsrException if component can't be duplicated the indicated number of times.
- */
- public void duplicateMultiple(int rowIndex, int multiple, TaskMonitor monitor)
- throws UsrException;
-
- public DataTypeComponent insert(DataType dataType) throws UsrException;
-
- public DataTypeComponent insert(int rowIndex, DataType dataType) throws UsrException;
-
- public DataTypeComponent insert(int rowIndex, DataType dt, int dtLength) throws UsrException;
-
- /**
- * Moves a contiguous selection of components up by a single position. The component that was
- * immediately above (at the index immediately preceding the selection) the selection will be
- * moved below the selection (to what was the maximum selected component index).
- * @return true if selected components were moved up.
- * @throws UsrException if components can't be moved up.
- */
- public boolean moveUp() throws UsrException;
-
- /**
- * Moves a contiguous selection of components down by a single position. The component that was
- * immediately below (at the index immediately following the selection) the selection will be
- * moved above the selection (to what was the minimum selected component index).
- * @return true if selected components were moved down.
- * @throws UsrException if components can't be moved down.
- */
- public boolean moveDown() throws UsrException;
-
- public DataTypeComponent replace(int rowIndex, DataType dt, int dtLength) throws UsrException;
-
- /**
- * 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.
- *
- * @param rowIndex index of the row in the editor's composite data type.
- * @return the length
- */
- public int getMaxAddLength(int rowIndex);
-
- /**
- * Determine the maximum number of duplicates that can be created for
- * the component at the indicated index. The duplicates would follow
- * the component. The number allowed depends on how many fit based on
- * the current lock/unlock state of the editor.
- *
Note: This method doesn't care whether there is a selection or not.
- *
- * @param rowIndex the index of the row for the component to be duplicated.
- * @return the maximum number of duplicates.
- */
- public int getMaxDuplicates(int rowIndex);
-
- /**
- * Determine the maximum number of array elements that can be created for
- * the current selection. The array data type is assumed to become the
- * data type of the first component in the selection. The current selection
- * must be contiguous or 0 is returned.
- *
- * @return the number of array elements that fit in the current selection.
- */
- public int getMaxElements();
-
- /**
- * Gets the maximum number of bytes available for a new data type that
- * will replace the current data type at the indicated component index.
- * If there isn't a component with the indicated index, the max length
- * will be determined by the lock mode.
- *
- * @param rowIndex index of the row for the component to replace.
- * @return the maximum number of bytes that can be replaced.
- */
- public int getMaxReplaceLength(int rowIndex);
-
- /**
- * Return the last number of bytes the user entered when prompted for
- * a data type size.
- * @return the number of bytes
- */
- public int getLastNumBytes();
-
- /**
- * Return the last number of duplicates the user entered when prompted for
- * creating duplicates of a component.
- * @return the number of duplicates
- */
- public int getLastNumDuplicates();
-
- /**
- * Return the last number of elements the user entered when prompted for
- * creating an array.
- * @return the number of elements
- */
- public int getLastNumElements();
-
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FavoritesAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FavoritesAction.java
index 4ffd43f70d..a07651bebc 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FavoritesAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FavoritesAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -45,7 +45,6 @@ public class FavoritesAction extends CompositeEditorTableAction {
new MenuData(new String[] { "Favorite", dt.getDisplayName() }, null, GROUP_NAME));
getPopupMenuData().setParentMenuGroup(GROUP_NAME);
- adjustEnablement();
}
public DataType getDataType() {
@@ -54,6 +53,9 @@ public class FavoritesAction extends CompositeEditorTableAction {
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
try {
model.add(dataType);
}
@@ -63,12 +65,6 @@ public class FavoritesAction extends CompositeEditorTableAction {
requestTableFocus();
}
- @Override
- public void adjustEnablement() {
- // we always want it enabled so the user gets a "doesn't fit" message.
- setEnabled(true);
- }
-
@Override
public String getHelpName() {
return "Favorite";
@@ -76,7 +72,7 @@ public class FavoritesAction extends CompositeEditorTableAction {
@Override
public boolean isEnabledForContext(ActionContext context) {
- return model.isAddAllowed(dataType);
+ return !hasIncompleteFieldEntry() && model.isAddAllowed(dataType);
}
@Override
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FindReferencesToStructureFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FindReferencesToStructureFieldAction.java
index 726b339fb5..9497c47434 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FindReferencesToStructureFieldAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FindReferencesToStructureFieldAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -34,13 +34,14 @@ public class FindReferencesToStructureFieldAction extends CompositeEditorTableAc
public FindReferencesToStructureFieldAction(CompositeEditorProvider provider) {
super(provider, ACTION_NAME, BASIC_ACTION_GROUP, new String[] { ACTION_NAME }, null, null);
setDescription(DESCRIPTION);
- adjustEnablement();
setHelpLocation(new HelpLocation(HelpTopics.FIND_REFERENCES, "Data_Types"));
}
@Override
public void actionPerformed(ActionContext context) {
-
+ if (!isEnabledForContext(context)) {
+ return;
+ }
FindAppliedDataTypesService service = tool.getService(FindAppliedDataTypesService.class);
if (service == null) {
Msg.showError(this, null, "Missing Plugin",
@@ -67,24 +68,27 @@ public class FindReferencesToStructureFieldAction extends CompositeEditorTableAc
}
@Override
- public void adjustEnablement() {
+ public boolean isEnabledForContext(ActionContext context) {
setEnabled(false);
+ if (!hasIncompleteFieldEntry()) {
+ return false;
+ }
if (model.getSelectedComponentRows().length != 1) {
- return;
+ return false;
}
Composite composite = model.getOriginalComposite();
if (composite == null) {
- return; // not sure if this can happen
+ return false; // not sure if this can happen
}
String fieldName = getFieldName();
if (fieldName == null) {
- return;
+ return false;
}
- setEnabled(true);
updateMenuName(fieldName);
+ return true;
}
private void updateMenuName(String name) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/HexNumbersAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/HexNumbersAction.java
index c0a2ce5784..3e1a3f7815 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/HexNumbersAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/HexNumbersAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -38,19 +38,21 @@ public class HexNumbersAction extends CompositeEditorTableAction implements Togg
public HexNumbersAction(CompositeEditorProvider provider) {
super(provider, ACTION_NAME, GROUP_NAME, PATH, PATH, null);
setDescription(DESCRIPTION);
- setEnabled(true);
setSelected(model.isShowingNumbersInHex());
setKeyBindingData(new KeyBindingData("Shift-H"));
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
model.displayNumbersInHex(!model.isShowingNumbersInHex());
}
@Override
- public void adjustEnablement() {
- // Always enabled.
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry();
}
@Override
@@ -70,7 +72,7 @@ public class HexNumbersAction extends CompositeEditorTableAction implements Togg
@Override
protected JMenuItem doCreateMenuItem() {
DockingCheckBoxMenuItem menuItem = new DockingCheckBoxMenuItem(isSelected);
- menuItem.setUI((DockingCheckboxMenuItemUI) DockingCheckboxMenuItemUI.createUI(menuItem));
+ menuItem.setUI(DockingCheckboxMenuItemUI.createUI(menuItem));
return menuItem;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/IDMapDB.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/IDMapDB.java
new file mode 100644
index 0000000000..3b4698dc83
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/IDMapDB.java
@@ -0,0 +1,145 @@
+/* ###
+ * 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 java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import db.*;
+import db.util.ErrorHandler;
+
+/**
+ * {@link IDMapDB} provides a bidirectional map for tracking view to/from original datatype ID
+ * correspondence and faciliate recovery across undo/redo of the view's datatype manager.
+ */
+class IDMapDB {
+ private final static String TABLE_NAME = "IDMap";
+
+ private final static Schema SCHEMA =
+ new Schema(0, "ViewID", new Class[] { LongField.class }, new String[] { "OriginalID" });
+
+ private static final int ORIGINAL_ID_COL = 0;
+
+ private final ErrorHandler errorHandler;
+ private final Table table;
+
+ private Map viewToOriginalMap;
+ private Map originalToViewMap;
+
+ /**
+ * Construct database-backed bidirectional datatype ID map
+ * @param dbHandle database handle for {@link CompositeViewerDataTypeManager}
+ * @param errorHandler error handler
+ */
+ IDMapDB(DBHandle dbHandle, ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ table = init(dbHandle, errorHandler);
+ viewToOriginalMap = new HashMap<>();
+ originalToViewMap = new HashMap<>();
+ }
+
+ private static Table init(DBHandle dbHandle, ErrorHandler errorHandler) {
+ try {
+ return dbHandle.createTable(TABLE_NAME, SCHEMA);
+ }
+ catch (IOException e) {
+ errorHandler.dbError(e); // will throw runtime exception
+ }
+ return null;
+ }
+
+ void invalidate() {
+ viewToOriginalMap = null;
+ originalToViewMap = null;
+ // delay reload until needed
+ }
+
+ void clearAll() throws IOException {
+ table.deleteAll();
+ viewToOriginalMap = new HashMap<>();
+ originalToViewMap = new HashMap<>();
+ }
+
+ private void reloadIfNeeded() {
+ if (viewToOriginalMap != null) {
+ return;
+ }
+
+ viewToOriginalMap = new HashMap<>();
+ originalToViewMap = new HashMap<>();
+ try {
+ RecordIterator it = table.iterator();
+ while (it.hasNext()) {
+ DBRecord rec = it.next();
+ long viewId = rec.getKey();
+ long originalId = rec.getLongValue(ORIGINAL_ID_COL);
+ viewToOriginalMap.put(viewId, originalId);
+ originalToViewMap.put(originalId, viewId);
+ }
+ }
+ catch (IOException e) {
+ errorHandler.dbError(e);
+ }
+ }
+
+ Long getOriginalIDFromViewID(long viewId) {
+ reloadIfNeeded();
+ return viewToOriginalMap.get(viewId);
+ }
+
+ Long getViewIDFromOriginalID(long originalId) {
+ reloadIfNeeded();
+ return originalToViewMap.get(originalId);
+ }
+
+ void put(long viewId, long originalId) {
+ try {
+ DBRecord rec = SCHEMA.createRecord(viewId);
+ rec.setLongValue(ORIGINAL_ID_COL, originalId);
+ table.putRecord(rec);
+
+ if (viewToOriginalMap != null) {
+ viewToOriginalMap.put(viewId, originalId);
+ originalToViewMap.put(originalId, viewId);
+ }
+ }
+ catch (IOException e) {
+ errorHandler.dbError(e);
+ }
+ }
+
+ Long remove(long viewId) {
+ Long originalId = null;
+ try {
+ DBRecord rec = table.getRecord(viewId);
+ if (rec != null) {
+ originalId = rec.getLongValue(ORIGINAL_ID_COL);
+ table.deleteRecord(viewId);
+
+ if (viewToOriginalMap != null) {
+ viewToOriginalMap.remove(viewId);
+ originalToViewMap.remove(originalId);
+ }
+ }
+ }
+ catch (IOException e) {
+ errorHandler.dbError(e);
+ }
+ return originalId;
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java
index 8b353f88ce..53243b60d0 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -46,11 +46,13 @@ public class InsertUndefinedAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
setDescription(DESCRIPTION);
setKeyBindingData(new KeyBindingData(KEY_STROKE));
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
try {
boolean isContiguousSelection = model.getSelection().getNumRanges() == 1;
if (isContiguousSelection) {
@@ -72,7 +74,10 @@ public class InsertUndefinedAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
+ public boolean isEnabledForContext(ActionContext context) {
+ if (hasIncompleteFieldEntry()) {
+ return false;
+ }
boolean enabled = false;
if (model.viewComposite instanceof Structure) {
boolean isContiguousSelection = model.getSelection().getNumRanges() == 1;
@@ -82,7 +87,7 @@ public class InsertUndefinedAction extends CompositeEditorTableAction {
enabled = isContiguousSelection &&
model.isInsertAllowed(model.getMinIndexSelected(), undefinedDt);
}
- setEnabled(enabled);
+ return enabled;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveDownAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveDownAction.java
index 228d00c821..f4354baf3e 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveDownAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveDownAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -45,11 +45,13 @@ public class MoveDownAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
setDescription(DESCRIPTION);
setKeyBindingData(new KeyBindingData(KEY_STROKE));
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
try {
model.moveDown();
}
@@ -60,7 +62,8 @@ public class MoveDownAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(model.isMoveDownAllowed());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isMoveDownAllowed();
}
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveUpAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveUpAction.java
index c05e1d58dc..b17fd118df 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveUpAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveUpAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -45,11 +45,13 @@ public class MoveUpAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
setDescription(DESCRIPTION);
setKeyBindingData(new KeyBindingData(KEY_STROKE));
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
try {
model.moveUp();
}
@@ -60,8 +62,8 @@ public class MoveUpAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(model.isMoveUpAllowed());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isMoveUpAllowed();
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/PointerAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/PointerAction.java
index b86a54eadc..6d5563ef9d 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/PointerAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/PointerAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -38,11 +38,13 @@ public class PointerAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, null, null, null);
setDescription(DESCRIPTION);
setKeyBindingData(new KeyBindingData(KeyEvent.VK_P, 0));
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
try {
model.add(POINTER_DT);
}
@@ -55,16 +57,8 @@ public class PointerAction extends CompositeEditorTableAction {
@Override
public boolean isEnabledForContext(ActionContext context) {
// Do nothing since we always want it enabled so the user gets a "doesn't fit" message.
- return model.getRowCount() > 0 && model.hasSelection() && model.isContiguousSelection();
+ return !hasIncompleteFieldEntry() && model.getRowCount() > 0 && model.hasSelection() &&
+ model.isContiguousSelection();
}
- @Override
- public void adjustEnablement() {
- // Allow the user to get a "doesn't fit" message on contiguous selection.
- // Also allow message indicating you must have a selection.
- boolean hasSelection = model.hasSelection();
- boolean enable = model.getRowCount() > 0 &&
- (!hasSelection || (hasSelection && model.isContiguousSelection()));
- setEnabled(enable);
- }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/RedoChangeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/RedoChangeAction.java
new file mode 100644
index 0000000000..21990e6fff
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/RedoChangeAction.java
@@ -0,0 +1,62 @@
+/* ###
+ * 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.KeyBindingData;
+import generic.theme.GIcon;
+
+/**
+ * {@link RedoChangeAction} facilitates an redo of recently undone/reverted composite editor changes.
+ */
+public class RedoChangeAction extends CompositeEditorTableAction {
+
+ public static String DESCRIPTION = "Redo Change";
+ public final static String ACTION_NAME = "Redo Editor Change";
+ private final static String GROUP_NAME = UNDOREDO_ACTION_GROUP;
+ private final static Icon ICON = new GIcon("icon.redo");
+ private final static String[] POPUP_PATH = new String[] { DESCRIPTION };
+
+ public RedoChangeAction(CompositeEditorProvider provider) {
+ super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
+ setKeyBindingData(new KeyBindingData("ctrl shift Z"));
+ setDescription("Redo editor change");
+ }
+
+ @Override
+ public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
+ CompositeViewerDataTypeManager viewDTM = model.getViewDataTypeManager();
+ viewDTM.redo();
+ }
+
+ @Override
+ public boolean isEnabledForContext(ActionContext context) {
+ if (hasIncompleteFieldEntry()) {
+ return false;
+ }
+ CompositeViewerDataTypeManager viewDTM = model.getViewDataTypeManager();
+ boolean canRedo = viewDTM.canRedo();
+ setEnabled(canRedo);
+ String description = DESCRIPTION + (canRedo ? (": " + viewDTM.getRedoName()) : "");
+ setDescription(description);
+ return canRedo;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowComponentPathAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowComponentPathAction.java
index 2617276a66..0e7ef8686d 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowComponentPathAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowComponentPathAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -35,11 +35,13 @@ public class ShowComponentPathAction extends CompositeEditorTableAction {
public ShowComponentPathAction(CompositeEditorProvider provider) {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, MENU_PATH, null);
setDescription(DESCRIPTION);
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
String message = " ";
int index = model.getMinIndexSelected();
DataTypeComponent dtc = model.getComponent(index);
@@ -54,7 +56,7 @@ public class ShowComponentPathAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(model.isSingleComponentRowSelection());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isSingleComponentRowSelection();
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java
index ee1faf4f5c..a524b99e9f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -36,14 +36,16 @@ public class ShowDataTypeInTreeAction extends CompositeEditorTableAction {
private static final Icon ICON = new GIcon("icon.plugin.composite.editor.show.type");
public ShowDataTypeInTreeAction(CompositeEditorProvider provider) {
- super(provider, ACTION_NAME, TOOLBAR_GROUP, null /*popupPath*/,
- null /*menuPath*/, ICON);
+ super(provider, ACTION_NAME, TOOLBAR_GROUP, null /*popupPath*/, null /*menuPath*/, ICON);
setToolBarData(new ToolBarData(ICON, TOOLBAR_GROUP));
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
DataTypeManagerService dtmService = tool.getService(DataTypeManagerService.class);
DataTypeManager dtm = provider.getDataTypeManager();
DataTypePath path = provider.getDtPath();
@@ -52,10 +54,13 @@ public class ShowDataTypeInTreeAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
+ public boolean isEnabledForContext(ActionContext context) {
+ if (hasIncompleteFieldEntry()) {
+ return false;
+ }
DataTypeManager dtm = provider.getDataTypeManager();
DataTypePath path = provider.getDtPath();
DataType dt = dtm.getDataType(path);
- setEnabled(dt != null);
+ return dt != null;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java
index 442662e54e..b38ec4a3ba 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -62,6 +62,11 @@ class StructureEditorModel extends CompEditorModel {
hiddenColumns = Collections.unmodifiableList(additionalColumns);
}
+ @Override
+ public String getTypeName() {
+ return "Structure";
+ }
+
@Override
protected List getHiddenColumns() {
return hiddenColumns;
@@ -97,11 +102,6 @@ class StructureEditorModel extends CompEditorModel {
return COMMENT;
}
- @Override
- public void load(Composite dataType) {
- super.load(dataType);
- }
-
/**
* Returns the number of component rows in the viewer. There may be a
* blank row at the end for selecting. Therefore this number can be
@@ -213,37 +213,40 @@ class StructureEditorModel extends CompEditorModel {
if (currentLength == size) {
return;
}
- Structure structure = (Structure) viewComposite;
- if (currentLength > size) {
- int numComponents = structure.getNumComponents();
- DataTypeComponent dtc = structure.getComponentContaining(size);
- int ordinal = dtc.getOrdinal();
+ viewDTM.withTransaction("Set Size", () -> {
+ int length = currentLength;
+ Structure structure = (Structure) viewComposite;
+ if (length > size) {
+ int numComponents = structure.getNumComponents();
- // retain any zero-length components which have an offset equal the new size
- while (dtc.getOffset() == size && dtc.getLength() == 0 &&
- (ordinal + 1) < numComponents) {
- dtc = structure.getComponent(++ordinal);
- }
+ DataTypeComponent dtc = structure.getComponentContaining(size);
+ int ordinal = dtc.getOrdinal();
- // remove trailing components outside of new size
- for (int index = numComponents - 1; index >= ordinal; index--) {
- structure.delete(index);
- int bitFieldResidualBytes = structure.getNumComponents() - index;
- for (int i = 0; i < bitFieldResidualBytes; i++) {
- // bitfield removal may cause injection of undefined bytes - remove them
- structure.delete(index);
+ // retain any zero-length components which have an offset equal the new size
+ while (dtc.getOffset() == size && dtc.getLength() == 0 &&
+ (ordinal + 1) < numComponents) {
+ dtc = structure.getComponent(++ordinal);
}
+
+ // remove trailing components outside of new size
+ for (int index = numComponents - 1; index >= ordinal; index--) {
+ structure.delete(index);
+ int bitFieldResidualBytes = structure.getNumComponents() - index;
+ for (int i = 0; i < bitFieldResidualBytes; i++) {
+ // bitfield removal may cause injection of undefined bytes - remove them
+ structure.delete(index);
+ }
+ }
+ // structure may shrink too much from component removal - may need to grow
+ length = (viewComposite.isZeroLength()) ? 0 : viewComposite.getLength();
}
- // structure may shrink too much from component removal - may need to grow
- currentLength = (viewComposite.isZeroLength()) ? 0 : viewComposite.getLength();
- }
- if (currentLength < size) {
- // Increasing structure length.
- structure.growStructure(size - currentLength);
- }
- updateAndCheckChangeState();
- fireTableDataChanged();
+ if (length < size) {
+ // Increasing structure length.
+ structure.growStructure(size - length);
+ }
+ });
+ notifyCompositeChanged();
}
@Override
@@ -286,38 +289,36 @@ class StructureEditorModel extends CompEditorModel {
clearComponents(getSelectedComponentRows());
}
- @Override
- public void clearComponent(int ordinal) {
- ((Structure) viewComposite).clearComponent(ordinal);
- }
-
@Override
public void clearComponents(int[] indices) {
if (isEditingField()) {
endFieldEditing();
}
+
Arrays.sort(indices);
// work from back to front so our indices aren't affected by each component's clear.
- for (int i = indices.length - 1; i >= 0; i--) {
- DataTypeComponent comp = getComponent(indices[i]);
- if (comp == null) {
- continue; // must be on blank last line.
- }
- boolean isSelected = selection.containsEntirely(BigInteger.valueOf(indices[i]));
- int numBytes = comp.getLength();
- ((Structure) viewComposite).clearComponent(indices[i]);
+ viewDTM.withTransaction("Clear Components", () -> {
+ for (int i = indices.length - 1; i >= 0; i--) {
+ DataTypeComponent comp = getComponent(indices[i]);
+ if (comp == null) {
+ continue; // must be on blank last line.
+ }
+ boolean isSelected = selection.containsEntirely(BigInteger.valueOf(indices[i]));
+ int numBytes = comp.getLength();
+ ((Structure) viewComposite).clearComponent(indices[i]);
- // Adjust the selection due to the clear.
- adjustSelection(indices[i] + 1, numBytes - 1);
- if (isSelected && numBytes > 1) {
- selection.addRange(indices[i] + 1, indices[i] + numBytes);
- }
+ // Adjust the selection due to the clear.
+ adjustSelection(indices[i] + 1, numBytes - 1);
+ if (isSelected && numBytes > 1) {
+ selection.addRange(indices[i] + 1, indices[i] + numBytes);
+ }
- if (indices[i] > 0) {
- consumeByComponent(indices[i] - 1);
+ if (indices[i] > 0) {
+ consumeByComponent(indices[i] - 1);
+ }
}
- }
+ });
componentEdited();
}
@@ -374,14 +375,16 @@ class StructureEditorModel extends CompEditorModel {
int dtLen = dt.getLength();
checkIsAllowableDataType(dt);
- int startIndex = index + 1;
- if (isShowingUndefinedBytes() && (dt != DataType.DEFAULT)) {
- int endIndex = startIndex + (dtLen * multiple) - 1;
- if (startIndex < getNumComponents()) {
- deleteComponentRange(startIndex, endIndex, monitor);
+ viewDTM.withTransaction("Duplicate Components", () -> {
+ int startIndex = index + 1;
+ if (isShowingUndefinedBytes() && (dt != DataType.DEFAULT)) {
+ int endIndex = startIndex + (dtLen * multiple) - 1;
+ if (startIndex < getNumComponents()) {
+ deleteComponentRange(startIndex, endIndex, monitor);
+ }
}
- }
- insertComponentMultiple(startIndex, dt, originalComp.getLength(), multiple, monitor);
+ insertComponentMultiple(startIndex, dt, originalComp.getLength(), multiple, monitor);
+ });
// Adjust the selection since we added some components. Select last component added.
// Ensure that last added component is selected to allow for repeated duplication
@@ -407,24 +410,25 @@ class StructureEditorModel extends CompEditorModel {
}
int len = getLength();
- DataTypeComponent comp = deleteComponentAndResidual(startIndex - 1);
-
- try {
- if (!isPackingEnabled() && comp.isBitFieldComponent()) {
- // insert residual undefined bytes before inserting non-packed bitfield
- int lenChange = len - getLength();
- insert(endIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY);
+ return viewDTM.withTransaction("Shift Up", () -> {
+ DataTypeComponent comp = deleteComponentAndResidual(startIndex - 1);
+ try {
+ if (!isPackingEnabled() && comp.isBitFieldComponent()) {
+ // insert residual undefined bytes before inserting non-packed bitfield
+ int lenChange = len - getLength();
+ insert(endIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY);
+ }
+ insert(endIndex, comp.getDataType(), comp.getLength(), comp.getFieldName(),
+ comp.getComment());
}
- insert(endIndex, comp.getDataType(), comp.getLength(), comp.getFieldName(),
- comp.getComment());
- }
- catch (CancelledException e) {
- // can't happen while using a dummy monitor
- }
- catch (InvalidDataTypeException e) {
- return false;
- }
- return true;
+ catch (CancelledException e) {
+ // can't happen while using a dummy monitor
+ }
+ catch (InvalidDataTypeException e) {
+ return false;
+ }
+ return true;
+ });
}
/**
@@ -443,24 +447,25 @@ class StructureEditorModel extends CompEditorModel {
}
int len = getLength();
- DataTypeComponent comp = deleteComponentAndResidual(endIndex + 1);
-
- try {
- if (!isPackingEnabled() && comp.isBitFieldComponent()) {
- // insert residual undefined bytes before inserting non-packed bitfield
- int lenChange = len - getLength();
- insert(startIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY);
+ return viewDTM.withTransaction("Shift Down", () -> {
+ DataTypeComponent comp = deleteComponentAndResidual(endIndex + 1);
+ try {
+ if (!isPackingEnabled() && comp.isBitFieldComponent()) {
+ // insert residual undefined bytes before inserting non-packed bitfield
+ int lenChange = len - getLength();
+ insert(startIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY);
+ }
+ insert(startIndex, comp.getDataType(), comp.getLength(), comp.getFieldName(),
+ comp.getComment());
}
- insert(startIndex, comp.getDataType(), comp.getLength(), comp.getFieldName(),
- comp.getComment());
- }
- catch (CancelledException e) {
- // can't happen while using a dummy monitor
- }
- catch (InvalidDataTypeException e) {
- return false;
- }
- return true;
+ catch (CancelledException e) {
+ // can't happen while using a dummy monitor
+ }
+ catch (InvalidDataTypeException e) {
+ return false;
+ }
+ return true;
+ });
}
private DataTypeComponent deleteComponentAndResidual(int index) {
@@ -887,23 +892,27 @@ class StructureEditorModel extends CompEditorModel {
String comment) throws InvalidDataTypeException {
checkIsAllowableDataType(dataType);
try {
- DataTypeComponent dtc;
- if (isPackingEnabled() || !(dataType instanceof BitFieldDataType)) {
- dtc = ((Structure) viewComposite).insert(rowIndex, dataType, length, name, comment);
- }
- else {
- BitFieldDataType bitfield = (BitFieldDataType) dataType;
- dtc = ((Structure) viewComposite).insertBitField(rowIndex, length,
- bitfield.getBitOffset(), bitfield.getBaseDataType(),
- bitfield.getDeclaredBitSize(), name, comment);
- }
- if (rowIndex <= row) {
- row++;
- }
- adjustSelection(rowIndex, 1);
- // Consume undefined bytes that may have been added, if needed.
- consumeByComponent(rowIndex - 1);
- return dtc;
+ return viewDTM.withTransaction("Insert Component", () -> {
+ DataTypeComponent dtc;
+ if (isPackingEnabled() || !(dataType instanceof BitFieldDataType)) {
+ dtc = ((Structure) viewComposite).insert(rowIndex, dataType, length, name,
+ comment);
+ }
+ else {
+ BitFieldDataType bitfield = (BitFieldDataType) dataType;
+ dtc = ((Structure) viewComposite).insertBitField(rowIndex, length,
+ bitfield.getBitOffset(), bitfield.getBaseDataType(),
+ bitfield.getDeclaredBitSize(), name, comment);
+ }
+ if (rowIndex <= currentEditRow) {
+ currentEditRow++;
+ }
+ adjustSelection(rowIndex, 1);
+ // Consume undefined bytes that may have been added, if needed.
+ consumeByComponent(rowIndex - 1);
+
+ return dtc;
+ });
}
catch (IllegalArgumentException exc) {
throw new InvalidDataTypeException(exc.getMessage());
@@ -918,20 +927,21 @@ class StructureEditorModel extends CompEditorModel {
int componentOrdinal = convertRowToOrdinal(rowIndex);
monitor.initialize(numCopies);
try {
+ viewDTM.withTransaction("Insert Multiple", () -> {
+ for (int i = 0; i < numCopies; i++) {
+ monitor.checkCancelled();
+ monitor.setMessage("Inserting " + (i + 1) + " of " + numCopies);
+ viewComposite.insert(componentOrdinal, dataType, length);
+ monitor.incrementProgress(1);
+ }
- for (int i = 0; i < numCopies; i++) {
- monitor.checkCancelled();
- monitor.setMessage("Inserting " + (i + 1) + " of " + numCopies);
- viewComposite.insert(componentOrdinal, dataType, length);
- monitor.incrementProgress(1);
- }
-
- if (rowIndex <= row) {
- row += numCopies;
- }
- adjustSelection(componentOrdinal, numCopies);
- // Consume undefined bytes that may have been added, if needed.
- consumeByComponent(componentOrdinal - numCopies);
+ if (rowIndex <= currentEditRow) {
+ currentEditRow += numCopies;
+ }
+ adjustSelection(componentOrdinal, numCopies);
+ // Consume undefined bytes that may have been added, if needed.
+ consumeByComponent(componentOrdinal - numCopies);
+ });
}
catch (IllegalArgumentException exc) {
throw new InvalidDataTypeException(exc.getMessage());
@@ -951,8 +961,10 @@ class StructureEditorModel extends CompEditorModel {
// FreeForm editing mode (showing Undefined Bytes).
if (isShowingUndefinedBytes() && !isAtEnd(rowIndex)) {
int origLen = getComponent(rowIndex).getLength();
- dtc = ((Structure) viewComposite).replace(componentOrdinal, dataType, length, name,
- comment);
+ dtc = viewDTM.withTransaction("Replace Component", () -> {
+ return ((Structure) viewComposite).replace(componentOrdinal, dataType, length,
+ name, comment);
+ });
diffLen = origLen - dtc.getLength();
int nextRowIndex = rowIndex + 1;
if (diffLen < 0) {
@@ -965,14 +977,16 @@ class StructureEditorModel extends CompEditorModel {
selection.addRange(nextRowIndex, nextRowIndex + diffLen);
}
}
- if (rowIndex < row) {
- row += diffLen;
+ if (rowIndex < currentEditRow) {
+ currentEditRow += diffLen;
}
}
else {
- ((Structure) viewComposite).delete(componentOrdinal);
- dtc = ((Structure) viewComposite).insert(componentOrdinal, dataType, length, name,
- comment);
+ dtc = viewDTM.withTransaction("Replace Component", () -> {
+ ((Structure) viewComposite).delete(componentOrdinal);
+ return ((Structure) viewComposite).insert(componentOrdinal, dataType, length,
+ name, comment);
+ });
}
return dtc;
}
@@ -1013,47 +1027,53 @@ class StructureEditorModel extends CompEditorModel {
overlap.intersect(selection);
boolean replacedSelected = (overlap.getNumRanges() > 0);
- // Remove the selected components.
- deleteComponentRange(startRowIndex, endRowIndex, monitor);
-
- int beginUndefs = startRowIndex + numComps;
- // Create the new components.
- insertMultiple(startRowIndex, datatype, length, numComps, monitor);
- int indexAfterMultiple = startRowIndex + numComps;
- if (replacedSelected) {
- selection.addRange(startRowIndex, indexAfterMultiple);
- fixSelection();
- }
-
- DataTypeComponent comp = getComponent(startRowIndex);
- // Set the field name and comment the same as before
+ int txId = viewDTM.startTransaction("Replace Multiple");
try {
- comp.setFieldName(fieldName);
- }
- catch (DuplicateNameException exc) {
- Msg.showError(this, null, null, null);
- }
- comp.setComment(comment);
+ // Remove the selected components.
+ deleteComponentRange(startRowIndex, endRowIndex, monitor);
- // Create any needed undefined data types.
- int remainingLength = numBytesInRange - (numComps * length);
- if (remainingLength > 0 && isShowingUndefinedBytes()) {
+ int beginUndefs = startRowIndex + numComps;
+ // Create the new components.
+ insertMultiple(startRowIndex, datatype, length, numComps, monitor);
+ int indexAfterMultiple = startRowIndex + numComps;
+ if (replacedSelected) {
+ selection.addRange(startRowIndex, indexAfterMultiple);
+ fixSelection();
+ }
+
+ DataTypeComponent comp = getComponent(startRowIndex);
+ // Set the field name and comment the same as before
try {
- insertComponentMultiple(beginUndefs, DataType.DEFAULT, DataType.DEFAULT.getLength(),
- remainingLength, monitor);
- if (replacedSelected) {
- selection.addRange(indexAfterMultiple, indexAfterMultiple + remainingLength);
+ comp.setFieldName(fieldName);
+ }
+ catch (DuplicateNameException exc) {
+ Msg.showError(this, null, null, null);
+ }
+ comp.setComment(comment);
+
+ // Create any needed undefined data types.
+ int remainingLength = numBytesInRange - (numComps * length);
+ if (remainingLength > 0 && isShowingUndefinedBytes()) {
+ try {
+ insertComponentMultiple(beginUndefs, DataType.DEFAULT,
+ DataType.DEFAULT.getLength(), remainingLength, monitor);
+ if (replacedSelected) {
+ selection.addRange(indexAfterMultiple,
+ indexAfterMultiple + remainingLength);
+ }
+ }
+ catch (InvalidDataTypeException idte) {
+ Msg.showError(this, null, "Structure Editor Error", idte.getMessage());
}
}
- catch (InvalidDataTypeException idte) {
- Msg.showError(this, null, "Structure Editor Error", idte.getMessage());
+ else if (remainingLength < 0) {
+ return false;
}
+ return true;
}
- else if (remainingLength < 0) {
- return false;
+ finally {
+ viewDTM.endTransaction(txId, true);
}
-
- return true;
}
@Override
@@ -1068,28 +1088,30 @@ class StructureEditorModel extends CompEditorModel {
}
}
- /**
- *
- */
@Override
void removeDtFromComponents(Composite comp) {
DataType newDt = viewDTM.getDataType(comp.getDataTypePath());
if (newDt == null) {
return;
}
- int num = getNumComponents();
- for (int i = num - 1; i >= 0; i--) {
- DataTypeComponent dtc = getComponent(i);
- DataType dt = dtc.getDataType();
- if (dt instanceof Composite) {
- Composite dtcComp = (Composite) dt;
- if (dtcComp.isPartOf(newDt)) {
- clearComponents(new int[] { i });
- String msg =
- "Components containing " + comp.getDisplayName() + " were cleared.";
- setStatus(msg, true);
+ boolean clearedComponents = viewDTM.withTransaction("Remove Components", () -> {
+ boolean cleared = false;
+ int num = getNumComponents();
+ for (int i = num - 1; i >= 0; i--) {
+ DataTypeComponent dtc = getComponent(i);
+ DataType dt = dtc.getDataType();
+ if (dt instanceof Composite) {
+ Composite dtcComp = (Composite) dt;
+ if (dtcComp.isPartOf(newDt)) {
+ clearComponents(new int[] { i });
+ cleared = true;
+ }
}
}
+ return cleared;
+ });
+ if (clearedComponents) {
+ setStatus("Components containing " + comp.getDisplayName() + " were cleared.", true);
}
}
@@ -1203,27 +1225,35 @@ class StructureEditorModel extends CompEditorModel {
}
DataType addedDataType = createDataTypeInOriginalDTM(structureDataType);
- if (viewComposite.isPackingEnabled()) {
- deleteSelectedComponents();
- insert(minRow, addedDataType, addedDataType.getLength());
+
+ int txId = viewDTM.startTransaction("Replace w/Structure");
+ try {
+ if (viewComposite.isPackingEnabled()) {
+ deleteSelectedComponents();
+ insert(minRow, addedDataType, addedDataType.getLength());
+ }
+ else {
+ int adjustmentBytes = 0;
+ if (firstDtc != null && firstDtc.isBitFieldComponent() && minRow > 0) {
+ DataTypeComponent dtc = getComponent(minRow - 1);
+ if (dtc.getEndOffset() == firstDtc.getOffset()) {
+ ++adjustmentBytes;
+ }
+ }
+ if (lastDtc != null && lastDtc.isBitFieldComponent() &&
+ maxRow < getNumComponents()) {
+ DataTypeComponent dtc = getComponent(maxRow);
+ if (dtc.getOffset() == lastDtc.getEndOffset()) {
+ ++adjustmentBytes;
+ }
+ }
+ clearSelectedComponents();
+ insertMultiple(minRow, DataType.DEFAULT, 1, adjustmentBytes, monitor);
+ replace(minRow, addedDataType, addedDataType.getLength());
+ }
}
- else {
- int adjustmentBytes = 0;
- if (firstDtc != null && firstDtc.isBitFieldComponent() && minRow > 0) {
- DataTypeComponent dtc = getComponent(minRow - 1);
- if (dtc.getEndOffset() == firstDtc.getOffset()) {
- ++adjustmentBytes;
- }
- }
- if (lastDtc != null && lastDtc.isBitFieldComponent() && maxRow < getNumComponents()) {
- DataTypeComponent dtc = getComponent(maxRow);
- if (dtc.getOffset() == lastDtc.getEndOffset()) {
- ++adjustmentBytes;
- }
- }
- clearSelectedComponents();
- insertMultiple(minRow, DataType.DEFAULT, 1, adjustmentBytes, monitor);
- replace(minRow, addedDataType, addedDataType.getLength());
+ finally {
+ viewDTM.endTransaction(txId, true);
}
}
@@ -1263,7 +1293,6 @@ class StructureEditorModel extends CompEditorModel {
private DataType createDataTypeInOriginalDTM(StructureDataType structureDataType) {
boolean commit = false;
- DataTypeManager originalDTM = getOriginalDataTypeManager();
int transactionID =
originalDTM.startTransaction("Create structure " + structureDataType.getName());
try {
@@ -1300,81 +1329,83 @@ class StructureEditorModel extends CompEditorModel {
endFieldEditing();
}
- Structure viewStruct = (Structure) viewComposite;
+ viewDTM.withTransaction("Unpack Component", () -> {
+ Structure viewStruct = (Structure) viewComposite;
- // Get the field name and comment before removing.
- String fieldName = currentComp.getFieldName();
- String comment = currentComp.getComment();
- int numComps = 0;
- // This component is an array so unpackage it.
- if (currentDataType instanceof Array) {
- Array array = (Array) currentDataType;
- int elementLen = array.getElementLength();
- numComps = array.getNumElements();
- // Remove the array.
- delete(componentOrdinal);
- if (numComps > 0) {
- // Add the array's elements
- try {
- DataType dt = array.getDataType();
- insertMultiple(rowIndex, dt, elementLen, numComps, monitor);
- }
- catch (InvalidDataTypeException ie) {
- // Do nothing.
- }
- catch (OutOfMemoryError memExc) {
- throw memExc; // rethrow the exception.
+ // Get the field name and comment before removing.
+ String fieldName = currentComp.getFieldName();
+ String comment = currentComp.getComment();
+ int numComps = 0;
+ // This component is an array so unpackage it.
+ if (currentDataType instanceof Array) {
+ Array array = (Array) currentDataType;
+ int elementLen = array.getElementLength();
+ numComps = array.getNumElements();
+ // Remove the array.
+ delete(componentOrdinal);
+ if (numComps > 0) {
+ // Add the array's elements
+ try {
+ DataType dt = array.getDataType();
+ insertMultiple(rowIndex, dt, elementLen, numComps, monitor);
+ }
+ catch (InvalidDataTypeException ie) {
+ // Do nothing.
+ }
+ catch (OutOfMemoryError memExc) {
+ throw memExc; // rethrow the exception.
+ }
}
}
- }
- // This component is a structure so unpackage it.
- else if (currentDataType instanceof Structure) {
- Structure struct = (Structure) currentDataType;
- numComps = struct.getNumComponents();
- if (numComps > 0) {
- // Remove the structure.
- int currentOffset = currentComp.getOffset();
- deleteComponent(rowIndex);
+ // This component is a structure so unpackage it.
+ else if (currentDataType instanceof Structure) {
+ Structure struct = (Structure) currentDataType;
+ numComps = struct.getNumComponents();
+ if (numComps > 0) {
+ // Remove the structure.
+ int currentOffset = currentComp.getOffset();
+ deleteComponent(rowIndex);
- // Add the structure's elements
- for (int i = 0; i < numComps; i++) {
- DataTypeComponent dtc = struct.getComponent(i);
- DataType dt = dtc.getDataType();
- int compLength = dtc.getLength();
- if (!isPackingEnabled()) {
- if (dtc.isBitFieldComponent()) {
- BitFieldDataType bitfield = (BitFieldDataType) dt;
- viewStruct.insertBitFieldAt(currentOffset + dtc.getOffset(), compLength,
- bitfield.getBitOffset(), bitfield.getBaseDataType(),
- bitfield.getDeclaredBitSize(), dtc.getFieldName(),
- dtc.getComment());
+ // Add the structure's elements
+ for (int i = 0; i < numComps; i++) {
+ DataTypeComponent dtc = struct.getComponent(i);
+ DataType dt = dtc.getDataType();
+ int compLength = dtc.getLength();
+ if (!isPackingEnabled()) {
+ if (dtc.isBitFieldComponent()) {
+ BitFieldDataType bitfield = (BitFieldDataType) dt;
+ viewStruct.insertBitFieldAt(currentOffset + dtc.getOffset(),
+ compLength, bitfield.getBitOffset(), bitfield.getBaseDataType(),
+ bitfield.getDeclaredBitSize(), dtc.getFieldName(),
+ dtc.getComment());
+ }
+ else {
+ viewStruct.insertAtOffset(currentOffset + dtc.getOffset(), dt,
+ compLength, dtc.getFieldName(), dtc.getComment());
+ }
}
else {
- viewStruct.insertAtOffset(currentOffset + dtc.getOffset(), dt,
- compLength, dtc.getFieldName(), dtc.getComment());
+ insert(rowIndex + i, dt, compLength, dtc.getFieldName(),
+ dtc.getComment());
}
}
- else {
- insert(rowIndex + i, dt, compLength, dtc.getFieldName(), dtc.getComment());
- }
}
}
- }
- selection.clear();
- selection.addRange(rowIndex, rowIndex + numComps);
+ selection.clear();
+ selection.addRange(rowIndex, rowIndex + numComps);
- DataTypeComponent comp = getComponent(rowIndex);
- // Set the field name and comment the same as before
- try {
- if (comp.getFieldName() == null) {
- comp.setFieldName(fieldName);
+ DataTypeComponent comp = getComponent(rowIndex);
+ // Set the field name and comment the same as before
+ try {
+ if (comp.getFieldName() == null) {
+ comp.setFieldName(fieldName);
+ }
}
- }
- catch (DuplicateNameException exc) {
- Msg.showError(this, null, null, null);
- }
- comp.setComment(comment);
-
+ catch (DuplicateNameException exc) {
+ Msg.showError(this, null, null, null);
+ }
+ comp.setComment(comment);
+ });
fixSelection();
componentEdited();
selectionChanged();
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java
index 84a44951cd..63f1e25433 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java
@@ -59,6 +59,8 @@ public class StructureEditorProvider extends CompositeEditorProvider {
//@formatter:off
return new CompositeEditorTableAction[] {
new ApplyAction(this),
+ new UndoChangeAction(this),
+ new RedoChangeAction(this),
// new ToggleLockAction(this),
new InsertUndefinedAction(this),
new MoveUpAction(this),
@@ -117,12 +119,11 @@ public class StructureEditorProvider extends CompositeEditorProvider {
int[] selectedRows = editorModel.getSelectedRows();
-// TODO: Add w/ GP-4740 merge
-// if (editorPanel.hasInvalidEntry() || editorPanel.hasUncomittedEntry() ||
-// selectedRows.length != 1 || editorModel.viewComposite.isPackingEnabled()) {
-// Msg.error(this, "Unsupported add bitfield editor use");
-// return;
-// }
+ if (editorPanel.hasInvalidEntry() || editorPanel.hasUncomittedEntry() ||
+ selectedRows.length != 1 || editorModel.viewComposite.isPackingEnabled()) {
+ Msg.error(this, "Unsupported add bitfield editor use");
+ return;
+ }
bitFieldEditor =
new BitFieldEditorDialog(editorModel.viewComposite, dtmService, -(selectedRows[0] + 1),
@@ -164,5 +165,4 @@ public class StructureEditorProvider extends CompositeEditorProvider {
}
return null;
}
-
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UndoChangeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UndoChangeAction.java
new file mode 100644
index 0000000000..aa44d786ed
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UndoChangeAction.java
@@ -0,0 +1,63 @@
+/* ###
+ * 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.KeyBindingData;
+import generic.theme.GIcon;
+
+/**
+ * {@link UndoChangeAction} facilitates an undo of recent composite editor changes.
+ */
+public class UndoChangeAction extends CompositeEditorTableAction {
+
+ public static String DESCRIPTION = "Undo Change";
+ public final static String ACTION_NAME = "Undo Editor Change";
+ private final static String GROUP_NAME = UNDOREDO_ACTION_GROUP;
+ private final static Icon ICON = new GIcon("icon.undo");
+ private final static String[] POPUP_PATH = new String[] { DESCRIPTION };
+
+ public UndoChangeAction(CompositeEditorProvider provider) {
+ super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
+ setKeyBindingData(new KeyBindingData("ctrl Z"));
+ setDescription(DESCRIPTION);
+ }
+
+ @Override
+ public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
+ CompositeViewerDataTypeManager viewDTM = model.getViewDataTypeManager();
+ viewDTM.undo();
+ }
+
+ @Override
+ public boolean isEnabledForContext(ActionContext context) {
+ if (hasIncompleteFieldEntry()) {
+ return false;
+ }
+ CompositeViewerDataTypeManager viewDTM = model.getViewDataTypeManager();
+ boolean canUndo = viewDTM.canUndo();
+ setEnabled(canUndo);
+ String description = DESCRIPTION + (canUndo ? (": " + viewDTM.getUndoName()) : "");
+ setDescription(description);
+ return canUndo;
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java
index 329e481068..fa19be5176 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,7 +16,8 @@
package ghidra.app.plugin.core.compositeeditor;
import java.math.BigInteger;
-import java.util.NoSuchElementException;
+
+import javax.help.UnsupportedOperationException;
import docking.widgets.fieldpanel.support.FieldRange;
import docking.widgets.fieldpanel.support.FieldSelection;
@@ -63,6 +64,11 @@ class UnionEditorModel extends CompEditorModel {
}
+ @Override
+ public String getTypeName() {
+ return "Union";
+ }
+
@Override
public int getOffsetColumn() {
return -1;
@@ -171,11 +177,6 @@ class UnionEditorModel extends CompEditorModel {
}
}
- @Override
- public void clearComponent(int rowIndex) {
- // clearing not supported
- }
-
/**
* Clear the selected components
*
@@ -345,8 +346,9 @@ class UnionEditorModel extends CompEditorModel {
if (range != null) {
// Determine the number of bytes.
// Get the size of the range.
- for (int i =
- range.getStart().getIndex().intValue(); i < range.getEnd().getIndex().intValue(); i++) {
+ for (int i = range.getStart().getIndex().intValue(); i < range.getEnd()
+ .getIndex()
+ .intValue(); i++) {
DataTypeComponent comp = getComponent(i);
numBytesInRange = Math.max(numBytesInRange, comp.getLength());
}
@@ -378,10 +380,10 @@ class UnionEditorModel extends CompEditorModel {
String comment) throws InvalidDataTypeException {
checkIsAllowableDataType(dataType);
try {
- DataTypeComponent dtc =
- ((Union) viewComposite).insert(rowIndex, dataType, length, name, comment);
- if (rowIndex <= row) {
- row++;
+ DataTypeComponent dtc = viewDTM.withTransaction("Add Component",
+ () -> ((Union) viewComposite).insert(rowIndex, dataType, length, name, comment));
+ if (rowIndex <= currentEditRow) {
+ currentEditRow++;
}
adjustSelection(rowIndex, 1);
notifyCompositeChanged();
@@ -395,12 +397,17 @@ class UnionEditorModel extends CompEditorModel {
@Override
public void insert(int rowIndex, DataType dataType, int length, int numCopies,
TaskMonitor monitor) throws InvalidDataTypeException, CancelledException {
-
- monitor.initialize(numCopies);
- for (int i = 0; i < numCopies; i++) {
- monitor.checkCancelled();
- insert(rowIndex + i, dataType, length, null, null);
- monitor.incrementProgress(1);
+ int txId = viewDTM.startTransaction("Insert Multiple");
+ try {
+ monitor.initialize(numCopies);
+ for (int i = 0; i < numCopies; i++) {
+ monitor.checkCancelled();
+ insert(rowIndex + i, dataType, length, null, null);
+ monitor.incrementProgress(1);
+ }
+ }
+ finally {
+ viewDTM.endTransaction(txId, true);
}
}
@@ -410,9 +417,10 @@ class UnionEditorModel extends CompEditorModel {
checkIsAllowableDataType(dataType);
try {
boolean isSelected = selection.containsEntirely(BigInteger.valueOf(rowIndex));
- ((Union) viewComposite).delete(rowIndex);
- DataTypeComponent dtc =
- ((Union) viewComposite).insert(rowIndex, dataType, length, name, comment);
+ DataTypeComponent dtc = viewDTM.withTransaction("Replace Component", () -> {
+ ((Union) viewComposite).delete(rowIndex);
+ return ((Union) viewComposite).insert(rowIndex, dataType, length, name, comment);
+ });
if (isSelected) {
selection.addRange(rowIndex, rowIndex + 1);
fixSelection();
@@ -456,12 +464,20 @@ class UnionEditorModel extends CompEditorModel {
FieldSelection overlap = new FieldSelection();
overlap.addRange(startRowIndex, endRowIndex + 1);
overlap.intersect(selection);
-
- // Union just replaces entire selection range with single instance of new component.
- deleteComponentRange(startRowIndex, endRowIndex, monitor);
-
boolean replacedSelected = (overlap.getNumRanges() > 0);
- insert(startRowIndex, datatype, length, null, null);
+
+ int txId = viewDTM.startTransaction("Insert Multiple");
+ try {
+
+ // Union just replaces entire selection range with single instance of new component.
+ deleteComponentRange(startRowIndex, endRowIndex, monitor);
+
+ insert(startRowIndex, datatype, length, null, null);
+ }
+ finally {
+ viewDTM.endTransaction(txId, true);
+ }
+
if (replacedSelected) {
selection.addRange(startRowIndex, startRowIndex + 1);
fixSelection();
@@ -475,30 +491,38 @@ class UnionEditorModel extends CompEditorModel {
}
@Override
- public void clearComponents(int[] rows) throws UsrException {
- throw new UsrException("Can't clear components in a union.");
+ protected void clearComponent(int rowIndex) {
+ throw new UnsupportedOperationException("Can't clear components in a union.");
+ }
+
+ @Override
+ public void clearComponents(int[] rows) {
+ throw new UnsupportedOperationException("Can't clear components in a union.");
}
@Override
void removeDtFromComponents(Composite comp) {
- DataType newDt = viewDTM.getDataType(comp.getDataTypePath());
+ DataTypePath path = comp.getDataTypePath();
+ DataType newDt = viewDTM.getDataType(path);
if (newDt == null) {
return;
}
- int num = getNumComponents();
- for (int i = num - 1; i >= 0; i--) {
- DataTypeComponent dtc = getComponent(i);
- DataType dt = dtc.getDataType();
- if (dt instanceof Composite) {
- Composite dtcComp = (Composite) dt;
- if (dtcComp.isPartOf(newDt)) {
- deleteComponent(i);
- String msg =
- "Components containing " + comp.getDisplayName() + " were removed.";
- setStatus(msg, true);
+ viewDTM.withTransaction("Remove use of " + path, () -> {
+ int num = getNumComponents();
+ for (int i = num - 1; i >= 0; i--) {
+ DataTypeComponent dtc = getComponent(i);
+ DataType dt = dtc.getDataType();
+ if (dt instanceof Composite) {
+ Composite dtcComp = (Composite) dt;
+ if (dtcComp.isPartOf(newDt)) {
+ deleteComponent(i);
+ String msg =
+ "Components containing " + comp.getDisplayName() + " were removed.";
+ setStatus(msg, true);
+ }
}
}
- }
+ });
}
/**
@@ -533,22 +557,6 @@ class UnionEditorModel extends CompEditorModel {
return 0;
}
- /**
- * Consumes the number of undefined bytes requested if they are available.
- *
- * @param rowIndex index of the row (component).
- * @param numDesired the number of Undefined bytes desired.
- * @return the number of components removed from the structure when the
- * bytes were consumed.
- * @throws java.util.NoSuchElementException if the index is invalid.
- * @throws InvalidDataTypeException if there aren't enough bytes.
- */
- @Override
- protected int consumeUndefinedBytes(int rowIndex, int numDesired)
- throws NoSuchElementException, InvalidDataTypeException {
- return 0;
- }
-
@Override
public boolean isShowingUndefinedBytes() {
return false;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorPanel.java
index ba01375759..32c5925f7f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorPanel.java
@@ -28,8 +28,4 @@ public class UnionEditorPanel extends CompEditorPanel {
return null;
}
- @Override
- protected boolean choosePacking() {
- return true; // packing is not destructive to unions, so safe to use without prompting
- }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java
index 9a17a3086e..a4c6edd7e0 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java
@@ -53,6 +53,8 @@ public class UnionEditorProvider extends CompositeEditorProvider {
//@formatter:off
return new CompositeEditorTableAction[] {
new ApplyAction(this),
+ new UndoChangeAction(this),
+ new RedoChangeAction(this),
new MoveUpAction(this),
new MoveDownAction(this),
new DuplicateAction(this),
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnpackageAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnpackageAction.java
index d21be580f0..65e191d16a 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnpackageAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnpackageAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -46,11 +46,13 @@ public class UnpackageAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
setDescription(DESCRIPTION);
setKeyBindingData(new KeyBindingData(KEY_STROKE));
- adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
+ if (!isEnabledForContext(context)) {
+ return;
+ }
// If lots of components, verify the user really wants to unpackage.
int currentRowIndex =
model.getSelection().getFieldRange(0).getStart().getIndex().intValue();
@@ -60,7 +62,7 @@ public class UnpackageAction extends CompositeEditorTableAction {
String title = "Continue with unpackage?";
int response =
OptionDialog.showYesNoDialog(model.getProvider().getComponent(), title, question);
- if (response != 1) { // User did not select yes.
+ if (response != OptionDialog.YES_OPTION) { // User did not select yes.
return;
}
}
@@ -85,7 +87,7 @@ public class UnpackageAction extends CompositeEditorTableAction {
}
@Override
- public void adjustEnablement() {
- setEnabled(model.isUnpackageAllowed());
+ public boolean isEnabledForContext(ActionContext context) {
+ return !hasIncompleteFieldEntry() && model.isUnpackageAllowed();
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java
index a7708d1455..cfd8c57280 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -165,12 +165,6 @@ public class DataTypesProvider extends ComponentProviderAdapter {
addLocalAction(new DeleteArchiveAction(plugin));
addLocalAction(new RenameAction(plugin));
addLocalAction(new EditAction(plugin));
- // NOTE: it make very little sense to blindly enable packing
-// addLocalAction(new PackDataTypeAction(plugin));
-// addLocalAction( new PackDataTypeAction( plugin ));
-// addLocalAction( new PackSizeDataTypeAction( plugin ));
-// addLocalAction(new PackAllDataTypesAction(plugin));
-// addLocalAction( new DefineDataTypeAlignmentAction( plugin ));
addLocalAction(new CreateEnumFromSelectionAction(plugin));
// File group
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java
index e2a33ab6cc..0f03830448 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -109,8 +109,7 @@ public class DeleteAction extends DockingAction {
"Confirm Delete Operation",
"Are you sure you want to delete selected\n" +
"data types and/or categories?\n\n" +
- "Note: There is no undo for archives and\n" +
- "changes may trigger the removal of related\n" +
+ "Note: Changes may trigger the removal of related\n" +
"data types, components and defined data.)");
//@formatter:on
if (choice != OptionDialog.OPTION_ONE) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/Pack1DataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/Pack1DataTypeAction.java
deleted file mode 100644
index 06790fcb92..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/Pack1DataTypeAction.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.plugin.core.datamgr.actions;
-
-import javax.swing.tree.TreePath;
-
-import docking.ActionContext;
-import docking.action.DockingAction;
-import docking.action.MenuData;
-import docking.widgets.tree.GTree;
-import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
-import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
-import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode;
-import ghidra.program.model.data.*;
-import ghidra.util.Msg;
-
-public class Pack1DataTypeAction extends DockingAction {
-
- private DataTypeManagerPlugin plugin;
-
- public Pack1DataTypeAction(DataTypeManagerPlugin plugin) {
- super("Pack1 Data Type", plugin.getName());
- this.plugin = plugin;
- setPopupMenuData(new MenuData(new String[] { "Pack (1)" }, "Edit"));
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- Object contextObject = context.getContextObject();
- if (!(contextObject instanceof GTree)) {
- return false;
- }
- GTree gTree = (GTree) contextObject;
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- if (selectionPaths.length != 1) {
- return false;
- }
- DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent();
-
- if (!(node instanceof DataTypeNode)) {
- return false;
- }
- setEnabled(node.isModifiable());
- return true;
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- GTree gTree = (GTree) context.getContextObject();
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- if (selectionPaths.length != 1) {
- Msg.error(this, "Pack is only allowed on an individual data type.");
- return;
- }
- TreePath treePath = selectionPaths[0];
- final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent();
- DataType dataType = dataTypeNode.getDataType();
- DataTypeManager dataTypeManager = dataType.getDataTypeManager();
- if (dataTypeManager == null) {
- Msg.error(this,
- "Can't pack data type " + dataType.getName() + " without a data type manager.");
- return;
- }
-
- int transactionID = -1;
- boolean commit = false;
- try {
- // start a transaction
- transactionID = dataTypeManager.startTransaction("Pack(1) " + dataType.getName());
- packDataType(dataType);
- commit = true;
- }
- finally {
- // commit the changes
- dataTypeManager.endTransaction(transactionID, commit);
- }
- }
-
- private void packDataType(DataType dataType) {
- if (!(dataType instanceof Composite)) {
- Msg.error(this,
- "Can't pack data type " + dataType.getName() + ". It's not a composite.");
- return;
- }
- ((Composite) dataType).pack(1);
- }
-
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackAllDataTypesAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackAllDataTypesAction.java
deleted file mode 100644
index de63eb7293..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackAllDataTypesAction.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.plugin.core.datamgr.actions;
-
-import java.util.Iterator;
-
-import javax.swing.tree.TreePath;
-
-import docking.ActionContext;
-import docking.action.DockingAction;
-import docking.action.MenuData;
-import docking.widgets.OptionDialog;
-import docking.widgets.tree.GTree;
-import docking.widgets.tree.GTreeNode;
-import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
-import ghidra.app.plugin.core.datamgr.archive.Archive;
-import ghidra.app.plugin.core.datamgr.tree.*;
-import ghidra.program.model.data.*;
-import ghidra.util.Msg;
-
-public class PackAllDataTypesAction extends DockingAction {
-
- private DataTypeManagerPlugin plugin;
-
- public PackAllDataTypesAction(DataTypeManagerPlugin plugin) {
- super("Pack All Composites", plugin.getName());
- this.plugin = plugin;
-
- setPopupMenuData(new MenuData(new String[] { "Pack All..." }, "Edit"));
-// setHelpLocation(new HelpLocation(plugin.getName(), getName()));
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- Object contextObject = context.getContextObject();
- if (!(contextObject instanceof GTree)) {
- return false;
- }
-
- GTree gTree = (GTree) contextObject;
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- if (selectionPaths == null || selectionPaths.length != 1) {
- return false;
- }
-
- GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
- if ((node instanceof ProgramArchiveNode) || (node instanceof ProjectArchiveNode) ||
- (node instanceof FileArchiveNode)) {
- ArchiveNode archiveNode = (ArchiveNode) node;
- if (!archiveNode.isEnabled()) {
- return false;
- }
- return true;
- }
- return false;
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- GTree gTree = (GTree) context.getContextObject();
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
- if ((node instanceof ProgramArchiveNode) || (node instanceof ProjectArchiveNode) ||
- (node instanceof FileArchiveNode)) {
- ArchiveNode archiveNode = (ArchiveNode) node;
- Archive archive = archiveNode.getArchive();
- if (archive.isModifiable()) {
- DataTypeManager dataTypeManager = archive.getDataTypeManager();
- DataOrganization dataOrganization = dataTypeManager.getDataOrganization();
-
- int result =
- OptionDialog.showOptionDialog(
- plugin.getTool().getToolFrame(),
- "Pack All Composites",
- "Are you sure you want to enable packing of all non-packed composites in " +
- dataTypeManager.getName() +
- "?\nAll structures and unions that are not currently packed will default packing enabled.\n" +
- "This could cause component offsets to change as well as size and alignment of these data types to change.\n" +
- "Do you want to continue?", "Continue", OptionDialog.WARNING_MESSAGE);
- if (result == OptionDialog.CANCEL_OPTION) {
- return;
- }
- packDataTypes(dataTypeManager, dataOrganization);
- }
- else {
- Msg.showWarn(this, gTree, "Modification Not Allowed",
- "The archive must be modifiable to pack data types.");
- }
- }
- }
-
- private void packDataTypes(DataTypeManager dataTypeManager, DataOrganization dataOrganization) {
- if (dataTypeManager == null) {
- Msg.error(this, "Can't pack data types without a data type manager.");
- return;
- }
- int transactionID = -1;
- boolean commit = false;
- try {
- // start a transaction
- transactionID =
- dataTypeManager.startTransaction("Pack Composite Types");
- packEachStructure(dataTypeManager, dataOrganization);
- commit = true;
- }
- finally {
- // commit the changes
- dataTypeManager.endTransaction(transactionID, commit);
- }
- }
-
- private void packEachStructure(DataTypeManager dataTypeManager,
- DataOrganization dataOrganization) {
- Iterator extends Composite> allComposites = dataTypeManager.getAllComposites();
- while (allComposites.hasNext()) {
- Composite composite = allComposites.next();
- if (!composite.isPackingEnabled()) {
- composite.setPackingEnabled(true);
- }
- }
- }
-
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackDataTypeAction.java
deleted file mode 100644
index 54ce96245c..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackDataTypeAction.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.plugin.core.datamgr.actions;
-
-import javax.swing.tree.TreePath;
-
-import docking.ActionContext;
-import docking.action.DockingAction;
-import docking.action.MenuData;
-import docking.widgets.tree.GTree;
-import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
-import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
-import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode;
-import ghidra.program.model.data.*;
-import ghidra.util.Msg;
-
-public class PackDataTypeAction extends DockingAction {
-
- public PackDataTypeAction(DataTypeManagerPlugin plugin) {
- super("Pack Data Type", plugin.getName());
- setPopupMenuData(new MenuData(new String[] { "Pack (default)" }, "Edit"));
-// setHelpLocation(new HelpLocation(plugin.getName(), getName()));
- }
-
- @Override
- public boolean isAddToPopup(ActionContext context) {
- DataTypeNode node = getSelectedDataTypeNode(context);
- if (node == null) {
- return false;
- }
- DataType dataType = node.getDataType();
- if (dataType instanceof BuiltInDataType || dataType instanceof Pointer ||
- dataType instanceof MissingBuiltInDataType) {
- return false;
- }
- if (!node.isModifiable()) {
- return false;
- }
- return true;
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- DataTypeNode node = getSelectedDataTypeNode(context);
- if (node == null) {
- return false;
- }
- DataType dataType = node.getDataType();
- if (dataType instanceof Composite) {
- return !((Composite) dataType).isPackingEnabled();
- }
- return false;
- }
-
- private DataTypeNode getSelectedDataTypeNode(ActionContext context) {
- Object contextObject = context.getContextObject();
- if (!(contextObject instanceof GTree)) {
- return null;
- }
- GTree gTree = (GTree) contextObject;
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- if (selectionPaths.length != 1) {
- return null;
- }
- DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent();
-
- if (!(node instanceof DataTypeNode)) {
- return null;
- }
- return (DataTypeNode) node;
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- GTree gTree = (GTree) context.getContextObject();
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- for (TreePath treePath : selectionPaths) {
- final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent();
- DataType dataType = dataTypeNode.getDataType();
- DataTypeManager dataTypeManager = dataType.getDataTypeManager();
- DataOrganization dataOrganization = dataTypeManager.getDataOrganization();
- alignDataType(dataType, dataOrganization);
- }
- }
-
- private void alignDataType(DataType dataType, DataOrganization dataOrganization) {
- DataTypeManager dataTypeManager = dataType.getDataTypeManager();
- if (dataTypeManager == null) {
- Msg.error(this,
- "Can't align data type " + dataType.getName() + " without a data type manager.");
- return;
- }
- if (!(dataType instanceof Structure)) {
- Msg.error(this,
- "Can't align data type " + dataType.getName() + ". It's not a structure.");
- return;
- }
- int transactionID = -1;
- boolean commit = false;
- try {
- // start a transaction
- transactionID = dataTypeManager.startTransaction("Pack " + dataType.getName());
- ((Structure) dataType).setPackingEnabled(true);
- commit = true;
- }
- finally {
- // commit the changes
- dataTypeManager.endTransaction(transactionID, commit);
- }
- }
-
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackSizeDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackSizeDataTypeAction.java
deleted file mode 100644
index e6a77569f6..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackSizeDataTypeAction.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.plugin.core.datamgr.actions;
-
-import javax.swing.tree.TreePath;
-
-import docking.ActionContext;
-import docking.action.DockingAction;
-import docking.action.MenuData;
-import docking.widgets.dialogs.NumberInputDialog;
-import docking.widgets.tree.GTree;
-import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
-import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
-import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode;
-import ghidra.program.model.data.*;
-import ghidra.util.Msg;
-
-public class PackSizeDataTypeAction extends DockingAction {
-
- private DataTypeManagerPlugin plugin;
-
- public PackSizeDataTypeAction(DataTypeManagerPlugin plugin) {
- super("Pack Size Data Type", plugin.getName());
- this.plugin = plugin;
- setPopupMenuData(new MenuData(new String[] { "Pack for Size..." }, "Edit"));
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- Object contextObject = context.getContextObject();
- if (!(contextObject instanceof GTree)) {
- return false;
- }
- GTree gTree = (GTree) contextObject;
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- if (selectionPaths.length != 1) {
- return false;
- }
- DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent();
-
- if (!(node instanceof DataTypeNode)) {
- return false;
- }
- setEnabled(node.isModifiable());
- return true;
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- GTree gTree = (GTree) context.getContextObject();
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- TreePath treePath = selectionPaths[0];
- final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent();
- DataType dataType = dataTypeNode.getDataType();
- DataTypeManager dataTypeManager = dataType.getDataTypeManager();
- if (dataTypeManager == null) {
- Msg.error(this,
- "Can't pack data type " + dataType.getName() + " without a data type manager.");
- return;
- }
-
- NumberInputDialog numberInputDialog =
- new NumberInputDialog("explicit pack value", 0, 0, 16);
- if (!numberInputDialog.show()) {
- return;
- }
- int packSize = numberInputDialog.getValue();
-
- int transactionID = -1;
- boolean commit = false;
- try {
- // start a transaction
- transactionID =
- dataTypeManager.startTransaction("Pack(" + packSize + ") " + dataType.getName());
- packDataType(dataType, packSize);
- commit = true;
- }
- catch (IllegalArgumentException iie) {
- Msg.showError(this, null, "Invalid Pack Value", iie.getMessage());
- }
- finally {
- // commit the changes
- dataTypeManager.endTransaction(transactionID, commit);
- }
- }
-
- private void packDataType(DataType dataType, int packSize) throws IllegalArgumentException {
- if (!(dataType instanceof Composite)) {
- Msg.error(this,
- "Can't pack data type " + dataType.getName() + ". It's not a composite.");
- return;
- }
- ((Composite) dataType).pack(packSize);
- }
-
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java
index ca9494510d..183d162308 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -32,10 +32,11 @@ import docking.widgets.fieldpanel.support.FieldSelection;
* When edit actions occur and there is a selection, the listener's are notified
* of the new selection via the listener's overrideSelection method.
*/
-import ghidra.app.plugin.core.compositeeditor.CompositeEditorModel;
-import ghidra.app.plugin.core.compositeeditor.DataTypeHelper;
+import ghidra.app.plugin.core.compositeeditor.*;
import ghidra.app.util.datatype.EmptyCompositeException;
import ghidra.framework.plugintool.Plugin;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.database.DatabaseObject;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
@@ -77,6 +78,11 @@ public class StackEditorModel extends CompositeEditorModel {
}
}
+ @Override
+ public String getTypeName() {
+ return "Stack";
+ }
+
@Override
protected boolean allowsZeroLengthComponents() {
return false;
@@ -105,8 +111,23 @@ public class StackEditorModel extends CompositeEditorModel {
}
@Override
- protected Composite createViewCompositeFromOriginalComposite(Composite original) {
- return (Composite) original.copy(original.getDataTypeManager());
+ protected void createViewCompositeFromOriginalComposite(Composite original) {
+
+ if (viewDTM != null) {
+ viewDTM.close();
+ viewDTM = null;
+ }
+
+ // Use temporary standalone view datatype manager which will not manage the viewComposite
+ viewDTM = new CompositeViewerDataTypeManager(originalDTM.getName(), originalDTM);
+
+ // NOTE: StackFrameDataType cannot be resolved
+ viewComposite = (Composite) original.copy(viewDTM);
+ }
+
+ @Override
+ protected void restoreEditor() {
+ throw new UnsupportedOperationException("undo/redo not supported");
}
StackFrameDataType getViewComposite() {
@@ -127,21 +148,10 @@ public class StackEditorModel extends CompositeEditorModel {
int stackLocalSize = originalStack.getLocalSize();
int stackParamOffset = originalStack.getParameterOffset();
int stackParamSize = originalStack.getParameterSize();
- hadChanges = (editReturnAddressOffset != stackReturnAddressOffset) ||
+ hasChanges = (editReturnAddressOffset != stackReturnAddressOffset) ||
(editLocalSize != stackLocalSize) || (editParamOffset != stackParamOffset) ||
(editParamSize != stackParamSize) || super.updateAndCheckChangeState();
- return hadChanges;
- }
-
- /**
- * Returns the current dataType name (Structure or Union) as a string.
- */
- @Override
- protected String getTypeName() {
- if (viewComposite instanceof StackFrameDataType) {
- return "Stack";
- }
- return super.getTypeName();
+ return hasChanges;
}
@Override
@@ -435,11 +445,6 @@ public class StackEditorModel extends CompositeEditorModel {
return (StackFrameDataType) viewComposite;
}
- @Override
- public void clearComponent(int ordinal) {
- ((StackFrameDataType) viewComposite).clearComponent(ordinal);
- }
-
@Override
public void clearSelectedComponents() throws UsrException {
OffsetPairs offsetSelection = getRelOffsetSelection();
@@ -683,11 +688,6 @@ public class StackEditorModel extends CompositeEditorModel {
return (getNumSelectedRows() > 0);
}
- @Override
- public boolean isCycleAllowed(CycleGroup cycleGroup) {
- return true;
- }
-
@Override
public boolean isDeleteAllowed() {
if (selection.getNumRanges() != 1) {
@@ -840,7 +840,7 @@ public class StackEditorModel extends CompositeEditorModel {
/** Gets the original field name within the parent data type for a given row in the editor */
private boolean isOriginalFieldName(String testName, int rowIndex) {
- StackFrameDataType dataType = (StackFrameDataType) getOriginalComposite();
+ StackFrameDataType dataType = getOriginalComposite();
String fieldName = getFieldNameAtRow(rowIndex, dataType);
return SystemUtilities.isEqual(fieldName, testName);
}
@@ -858,7 +858,11 @@ public class StackEditorModel extends CompositeEditorModel {
@Override
public DataTypeComponent add(DataType dataType) throws UsrException {
- return replace(dataType);
+ int rowIndex = getMinIndexSelected();
+ if (rowIndex < 0) {
+ throw new UsrException("A component must be selected.");
+ }
+ return replace(rowIndex, dataType);
}
/**
@@ -1019,7 +1023,7 @@ public class StackEditorModel extends CompositeEditorModel {
newSv.setComment(comment);
}
}
- load(new StackFrameDataType(original, dtm));
+ load(new StackFrameDataType(original, viewDTM));
clearStatus();
return true;
}
@@ -1106,10 +1110,7 @@ public class StackEditorModel extends CompositeEditorModel {
return replace(rowIndex, dataType);
}
- /*
- *
- */
- public DataTypeComponent replace(int index, DataType dataType) throws UsrException {
+ private DataTypeComponent replace(int index, DataType dataType) throws UsrException {
try {
DataTypeInstance dti = getDropDataType(index, dataType);
return replace(index, dti.getDataType(), dti.getLength());
@@ -1121,17 +1122,12 @@ public class StackEditorModel extends CompositeEditorModel {
@Override
public DataTypeComponent replace(int index, DataType dt, int dtLength) throws UsrException {
+
OffsetPairs offsetSelection = getRelOffsetSelection();
- int transID = startTransaction("Apply Data Type \"" + dt.getName() + "\"");
- try {
- fieldEdited(
- DataTypeInstance.getDataTypeInstance(dt, dtLength, usesAlignedLengthComponents()),
- index, getDataTypeColumn());
- setRelOffsetSelection(offsetSelection);
- }
- finally {
- endTransaction(transID);
- }
+ fieldEdited(
+ DataTypeInstance.getDataTypeInstance(dt, dtLength, usesAlignedLengthComponents()),
+ index, getDataTypeColumn());
+ setRelOffsetSelection(offsetSelection);
return getComponent(index);
}
@@ -1159,6 +1155,48 @@ public class StackEditorModel extends CompositeEditorModel {
return max / dtc.getDataType().getAlignedLength();
}
+ @Override
+ public void restored(DataTypeManager dataTypeManager) {
+
+ StackFrameDataType sfdt = getOriginalComposite();
+ Function function = sfdt.getFunction();
+ if (function.isDeleted()) {
+ // Cancel Editor.
+ provider.dispose();
+ PluginTool tool = ((StackEditorProvider) provider).getPlugin().getTool();
+ tool.setStatusInfo("Stack Editor was closed for " + provider.getName());
+ return;
+ }
+
+ updateAndCheckChangeState();
+
+ boolean reload = true;
+ if (hasChanges) {
+ // The user has modified the structure so prompt for whether or
+ // not to reload the structure.
+ String question = "The program \"dtm.getName()\" has been restored.\n" + "\"" +
+ currentName + "\" may have changed outside the editor.\n" +
+ "Discard edits and reload the Stack Editor?";
+ String title = "Reload Stack Editor?";
+ int response = OptionDialog
+ .showYesNoDialogWithNoAsDefaultButton(provider.getComponent(), title, question);
+ if (response != 1) {
+ reload = false;
+ }
+ }
+ if (reload) {
+
+ StackFrame stack = function.getStackFrame();
+ StackFrameDataType newSfdt =
+ new StackFrameDataType(stack, function.getProgram().getDataTypeManager());
+
+ load(newSfdt); // reload the stack model based on current stack frame
+ }
+ else {
+ refresh();
+ }
+ }
+
@Override
public void dataTypeChanged(DataTypeManager dataTypeManager, DataTypePath path) {
if (isLoaded()) {
@@ -1235,10 +1273,18 @@ public class StackEditorModel extends CompositeEditorModel {
}
}
+ /**
+ * Get the stack frame model datatype being edited
+ * @return stack frame model datatype
+ */
@Override
- protected Composite getOriginalComposite() {
- // This is to allow the stack editor panel to have access.
- return originalComposite; // not contained within datatype manager
+ protected StackFrameDataType getOriginalComposite() {
+ return (StackFrameDataType) originalComposite;
+ }
+
+ @Override
+ protected boolean originalCompositeExists() {
+ return false;
}
@Override
@@ -1247,12 +1293,6 @@ public class StackEditorModel extends CompositeEditorModel {
return super.getOriginalDataTypeManager();
}
- @Override
- protected void fixupOriginalPath(Composite composite) {
- // This is to allow the stack editor panel to have access.
- super.fixupOriginalPath(composite);
- }
-
@Override
protected long getCompositeID() {
// This is to allow the stack editor panel to have access.
@@ -1276,10 +1316,25 @@ public class StackEditorModel extends CompositeEditorModel {
for (int i = comps.length - 1; i >= 0; i--) {
DataTypeComponent component = comps[i];
DataType compDt = component.getDataType();
- if (compDt.isDeleted()) {
- clearComponent(component.getOrdinal());
+ if (compDt instanceof DatabaseObject) {
+ // NOTE: viewDTM only maps view-to-original IDs for DataTypeDB
+ long myId = viewDTM.getID(compDt);
+ if (viewDTM.findOriginalDataTypeFromMyID(myId) == null) {
+ // Datatype not found
+ clearComponent(component.getOrdinal());
+ }
}
}
+
+ viewDTM.refreshDBTypesFromOriginal();
+ }
+
+ @Override
+ protected void clearComponents(int[] rows) {
+ for (int i = rows.length - 1; i >= 0; i--) {
+ ((StackFrameDataType) viewComposite).clearComponent(rows[i]);
+ }
+ notifyCompositeChanged();
}
//**************************************************************************
@@ -1289,24 +1344,6 @@ public class StackEditorModel extends CompositeEditorModel {
//**************************************************************************
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- @Override
- public DataType resolve(DataType dt) {
- if (dt instanceof StackPieceDataType) {
- return dt;
- }
- return DataTypeHelper.resolveDataType(dt, viewDTM, null);
- }
-
- /**
- * This method overrides the CompositeEditorModel to wrap the resolve of the data type
- * in a transaction.
- */
- @Override
- public DataType resolveDataType(DataType dt, DataTypeManager resolveDtm,
- DataTypeConflictHandler conflictHandler) {
- return DataTypeHelper.resolveDataType(dt, resolveDtm, conflictHandler);
- }
-
@Override
public DataTypeInstance validateComponentDataType(int index, String dtString)
throws CancelledException, UsrException {
@@ -1323,7 +1360,6 @@ public class StackEditorModel extends CompositeEditorModel {
}
}
- DataTypeManager originalDTM = getOriginalDataTypeManager();
DataType newDt = DataTypeHelper.parseDataType(index, dtString, this, originalDTM,
provider.getDtmService());
@@ -1337,7 +1373,7 @@ public class StackEditorModel extends CompositeEditorModel {
int newLength = newDt.getLength();
checkIsAllowableDataType(newDt);
- newDt = DataTypeHelper.resolveDataType(newDt, viewDTM, null);
+ newDt = resolveDataType(newDt, viewDTM, null);
int maxLength = getMaxReplaceLength(index);
if (newLength <= 0) {
throw new UsrException("Can't currently add this data type--not enough space.");
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorPanel.java
index 7647bb72ba..06176c2a25 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorPanel.java
@@ -21,12 +21,8 @@ import java.util.List;
import javax.swing.*;
-import docking.widgets.OptionDialog;
import ghidra.app.plugin.core.compositeeditor.CompositeEditorPanel;
-import ghidra.framework.plugintool.PluginTool;
-import ghidra.program.model.data.Composite;
-import ghidra.program.model.data.DataTypeManager;
-import ghidra.program.model.listing.*;
+import ghidra.program.model.listing.Program;
import ghidra.util.exception.UsrException;
/**
@@ -45,6 +41,27 @@ public class StackEditorPanel extends CompositeEditorPanel {
super(model, provider);
}
+ private StackEditorModel getStackModel() {
+ return (StackEditorModel) model;
+ }
+
+ @Override
+ protected boolean hasUncomittedEntry() {
+ // Stack editor has not yet been modified to use GFormattedTextField
+ return false;
+ }
+
+ @Override
+ protected boolean hasInvalidEntry() {
+ // Stack editor has not yet been modified to use GFormattedTextField
+ return false;
+ }
+
+ @Override
+ protected void comitEntryChanges() {
+ // do nothing
+ }
+
int getFrameSize() {
return Integer.decode(frameSizeField.getText()).intValue();
}
@@ -149,7 +166,7 @@ public class StackEditorPanel extends CompositeEditorPanel {
}
else {
try {
- ((StackEditorModel) model).setLocalSize(localSize);
+ getStackModel().setLocalSize(localSize);
}
catch (UsrException ue) {
model.setStatus("Invalid local size \"" + valueStr + "\". " + ue.getMessage(),
@@ -193,7 +210,7 @@ public class StackEditorPanel extends CompositeEditorPanel {
value = Integer.decode(valueStr);
int paramSize = value.intValue();
try {
- ((StackEditorModel) model).setParameterSize(paramSize);
+ getStackModel().setParameterSize(paramSize);
}
catch (UsrException ue) {
model.setStatus("Invalid parameter size \"" + valueStr + "\". " + ue.getMessage(),
@@ -220,9 +237,6 @@ public class StackEditorPanel extends CompositeEditorPanel {
returnAddrOffsetField.setEnabled(false);
}
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeModelDataListener#compositeInfoChanged()
- */
@Override
public void compositeInfoChanged() {
adjustStackInfo();
@@ -233,11 +247,8 @@ public class StackEditorPanel extends CompositeEditorPanel {
: Integer.toString(value);
}
- /**
- *
- */
private void adjustStackInfo() {
- StackFrameDataType editorStack = ((StackEditorModel) model).getEditorStack();
+ StackFrameDataType editorStack = getStackModel().getEditorStack();
String frameSize = getNumberString(editorStack.getFrameSize());
if (!frameSizeField.getText().trim().equals(frameSize)) {
@@ -265,67 +276,11 @@ public class StackEditorPanel extends CompositeEditorPanel {
}
}
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeViewerModelListener#componentDataChanged()
- */
@Override
public void componentDataChanged() {
// Don't need to update other than table when component data changes.
}
- @Override
- protected void dataTypeManagerRestored() {
- boolean reload = true;
- String objectType = "program";
- DataTypeManager dtm = ((StackEditorModel) model).getOriginalDataTypeManager();
- Composite originalDt = ((StackEditorModel) model).getOriginalComposite();
- if (originalDt instanceof StackFrameDataType) {
- StackFrameDataType sfdt = (StackFrameDataType) originalDt;
- Function function = sfdt.getFunction();
- if (function.isDeleted()) {
- // Cancel Editor.
- provider.dispose();
- PluginTool tool = ((StackEditorProvider) provider).getPlugin().getTool();
- tool.setStatusInfo("Stack Editor was closed for " + provider.getName());
- return;
- }
- StackFrame stack = function.getStackFrame();
- StackFrameDataType newSfdt = new StackFrameDataType(stack, dtm);
- if (!newSfdt.equals(((StackEditorModel) model).getViewComposite())) {
- originalDt = newSfdt;
- }
- }
- ((StackEditorModel) model).updateAndCheckChangeState();
- if (model.hasChanges()) {
- String name = ((StackEditorModel) model).getTypeName();
- // The user has modified the structure so prompt for whether or
- // not to reload the structure.
- String question =
- "The " + objectType + " \"" + dtm.getName() + "\" has been restored.\n" + "\"" +
- model.getCompositeName() + "\" may have changed outside the editor.\n" +
- "Discard edits & reload the " + name + " Editor?";
- String title = "Reload " + name + " Editor?";
- int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
- if (response != 1) {
- reload = false;
- }
- }
- if (reload) {
- cancelCellEditing();
- // TODO
-// boolean lockState = model.isLocked(); // save the lock state
- model.load(originalDt); // reload the structure
-// model.setLocked(lockState); // restore the lock state
- model.updateAndCheckChangeState();
- }
- else {
- ((StackEditorModel) model).refresh();
- }
- }
-
- /* (non-Javadoc)
- * @see ghidra.app.plugin.compositeeditor.CompositeEditorPanel#dispose()
- */
@Override
public void dispose() {
removeFocusListeners(localSizeField);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider.java
index 0db383d55a..9ee2973bfd 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -146,7 +146,7 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
}
private void refreshName() {
- StackFrameDataType origDt = (StackFrameDataType) stackModel.getOriginalComposite();
+ StackFrameDataType origDt = stackModel.getOriginalComposite();
StackFrameDataType viewDt = stackModel.getViewComposite();
String oldName = origDt.getName();
String newName = function.getName();
@@ -219,12 +219,13 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
}
break;
case SYMBOL_PRIMARY_STATE_CHANGED:
- sym = (Symbol) ((ProgramChangeRecord) rec).getObject();
+ sym = (Symbol) ((ProgramChangeRecord) rec).getNewValue();
symType = sym.getSymbolType();
if (symType == SymbolType.LABEL &&
sym.getAddress().equals(function.getEntryPoint())) {
refreshName();
}
+ break;
default:
}
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorTest.java
index 6e42d84ee1..f3b9663f1b 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -29,6 +29,8 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest {
// Editor Actions
ApplyAction applyAction;
+ UndoChangeAction undoAction;
+ RedoChangeAction redoAction;
ArrayAction arrayAction;
ClearAction clearAction;
CreateInternalStructureAction createInternalStructureAction;
@@ -46,6 +48,7 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest {
InsertUndefinedAction insertUndefinedAction;
HexNumbersAction hexNumbersAction;
+ @Override
@After
public void tearDown() throws Exception {
clearActions();
@@ -100,6 +103,8 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest {
favorites.clear();
cycles.clear();
applyAction = null;
+ undoAction = null;
+ redoAction = null;
arrayAction = null;
clearAction = null;
createInternalStructureAction = null;
@@ -130,6 +135,12 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest {
else if (action instanceof ApplyAction) {
applyAction = (ApplyAction) action;
}
+ else if (action instanceof UndoChangeAction) {
+ undoAction = (UndoChangeAction) action;
+ }
+ else if (action instanceof RedoChangeAction) {
+ redoAction = (RedoChangeAction) action;
+ }
else if (action instanceof ArrayAction) {
arrayAction = (ArrayAction) action;
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorAlignmentTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorAlignmentTest.java
index 81e8c3d39d..89ec02305d 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorAlignmentTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorAlignmentTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -23,7 +23,6 @@ import javax.swing.*;
import org.junit.Test;
-import docking.widgets.OptionDialog;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Library;
@@ -126,6 +125,8 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest {
addDataType(new FloatDataType());
addDataType(arrayDt);
+ structureModel.viewDTM.clearUndo();
+
waitForSwing();
assertTrue(Arrays.equals(new int[] { 0 }, model.getSelectedRows()));
@@ -807,11 +808,7 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest {
if (packingButton.isSelected()) {
return;
}
-
- pressButton(packingButton, false);
- OptionDialog confirmDialog = waitForDialogComponent(OptionDialog.class);
- pressButtonByText(confirmDialog, "Yes");
- waitForSwing();
+ pressButton(packingButton, true);
}
private void checkRow(int rowIndex, int offset, int length, String mnemonic, DataType dataType,
@@ -829,6 +826,7 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest {
}
private DataTypeComponent addDataType(DataType dataType) {
- return structureModel.viewComposite.add(dataType);
+ return structureModel.viewDTM.withTransaction("Add Test Component",
+ () -> structureModel.viewComposite.add(dataType));
}
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java
index 10309c100b..3687e1d410 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,6 +22,8 @@ import javax.swing.*;
import org.junit.Test;
import docking.widgets.OptionDialog;
+import ghidra.program.database.DatabaseObject;
+import ghidra.program.database.data.StructureDBTest;
import ghidra.program.model.data.*;
public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTest {
@@ -305,12 +307,17 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes
}
private DataTypeComponent addDataType(DataType dataType) {
- return structureModel.viewComposite.add(dataType);
+ return structureModel.viewDTM.withTransaction("Add Test Component",
+ () -> structureModel.viewComposite.add(dataType));
}
private DataTypeComponent addFlexDataType(Structure struct, DataType dataType, String name,
String comment) {
ArrayDataType a = new ArrayDataType(dataType, 0, 1);
+ if (struct instanceof DatabaseObject) {
+ DataTypeManager dtm = struct.getDataTypeManager();
+ return dtm.withTransaction("Add Flex Array", () -> struct.add(a, name, comment));
+ }
return struct.add(a, name, comment);
}
@@ -319,11 +326,7 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes
if (packingButton.isSelected()) {
return;
}
-
- pressButton(packingButton, false);
- OptionDialog confirmDialog = waitForDialogComponent(OptionDialog.class);
- pressButtonByText(confirmDialog, "Yes");
- waitForSwing();
+ pressButton(packingButton, true);
}
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions1Test.java
index 8e137e0011..68d094214c 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions1Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions1Test.java
@@ -45,12 +45,7 @@ public class StructureEditorLockedActions1Test extends AbstractStructureEditorTe
public void testArrayOnSelectionExtraUndefineds() throws Exception {
init(simpleStructure, pgmBbCat);
runSwing(() -> {
- try {
- model.clearComponents(new int[] { 4, 5 });
- }
- catch (UsrException e) {
- failWithException("Unexpected error", e);
- }
+ model.clearComponents(new int[] { 4, 5 });
});
setSelection(new int[] { 3, 4, 5, 6, 7, 8, 9, 10 });// starts with DWord
DataType dt3 = getDataType(3);
@@ -103,12 +98,7 @@ public class StructureEditorLockedActions1Test extends AbstractStructureEditorTe
public void testCreateCycleOnPointer() throws Exception {
init(simpleStructure, pgmBbCat);
runSwing(() -> {
- try {
- model.clearComponents(new int[] { 2, 3 });
- }
- catch (UsrException e) {
- failWithException("Unexpected error", e);
- }
+ model.clearComponents(new int[] { 2, 3 });
});
setSelection(new int[] { 1 });
invoke(pointerAction);
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorNotifiedTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorNotifiedTest.java
index 024def9756..7484ad7d9d 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorNotifiedTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorNotifiedTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -596,7 +596,7 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
int numComps = model.getNumComponents();
int len = model.getLength();
-
+
assertEquals(87, complexStructure.getComponent(16).getDataType().getLength());
assertEquals(29, complexStructure.getComponent(19).getDataType().getLength());
assertEquals(24, complexStructure.getComponent(20).getDataType().getLength());
@@ -675,17 +675,14 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
programDTM.replaceDataType(complexStructure, newComplexStructure, true);
waitForSwing();
- DataType origCopy = newComplexStructure.clone(null);
// Verify the Reload Structure Editor? dialog is displayed.
- dialog = waitForWindow("Reload Structure Editor?");
+ dialog = waitForWindow("Close Structure Editor?");
assertNotNull(dialog);
pressButtonByText(dialog, "Yes");
- dialog.dispose();
- dialog = null;
+ waitForSwing();
- assertEquals(((Structure) origCopy).getNumComponents(), model.getNumComponents());
- assertTrue(origCopy.isEquivalent(model.viewComposite));
+ assertFalse(provider.isVisible());
}
@Test
@@ -708,10 +705,12 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
waitForSwing();
// Verify the Reload Structure Editor? dialog is displayed.
- Window dialog = waitForWindow("Reload Structure Editor?");
+ Window dialog = waitForWindow("Close Structure Editor?");
assertNotNull(dialog);
pressButtonByText(dialog, "No");
- dialog.dispose();
+ waitForSwing();
+
+ assertTrue(provider.isVisible());
assertEquals(((Structure) viewCopy).getNumComponents(), model.getNumComponents());
assertTrue(viewCopy.isEquivalent(model.viewComposite));
@@ -728,8 +727,14 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
assertTrue(complexStructure.isEquivalent(model.viewComposite));
programDTM.replaceDataType(complexStructure, newComplexStructure, true);
+
+ // Verify Structure Editor closes (we don't want two editors for the same type)
+ Window dialog = waitForWindow("Closing Structure Editor");
+ assertNotNull(dialog);
+ pressButtonByText(dialog, "OK");
waitForSwing();
- assertTrue(newComplexStructure.isEquivalent(model.viewComposite));
+
+ assertFalse(provider.isVisible());
}
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProviderTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProviderTest.java
index ffc556bc26..4e77bc56d2 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProviderTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProviderTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -36,7 +36,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
@Override
protected void init(Structure dt, final Category cat, final boolean showInHex) {
- boolean commit = true;
startTransaction("Structure Editor Test Initialization");
try {
DataTypeManager dataTypeManager = cat.getDataTypeManager();
@@ -49,13 +48,12 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
dt.setCategoryPath(categoryPath);
}
catch (DuplicateNameException e) {
- commit = false;
Assert.fail(e.getMessage());
}
}
}
finally {
- endTransaction(commit);
+ endTransaction(true);
}
final Structure structDt = dt;
runSwing(() -> {
@@ -67,364 +65,249 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
getActions();
}
- @Test
- public void testDataTypeChanged() throws Exception {
- try {
- txId = program.startTransaction("Grow DataType");
- complexStructure.insert(22, DataType.DEFAULT);
- complexStructure.insert(22, DataType.DEFAULT);
- complexStructure.insert(22, DataType.DEFAULT);
- complexStructure.insert(22, DataType.DEFAULT);
- complexStructure.insert(22, DataType.DEFAULT);
- complexStructure.insert(22, DataType.DEFAULT);
- assertEquals(87, complexStructure.getComponent(16).getDataType().getLength());
- assertEquals(29, complexStructure.getComponent(19).getDataType().getLength());
- assertEquals(29, complexStructure.getComponent(21).getDataType().getLength());
- assertEquals(87, complexStructure.getComponent(16).getLength());
- assertEquals(29, complexStructure.getComponent(19).getLength());
- assertEquals(29, complexStructure.getComponent(21).getLength());
- assertEquals(1, complexStructure.getComponent(22).getLength());
- assertEquals(4, complexStructure.getComponent(28).getLength());
- assertEquals(331, complexStructure.getLength());
- assertEquals(29, complexStructure.getNumComponents());
- // Change the struct. simpleStructure was 29 bytes.
- simpleStructure.add(new DWordDataType());
- assertEquals(331, complexStructure.getLength());
- assertEquals(25, complexStructure.getNumComponents());
- assertEquals(99, complexStructure.getComponent(16).getDataType().getLength());
- assertEquals(33, complexStructure.getComponent(19).getDataType().getLength());
- assertEquals(33, complexStructure.getComponent(21).getDataType().getLength());
- assertEquals(87, complexStructure.getComponent(16).getLength());
- assertEquals(29, complexStructure.getComponent(19).getLength());
- assertEquals(33, complexStructure.getComponent(21).getLength());
- assertEquals(1, complexStructure.getComponent(22).getLength());
- assertEquals(4, complexStructure.getComponent(24).getLength());
- }
- finally {
- program.endTransaction(txId, true);
- }
- }
-
// Test Undo / Redo of program.
@Test
public void testModifiedDtAndProgramRestored() throws Exception {
- Window dialog;
- try {
- init(complexStructure, pgmTestCat, false);
+ init(complexStructure, pgmTestCat, false);
- // Change the structure
- runSwingLater(() -> {
- getTable().requestFocus();
- setSelection(new int[] { 4, 5 });
- deleteAction.actionPerformed(new DefaultActionContext());
- try {
- model.add(new WordDataType());
- }
- catch (UsrException e) {
- Assert.fail(e.getMessage());
- }
- });
- waitForSwing();
- assertFalse(complexStructure.isEquivalent(model.viewComposite));
+ // Change the structure
+ runSwingLater(() -> {
+ getTable().requestFocus();
+ setSelection(new int[] { 4, 5 });
+ deleteAction.actionPerformed(new DefaultActionContext());
+ try {
+ model.add(new WordDataType());
+ }
+ catch (UsrException e) {
+ Assert.fail(e.getMessage());
+ }
+ });
+ waitForSwing();
+ assertFalse(complexStructure.isEquivalent(model.viewComposite));
- // Apply the changes
- invoke(applyAction);
- assertTrue(complexStructure.isEquivalent(model.viewComposite));
+ // Apply the changes
+ invoke(applyAction);
+ assertTrue(complexStructure.isEquivalent(model.viewComposite));
- // Change the structure again.
- runSwingLater(() -> {
- getTable().requestFocus();
- setSelection(new int[] { 1 });
- clearAction.actionPerformed(new DefaultActionContext());
- deleteAction.actionPerformed(new DefaultActionContext());// Must be undefined before it can delete.
- });
- waitForSwing();
- assertFalse(complexStructure.isEquivalent(model.viewComposite));
+ // Change the structure again.
+ runSwingLater(() -> {
+ getTable().requestFocus();
+ setSelection(new int[] { 1 });
+ clearAction.actionPerformed(new DefaultActionContext());
+ deleteAction.actionPerformed(new DefaultActionContext());// Must be undefined before it can delete.
+ });
+ waitForSwing();
+ assertFalse(complexStructure.isEquivalent(model.viewComposite));
- // Undo the apply
- undo(program, false);
- // Verify the Reload Structure Editor? dialog is displayed.
- dialog = waitForWindow("Reload Structure Editor?");
- assertNotNull(dialog);
- pressButton(dialog, "No");
- dialog.dispose();
- dialog = null;
- assertFalse(complexStructure.isEquivalent(model.viewComposite));
+ // Undo the apply
+ undo(program, false);
+ // Verify the Reload Structure Editor? dialog is displayed.
+ Window dialog = waitForWindow("Reload Structure Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "No");
+ waitForSwing();
- // Redo the apply
- redo(program, false);
- // Verify the Reload Structure Editor? dialog is displayed.
- dialog = waitForWindow("Reload Structure Editor?");
- assertNotNull(dialog);
- pressButton(dialog, "No");
- dialog.dispose();
- dialog = null;
- assertFalse(complexStructure.isEquivalent(model.viewComposite));
- }
- finally {
- dialog = null;
- }
+ assertFalse(complexStructure.isEquivalent(model.viewComposite));
+
+ // Redo the apply
+ redo(program, false);
+
+ // Verify the Reload Structure Editor? dialog is displayed.
+ dialog = waitForWindow("Reload Structure Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "No");
+ waitForSwing();
+
+ assertFalse(complexStructure.isEquivalent(model.viewComposite));
}
// Test add a structure, start to edit it, and then undo the program so it goes away.
- // This should close the edit session.
@Test
public void testProgramRestoreRemovesEditedDt() throws Exception {
- Window dialog;
+
+ Structure s1 = new StructureDataType("s1", 0);
+ s1.add(new ByteDataType());
+ Structure s2 = new StructureDataType("s2", 0);
+ s2.add(new WordDataType());
+ s1.add(s2);
+
+ // Add the s1 data type so that we can undo its add.
+ Structure s1Struct = null;
+ startTransaction("Structure Editor Test Initialization");
try {
- Structure s1 = new StructureDataType("s1", 0);
- s1.add(new ByteDataType());
- Structure s2 = new StructureDataType("s2", 0);
- s2.add(new WordDataType());
- s1.add(s2);
-
- // Add the s1 data type so that we can undo its add.
- boolean commit = true;
- Structure s1Struct = null;
- startTransaction("Structure Editor Test Initialization");
- try {
- s1Struct =
- (Structure) pgmTestCat.addDataType(s1, DataTypeConflictHandler.DEFAULT_HANDLER);
- }
- finally {
- endTransaction(commit);
- }
- assertNotNull(s1Struct);
- final Structure myS1Structure = s1Struct;
-
- init(myS1Structure, pgmTestCat, false);
-
- // Change the structure.
- runSwingLater(() -> {
- getTable().requestFocus();
- // Select blank line after components.
- setSelection(new int[] { myS1Structure.getNumComponents() });
- try {
- model.add(new WordDataType());
- }
- catch (UsrException e) {
- Assert.fail(e.getMessage());
- }
- });
- waitForSwing();
- assertFalse(complexStructure.isEquivalent(model.viewComposite));
-
- // Verify the editor provider is displayed.
- String mySubTitle = getProviderSubTitle(myS1Structure);
- assertTrue("Couldn't find editor = " + mySubTitle,
- isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
-
- // Undo the apply
- undo(program, false);
- waitForSwing();
-
- // Verify the "Reload Structure Editor?" dialog is NOT displayed.
- dialog = getWindow("Reload Structure Editor?");
- assertNull(dialog);
-
- // Verify the editor provider is gone.
- assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
+ s1Struct =
+ (Structure) pgmTestCat.addDataType(s1, DataTypeConflictHandler.DEFAULT_HANDLER);
}
finally {
- dialog = null;
+ endTransaction(true);
}
+ assertNotNull(s1Struct);
+ final Structure myS1Structure = s1Struct;
+
+ init(myS1Structure, pgmTestCat, false);
+
+ // Change the structure.
+ runSwingLater(() -> {
+ getTable().requestFocus();
+ // Select blank line after components.
+ setSelection(new int[] { myS1Structure.getNumComponents() });
+ try {
+ model.add(new WordDataType());
+ }
+ catch (UsrException e) {
+ Assert.fail(e.getMessage());
+ }
+ });
+ waitForSwing();
+ assertFalse(complexStructure.isEquivalent(model.viewComposite));
+
+ // Verify the editor provider is displayed.
+ String mySubTitle = getProviderSubTitle(myS1Structure);
+ assertTrue("Couldn't find editor = " + mySubTitle,
+ isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
+
+ // Undo the apply
+ undo(program, false);
+
+ Window dialog = waitForWindow("Close Structure Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "Yes");
+ waitForSwing();
+
+ // Verify the editor provider is gone.
+ assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
}
// Test add a structure containing inner struct, start to edit inner struct, and then undo the
- // program so it goes away. This should close the edit session.
+ // program so it goes away.
@Test
public void testProgramRestoreRemovesEditedDtComp() throws Exception {
- Window dialog;
+ Structure s1 = new StructureDataType("s1", 0);
+ s1.setCategoryPath(pgmTestCat.getCategoryPath());
+ s1.add(new ByteDataType());
+ Structure s2 = new StructureDataType("s2", 0);
+ s2.setCategoryPath(pgmTestCat.getCategoryPath());
+ s2.add(new WordDataType());
+ s1.add(s2);
+
+ // Add the s1 data type so that we can undo its add.
+ Structure s1Struct = null;
+ startTransaction("Resolve s1");
try {
- Structure s1 = new StructureDataType("s1", 0);
- s1.setCategoryPath(pgmTestCat.getCategoryPath());
- s1.add(new ByteDataType());
- Structure s2 = new StructureDataType("s2", 0);
- s2.setCategoryPath(pgmTestCat.getCategoryPath());
- s2.add(new WordDataType());
- s1.add(s2);
-
- // Add the s1 data type so that we can undo its add.
- boolean commit = true;
- Structure s1Struct = null;
- startTransaction("Structure Editor Test Initialization");
- try {
- s1Struct =
- (Structure) pgmTestCat.addDataType(s1, DataTypeConflictHandler.DEFAULT_HANDLER);
- }
- finally {
- endTransaction(commit);
- }
- assertNotNull(s1Struct);
- final Structure myS2Structure = (Structure) s1Struct.getComponent(1).getDataType();
- assertNotNull(myS2Structure);
- assertTrue(s2.isEquivalent(myS2Structure));
-
- init(myS2Structure, pgmTestCat, false);
-
- // Change the structure.
- runSwing(() -> {
- getTable().requestFocus();
- // Select blank line after components.
- setSelection(new int[] { myS2Structure.getNumComponents() });
- try {
- model.add(new WordDataType());
- }
- catch (UsrException e) {
- Assert.fail(e.getMessage());
- }
- }, false);
- waitForSwing();
- assertFalse(complexStructure.isEquivalent(model.viewComposite));
-
- // Verify the editor provider is displayed.
- String mySubTitle = getProviderSubTitle(myS2Structure);
- assertTrue("Couldn't find editor = " + mySubTitle,
- isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
-
- // Undo the apply
- undo(program, false);
- waitForSwing();
-
- // Verify the "Reload Structure Editor?" dialog is NOT displayed.
- dialog = getWindow("Reload Structure Editor?");
- assertNull(dialog);
-
- // Verify the editor provider is gone.
- assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
+ s1Struct =
+ (Structure) pgmTestCat.addDataType(s1, DataTypeConflictHandler.DEFAULT_HANDLER);
}
finally {
- dialog = null;
+ endTransaction(true);
}
+ assertNotNull(s1Struct);
+ final Structure myS2Structure = (Structure) s1Struct.getComponent(1).getDataType();
+ assertNotNull(myS2Structure);
+ assertTrue(s2.isEquivalent(myS2Structure));
+
+ Structure editStruct = s1Struct;
+ init(editStruct, pgmTestCat, false);
+
+ // Change the structure.
+
+ runSwing(() -> {
+ getTable().requestFocus();
+ // Select blank line after components.
+ setSelection(new int[] { editStruct.getNumComponents() });
+ try {
+ model.add(new WordDataType());
+ }
+ catch (UsrException e) {
+ Assert.fail(e.getMessage());
+ }
+ }, false);
+ waitForSwing();
+ assertFalse(s1.isEquivalent(model.viewComposite));
+
+ // Verify the editor provider is displayed.
+ String mySubTitle = getProviderSubTitle(editStruct);
+ assertTrue("Couldn't find editor = " + mySubTitle,
+ isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
+
+ // Undo the apply
+ undo(program, false);
+ waitForSwing();
+
+ Window dialog = waitForWindow("Close Structure Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "Yes");
+ waitForSwing();
+
+ // Verify the editor provider is gone.
+ assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
}
// Test add a structure, start to edit a structure that contains it, and then undo the program
- // so it goes away. The editor stays since the structure existed previously, but editor reloads.
+ // so it goes away. The editor stays since the structure existed previously and has been modified.
@Test
- public void testProgramRestoreRemovesEditedComponentDtYes() throws Exception {
- Window dialog;
+ public void testProgramRestoreRemovesEditedComponentDt() throws Exception {
+
+ Structure myStruct = new StructureDataType("myStruct", 0);
+ myStruct.add(new WordDataType());
+
+ init(emptyStructure, pgmTestCat, false);
+
+ // Add the data type so that we can undo its add.
+ Structure pgmMyStruct = null;
+ startTransaction("Structure Editor Test Initialization");
try {
- Structure myStruct = new StructureDataType("myStruct", 0);
- myStruct.add(new WordDataType());
-
- init(emptyStructure, pgmTestCat, false);
-
- // Add the data type so that we can undo its add.
- boolean commit = true;
- Structure pgmMyStruct = null;
- startTransaction("Structure Editor Test Initialization");
- try {
- pgmMyStruct = (Structure) pgmTestCat.addDataType(myStruct,
- DataTypeConflictHandler.DEFAULT_HANDLER);
- }
- finally {
- endTransaction(commit);
- }
- assertNotNull(pgmMyStruct);
- final Structure myStructure = pgmMyStruct;
-
- // Change the structure.
- runSwingLater(() -> {
- getTable().requestFocus();
- // Select blank line after components.
- setSelection(new int[] { emptyStructure.getNumComponents() });
- try {
- model.add(myStructure);
- }
- catch (UsrException e) {
- Assert.fail(e.getMessage());
- }
- });
- waitForSwing();
- assertFalse(emptyStructure.isEquivalent(model.viewComposite));
-
- // Verify the editor provider is displayed.
- String mySubTitle = getProviderSubTitle(emptyStructure);
- assertTrue("Couldn't find editor = " + mySubTitle,
- isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
-
- // Undo the apply
- undo(program, false);
-
- // Verify the Reload Structure Editor? dialog is displayed.
- dialog = waitForWindow("Reload Structure Editor?");
- assertNotNull(dialog);
- pressButton(dialog, "Yes");
- dialog.dispose();
- dialog = null;
- assertTrue(emptyStructure.isEquivalent(model.viewComposite));
-
- // Verify the editor provider is reloaded.
- assertTrue(
- isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
+ pgmMyStruct = (Structure) pgmTestCat.addDataType(myStruct,
+ DataTypeConflictHandler.DEFAULT_HANDLER);
}
finally {
- dialog = null;
+ endTransaction(true);
}
- }
+ assertNotNull(pgmMyStruct);
+ final Structure myStructure = pgmMyStruct;
- // Test add a structure, start to edit a structure that contains it, and then undo the program
- // so it goes away. The editor stays since the structure existed previously, but doesn't reload.
- @Test
- public void testProgramRestoreRemovesEditedComponentDtNo() throws Exception {
- Window dialog;
- try {
- Structure myStruct = new StructureDataType("myStruct", 0);
- myStruct.add(new WordDataType());
-
- init(emptyStructure, pgmTestCat, false);
-
- // Add the data type so that we can undo its add.
- boolean commit = true;
- Structure pgmMyStruct = null;
- startTransaction("Structure Editor Test Initialization");
+ // Change the structure.
+ runSwingLater(() -> {
+ getTable().requestFocus();
+ // Select blank line after components.
+ setSelection(new int[] { emptyStructure.getNumComponents() });
try {
- pgmMyStruct = (Structure) pgmTestCat.addDataType(myStruct,
- DataTypeConflictHandler.DEFAULT_HANDLER);
+ model.add(myStructure);
}
- finally {
- endTransaction(commit);
+ catch (UsrException e) {
+ Assert.fail(e.getMessage());
}
- assertNotNull(pgmMyStruct);
- final Structure myStructure = pgmMyStruct;
+ });
+ waitForSwing();
+ assertFalse(emptyStructure.isEquivalent(model.viewComposite));
- // Change the structure.
- runSwingLater(() -> {
- getTable().requestFocus();
- // Select blank line after components.
- setSelection(new int[] { emptyStructure.getNumComponents() });
- try {
- model.add(myStructure);
- }
- catch (UsrException e) {
- Assert.fail(e.getMessage());
- }
- });
- waitForSwing();
- assertFalse(emptyStructure.isEquivalent(model.viewComposite));
+ // Verify the editor provider is displayed.
+ String mySubTitle = getProviderSubTitle(emptyStructure);
+ assertTrue("Couldn't find editor = " + mySubTitle,
+ isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
- // Verify the editor provider is displayed.
- String mySubTitle = getProviderSubTitle(emptyStructure);
- assertTrue("Couldn't find editor = " + mySubTitle,
- isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
+ DataType dtCopy = model.viewComposite.copy(model.viewDTM);
- // Undo the apply
- undo(program, false);
- // Verify the Reload Structure Editor? dialog is displayed.
- dialog = waitForWindow("Reload Structure Editor?");
- assertNotNull(dialog);
- pressButton(dialog, "No");
- dialog.dispose();
- dialog = null;
- assertFalse(emptyStructure.isEquivalent(model.viewComposite));
+ // Undo the apply
+ undo(program, false);
+ waitForSwing();
- // Verify the editor provider is still on screen.
- assertTrue(
- isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
- }
- finally {
- dialog = null;
- }
+ assertTrue(pgmMyStruct.isDeleted());
+
+ // Verify the a reload/close dialog is not displayed.
+ Window dialog = getWindow("Reload Structure Editor?");
+ assertNull(dialog);
+ dialog = getWindow("Close Structure Editor?");
+ assertNull(dialog);
+
+ assertTrue(
+ isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
+
+ // Verify the editor provider remains visible with myStructure use cleared.
+ assertFalse(emptyStructure.isEquivalent(model.viewComposite));
+ assertFalse(dtCopy.isEquivalent(model.viewComposite));
+ assertEquals(dtCopy.getLength(), model.viewComposite.getLength());
+ assertEquals(2, model.viewComposite.getNumComponents());
+ assertEquals(0, model.viewComposite.getNumDefinedComponents());
}
// Test Undo / Redo of program.
@@ -446,14 +329,29 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
});
waitForSwing();
assertFalse(complexStructure.isEquivalent(model.viewComposite));
+
// Apply the changes
invoke(applyAction);
assertTrue(complexStructure.isEquivalent(model.viewComposite));
+
// Undo the apply
undo(program);
+
+ Window dialog = waitForWindow("Reload Structure Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "Yes");
+ waitForSwing();
+
assertTrue(complexStructure.isEquivalent(model.viewComposite));
+
// Redo the apply
redo(program);
+
+ dialog = waitForWindow("Reload Structure Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "Yes");
+ waitForSwing();
+
assertTrue(complexStructure.isEquivalent(model.viewComposite));
}
@@ -569,8 +467,7 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
dialog = waitForWindow("Save Structure Editor Changes?");
assertNotNull(dialog);
pressButton(dialog, "Cancel");
- dialog.dispose();
- dialog = null;
+
assertTrue(tool.isVisible(provider));
assertFalse(complexStructure.isEquivalent(model.viewComposite));
assertTrue(newDt.isEquivalent(model.viewComposite));
@@ -706,8 +603,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
assertTrue(tool.isVisible(provider));
assertTrue(complexStructure.isEquivalent(model.viewComposite));
- Composite dt = model.viewComposite;
-
// set selected row
int row = 2;
setSelection(new int[] { row });
@@ -716,7 +611,7 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
int editRow = 4; // offset 8; 'simpleUnion'
String newFieldName = "newFieldName";
tx(program, () -> {
- DataTypeComponent dtc = dt.getComponent(editRow);
+ DataTypeComponent dtc = complexStructure.getComponent(editRow);
dtc.setFieldName(newFieldName);
});
@@ -728,7 +623,8 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
assertEquals(1, rows.length);
assertEquals(row, rows[0]);
- closeProviderIgnoringChanges();
+ // External change should not register as unsved change to model
+ assertFalse(model.hasChanges());
}
private void closeProviderIgnoringChanges() {
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java
index e8c50b06e2..037af8ee59 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -94,31 +94,84 @@ public class StructureEditorUnlockedActions5Test extends AbstractStructureEditor
// model.getStatus());
}
- // Ignoring test for now. Don't know how to make the name invalid
+ @Test
public void testApplyWithInvalidName() throws Exception {
init(complexStructure, pgmTestCat);
- CompEditorPanel panel = (CompEditorPanel) getPanel();
- JTextField nameField = panel.nameTextField;
assertTrue(model.isValidName());
- triggerActionKey(nameField, 0, KeyEvent.VK_END);
- triggerText(nameField, "#$/");
- DataType viewCopy = model.viewComposite.clone(null);
- assertTrue(!model.isValidName());
- assertEquals("complexStructure#$/", nameField.getText());
- assertEquals("complexStructure#$/", model.getCompositeName());
- assertEquals("complexStructure", complexStructure.getName());
- assertTrue(complexStructure.isEquivalent(model.viewComposite));
- assertTrue(viewCopy.isEquivalent(model.viewComposite));
- assertEquals(model.getStatus(), "complexStructure#$/ is not a valid name.");
- invoke(applyAction);
- assertEquals(model.getStatus(), "Name is not valid.");
- assertTrue(complexStructure.isEquivalent(model.viewComposite));
- assertTrue(viewCopy.isEquivalent(model.viewComposite));
- assertTrue(model.getStatus().length() > 0);
- assertEquals("complexStructure#$/", model.getCompositeName());
- assertEquals("complexStructure", complexStructure.getName());
+ CompEditorPanel panel = (CompEditorPanel) getPanel();
+ assertFalse(panel.hasInvalidEntry());
+ assertFalse(panel.hasUncomittedEntry());
+
+ JTextField nameField = panel.nameTextField;
+ nameField.setText(null);
+ triggerActionKey(nameField, 0, KeyEvent.VK_END);
+ triggerText(nameField, " ");
+
+ assertTrue(panel.hasInvalidEntry());
+ assertFalse(applyAction.isEnabled());
+ assertFalse(applyAction.isEnabled());
+
+ assertEquals("complexStructure", model.getCompositeName()); // no change yet
+
+ triggerActionKey(nameField, 0, KeyEvent.VK_DELETE);
+ triggerText(nameField, "xyz");
+
+ assertFalse(panel.hasInvalidEntry());
+ assertTrue(panel.hasUncomittedEntry());
+ assertFalse(applyAction.isEnabled());
+ assertFalse(applyAction.isEnabled());
+
+ assertEquals("complexStructure", model.getCompositeName()); // no change yet
+
+ triggerActionKey(nameField, 0, KeyEvent.VK_ENTER);
+
+ assertFalse(panel.hasInvalidEntry());
+ assertFalse(panel.hasUncomittedEntry());
+ assertTrue(applyAction.isEnabled());
+ assertTrue(applyAction.isEnabled());
+
+ assertTrue(model.isValidName());
+
+ assertEquals("xyz", model.getCompositeName()); // no change yet
+ }
+
+ @Test
+ public void testUncomittedNameRevert() throws Exception {
+ init(complexStructure, pgmTestCat);
+
+ assertTrue(model.isValidName());
+
+ CompEditorPanel panel = (CompEditorPanel) getPanel();
+ assertFalse(panel.hasInvalidEntry());
+ assertFalse(panel.hasUncomittedEntry());
+
+ JTextField nameField = panel.nameTextField;
+ nameField.setText(null);
+ triggerActionKey(nameField, 0, KeyEvent.VK_END);
+ triggerText(nameField, "xyz");
+ assertEquals("xyz", nameField.getText());
+
+ assertFalse(panel.hasInvalidEntry());
+ assertTrue(panel.hasUncomittedEntry());
+ assertFalse(applyAction.isEnabled());
+ assertFalse(applyAction.isEnabled());
+
+ assertEquals("complexStructure", model.getCompositeName()); // no change yet
+
+ triggerActionKey(nameField, 0, KeyEvent.VK_ESCAPE);
+
+ assertEquals("complexStructure", nameField.getText());
+
+ assertFalse(panel.hasInvalidEntry());
+ assertFalse(panel.hasUncomittedEntry());
+ assertFalse(applyAction.isEnabled());
+ assertFalse(applyAction.isEnabled());
+
+ assertTrue(model.isValidName());
+
+ assertEquals("complexStructure", model.getCompositeName()); // no change yet
}
@Test
@@ -606,24 +659,53 @@ public class StructureEditorUnlockedActions5Test extends AbstractStructureEditor
CompEditorPanel panel = (CompEditorPanel) getPanel();
JTextField nameField = panel.nameTextField;
- runSwing(() -> nameField.setText("myStruct"));
+
+ setText(nameField, "myStruct");
+ triggerEnter(nameField);
+
+ assertEquals("myStruct", nameField.getText());
+ assertEquals("myStruct", model.getCompositeName());
+
invoke(applyAction);
- runSwing(() -> nameField.setText("myStruct2"));
- invoke(applyAction);
- undo(program, false);
- program.flushEvents();
- waitForSwing();
- runSwing(() -> provider.dataTypeManagerRestored(), true);
+
waitForSwing();
+ assertEquals("myStruct", nameField.getText());
assertEquals("myStruct", model.getCompositeName());
- redo(program, false);
- program.flushEvents();
- waitForSwing();
- runSwing(() -> provider.dataTypeManagerRestored(), true);
- waitForSwing();
+
+ setText(nameField, "myStruct2");
+ triggerEnter(nameField);
+
+ assertEquals("myStruct2", nameField.getText());
assertEquals("myStruct2", model.getCompositeName());
+ invoke(applyAction);
+
+ waitForSwing();
+
+ assertEquals("myStruct2", nameField.getText());
+ assertEquals("myStruct2", model.getCompositeName());
+
+ undo(program, true);
+
+ Window dialog = waitForWindow("Reload Structure Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "Yes");
+ waitForSwing();
+
+ assertEquals("myStruct", nameField.getText());
+ assertEquals("myStruct", model.getCompositeName());
+
+ redo(program, true);
+
+ dialog = waitForWindow("Reload Structure Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "No");
+ waitForSwing();
+
+ assertEquals("myStruct", nameField.getText());
+ assertEquals("myStruct", model.getCompositeName());
+
}
@Test
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java
index 4364c8d974..6cdcadafa7 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -253,8 +253,9 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
throws ArrayIndexOutOfBoundsException, InvalidDataTypeException {
init(complexStructure, pgmBbCat);
- ((Structure) structureModel.viewComposite).insertBitField(2, 1, 4, CharDataType.dataType, 2,
- "bf1", null);
+ structureModel.viewDTM.withTransaction("Add Bitfield",
+ () -> ((Structure) structureModel.viewComposite).insertBitField(2, 1, 4,
+ CharDataType.dataType, 2, "bf1", null));
setSelection(new int[] { 2 });
assertEquals("char:2", getDataType(2).getDisplayName());
@@ -294,8 +295,9 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
throws ArrayIndexOutOfBoundsException, InvalidDataTypeException {
init(complexStructure, pgmBbCat);
- ((Structure) structureModel.viewComposite).insertBitField(2, 1, 4, CharDataType.dataType, 2,
- "bf1", null);
+ structureModel.viewDTM.withTransaction("Add Bitfield",
+ () -> ((Structure) structureModel.viewComposite).insertBitField(2, 1, 4,
+ CharDataType.dataType, 2, "bf1", null));
setSelection(new int[] { 2 });
assertEquals("char:2", getDataType(2).getDisplayName());
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorAlignmentTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorAlignmentTest.java
index dc730d5b72..f3d1280b9f 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorAlignmentTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorAlignmentTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -27,7 +27,7 @@ import ghidra.program.model.data.*;
public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
@Test
- public void testUnalignedUnion() {
+ public void testUnalignedUnion() {
init(emptyUnion, pgmRootCat, false);
assertTrue(unionModel.hasChanges());// empty union that hasn't been saved yet.
@@ -49,17 +49,14 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
// Check enablement for empty table with modified state.
CompositeEditorTableAction[] pActions = provider.getActions();
- for (int i = 0; i < pActions.length; i++) {
- if ((pActions[i] instanceof FavoritesAction) ||
- (pActions[i] instanceof CycleGroupAction) ||
- (pActions[i] instanceof EditFieldAction) ||
- (pActions[i] instanceof PointerAction) ||
- (pActions[i] instanceof HexNumbersAction) ||
- (pActions[i] instanceof ApplyAction)) {
- checkEnablement(pActions[i], true);
+ for (CompositeEditorTableAction pAction : pActions) {
+ if ((pAction instanceof FavoritesAction) || (pAction instanceof CycleGroupAction) ||
+ (pAction instanceof EditFieldAction) || (pAction instanceof PointerAction) ||
+ (pAction instanceof HexNumbersAction) || (pAction instanceof ApplyAction)) {
+ checkEnablement(pAction, true);
}
else {
- checkEnablement(pActions[i], false);
+ checkEnablement(pAction, false);
}
}
@@ -78,7 +75,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testDefaultAlignedUnion() throws Exception {
+ public void testDefaultAlignedUnion() throws Exception {
init(emptyUnion, pgmRootCat, false);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -101,34 +98,36 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testEnablementDefaultAlignedUnion() throws Exception {
+ public void testEnablementDefaultAlignedUnion() throws Exception {
emptyUnion.setPackingEnabled(true);
init(emptyUnion, pgmRootCat, false);
+ CompositeEditorTableAction undoAction =
+ provider.actionMgr.getNamedAction("Undo Editor Change");
+ assertNotNull(undoAction);
+ checkEnablement(undoAction, false);
+
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
addDataType(new ByteDataType());
addDataType(new FloatDataType());
addDataType(arrayDt);
+ // Undo should be enabled
+
// Check enablement.
CompositeEditorTableAction[] pActions = provider.getActions();
- for (int i = 0; i < pActions.length; i++) {
- if ((pActions[i] instanceof FavoritesAction) ||
- (pActions[i] instanceof CycleGroupAction) ||
- (pActions[i] instanceof EditFieldAction) ||
- (pActions[i] instanceof PointerAction) ||
- (pActions[i] instanceof HexNumbersAction) ||
- (pActions[i] instanceof MoveDownAction) ||
- (pActions[i] instanceof DuplicateAction) ||
- (pActions[i] instanceof DuplicateMultipleAction) ||
- (pActions[i] instanceof DeleteAction) ||
- (pActions[i] instanceof ArrayAction) ||
- (pActions[i] instanceof ShowComponentPathAction) ||
- (pActions[i] instanceof ApplyAction)) {
- checkEnablement(pActions[i], true);
+ for (CompositeEditorTableAction pAction : pActions) {
+ if ((pAction instanceof FavoritesAction) || (pAction instanceof CycleGroupAction) ||
+ (pAction instanceof EditFieldAction) || (pAction instanceof PointerAction) ||
+ (pAction instanceof HexNumbersAction) || (pAction instanceof MoveDownAction) ||
+ (pAction instanceof DuplicateAction) ||
+ (pAction instanceof DuplicateMultipleAction) || (pAction instanceof DeleteAction) ||
+ (pAction instanceof ArrayAction) || (pAction instanceof ShowComponentPathAction) ||
+ (pAction instanceof ApplyAction) || (pAction instanceof UndoChangeAction)) {
+ checkEnablement(pAction, true);
}
else {
- checkEnablement(pActions[i], false);
+ checkEnablement(pAction, false);
}
}
@@ -143,7 +142,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testMachineAlignedUnion() throws Exception {
+ public void testMachineAlignedUnion() throws Exception {
init(emptyUnion, pgmRootCat, false);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -168,7 +167,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testByValueAlignedUnion() throws Exception {
+ public void testByValueAlignedUnion() throws Exception {
init(emptyUnion, pgmRootCat, false);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -200,31 +199,32 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testByValue1AlignedUnion() throws Exception {
+ public void testByValue1AlignedUnion() throws Exception {
checkByValueAlignedUnion(1, 4, 8);
}
@Test
- public void testByValue2AlignedUnion() throws Exception {
+ public void testByValue2AlignedUnion() throws Exception {
checkByValueAlignedUnion(2, 4, 8);
}
@Test
- public void testByValue4AlignedUnion() throws Exception {
+ public void testByValue4AlignedUnion() throws Exception {
checkByValueAlignedUnion(4, 4, 8);
}
@Test
- public void testByValue8AlignedUnion() throws Exception {
+ public void testByValue8AlignedUnion() throws Exception {
checkByValueAlignedUnion(8, 8, 8);
}
@Test
- public void testByValue16AlignedUnion() throws Exception {
+ public void testByValue16AlignedUnion() throws Exception {
checkByValueAlignedUnion(16, 16, 16);
}
- public void checkByValueAlignedUnion(int minAlignment, int alignment, int length) throws Exception {
+ public void checkByValueAlignedUnion(int minAlignment, int alignment, int length)
+ throws Exception {
emptyUnion.setPackingEnabled(true);
emptyUnion.setExplicitMinimumAlignment(minAlignment);
@@ -256,7 +256,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testTurnOffAlignmentInUnion() throws Exception {
+ public void testTurnOffAlignmentInUnion() throws Exception {
emptyUnion.setPackingEnabled(true);
emptyUnion.setExplicitMinimumAlignment(8);
@@ -304,7 +304,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testInsertUnaligned1() throws Exception {
+ public void testInsertUnaligned1() throws Exception {
emptyUnion.setPackingEnabled(false);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -337,7 +337,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testInsertUnaligned2() throws Exception {
+ public void testInsertUnaligned2() throws Exception {
emptyUnion.setPackingEnabled(false);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -370,7 +370,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testInsertUnaligned3() throws Exception {
+ public void testInsertUnaligned3() throws Exception {
emptyUnion.setPackingEnabled(false);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -403,7 +403,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testReplaceUnaligned1() throws Exception {
+ public void testReplaceUnaligned1() throws Exception {
emptyUnion.setPackingEnabled(false);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -435,7 +435,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testReplaceUnaligned2() throws Exception {
+ public void testReplaceUnaligned2() throws Exception {
emptyUnion.setPackingEnabled(false);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -468,7 +468,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testInsertAligned1() throws Exception {
+ public void testInsertAligned1() throws Exception {
emptyUnion.setPackingEnabled(true);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -501,7 +501,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testInsertAligned2() throws Exception {
+ public void testInsertAligned2() throws Exception {
emptyUnion.setPackingEnabled(true);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -534,7 +534,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testInsertAligned3() throws Exception {
+ public void testInsertAligned3() throws Exception {
emptyUnion.setPackingEnabled(true);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -567,7 +567,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testReplaceAligned1() throws Exception {
+ public void testReplaceAligned1() throws Exception {
emptyUnion.setPackingEnabled(true);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -599,7 +599,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
@Test
- public void testReplaceAligned2() throws Exception {
+ public void testReplaceAligned2() throws Exception {
emptyUnion.setPackingEnabled(true);
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
@@ -643,7 +643,8 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
}
private DataTypeComponent addDataType(DataType dataType) {
- return unionModel.viewComposite.add(dataType);
+ return unionModel.viewDTM.withTransaction("Add Component",
+ () -> unionModel.viewComposite.add(dataType));
}
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java
index 141d902c16..6b6df1f6c2 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -79,10 +79,8 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
assertEquals(pgmBbCat.getCategoryPathName(), model.getOriginalCategoryPath().getPath());
pgmTestCat.moveCategory(pgmBbCat, TaskMonitor.DUMMY);
waitForSwing();
- assertTrue(model.getOriginalCategoryPath()
- .getPath()
- .startsWith(
- pgmTestCat.getCategoryPathName()));
+ assertTrue(
+ model.getOriginalCategoryPath().getPath().startsWith(pgmTestCat.getCategoryPathName()));
assertEquals(pgmBbCat.getCategoryPathName(), model.getOriginalCategoryPath().getPath());
}
@@ -117,8 +115,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
SwingUtilities.invokeLater(() -> {
programDTM.remove(complexUnion, TaskMonitor.DUMMY);
programDTM.getCategory(pgmRootCat.getCategoryPath())
- .removeCategory("Temp",
- TaskMonitor.DUMMY);
+ .removeCategory("Temp", TaskMonitor.DUMMY);
});
waitForSwing();
@@ -280,8 +277,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
public void testEditedDataTypeMoved() {
init(complexUnion, pgmTestCat, false);
- assertEquals(pgmTestCat.getCategoryPathName(),
- model.getOriginalCategoryPath().getPath());
+ assertEquals(pgmTestCat.getCategoryPathName(), model.getOriginalCategoryPath().getPath());
SwingUtilities.invokeLater(() -> {
try {
pgmAaCat.moveDataType(complexUnion, DataTypeConflictHandler.DEFAULT_HANDLER);
@@ -299,9 +295,8 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
init(complexUnion, pgmTestCat, false);
assertEquals(21, model.getNumComponents());
- SwingUtilities.invokeLater(() -> complexUnion.getDataTypeManager()
- .remove(
- simpleStructure, TaskMonitor.DUMMY));
+ SwingUtilities.invokeLater(
+ () -> complexUnion.getDataTypeManager().remove(simpleStructure, TaskMonitor.DUMMY));
waitForSwing();
assertEquals(15, model.getNumComponents());
}
@@ -321,9 +316,8 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
waitForSwing();
assertTrue(simpleUnion.isEquivalent(getDataType(0)));
- SwingUtilities.invokeLater(() -> simpleUnion.getDataTypeManager()
- .remove(simpleUnion,
- TaskMonitor.DUMMY));
+ SwingUtilities.invokeLater(
+ () -> simpleUnion.getDataTypeManager().remove(simpleUnion, TaskMonitor.DUMMY));
waitForSwing();
assertEquals(0, model.getNumComponents());
}
@@ -497,18 +491,14 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
newComplexUnion.add(new CharDataType(), 1);
programDTM.replaceDataType(complexUnion, newComplexUnion, true);
- waitForSwing();
- DataType origCopy = newComplexUnion.clone(null);
// Verify the Reload Union Editor? dialog is displayed.
- Window dialog = waitForWindow("Reload Union Editor?");
+ Window dialog = waitForWindow("Close Union Editor?");
assertNotNull(dialog);
pressButtonByText(dialog, "Yes");
- dialog.dispose();
- dialog = null;
+ waitForSwing();
- assertEquals(((Union) origCopy).getNumComponents(), model.getNumComponents());
- assertTrue(origCopy.isEquivalent(model.viewComposite));
+ assertFalse(provider.isVisible());
}
@Test
@@ -532,14 +522,14 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
newComplexUnion.add(new CharDataType(), 1);
programDTM.replaceDataType(complexUnion, newComplexUnion, true);
- waitForSwing();
// Verify the Reload Union Editor? dialog is displayed.
- Window dialog = waitForWindow("Reload Union Editor?");
+ Window dialog = waitForWindow("Close Union Editor?");
assertNotNull(dialog);
pressButtonByText(dialog, "No");
- dialog.dispose();
- dialog = null;
+ waitForSwing();
+
+ assertTrue(provider.isVisible());
assertEquals(((Union) viewCopy).getNumComponents(), model.getNumComponents());
assertTrue(viewCopy.isEquivalent(model.viewComposite));
@@ -556,8 +546,14 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
assertTrue(complexUnion.isEquivalent(model.viewComposite));
programDTM.replaceDataType(complexUnion, newComplexUnion, true);
+
+ // Verify Union Editor closes (we don't want two editors for the same type)
+ Window dialog = waitForWindow("Closing Union Editor");
+ assertNotNull(dialog);
+ pressButtonByText(dialog, "OK");
waitForSwing();
- assertTrue(newComplexUnion.isEquivalent(model.viewComposite));
+
+ assertFalse(provider.isVisible());
}
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProviderTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProviderTest.java
index 2743888c6d..e14d8beb87 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProviderTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProviderTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -160,14 +160,28 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
// Apply the changes
invoke(applyAction);
+ waitForSwing();
+
assertTrue(complexUnion.isEquivalent(model.viewComposite));
// Undo the apply
undo(program);
+
+ Window dialog = waitForWindow("Reload Union Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "Yes");
+ waitForSwing();
+
assertTrue(complexUnion.isEquivalent(model.viewComposite));
// Redo the apply
redo(program);
+
+ dialog = waitForWindow("Reload Union Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "Yes");
+ waitForSwing();
+
assertTrue(complexUnion.isEquivalent(model.viewComposite));
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider1Test.java
index 6637f3d765..76afce5a1c 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider1Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider1Test.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -170,7 +170,7 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
failWithException("Editor apply failure", e);
}
});
-
+
deleteFunction("0x200");
// Verify the Reload Stack Editor? dialog is not displayed.
@@ -378,7 +378,6 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
@Test
public void testUndoApplyComponentChanges() throws Exception {
- Window dialog;
editStack(function.getEntryPoint().toString());
@@ -404,7 +403,7 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
// Verify the Reload Stack Editor? dialog is displayed.
waitForSwing();
- dialog = getWindow("Reload Stack Editor?");
+ Window dialog = getWindow("Reload Stack Editor?");
assertNull(dialog);
sv = stack.getVariableContaining(-0x8);
assertNotNull(sv);
@@ -428,41 +427,60 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
}
@Test
- public void testUndoNewDtComponent() throws Exception {
+ public void testUndoNewDtComponentWithChange() throws Exception {
- // NOTE: This test appears to verify that the undefined*16 type
- // resolved against the program DTM used by the stack editor
- // is removed on the first undo - unfortunately, the redo
- // does not restore the editor state. It is unclear why a private
- // DTM is not employed similar to the Structure editor which
- // would allow the new undefined*16 type to persist after the undo (see SCR 10280)
+ editStack(function.getEntryPoint().toString());
- Window dialog;
+ // Put 2 byte pointer at -0x1b
+ DataType ptr = new Pointer16DataType();
+ setType(ptr, 4);
+
+ apply();
+ waitForSwing();
+
+ // Put word pointer at -0x1b
+ setType(WordDataType.dataType, 4);
+
+ // Undo the apply of a new data type to an editor component.
+ undo(program, false);
+
+ // Verify the Reload Stack Editor? dialog is displayed.
+ Window dialog = waitForWindow("Reload Stack Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "No");
+
+ waitForSwing();
+
+ // Redo the apply
+ redo(program, false);
+
+ dialog = waitForWindow("Reload Stack Editor?");
+ assertNotNull(dialog);
+ pressButton(dialog, "Yes");
+ waitForSwing();
+
+ assertTrue(ptr.isEquivalent(getDataType(4)));
+ }
+
+ @Test
+ public void testUndoNewDtComponentWithoutChange() throws Exception {
editStack(function.getEntryPoint().toString());
// Put 2 byte pointer at -0x1b
setType(new Pointer16DataType(), 4);
+ apply();
+ waitForSwing();
+
// Undo the apply of a new data type to an editor component.
undo(program, false);
// Verify the Reload Stack Editor? dialog is displayed.
- dialog = waitForWindow("Reload Stack Editor?");
- assertNotNull(dialog);
- pressButton(dialog, "No");
- dialog.dispose();
waitForSwing();
-
- dialog = getWindow("Reload Stack Editor?");
+ Window dialog = getWindow("Reload Stack Editor?");
assertNull(dialog);
- // Redo the apply
- redo(program, false);
- waitForSwing();
- dialog = getWindow("Reload Stack Editor?");
- assertNull(dialog);
-
- cleanup();
+ assertEquals(DataType.DEFAULT, getDataType(4));
}
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java
index fa94f5b6cd..6f019bb9df 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -685,6 +685,8 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
DataType byteDt = root.getDataType("byte");
DataType wordDt = root.getDataType("word");
+ assertEquals(4, getEventCount());
+
Event ev = getEvent(0);
assertEquals("Cat Added", ev.evName);
assertEquals(null, ev.dt);
@@ -701,22 +703,10 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(root.getCategoryPath(), ev.parent);
ev = getEvent(3);
- assertEquals("DT Changed", ev.evName);
- assertTrue(dt.isEquivalent(ev.dt));
- assertEquals(null, ev.parent);
-
-// ev = getEvent(4); // eliminated size change event during creation
-// assertEquals("DT Changed", ev.evName);
-// assertTrue(dt.isEquivalent(ev.dt));
-// assertEquals(null, ev.parent);
-
- ev = getEvent(4);
assertEquals("DT Added", ev.evName);
assertTrue(dt.isEquivalent(ev.dt));
assertEquals(sub1.getCategoryPath(), ev.parent);
- assertEquals(5, getEventCount());
-
}
@Test
@@ -807,8 +797,9 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
struct2 = (Structure) newDt.insert(3, struct2).getDataType();
- assertEquals(4, getEventCount());
- Event ev = getEvent(3);
+ assertEquals(3, getEventCount());
+
+ Event ev = getEvent(2);
assertEquals("DT Changed", ev.evName);
assertEquals(newDt, ev.dt);
}
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java
index 75bdec1617..96d1848021 100644
--- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java
+++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -283,6 +283,7 @@ public class UnionDataTypeTest extends AbstractGenericTest {
union.delete(Sets.newHashSet(2, 4));
assertEquals(2, union.getLength());
+
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"pack(disabled)\n" +
@@ -293,6 +294,10 @@ public class UnionDataTypeTest extends AbstractGenericTest {
"}\n" +
"Length: 2 Alignment: 1", union);
//@formatter:on
+
+ DataTypeComponent[] comps = union.getDefinedComponents();
+ assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
+ assertEquals(2, comps[2].getOrdinal());
}
@Test
diff --git a/Ghidra/Features/VersionTracking/data/version.tracking.theme.properties b/Ghidra/Features/VersionTracking/data/version.tracking.theme.properties
index 65e4177011..26da800df6 100644
--- a/Ghidra/Features/VersionTracking/data/version.tracking.theme.properties
+++ b/Ghidra/Features/VersionTracking/data/version.tracking.theme.properties
@@ -10,10 +10,6 @@ color.bg.version.tracking.dual.listing.highlight.markup.failed = color.palette.r
color.bg.version.tracking.dual.listing.highlight.markup.no.address = color.palette.lavender
color.bg.version.tracking.dual.listing.highlight.markup.same = color.palette.lightskyblue
color.bg.version.tracking.dual.listing.highlight.markup.conflict = color.palette.gold
-
-color.bg.version.tracking.filter.formatted.field.error = color.palette.lightgray
-color.bg.version.tracking.filter.formatted.field.editing = color.bg.filterfield
-color.fg.version.tracking.filter.formatted.field.editing = color.fg.filterfield
color.bg.version.tracking.match.table.locked.out = color.palette.aliceblue
color.bg.version.tracking.match.table.markup.status.applied = color.palette.limegreen
@@ -83,7 +79,6 @@ icon.version.tracking.tag.status.deleted = tag_blue_delete.png
icon.version.tracking.tag.status.existing = tag_blue.png
icon.version.tracking.tag.button.undo = undo-apply.png
-icon.version.tracking.filter.status.changed = bullet_black.png
icon.version.tracking.filter.status.invalid = no_small.png
icon.version.tracking.filter.status.applied = bullet_green.png
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/AbstractAddressRangeFilter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/AbstractAddressRangeFilter.java
index d32197ce80..b3e7e60598 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/AbstractAddressRangeFilter.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/AbstractAddressRangeFilter.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -29,7 +29,9 @@ import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.label.GDLabel;
import docking.widgets.label.GHtmlLabel;
-import docking.widgets.textfield.HexIntegerFormatter;
+import docking.widgets.numberformat.HexIntegerFormatter;
+import docking.widgets.numberformat.IntegerFormatterFactory;
+import docking.widgets.textfield.*;
import generic.theme.GColor;
import ghidra.feature.vt.api.main.VTAssociation;
import ghidra.feature.vt.gui.provider.matchtable.NumberRangeProducer;
@@ -59,8 +61,8 @@ public abstract class AbstractAddressRangeFilter extends AncillaryFilter
private static final Long MAX_ADDRESS_VALUE = Long.MAX_VALUE;
private JComponent component;
- private FilterFormattedTextField lowerAddressRangeTextField;
- private FilterFormattedTextField upperAddressRangeTextField;
+ private GFormattedTextField lowerAddressRangeTextField;
+ private GFormattedTextField upperAddressRangeTextField;
private JComboBox lowerRangeComboBox;
private JComboBox upperRangeComboBox;
@@ -90,14 +92,14 @@ public abstract class AbstractAddressRangeFilter extends AncillaryFilter
enablePanel.add(enableCheckBox, BorderLayout.NORTH);
// begin address field (long input field with hex)
- lowerAddressRangeTextField = new FilterFormattedTextField(
+ lowerAddressRangeTextField = new GFormattedTextField(
new IntegerFormatterFactory(new HexIntegerFormatter(), false), MIN_ADDRESS_VALUE);
lowerAddressRangeTextField.setName("Lower Address Range Text Field"); // for tracking state
lowerAddressRangeTextField.setColumns(15);
lowerAddressRangeTextField.setMinimumSize(lowerAddressRangeTextField.getPreferredSize());
// end address field (long input field with hex)
- upperAddressRangeTextField = new FilterFormattedTextField(
+ upperAddressRangeTextField = new GFormattedTextField(
new IntegerFormatterFactory(new HexIntegerFormatter(), false), MAX_ADDRESS_VALUE);
upperAddressRangeTextField.setName("Upper Address Range Text Field"); // for tracking state
upperAddressRangeTextField.setColumns(15);
@@ -169,17 +171,20 @@ public abstract class AbstractAddressRangeFilter extends AncillaryFilter
}
};
- FilterStatusListener notificationListener = status -> fireStatusChanged(status);
+ TextEntryStatusListener notificationListener = s -> {
+ FilterEditingStatus status = FilterEditingStatus.getFilterStatus(s);
+ fireStatusChanged(status);
+ };
StatusLabel lowerScoreStatusLabel =
new StatusLabel(lowerAddressRangeTextField, MIN_ADDRESS_VALUE);
- lowerAddressRangeTextField.addFilterStatusListener(lowerScoreStatusLabel);
- lowerAddressRangeTextField.addFilterStatusListener(notificationListener);
+ lowerAddressRangeTextField.addTextEntryStatusListener(lowerScoreStatusLabel);
+ lowerAddressRangeTextField.addTextEntryStatusListener(notificationListener);
StatusLabel upperScoreStatusLabel =
new StatusLabel(upperAddressRangeTextField, MAX_ADDRESS_VALUE);
- upperAddressRangeTextField.addFilterStatusListener(upperScoreStatusLabel);
- upperAddressRangeTextField.addFilterStatusListener(notificationListener);
+ upperAddressRangeTextField.addTextEntryStatusListener(upperScoreStatusLabel);
+ upperAddressRangeTextField.addTextEntryStatusListener(notificationListener);
disabledScreen = createDisabledScreen(layeredPane);
@@ -246,7 +251,7 @@ public abstract class AbstractAddressRangeFilter extends AncillaryFilter
component.validate();
}
- private JComboBox createComboBox(FilterFormattedTextField field, Long defaultValue,
+ private JComboBox createComboBox(GFormattedTextField field, Long defaultValue,
String prototypeString) {
GhidraComboBox comboBox = new GhidraComboBox<>(new LimitedHistoryComboBoxModel()) {
// overridden to paint seamlessly with out color changing text field
@@ -287,14 +292,22 @@ public abstract class AbstractAddressRangeFilter extends AncillaryFilter
return component;
}
+ private FilterEditingStatus getLowerAddressRangeStatus() {
+ return FilterEditingStatus.getFilterStatus(lowerAddressRangeTextField);
+ }
+
+ private FilterEditingStatus getUpperAddressRangeStatus() {
+ return FilterEditingStatus.getFilterStatus(upperAddressRangeTextField);
+ }
+
@Override
public FilterEditingStatus getFilterStatus() {
if (!isEnabled) {
return FilterEditingStatus.NONE;
}
- FilterEditingStatus lowerStatus = lowerAddressRangeTextField.getFilterStatus();
- FilterEditingStatus upperStatus = upperAddressRangeTextField.getFilterStatus();
+ FilterEditingStatus lowerStatus = getLowerAddressRangeStatus();
+ FilterEditingStatus upperStatus = getUpperAddressRangeStatus();
if (lowerStatus == FilterEditingStatus.ERROR || upperStatus == FilterEditingStatus.ERROR) {
return FilterEditingStatus.ERROR;
@@ -339,8 +352,8 @@ public abstract class AbstractAddressRangeFilter extends AncillaryFilter
return true;
}
- if (lowerAddressRangeTextField.getFilterStatus() == FilterEditingStatus.ERROR ||
- upperAddressRangeTextField.getFilterStatus() == FilterEditingStatus.ERROR) {
+ if (getLowerAddressRangeStatus() == FilterEditingStatus.ERROR ||
+ getUpperAddressRangeStatus() == FilterEditingStatus.ERROR) {
return true; // for an invalid filter state, we let all values through
}
@@ -572,10 +585,10 @@ public abstract class AbstractAddressRangeFilter extends AncillaryFilter
private class FormattedFieldComboBoxEditor implements ComboBoxEditor {
private EventListenerList listeners = new EventListenerList();
- private final FilterFormattedTextField textField;
+ private final GFormattedTextField textField;
private final Object defaultValue;
- FormattedFieldComboBoxEditor(FilterFormattedTextField textField) {
+ FormattedFieldComboBoxEditor(GFormattedTextField textField) {
this.textField = textField;
defaultValue = textField.getValue();
}
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/Filter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/Filter.java
index 6b37eee795..96eae5d91a 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/Filter.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/Filter.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,6 +22,7 @@ import java.util.Set;
import javax.swing.Icon;
import javax.swing.JComponent;
+import docking.widgets.textfield.GFormattedTextField;
import generic.theme.GIcon;
import ghidra.framework.options.SaveState;
import ghidra.util.exception.AssertException;
@@ -72,8 +73,6 @@ public abstract class Filter {
public enum FilterEditingStatus {
NONE("", null),
- DIRTY("Filter contents have changed, but are not yet applied", new GIcon(
- "icon.version.tracking.filter.status.changed")),
ERROR("Filter contents are not valid", new GIcon(
"icon.version.tracking.filter.status.invalid")),
APPLIED("Filter applied", new GIcon("icon.version.tracking.filter.status.applied"));
@@ -93,6 +92,17 @@ public abstract class Filter {
Icon getIcon() {
return icon;
}
+
+ public static FilterEditingStatus getFilterStatus(GFormattedTextField textEntryField) {
+ switch (textEntryField.getTextEntryStatus()) {
+ case INVALID:
+ return FilterEditingStatus.ERROR;
+ case CHANGED:
+ return FilterEditingStatus.APPLIED;
+ default:
+ return FilterEditingStatus.NONE;
+ }
+ }
}
/**
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/StatusLabel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/StatusLabel.java
index 8a2db315a7..aa80c38fe8 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/StatusLabel.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/StatusLabel.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,9 +21,11 @@ import java.awt.event.*;
import javax.swing.*;
import docking.widgets.label.GDLabel;
+import docking.widgets.textfield.GFormattedTextField;
+import docking.widgets.textfield.TextEntryStatusListener;
import ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus;
-public class StatusLabel extends GDLabel implements FilterStatusListener {
+public class StatusLabel extends GDLabel implements TextEntryStatusListener {
private final JFormattedTextField textField;
@@ -82,7 +84,8 @@ public class StatusLabel extends GDLabel implements FilterStatusListener {
}
@Override
- public void filterStatusChanged(FilterEditingStatus status) {
+ public void statusChanged(GFormattedTextField textEntryField) {
+ FilterEditingStatus status = FilterEditingStatus.getFilterStatus(textEntryField);
resetBounds();
setIcon(status.getIcon());
setToolTipText(status.getDescription() + " (click to reset)");
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/AbstractDoubleRangeFilter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/AbstractDoubleRangeFilter.java
index 158933b416..eba57b5644 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/AbstractDoubleRangeFilter.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/AbstractDoubleRangeFilter.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -26,6 +26,8 @@ import org.apache.commons.lang3.StringUtils;
import docking.widgets.label.GDLabel;
import docking.widgets.numberformat.BoundedRangeDecimalFormatterFactory;
+import docking.widgets.textfield.GFormattedTextField;
+import docking.widgets.textfield.TextEntryStatusListener;
import ghidra.feature.vt.gui.filters.*;
import ghidra.framework.options.SaveState;
import ghidra.util.layout.HorizontalLayout;
@@ -41,8 +43,8 @@ public abstract class AbstractDoubleRangeFilter extends Filter
private final Double minValue;
private JComponent component;
- private FilterFormattedTextField upperBoundField;
- private FilterFormattedTextField lowerBoundField;
+ private GFormattedTextField upperBoundField;
+ private GFormattedTextField lowerBoundField;
private String filterName;
AbstractDoubleRangeFilter(String filterName, Double minValue, Double maxValue) {
@@ -54,7 +56,7 @@ public abstract class AbstractDoubleRangeFilter extends Filter
}
private void createLowerBoundField() {
- lowerBoundField = new FilterFormattedTextField(
+ lowerBoundField = new GFormattedTextField(
new BoundedRangeDecimalFormatterFactory(maxValue, minValue, FORMAT), minValue);
lowerBoundField.setName("Lower " + filterName + " Filter Field"); // for debugging
lowerBoundField.setColumns(4);
@@ -63,7 +65,7 @@ public abstract class AbstractDoubleRangeFilter extends Filter
}
private void createUpperBoundField() {
- upperBoundField = new FilterFormattedTextField(
+ upperBoundField = new GFormattedTextField(
new BoundedRangeDecimalFormatterFactory(maxValue, minValue, FORMAT), maxValue);
upperBoundField.setName("Upper " + filterName + " Filter Field"); // for debugging
upperBoundField.setColumns(4);
@@ -93,15 +95,18 @@ public abstract class AbstractDoubleRangeFilter extends Filter
panel.add(middleLabel);
panel.add(upperBoundField);
- FilterStatusListener notificationListener = status -> fireStatusChanged(status);
+ TextEntryStatusListener notificationListener = s -> {
+ FilterEditingStatus status = FilterEditingStatus.getFilterStatus(s);
+ fireStatusChanged(status);
+ };
StatusLabel lowerBoundStatusLabel = new StatusLabel(lowerBoundField, minValue);
- lowerBoundField.addFilterStatusListener(lowerBoundStatusLabel);
- lowerBoundField.addFilterStatusListener(notificationListener);
+ lowerBoundField.addTextEntryStatusListener(lowerBoundStatusLabel);
+ lowerBoundField.addTextEntryStatusListener(notificationListener);
StatusLabel upperBoundStatusLabel = new StatusLabel(upperBoundField, maxValue);
- upperBoundField.addFilterStatusListener(upperBoundStatusLabel);
- upperBoundField.addFilterStatusListener(notificationListener);
+ upperBoundField.addTextEntryStatusListener(upperBoundStatusLabel);
+ upperBoundField.addTextEntryStatusListener(notificationListener);
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.add(panel, BASE_COMPONENT_LAYER);
@@ -127,10 +132,18 @@ public abstract class AbstractDoubleRangeFilter extends Filter
return component;
}
+ private FilterEditingStatus getLowerBoundStatus() {
+ return FilterEditingStatus.getFilterStatus(lowerBoundField);
+ }
+
+ private FilterEditingStatus getUpperBoundStatus() {
+ return FilterEditingStatus.getFilterStatus(upperBoundField);
+ }
+
@Override
public FilterEditingStatus getFilterStatus() {
- FilterEditingStatus lowerStatus = lowerBoundField.getFilterStatus();
- FilterEditingStatus upperStatus = upperBoundField.getFilterStatus();
+ FilterEditingStatus lowerStatus = getLowerBoundStatus();
+ FilterEditingStatus upperStatus = getUpperBoundStatus();
if (lowerStatus == FilterEditingStatus.ERROR || upperStatus == FilterEditingStatus.ERROR) {
return FilterEditingStatus.ERROR;
@@ -146,8 +159,8 @@ public abstract class AbstractDoubleRangeFilter extends Filter
@Override
public boolean passesFilter(T t) {
- if (lowerBoundField.getFilterStatus() == FilterEditingStatus.ERROR ||
- upperBoundField.getFilterStatus() == FilterEditingStatus.ERROR) {
+ if (getLowerBoundStatus() == FilterEditingStatus.ERROR ||
+ getUpperBoundStatus() == FilterEditingStatus.ERROR) {
return true; // for an invalid filter state, we let all values through
}
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/LengthFilter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/LengthFilter.java
index 6114e3c967..dfa306434c 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/LengthFilter.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/LengthFilter.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -27,6 +27,8 @@ import javax.swing.border.Border;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.label.GDLabel;
+import docking.widgets.numberformat.IntegerFormatterFactory;
+import docking.widgets.textfield.GFormattedTextField;
import ghidra.feature.vt.api.main.VTMatch;
import ghidra.feature.vt.gui.filters.*;
import ghidra.framework.options.SaveState;
@@ -39,7 +41,7 @@ public class LengthFilter extends Filter {
private static final Integer DEFAULT_FILTER_VALUE = 0;
private JComponent component;
- private FilterFormattedTextField textField;
+ private GFormattedTextField textField;
public LengthFilter() {
component = createComponent();
@@ -49,7 +51,7 @@ public class LengthFilter extends Filter {
final JLabel label = new GDLabel("Length Filter: ");
Integer defaultValue = DEFAULT_FILTER_VALUE;
- textField = new FilterFormattedTextField(new IntegerFormatterFactory(false), defaultValue);
+ textField = new GFormattedTextField(new IntegerFormatterFactory(false), defaultValue);
textField.setName("Length Filter Field"); // for debugging
textField.setInputVerifier(new IntegerInputVerifier());
textField.setHorizontalAlignment(SwingConstants.RIGHT);
@@ -67,8 +69,11 @@ public class LengthFilter extends Filter {
final JLayeredPane layeredPane = new JLayeredPane();
StatusLabel statusLabel = new StatusLabel(textField, defaultValue);
- textField.addFilterStatusListener(statusLabel);
- textField.addFilterStatusListener(status -> fireStatusChanged(status));
+ textField.addTextEntryStatusListener(statusLabel);
+ textField.addTextEntryStatusListener(s -> {
+ FilterEditingStatus status = FilterEditingStatus.getFilterStatus(s);
+ fireStatusChanged(status);
+ });
layeredPane.add(panel, BASE_COMPONENT_LAYER);
layeredPane.add(statusLabel, HOVER_COMPONENT_LAYER);
layeredPane.setPreferredSize(panel.getPreferredSize());
@@ -91,7 +96,7 @@ public class LengthFilter extends Filter {
@Override
public FilterEditingStatus getFilterStatus() {
- return textField.getFilterStatus();
+ return FilterEditingStatus.getFilterStatus(textField);
}
@Override
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/AbstractTextFilter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/AbstractTextFilter.java
index 443ec455e6..684771396f 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/AbstractTextFilter.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/AbstractTextFilter.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -28,9 +28,11 @@ import javax.swing.text.DefaultFormatterFactory;
import docking.widgets.label.GDLabel;
import docking.widgets.table.GTable;
+import docking.widgets.textfield.GFormattedTextField;
import ghidra.feature.vt.api.main.VTAssociation;
import ghidra.feature.vt.api.main.VTSession;
-import ghidra.feature.vt.gui.filters.*;
+import ghidra.feature.vt.gui.filters.Filter;
+import ghidra.feature.vt.gui.filters.StatusLabel;
import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.framework.options.SaveState;
import ghidra.program.model.address.Address;
@@ -44,7 +46,7 @@ public abstract class AbstractTextFilter extends Filter {
private static final Integer HOVER_COMPONENT_LAYER = 2;
private JComponent component;
- private FilterFormattedTextField textField;
+ private GFormattedTextField textField;
private String defaultValue = "";
protected VTController controller;
protected final GTable table;
@@ -62,7 +64,7 @@ public abstract class AbstractTextFilter extends Filter {
panel.setBorder(BorderFactory.createCompoundBorder(outsideBorder, paddingBorder));
DefaultFormatterFactory factory = new DefaultFormatterFactory(new DefaultFormatter());
- textField = new FilterFormattedTextField(factory, defaultValue);
+ textField = new GFormattedTextField(factory, defaultValue);
textField.setName(filterName + " Field"); // for debugging
textField.setColumns(20);
textField.setMinimumSize(textField.getPreferredSize());
@@ -76,8 +78,11 @@ public abstract class AbstractTextFilter extends Filter {
panel.add(textField, BorderLayout.CENTER);
StatusLabel nameFieldStatusLabel = new StatusLabel(textField, defaultValue);
- textField.addFilterStatusListener(nameFieldStatusLabel);
- textField.addFilterStatusListener(status -> fireStatusChanged(status));
+ textField.addTextEntryStatusListener(nameFieldStatusLabel);
+ textField.addTextEntryStatusListener(s -> {
+ FilterEditingStatus status = FilterEditingStatus.getFilterStatus(s);
+ fireStatusChanged(status);
+ });
final JLayeredPane layeredPane = new JLayeredPane();
layeredPane.add(panel, BASE_COMPONENT_LAYER);
@@ -127,7 +132,7 @@ public abstract class AbstractTextFilter extends Filter {
@Override
public FilterEditingStatus getFilterStatus() {
- return textField.getFilterStatus();
+ return FilterEditingStatus.getFilterStatus(textField);
}
protected String getTextFieldText() {
diff --git a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java
index 1f00f89d95..96b6880b9a 100644
--- a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java
+++ b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -532,6 +532,16 @@ public class DBHandle {
return false;
}
+ /**
+ * Provides a means of detecting changes to the underlying database buffers
+ * during a transaction.
+ *
+ * @return current modification count
+ */
+ public long getModCount() {
+ return bufferMgr.getModCount();
+ }
+
/**
* Returns true if there are uncommitted changes to the database.
* @return true if there are uncommitted changes to the database.
diff --git a/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java b/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java
index ade2f55f36..3697483c3e 100644
--- a/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java
+++ b/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -68,6 +68,7 @@ public class BufferMgr {
private Object snapshotLock = new Object(); // Used to prevent BufferNode modifications during snapshot
private boolean modifiedSinceSnapshot = false;
private boolean hasNonUndoableChanges = false;
+ private long modCount;
private int bufferSize;
@@ -238,7 +239,7 @@ public class BufferMgr {
if (lockCount != 0) {
throw new IOException("Unable to re-initialize buffer cache while in-use");
}
-
+
if (cacheFile != null) {
cacheFile.delete();
}
@@ -248,7 +249,7 @@ public class BufferMgr {
cacheTail = new BufferNode(TAIL, -1);
cacheHead.nextCached = cacheTail;
cacheTail.prevCached = cacheHead;
-
+
cacheSize = 0;
buffersOnHand = 0;
@@ -264,7 +265,7 @@ public class BufferMgr {
cacheFile.setParameter(name, sourceFile.getParameter(name));
}
}
-
+
resetCacheStatistics();
if (alwaysPreCache) {
@@ -1093,6 +1094,7 @@ public class BufferMgr {
throw new AssertException();
}
+ ++modCount;
modifiedSinceSnapshot = true;
// Establish current checkpoint if necessary
@@ -1199,6 +1201,14 @@ public class BufferMgr {
}
}
+ /**
+ * Provides a means of detecting changes to the underlying database during a transaction.
+ * @return current modification count
+ */
+ public synchronized long getModCount() {
+ return modCount;
+ }
+
/**
* @return true if unsaved "buffer" changes exist.
* If no changes have been made, or all changes have been
diff --git a/Ghidra/Framework/Docking/data/docking.theme.properties b/Ghidra/Framework/Docking/data/docking.theme.properties
index cf957e6ef2..ddaa0432c9 100644
--- a/Ghidra/Framework/Docking/data/docking.theme.properties
+++ b/Ghidra/Framework/Docking/data/docking.theme.properties
@@ -54,6 +54,10 @@ color.bg.widget.tabs.more.tabs.hover = color.bg.widget.tabs.selected
color.fg.widget.tabs.list = color.fg
+color.bg.formatted.field.error = color.palette.lightcoral
+color.bg.formatted.field.editing = color.bg.filterfield
+color.fg.formatted.field.editing = color.fg.filterfield
+
icon.folder.new = folder_add.png
icon.toggle.expand = expand.gif
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexIntegerFormatter.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/HexIntegerFormatter.java
similarity index 98%
rename from Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexIntegerFormatter.java
rename to Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/HexIntegerFormatter.java
index b068e59d7c..4e571460f0 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexIntegerFormatter.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/HexIntegerFormatter.java
@@ -4,16 +4,16 @@
* 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 docking.widgets.textfield;
+package docking.widgets.numberformat;
import java.text.Format;
import java.text.ParseException;
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerFormatter.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/IntegerFormatter.java
similarity index 98%
rename from Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerFormatter.java
rename to Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/IntegerFormatter.java
index 6e52a6f816..51463c3a02 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerFormatter.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/IntegerFormatter.java
@@ -1,20 +1,19 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 docking.widgets.textfield;
+package docking.widgets.numberformat;
import java.awt.Toolkit;
import java.text.*;
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/IntegerFormatterFactory.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/IntegerFormatterFactory.java
similarity index 91%
rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/IntegerFormatterFactory.java
rename to Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/IntegerFormatterFactory.java
index 862a23c319..3f24890c9a 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/IntegerFormatterFactory.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/numberformat/IntegerFormatterFactory.java
@@ -1,27 +1,24 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.feature.vt.gui.filters;
+package docking.widgets.numberformat;
import javax.swing.JFormattedTextField;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.text.DefaultFormatterFactory;
-import docking.widgets.textfield.IntegerFormatter;
-
public class IntegerFormatterFactory extends DefaultFormatterFactory {
private AbstractFormatter formatter = new IntegerFormatter();
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/FilterFormattedTextField.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/GFormattedTextField.java
similarity index 73%
rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/FilterFormattedTextField.java
rename to Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/GFormattedTextField.java
index cf7c51141d..e76b1cfaa1 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/filters/FilterFormattedTextField.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/GFormattedTextField.java
@@ -4,18 +4,16 @@
* 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.feature.vt.gui.filters;
-
-import static ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus.*;
+package docking.widgets.textfield;
import java.awt.Color;
import java.awt.event.FocusEvent;
@@ -30,34 +28,42 @@ import javax.swing.event.DocumentListener;
import generic.theme.GColor;
import generic.theme.GThemeDefaults.Colors;
-import ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus;
import ghidra.util.SystemUtilities;
-public class FilterFormattedTextField extends JFormattedTextField {
+/**
+ * {@link GFormattedTextField} provides an implementation of {@link JFormattedTextField}
+ * which facilitates entry validation with an indication of its current status.
+ *
+ * When modified from its default value the field background will reflect its
+ * current status.
+ */
+public class GFormattedTextField extends JFormattedTextField {
private static final Color ERROR_BACKGROUND_COLOR =
- new GColor("color.bg.version.tracking.filter.formatted.field.error");
+ new GColor("color.bg.formatted.field.error");
private static final Color EDITING_BACKGROUND_COLOR =
- new GColor("color.bg.version.tracking.filter.formatted.field.editing");
+ new GColor("color.bg.formatted.field.editing");
private static final Color EDITING_FOREGROUND_COLOR =
- new GColor("color.fg.version.tracking.filter.formatted.field.editing");
+ new GColor("color.fg.formatted.field.editing");
- private Set listeners = new HashSet<>();
+ public static enum Status {
+ UNCHANGED, CHANGED, INVALID;
+ }
- private FilterEditingStatus currentStatus = NONE;
- private final Object defaultValue;
- private final String defaultText;
+ private Set listeners = new HashSet<>();
+
+ private Status currentStatus = Status.UNCHANGED;
+ private Object defaultValue;
+ private String defaultText;
private boolean isError;
private boolean ignoreFocusEditChanges;
/** A flag to let us know when we can ignore focus updates */
private boolean isProcessingFocusEvent;
- public FilterFormattedTextField(AbstractFormatterFactory factory, Object defaultValue) {
+ public GFormattedTextField(AbstractFormatterFactory factory, Object defaultValue) {
super(factory);
+
setValue(defaultValue);
- this.defaultValue = defaultValue;
- this.defaultText = getText(); // get the formatted text
- this.currentStatus = NONE;
getDocument().addDocumentListener(new DocumentListener() {
@Override
@@ -76,8 +82,18 @@ public class FilterFormattedTextField extends JFormattedTextField {
}
});
- addPropertyChangeListener("value", evt -> editingFinished());
+ setDefaultValue(defaultValue);
+ addPropertyChangeListener("value", evt -> editingFinished());
+ }
+
+ /**
+ * Establish default value. Text field value should be set before invoking this method.
+ * @param defaultValue default value
+ */
+ public void setDefaultValue(Object defaultValue) {
+ this.defaultValue = defaultValue;
+ this.defaultText = getText(); // get the formatted text
update();
}
@@ -100,22 +116,22 @@ public class FilterFormattedTextField extends JFormattedTextField {
isProcessingFocusEvent = false;
}
- public FilterEditingStatus getFilterStatus() {
+ public Status getTextEntryStatus() {
return currentStatus;
}
- public void addFilterStatusListener(FilterStatusListener listener) {
+ public void addTextEntryStatusListener(TextEntryStatusListener listener) {
listeners.add(listener);
}
- private void filterStatusChanged(FilterEditingStatus status) {
+ private void textEntryStatusChanged(Status status) {
currentStatus = status;
if (listeners == null) {
return; // happens during construction
}
- for (FilterStatusListener listener : listeners) {
- listener.filterStatusChanged(status);
+ for (TextEntryStatusListener listener : listeners) {
+ listener.statusChanged(this);
}
}
@@ -195,24 +211,24 @@ public class FilterFormattedTextField extends JFormattedTextField {
setBackground(Colors.BACKGROUND);
}
- filterStatusChanged(currentStatus);
+ textEntryStatusChanged(currentStatus);
}
private void updateStatus() {
- FilterEditingStatus oldStatus = currentStatus;
+ Status oldStatus = currentStatus;
if (isError) {
- currentStatus = FilterEditingStatus.ERROR;
+ currentStatus = Status.INVALID;
}
else if (hasNonDefaultValue()) {
- currentStatus = APPLIED;
+ currentStatus = Status.CHANGED;
}
else {
- currentStatus = NONE;
+ currentStatus = Status.UNCHANGED;
}
if (oldStatus != currentStatus) {
- filterStatusChanged(currentStatus);
+ textEntryStatusChanged(currentStatus);
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorAction.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/TextEntryStatusListener.java
similarity index 51%
rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorAction.java
rename to Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/TextEntryStatusListener.java
index f1d0e26536..e824b0d2e6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorAction.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/TextEntryStatusListener.java
@@ -4,28 +4,18 @@
* 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;
+package docking.widgets.textfield;
-public interface EditorAction extends CompositeEditorModelListener {
-
- static final String BASIC_ACTION_GROUP = "1_BASIC_EDITOR_ACTION";
- static final String DATA_ACTION_GROUP = "2_DATA_EDITOR_ACTION";
- static final String COMPONENT_ACTION_GROUP = "3_COMPONENT_EDITOR_ACTION";
- static final String BITFIELD_ACTION_GROUP = "4_COMPONENT_EDITOR_ACTION";
-
- /**
- * Method to set the action's enablement based on the associated editor
- * model's current state.
- */
- public void adjustEnablement();
+public interface TextEntryStatusListener {
+ public void statusChanged(GFormattedTextField textField);
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java
index 74eefed2e9..c40709504a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java
@@ -16,6 +16,7 @@
package ghidra.program.database.data;
import java.io.IOException;
+import java.util.Objects;
import db.DBRecord;
import ghidra.docking.settings.Settings;
@@ -212,6 +213,9 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
lock.acquire();
try {
checkDeleted();
+ if (Objects.equals(desc, record.getString(CompositeDBAdapter.COMPOSITE_COMMENT_COL))) {
+ return;
+ }
record.setString(CompositeDBAdapter.COMPOSITE_COMMENT_COL, desc);
compositeAdapter.updateRecord(record, true);
dataMgr.dataTypeChanged(this, false);
@@ -391,13 +395,17 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
return record.getLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL);
}
+ void doSetLastChangeTime(long lastChangeTime) throws IOException {
+ record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime);
+ compositeAdapter.updateRecord(record, false);
+ }
+
@Override
public void setLastChangeTime(long lastChangeTime) {
lock.acquire();
try {
checkDeleted();
- record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime);
- compositeAdapter.updateRecord(record, false);
+ doSetLastChangeTime(lastChangeTime);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java
index 1885f9a8f9..eaba8ff8ec 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -138,7 +138,11 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
if (id == -1) {
return DataType.DEFAULT;
}
- return dataMgr.getDataType(id);
+ DataType dt = dataMgr.getDataType(id);
+ if (dt == null) {
+ return BadDataType.dataType;
+ }
+ return dt;
}
@Override
@@ -191,6 +195,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
@Override
public Settings getDefaultSettings() {
+
if (!hasSettings()) {
return SettingsImpl.NO_SETTINGS;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
index e5058f0edf..6cddc3457d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
@@ -1693,8 +1693,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
// (preference is given to similar kind of datatype when checking existing conflict types)
DataType existingDataType = findDataTypeSameLocation(dataType);
if (existingDataType == null) {
- return createDataType(dataType, getUnusedConflictName(dataType), sourceArchive,
- currentHandler);
+ // create non-existing datatype - keep original name unless it is already used
+ String name = dataType.getName();
+ if (getDataType(dataType.getCategoryPath(), name) != null) {
+ name = getUnusedConflictName(dataType);
+ }
+ return createDataType(dataType, name, sourceArchive, currentHandler);
}
// So we have a dataType with the same path and name, but not equivalent, so use
@@ -2310,7 +2314,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
if (id <= 0) { // removal of certain special types not permitted
return false;
}
- idsToDelete.add(Long.valueOf(id));
+ idsToDelete.add(id);
removeQueuedDataTypes();
return true;
}
@@ -3130,8 +3134,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
structDB.doReplaceWith(struct, false);
- // doReplaceWith may have updated the last change time so set it back to what we want.
- structDB.setLastChangeTime(struct.getLastChangeTime());
+ // doReplaceWith may have updated the last change time so set it back to what we want
+ // without triggering change notification
+ structDB.doSetLastChangeTime(struct.getLastChangeTime());
return structDB;
}
@@ -3198,8 +3203,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
unionDB.doReplaceWith(union, false);
- // doReplaceWith updated the last change time so set it back to what we want.
- unionDB.setLastChangeTime(union.getLastChangeTime());
+ // doReplaceWith may have updated the last change time so set it back to what we want
+ // without triggering change notification
+ unionDB.doSetLastChangeTime(union.getLastChangeTime());
return unionDB;
}
@@ -3717,7 +3723,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
}
- void removeParentChildRecord(long parentID, long childID) {
+ protected void removeParentChildRecord(long parentID, long childID) {
if (isBulkRemoving) {
// we are in the process of bulk removing the given child; no need to call
@@ -3733,6 +3739,26 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
}
+ protected Set getChildIds(long parentID) {
+ try {
+ return parentChildAdapter.getChildIds(parentID);
+ }
+ catch (IOException e) {
+ dbError(e);
+ }
+ return Set.of();
+ }
+
+ protected boolean hasParent(long childID) {
+ try {
+ return parentChildAdapter.hasParent(childID);
+ }
+ catch (IOException e) {
+ dbError(e);
+ }
+ return false;
+ }
+
List getParentDataTypes(long dataTypeId) {
lock.acquire();
try {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java
index b4690e5a08..dcafb22933 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -80,6 +80,16 @@ abstract class ParentChildAdapter {
abstract void removeRecord(long parentID, long childID) throws IOException;
+ /**
+ * Get the unique set of child IDs associated with the specified parent ID.
+ * Since a parent may have duplicate parent-child records, this method
+ * avoids returning the same child more than once.
+ * @param parentID parent datatype ID
+ * @return set of child datatype IDs
+ * @throws IOException if a DB IO error occurs
+ */
+ abstract Set getChildIds(long parentID) throws IOException;
+
/**
* Get the unique set of parent ID associated with the specified childID.
* Since composite parents may have duplicate parent-child records, this method
@@ -90,6 +100,14 @@ abstract class ParentChildAdapter {
*/
abstract Set getParentIds(long childID) throws IOException;
+ /**
+ * Determine if there is one or more parents associated with the specified childID.
+ * @param childID child datatype ID
+ * @return true if a parent was identified, else false
+ * @throws IOException if a DB IO error occurs
+ */
+ abstract boolean hasParent(long childID) throws IOException;
+
abstract void removeAllRecordsForParent(long parentID) throws IOException;
abstract void removeAllRecordsForChild(long childID) throws IOException;
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterNoTable.java
index a612895a5e..803392fcef 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterNoTable.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterNoTable.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -43,11 +43,21 @@ class ParentChildDBAdapterNoTable extends ParentChildAdapter {
throw new UnsupportedOperationException();
}
+ @Override
+ Set getChildIds(long parentID) throws IOException {
+ return Set.of();
+ }
+
@Override
Set getParentIds(long childID) throws IOException {
return Set.of();
}
+ @Override
+ boolean hasParent(long childID) throws IOException {
+ return false;
+ }
+
@Override
boolean needsInitializing() {
return false;
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java
index e9f4a22984..aedb494d7b 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -88,17 +88,33 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter {
}
}
+ @Override
+ Set getChildIds(long parentID) throws IOException {
+ Field[] ids = table.findRecords(new LongField(parentID), PARENT_COL);
+ Set childIds = new HashSet<>(ids.length);
+ for (Field id : ids) {
+ DBRecord rec = table.getRecord(id);
+ childIds.add(rec.getLongValue(CHILD_COL));
+ }
+ return childIds;
+ }
+
@Override
Set getParentIds(long childID) throws IOException {
Field[] ids = table.findRecords(new LongField(childID), CHILD_COL);
Set parentIds = new HashSet<>(ids.length);
- for (int i = 0; i < ids.length; i++) {
- DBRecord rec = table.getRecord(ids[i]);
+ for (Field id : ids) {
+ DBRecord rec = table.getRecord(id);
parentIds.add(rec.getLongValue(PARENT_COL));
}
return parentIds;
}
+ @Override
+ boolean hasParent(long childID) throws IOException {
+ return table.hasRecord(new LongField(childID), CHILD_COL);
+ }
+
public void setNeedsInitializing() {
needsInitializing = true;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java
index a5ffbb492a..338c56cce3 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -111,9 +111,6 @@ abstract class PointerDBAdapter implements RecordTranslator {
}
}
- /**
- *
- */
abstract void deleteTable(DBHandle handle) throws IOException;
/**
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java
index 59ba1ec41e..2ef424b4ca 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java
@@ -2301,7 +2301,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
checkAncestry(replacementDt);
}
catch (Exception e) {
- // TODO: should we flag bad replacement
+ // Handle bad replacement with use of undefined component
replacementDt = isPackingEnabled() ? Undefined1DataType.dataType : DataType.DEFAULT;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataUtilities.java
index d0a8d5a4f6..6cf8153f3d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataUtilities.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataUtilities.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,6 +15,8 @@
*/
package ghidra.program.model.data;
+import org.apache.commons.lang3.StringUtils;
+
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
@@ -36,7 +38,7 @@ public final class DataUtilities {
* @return true if name is valid, else false
*/
public static boolean isValidDataTypeName(String name) {
- if (name == null || name.length() == 0) {
+ if (StringUtils.isBlank(name)) {
return false;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java
index 4fe6f61dae..2cffd19e08 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java
@@ -872,6 +872,14 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
return transaction.intValue();
}
+ /**
+ * Get the number of active transactions
+ * @return number of active transactions
+ */
+ protected int getTransactionCount() {
+ return transactionCount;
+ }
+
@Override
public void endTransaction(int transactionID, boolean commit) {
boolean restored = false;
@@ -953,6 +961,8 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
protected synchronized void clearUndo() {
undoList.clear();
redoList.clear();
+
+ // Flatten all checkpoints then restore undo stack size
dbHandle.setMaxUndos(0);
dbHandle.setMaxUndos(NUM_UNDOS);
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java
index bc002ee434..3bc5fc85b9 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java
@@ -1640,13 +1640,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
* @param dataType the data type of the new component
* @param newOffset offset of replacement component which must fall within origComponents bounds
* @param length the length of the new component
- * @param name the field name of the new component
+ * @param fieldName the field name of the new component
* @param comment the comment for the new component
* @return the new component or null if only a clear operation was performed.
* @throws IllegalArgumentException if unable to identify/make sufficient space
*/
private DataTypeComponent replaceComponents(LinkedList origComponents,
- DataType dataType, int newOffset, int length, String name, String comment)
+ DataType dataType, int newOffset, int length, String fieldName, String comment)
throws IllegalArgumentException {
boolean clearOnly = false;
@@ -1721,8 +1721,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
DataTypeComponentImpl newDtc = null;
if (!clearOnly) {
// insert new component
- newDtc = new DataTypeComponentImpl(dataType, this, length, newOrdinal, newOffset, name,
- comment);
+ newDtc = new DataTypeComponentImpl(dataType, this, length, newOrdinal, newOffset,
+ fieldName, comment);
components.add(index, newDtc);
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java
index e255431f9d..7c5cdb3248 100644
--- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java
+++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -32,7 +32,8 @@ import ghidra.util.task.TaskMonitorAdapter;
public class StructureDBTest extends AbstractGenericTest {
private StructureDB struct;
- private DataTypeManagerDB dataMgr;
+ private StandAloneDataTypeManager dataMgr;
+ private int txId;
@Before
public void setUp() throws Exception {
@@ -42,7 +43,7 @@ public class StructureDBTest extends AbstractGenericTest {
// default data organization is little-endian
// default BitFieldPackingImpl uses gcc conventions with type alignment enabled
- dataMgr.startTransaction("Test");
+ txId = dataMgr.startTransaction("Test");
struct = createStructure("Test", 0);
struct.add(new ByteDataType(), "field1", "Comment1");
@@ -52,6 +53,14 @@ public class StructureDBTest extends AbstractGenericTest {
}
+ @After
+ public void tearDown() {
+ if (dataMgr != null) {
+ dataMgr.endTransaction(txId, true);
+ dataMgr.close();
+ }
+ }
+
private void transitionToBigEndian() {
Structure structClone = struct.clone(null);
@@ -1442,7 +1451,45 @@ public class StructureDBTest extends AbstractGenericTest {
}
@Test
- public void testDeleteMany() throws InvalidDataTypeException {
+ public void testDeleteMany() {
+
+ struct.growStructure(20);
+ struct.insertAtOffset(12, WordDataType.dataType, -1, "A", null);
+ struct.insertAtOffset(16, WordDataType.dataType, -1, "B", null);
+
+ assertEquals(32, struct.getLength());
+ assertEquals(26, struct.getNumComponents());
+ assertEquals(6, struct.getNumDefinedComponents());
+
+ struct.delete(Sets.newHashSet(1, 4, 5));
+
+ assertEquals(28, struct.getLength());
+ assertEquals(23, struct.getNumComponents());
+ assertEquals(5, struct.getNumDefinedComponents());
+
+ DataTypeComponent[] comps = struct.getDefinedComponents();
+ assertEquals(WordDataType.class, comps[3].getDataType().getClass());
+ assertEquals(5, comps[3].getOrdinal());
+ assertEquals(8, comps[3].getOffset());
+
+ // Verify that records were properly updated by comitting and performing an undo/redo
+ dataMgr.endTransaction(txId, true);
+ dataMgr.undo();
+ dataMgr.redo();
+ txId = dataMgr.startTransaction("Continue Test");
+
+ assertEquals(28, struct.getLength());
+ assertEquals(23, struct.getNumComponents());
+ assertEquals(5, struct.getNumDefinedComponents());
+
+ comps = struct.getDefinedComponents();
+ assertEquals(WordDataType.class, comps[3].getDataType().getClass());
+ assertEquals(5, comps[3].getOrdinal());
+ assertEquals(8, comps[3].getOffset());
+ }
+
+ @Test
+ public void testDeleteManyBF() throws InvalidDataTypeException {
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java
index 3fc6a377d9..fa791969e6 100644
--- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java
+++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,8 +30,9 @@ import ghidra.util.task.TaskMonitor;
*/
public class UnionDBTest extends AbstractGenericTest {
- private DataTypeManager dataMgr;
+ private StandAloneDataTypeManager dataMgr;
private UnionDB union;
+ private int txId;
@Before
public void setUp() throws Exception {
@@ -41,7 +42,7 @@ public class UnionDBTest extends AbstractGenericTest {
// default data organization is little-endian
// default BitFieldPackingImpl uses gcc conventions
- dataMgr.startTransaction("Test");
+ txId = dataMgr.startTransaction("Test");
union = createUnion("TestUnion");
union.add(new ByteDataType(), "field1", "Comment1");
@@ -50,6 +51,14 @@ public class UnionDBTest extends AbstractGenericTest {
union.add(new ByteDataType(), "field4", "Comment4");
}
+ @After
+ public void tearDown() {
+ if (dataMgr != null) {
+ dataMgr.endTransaction(txId, true);
+ dataMgr.close();
+ }
+ }
+
private void transitionToBigEndian() {
Union unionClone = union.clone(null);
@@ -380,6 +389,7 @@ public class UnionDBTest extends AbstractGenericTest {
union.delete(Sets.newHashSet(2, 4));
assertEquals(2, union.getLength());
+
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"pack(disabled)\n" +
@@ -390,6 +400,33 @@ public class UnionDBTest extends AbstractGenericTest {
"}\n" +
"Length: 2 Alignment: 1", union);
//@formatter:on
+
+ DataTypeComponent[] comps = union.getDefinedComponents();
+ assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
+ assertEquals(2, comps[2].getOrdinal());
+
+ // Verify that records were properly updated by comitting and performing an undo/redo
+ dataMgr.endTransaction(txId, true);
+ dataMgr.undo();
+ dataMgr.redo();
+ txId = dataMgr.startTransaction("Continue Test");
+
+ assertEquals(2, union.getLength());
+
+ //@formatter:off
+ CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
+ "pack(disabled)\n" +
+ "Union TestUnion {\n" +
+ " 0 byte 1 field1 \"Comment1\"\n" +
+ " 0 word 2 \"Comment2\"\n" +
+ " 0 byte 1 field4 \"Comment4\"\n" +
+ "}\n" +
+ "Length: 2 Alignment: 1", union);
+ //@formatter:on
+
+ comps = union.getDefinedComponents();
+ assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
+ assertEquals(2, comps[2].getOrdinal());
}
@Test
diff --git a/Ghidra/Test/IntegrationTest/src/test/java/docking/widgets/formatter/HexIntegerFormatterTest.java b/Ghidra/Test/IntegrationTest/src/test/java/docking/widgets/formatter/HexIntegerFormatterTest.java
index a6196d72bd..12199824c4 100644
--- a/Ghidra/Test/IntegrationTest/src/test/java/docking/widgets/formatter/HexIntegerFormatterTest.java
+++ b/Ghidra/Test/IntegrationTest/src/test/java/docking/widgets/formatter/HexIntegerFormatterTest.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -26,8 +26,8 @@ import javax.swing.JFormattedTextField;
import org.junit.Before;
import org.junit.Test;
-import docking.widgets.textfield.HexIntegerFormatter;
-import ghidra.feature.vt.gui.filters.IntegerFormatterFactory;
+import docking.widgets.numberformat.HexIntegerFormatter;
+import docking.widgets.numberformat.IntegerFormatterFactory;
public class HexIntegerFormatterTest {