GP-4740 Added undo/redo support to composite editor. Switched actions to use isEnabledForContext. Transitioned VT FilterFormattedTestField to GFormattedTextField and use for editor text entry fields. Cleanup of old datatype tree actions no longer in use. Lots of changes to improve handling of data type dependency changes and restored original DTM.

This commit is contained in:
ghidra1
2024-08-20 13:06:03 -04:00
parent 6347d8bd94
commit 0ccb142e7e
98 changed files with 3898 additions and 3544 deletions
@@ -87,6 +87,15 @@
<LI>Immediately below the structure information area is a status line where status messages
will appear.</LI>
</UL>
<P><IMG src="help/shared/note.png" alt=""> All actions will be disabled while information
entries are being modified and have not yet been comitted (e.g., Name, Description, Size, etc.).
Such edits must either be comitted or reverted
before other actions may be performed. An entry's background will changed to reflect the
validity of an uncomitted value. A valid entry will be comitted by hitting the &lt;Enter&gt;
key or changing focus. While in this edit state the entry may be reverted by hitting the
&lt;Escape&gt; key.</P>
</BLOCKQUOTE>
<H2><A name="Structure_Editor_Apply_Editor_Changes"></A>Applying Changes</H2>
@@ -100,8 +109,46 @@
applied and it is assigned to data in the program, all data items with the structure or union
as the data type now have the new data type. In other words, the size or composition of those
data items in the program will have changed due to the apply.</P>
</BLOCKQUOTE>
<H2><A name="Structure_Editor_Undo_Editor_Change"></A>Undo Change</H2>
<BLOCKQUOTE>
<P>Select the Undo Change icon <IMG src="icon.undo" alt=""> in the toolbar to revert
the previous change within the editor. The editor state maintains a stack of changes
made within the editor. The last change which may be reverted is described by the button's
tooltip. If this action is used and a change is reverted it may be re-applied by using the
<A href="#Structure_Editor_Redo_Editor_Change">Redo Change</A> action. When changes are
<A href="#Structure_Editor_Apply_Editor_Changes">applied</A>
back to the original program or archive the undo/redo stack is cleared.<BR>
</P>
<P><IMG src="help/shared/note.png" alt=""> Any change made to the editor's origininating
datatype manager (i.e., datatype or categories) which impact any datatype directly, or
indirectly, referenced by the edited composite at anytime during the edit session will
cause the undo/redo stack to be cleared.</P>
</BLOCKQUOTE>
<H2><A name="Structure_Editor_Redo_Editor_Change"></A>Redo Change</H2>
<BLOCKQUOTE>
<P>Select the Redo Change icon <IMG src="icon.redo" alt=""> in the toolbar to re-apply
a previous change which was just <A href="#Structure_Editor_Undo_Editor_Change">reverted</A>.
The last reverted change which may be re-applied is described by the button's
tooltip. If this action is used and a change is re-applied it may again be reverted by using the
<A href="#Structure_Editor_Undo_Editor_Change">Undo Change</A> action. When changes are
<A href="#Structure_Editor_Apply_Editor_Changes">applied</A>
back to the original program or archive the undo/redo stack is cleared.<BR>
</P>
<P><IMG src="help/shared/note.png" alt=""> Any change made to the editor's origininating
datatype manager (i.e., datatype or categories) which impact any datatype directly, or
indirectly, referenced by the edited composite at anytime during the edit session will
cause the undo/redo stack to be cleared.</P>
</BLOCKQUOTE>
<H2><A name="Show_In_Data_Type_Manager"></A><A name="Structure_Editor_Show_In_Data_Type_Manager">Show In Data Type Manager</H2>
@@ -492,16 +492,17 @@
changes, a dialog will appear providing an opportunity to save the changes.</P>
</BLOCKQUOTE>
<H3><A name="Save"></A>Saving Changes to a File Data Type Archive</H3>
<H3><A name="Save"></A>Saving Changes to a Data Type Archive</H3>
<BLOCKQUOTE>
<P>Whenever a data type archive has been <A href="#open_for_editing">opened for
editing</A> and has unsaved changes, the node will display its name with '*' attached.
<P>Whenever a data type archive has been opened for
editing and has unsaved changes, the node will display its name with '*' attached.
For example the archive "MyArchive" will display as "MyArchive *". To save these changes,
right-click on the unsaved archive and select the <I><B>Save Archive</B></I> action. The
changes will be saved and the name will be updated to not show a '*'.</P>
</BLOCKQUOTE>
<!-- Unimplemented Action
<H3><A name="Save_As"></A>Saving a File Data Type Archive to a New File<BR>
</H3>
@@ -511,6 +512,37 @@
and filename for the new archive that will be created. The tree will be updated to show
the new name for the archive (the filename). The original archive file is unaffected.</P>
</BLOCKQUOTE>
-->
<H3><A name="Undo_Archive_Change"></A>Undo Unsaved Archive Change</H3>
<BLOCKQUOTE>
<P>The previous unsaved change made to an archive may be reverted by selecting the <B>Undo Change:...</B>
popup menu action while that archive is selected in the data type tree.
Each data type archive which is open for editing maintains a
stack of unsaved changes. The next change which may be reverted is described by the archive's
Undo Change popup menu item. If this action is used and a change is reverted it may be re-applied by using the
<A href="#Redo_Archive_Change">Redo Change</A> action. When the data type archive is
<A href="#Save">saved</A> or <A href="#Lock_Archive">closed for editing</A> the undo/redo stack is
cleared.
</P>
</BLOCKQUOTE>
<H3><A name="Redo_Archive_Change"></A>Redo Unsaved Archive Change</H3>
<BLOCKQUOTE>
<P>The previous <A href="#Undo_Archive_Change">reverted</A> unsaved archive change may be
re-applied by selecting the <B>Redo Change:...</B> popup menu action while that archive is selected in
the data type tree.
The next reverted change which may be re-applied is described by the archive's
Redo Change popup menu item. If this action is used and a change is re-applied it may again be reverted by using the
<A href="#Undo_Archive_Change">Undo Change</A> action. When the data type archive is
<A href="#Save">saved</A> or <A href="#Lock_Archive">closed for editing</A> the undo/redo stack is
cleared.
</P>
</BLOCKQUOTE>
<H3><A name="Delete_Archive"></A>Deleting a Data Type Archive</H3>
@@ -524,24 +556,14 @@
<H3><A name="Remove_Invalid_Archive"></A>Removing an Invalid Data Type Archive</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>When an archive file fails to open (when Ghidra can't find the file in the archive path
or encounters a permission problem) it will be displayed with the <IMG alt="" src=
"images/closedFolderInvalid.png"> icon. If you wish to permanently remove the file path
from the tool configuration and the current program options, you may right-click on it and
select the <I><B>Remove Invalid
Archive</B></I> action.</P>
select the <I><B>Remove Invalid Archive</B></I> action.</P>
</BLOCKQUOTE>
<H3><A name="Pack_All_Data_Types"></A>Pack All Data Types In a Program or Archive</H3>
<BLOCKQUOTE>
<P>Right-click on the program or data type archive where structures and unions are to be packed,
and select the <I><B>Pack All...</B></I> action. A confirmation dialog will appear to
make sure you want to pack all composites in the program or data type
archive. If you continue, all non-packed composites will have default packing enabled.</P>
</BLOCKQUOTE>
<H3><A name="synchronizing"></A> <A name="Update"></A> Updating an Archive From a Source
Archive</H3>
@@ -1000,14 +1022,7 @@
</TBODY>
</TABLE>
</BLOCKQUOTE>
<!-- disabled feature
<H3><A name="Align_Data_Type"></A>Aligning a Data Type</H3>
<BLOCKQUOTE>
<P>Right-click on the structure or union to be packed. Select the <I><B>Pack</B></I>
action. If the data type is non-packed it will be changed to be default packed.</P>
</BLOCKQUOTE>
-->
<H3><A name="Commit_To_Archive"></A>Committing Changes To Source Archive</H3>
<BLOCKQUOTE>
@@ -32,7 +32,6 @@ public class AddBitFieldAction extends CompositeEditorTableAction {
if (!(model instanceof CompEditorModel)) {
throw new AssertException("unsupported use");
}
adjustEnablement();
}
@Override
@@ -42,7 +41,10 @@ public class AddBitFieldAction extends CompositeEditorTableAction {
}
@Override
public void adjustEnablement() {
public boolean isEnabledForContext(ActionContext context) {
if (hasIncompleteFieldEntry()) {
return false;
}
boolean enabled = true;
CompEditorModel editorModel = (CompEditorModel) model;
// Unions do not support non-packed manipulation of bitfields
@@ -50,7 +52,7 @@ public class AddBitFieldAction extends CompositeEditorTableAction {
editorModel.isPackingEnabled() || editorModel.getNumSelectedRows() != 1) {
enabled = false;
}
setEnabled(enabled);
return enabled;
}
}
@@ -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,7 +28,7 @@ import ghidra.program.model.data.InvalidDataTypeException;
public class ApplyAction extends CompositeEditorTableAction {
public final static String ACTION_NAME = "Apply Editor Changes";
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
private final static String GROUP_NAME = MAIN_ACTION_GROUP;
private final static Icon ICON = new GIcon("icon.plugin.composite.editor.apply");
private final static String[] POPUP_PATH = new String[] { "Apply Edits" };
@@ -36,28 +36,29 @@ public class ApplyAction extends CompositeEditorTableAction {
super(provider, ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON);
setDescription("Apply editor changes");
adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
if (!model.isValidName()) {
model.setStatus("Name is not valid.", true);
if (!isEnabledForContext(context)) {
return;
}
provider.editorPanel.comitEntryChanges();
try {
model.apply();
}
catch (EmptyCompositeException | InvalidDataTypeException e) {
model.setStatus(e.getMessage(), true);
}
requestTableFocus();
}
@Override
public void adjustEnablement() {
boolean hasChanges = model.hasChanges();
boolean validName = model.isValidName();
setEnabled(hasChanges && validName);
public boolean isEnabledForContext(ActionContext context) {
if (hasIncompleteFieldEntry()) {
return false;
}
return model.hasChanges() && model.isValidName();
}
}
@@ -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.
@@ -42,11 +42,13 @@ public class ArrayAction 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.createArray();
}
@@ -57,7 +59,8 @@ public class ArrayAction extends CompositeEditorTableAction {
}
@Override
public void adjustEnablement() {
setEnabled(model.isArrayAllowed());
public boolean isEnabledForContext(ActionContext context) {
return !hasIncompleteFieldEntry() && model.isArrayAllowed();
}
}
@@ -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.
@@ -173,7 +173,8 @@ public class BitFieldEditorDialog extends DialogComponentProvider {
return;
}
int ordinal = bitfieldDtc.getOrdinal();
composite.delete(ordinal);
composite.getDataTypeManager()
.withTransaction("Delete Bitfield", () -> composite.delete(ordinal));
bitFieldEditorPanel.componentDeleted(ordinal);
if (listener != null) {
listener.componentChanged(ordinal);
@@ -192,7 +193,6 @@ public class BitFieldEditorDialog extends DialogComponentProvider {
ToggleHexUseAction() {
super("Show Byte Offsets in Hexadecimal", "BitFieldEditorDialog");
setEnabled(true);
setSelected(bitFieldEditorPanel.isShowOffsetsInHex());
setPopupMenuData(new MenuData(new String[] { getName() }));
setHelpLocation(new HelpLocation("DataTypeEditors", "Structure_Bitfield_Editor"));
@@ -225,8 +225,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider {
@Override
protected JMenuItem doCreateMenuItem() {
DockingCheckBoxMenuItem menuItem = new DockingCheckBoxMenuItem(isSelected);
menuItem.setUI(
(DockingCheckboxMenuItemUI) DockingCheckboxMenuItemUI.createUI(menuItem));
menuItem.setUI(DockingCheckboxMenuItemUI.createUI(menuItem));
return menuItem;
}
}
@@ -645,8 +645,14 @@ public class BitFieldEditorPanel extends JPanel {
}
deleteConflicts = (option == OptionDialog.OPTION_ONE);
}
placementComponent.applyBitField(baseDataType, fieldNameTextField.getText().trim(),
fieldCommentTextField.getText().trim(), deleteConflicts, listener);
boolean doDeleteConflicts = deleteConflicts;
this.composite.getDataTypeManager()
.withTransaction("Apply Bitfield",
() -> placementComponent.applyBitField(baseDataType,
fieldNameTextField.getText().trim(), fieldCommentTextField.getText().trim(),
doDeleteConflicts, listener));
enableControls(false);
return true;
}
@@ -460,7 +460,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
repaint();
}
void applyBitField(DataType baseDataType, String fieldName, String fieldComment,
DataTypeComponent applyBitField(DataType baseDataType, String fieldName, String fieldComment,
boolean deleteConflicts, CompositeChangeListener listener) {
if (!editUseEnabled) {
throw new IllegalStateException("component not constructed for edit use");
@@ -519,9 +519,11 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
if (listener != null) {
listener.componentChanged(dtc.getOrdinal());
}
return dtc;
}
catch (ArrayIndexOutOfBoundsException | InvalidDataTypeException e) {
Msg.error(this, "Unexpected bitfield apply error", e);
Msg.showError(this, this, "Unexpected bitfield apply error", e);
return null;
}
finally {
editMode = EditMode.NONE;
@@ -1032,14 +1034,6 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
rightChopBytes = rightChop;
allocationBytes = allocationByteSize - leftChopBytes - rightChopBytes;
if (allocationBytes <= 0) {
int junk = 0;
// allocation shrunk - need to adjust window
// TODO: Need to adjust view port sizing when allocationByteSize changes
}
allocateBits();
layoutBits();
}
@@ -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 ClearAction extends CompositeEditorTableAction {
setDescription("Clear the selected components");
setKeyBindingData(new KeyBindingData(KEY_STROKE));
adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
if (!isEnabledForContext(context)) {
return;
}
try {
model.clearSelectedComponents();
}
@@ -53,7 +55,8 @@ public class ClearAction extends CompositeEditorTableAction {
}
@Override
public void adjustEnablement() {
setEnabled(model.isClearAllowed());
public boolean isEnabledForContext(ActionContext context) {
return !hasIncompleteFieldEntry() && model.isClearAllowed();
}
}
@@ -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
}
}
@@ -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
}
}
}
@@ -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);
@@ -30,7 +30,15 @@ import ghidra.util.HelpLocation;
* <p>
* 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();
}
}
@@ -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<Long> 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<DataType> 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;
}
}
@@ -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() {
@@ -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
@@ -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.
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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);
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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.
* <br>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();
}
@@ -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
@@ -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) {
@@ -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;
}
}
@@ -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<Long, Long> viewToOriginalMap;
private Map<Long, Long> 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;
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
@@ -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
}
}
@@ -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),
@@ -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();
}
}
@@ -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
@@ -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) {
@@ -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);
}
}
@@ -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);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More