mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-10 06:02:09 +00:00
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:
parent
6347d8bd94
commit
0ccb142e7e
@ -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 <Enter>
|
||||
key or changing focus. While in this edit state the entry may be reverted by hitting the
|
||||
<Escape> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
@ -19,6 +19,7 @@ import java.util.*;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import ghidra.program.database.DatabaseObject;
|
||||
import ghidra.program.database.data.DataTypeUtilities;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.InsufficientBytesException;
|
||||
@ -28,6 +29,8 @@ import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
|
||||
private volatile boolean consideringReplacedDataType = false;
|
||||
|
||||
/**
|
||||
* Creates a model for editing a composite data type.
|
||||
* @param provider the provider that is using this model for editing.
|
||||
@ -38,8 +41,7 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
|
||||
@Override
|
||||
public boolean hasChanges() {
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if ((originalDTM != null) && !originalDTM.contains(originalComposite)) {
|
||||
if (originalDTM != null && !originalDTM.contains(originalComposite)) {
|
||||
return true;
|
||||
}
|
||||
return super.hasChanges();
|
||||
@ -56,20 +58,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
selectionChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current dataType name (Structure or Union) as a string.
|
||||
*/
|
||||
@Override
|
||||
protected String getTypeName() {
|
||||
if (viewComposite instanceof Structure) {
|
||||
return "Structure";
|
||||
}
|
||||
else if (viewComposite instanceof Union) {
|
||||
return "Union";
|
||||
}
|
||||
return super.getTypeName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the changes for the current edited composite back to the
|
||||
* original composite.
|
||||
@ -87,7 +75,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
|
||||
FieldSelection saveSelection = new FieldSelection(selection);
|
||||
Composite originalDt = getOriginalComposite();
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (originalDt == null || originalDTM == null) {
|
||||
throw new IllegalStateException(
|
||||
"Can't apply edits without a data type or data type manager.");
|
||||
@ -103,8 +90,7 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
if (renamed) {
|
||||
action += "/Rename";
|
||||
}
|
||||
String type = (originalDt instanceof Union) ? " Union " : " Structure ";
|
||||
int transactionID = originalDTM.startTransaction(action + type + getCompositeName());
|
||||
int transactionID = originalDTM.startTransaction(action + " " + getTypeName());
|
||||
try {
|
||||
if (originalDtExists) {
|
||||
// Update the original structure.
|
||||
@ -137,7 +123,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
finally {
|
||||
provider.updateTitle();
|
||||
// selection = saveSelection;
|
||||
setSelection(saveSelection);
|
||||
originalDTM.endTransaction(transactionID, true);
|
||||
}
|
||||
@ -260,15 +245,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void setDataType(int rowIndex, DataType dt, int length) throws UsrException {
|
||||
if (rowIndex < getNumComponents()) {
|
||||
replace(rowIndex, dt, length);
|
||||
}
|
||||
else {
|
||||
insert(rowIndex, dt, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTypeInstance validateComponentDataType(int rowIndex, String dtString)
|
||||
throws UsrException {
|
||||
@ -295,11 +271,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
return (getNumSelectedRows() > 0) && !isBlankLastLineSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCycleAllowed(CycleGroup cycleGroup) {
|
||||
return (getNumSelectedRows() == 1);
|
||||
}
|
||||
|
||||
public boolean isInsertAllowed(DataType dataType) {
|
||||
int rowIndex = getMinIndexSelected();
|
||||
if (rowIndex == -1) {
|
||||
@ -343,9 +314,11 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
|
||||
private void doDelete(int componentOrdinal) {
|
||||
viewComposite.delete(componentOrdinal);
|
||||
if (componentOrdinal < row) {
|
||||
row--;
|
||||
viewDTM.withTransaction("Delete Component", () -> {
|
||||
viewComposite.delete(componentOrdinal);
|
||||
});
|
||||
if (componentOrdinal < currentEditRow) {
|
||||
currentEditRow--;
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,13 +341,13 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
int rowIndex = rows[i];
|
||||
int componentOrdinal = convertRowToOrdinal(rowIndex);
|
||||
if (componentOrdinal < row) {
|
||||
row--;
|
||||
if (componentOrdinal < currentEditRow) {
|
||||
currentEditRow--;
|
||||
}
|
||||
rowSet.add(componentOrdinal);
|
||||
}
|
||||
|
||||
viewComposite.delete(rowSet);
|
||||
viewDTM.withTransaction("Delete Components", () -> viewComposite.delete(rowSet));
|
||||
|
||||
// Not sure if this is the right behavior. Assuming the deleted rows were selected,
|
||||
// restore the selection to be the first row that was deleted so that the UI leaves the
|
||||
@ -427,14 +400,14 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
monitor.checkCancelled();
|
||||
int componentOrdinal = convertRowToOrdinal(rowIndex);
|
||||
ordinals.add(componentOrdinal);
|
||||
if (componentOrdinal < row) {
|
||||
row--;
|
||||
if (componentOrdinal < currentEditRow) {
|
||||
currentEditRow--;
|
||||
}
|
||||
selection.removeRange(componentOrdinal, componentOrdinal + 1);
|
||||
adjustSelection(componentOrdinal + 1, -1);
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
viewComposite.delete(ordinals);
|
||||
viewDTM.withTransaction("Delete Components", () -> viewComposite.delete(ordinals));
|
||||
fixSelection();
|
||||
componentEdited();
|
||||
notifyCompositeChanged();
|
||||
@ -612,14 +585,18 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
*/
|
||||
@Override
|
||||
public DataTypeComponent add(int rowIndex, DataType dt) throws UsrException {
|
||||
dt = viewDTM.resolve(dt, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
try {
|
||||
DataTypeInstance dti = getDropDataType(rowIndex, dt);
|
||||
return add(rowIndex, dti.getDataType(), dti.getLength());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return null;
|
||||
}
|
||||
String descr = rowIndex < getNumComponents() ? "Replace Component" : "Add Component";
|
||||
return viewDTM.withTransaction(descr, () -> {
|
||||
DataType resolvedDt = viewDTM.resolve(dt, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
try {
|
||||
DataTypeInstance dti = getDropDataType(rowIndex, resolvedDt);
|
||||
return add(rowIndex, dti.getDataType(), dti.getLength()); // add or replace
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -629,27 +606,27 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
*
|
||||
* @param rowIndex the index of the row where the data type should be added.
|
||||
* @param dt the data type to add
|
||||
*
|
||||
* @return true if the component is added, false if it doesn't.
|
||||
* @param dtLength datatype instance length
|
||||
* @return the component is added, null if it doesn't.
|
||||
* @throws UsrException if add fails
|
||||
*/
|
||||
@Override
|
||||
public DataTypeComponent add(int rowIndex, DataType dt, int dtLength) throws UsrException {
|
||||
DataTypeComponent dtc = null;
|
||||
if (rowIndex < getNumComponents()) {
|
||||
FieldRange range = getSelectedRangeContaining(rowIndex);
|
||||
if ((range == null) ||
|
||||
(range.getStart().getIndex().intValue() == range.getEnd().getIndex().intValue() -
|
||||
1)) {
|
||||
dtc = replace(rowIndex, dt, dtLength);
|
||||
}
|
||||
else {
|
||||
dtc = replaceComponentRange(range.getStart().getIndex().intValue(),
|
||||
dtc = viewDTM.withTransaction("Replace Component", () -> {
|
||||
FieldRange range = getSelectedRangeContaining(rowIndex);
|
||||
if ((range == null) || (range.getStart()
|
||||
.getIndex()
|
||||
.intValue() == range.getEnd().getIndex().intValue() - 1)) {
|
||||
return replace(rowIndex, dt, dtLength);
|
||||
}
|
||||
return replaceComponentRange(range.getStart().getIndex().intValue(),
|
||||
range.getEnd().getIndex().intValue() - 1, dt, dtLength);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
dtc = insert(rowIndex, dt, dtLength);
|
||||
dtc = viewDTM.withTransaction("Add Component", () -> insert(rowIndex, dt, dtLength));
|
||||
}
|
||||
return dtc;
|
||||
}
|
||||
@ -669,29 +646,28 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
* @param rowIndex the index of row where the data type should be replaced.
|
||||
* @param dt the new data type
|
||||
*
|
||||
* @return true if the component is added, false if it doesn't.
|
||||
* @throws UsrException if add fails
|
||||
* @return component added, null or exception if it does not
|
||||
* @throws UsrException if add error occurs
|
||||
*/
|
||||
public DataTypeComponent replace(int rowIndex, DataType dt) throws UsrException {
|
||||
DataTypeInstance dti =
|
||||
DataTypeHelper.getFixedLength(this, rowIndex, dt, usesAlignedLengthComponents());
|
||||
if (dti == null) {
|
||||
return null; // User cancelled from size dialog.
|
||||
}
|
||||
DataTypeComponent dtc = null;
|
||||
if (rowIndex < getNumComponents()) {
|
||||
FieldRange range = getSelectedRangeContaining(rowIndex);
|
||||
if ((range == null) ||
|
||||
(range.getStart().getIndex().intValue() == range.getEnd().getIndex().intValue() -
|
||||
1)) {
|
||||
dtc = replace(rowIndex, dti.getDataType(), dti.getLength());
|
||||
return viewDTM.withTransaction("Replace Component", () -> {
|
||||
DataTypeInstance dti =
|
||||
DataTypeHelper.getFixedLength(this, rowIndex, dt, usesAlignedLengthComponents());
|
||||
if (dti == null) {
|
||||
return null; // User cancelled from size dialog.
|
||||
}
|
||||
else {
|
||||
dtc = replaceComponentRange(range.getStart().getIndex().intValue(),
|
||||
if (rowIndex < getNumComponents()) {
|
||||
FieldRange range = getSelectedRangeContaining(rowIndex);
|
||||
if ((range == null) || (range.getStart()
|
||||
.getIndex()
|
||||
.intValue() == range.getEnd().getIndex().intValue() - 1)) {
|
||||
return replace(rowIndex, dti.getDataType(), dti.getLength());
|
||||
}
|
||||
return replaceComponentRange(range.getStart().getIndex().intValue(),
|
||||
range.getEnd().getIndex().intValue() - 1, dti.getDataType(), dti.getLength());
|
||||
}
|
||||
}
|
||||
return dtc;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -747,7 +723,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
// Get the current data type at the index.
|
||||
DataTypeComponent oldDtc = getComponent(rowIndex);
|
||||
if (oldDtc == null) {
|
||||
// TODO should this throw exception instead?
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -869,6 +844,8 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
|
||||
/**
|
||||
* Replaces the components of the original structure with those of the edited one.
|
||||
* Transaction must already be started on the {@link #getOriginalDataTypeManager()
|
||||
* original datatype manager}.
|
||||
*/
|
||||
protected abstract void replaceOriginalComponents();
|
||||
|
||||
@ -1006,9 +983,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
lastNumDuplicates = multiple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void clearComponents(int[] rows) throws UsrException;
|
||||
|
||||
@Override
|
||||
protected void createArray(int numElements) throws InvalidDataTypeException, UsrException {
|
||||
if (selection.getNumRanges() != 1) {
|
||||
@ -1027,15 +1001,16 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
DataType dt = comp.getDataType();
|
||||
|
||||
ArrayDataType array = new ArrayDataType(dt, numElements, comp.getLength(), viewDTM);
|
||||
|
||||
if (getNumSelectedComponentRows() > 1) {
|
||||
replaceComponentRange(rowIndex,
|
||||
selection.getFieldRange(0).getEnd().getIndex().intValue() - 1, array,
|
||||
array.getLength());
|
||||
}
|
||||
else {
|
||||
replace(rowIndex, array, array.getLength()); // Can throw UsrException.
|
||||
}
|
||||
viewDTM.withTransaction("Create Array", () -> {
|
||||
if (getNumSelectedComponentRows() > 1) {
|
||||
replaceComponentRange(rowIndex,
|
||||
selection.getFieldRange(0).getEnd().getIndex().intValue() - 1, array,
|
||||
array.getLength());
|
||||
}
|
||||
else {
|
||||
replace(rowIndex, array, array.getLength()); // Can throw UsrException.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1126,7 +1101,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
* @return the number of Undefined bytes consumed.
|
||||
*/
|
||||
protected int consumeByComponent(int rowIndex) {
|
||||
// TODO FIXME
|
||||
int numComps = viewComposite.getNumComponents();
|
||||
if (rowIndex >= 0 && rowIndex < numComps) {
|
||||
DataTypeComponent comp = viewComposite.getComponent(rowIndex);
|
||||
@ -1151,72 +1125,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
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.
|
||||
*/
|
||||
protected int consumeUndefinedBytes(int rowIndex, int numDesired)
|
||||
throws NoSuchElementException, InvalidDataTypeException {
|
||||
// TODO FIXME
|
||||
if (numDesired <= 0) {
|
||||
return 0;
|
||||
}
|
||||
int numRowComponents = getNumComponents();
|
||||
int numAvailable = getNumUndefinedBytesAt(rowIndex);
|
||||
int numIndicesRemoved = 0;
|
||||
if (numDesired > numAvailable) {
|
||||
throw new InvalidDataTypeException("Not enough undefined bytes."); // don't have enough undefined bytes there.
|
||||
}
|
||||
|
||||
int numBytesNeeded = numDesired;
|
||||
if (rowIndex >= numRowComponents) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
for (int i = rowIndex; i < numRowComponents; i++) {
|
||||
// Get the current data type at the index.
|
||||
DataTypeComponent comp = viewComposite.getComponent(rowIndex);
|
||||
DataType dt = comp.getDataType();
|
||||
int compLength = 0;
|
||||
// A single undefined byte.
|
||||
if (dt == DataType.DEFAULT) {
|
||||
compLength = comp.getLength();
|
||||
}
|
||||
else {
|
||||
throw new InvalidDataTypeException("Not enough undefined bytes."); // Ran into data type other than undefined byte.
|
||||
}
|
||||
if (compLength < numBytesNeeded) {
|
||||
// consume all of this undefined bytes data type.
|
||||
numBytesNeeded -= compLength;
|
||||
deleteComponent(rowIndex);
|
||||
numIndicesRemoved++;
|
||||
}
|
||||
else {
|
||||
// Determine number of bytes left over.
|
||||
int leftOverBytes = compLength - numBytesNeeded;
|
||||
deleteComponent(rowIndex);
|
||||
numIndicesRemoved++;
|
||||
if (leftOverBytes == 1) {
|
||||
insert(rowIndex, DataType.DEFAULT, 1, null, null);
|
||||
numIndicesRemoved--;
|
||||
}
|
||||
else if (leftOverBytes > 1) {
|
||||
DataType newDt = new ArrayDataType(DataType.DEFAULT, leftOverBytes, 1, viewDTM);
|
||||
insert(rowIndex, newDt, leftOverBytes, null, null);
|
||||
numIndicesRemoved--;
|
||||
}
|
||||
break; // We're done.
|
||||
}
|
||||
}
|
||||
return numIndicesRemoved;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
int numRows = 0;
|
||||
@ -1275,13 +1183,15 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
if (nameExistsElsewhere(name, rowIndex)) {
|
||||
throw new InvalidNameException("Name \"" + name + "\" already exists.");
|
||||
}
|
||||
try {
|
||||
getComponent(rowIndex).setFieldName(name); // setFieldName handles trimming
|
||||
return true;
|
||||
}
|
||||
catch (DuplicateNameException exc) {
|
||||
throw new InvalidNameException(exc.getMessage());
|
||||
}
|
||||
return viewDTM.withTransaction("Set Component Name", () -> {
|
||||
try {
|
||||
getComponent(rowIndex).setFieldName(name); // setFieldName handles trimming
|
||||
return true;
|
||||
}
|
||||
catch (DuplicateNameException exc) {
|
||||
throw new InvalidNameException(exc.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1297,7 +1207,9 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
return false;
|
||||
}
|
||||
|
||||
getComponent(rowIndex).setComment(newComment);
|
||||
viewDTM.withTransaction("Set Component Comment",
|
||||
() -> getComponent(rowIndex).setComment(comment));
|
||||
|
||||
fireTableCellUpdated(rowIndex, getCommentColumn());
|
||||
componentDataChanged();
|
||||
return true;
|
||||
@ -1324,17 +1236,237 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
(selection.getFieldRange(0).getEnd().getIndex().intValue() < getNumComponents()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
|
||||
if (originalDTM == null) {
|
||||
// editor unloaded
|
||||
return;
|
||||
}
|
||||
|
||||
if (!originalCompositeExists()) {
|
||||
|
||||
if (originalCompositeId != DataTypeManager.NULL_DATATYPE_ID && !hasChanges) {
|
||||
provider.dispose(); // Close editor
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Removed types will remain if used directly by edited components.
|
||||
if (viewDTM.refreshDBTypesFromOriginal()) {
|
||||
setStatus("Dependency datatypes have changed or been removed");
|
||||
}
|
||||
|
||||
if (originalCompositeId != DataTypeManager.NULL_DATATYPE_ID) {
|
||||
provider.show();
|
||||
// The user has modified the structure so prompt for whether or
|
||||
// not to close the structure.
|
||||
String question = "The " + getOriginType() + " \"" + originalDTM.getName() +
|
||||
"\" has changed and \n" + "\"" + currentName +
|
||||
"\" no longer exists outside the editor.\n" + "Discard edits and close the " +
|
||||
getTypeName() + " editor?";
|
||||
String title = "Close " + getTypeName() + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(
|
||||
provider.getComponent(), title, question);
|
||||
if (response == OptionDialog.YES_OPTION) {
|
||||
provider.dispose(); // Close editor
|
||||
return;
|
||||
}
|
||||
|
||||
reloadFromView();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
Composite composite = getOriginalComposite();
|
||||
boolean reload = true;
|
||||
if (hasChanges || !viewComposite.isEquivalent(composite)) {
|
||||
hasChanges = true;
|
||||
provider.show();
|
||||
// The user has modified the structure so prompt for whether or
|
||||
// not to reload the structure.
|
||||
String question = "The " + getOriginType() + " \"" + originalDTM.getName() +
|
||||
"\" has been restored.\n" + "\"" + currentName +
|
||||
"\" may have changed outside the editor.\n" + "Discard edits and reload the " +
|
||||
getTypeName() + "?";
|
||||
String title = "Reload " + getTypeName() + " Editor?";
|
||||
int response = OptionDialog
|
||||
.showYesNoDialogWithNoAsDefaultButton(provider.getComponent(), title, question);
|
||||
if (response != OptionDialog.YES_OPTION) {
|
||||
reload = false;
|
||||
}
|
||||
}
|
||||
if (reload) {
|
||||
load(composite); // reload the structure
|
||||
setStatus("Editor reloaded");
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewDTM.refreshDBTypesFromOriginal()) {
|
||||
setStatus("Dependency datatypes have changed or been removed");
|
||||
}
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Override CompositeViewerModel CategoryChangeListener methods
|
||||
//==================================================================================================
|
||||
|
||||
@Override
|
||||
public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) {
|
||||
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
DataType dataType = viewDTM.getDataType(path.getCategoryPath(), path.getDataTypeName());
|
||||
if (dataType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!path.equals(originalDataTypePath)) {
|
||||
DataType dt = viewDTM.getDataType(path);
|
||||
if (dt != null) {
|
||||
if (hasSubDt(viewComposite, path)) {
|
||||
String msg = "Removed sub-component data type \"" + path;
|
||||
setStatus(msg, true);
|
||||
}
|
||||
viewDTM.withTransaction("Removed Dependency", () -> {
|
||||
viewDTM.clearUndoOnChange();
|
||||
viewDTM.remove(dt, TaskMonitor.DUMMY);
|
||||
});
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (originalCompositeId == DataTypeManager.NULL_DATATYPE_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
consideringReplacedDataType = true;
|
||||
try {
|
||||
provider.show();
|
||||
// The user has modified the structure so prompt for whether or
|
||||
// not to close the structure.
|
||||
String question =
|
||||
"The " + getOriginType() + " \"" + originalDTM.getName() + "\" has changed and \n" +
|
||||
"\"" + getCompositeName() + "\" no longer exists outside the editor.\n" +
|
||||
"Discard edits and close the " + getTypeName() + " editor?";
|
||||
String title = "Close " + getTypeName() + " Editor?";
|
||||
int response = OptionDialog
|
||||
.showYesNoDialogWithNoAsDefaultButton(provider.getComponent(), title, question);
|
||||
if (response == OptionDialog.YES_OPTION) {
|
||||
provider.closeComponent(true); // Close editor
|
||||
return;
|
||||
}
|
||||
|
||||
reloadFromView();
|
||||
}
|
||||
finally {
|
||||
consideringReplacedDataType = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
|
||||
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldPath.getDataTypeName().equals(newPath.getDataTypeName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
String newName = newPath.getDataTypeName();
|
||||
String oldName = oldPath.getDataTypeName();
|
||||
|
||||
// Does the old name match our original name.
|
||||
// Check originalCompositeId to ensure original type is managed
|
||||
if (originalCompositeId != DataTypeManager.NULL_DATATYPE_ID &&
|
||||
oldPath.equals(originalDataTypePath)) {
|
||||
originalDataTypePath = newPath;
|
||||
try {
|
||||
if (viewComposite.getName().equals(oldName)) {
|
||||
setName(newName);
|
||||
}
|
||||
}
|
||||
catch (InvalidNameException | DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for managed datatype changing
|
||||
DataType dt = viewDTM.getDataType(oldPath);
|
||||
if (dt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewDTM.withTransaction("Renamed Dependency", () -> {
|
||||
viewDTM.clearUndoOnChange();
|
||||
try {
|
||||
dt.setName(newPath.getDataTypeName());
|
||||
}
|
||||
catch (InvalidNameException | DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
|
||||
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
DataType dt = viewDTM.getDataType(oldPath);
|
||||
if (dt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
viewDTM.withTransaction("Moved " + oldPath, () -> {
|
||||
viewDTM.clearUndoOnChange();
|
||||
Category newDtCat = viewDTM.createCategory(newPath.getCategoryPath());
|
||||
newDtCat.moveDataType(dt, null);
|
||||
});
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
throw new AssertException(e);
|
||||
}
|
||||
|
||||
if (originalDataTypePath.getDataTypeName().equals(newPath.getDataTypeName()) &&
|
||||
originalDataTypePath.getCategoryPath().equals(oldPath.getCategoryPath())) {
|
||||
originalDataTypePath = newPath;
|
||||
compositeInfoChanged();
|
||||
}
|
||||
else {
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) {
|
||||
try {
|
||||
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (originalDTM == null) {
|
||||
// editor unloaded
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1348,14 +1480,9 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't currently have any modifications that need applying and
|
||||
// the structure in the editor just changed, then show the changed
|
||||
// structure.
|
||||
String oldName = path.getDataTypeName();
|
||||
if (path.equals(originalDataTypePath)) {
|
||||
if (consideringReplacedDataType) {
|
||||
return;
|
||||
@ -1369,10 +1496,12 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
originalIsChanging = true;
|
||||
try {
|
||||
if (hadChanges) {
|
||||
String message = "<html>" + HTMLUtilities.escapeHTML(oldName) +
|
||||
" has changed outside the editor.<br>" + "Discard edits & reload the " +
|
||||
getTypeName() + "?";
|
||||
if (hasChanges) {
|
||||
provider.show();
|
||||
String message = "<html>" +
|
||||
HTMLUtilities.escapeHTML(originalDataTypePath.getDataTypeName()) +
|
||||
" has changed outside the editor.<br>" +
|
||||
"Discard edits and reload the " + getTypeName() + "?";
|
||||
String title = "Reload " + getTypeName() + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(
|
||||
provider.getComponent(), title, message);
|
||||
@ -1395,28 +1524,32 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
}
|
||||
else {
|
||||
DataType viewDt = viewDTM.getDataType(path);
|
||||
// NOTE: There is the risk of a cascade of change notifications resulting in multiple
|
||||
// undo transactions for the viewDTM. An editor save could generate quite a few with
|
||||
// potentially many types getting changed by one change.
|
||||
DataType changedDt = originalDTM.getDataType(path);
|
||||
if (!(changedDt instanceof DatabaseObject)) {
|
||||
// NOTE: viewDTM only maps view-to-original IDs for DataTypeDB
|
||||
return;
|
||||
}
|
||||
long originalId = originalDTM.getID(changedDt);
|
||||
DataType viewDt = viewDTM.findMyDataTypeFromOriginalID(originalId);
|
||||
if (viewDt == null) {
|
||||
return;
|
||||
}
|
||||
int origDtLen = viewDt.getLength();
|
||||
DataType changedDt = dtm.getDataType(path);
|
||||
if (changedDt != null) {
|
||||
if ((viewDt instanceof Composite) && (changedDt instanceof Composite)) {
|
||||
Composite comp = (Composite) changedDt;
|
||||
Composite origDt = getOriginalComposite();
|
||||
if ((origDt != null) && comp.isPartOf(origDt)) {
|
||||
removeDtFromComponents(comp);
|
||||
}
|
||||
|
||||
((Composite) viewDt)
|
||||
.setDescription(((Composite) changedDt).getDescription());
|
||||
}
|
||||
viewDt = viewDTM.resolve(changedDt, DataTypeConflictHandler.REPLACE_HANDLER);
|
||||
if (origDtLen != viewDt.getLength()) {
|
||||
viewComposite.dataTypeSizeChanged(viewDt);
|
||||
}
|
||||
try {
|
||||
viewDTM.withTransaction("Changed " + path, () -> {
|
||||
viewDTM.clearUndoOnChange();
|
||||
viewDTM.replaceDataType(viewDt, changedDt, true);
|
||||
});
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
throw new AssertException(e);
|
||||
}
|
||||
|
||||
// Clear undo/redo stack to avoid inconsistency with originalDTM
|
||||
viewDTM.clearUndo();
|
||||
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
@ -1426,13 +1559,10 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
private volatile boolean consideringReplacedDataType = false;
|
||||
|
||||
@Override
|
||||
public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath,
|
||||
DataType newDataType) {
|
||||
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
@ -1441,64 +1571,64 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
return;
|
||||
}
|
||||
|
||||
String dtName = oldPath.getDataTypeName();
|
||||
DataTypePath dtPath = new DataTypePath(newDataType.getCategoryPath(), dtName);
|
||||
if (!dtPath.equals(originalDataTypePath)) {
|
||||
DataType dt = viewDTM.getDataType(dtPath);
|
||||
if (!oldPath.equals(originalDataTypePath)) {
|
||||
// Check for type which may be referenced by viewComposite
|
||||
DataType dt = viewDTM.getDataType(oldPath);
|
||||
if (dt != null) {
|
||||
if (hasSubDt(viewComposite, dtPath)) {
|
||||
String msg = "Replaced data type \"" + dtPath +
|
||||
if (hasSubDt(viewComposite, oldPath)) {
|
||||
String msg = "Replaced data type \"" + oldPath +
|
||||
"\", which is a sub-component of \"" + getOriginalDataTypeName() + "\".";
|
||||
setStatus(msg, true);
|
||||
}
|
||||
// NOTE: depending upon event sequence and handling a
|
||||
// re-load may have occurred and replacement may be unnecessary
|
||||
try {
|
||||
viewDTM.replaceDataType(dt, newDataType, true);
|
||||
viewDTM.withTransaction("Replaced Dependency", () -> {
|
||||
viewDTM.clearUndoOnChange();
|
||||
viewDTM.replaceDataType(dt, newDataType, true);
|
||||
});
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
throw new AssertException(e);
|
||||
}
|
||||
|
||||
// Clear undo/redo stack to avoid inconsistency with originalDTM
|
||||
viewDTM.clearUndo();
|
||||
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (this.hadChanges) {
|
||||
if (originalDataTypePath.equals(oldPath)) {
|
||||
if (hadChanges) {
|
||||
consideringReplacedDataType = true;
|
||||
try {
|
||||
String message =
|
||||
"<html>" + HTMLUtilities.escapeHTML(oldPath.getPath()) +
|
||||
" has changed outside the editor.<br>" +
|
||||
"Discard edits & reload the " + getTypeName() + "?";
|
||||
String title = "Reload " + getTypeName() + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(
|
||||
provider.getComponent(), title, message);
|
||||
if (response == OptionDialog.OPTION_ONE) {
|
||||
load(getOriginalComposite());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
consideringReplacedDataType = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
load(getOriginalComposite());
|
||||
setStatus(viewComposite.getPathName() + " changed outside the editor.",
|
||||
false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String msg = "\"" + oldPath.getPath() + "\" was replaced with " +
|
||||
newDataType.getPathName() + " in the data type manager.";
|
||||
setStatus(msg, true);
|
||||
|
||||
consideringReplacedDataType = true;
|
||||
try {
|
||||
provider.show();
|
||||
|
||||
if (hasChanges) {
|
||||
String message = "<html>" + HTMLUtilities.escapeHTML(oldPath.getPath()) +
|
||||
" has been replaced outside the editor.<br>" +
|
||||
"Discard edits and close?</html>";
|
||||
String title = "Close " + getTypeName() + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(
|
||||
provider.getComponent(), title, message);
|
||||
if (response != OptionDialog.OPTION_ONE) {
|
||||
compositeInfoChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
load((Composite) newDataType);
|
||||
String message = "<html>" + HTMLUtilities.escapeHTML(oldPath.getPath()) +
|
||||
" has been replaced outside the editor.</html>";
|
||||
Msg.showWarn(this, provider.getComponent(), "Closing " + getTypeName() + " Editor",
|
||||
message);
|
||||
}
|
||||
|
||||
// fast close, discard any changes
|
||||
provider.closeComponent(true);
|
||||
}
|
||||
finally {
|
||||
consideringReplacedDataType = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1635,20 +1765,20 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
Composite oldComposite = getOriginalComposite();
|
||||
if (oldComposite == null) {
|
||||
hadChanges = false;
|
||||
return hadChanges;
|
||||
hasChanges = false;
|
||||
return hasChanges;
|
||||
}
|
||||
|
||||
PackingType packingType = getPackingType();
|
||||
AlignmentType alignmentType = getAlignmentType();
|
||||
|
||||
hadChanges = (packingType != oldComposite.getPackingType()) ||
|
||||
hasChanges = (packingType != oldComposite.getPackingType()) ||
|
||||
(alignmentType != oldComposite.getAlignmentType()) ||
|
||||
(packingType == PackingType.EXPLICIT &&
|
||||
getExplicitPackingValue() != oldComposite.getExplicitPackingValue()) ||
|
||||
(alignmentType == AlignmentType.EXPLICIT &&
|
||||
getExplicitMinimumAlignment() != oldComposite.getExplicitMinimumAlignment());
|
||||
return hadChanges;
|
||||
return hasChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1664,26 +1794,28 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
|
||||
public void setAlignmentType(AlignmentType alignmentType, int explicitValue) {
|
||||
AlignmentType currentAlignType = getAlignmentType();
|
||||
if (alignmentType == AlignmentType.DEFAULT) {
|
||||
if (currentAlignType == AlignmentType.DEFAULT) {
|
||||
return;
|
||||
viewDTM.withTransaction("Set Alignment", () -> {
|
||||
AlignmentType currentAlignType = getAlignmentType();
|
||||
if (alignmentType == AlignmentType.DEFAULT) {
|
||||
if (currentAlignType == AlignmentType.DEFAULT) {
|
||||
return;
|
||||
}
|
||||
viewComposite.setToDefaultAligned();
|
||||
}
|
||||
viewComposite.setToDefaultAligned();
|
||||
}
|
||||
else if (alignmentType == AlignmentType.MACHINE) {
|
||||
if (currentAlignType == AlignmentType.MACHINE) {
|
||||
return;
|
||||
else if (alignmentType == AlignmentType.MACHINE) {
|
||||
if (currentAlignType == AlignmentType.MACHINE) {
|
||||
return;
|
||||
}
|
||||
viewComposite.setToMachineAligned();
|
||||
}
|
||||
viewComposite.setToMachineAligned();
|
||||
}
|
||||
else {
|
||||
if (currentAlignType == AlignmentType.EXPLICIT &&
|
||||
explicitValue == viewComposite.getExplicitMinimumAlignment()) {
|
||||
return;
|
||||
else {
|
||||
if (currentAlignType == AlignmentType.EXPLICIT &&
|
||||
explicitValue == viewComposite.getExplicitMinimumAlignment()) {
|
||||
return;
|
||||
}
|
||||
viewComposite.setExplicitMinimumAlignment(explicitValue);
|
||||
}
|
||||
viewComposite.setExplicitMinimumAlignment(explicitValue);
|
||||
}
|
||||
});
|
||||
if (fixSelection()) {
|
||||
selectionChanged();
|
||||
}
|
||||
@ -1703,26 +1835,28 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
|
||||
public void setPackingType(PackingType packingType, int explicitValue) {
|
||||
PackingType currentPacktype = getPackingType();
|
||||
if (packingType == PackingType.DISABLED) {
|
||||
if (currentPacktype == PackingType.DISABLED) {
|
||||
return;
|
||||
viewDTM.withTransaction("Set Packing", () -> {
|
||||
PackingType currentPacktype = getPackingType();
|
||||
if (packingType == PackingType.DISABLED) {
|
||||
if (currentPacktype == PackingType.DISABLED) {
|
||||
return;
|
||||
}
|
||||
viewComposite.setPackingEnabled(false);
|
||||
}
|
||||
viewComposite.setPackingEnabled(false);
|
||||
}
|
||||
else if (packingType == PackingType.DEFAULT) {
|
||||
if (currentPacktype == PackingType.DEFAULT) {
|
||||
return;
|
||||
else if (packingType == PackingType.DEFAULT) {
|
||||
if (currentPacktype == PackingType.DEFAULT) {
|
||||
return;
|
||||
}
|
||||
viewComposite.setToDefaultPacking();
|
||||
}
|
||||
viewComposite.setToDefaultPacking();
|
||||
}
|
||||
else {
|
||||
if (currentPacktype == PackingType.EXPLICIT &&
|
||||
explicitValue == viewComposite.getExplicitPackingValue()) {
|
||||
return;
|
||||
else {
|
||||
if (currentPacktype == PackingType.EXPLICIT &&
|
||||
explicitValue == viewComposite.getExplicitPackingValue()) {
|
||||
return;
|
||||
}
|
||||
viewComposite.setExplicitPackingValue(explicitValue);
|
||||
}
|
||||
viewComposite.setExplicitPackingValue(explicitValue);
|
||||
}
|
||||
});
|
||||
if (fixSelection()) {
|
||||
selectionChanged();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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.
|
||||
@ -24,9 +24,9 @@ import javax.swing.table.TableColumn;
|
||||
|
||||
import docking.widgets.fieldpanel.support.FieldRange;
|
||||
import docking.widgets.fieldpanel.support.FieldSelection;
|
||||
import ghidra.program.database.data.DataTypeUtilities;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utility.function.Callback;
|
||||
@ -43,9 +43,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
protected Composite originalComposite;
|
||||
protected DataTypePath originalDataTypePath;
|
||||
protected long originalCompositeId;
|
||||
protected DataTypeManager originalDTM;
|
||||
|
||||
protected Composite viewComposite;
|
||||
protected DataTypeManager viewDTM;
|
||||
protected CompositeViewerDataTypeManager viewDTM;
|
||||
|
||||
private List<CompositeViewerModelListener> modelListeners = new ArrayList<>();
|
||||
|
||||
@ -75,9 +76,9 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
/** Width of left margin in pixels for the component area. */
|
||||
protected int leftMargin = 10;
|
||||
/** the current row for a field edit */
|
||||
protected int row = -1;
|
||||
protected int currentEditRow = -1;
|
||||
/** the current column for a field edit */
|
||||
protected int column = -1;
|
||||
protected int currentEditColumn = -1;
|
||||
protected CompositeEditorProvider provider;
|
||||
protected boolean showHexNumbers = false;
|
||||
|
||||
@ -134,7 +135,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
/**
|
||||
* Terminates listening for category change events within the model.
|
||||
*/
|
||||
void dispose() {
|
||||
protected void dispose() {
|
||||
// Unregister the listeners.
|
||||
// No longer want to listen for changes to previous category.
|
||||
unload();
|
||||
@ -164,7 +165,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
*
|
||||
* @param dataType the composite date type to be viewed.
|
||||
*/
|
||||
abstract void load(Composite dataType);
|
||||
protected abstract void load(Composite dataType);
|
||||
|
||||
/**
|
||||
* Unloads the currently loaded composite data type.
|
||||
@ -174,14 +175,13 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* a new composite data type.
|
||||
*/
|
||||
void unload() {
|
||||
DataTypeManager originalDTM =
|
||||
(originalComposite != null) ? originalComposite.getDataTypeManager() : null;
|
||||
// Unregister the listeners.
|
||||
// No longer want to listen for changes to previous category.
|
||||
if (originalDTM != null) {
|
||||
originalDTM.removeDataTypeManagerListener(this);
|
||||
originalDTM = null;
|
||||
}
|
||||
originalDTM = null;
|
||||
originalComposite = null;
|
||||
originalCompositeId = DataTypeManager.NULL_DATATYPE_ID;
|
||||
viewComposite = null;
|
||||
@ -192,16 +192,34 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the data type against the indicated data type manager using the specified
|
||||
* conflictHandler. In general, a transaction should have already been initiated prior to
|
||||
* calling this method so that the true nature of the transaction may be established for
|
||||
* use with undo/redo (e.g., Set Datatype).
|
||||
*
|
||||
* @param dataType the data type to be resolved
|
||||
* @param resolveDtm the data type manager to resolve the data type against
|
||||
* @param conflictHandler the handler to be used for any conflicts encountered while resolving
|
||||
* @return the resolved data type
|
||||
*/
|
||||
protected final DataType resolveDataType(DataType dataType, DataTypeManager resolveDtm,
|
||||
DataTypeConflictHandler conflictHandler) {
|
||||
if (resolveDtm == null || dataType == DataType.DEFAULT) {
|
||||
return DataType.DEFAULT;
|
||||
}
|
||||
return resolveDtm.withTransaction("Resolve " + dataType.getPathName(), () -> {
|
||||
return resolveDtm.resolve(dataType, conflictHandler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the indicated data type against the working copy in the viewer's data type manager.
|
||||
* @param dataType the data type
|
||||
* @return the working copy of the data type.
|
||||
*/
|
||||
DataType resolve(DataType dataType) {
|
||||
if (viewDTM == null) {
|
||||
return DataType.DEFAULT;
|
||||
}
|
||||
return viewDTM.resolve(dataType, null);
|
||||
public DataType resolve(DataType dataType) {
|
||||
return resolveDataType(dataType, viewDTM, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,7 +227,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* @return the current row
|
||||
*/
|
||||
public int getRow() {
|
||||
return row;
|
||||
return currentEditRow;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -217,7 +235,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* @param row the new row
|
||||
*/
|
||||
public void setRow(int row) {
|
||||
this.row = row;
|
||||
this.currentEditRow = row;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,7 +243,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* @return the current column
|
||||
*/
|
||||
public int getColumn() {
|
||||
return column;
|
||||
return currentEditColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,7 +257,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
return;
|
||||
}
|
||||
|
||||
this.column = column;
|
||||
this.currentEditColumn = column;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,23 +266,43 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* @param column the new column
|
||||
*/
|
||||
protected void setLocation(int row, int column) {
|
||||
this.row = row;
|
||||
this.column = column;
|
||||
this.currentEditRow = row;
|
||||
this.currentEditColumn = column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original CompositeDataType that was used to construct this.
|
||||
* @return the original composite being viewed or null if it doesn't exist.
|
||||
* Returns the original Composite DataType that currently exists within the original
|
||||
* DataTypeManager, or if not found the instance originally loaded.
|
||||
* @return the original composite being viewed or null if nothing is currently loaded in
|
||||
* the model.
|
||||
*/
|
||||
protected Composite getOriginalComposite() {
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (originalDataTypePath != null && originalDTM != null) {
|
||||
DataType dt = originalDTM.getDataType(originalDataTypePath);
|
||||
if (dt instanceof Composite) {
|
||||
Composite existingOriginal = getExistingOriginalComposite();
|
||||
return existingOriginal != null ? existingOriginal : originalComposite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the original composite exists within the original datatype manager.
|
||||
* NOTE: If this method returns true, the current datatype which exists within the original
|
||||
* datatype manager will be returned by {@link #getOriginalComposite()}, although its name
|
||||
* may differ.
|
||||
* @return true if datatype found else false
|
||||
*/
|
||||
protected boolean originalCompositeExists() {
|
||||
return getExistingOriginalComposite() != null;
|
||||
}
|
||||
|
||||
private Composite getExistingOriginalComposite() {
|
||||
long originalId = getCompositeID();
|
||||
if (originalId != DataTypeManager.NULL_DATATYPE_ID && originalDataTypePath != null &&
|
||||
originalDTM != null) {
|
||||
DataType dt = originalDTM.getDataType(originalId);
|
||||
if (dt instanceof Composite &&
|
||||
DataTypeUtilities.isSameKindDataType(originalComposite, dt)) {
|
||||
return (Composite) dt;
|
||||
}
|
||||
}
|
||||
return originalComposite;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,7 +318,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* @return the manager
|
||||
*/
|
||||
protected DataTypeManager getOriginalDataTypeManager() {
|
||||
return (originalComposite != null) ? originalComposite.getDataTypeManager() : null;
|
||||
return originalDTM;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -288,7 +326,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* @return the category
|
||||
*/
|
||||
public final Category getOriginalCategory() {
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (originalDataTypePath != null && originalDTM != null) {
|
||||
CategoryPath originalCategoryPath = originalDataTypePath.getCategoryPath();
|
||||
if (originalDTM.containsCategory(originalCategoryPath)) {
|
||||
@ -346,7 +383,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* Return the data type name of the structure being viewed
|
||||
* @return the name
|
||||
*/
|
||||
public String getCompositeName() {
|
||||
protected String getCompositeName() {
|
||||
return (viewComposite != null) ? viewComposite.getDisplayName() : "";
|
||||
}
|
||||
|
||||
@ -587,12 +624,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current dataType name (Structure or Union) as a string.
|
||||
* @return the name
|
||||
* Returns the current dataType name (Structure, Union, etc.) as a string.
|
||||
* @return the type of composite being edited
|
||||
*/
|
||||
protected String getTypeName() {
|
||||
return "Composite Data Type";
|
||||
}
|
||||
public abstract String getTypeName();
|
||||
|
||||
/**
|
||||
* Returns the current status string.
|
||||
@ -635,26 +670,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
setStatus(status, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up the original name and category because a program restoration may have changed the
|
||||
* original composite.
|
||||
* @param composite the restored copy of our original composite
|
||||
*/
|
||||
protected void fixupOriginalPath(Composite composite) {
|
||||
String newName = composite.getName();
|
||||
CategoryPath newCatPath = composite.getCategoryPath();
|
||||
CategoryPath oldCatPath = viewComposite.getCategoryPath();
|
||||
DataTypePath newDtPath = new DataTypePath(newCatPath, composite.getName());
|
||||
DataTypePath oldDtPath = new DataTypePath(oldCatPath, viewComposite.getName());
|
||||
|
||||
if (!oldCatPath.equals(newCatPath)) {
|
||||
dataTypeMoved(viewDTM, oldDtPath, newDtPath);
|
||||
}
|
||||
if (!originalDataTypePath.getDataTypeName().equals(newName)) {
|
||||
dataTypeRenamed(viewDTM, oldDtPath, newDtPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a CompositeViewerModelListener to be notified when model changes occur
|
||||
* @param listener the listener
|
||||
@ -713,7 +728,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
|
||||
@Override
|
||||
public void categoryRemoved(DataTypeManager dtm, CategoryPath path) {
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
@ -739,7 +753,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
|
||||
@Override
|
||||
public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) {
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
@ -747,24 +760,27 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
return;
|
||||
}
|
||||
Category oldCat = viewDTM.getCategory(oldPath);
|
||||
try {
|
||||
oldCat.setName(newPath.getName());
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
if (originalDataTypePath.isAncestor(oldPath)) {
|
||||
changeOriginalDataTypeCategory(oldPath, newPath);
|
||||
}
|
||||
viewDTM.withTransaction("Category Renamed", () -> {
|
||||
viewDTM.clearUndoOnChange();
|
||||
try {
|
||||
oldCat.setName(newPath.getName());
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
if (originalDataTypePath.isAncestor(oldPath)) {
|
||||
changeOriginalDataTypeCategory(oldPath, newPath);
|
||||
}
|
||||
});
|
||||
|
||||
compositeInfoChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) {
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
@ -776,14 +792,17 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
return;
|
||||
}
|
||||
CategoryPath parent = newPath.getParent();
|
||||
viewDTM.createCategory(parent);
|
||||
Category newCat = viewDTM.getCategory(parent);
|
||||
try {
|
||||
newCat.moveCategory(oldCat, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
viewDTM.withTransaction("Category Moved", () -> {
|
||||
viewDTM.clearUndoOnChange();
|
||||
viewDTM.createCategory(parent);
|
||||
Category newCat = viewDTM.getCategory(parent);
|
||||
try {
|
||||
newCat.moveCategory(oldCat, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
if (originalDataTypePath.isAncestor(oldPath)) {
|
||||
changeOriginalDataTypeCategory(oldPath, newPath);
|
||||
}
|
||||
@ -795,165 +814,6 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
// Adding a new data type doesn't affect this one?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) {
|
||||
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
DataType dataType = viewDTM.getDataType(path.getCategoryPath(), path.getDataTypeName());
|
||||
if (dataType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataType baseDt = DataTypeHelper.getBaseType(dataType);
|
||||
DataTypePath dtPath = new DataTypePath(path.getCategoryPath(), baseDt.getName());
|
||||
if (!dtPath.equals(originalDataTypePath)) {
|
||||
DataType dt = viewDTM.getDataType(dtPath);
|
||||
if (dt != null) {
|
||||
if (hasSubDt(viewComposite, dtPath)) {
|
||||
String msg = "Removed sub-component data type \"" + dtPath;
|
||||
setStatus(msg, true);
|
||||
}
|
||||
viewDTM.remove(dt, TaskMonitor.DUMMY);
|
||||
// If a datatype we are using is removed, change it to undefined data types.
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!dataType.equals(baseDt)) {
|
||||
return; // ignore typedefs, arrays, and pointers of the Datatype being edited.
|
||||
}
|
||||
String msg = "\"" + dtPath + "\" was removed from the data type manager.";
|
||||
setStatus(msg, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
|
||||
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
DataType dt = viewDTM.getDataType(oldPath);
|
||||
if (dt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
dt.setName(newPath.getDataTypeName());
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
catch (InvalidNameException | DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
|
||||
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
DataType dt = viewDTM.getDataType(oldPath);
|
||||
if (dt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Category newDtCat = viewDTM.createCategory(newPath.getCategoryPath());
|
||||
try {
|
||||
newDtCat.moveDataType(dt, null);
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
throw new AssertException(e);
|
||||
}
|
||||
|
||||
if (originalDataTypePath.getDataTypeName().equals(newPath.getDataTypeName()) &&
|
||||
originalDataTypePath.getCategoryPath().equals(oldPath.getCategoryPath())) {
|
||||
originalDataTypePath = newPath;
|
||||
compositeInfoChanged();
|
||||
}
|
||||
else {
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) {
|
||||
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
if (isLoaded()) {
|
||||
if (originalCompositeId != DataTypeManager.NULL_DATATYPE_ID &&
|
||||
path.equals(originalDataTypePath)) {
|
||||
compositeInfoChanged();
|
||||
}
|
||||
else {
|
||||
CategoryPath cat = path.getCategoryPath();
|
||||
viewDTM.createCategory(cat);
|
||||
DataType dt = viewDTM.getDataType(path);
|
||||
if (dt == null) {
|
||||
return;
|
||||
}
|
||||
if (originalDTM != viewDTM) {
|
||||
// update changed datatype
|
||||
DataType dataType = dtm.getDataType(path);
|
||||
viewDTM.resolve(dataType, DataTypeConflictHandler.REPLACE_HANDLER);
|
||||
}
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath,
|
||||
DataType newDataType) {
|
||||
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
if (dtm != originalDTM) {
|
||||
return; // Different DTM than the one for this data type.
|
||||
}
|
||||
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!oldPath.equals(originalDataTypePath)) { // am I editing the replaced dataType?
|
||||
DataType dt = viewDTM.getDataType(oldPath);
|
||||
if (dt != null) {
|
||||
if (hasSubDt(viewComposite, oldPath)) {
|
||||
String msg = "Replaced sub-component data type \"" + oldPath.getPath();
|
||||
setStatus(msg, true);
|
||||
}
|
||||
try {
|
||||
|
||||
viewDTM.replaceDataType(dt, newDataType, true);
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
throw new AssertException(e);
|
||||
}
|
||||
fireTableDataChanged();
|
||||
componentDataChanged();
|
||||
}
|
||||
}
|
||||
else {
|
||||
load((Composite) newDataType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) {
|
||||
// Don't care.
|
||||
@ -965,9 +825,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
provider.dataTypeManagerRestored();
|
||||
}
|
||||
public abstract void restored(DataTypeManager dataTypeManager);
|
||||
|
||||
//=================================================================================================
|
||||
// Helper methods for CategoryChangeListener methods.
|
||||
@ -1112,7 +970,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* Returns true if the selection is a single row.
|
||||
* @return true if the selection is a single row
|
||||
*/
|
||||
public boolean isSingleRowSelection() {
|
||||
protected boolean isSingleRowSelection() {
|
||||
if (selection.getNumRanges() != 1) {
|
||||
return false;
|
||||
}
|
||||
@ -1124,7 +982,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* Returns true if the list selection is contiguous and only contains component rows.
|
||||
* @return true if the list selection is contiguous and only contains component rows
|
||||
*/
|
||||
public boolean isContiguousComponentSelection() {
|
||||
protected boolean isContiguousComponentSelection() {
|
||||
return ((selection.getNumRanges() == 1) &&
|
||||
selection.getFieldRange(0).getStart().getIndex().intValue() < getNumComponents());
|
||||
}
|
||||
@ -1133,7 +991,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* Get an array of the indices for all the selected rows.
|
||||
* @return the selected rows
|
||||
*/
|
||||
public int[] getSelectedRows() {
|
||||
protected int[] getSelectedRows() {
|
||||
ArrayList<Integer> list = new ArrayList<>();
|
||||
for (FieldRange range : this.selection) {
|
||||
int endIndex = range.getEnd().getIndex().intValue();
|
||||
@ -1149,7 +1007,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* Get an array of the row indices for all the selected components.
|
||||
* @return the selected rows
|
||||
*/
|
||||
public int[] getSelectedComponentRows() {
|
||||
protected int[] getSelectedComponentRows() {
|
||||
ArrayList<Integer> list = new ArrayList<>();
|
||||
int numComponents = getNumComponents();
|
||||
for (FieldRange range : this.selection) {
|
||||
@ -1169,7 +1027,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* @param rowIndex the row index
|
||||
* @return the range or null
|
||||
*/
|
||||
public FieldRange getSelectedRangeContaining(int rowIndex) {
|
||||
protected FieldRange getSelectedRangeContaining(int rowIndex) {
|
||||
FieldRange fieldRange = null;
|
||||
if (selection.containsEntirely(BigInteger.valueOf(rowIndex))) {
|
||||
// Get the size of the selection range we are in.
|
||||
@ -1203,7 +1061,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
*
|
||||
* @param rows the indices for the selected rows.
|
||||
*/
|
||||
public void setSelection(int[] rows) {
|
||||
protected void setSelection(int[] rows) {
|
||||
|
||||
if (updatingSelection) {
|
||||
return;
|
||||
@ -1229,7 +1087,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
* it gets adjusted to the empty last line when in unlocked mode.
|
||||
* @param selection the new selection
|
||||
*/
|
||||
public void setSelection(FieldSelection selection) {
|
||||
protected void setSelection(FieldSelection selection) {
|
||||
if (updatingSelection) {
|
||||
return;
|
||||
}
|
||||
@ -1292,7 +1150,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to run the given task on the swing thread now if swing or later if not.
|
||||
* Convenience method to run the given task on the swing thread.
|
||||
* @param r the runnable
|
||||
*/
|
||||
protected void swing(Runnable r) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -62,6 +62,11 @@ class StructureEditorModel extends CompEditorModel {
|
||||
hiddenColumns = Collections.unmodifiableList(additionalColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "Structure";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TableColumn> getHiddenColumns() {
|
||||
return hiddenColumns;
|
||||
@ -97,11 +102,6 @@ class StructureEditorModel extends CompEditorModel {
|
||||
return COMMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Composite dataType) {
|
||||
super.load(dataType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of component rows in the viewer. There may be a
|
||||
* blank row at the end for selecting. Therefore this number can be
|
||||
@ -213,37 +213,40 @@ class StructureEditorModel extends CompEditorModel {
|
||||
if (currentLength == size) {
|
||||
return;
|
||||
}
|
||||
Structure structure = (Structure) viewComposite;
|
||||
if (currentLength > size) {
|
||||
int numComponents = structure.getNumComponents();
|
||||
|
||||
DataTypeComponent dtc = structure.getComponentContaining(size);
|
||||
int ordinal = dtc.getOrdinal();
|
||||
viewDTM.withTransaction("Set Size", () -> {
|
||||
int length = currentLength;
|
||||
Structure structure = (Structure) viewComposite;
|
||||
if (length > size) {
|
||||
int numComponents = structure.getNumComponents();
|
||||
|
||||
// retain any zero-length components which have an offset equal the new size
|
||||
while (dtc.getOffset() == size && dtc.getLength() == 0 &&
|
||||
(ordinal + 1) < numComponents) {
|
||||
dtc = structure.getComponent(++ordinal);
|
||||
}
|
||||
DataTypeComponent dtc = structure.getComponentContaining(size);
|
||||
int ordinal = dtc.getOrdinal();
|
||||
|
||||
// remove trailing components outside of new size
|
||||
for (int index = numComponents - 1; index >= ordinal; index--) {
|
||||
structure.delete(index);
|
||||
int bitFieldResidualBytes = structure.getNumComponents() - index;
|
||||
for (int i = 0; i < bitFieldResidualBytes; i++) {
|
||||
// bitfield removal may cause injection of undefined bytes - remove them
|
||||
structure.delete(index);
|
||||
// retain any zero-length components which have an offset equal the new size
|
||||
while (dtc.getOffset() == size && dtc.getLength() == 0 &&
|
||||
(ordinal + 1) < numComponents) {
|
||||
dtc = structure.getComponent(++ordinal);
|
||||
}
|
||||
|
||||
// remove trailing components outside of new size
|
||||
for (int index = numComponents - 1; index >= ordinal; index--) {
|
||||
structure.delete(index);
|
||||
int bitFieldResidualBytes = structure.getNumComponents() - index;
|
||||
for (int i = 0; i < bitFieldResidualBytes; i++) {
|
||||
// bitfield removal may cause injection of undefined bytes - remove them
|
||||
structure.delete(index);
|
||||
}
|
||||
}
|
||||
// structure may shrink too much from component removal - may need to grow
|
||||
length = (viewComposite.isZeroLength()) ? 0 : viewComposite.getLength();
|
||||
}
|
||||
// structure may shrink too much from component removal - may need to grow
|
||||
currentLength = (viewComposite.isZeroLength()) ? 0 : viewComposite.getLength();
|
||||
}
|
||||
if (currentLength < size) {
|
||||
// Increasing structure length.
|
||||
structure.growStructure(size - currentLength);
|
||||
}
|
||||
updateAndCheckChangeState();
|
||||
fireTableDataChanged();
|
||||
if (length < size) {
|
||||
// Increasing structure length.
|
||||
structure.growStructure(size - length);
|
||||
}
|
||||
});
|
||||
notifyCompositeChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -286,38 +289,36 @@ class StructureEditorModel extends CompEditorModel {
|
||||
clearComponents(getSelectedComponentRows());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearComponent(int ordinal) {
|
||||
((Structure) viewComposite).clearComponent(ordinal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearComponents(int[] indices) {
|
||||
if (isEditingField()) {
|
||||
endFieldEditing();
|
||||
}
|
||||
|
||||
Arrays.sort(indices);
|
||||
|
||||
// work from back to front so our indices aren't affected by each component's clear.
|
||||
for (int i = indices.length - 1; i >= 0; i--) {
|
||||
DataTypeComponent comp = getComponent(indices[i]);
|
||||
if (comp == null) {
|
||||
continue; // must be on blank last line.
|
||||
}
|
||||
boolean isSelected = selection.containsEntirely(BigInteger.valueOf(indices[i]));
|
||||
int numBytes = comp.getLength();
|
||||
((Structure) viewComposite).clearComponent(indices[i]);
|
||||
viewDTM.withTransaction("Clear Components", () -> {
|
||||
for (int i = indices.length - 1; i >= 0; i--) {
|
||||
DataTypeComponent comp = getComponent(indices[i]);
|
||||
if (comp == null) {
|
||||
continue; // must be on blank last line.
|
||||
}
|
||||
boolean isSelected = selection.containsEntirely(BigInteger.valueOf(indices[i]));
|
||||
int numBytes = comp.getLength();
|
||||
((Structure) viewComposite).clearComponent(indices[i]);
|
||||
|
||||
// Adjust the selection due to the clear.
|
||||
adjustSelection(indices[i] + 1, numBytes - 1);
|
||||
if (isSelected && numBytes > 1) {
|
||||
selection.addRange(indices[i] + 1, indices[i] + numBytes);
|
||||
}
|
||||
// Adjust the selection due to the clear.
|
||||
adjustSelection(indices[i] + 1, numBytes - 1);
|
||||
if (isSelected && numBytes > 1) {
|
||||
selection.addRange(indices[i] + 1, indices[i] + numBytes);
|
||||
}
|
||||
|
||||
if (indices[i] > 0) {
|
||||
consumeByComponent(indices[i] - 1);
|
||||
if (indices[i] > 0) {
|
||||
consumeByComponent(indices[i] - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
componentEdited();
|
||||
}
|
||||
|
||||
@ -374,14 +375,16 @@ class StructureEditorModel extends CompEditorModel {
|
||||
int dtLen = dt.getLength();
|
||||
checkIsAllowableDataType(dt);
|
||||
|
||||
int startIndex = index + 1;
|
||||
if (isShowingUndefinedBytes() && (dt != DataType.DEFAULT)) {
|
||||
int endIndex = startIndex + (dtLen * multiple) - 1;
|
||||
if (startIndex < getNumComponents()) {
|
||||
deleteComponentRange(startIndex, endIndex, monitor);
|
||||
viewDTM.withTransaction("Duplicate Components", () -> {
|
||||
int startIndex = index + 1;
|
||||
if (isShowingUndefinedBytes() && (dt != DataType.DEFAULT)) {
|
||||
int endIndex = startIndex + (dtLen * multiple) - 1;
|
||||
if (startIndex < getNumComponents()) {
|
||||
deleteComponentRange(startIndex, endIndex, monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
insertComponentMultiple(startIndex, dt, originalComp.getLength(), multiple, monitor);
|
||||
insertComponentMultiple(startIndex, dt, originalComp.getLength(), multiple, monitor);
|
||||
});
|
||||
|
||||
// Adjust the selection since we added some components. Select last component added.
|
||||
// Ensure that last added component is selected to allow for repeated duplication
|
||||
@ -407,24 +410,25 @@ class StructureEditorModel extends CompEditorModel {
|
||||
}
|
||||
int len = getLength();
|
||||
|
||||
DataTypeComponent comp = deleteComponentAndResidual(startIndex - 1);
|
||||
|
||||
try {
|
||||
if (!isPackingEnabled() && comp.isBitFieldComponent()) {
|
||||
// insert residual undefined bytes before inserting non-packed bitfield
|
||||
int lenChange = len - getLength();
|
||||
insert(endIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY);
|
||||
return viewDTM.withTransaction("Shift Up", () -> {
|
||||
DataTypeComponent comp = deleteComponentAndResidual(startIndex - 1);
|
||||
try {
|
||||
if (!isPackingEnabled() && comp.isBitFieldComponent()) {
|
||||
// insert residual undefined bytes before inserting non-packed bitfield
|
||||
int lenChange = len - getLength();
|
||||
insert(endIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY);
|
||||
}
|
||||
insert(endIndex, comp.getDataType(), comp.getLength(), comp.getFieldName(),
|
||||
comp.getComment());
|
||||
}
|
||||
insert(endIndex, comp.getDataType(), comp.getLength(), comp.getFieldName(),
|
||||
comp.getComment());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// can't happen while using a dummy monitor
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
catch (CancelledException e) {
|
||||
// can't happen while using a dummy monitor
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -443,24 +447,25 @@ class StructureEditorModel extends CompEditorModel {
|
||||
}
|
||||
int len = getLength();
|
||||
|
||||
DataTypeComponent comp = deleteComponentAndResidual(endIndex + 1);
|
||||
|
||||
try {
|
||||
if (!isPackingEnabled() && comp.isBitFieldComponent()) {
|
||||
// insert residual undefined bytes before inserting non-packed bitfield
|
||||
int lenChange = len - getLength();
|
||||
insert(startIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY);
|
||||
return viewDTM.withTransaction("Shift Down", () -> {
|
||||
DataTypeComponent comp = deleteComponentAndResidual(endIndex + 1);
|
||||
try {
|
||||
if (!isPackingEnabled() && comp.isBitFieldComponent()) {
|
||||
// insert residual undefined bytes before inserting non-packed bitfield
|
||||
int lenChange = len - getLength();
|
||||
insert(startIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY);
|
||||
}
|
||||
insert(startIndex, comp.getDataType(), comp.getLength(), comp.getFieldName(),
|
||||
comp.getComment());
|
||||
}
|
||||
insert(startIndex, comp.getDataType(), comp.getLength(), comp.getFieldName(),
|
||||
comp.getComment());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// can't happen while using a dummy monitor
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
catch (CancelledException e) {
|
||||
// can't happen while using a dummy monitor
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private DataTypeComponent deleteComponentAndResidual(int index) {
|
||||
@ -887,23 +892,27 @@ class StructureEditorModel extends CompEditorModel {
|
||||
String comment) throws InvalidDataTypeException {
|
||||
checkIsAllowableDataType(dataType);
|
||||
try {
|
||||
DataTypeComponent dtc;
|
||||
if (isPackingEnabled() || !(dataType instanceof BitFieldDataType)) {
|
||||
dtc = ((Structure) viewComposite).insert(rowIndex, dataType, length, name, comment);
|
||||
}
|
||||
else {
|
||||
BitFieldDataType bitfield = (BitFieldDataType) dataType;
|
||||
dtc = ((Structure) viewComposite).insertBitField(rowIndex, length,
|
||||
bitfield.getBitOffset(), bitfield.getBaseDataType(),
|
||||
bitfield.getDeclaredBitSize(), name, comment);
|
||||
}
|
||||
if (rowIndex <= row) {
|
||||
row++;
|
||||
}
|
||||
adjustSelection(rowIndex, 1);
|
||||
// Consume undefined bytes that may have been added, if needed.
|
||||
consumeByComponent(rowIndex - 1);
|
||||
return dtc;
|
||||
return viewDTM.withTransaction("Insert Component", () -> {
|
||||
DataTypeComponent dtc;
|
||||
if (isPackingEnabled() || !(dataType instanceof BitFieldDataType)) {
|
||||
dtc = ((Structure) viewComposite).insert(rowIndex, dataType, length, name,
|
||||
comment);
|
||||
}
|
||||
else {
|
||||
BitFieldDataType bitfield = (BitFieldDataType) dataType;
|
||||
dtc = ((Structure) viewComposite).insertBitField(rowIndex, length,
|
||||
bitfield.getBitOffset(), bitfield.getBaseDataType(),
|
||||
bitfield.getDeclaredBitSize(), name, comment);
|
||||
}
|
||||
if (rowIndex <= currentEditRow) {
|
||||
currentEditRow++;
|
||||
}
|
||||
adjustSelection(rowIndex, 1);
|
||||
// Consume undefined bytes that may have been added, if needed.
|
||||
consumeByComponent(rowIndex - 1);
|
||||
|
||||
return dtc;
|
||||
});
|
||||
}
|
||||
catch (IllegalArgumentException exc) {
|
||||
throw new InvalidDataTypeException(exc.getMessage());
|
||||
@ -918,20 +927,21 @@ class StructureEditorModel extends CompEditorModel {
|
||||
int componentOrdinal = convertRowToOrdinal(rowIndex);
|
||||
monitor.initialize(numCopies);
|
||||
try {
|
||||
viewDTM.withTransaction("Insert Multiple", () -> {
|
||||
for (int i = 0; i < numCopies; i++) {
|
||||
monitor.checkCancelled();
|
||||
monitor.setMessage("Inserting " + (i + 1) + " of " + numCopies);
|
||||
viewComposite.insert(componentOrdinal, dataType, length);
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numCopies; i++) {
|
||||
monitor.checkCancelled();
|
||||
monitor.setMessage("Inserting " + (i + 1) + " of " + numCopies);
|
||||
viewComposite.insert(componentOrdinal, dataType, length);
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
||||
if (rowIndex <= row) {
|
||||
row += numCopies;
|
||||
}
|
||||
adjustSelection(componentOrdinal, numCopies);
|
||||
// Consume undefined bytes that may have been added, if needed.
|
||||
consumeByComponent(componentOrdinal - numCopies);
|
||||
if (rowIndex <= currentEditRow) {
|
||||
currentEditRow += numCopies;
|
||||
}
|
||||
adjustSelection(componentOrdinal, numCopies);
|
||||
// Consume undefined bytes that may have been added, if needed.
|
||||
consumeByComponent(componentOrdinal - numCopies);
|
||||
});
|
||||
}
|
||||
catch (IllegalArgumentException exc) {
|
||||
throw new InvalidDataTypeException(exc.getMessage());
|
||||
@ -951,8 +961,10 @@ class StructureEditorModel extends CompEditorModel {
|
||||
// FreeForm editing mode (showing Undefined Bytes).
|
||||
if (isShowingUndefinedBytes() && !isAtEnd(rowIndex)) {
|
||||
int origLen = getComponent(rowIndex).getLength();
|
||||
dtc = ((Structure) viewComposite).replace(componentOrdinal, dataType, length, name,
|
||||
comment);
|
||||
dtc = viewDTM.withTransaction("Replace Component", () -> {
|
||||
return ((Structure) viewComposite).replace(componentOrdinal, dataType, length,
|
||||
name, comment);
|
||||
});
|
||||
diffLen = origLen - dtc.getLength();
|
||||
int nextRowIndex = rowIndex + 1;
|
||||
if (diffLen < 0) {
|
||||
@ -965,14 +977,16 @@ class StructureEditorModel extends CompEditorModel {
|
||||
selection.addRange(nextRowIndex, nextRowIndex + diffLen);
|
||||
}
|
||||
}
|
||||
if (rowIndex < row) {
|
||||
row += diffLen;
|
||||
if (rowIndex < currentEditRow) {
|
||||
currentEditRow += diffLen;
|
||||
}
|
||||
}
|
||||
else {
|
||||
((Structure) viewComposite).delete(componentOrdinal);
|
||||
dtc = ((Structure) viewComposite).insert(componentOrdinal, dataType, length, name,
|
||||
comment);
|
||||
dtc = viewDTM.withTransaction("Replace Component", () -> {
|
||||
((Structure) viewComposite).delete(componentOrdinal);
|
||||
return ((Structure) viewComposite).insert(componentOrdinal, dataType, length,
|
||||
name, comment);
|
||||
});
|
||||
}
|
||||
return dtc;
|
||||
}
|
||||
@ -1013,47 +1027,53 @@ class StructureEditorModel extends CompEditorModel {
|
||||
overlap.intersect(selection);
|
||||
boolean replacedSelected = (overlap.getNumRanges() > 0);
|
||||
|
||||
// Remove the selected components.
|
||||
deleteComponentRange(startRowIndex, endRowIndex, monitor);
|
||||
|
||||
int beginUndefs = startRowIndex + numComps;
|
||||
// Create the new components.
|
||||
insertMultiple(startRowIndex, datatype, length, numComps, monitor);
|
||||
int indexAfterMultiple = startRowIndex + numComps;
|
||||
if (replacedSelected) {
|
||||
selection.addRange(startRowIndex, indexAfterMultiple);
|
||||
fixSelection();
|
||||
}
|
||||
|
||||
DataTypeComponent comp = getComponent(startRowIndex);
|
||||
// Set the field name and comment the same as before
|
||||
int txId = viewDTM.startTransaction("Replace Multiple");
|
||||
try {
|
||||
comp.setFieldName(fieldName);
|
||||
}
|
||||
catch (DuplicateNameException exc) {
|
||||
Msg.showError(this, null, null, null);
|
||||
}
|
||||
comp.setComment(comment);
|
||||
// Remove the selected components.
|
||||
deleteComponentRange(startRowIndex, endRowIndex, monitor);
|
||||
|
||||
// Create any needed undefined data types.
|
||||
int remainingLength = numBytesInRange - (numComps * length);
|
||||
if (remainingLength > 0 && isShowingUndefinedBytes()) {
|
||||
int beginUndefs = startRowIndex + numComps;
|
||||
// Create the new components.
|
||||
insertMultiple(startRowIndex, datatype, length, numComps, monitor);
|
||||
int indexAfterMultiple = startRowIndex + numComps;
|
||||
if (replacedSelected) {
|
||||
selection.addRange(startRowIndex, indexAfterMultiple);
|
||||
fixSelection();
|
||||
}
|
||||
|
||||
DataTypeComponent comp = getComponent(startRowIndex);
|
||||
// Set the field name and comment the same as before
|
||||
try {
|
||||
insertComponentMultiple(beginUndefs, DataType.DEFAULT, DataType.DEFAULT.getLength(),
|
||||
remainingLength, monitor);
|
||||
if (replacedSelected) {
|
||||
selection.addRange(indexAfterMultiple, indexAfterMultiple + remainingLength);
|
||||
comp.setFieldName(fieldName);
|
||||
}
|
||||
catch (DuplicateNameException exc) {
|
||||
Msg.showError(this, null, null, null);
|
||||
}
|
||||
comp.setComment(comment);
|
||||
|
||||
// Create any needed undefined data types.
|
||||
int remainingLength = numBytesInRange - (numComps * length);
|
||||
if (remainingLength > 0 && isShowingUndefinedBytes()) {
|
||||
try {
|
||||
insertComponentMultiple(beginUndefs, DataType.DEFAULT,
|
||||
DataType.DEFAULT.getLength(), remainingLength, monitor);
|
||||
if (replacedSelected) {
|
||||
selection.addRange(indexAfterMultiple,
|
||||
indexAfterMultiple + remainingLength);
|
||||
}
|
||||
}
|
||||
catch (InvalidDataTypeException idte) {
|
||||
Msg.showError(this, null, "Structure Editor Error", idte.getMessage());
|
||||
}
|
||||
}
|
||||
catch (InvalidDataTypeException idte) {
|
||||
Msg.showError(this, null, "Structure Editor Error", idte.getMessage());
|
||||
else if (remainingLength < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (remainingLength < 0) {
|
||||
return false;
|
||||
finally {
|
||||
viewDTM.endTransaction(txId, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1068,28 +1088,30 @@ class StructureEditorModel extends CompEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
void removeDtFromComponents(Composite comp) {
|
||||
DataType newDt = viewDTM.getDataType(comp.getDataTypePath());
|
||||
if (newDt == null) {
|
||||
return;
|
||||
}
|
||||
int num = getNumComponents();
|
||||
for (int i = num - 1; i >= 0; i--) {
|
||||
DataTypeComponent dtc = getComponent(i);
|
||||
DataType dt = dtc.getDataType();
|
||||
if (dt instanceof Composite) {
|
||||
Composite dtcComp = (Composite) dt;
|
||||
if (dtcComp.isPartOf(newDt)) {
|
||||
clearComponents(new int[] { i });
|
||||
String msg =
|
||||
"Components containing " + comp.getDisplayName() + " were cleared.";
|
||||
setStatus(msg, true);
|
||||
boolean clearedComponents = viewDTM.withTransaction("Remove Components", () -> {
|
||||
boolean cleared = false;
|
||||
int num = getNumComponents();
|
||||
for (int i = num - 1; i >= 0; i--) {
|
||||
DataTypeComponent dtc = getComponent(i);
|
||||
DataType dt = dtc.getDataType();
|
||||
if (dt instanceof Composite) {
|
||||
Composite dtcComp = (Composite) dt;
|
||||
if (dtcComp.isPartOf(newDt)) {
|
||||
clearComponents(new int[] { i });
|
||||
cleared = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cleared;
|
||||
});
|
||||
if (clearedComponents) {
|
||||
setStatus("Components containing " + comp.getDisplayName() + " were cleared.", true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1203,27 +1225,35 @@ class StructureEditorModel extends CompEditorModel {
|
||||
}
|
||||
|
||||
DataType addedDataType = createDataTypeInOriginalDTM(structureDataType);
|
||||
if (viewComposite.isPackingEnabled()) {
|
||||
deleteSelectedComponents();
|
||||
insert(minRow, addedDataType, addedDataType.getLength());
|
||||
|
||||
int txId = viewDTM.startTransaction("Replace w/Structure");
|
||||
try {
|
||||
if (viewComposite.isPackingEnabled()) {
|
||||
deleteSelectedComponents();
|
||||
insert(minRow, addedDataType, addedDataType.getLength());
|
||||
}
|
||||
else {
|
||||
int adjustmentBytes = 0;
|
||||
if (firstDtc != null && firstDtc.isBitFieldComponent() && minRow > 0) {
|
||||
DataTypeComponent dtc = getComponent(minRow - 1);
|
||||
if (dtc.getEndOffset() == firstDtc.getOffset()) {
|
||||
++adjustmentBytes;
|
||||
}
|
||||
}
|
||||
if (lastDtc != null && lastDtc.isBitFieldComponent() &&
|
||||
maxRow < getNumComponents()) {
|
||||
DataTypeComponent dtc = getComponent(maxRow);
|
||||
if (dtc.getOffset() == lastDtc.getEndOffset()) {
|
||||
++adjustmentBytes;
|
||||
}
|
||||
}
|
||||
clearSelectedComponents();
|
||||
insertMultiple(minRow, DataType.DEFAULT, 1, adjustmentBytes, monitor);
|
||||
replace(minRow, addedDataType, addedDataType.getLength());
|
||||
}
|
||||
}
|
||||
else {
|
||||
int adjustmentBytes = 0;
|
||||
if (firstDtc != null && firstDtc.isBitFieldComponent() && minRow > 0) {
|
||||
DataTypeComponent dtc = getComponent(minRow - 1);
|
||||
if (dtc.getEndOffset() == firstDtc.getOffset()) {
|
||||
++adjustmentBytes;
|
||||
}
|
||||
}
|
||||
if (lastDtc != null && lastDtc.isBitFieldComponent() && maxRow < getNumComponents()) {
|
||||
DataTypeComponent dtc = getComponent(maxRow);
|
||||
if (dtc.getOffset() == lastDtc.getEndOffset()) {
|
||||
++adjustmentBytes;
|
||||
}
|
||||
}
|
||||
clearSelectedComponents();
|
||||
insertMultiple(minRow, DataType.DEFAULT, 1, adjustmentBytes, monitor);
|
||||
replace(minRow, addedDataType, addedDataType.getLength());
|
||||
finally {
|
||||
viewDTM.endTransaction(txId, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1263,7 +1293,6 @@ class StructureEditorModel extends CompEditorModel {
|
||||
|
||||
private DataType createDataTypeInOriginalDTM(StructureDataType structureDataType) {
|
||||
boolean commit = false;
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
int transactionID =
|
||||
originalDTM.startTransaction("Create structure " + structureDataType.getName());
|
||||
try {
|
||||
@ -1300,81 +1329,83 @@ class StructureEditorModel extends CompEditorModel {
|
||||
endFieldEditing();
|
||||
}
|
||||
|
||||
Structure viewStruct = (Structure) viewComposite;
|
||||
viewDTM.withTransaction("Unpack Component", () -> {
|
||||
Structure viewStruct = (Structure) viewComposite;
|
||||
|
||||
// Get the field name and comment before removing.
|
||||
String fieldName = currentComp.getFieldName();
|
||||
String comment = currentComp.getComment();
|
||||
int numComps = 0;
|
||||
// This component is an array so unpackage it.
|
||||
if (currentDataType instanceof Array) {
|
||||
Array array = (Array) currentDataType;
|
||||
int elementLen = array.getElementLength();
|
||||
numComps = array.getNumElements();
|
||||
// Remove the array.
|
||||
delete(componentOrdinal);
|
||||
if (numComps > 0) {
|
||||
// Add the array's elements
|
||||
try {
|
||||
DataType dt = array.getDataType();
|
||||
insertMultiple(rowIndex, dt, elementLen, numComps, monitor);
|
||||
}
|
||||
catch (InvalidDataTypeException ie) {
|
||||
// Do nothing.
|
||||
}
|
||||
catch (OutOfMemoryError memExc) {
|
||||
throw memExc; // rethrow the exception.
|
||||
// Get the field name and comment before removing.
|
||||
String fieldName = currentComp.getFieldName();
|
||||
String comment = currentComp.getComment();
|
||||
int numComps = 0;
|
||||
// This component is an array so unpackage it.
|
||||
if (currentDataType instanceof Array) {
|
||||
Array array = (Array) currentDataType;
|
||||
int elementLen = array.getElementLength();
|
||||
numComps = array.getNumElements();
|
||||
// Remove the array.
|
||||
delete(componentOrdinal);
|
||||
if (numComps > 0) {
|
||||
// Add the array's elements
|
||||
try {
|
||||
DataType dt = array.getDataType();
|
||||
insertMultiple(rowIndex, dt, elementLen, numComps, monitor);
|
||||
}
|
||||
catch (InvalidDataTypeException ie) {
|
||||
// Do nothing.
|
||||
}
|
||||
catch (OutOfMemoryError memExc) {
|
||||
throw memExc; // rethrow the exception.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// This component is a structure so unpackage it.
|
||||
else if (currentDataType instanceof Structure) {
|
||||
Structure struct = (Structure) currentDataType;
|
||||
numComps = struct.getNumComponents();
|
||||
if (numComps > 0) {
|
||||
// Remove the structure.
|
||||
int currentOffset = currentComp.getOffset();
|
||||
deleteComponent(rowIndex);
|
||||
// This component is a structure so unpackage it.
|
||||
else if (currentDataType instanceof Structure) {
|
||||
Structure struct = (Structure) currentDataType;
|
||||
numComps = struct.getNumComponents();
|
||||
if (numComps > 0) {
|
||||
// Remove the structure.
|
||||
int currentOffset = currentComp.getOffset();
|
||||
deleteComponent(rowIndex);
|
||||
|
||||
// Add the structure's elements
|
||||
for (int i = 0; i < numComps; i++) {
|
||||
DataTypeComponent dtc = struct.getComponent(i);
|
||||
DataType dt = dtc.getDataType();
|
||||
int compLength = dtc.getLength();
|
||||
if (!isPackingEnabled()) {
|
||||
if (dtc.isBitFieldComponent()) {
|
||||
BitFieldDataType bitfield = (BitFieldDataType) dt;
|
||||
viewStruct.insertBitFieldAt(currentOffset + dtc.getOffset(), compLength,
|
||||
bitfield.getBitOffset(), bitfield.getBaseDataType(),
|
||||
bitfield.getDeclaredBitSize(), dtc.getFieldName(),
|
||||
dtc.getComment());
|
||||
// Add the structure's elements
|
||||
for (int i = 0; i < numComps; i++) {
|
||||
DataTypeComponent dtc = struct.getComponent(i);
|
||||
DataType dt = dtc.getDataType();
|
||||
int compLength = dtc.getLength();
|
||||
if (!isPackingEnabled()) {
|
||||
if (dtc.isBitFieldComponent()) {
|
||||
BitFieldDataType bitfield = (BitFieldDataType) dt;
|
||||
viewStruct.insertBitFieldAt(currentOffset + dtc.getOffset(),
|
||||
compLength, bitfield.getBitOffset(), bitfield.getBaseDataType(),
|
||||
bitfield.getDeclaredBitSize(), dtc.getFieldName(),
|
||||
dtc.getComment());
|
||||
}
|
||||
else {
|
||||
viewStruct.insertAtOffset(currentOffset + dtc.getOffset(), dt,
|
||||
compLength, dtc.getFieldName(), dtc.getComment());
|
||||
}
|
||||
}
|
||||
else {
|
||||
viewStruct.insertAtOffset(currentOffset + dtc.getOffset(), dt,
|
||||
compLength, dtc.getFieldName(), dtc.getComment());
|
||||
insert(rowIndex + i, dt, compLength, dtc.getFieldName(),
|
||||
dtc.getComment());
|
||||
}
|
||||
}
|
||||
else {
|
||||
insert(rowIndex + i, dt, compLength, dtc.getFieldName(), dtc.getComment());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
selection.clear();
|
||||
selection.addRange(rowIndex, rowIndex + numComps);
|
||||
selection.clear();
|
||||
selection.addRange(rowIndex, rowIndex + numComps);
|
||||
|
||||
DataTypeComponent comp = getComponent(rowIndex);
|
||||
// Set the field name and comment the same as before
|
||||
try {
|
||||
if (comp.getFieldName() == null) {
|
||||
comp.setFieldName(fieldName);
|
||||
DataTypeComponent comp = getComponent(rowIndex);
|
||||
// Set the field name and comment the same as before
|
||||
try {
|
||||
if (comp.getFieldName() == null) {
|
||||
comp.setFieldName(fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (DuplicateNameException exc) {
|
||||
Msg.showError(this, null, null, null);
|
||||
}
|
||||
comp.setComment(comment);
|
||||
|
||||
catch (DuplicateNameException exc) {
|
||||
Msg.showError(this, null, null, null);
|
||||
}
|
||||
comp.setComment(comment);
|
||||
});
|
||||
fixSelection();
|
||||
componentEdited();
|
||||
selectionChanged();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.tree.GTree;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class PackDataTypeAction extends DockingAction {
|
||||
|
||||
public PackDataTypeAction(DataTypeManagerPlugin plugin) {
|
||||
super("Pack Data Type", plugin.getName());
|
||||
setPopupMenuData(new MenuData(new String[] { "Pack (default)" }, "Edit"));
|
||||
// setHelpLocation(new HelpLocation(plugin.getName(), getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
DataTypeNode node = getSelectedDataTypeNode(context);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
DataType dataType = node.getDataType();
|
||||
if (dataType instanceof BuiltInDataType || dataType instanceof Pointer ||
|
||||
dataType instanceof MissingBuiltInDataType) {
|
||||
return false;
|
||||
}
|
||||
if (!node.isModifiable()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
DataTypeNode node = getSelectedDataTypeNode(context);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
DataType dataType = node.getDataType();
|
||||
if (dataType instanceof Composite) {
|
||||
return !((Composite) dataType).isPackingEnabled();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private DataTypeNode getSelectedDataTypeNode(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
if (!(contextObject instanceof GTree)) {
|
||||
return null;
|
||||
}
|
||||
GTree gTree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
if (selectionPaths.length != 1) {
|
||||
return null;
|
||||
}
|
||||
DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent();
|
||||
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
return null;
|
||||
}
|
||||
return (DataTypeNode) node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTree gTree = (GTree) context.getContextObject();
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
for (TreePath treePath : selectionPaths) {
|
||||
final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent();
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
DataOrganization dataOrganization = dataTypeManager.getDataOrganization();
|
||||
alignDataType(dataType, dataOrganization);
|
||||
}
|
||||
}
|
||||
|
||||
private void alignDataType(DataType dataType, DataOrganization dataOrganization) {
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
if (dataTypeManager == null) {
|
||||
Msg.error(this,
|
||||
"Can't align data type " + dataType.getName() + " without a data type manager.");
|
||||
return;
|
||||
}
|
||||
if (!(dataType instanceof Structure)) {
|
||||
Msg.error(this,
|
||||
"Can't align data type " + dataType.getName() + ". It's not a structure.");
|
||||
return;
|
||||
}
|
||||
int transactionID = -1;
|
||||
boolean commit = false;
|
||||
try {
|
||||
// start a transaction
|
||||
transactionID = dataTypeManager.startTransaction("Pack " + dataType.getName());
|
||||
((Structure) dataType).setPackingEnabled(true);
|
||||
commit = true;
|
||||
}
|
||||
finally {
|
||||
// commit the changes
|
||||
dataTypeManager.endTransaction(transactionID, commit);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.dialogs.NumberInputDialog;
|
||||
import docking.widgets.tree.GTree;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class PackSizeDataTypeAction extends DockingAction {
|
||||
|
||||
private DataTypeManagerPlugin plugin;
|
||||
|
||||
public PackSizeDataTypeAction(DataTypeManagerPlugin plugin) {
|
||||
super("Pack Size Data Type", plugin.getName());
|
||||
this.plugin = plugin;
|
||||
setPopupMenuData(new MenuData(new String[] { "Pack for Size..." }, "Edit"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
if (!(contextObject instanceof GTree)) {
|
||||
return false;
|
||||
}
|
||||
GTree gTree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
if (selectionPaths.length != 1) {
|
||||
return false;
|
||||
}
|
||||
DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent();
|
||||
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
return false;
|
||||
}
|
||||
setEnabled(node.isModifiable());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTree gTree = (GTree) context.getContextObject();
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
TreePath treePath = selectionPaths[0];
|
||||
final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent();
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
if (dataTypeManager == null) {
|
||||
Msg.error(this,
|
||||
"Can't pack data type " + dataType.getName() + " without a data type manager.");
|
||||
return;
|
||||
}
|
||||
|
||||
NumberInputDialog numberInputDialog =
|
||||
new NumberInputDialog("explicit pack value", 0, 0, 16);
|
||||
if (!numberInputDialog.show()) {
|
||||
return;
|
||||
}
|
||||
int packSize = numberInputDialog.getValue();
|
||||
|
||||
int transactionID = -1;
|
||||
boolean commit = false;
|
||||
try {
|
||||
// start a transaction
|
||||
transactionID =
|
||||
dataTypeManager.startTransaction("Pack(" + packSize + ") " + dataType.getName());
|
||||
packDataType(dataType, packSize);
|
||||
commit = true;
|
||||
}
|
||||
catch (IllegalArgumentException iie) {
|
||||
Msg.showError(this, null, "Invalid Pack Value", iie.getMessage());
|
||||
}
|
||||
finally {
|
||||
// commit the changes
|
||||
dataTypeManager.endTransaction(transactionID, commit);
|
||||
}
|
||||
}
|
||||
|
||||
private void packDataType(DataType dataType, int packSize) throws IllegalArgumentException {
|
||||
if (!(dataType instanceof Composite)) {
|
||||
Msg.error(this,
|
||||
"Can't pack data type " + dataType.getName() + ". It's not a composite.");
|
||||
return;
|
||||
}
|
||||
((Composite) dataType).pack(packSize);
|
||||
}
|
||||
|
||||
}
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -32,10 +32,11 @@ import docking.widgets.fieldpanel.support.FieldSelection;
|
||||
* When edit actions occur and there is a selection, the listener's are notified
|
||||
* of the new selection via the listener's overrideSelection method.
|
||||
*/
|
||||
import ghidra.app.plugin.core.compositeeditor.CompositeEditorModel;
|
||||
import ghidra.app.plugin.core.compositeeditor.DataTypeHelper;
|
||||
import ghidra.app.plugin.core.compositeeditor.*;
|
||||
import ghidra.app.util.datatype.EmptyCompositeException;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.DatabaseObject;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
@ -77,6 +78,11 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "Stack";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean allowsZeroLengthComponents() {
|
||||
return false;
|
||||
@ -105,8 +111,23 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Composite createViewCompositeFromOriginalComposite(Composite original) {
|
||||
return (Composite) original.copy(original.getDataTypeManager());
|
||||
protected void createViewCompositeFromOriginalComposite(Composite original) {
|
||||
|
||||
if (viewDTM != null) {
|
||||
viewDTM.close();
|
||||
viewDTM = null;
|
||||
}
|
||||
|
||||
// Use temporary standalone view datatype manager which will not manage the viewComposite
|
||||
viewDTM = new CompositeViewerDataTypeManager(originalDTM.getName(), originalDTM);
|
||||
|
||||
// NOTE: StackFrameDataType cannot be resolved
|
||||
viewComposite = (Composite) original.copy(viewDTM);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void restoreEditor() {
|
||||
throw new UnsupportedOperationException("undo/redo not supported");
|
||||
}
|
||||
|
||||
StackFrameDataType getViewComposite() {
|
||||
@ -127,21 +148,10 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
int stackLocalSize = originalStack.getLocalSize();
|
||||
int stackParamOffset = originalStack.getParameterOffset();
|
||||
int stackParamSize = originalStack.getParameterSize();
|
||||
hadChanges = (editReturnAddressOffset != stackReturnAddressOffset) ||
|
||||
hasChanges = (editReturnAddressOffset != stackReturnAddressOffset) ||
|
||||
(editLocalSize != stackLocalSize) || (editParamOffset != stackParamOffset) ||
|
||||
(editParamSize != stackParamSize) || super.updateAndCheckChangeState();
|
||||
return hadChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current dataType name (Structure or Union) as a string.
|
||||
*/
|
||||
@Override
|
||||
protected String getTypeName() {
|
||||
if (viewComposite instanceof StackFrameDataType) {
|
||||
return "Stack";
|
||||
}
|
||||
return super.getTypeName();
|
||||
return hasChanges;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -435,11 +445,6 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
return (StackFrameDataType) viewComposite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearComponent(int ordinal) {
|
||||
((StackFrameDataType) viewComposite).clearComponent(ordinal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelectedComponents() throws UsrException {
|
||||
OffsetPairs offsetSelection = getRelOffsetSelection();
|
||||
@ -683,11 +688,6 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
return (getNumSelectedRows() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCycleAllowed(CycleGroup cycleGroup) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleteAllowed() {
|
||||
if (selection.getNumRanges() != 1) {
|
||||
@ -840,7 +840,7 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
|
||||
/** Gets the original field name within the parent data type for a given row in the editor */
|
||||
private boolean isOriginalFieldName(String testName, int rowIndex) {
|
||||
StackFrameDataType dataType = (StackFrameDataType) getOriginalComposite();
|
||||
StackFrameDataType dataType = getOriginalComposite();
|
||||
String fieldName = getFieldNameAtRow(rowIndex, dataType);
|
||||
return SystemUtilities.isEqual(fieldName, testName);
|
||||
}
|
||||
@ -858,7 +858,11 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
|
||||
@Override
|
||||
public DataTypeComponent add(DataType dataType) throws UsrException {
|
||||
return replace(dataType);
|
||||
int rowIndex = getMinIndexSelected();
|
||||
if (rowIndex < 0) {
|
||||
throw new UsrException("A component must be selected.");
|
||||
}
|
||||
return replace(rowIndex, dataType);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1019,7 +1023,7 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
newSv.setComment(comment);
|
||||
}
|
||||
}
|
||||
load(new StackFrameDataType(original, dtm));
|
||||
load(new StackFrameDataType(original, viewDTM));
|
||||
clearStatus();
|
||||
return true;
|
||||
}
|
||||
@ -1106,10 +1110,7 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
return replace(rowIndex, dataType);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public DataTypeComponent replace(int index, DataType dataType) throws UsrException {
|
||||
private DataTypeComponent replace(int index, DataType dataType) throws UsrException {
|
||||
try {
|
||||
DataTypeInstance dti = getDropDataType(index, dataType);
|
||||
return replace(index, dti.getDataType(), dti.getLength());
|
||||
@ -1121,17 +1122,12 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
|
||||
@Override
|
||||
public DataTypeComponent replace(int index, DataType dt, int dtLength) throws UsrException {
|
||||
|
||||
OffsetPairs offsetSelection = getRelOffsetSelection();
|
||||
int transID = startTransaction("Apply Data Type \"" + dt.getName() + "\"");
|
||||
try {
|
||||
fieldEdited(
|
||||
DataTypeInstance.getDataTypeInstance(dt, dtLength, usesAlignedLengthComponents()),
|
||||
index, getDataTypeColumn());
|
||||
setRelOffsetSelection(offsetSelection);
|
||||
}
|
||||
finally {
|
||||
endTransaction(transID);
|
||||
}
|
||||
fieldEdited(
|
||||
DataTypeInstance.getDataTypeInstance(dt, dtLength, usesAlignedLengthComponents()),
|
||||
index, getDataTypeColumn());
|
||||
setRelOffsetSelection(offsetSelection);
|
||||
return getComponent(index);
|
||||
}
|
||||
|
||||
@ -1159,6 +1155,48 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
return max / dtc.getDataType().getAlignedLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
|
||||
StackFrameDataType sfdt = getOriginalComposite();
|
||||
Function function = sfdt.getFunction();
|
||||
if (function.isDeleted()) {
|
||||
// Cancel Editor.
|
||||
provider.dispose();
|
||||
PluginTool tool = ((StackEditorProvider) provider).getPlugin().getTool();
|
||||
tool.setStatusInfo("Stack Editor was closed for " + provider.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
updateAndCheckChangeState();
|
||||
|
||||
boolean reload = true;
|
||||
if (hasChanges) {
|
||||
// The user has modified the structure so prompt for whether or
|
||||
// not to reload the structure.
|
||||
String question = "The program \"dtm.getName()\" has been restored.\n" + "\"" +
|
||||
currentName + "\" may have changed outside the editor.\n" +
|
||||
"Discard edits and reload the Stack Editor?";
|
||||
String title = "Reload Stack Editor?";
|
||||
int response = OptionDialog
|
||||
.showYesNoDialogWithNoAsDefaultButton(provider.getComponent(), title, question);
|
||||
if (response != 1) {
|
||||
reload = false;
|
||||
}
|
||||
}
|
||||
if (reload) {
|
||||
|
||||
StackFrame stack = function.getStackFrame();
|
||||
StackFrameDataType newSfdt =
|
||||
new StackFrameDataType(stack, function.getProgram().getDataTypeManager());
|
||||
|
||||
load(newSfdt); // reload the stack model based on current stack frame
|
||||
}
|
||||
else {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeChanged(DataTypeManager dataTypeManager, DataTypePath path) {
|
||||
if (isLoaded()) {
|
||||
@ -1235,10 +1273,18 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stack frame model datatype being edited
|
||||
* @return stack frame model datatype
|
||||
*/
|
||||
@Override
|
||||
protected Composite getOriginalComposite() {
|
||||
// This is to allow the stack editor panel to have access.
|
||||
return originalComposite; // not contained within datatype manager
|
||||
protected StackFrameDataType getOriginalComposite() {
|
||||
return (StackFrameDataType) originalComposite;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean originalCompositeExists() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1247,12 +1293,6 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
return super.getOriginalDataTypeManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fixupOriginalPath(Composite composite) {
|
||||
// This is to allow the stack editor panel to have access.
|
||||
super.fixupOriginalPath(composite);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getCompositeID() {
|
||||
// This is to allow the stack editor panel to have access.
|
||||
@ -1276,10 +1316,25 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
for (int i = comps.length - 1; i >= 0; i--) {
|
||||
DataTypeComponent component = comps[i];
|
||||
DataType compDt = component.getDataType();
|
||||
if (compDt.isDeleted()) {
|
||||
clearComponent(component.getOrdinal());
|
||||
if (compDt instanceof DatabaseObject) {
|
||||
// NOTE: viewDTM only maps view-to-original IDs for DataTypeDB
|
||||
long myId = viewDTM.getID(compDt);
|
||||
if (viewDTM.findOriginalDataTypeFromMyID(myId) == null) {
|
||||
// Datatype not found
|
||||
clearComponent(component.getOrdinal());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewDTM.refreshDBTypesFromOriginal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clearComponents(int[] rows) {
|
||||
for (int i = rows.length - 1; i >= 0; i--) {
|
||||
((StackFrameDataType) viewComposite).clearComponent(rows[i]);
|
||||
}
|
||||
notifyCompositeChanged();
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
@ -1289,24 +1344,6 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
//**************************************************************************
|
||||
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
|
||||
@Override
|
||||
public DataType resolve(DataType dt) {
|
||||
if (dt instanceof StackPieceDataType) {
|
||||
return dt;
|
||||
}
|
||||
return DataTypeHelper.resolveDataType(dt, viewDTM, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides the CompositeEditorModel to wrap the resolve of the data type
|
||||
* in a transaction.
|
||||
*/
|
||||
@Override
|
||||
public DataType resolveDataType(DataType dt, DataTypeManager resolveDtm,
|
||||
DataTypeConflictHandler conflictHandler) {
|
||||
return DataTypeHelper.resolveDataType(dt, resolveDtm, conflictHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTypeInstance validateComponentDataType(int index, String dtString)
|
||||
throws CancelledException, UsrException {
|
||||
@ -1323,7 +1360,6 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
DataType newDt = DataTypeHelper.parseDataType(index, dtString, this, originalDTM,
|
||||
provider.getDtmService());
|
||||
|
||||
@ -1337,7 +1373,7 @@ public class StackEditorModel extends CompositeEditorModel {
|
||||
int newLength = newDt.getLength();
|
||||
|
||||
checkIsAllowableDataType(newDt);
|
||||
newDt = DataTypeHelper.resolveDataType(newDt, viewDTM, null);
|
||||
newDt = resolveDataType(newDt, viewDTM, null);
|
||||
int maxLength = getMaxReplaceLength(index);
|
||||
if (newLength <= 0) {
|
||||
throw new UsrException("Can't currently add this data type--not enough space.");
|
||||
|
@ -21,12 +21,8 @@ import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.compositeeditor.CompositeEditorPanel;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
/**
|
||||
@ -45,6 +41,27 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
super(model, provider);
|
||||
}
|
||||
|
||||
private StackEditorModel getStackModel() {
|
||||
return (StackEditorModel) model;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasUncomittedEntry() {
|
||||
// Stack editor has not yet been modified to use GFormattedTextField
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasInvalidEntry() {
|
||||
// Stack editor has not yet been modified to use GFormattedTextField
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void comitEntryChanges() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
int getFrameSize() {
|
||||
return Integer.decode(frameSizeField.getText()).intValue();
|
||||
}
|
||||
@ -149,7 +166,7 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
((StackEditorModel) model).setLocalSize(localSize);
|
||||
getStackModel().setLocalSize(localSize);
|
||||
}
|
||||
catch (UsrException ue) {
|
||||
model.setStatus("Invalid local size \"" + valueStr + "\". " + ue.getMessage(),
|
||||
@ -193,7 +210,7 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
value = Integer.decode(valueStr);
|
||||
int paramSize = value.intValue();
|
||||
try {
|
||||
((StackEditorModel) model).setParameterSize(paramSize);
|
||||
getStackModel().setParameterSize(paramSize);
|
||||
}
|
||||
catch (UsrException ue) {
|
||||
model.setStatus("Invalid parameter size \"" + valueStr + "\". " + ue.getMessage(),
|
||||
@ -220,9 +237,6 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
returnAddrOffsetField.setEnabled(false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.compositeeditor.CompositeModelDataListener#compositeInfoChanged()
|
||||
*/
|
||||
@Override
|
||||
public void compositeInfoChanged() {
|
||||
adjustStackInfo();
|
||||
@ -233,11 +247,8 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
: Integer.toString(value);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void adjustStackInfo() {
|
||||
StackFrameDataType editorStack = ((StackEditorModel) model).getEditorStack();
|
||||
StackFrameDataType editorStack = getStackModel().getEditorStack();
|
||||
|
||||
String frameSize = getNumberString(editorStack.getFrameSize());
|
||||
if (!frameSizeField.getText().trim().equals(frameSize)) {
|
||||
@ -265,67 +276,11 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.compositeeditor.CompositeViewerModelListener#componentDataChanged()
|
||||
*/
|
||||
@Override
|
||||
public void componentDataChanged() {
|
||||
// Don't need to update other than table when component data changes.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dataTypeManagerRestored() {
|
||||
boolean reload = true;
|
||||
String objectType = "program";
|
||||
DataTypeManager dtm = ((StackEditorModel) model).getOriginalDataTypeManager();
|
||||
Composite originalDt = ((StackEditorModel) model).getOriginalComposite();
|
||||
if (originalDt instanceof StackFrameDataType) {
|
||||
StackFrameDataType sfdt = (StackFrameDataType) originalDt;
|
||||
Function function = sfdt.getFunction();
|
||||
if (function.isDeleted()) {
|
||||
// Cancel Editor.
|
||||
provider.dispose();
|
||||
PluginTool tool = ((StackEditorProvider) provider).getPlugin().getTool();
|
||||
tool.setStatusInfo("Stack Editor was closed for " + provider.getName());
|
||||
return;
|
||||
}
|
||||
StackFrame stack = function.getStackFrame();
|
||||
StackFrameDataType newSfdt = new StackFrameDataType(stack, dtm);
|
||||
if (!newSfdt.equals(((StackEditorModel) model).getViewComposite())) {
|
||||
originalDt = newSfdt;
|
||||
}
|
||||
}
|
||||
((StackEditorModel) model).updateAndCheckChangeState();
|
||||
if (model.hasChanges()) {
|
||||
String name = ((StackEditorModel) model).getTypeName();
|
||||
// The user has modified the structure so prompt for whether or
|
||||
// not to reload the structure.
|
||||
String question =
|
||||
"The " + objectType + " \"" + dtm.getName() + "\" has been restored.\n" + "\"" +
|
||||
model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"Discard edits & reload the " + name + " Editor?";
|
||||
String title = "Reload " + name + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
|
||||
if (response != 1) {
|
||||
reload = false;
|
||||
}
|
||||
}
|
||||
if (reload) {
|
||||
cancelCellEditing();
|
||||
// TODO
|
||||
// boolean lockState = model.isLocked(); // save the lock state
|
||||
model.load(originalDt); // reload the structure
|
||||
// model.setLocked(lockState); // restore the lock state
|
||||
model.updateAndCheckChangeState();
|
||||
}
|
||||
else {
|
||||
((StackEditorModel) model).refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.compositeeditor.CompositeEditorPanel#dispose()
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
removeFocusListeners(localSizeField);
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -146,7 +146,7 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
|
||||
}
|
||||
|
||||
private void refreshName() {
|
||||
StackFrameDataType origDt = (StackFrameDataType) stackModel.getOriginalComposite();
|
||||
StackFrameDataType origDt = stackModel.getOriginalComposite();
|
||||
StackFrameDataType viewDt = stackModel.getViewComposite();
|
||||
String oldName = origDt.getName();
|
||||
String newName = function.getName();
|
||||
@ -219,12 +219,13 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
|
||||
}
|
||||
break;
|
||||
case SYMBOL_PRIMARY_STATE_CHANGED:
|
||||
sym = (Symbol) ((ProgramChangeRecord) rec).getObject();
|
||||
sym = (Symbol) ((ProgramChangeRecord) rec).getNewValue();
|
||||
symType = sym.getSymbolType();
|
||||
if (symType == SymbolType.LABEL &&
|
||||
sym.getAddress().equals(function.getEntryPoint())) {
|
||||
refreshName();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -29,6 +29,8 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest {
|
||||
|
||||
// Editor Actions
|
||||
ApplyAction applyAction;
|
||||
UndoChangeAction undoAction;
|
||||
RedoChangeAction redoAction;
|
||||
ArrayAction arrayAction;
|
||||
ClearAction clearAction;
|
||||
CreateInternalStructureAction createInternalStructureAction;
|
||||
@ -46,6 +48,7 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest {
|
||||
InsertUndefinedAction insertUndefinedAction;
|
||||
HexNumbersAction hexNumbersAction;
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
clearActions();
|
||||
@ -100,6 +103,8 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest {
|
||||
favorites.clear();
|
||||
cycles.clear();
|
||||
applyAction = null;
|
||||
undoAction = null;
|
||||
redoAction = null;
|
||||
arrayAction = null;
|
||||
clearAction = null;
|
||||
createInternalStructureAction = null;
|
||||
@ -130,6 +135,12 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest {
|
||||
else if (action instanceof ApplyAction) {
|
||||
applyAction = (ApplyAction) action;
|
||||
}
|
||||
else if (action instanceof UndoChangeAction) {
|
||||
undoAction = (UndoChangeAction) action;
|
||||
}
|
||||
else if (action instanceof RedoChangeAction) {
|
||||
redoAction = (RedoChangeAction) action;
|
||||
}
|
||||
else if (action instanceof ArrayAction) {
|
||||
arrayAction = (ArrayAction) action;
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -23,7 +23,6 @@ import javax.swing.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Library;
|
||||
@ -126,6 +125,8 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest {
|
||||
addDataType(new FloatDataType());
|
||||
addDataType(arrayDt);
|
||||
|
||||
structureModel.viewDTM.clearUndo();
|
||||
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(Arrays.equals(new int[] { 0 }, model.getSelectedRows()));
|
||||
@ -807,11 +808,7 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest {
|
||||
if (packingButton.isSelected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pressButton(packingButton, false);
|
||||
OptionDialog confirmDialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(confirmDialog, "Yes");
|
||||
waitForSwing();
|
||||
pressButton(packingButton, true);
|
||||
}
|
||||
|
||||
private void checkRow(int rowIndex, int offset, int length, String mnemonic, DataType dataType,
|
||||
@ -829,6 +826,7 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest {
|
||||
}
|
||||
|
||||
private DataTypeComponent addDataType(DataType dataType) {
|
||||
return structureModel.viewComposite.add(dataType);
|
||||
return structureModel.viewDTM.withTransaction("Add Test Component",
|
||||
() -> structureModel.viewComposite.add(dataType));
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -22,6 +22,8 @@ import javax.swing.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.program.database.DatabaseObject;
|
||||
import ghidra.program.database.data.StructureDBTest;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTest {
|
||||
@ -305,12 +307,17 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes
|
||||
}
|
||||
|
||||
private DataTypeComponent addDataType(DataType dataType) {
|
||||
return structureModel.viewComposite.add(dataType);
|
||||
return structureModel.viewDTM.withTransaction("Add Test Component",
|
||||
() -> structureModel.viewComposite.add(dataType));
|
||||
}
|
||||
|
||||
private DataTypeComponent addFlexDataType(Structure struct, DataType dataType, String name,
|
||||
String comment) {
|
||||
ArrayDataType a = new ArrayDataType(dataType, 0, 1);
|
||||
if (struct instanceof DatabaseObject) {
|
||||
DataTypeManager dtm = struct.getDataTypeManager();
|
||||
return dtm.withTransaction("Add Flex Array", () -> struct.add(a, name, comment));
|
||||
}
|
||||
return struct.add(a, name, comment);
|
||||
}
|
||||
|
||||
@ -319,11 +326,7 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes
|
||||
if (packingButton.isSelected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pressButton(packingButton, false);
|
||||
OptionDialog confirmDialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(confirmDialog, "Yes");
|
||||
waitForSwing();
|
||||
pressButton(packingButton, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -45,12 +45,7 @@ public class StructureEditorLockedActions1Test extends AbstractStructureEditorTe
|
||||
public void testArrayOnSelectionExtraUndefineds() throws Exception {
|
||||
init(simpleStructure, pgmBbCat);
|
||||
runSwing(() -> {
|
||||
try {
|
||||
model.clearComponents(new int[] { 4, 5 });
|
||||
}
|
||||
catch (UsrException e) {
|
||||
failWithException("Unexpected error", e);
|
||||
}
|
||||
model.clearComponents(new int[] { 4, 5 });
|
||||
});
|
||||
setSelection(new int[] { 3, 4, 5, 6, 7, 8, 9, 10 });// starts with DWord
|
||||
DataType dt3 = getDataType(3);
|
||||
@ -103,12 +98,7 @@ public class StructureEditorLockedActions1Test extends AbstractStructureEditorTe
|
||||
public void testCreateCycleOnPointer() throws Exception {
|
||||
init(simpleStructure, pgmBbCat);
|
||||
runSwing(() -> {
|
||||
try {
|
||||
model.clearComponents(new int[] { 2, 3 });
|
||||
}
|
||||
catch (UsrException e) {
|
||||
failWithException("Unexpected error", e);
|
||||
}
|
||||
model.clearComponents(new int[] { 2, 3 });
|
||||
});
|
||||
setSelection(new int[] { 1 });
|
||||
invoke(pointerAction);
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -596,7 +596,7 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
|
||||
|
||||
int numComps = model.getNumComponents();
|
||||
int len = model.getLength();
|
||||
|
||||
|
||||
assertEquals(87, complexStructure.getComponent(16).getDataType().getLength());
|
||||
assertEquals(29, complexStructure.getComponent(19).getDataType().getLength());
|
||||
assertEquals(24, complexStructure.getComponent(20).getDataType().getLength());
|
||||
@ -675,17 +675,14 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
|
||||
|
||||
programDTM.replaceDataType(complexStructure, newComplexStructure, true);
|
||||
waitForSwing();
|
||||
DataType origCopy = newComplexStructure.clone(null);
|
||||
|
||||
// Verify the Reload Structure Editor? dialog is displayed.
|
||||
dialog = waitForWindow("Reload Structure Editor?");
|
||||
dialog = waitForWindow("Close Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "Yes");
|
||||
dialog.dispose();
|
||||
dialog = null;
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(((Structure) origCopy).getNumComponents(), model.getNumComponents());
|
||||
assertTrue(origCopy.isEquivalent(model.viewComposite));
|
||||
assertFalse(provider.isVisible());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -708,10 +705,12 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
|
||||
waitForSwing();
|
||||
|
||||
// Verify the Reload Structure Editor? dialog is displayed.
|
||||
Window dialog = waitForWindow("Reload Structure Editor?");
|
||||
Window dialog = waitForWindow("Close Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "No");
|
||||
dialog.dispose();
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(provider.isVisible());
|
||||
|
||||
assertEquals(((Structure) viewCopy).getNumComponents(), model.getNumComponents());
|
||||
assertTrue(viewCopy.isEquivalent(model.viewComposite));
|
||||
@ -728,8 +727,14 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
|
||||
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
programDTM.replaceDataType(complexStructure, newComplexStructure, true);
|
||||
|
||||
// Verify Structure Editor closes (we don't want two editors for the same type)
|
||||
Window dialog = waitForWindow("Closing Structure Editor");
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForSwing();
|
||||
assertTrue(newComplexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
assertFalse(provider.isVisible());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -36,7 +36,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
|
||||
@Override
|
||||
protected void init(Structure dt, final Category cat, final boolean showInHex) {
|
||||
boolean commit = true;
|
||||
startTransaction("Structure Editor Test Initialization");
|
||||
try {
|
||||
DataTypeManager dataTypeManager = cat.getDataTypeManager();
|
||||
@ -49,13 +48,12 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
dt.setCategoryPath(categoryPath);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
commit = false;
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
endTransaction(commit);
|
||||
endTransaction(true);
|
||||
}
|
||||
final Structure structDt = dt;
|
||||
runSwing(() -> {
|
||||
@ -67,364 +65,249 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
getActions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataTypeChanged() throws Exception {
|
||||
try {
|
||||
txId = program.startTransaction("Grow DataType");
|
||||
complexStructure.insert(22, DataType.DEFAULT);
|
||||
complexStructure.insert(22, DataType.DEFAULT);
|
||||
complexStructure.insert(22, DataType.DEFAULT);
|
||||
complexStructure.insert(22, DataType.DEFAULT);
|
||||
complexStructure.insert(22, DataType.DEFAULT);
|
||||
complexStructure.insert(22, DataType.DEFAULT);
|
||||
assertEquals(87, complexStructure.getComponent(16).getDataType().getLength());
|
||||
assertEquals(29, complexStructure.getComponent(19).getDataType().getLength());
|
||||
assertEquals(29, complexStructure.getComponent(21).getDataType().getLength());
|
||||
assertEquals(87, complexStructure.getComponent(16).getLength());
|
||||
assertEquals(29, complexStructure.getComponent(19).getLength());
|
||||
assertEquals(29, complexStructure.getComponent(21).getLength());
|
||||
assertEquals(1, complexStructure.getComponent(22).getLength());
|
||||
assertEquals(4, complexStructure.getComponent(28).getLength());
|
||||
assertEquals(331, complexStructure.getLength());
|
||||
assertEquals(29, complexStructure.getNumComponents());
|
||||
// Change the struct. simpleStructure was 29 bytes.
|
||||
simpleStructure.add(new DWordDataType());
|
||||
assertEquals(331, complexStructure.getLength());
|
||||
assertEquals(25, complexStructure.getNumComponents());
|
||||
assertEquals(99, complexStructure.getComponent(16).getDataType().getLength());
|
||||
assertEquals(33, complexStructure.getComponent(19).getDataType().getLength());
|
||||
assertEquals(33, complexStructure.getComponent(21).getDataType().getLength());
|
||||
assertEquals(87, complexStructure.getComponent(16).getLength());
|
||||
assertEquals(29, complexStructure.getComponent(19).getLength());
|
||||
assertEquals(33, complexStructure.getComponent(21).getLength());
|
||||
assertEquals(1, complexStructure.getComponent(22).getLength());
|
||||
assertEquals(4, complexStructure.getComponent(24).getLength());
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(txId, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testModifiedDtAndProgramRestored() throws Exception {
|
||||
Window dialog;
|
||||
try {
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
setSelection(new int[] { 4, 5 });
|
||||
deleteAction.actionPerformed(new DefaultActionContext());
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
setSelection(new int[] { 4, 5 });
|
||||
deleteAction.actionPerformed(new DefaultActionContext());
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Change the structure again.
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
setSelection(new int[] { 1 });
|
||||
clearAction.actionPerformed(new DefaultActionContext());
|
||||
deleteAction.actionPerformed(new DefaultActionContext());// Must be undefined before it can delete.
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Change the structure again.
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
setSelection(new int[] { 1 });
|
||||
clearAction.actionPerformed(new DefaultActionContext());
|
||||
deleteAction.actionPerformed(new DefaultActionContext());// Must be undefined before it can delete.
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
// Verify the Reload Structure Editor? dialog is displayed.
|
||||
dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "No");
|
||||
dialog.dispose();
|
||||
dialog = null;
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
// Verify the Reload Structure Editor? dialog is displayed.
|
||||
Window dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "No");
|
||||
waitForSwing();
|
||||
|
||||
// Redo the apply
|
||||
redo(program, false);
|
||||
// Verify the Reload Structure Editor? dialog is displayed.
|
||||
dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "No");
|
||||
dialog.dispose();
|
||||
dialog = null;
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
}
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Redo the apply
|
||||
redo(program, false);
|
||||
|
||||
// Verify the Reload Structure Editor? dialog is displayed.
|
||||
dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "No");
|
||||
waitForSwing();
|
||||
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
}
|
||||
|
||||
// Test add a structure, start to edit it, and then undo the program so it goes away.
|
||||
// This should close the edit session.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedDt() throws Exception {
|
||||
Window dialog;
|
||||
|
||||
Structure s1 = new StructureDataType("s1", 0);
|
||||
s1.add(new ByteDataType());
|
||||
Structure s2 = new StructureDataType("s2", 0);
|
||||
s2.add(new WordDataType());
|
||||
s1.add(s2);
|
||||
|
||||
// Add the s1 data type so that we can undo its add.
|
||||
Structure s1Struct = null;
|
||||
startTransaction("Structure Editor Test Initialization");
|
||||
try {
|
||||
Structure s1 = new StructureDataType("s1", 0);
|
||||
s1.add(new ByteDataType());
|
||||
Structure s2 = new StructureDataType("s2", 0);
|
||||
s2.add(new WordDataType());
|
||||
s1.add(s2);
|
||||
|
||||
// Add the s1 data type so that we can undo its add.
|
||||
boolean commit = true;
|
||||
Structure s1Struct = null;
|
||||
startTransaction("Structure Editor Test Initialization");
|
||||
try {
|
||||
s1Struct =
|
||||
(Structure) pgmTestCat.addDataType(s1, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
}
|
||||
finally {
|
||||
endTransaction(commit);
|
||||
}
|
||||
assertNotNull(s1Struct);
|
||||
final Structure myS1Structure = s1Struct;
|
||||
|
||||
init(myS1Structure, pgmTestCat, false);
|
||||
|
||||
// Change the structure.
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
// Select blank line after components.
|
||||
setSelection(new int[] { myS1Structure.getNumComponents() });
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Verify the editor provider is displayed.
|
||||
String mySubTitle = getProviderSubTitle(myS1Structure);
|
||||
assertTrue("Couldn't find editor = " + mySubTitle,
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
|
||||
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
waitForSwing();
|
||||
|
||||
// Verify the "Reload Structure Editor?" dialog is NOT displayed.
|
||||
dialog = getWindow("Reload Structure Editor?");
|
||||
assertNull(dialog);
|
||||
|
||||
// Verify the editor provider is gone.
|
||||
assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
|
||||
s1Struct =
|
||||
(Structure) pgmTestCat.addDataType(s1, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
endTransaction(true);
|
||||
}
|
||||
assertNotNull(s1Struct);
|
||||
final Structure myS1Structure = s1Struct;
|
||||
|
||||
init(myS1Structure, pgmTestCat, false);
|
||||
|
||||
// Change the structure.
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
// Select blank line after components.
|
||||
setSelection(new int[] { myS1Structure.getNumComponents() });
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Verify the editor provider is displayed.
|
||||
String mySubTitle = getProviderSubTitle(myS1Structure);
|
||||
assertTrue("Couldn't find editor = " + mySubTitle,
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
|
||||
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
|
||||
Window dialog = waitForWindow("Close Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
// Verify the editor provider is gone.
|
||||
assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
|
||||
}
|
||||
|
||||
// Test add a structure containing inner struct, start to edit inner struct, and then undo the
|
||||
// program so it goes away. This should close the edit session.
|
||||
// program so it goes away.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedDtComp() throws Exception {
|
||||
Window dialog;
|
||||
Structure s1 = new StructureDataType("s1", 0);
|
||||
s1.setCategoryPath(pgmTestCat.getCategoryPath());
|
||||
s1.add(new ByteDataType());
|
||||
Structure s2 = new StructureDataType("s2", 0);
|
||||
s2.setCategoryPath(pgmTestCat.getCategoryPath());
|
||||
s2.add(new WordDataType());
|
||||
s1.add(s2);
|
||||
|
||||
// Add the s1 data type so that we can undo its add.
|
||||
Structure s1Struct = null;
|
||||
startTransaction("Resolve s1");
|
||||
try {
|
||||
Structure s1 = new StructureDataType("s1", 0);
|
||||
s1.setCategoryPath(pgmTestCat.getCategoryPath());
|
||||
s1.add(new ByteDataType());
|
||||
Structure s2 = new StructureDataType("s2", 0);
|
||||
s2.setCategoryPath(pgmTestCat.getCategoryPath());
|
||||
s2.add(new WordDataType());
|
||||
s1.add(s2);
|
||||
|
||||
// Add the s1 data type so that we can undo its add.
|
||||
boolean commit = true;
|
||||
Structure s1Struct = null;
|
||||
startTransaction("Structure Editor Test Initialization");
|
||||
try {
|
||||
s1Struct =
|
||||
(Structure) pgmTestCat.addDataType(s1, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
}
|
||||
finally {
|
||||
endTransaction(commit);
|
||||
}
|
||||
assertNotNull(s1Struct);
|
||||
final Structure myS2Structure = (Structure) s1Struct.getComponent(1).getDataType();
|
||||
assertNotNull(myS2Structure);
|
||||
assertTrue(s2.isEquivalent(myS2Structure));
|
||||
|
||||
init(myS2Structure, pgmTestCat, false);
|
||||
|
||||
// Change the structure.
|
||||
runSwing(() -> {
|
||||
getTable().requestFocus();
|
||||
// Select blank line after components.
|
||||
setSelection(new int[] { myS2Structure.getNumComponents() });
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
}, false);
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Verify the editor provider is displayed.
|
||||
String mySubTitle = getProviderSubTitle(myS2Structure);
|
||||
assertTrue("Couldn't find editor = " + mySubTitle,
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
|
||||
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
waitForSwing();
|
||||
|
||||
// Verify the "Reload Structure Editor?" dialog is NOT displayed.
|
||||
dialog = getWindow("Reload Structure Editor?");
|
||||
assertNull(dialog);
|
||||
|
||||
// Verify the editor provider is gone.
|
||||
assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
|
||||
s1Struct =
|
||||
(Structure) pgmTestCat.addDataType(s1, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
endTransaction(true);
|
||||
}
|
||||
assertNotNull(s1Struct);
|
||||
final Structure myS2Structure = (Structure) s1Struct.getComponent(1).getDataType();
|
||||
assertNotNull(myS2Structure);
|
||||
assertTrue(s2.isEquivalent(myS2Structure));
|
||||
|
||||
Structure editStruct = s1Struct;
|
||||
init(editStruct, pgmTestCat, false);
|
||||
|
||||
// Change the structure.
|
||||
|
||||
runSwing(() -> {
|
||||
getTable().requestFocus();
|
||||
// Select blank line after components.
|
||||
setSelection(new int[] { editStruct.getNumComponents() });
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
}, false);
|
||||
waitForSwing();
|
||||
assertFalse(s1.isEquivalent(model.viewComposite));
|
||||
|
||||
// Verify the editor provider is displayed.
|
||||
String mySubTitle = getProviderSubTitle(editStruct);
|
||||
assertTrue("Couldn't find editor = " + mySubTitle,
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
|
||||
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
waitForSwing();
|
||||
|
||||
Window dialog = waitForWindow("Close Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
// Verify the editor provider is gone.
|
||||
assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", mySubTitle));
|
||||
}
|
||||
|
||||
// Test add a structure, start to edit a structure that contains it, and then undo the program
|
||||
// so it goes away. The editor stays since the structure existed previously, but editor reloads.
|
||||
// so it goes away. The editor stays since the structure existed previously and has been modified.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedComponentDtYes() throws Exception {
|
||||
Window dialog;
|
||||
public void testProgramRestoreRemovesEditedComponentDt() throws Exception {
|
||||
|
||||
Structure myStruct = new StructureDataType("myStruct", 0);
|
||||
myStruct.add(new WordDataType());
|
||||
|
||||
init(emptyStructure, pgmTestCat, false);
|
||||
|
||||
// Add the data type so that we can undo its add.
|
||||
Structure pgmMyStruct = null;
|
||||
startTransaction("Structure Editor Test Initialization");
|
||||
try {
|
||||
Structure myStruct = new StructureDataType("myStruct", 0);
|
||||
myStruct.add(new WordDataType());
|
||||
|
||||
init(emptyStructure, pgmTestCat, false);
|
||||
|
||||
// Add the data type so that we can undo its add.
|
||||
boolean commit = true;
|
||||
Structure pgmMyStruct = null;
|
||||
startTransaction("Structure Editor Test Initialization");
|
||||
try {
|
||||
pgmMyStruct = (Structure) pgmTestCat.addDataType(myStruct,
|
||||
DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
}
|
||||
finally {
|
||||
endTransaction(commit);
|
||||
}
|
||||
assertNotNull(pgmMyStruct);
|
||||
final Structure myStructure = pgmMyStruct;
|
||||
|
||||
// Change the structure.
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
// Select blank line after components.
|
||||
setSelection(new int[] { emptyStructure.getNumComponents() });
|
||||
try {
|
||||
model.add(myStructure);
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(emptyStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Verify the editor provider is displayed.
|
||||
String mySubTitle = getProviderSubTitle(emptyStructure);
|
||||
assertTrue("Couldn't find editor = " + mySubTitle,
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
|
||||
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
|
||||
// Verify the Reload Structure Editor? dialog is displayed.
|
||||
dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
dialog.dispose();
|
||||
dialog = null;
|
||||
assertTrue(emptyStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Verify the editor provider is reloaded.
|
||||
assertTrue(
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
|
||||
pgmMyStruct = (Structure) pgmTestCat.addDataType(myStruct,
|
||||
DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
endTransaction(true);
|
||||
}
|
||||
}
|
||||
assertNotNull(pgmMyStruct);
|
||||
final Structure myStructure = pgmMyStruct;
|
||||
|
||||
// Test add a structure, start to edit a structure that contains it, and then undo the program
|
||||
// so it goes away. The editor stays since the structure existed previously, but doesn't reload.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedComponentDtNo() throws Exception {
|
||||
Window dialog;
|
||||
try {
|
||||
Structure myStruct = new StructureDataType("myStruct", 0);
|
||||
myStruct.add(new WordDataType());
|
||||
|
||||
init(emptyStructure, pgmTestCat, false);
|
||||
|
||||
// Add the data type so that we can undo its add.
|
||||
boolean commit = true;
|
||||
Structure pgmMyStruct = null;
|
||||
startTransaction("Structure Editor Test Initialization");
|
||||
// Change the structure.
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
// Select blank line after components.
|
||||
setSelection(new int[] { emptyStructure.getNumComponents() });
|
||||
try {
|
||||
pgmMyStruct = (Structure) pgmTestCat.addDataType(myStruct,
|
||||
DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
model.add(myStructure);
|
||||
}
|
||||
finally {
|
||||
endTransaction(commit);
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
assertNotNull(pgmMyStruct);
|
||||
final Structure myStructure = pgmMyStruct;
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(emptyStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Change the structure.
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
// Select blank line after components.
|
||||
setSelection(new int[] { emptyStructure.getNumComponents() });
|
||||
try {
|
||||
model.add(myStructure);
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(emptyStructure.isEquivalent(model.viewComposite));
|
||||
// Verify the editor provider is displayed.
|
||||
String mySubTitle = getProviderSubTitle(emptyStructure);
|
||||
assertTrue("Couldn't find editor = " + mySubTitle,
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
|
||||
|
||||
// Verify the editor provider is displayed.
|
||||
String mySubTitle = getProviderSubTitle(emptyStructure);
|
||||
assertTrue("Couldn't find editor = " + mySubTitle,
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
|
||||
DataType dtCopy = model.viewComposite.copy(model.viewDTM);
|
||||
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
// Verify the Reload Structure Editor? dialog is displayed.
|
||||
dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "No");
|
||||
dialog.dispose();
|
||||
dialog = null;
|
||||
assertFalse(emptyStructure.isEquivalent(model.viewComposite));
|
||||
// Undo the apply
|
||||
undo(program, false);
|
||||
waitForSwing();
|
||||
|
||||
// Verify the editor provider is still on screen.
|
||||
assertTrue(
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
}
|
||||
assertTrue(pgmMyStruct.isDeleted());
|
||||
|
||||
// Verify the a reload/close dialog is not displayed.
|
||||
Window dialog = getWindow("Reload Structure Editor?");
|
||||
assertNull(dialog);
|
||||
dialog = getWindow("Close Structure Editor?");
|
||||
assertNull(dialog);
|
||||
|
||||
assertTrue(
|
||||
isProviderShown(tool.getToolFrame(), "Structure Editor", "emptyStructure (Test)"));
|
||||
|
||||
// Verify the editor provider remains visible with myStructure use cleared.
|
||||
assertFalse(emptyStructure.isEquivalent(model.viewComposite));
|
||||
assertFalse(dtCopy.isEquivalent(model.viewComposite));
|
||||
assertEquals(dtCopy.getLength(), model.viewComposite.getLength());
|
||||
assertEquals(2, model.viewComposite.getNumComponents());
|
||||
assertEquals(0, model.viewComposite.getNumDefinedComponents());
|
||||
}
|
||||
|
||||
// Test Undo / Redo of program.
|
||||
@ -446,14 +329,29 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
|
||||
Window dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
|
||||
dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
}
|
||||
|
||||
@ -569,8 +467,7 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
dialog = waitForWindow("Save Structure Editor Changes?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Cancel");
|
||||
dialog.dispose();
|
||||
dialog = null;
|
||||
|
||||
assertTrue(tool.isVisible(provider));
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
assertTrue(newDt.isEquivalent(model.viewComposite));
|
||||
@ -706,8 +603,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
assertTrue(tool.isVisible(provider));
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
|
||||
Composite dt = model.viewComposite;
|
||||
|
||||
// set selected row
|
||||
int row = 2;
|
||||
setSelection(new int[] { row });
|
||||
@ -716,7 +611,7 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
int editRow = 4; // offset 8; 'simpleUnion'
|
||||
String newFieldName = "newFieldName";
|
||||
tx(program, () -> {
|
||||
DataTypeComponent dtc = dt.getComponent(editRow);
|
||||
DataTypeComponent dtc = complexStructure.getComponent(editRow);
|
||||
dtc.setFieldName(newFieldName);
|
||||
});
|
||||
|
||||
@ -728,7 +623,8 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
assertEquals(1, rows.length);
|
||||
assertEquals(row, rows[0]);
|
||||
|
||||
closeProviderIgnoringChanges();
|
||||
// External change should not register as unsved change to model
|
||||
assertFalse(model.hasChanges());
|
||||
}
|
||||
|
||||
private void closeProviderIgnoringChanges() {
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -94,31 +94,84 @@ public class StructureEditorUnlockedActions5Test extends AbstractStructureEditor
|
||||
// model.getStatus());
|
||||
}
|
||||
|
||||
// Ignoring test for now. Don't know how to make the name invalid
|
||||
@Test
|
||||
public void testApplyWithInvalidName() throws Exception {
|
||||
init(complexStructure, pgmTestCat);
|
||||
|
||||
CompEditorPanel panel = (CompEditorPanel) getPanel();
|
||||
JTextField nameField = panel.nameTextField;
|
||||
assertTrue(model.isValidName());
|
||||
triggerActionKey(nameField, 0, KeyEvent.VK_END);
|
||||
triggerText(nameField, "#$/");
|
||||
DataType viewCopy = model.viewComposite.clone(null);
|
||||
|
||||
assertTrue(!model.isValidName());
|
||||
assertEquals("complexStructure#$/", nameField.getText());
|
||||
assertEquals("complexStructure#$/", model.getCompositeName());
|
||||
assertEquals("complexStructure", complexStructure.getName());
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
assertTrue(viewCopy.isEquivalent(model.viewComposite));
|
||||
assertEquals(model.getStatus(), "complexStructure#$/ is not a valid name.");
|
||||
invoke(applyAction);
|
||||
assertEquals(model.getStatus(), "Name is not valid.");
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
assertTrue(viewCopy.isEquivalent(model.viewComposite));
|
||||
assertTrue(model.getStatus().length() > 0);
|
||||
assertEquals("complexStructure#$/", model.getCompositeName());
|
||||
assertEquals("complexStructure", complexStructure.getName());
|
||||
CompEditorPanel panel = (CompEditorPanel) getPanel();
|
||||
assertFalse(panel.hasInvalidEntry());
|
||||
assertFalse(panel.hasUncomittedEntry());
|
||||
|
||||
JTextField nameField = panel.nameTextField;
|
||||
nameField.setText(null);
|
||||
triggerActionKey(nameField, 0, KeyEvent.VK_END);
|
||||
triggerText(nameField, " ");
|
||||
|
||||
assertTrue(panel.hasInvalidEntry());
|
||||
assertFalse(applyAction.isEnabled());
|
||||
assertFalse(applyAction.isEnabled());
|
||||
|
||||
assertEquals("complexStructure", model.getCompositeName()); // no change yet
|
||||
|
||||
triggerActionKey(nameField, 0, KeyEvent.VK_DELETE);
|
||||
triggerText(nameField, "xyz");
|
||||
|
||||
assertFalse(panel.hasInvalidEntry());
|
||||
assertTrue(panel.hasUncomittedEntry());
|
||||
assertFalse(applyAction.isEnabled());
|
||||
assertFalse(applyAction.isEnabled());
|
||||
|
||||
assertEquals("complexStructure", model.getCompositeName()); // no change yet
|
||||
|
||||
triggerActionKey(nameField, 0, KeyEvent.VK_ENTER);
|
||||
|
||||
assertFalse(panel.hasInvalidEntry());
|
||||
assertFalse(panel.hasUncomittedEntry());
|
||||
assertTrue(applyAction.isEnabled());
|
||||
assertTrue(applyAction.isEnabled());
|
||||
|
||||
assertTrue(model.isValidName());
|
||||
|
||||
assertEquals("xyz", model.getCompositeName()); // no change yet
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUncomittedNameRevert() throws Exception {
|
||||
init(complexStructure, pgmTestCat);
|
||||
|
||||
assertTrue(model.isValidName());
|
||||
|
||||
CompEditorPanel panel = (CompEditorPanel) getPanel();
|
||||
assertFalse(panel.hasInvalidEntry());
|
||||
assertFalse(panel.hasUncomittedEntry());
|
||||
|
||||
JTextField nameField = panel.nameTextField;
|
||||
nameField.setText(null);
|
||||
triggerActionKey(nameField, 0, KeyEvent.VK_END);
|
||||
triggerText(nameField, "xyz");
|
||||
assertEquals("xyz", nameField.getText());
|
||||
|
||||
assertFalse(panel.hasInvalidEntry());
|
||||
assertTrue(panel.hasUncomittedEntry());
|
||||
assertFalse(applyAction.isEnabled());
|
||||
assertFalse(applyAction.isEnabled());
|
||||
|
||||
assertEquals("complexStructure", model.getCompositeName()); // no change yet
|
||||
|
||||
triggerActionKey(nameField, 0, KeyEvent.VK_ESCAPE);
|
||||
|
||||
assertEquals("complexStructure", nameField.getText());
|
||||
|
||||
assertFalse(panel.hasInvalidEntry());
|
||||
assertFalse(panel.hasUncomittedEntry());
|
||||
assertFalse(applyAction.isEnabled());
|
||||
assertFalse(applyAction.isEnabled());
|
||||
|
||||
assertTrue(model.isValidName());
|
||||
|
||||
assertEquals("complexStructure", model.getCompositeName()); // no change yet
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -606,24 +659,53 @@ public class StructureEditorUnlockedActions5Test extends AbstractStructureEditor
|
||||
|
||||
CompEditorPanel panel = (CompEditorPanel) getPanel();
|
||||
JTextField nameField = panel.nameTextField;
|
||||
runSwing(() -> nameField.setText("myStruct"));
|
||||
|
||||
setText(nameField, "myStruct");
|
||||
triggerEnter(nameField);
|
||||
|
||||
assertEquals("myStruct", nameField.getText());
|
||||
assertEquals("myStruct", model.getCompositeName());
|
||||
|
||||
invoke(applyAction);
|
||||
runSwing(() -> nameField.setText("myStruct2"));
|
||||
invoke(applyAction);
|
||||
undo(program, false);
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
runSwing(() -> provider.dataTypeManagerRestored(), true);
|
||||
|
||||
waitForSwing();
|
||||
|
||||
assertEquals("myStruct", nameField.getText());
|
||||
assertEquals("myStruct", model.getCompositeName());
|
||||
redo(program, false);
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
runSwing(() -> provider.dataTypeManagerRestored(), true);
|
||||
waitForSwing();
|
||||
|
||||
setText(nameField, "myStruct2");
|
||||
triggerEnter(nameField);
|
||||
|
||||
assertEquals("myStruct2", nameField.getText());
|
||||
assertEquals("myStruct2", model.getCompositeName());
|
||||
|
||||
invoke(applyAction);
|
||||
|
||||
waitForSwing();
|
||||
|
||||
assertEquals("myStruct2", nameField.getText());
|
||||
assertEquals("myStruct2", model.getCompositeName());
|
||||
|
||||
undo(program, true);
|
||||
|
||||
Window dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
assertEquals("myStruct", nameField.getText());
|
||||
assertEquals("myStruct", model.getCompositeName());
|
||||
|
||||
redo(program, true);
|
||||
|
||||
dialog = waitForWindow("Reload Structure Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "No");
|
||||
waitForSwing();
|
||||
|
||||
assertEquals("myStruct", nameField.getText());
|
||||
assertEquals("myStruct", model.getCompositeName());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -253,8 +253,9 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||
throws ArrayIndexOutOfBoundsException, InvalidDataTypeException {
|
||||
init(complexStructure, pgmBbCat);
|
||||
|
||||
((Structure) structureModel.viewComposite).insertBitField(2, 1, 4, CharDataType.dataType, 2,
|
||||
"bf1", null);
|
||||
structureModel.viewDTM.withTransaction("Add Bitfield",
|
||||
() -> ((Structure) structureModel.viewComposite).insertBitField(2, 1, 4,
|
||||
CharDataType.dataType, 2, "bf1", null));
|
||||
|
||||
setSelection(new int[] { 2 });
|
||||
assertEquals("char:2", getDataType(2).getDisplayName());
|
||||
@ -294,8 +295,9 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
|
||||
throws ArrayIndexOutOfBoundsException, InvalidDataTypeException {
|
||||
init(complexStructure, pgmBbCat);
|
||||
|
||||
((Structure) structureModel.viewComposite).insertBitField(2, 1, 4, CharDataType.dataType, 2,
|
||||
"bf1", null);
|
||||
structureModel.viewDTM.withTransaction("Add Bitfield",
|
||||
() -> ((Structure) structureModel.viewComposite).insertBitField(2, 1, 4,
|
||||
CharDataType.dataType, 2, "bf1", null));
|
||||
|
||||
setSelection(new int[] { 2 });
|
||||
assertEquals("char:2", getDataType(2).getDisplayName());
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -27,7 +27,7 @@ import ghidra.program.model.data.*;
|
||||
public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
|
||||
@Test
|
||||
public void testUnalignedUnion() {
|
||||
public void testUnalignedUnion() {
|
||||
init(emptyUnion, pgmRootCat, false);
|
||||
|
||||
assertTrue(unionModel.hasChanges());// empty union that hasn't been saved yet.
|
||||
@ -49,17 +49,14 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
|
||||
// Check enablement for empty table with modified state.
|
||||
CompositeEditorTableAction[] pActions = provider.getActions();
|
||||
for (int i = 0; i < pActions.length; i++) {
|
||||
if ((pActions[i] instanceof FavoritesAction) ||
|
||||
(pActions[i] instanceof CycleGroupAction) ||
|
||||
(pActions[i] instanceof EditFieldAction) ||
|
||||
(pActions[i] instanceof PointerAction) ||
|
||||
(pActions[i] instanceof HexNumbersAction) ||
|
||||
(pActions[i] instanceof ApplyAction)) {
|
||||
checkEnablement(pActions[i], true);
|
||||
for (CompositeEditorTableAction pAction : pActions) {
|
||||
if ((pAction instanceof FavoritesAction) || (pAction instanceof CycleGroupAction) ||
|
||||
(pAction instanceof EditFieldAction) || (pAction instanceof PointerAction) ||
|
||||
(pAction instanceof HexNumbersAction) || (pAction instanceof ApplyAction)) {
|
||||
checkEnablement(pAction, true);
|
||||
}
|
||||
else {
|
||||
checkEnablement(pActions[i], false);
|
||||
checkEnablement(pAction, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +75,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultAlignedUnion() throws Exception {
|
||||
public void testDefaultAlignedUnion() throws Exception {
|
||||
init(emptyUnion, pgmRootCat, false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -101,34 +98,36 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnablementDefaultAlignedUnion() throws Exception {
|
||||
public void testEnablementDefaultAlignedUnion() throws Exception {
|
||||
emptyUnion.setPackingEnabled(true);
|
||||
init(emptyUnion, pgmRootCat, false);
|
||||
|
||||
CompositeEditorTableAction undoAction =
|
||||
provider.actionMgr.getNamedAction("Undo Editor Change");
|
||||
assertNotNull(undoAction);
|
||||
checkEnablement(undoAction, false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
addDataType(new ByteDataType());
|
||||
addDataType(new FloatDataType());
|
||||
addDataType(arrayDt);
|
||||
|
||||
// Undo should be enabled
|
||||
|
||||
// Check enablement.
|
||||
CompositeEditorTableAction[] pActions = provider.getActions();
|
||||
for (int i = 0; i < pActions.length; i++) {
|
||||
if ((pActions[i] instanceof FavoritesAction) ||
|
||||
(pActions[i] instanceof CycleGroupAction) ||
|
||||
(pActions[i] instanceof EditFieldAction) ||
|
||||
(pActions[i] instanceof PointerAction) ||
|
||||
(pActions[i] instanceof HexNumbersAction) ||
|
||||
(pActions[i] instanceof MoveDownAction) ||
|
||||
(pActions[i] instanceof DuplicateAction) ||
|
||||
(pActions[i] instanceof DuplicateMultipleAction) ||
|
||||
(pActions[i] instanceof DeleteAction) ||
|
||||
(pActions[i] instanceof ArrayAction) ||
|
||||
(pActions[i] instanceof ShowComponentPathAction) ||
|
||||
(pActions[i] instanceof ApplyAction)) {
|
||||
checkEnablement(pActions[i], true);
|
||||
for (CompositeEditorTableAction pAction : pActions) {
|
||||
if ((pAction instanceof FavoritesAction) || (pAction instanceof CycleGroupAction) ||
|
||||
(pAction instanceof EditFieldAction) || (pAction instanceof PointerAction) ||
|
||||
(pAction instanceof HexNumbersAction) || (pAction instanceof MoveDownAction) ||
|
||||
(pAction instanceof DuplicateAction) ||
|
||||
(pAction instanceof DuplicateMultipleAction) || (pAction instanceof DeleteAction) ||
|
||||
(pAction instanceof ArrayAction) || (pAction instanceof ShowComponentPathAction) ||
|
||||
(pAction instanceof ApplyAction) || (pAction instanceof UndoChangeAction)) {
|
||||
checkEnablement(pAction, true);
|
||||
}
|
||||
else {
|
||||
checkEnablement(pActions[i], false);
|
||||
checkEnablement(pAction, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +142,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMachineAlignedUnion() throws Exception {
|
||||
public void testMachineAlignedUnion() throws Exception {
|
||||
init(emptyUnion, pgmRootCat, false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -168,7 +167,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByValueAlignedUnion() throws Exception {
|
||||
public void testByValueAlignedUnion() throws Exception {
|
||||
init(emptyUnion, pgmRootCat, false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -200,31 +199,32 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByValue1AlignedUnion() throws Exception {
|
||||
public void testByValue1AlignedUnion() throws Exception {
|
||||
checkByValueAlignedUnion(1, 4, 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByValue2AlignedUnion() throws Exception {
|
||||
public void testByValue2AlignedUnion() throws Exception {
|
||||
checkByValueAlignedUnion(2, 4, 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByValue4AlignedUnion() throws Exception {
|
||||
public void testByValue4AlignedUnion() throws Exception {
|
||||
checkByValueAlignedUnion(4, 4, 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByValue8AlignedUnion() throws Exception {
|
||||
public void testByValue8AlignedUnion() throws Exception {
|
||||
checkByValueAlignedUnion(8, 8, 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByValue16AlignedUnion() throws Exception {
|
||||
public void testByValue16AlignedUnion() throws Exception {
|
||||
checkByValueAlignedUnion(16, 16, 16);
|
||||
}
|
||||
|
||||
public void checkByValueAlignedUnion(int minAlignment, int alignment, int length) throws Exception {
|
||||
public void checkByValueAlignedUnion(int minAlignment, int alignment, int length)
|
||||
throws Exception {
|
||||
emptyUnion.setPackingEnabled(true);
|
||||
emptyUnion.setExplicitMinimumAlignment(minAlignment);
|
||||
|
||||
@ -256,7 +256,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTurnOffAlignmentInUnion() throws Exception {
|
||||
public void testTurnOffAlignmentInUnion() throws Exception {
|
||||
emptyUnion.setPackingEnabled(true);
|
||||
emptyUnion.setExplicitMinimumAlignment(8);
|
||||
|
||||
@ -304,7 +304,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertUnaligned1() throws Exception {
|
||||
public void testInsertUnaligned1() throws Exception {
|
||||
emptyUnion.setPackingEnabled(false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -337,7 +337,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertUnaligned2() throws Exception {
|
||||
public void testInsertUnaligned2() throws Exception {
|
||||
emptyUnion.setPackingEnabled(false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -370,7 +370,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertUnaligned3() throws Exception {
|
||||
public void testInsertUnaligned3() throws Exception {
|
||||
emptyUnion.setPackingEnabled(false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -403,7 +403,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceUnaligned1() throws Exception {
|
||||
public void testReplaceUnaligned1() throws Exception {
|
||||
emptyUnion.setPackingEnabled(false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -435,7 +435,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceUnaligned2() throws Exception {
|
||||
public void testReplaceUnaligned2() throws Exception {
|
||||
emptyUnion.setPackingEnabled(false);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -468,7 +468,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAligned1() throws Exception {
|
||||
public void testInsertAligned1() throws Exception {
|
||||
emptyUnion.setPackingEnabled(true);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -501,7 +501,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAligned2() throws Exception {
|
||||
public void testInsertAligned2() throws Exception {
|
||||
emptyUnion.setPackingEnabled(true);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -534,7 +534,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAligned3() throws Exception {
|
||||
public void testInsertAligned3() throws Exception {
|
||||
emptyUnion.setPackingEnabled(true);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -567,7 +567,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceAligned1() throws Exception {
|
||||
public void testReplaceAligned1() throws Exception {
|
||||
emptyUnion.setPackingEnabled(true);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -599,7 +599,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceAligned2() throws Exception {
|
||||
public void testReplaceAligned2() throws Exception {
|
||||
emptyUnion.setPackingEnabled(true);
|
||||
|
||||
DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1);
|
||||
@ -643,7 +643,8 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
|
||||
private DataTypeComponent addDataType(DataType dataType) {
|
||||
return unionModel.viewComposite.add(dataType);
|
||||
return unionModel.viewDTM.withTransaction("Add Component",
|
||||
() -> unionModel.viewComposite.add(dataType));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -79,10 +79,8 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
|
||||
assertEquals(pgmBbCat.getCategoryPathName(), model.getOriginalCategoryPath().getPath());
|
||||
pgmTestCat.moveCategory(pgmBbCat, TaskMonitor.DUMMY);
|
||||
waitForSwing();
|
||||
assertTrue(model.getOriginalCategoryPath()
|
||||
.getPath()
|
||||
.startsWith(
|
||||
pgmTestCat.getCategoryPathName()));
|
||||
assertTrue(
|
||||
model.getOriginalCategoryPath().getPath().startsWith(pgmTestCat.getCategoryPathName()));
|
||||
assertEquals(pgmBbCat.getCategoryPathName(), model.getOriginalCategoryPath().getPath());
|
||||
}
|
||||
|
||||
@ -117,8 +115,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
programDTM.remove(complexUnion, TaskMonitor.DUMMY);
|
||||
programDTM.getCategory(pgmRootCat.getCategoryPath())
|
||||
.removeCategory("Temp",
|
||||
TaskMonitor.DUMMY);
|
||||
.removeCategory("Temp", TaskMonitor.DUMMY);
|
||||
});
|
||||
|
||||
waitForSwing();
|
||||
@ -280,8 +277,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
|
||||
public void testEditedDataTypeMoved() {
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
|
||||
assertEquals(pgmTestCat.getCategoryPathName(),
|
||||
model.getOriginalCategoryPath().getPath());
|
||||
assertEquals(pgmTestCat.getCategoryPathName(), model.getOriginalCategoryPath().getPath());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
pgmAaCat.moveDataType(complexUnion, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
@ -299,9 +295,8 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
|
||||
assertEquals(21, model.getNumComponents());
|
||||
SwingUtilities.invokeLater(() -> complexUnion.getDataTypeManager()
|
||||
.remove(
|
||||
simpleStructure, TaskMonitor.DUMMY));
|
||||
SwingUtilities.invokeLater(
|
||||
() -> complexUnion.getDataTypeManager().remove(simpleStructure, TaskMonitor.DUMMY));
|
||||
waitForSwing();
|
||||
assertEquals(15, model.getNumComponents());
|
||||
}
|
||||
@ -321,9 +316,8 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
|
||||
waitForSwing();
|
||||
assertTrue(simpleUnion.isEquivalent(getDataType(0)));
|
||||
|
||||
SwingUtilities.invokeLater(() -> simpleUnion.getDataTypeManager()
|
||||
.remove(simpleUnion,
|
||||
TaskMonitor.DUMMY));
|
||||
SwingUtilities.invokeLater(
|
||||
() -> simpleUnion.getDataTypeManager().remove(simpleUnion, TaskMonitor.DUMMY));
|
||||
waitForSwing();
|
||||
assertEquals(0, model.getNumComponents());
|
||||
}
|
||||
@ -497,18 +491,14 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
|
||||
newComplexUnion.add(new CharDataType(), 1);
|
||||
|
||||
programDTM.replaceDataType(complexUnion, newComplexUnion, true);
|
||||
waitForSwing();
|
||||
DataType origCopy = newComplexUnion.clone(null);
|
||||
|
||||
// Verify the Reload Union Editor? dialog is displayed.
|
||||
Window dialog = waitForWindow("Reload Union Editor?");
|
||||
Window dialog = waitForWindow("Close Union Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "Yes");
|
||||
dialog.dispose();
|
||||
dialog = null;
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(((Union) origCopy).getNumComponents(), model.getNumComponents());
|
||||
assertTrue(origCopy.isEquivalent(model.viewComposite));
|
||||
assertFalse(provider.isVisible());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -532,14 +522,14 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
|
||||
newComplexUnion.add(new CharDataType(), 1);
|
||||
|
||||
programDTM.replaceDataType(complexUnion, newComplexUnion, true);
|
||||
waitForSwing();
|
||||
|
||||
// Verify the Reload Union Editor? dialog is displayed.
|
||||
Window dialog = waitForWindow("Reload Union Editor?");
|
||||
Window dialog = waitForWindow("Close Union Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "No");
|
||||
dialog.dispose();
|
||||
dialog = null;
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(provider.isVisible());
|
||||
|
||||
assertEquals(((Union) viewCopy).getNumComponents(), model.getNumComponents());
|
||||
assertTrue(viewCopy.isEquivalent(model.viewComposite));
|
||||
@ -556,8 +546,14 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
|
||||
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
programDTM.replaceDataType(complexUnion, newComplexUnion, true);
|
||||
|
||||
// Verify Union Editor closes (we don't want two editors for the same type)
|
||||
Window dialog = waitForWindow("Closing Union Editor");
|
||||
assertNotNull(dialog);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForSwing();
|
||||
assertTrue(newComplexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
assertFalse(provider.isVisible());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -160,14 +160,28 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
|
||||
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
|
||||
Window dialog = waitForWindow("Reload Union Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
|
||||
dialog = waitForWindow("Reload Union Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -170,7 +170,7 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
|
||||
failWithException("Editor apply failure", e);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
deleteFunction("0x200");
|
||||
|
||||
// Verify the Reload Stack Editor? dialog is not displayed.
|
||||
@ -378,7 +378,6 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
|
||||
|
||||
@Test
|
||||
public void testUndoApplyComponentChanges() throws Exception {
|
||||
Window dialog;
|
||||
|
||||
editStack(function.getEntryPoint().toString());
|
||||
|
||||
@ -404,7 +403,7 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
|
||||
|
||||
// Verify the Reload Stack Editor? dialog is displayed.
|
||||
waitForSwing();
|
||||
dialog = getWindow("Reload Stack Editor?");
|
||||
Window dialog = getWindow("Reload Stack Editor?");
|
||||
assertNull(dialog);
|
||||
sv = stack.getVariableContaining(-0x8);
|
||||
assertNotNull(sv);
|
||||
@ -428,41 +427,60 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoNewDtComponent() throws Exception {
|
||||
public void testUndoNewDtComponentWithChange() throws Exception {
|
||||
|
||||
// NOTE: This test appears to verify that the undefined*16 type
|
||||
// resolved against the program DTM used by the stack editor
|
||||
// is removed on the first undo - unfortunately, the redo
|
||||
// does not restore the editor state. It is unclear why a private
|
||||
// DTM is not employed similar to the Structure editor which
|
||||
// would allow the new undefined*16 type to persist after the undo (see SCR 10280)
|
||||
editStack(function.getEntryPoint().toString());
|
||||
|
||||
Window dialog;
|
||||
// Put 2 byte pointer at -0x1b
|
||||
DataType ptr = new Pointer16DataType();
|
||||
setType(ptr, 4);
|
||||
|
||||
apply();
|
||||
waitForSwing();
|
||||
|
||||
// Put word pointer at -0x1b
|
||||
setType(WordDataType.dataType, 4);
|
||||
|
||||
// Undo the apply of a new data type to an editor component.
|
||||
undo(program, false);
|
||||
|
||||
// Verify the Reload Stack Editor? dialog is displayed.
|
||||
Window dialog = waitForWindow("Reload Stack Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "No");
|
||||
|
||||
waitForSwing();
|
||||
|
||||
// Redo the apply
|
||||
redo(program, false);
|
||||
|
||||
dialog = waitForWindow("Reload Stack Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(ptr.isEquivalent(getDataType(4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoNewDtComponentWithoutChange() throws Exception {
|
||||
|
||||
editStack(function.getEntryPoint().toString());
|
||||
|
||||
// Put 2 byte pointer at -0x1b
|
||||
setType(new Pointer16DataType(), 4);
|
||||
|
||||
apply();
|
||||
waitForSwing();
|
||||
|
||||
// Undo the apply of a new data type to an editor component.
|
||||
undo(program, false);
|
||||
|
||||
// Verify the Reload Stack Editor? dialog is displayed.
|
||||
dialog = waitForWindow("Reload Stack Editor?");
|
||||
assertNotNull(dialog);
|
||||
pressButton(dialog, "No");
|
||||
dialog.dispose();
|
||||
waitForSwing();
|
||||
|
||||
dialog = getWindow("Reload Stack Editor?");
|
||||
Window dialog = getWindow("Reload Stack Editor?");
|
||||
assertNull(dialog);
|
||||
|
||||
// Redo the apply
|
||||
redo(program, false);
|
||||
waitForSwing();
|
||||
dialog = getWindow("Reload Stack Editor?");
|
||||
assertNull(dialog);
|
||||
|
||||
cleanup();
|
||||
assertEquals(DataType.DEFAULT, getDataType(4));
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -685,6 +685,8 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
DataType byteDt = root.getDataType("byte");
|
||||
DataType wordDt = root.getDataType("word");
|
||||
|
||||
assertEquals(4, getEventCount());
|
||||
|
||||
Event ev = getEvent(0);
|
||||
assertEquals("Cat Added", ev.evName);
|
||||
assertEquals(null, ev.dt);
|
||||
@ -701,22 +703,10 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertEquals(root.getCategoryPath(), ev.parent);
|
||||
|
||||
ev = getEvent(3);
|
||||
assertEquals("DT Changed", ev.evName);
|
||||
assertTrue(dt.isEquivalent(ev.dt));
|
||||
assertEquals(null, ev.parent);
|
||||
|
||||
// ev = getEvent(4); // eliminated size change event during creation
|
||||
// assertEquals("DT Changed", ev.evName);
|
||||
// assertTrue(dt.isEquivalent(ev.dt));
|
||||
// assertEquals(null, ev.parent);
|
||||
|
||||
ev = getEvent(4);
|
||||
assertEquals("DT Added", ev.evName);
|
||||
assertTrue(dt.isEquivalent(ev.dt));
|
||||
assertEquals(sub1.getCategoryPath(), ev.parent);
|
||||
|
||||
assertEquals(5, getEventCount());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -807,8 +797,9 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
struct2 = (Structure) newDt.insert(3, struct2).getDataType();
|
||||
|
||||
assertEquals(4, getEventCount());
|
||||
Event ev = getEvent(3);
|
||||
assertEquals(3, getEventCount());
|
||||
|
||||
Event ev = getEvent(2);
|
||||
assertEquals("DT Changed", ev.evName);
|
||||
assertEquals(newDt, ev.dt);
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -283,6 +283,7 @@ public class UnionDataTypeTest extends AbstractGenericTest {
|
||||
union.delete(Sets.newHashSet(2, 4));
|
||||
|
||||
assertEquals(2, union.getLength());
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
|
||||
"pack(disabled)\n" +
|
||||
@ -293,6 +294,10 @@ public class UnionDataTypeTest extends AbstractGenericTest {
|
||||
"}\n" +
|
||||
"Length: 2 Alignment: 1", union);
|
||||
//@formatter:on
|
||||
|
||||
DataTypeComponent[] comps = union.getDefinedComponents();
|
||||
assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
|
||||
assertEquals(2, comps[2].getOrdinal());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -10,10 +10,6 @@ color.bg.version.tracking.dual.listing.highlight.markup.failed = color.palette.r
|
||||
color.bg.version.tracking.dual.listing.highlight.markup.no.address = color.palette.lavender
|
||||
color.bg.version.tracking.dual.listing.highlight.markup.same = color.palette.lightskyblue
|
||||
color.bg.version.tracking.dual.listing.highlight.markup.conflict = color.palette.gold
|
||||
|
||||
color.bg.version.tracking.filter.formatted.field.error = color.palette.lightgray
|
||||
color.bg.version.tracking.filter.formatted.field.editing = color.bg.filterfield
|
||||
color.fg.version.tracking.filter.formatted.field.editing = color.fg.filterfield
|
||||
|
||||
color.bg.version.tracking.match.table.locked.out = color.palette.aliceblue
|
||||
color.bg.version.tracking.match.table.markup.status.applied = color.palette.limegreen
|
||||
@ -83,7 +79,6 @@ icon.version.tracking.tag.status.deleted = tag_blue_delete.png
|
||||
icon.version.tracking.tag.status.existing = tag_blue.png
|
||||
icon.version.tracking.tag.button.undo = undo-apply.png
|
||||
|
||||
icon.version.tracking.filter.status.changed = bullet_black.png
|
||||
icon.version.tracking.filter.status.invalid = no_small.png
|
||||
icon.version.tracking.filter.status.applied = bullet_green.png
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -29,7 +29,9 @@ import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GHtmlLabel;
|
||||
import docking.widgets.textfield.HexIntegerFormatter;
|
||||
import docking.widgets.numberformat.HexIntegerFormatter;
|
||||
import docking.widgets.numberformat.IntegerFormatterFactory;
|
||||
import docking.widgets.textfield.*;
|
||||
import generic.theme.GColor;
|
||||
import ghidra.feature.vt.api.main.VTAssociation;
|
||||
import ghidra.feature.vt.gui.provider.matchtable.NumberRangeProducer;
|
||||
@ -59,8 +61,8 @@ public abstract class AbstractAddressRangeFilter<T> extends AncillaryFilter<T>
|
||||
private static final Long MAX_ADDRESS_VALUE = Long.MAX_VALUE;
|
||||
|
||||
private JComponent component;
|
||||
private FilterFormattedTextField lowerAddressRangeTextField;
|
||||
private FilterFormattedTextField upperAddressRangeTextField;
|
||||
private GFormattedTextField lowerAddressRangeTextField;
|
||||
private GFormattedTextField upperAddressRangeTextField;
|
||||
private JComboBox<String> lowerRangeComboBox;
|
||||
private JComboBox<String> upperRangeComboBox;
|
||||
|
||||
@ -90,14 +92,14 @@ public abstract class AbstractAddressRangeFilter<T> extends AncillaryFilter<T>
|
||||
enablePanel.add(enableCheckBox, BorderLayout.NORTH);
|
||||
|
||||
// begin address field (long input field with hex)
|
||||
lowerAddressRangeTextField = new FilterFormattedTextField(
|
||||
lowerAddressRangeTextField = new GFormattedTextField(
|
||||
new IntegerFormatterFactory(new HexIntegerFormatter(), false), MIN_ADDRESS_VALUE);
|
||||
lowerAddressRangeTextField.setName("Lower Address Range Text Field"); // for tracking state
|
||||
lowerAddressRangeTextField.setColumns(15);
|
||||
lowerAddressRangeTextField.setMinimumSize(lowerAddressRangeTextField.getPreferredSize());
|
||||
|
||||
// end address field (long input field with hex)
|
||||
upperAddressRangeTextField = new FilterFormattedTextField(
|
||||
upperAddressRangeTextField = new GFormattedTextField(
|
||||
new IntegerFormatterFactory(new HexIntegerFormatter(), false), MAX_ADDRESS_VALUE);
|
||||
upperAddressRangeTextField.setName("Upper Address Range Text Field"); // for tracking state
|
||||
upperAddressRangeTextField.setColumns(15);
|
||||
@ -169,17 +171,20 @@ public abstract class AbstractAddressRangeFilter<T> extends AncillaryFilter<T>
|
||||
}
|
||||
};
|
||||
|
||||
FilterStatusListener notificationListener = status -> fireStatusChanged(status);
|
||||
TextEntryStatusListener notificationListener = s -> {
|
||||
FilterEditingStatus status = FilterEditingStatus.getFilterStatus(s);
|
||||
fireStatusChanged(status);
|
||||
};
|
||||
|
||||
StatusLabel lowerScoreStatusLabel =
|
||||
new StatusLabel(lowerAddressRangeTextField, MIN_ADDRESS_VALUE);
|
||||
lowerAddressRangeTextField.addFilterStatusListener(lowerScoreStatusLabel);
|
||||
lowerAddressRangeTextField.addFilterStatusListener(notificationListener);
|
||||
lowerAddressRangeTextField.addTextEntryStatusListener(lowerScoreStatusLabel);
|
||||
lowerAddressRangeTextField.addTextEntryStatusListener(notificationListener);
|
||||
|
||||
StatusLabel upperScoreStatusLabel =
|
||||
new StatusLabel(upperAddressRangeTextField, MAX_ADDRESS_VALUE);
|
||||
upperAddressRangeTextField.addFilterStatusListener(upperScoreStatusLabel);
|
||||
upperAddressRangeTextField.addFilterStatusListener(notificationListener);
|
||||
upperAddressRangeTextField.addTextEntryStatusListener(upperScoreStatusLabel);
|
||||
upperAddressRangeTextField.addTextEntryStatusListener(notificationListener);
|
||||
|
||||
disabledScreen = createDisabledScreen(layeredPane);
|
||||
|
||||
@ -246,7 +251,7 @@ public abstract class AbstractAddressRangeFilter<T> extends AncillaryFilter<T>
|
||||
component.validate();
|
||||
}
|
||||
|
||||
private JComboBox<String> createComboBox(FilterFormattedTextField field, Long defaultValue,
|
||||
private JComboBox<String> createComboBox(GFormattedTextField field, Long defaultValue,
|
||||
String prototypeString) {
|
||||
GhidraComboBox<String> comboBox = new GhidraComboBox<>(new LimitedHistoryComboBoxModel()) {
|
||||
// overridden to paint seamlessly with out color changing text field
|
||||
@ -287,14 +292,22 @@ public abstract class AbstractAddressRangeFilter<T> extends AncillaryFilter<T>
|
||||
return component;
|
||||
}
|
||||
|
||||
private FilterEditingStatus getLowerAddressRangeStatus() {
|
||||
return FilterEditingStatus.getFilterStatus(lowerAddressRangeTextField);
|
||||
}
|
||||
|
||||
private FilterEditingStatus getUpperAddressRangeStatus() {
|
||||
return FilterEditingStatus.getFilterStatus(upperAddressRangeTextField);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
if (!isEnabled) {
|
||||
return FilterEditingStatus.NONE;
|
||||
}
|
||||
|
||||
FilterEditingStatus lowerStatus = lowerAddressRangeTextField.getFilterStatus();
|
||||
FilterEditingStatus upperStatus = upperAddressRangeTextField.getFilterStatus();
|
||||
FilterEditingStatus lowerStatus = getLowerAddressRangeStatus();
|
||||
FilterEditingStatus upperStatus = getUpperAddressRangeStatus();
|
||||
|
||||
if (lowerStatus == FilterEditingStatus.ERROR || upperStatus == FilterEditingStatus.ERROR) {
|
||||
return FilterEditingStatus.ERROR;
|
||||
@ -339,8 +352,8 @@ public abstract class AbstractAddressRangeFilter<T> extends AncillaryFilter<T>
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lowerAddressRangeTextField.getFilterStatus() == FilterEditingStatus.ERROR ||
|
||||
upperAddressRangeTextField.getFilterStatus() == FilterEditingStatus.ERROR) {
|
||||
if (getLowerAddressRangeStatus() == FilterEditingStatus.ERROR ||
|
||||
getUpperAddressRangeStatus() == FilterEditingStatus.ERROR) {
|
||||
return true; // for an invalid filter state, we let all values through
|
||||
}
|
||||
|
||||
@ -572,10 +585,10 @@ public abstract class AbstractAddressRangeFilter<T> extends AncillaryFilter<T>
|
||||
private class FormattedFieldComboBoxEditor implements ComboBoxEditor {
|
||||
|
||||
private EventListenerList listeners = new EventListenerList();
|
||||
private final FilterFormattedTextField textField;
|
||||
private final GFormattedTextField textField;
|
||||
private final Object defaultValue;
|
||||
|
||||
FormattedFieldComboBoxEditor(FilterFormattedTextField textField) {
|
||||
FormattedFieldComboBoxEditor(GFormattedTextField textField) {
|
||||
this.textField = textField;
|
||||
defaultValue = textField.getValue();
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -22,6 +22,7 @@ import java.util.Set;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.textfield.GFormattedTextField;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.util.exception.AssertException;
|
||||
@ -72,8 +73,6 @@ public abstract class Filter<T> {
|
||||
|
||||
public enum FilterEditingStatus {
|
||||
NONE("", null),
|
||||
DIRTY("Filter contents have changed, but are not yet applied", new GIcon(
|
||||
"icon.version.tracking.filter.status.changed")),
|
||||
ERROR("Filter contents are not valid", new GIcon(
|
||||
"icon.version.tracking.filter.status.invalid")),
|
||||
APPLIED("Filter applied", new GIcon("icon.version.tracking.filter.status.applied"));
|
||||
@ -93,6 +92,17 @@ public abstract class Filter<T> {
|
||||
Icon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static FilterEditingStatus getFilterStatus(GFormattedTextField textEntryField) {
|
||||
switch (textEntryField.getTextEntryStatus()) {
|
||||
case INVALID:
|
||||
return FilterEditingStatus.ERROR;
|
||||
case CHANGED:
|
||||
return FilterEditingStatus.APPLIED;
|
||||
default:
|
||||
return FilterEditingStatus.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -21,9 +21,11 @@ import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.textfield.GFormattedTextField;
|
||||
import docking.widgets.textfield.TextEntryStatusListener;
|
||||
import ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus;
|
||||
|
||||
public class StatusLabel extends GDLabel implements FilterStatusListener {
|
||||
public class StatusLabel extends GDLabel implements TextEntryStatusListener {
|
||||
|
||||
private final JFormattedTextField textField;
|
||||
|
||||
@ -82,7 +84,8 @@ public class StatusLabel extends GDLabel implements FilterStatusListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filterStatusChanged(FilterEditingStatus status) {
|
||||
public void statusChanged(GFormattedTextField textEntryField) {
|
||||
FilterEditingStatus status = FilterEditingStatus.getFilterStatus(textEntryField);
|
||||
resetBounds();
|
||||
setIcon(status.getIcon());
|
||||
setToolTipText(status.getDescription() + " (click to reset)");
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -26,6 +26,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.numberformat.BoundedRangeDecimalFormatterFactory;
|
||||
import docking.widgets.textfield.GFormattedTextField;
|
||||
import docking.widgets.textfield.TextEntryStatusListener;
|
||||
import ghidra.feature.vt.gui.filters.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.util.layout.HorizontalLayout;
|
||||
@ -41,8 +43,8 @@ public abstract class AbstractDoubleRangeFilter<T> extends Filter<T>
|
||||
private final Double minValue;
|
||||
|
||||
private JComponent component;
|
||||
private FilterFormattedTextField upperBoundField;
|
||||
private FilterFormattedTextField lowerBoundField;
|
||||
private GFormattedTextField upperBoundField;
|
||||
private GFormattedTextField lowerBoundField;
|
||||
private String filterName;
|
||||
|
||||
AbstractDoubleRangeFilter(String filterName, Double minValue, Double maxValue) {
|
||||
@ -54,7 +56,7 @@ public abstract class AbstractDoubleRangeFilter<T> extends Filter<T>
|
||||
}
|
||||
|
||||
private void createLowerBoundField() {
|
||||
lowerBoundField = new FilterFormattedTextField(
|
||||
lowerBoundField = new GFormattedTextField(
|
||||
new BoundedRangeDecimalFormatterFactory(maxValue, minValue, FORMAT), minValue);
|
||||
lowerBoundField.setName("Lower " + filterName + " Filter Field"); // for debugging
|
||||
lowerBoundField.setColumns(4);
|
||||
@ -63,7 +65,7 @@ public abstract class AbstractDoubleRangeFilter<T> extends Filter<T>
|
||||
}
|
||||
|
||||
private void createUpperBoundField() {
|
||||
upperBoundField = new FilterFormattedTextField(
|
||||
upperBoundField = new GFormattedTextField(
|
||||
new BoundedRangeDecimalFormatterFactory(maxValue, minValue, FORMAT), maxValue);
|
||||
upperBoundField.setName("Upper " + filterName + " Filter Field"); // for debugging
|
||||
upperBoundField.setColumns(4);
|
||||
@ -93,15 +95,18 @@ public abstract class AbstractDoubleRangeFilter<T> extends Filter<T>
|
||||
panel.add(middleLabel);
|
||||
panel.add(upperBoundField);
|
||||
|
||||
FilterStatusListener notificationListener = status -> fireStatusChanged(status);
|
||||
TextEntryStatusListener notificationListener = s -> {
|
||||
FilterEditingStatus status = FilterEditingStatus.getFilterStatus(s);
|
||||
fireStatusChanged(status);
|
||||
};
|
||||
|
||||
StatusLabel lowerBoundStatusLabel = new StatusLabel(lowerBoundField, minValue);
|
||||
lowerBoundField.addFilterStatusListener(lowerBoundStatusLabel);
|
||||
lowerBoundField.addFilterStatusListener(notificationListener);
|
||||
lowerBoundField.addTextEntryStatusListener(lowerBoundStatusLabel);
|
||||
lowerBoundField.addTextEntryStatusListener(notificationListener);
|
||||
|
||||
StatusLabel upperBoundStatusLabel = new StatusLabel(upperBoundField, maxValue);
|
||||
upperBoundField.addFilterStatusListener(upperBoundStatusLabel);
|
||||
upperBoundField.addFilterStatusListener(notificationListener);
|
||||
upperBoundField.addTextEntryStatusListener(upperBoundStatusLabel);
|
||||
upperBoundField.addTextEntryStatusListener(notificationListener);
|
||||
|
||||
JLayeredPane layeredPane = new JLayeredPane();
|
||||
layeredPane.add(panel, BASE_COMPONENT_LAYER);
|
||||
@ -127,10 +132,18 @@ public abstract class AbstractDoubleRangeFilter<T> extends Filter<T>
|
||||
return component;
|
||||
}
|
||||
|
||||
private FilterEditingStatus getLowerBoundStatus() {
|
||||
return FilterEditingStatus.getFilterStatus(lowerBoundField);
|
||||
}
|
||||
|
||||
private FilterEditingStatus getUpperBoundStatus() {
|
||||
return FilterEditingStatus.getFilterStatus(upperBoundField);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
FilterEditingStatus lowerStatus = lowerBoundField.getFilterStatus();
|
||||
FilterEditingStatus upperStatus = upperBoundField.getFilterStatus();
|
||||
FilterEditingStatus lowerStatus = getLowerBoundStatus();
|
||||
FilterEditingStatus upperStatus = getUpperBoundStatus();
|
||||
|
||||
if (lowerStatus == FilterEditingStatus.ERROR || upperStatus == FilterEditingStatus.ERROR) {
|
||||
return FilterEditingStatus.ERROR;
|
||||
@ -146,8 +159,8 @@ public abstract class AbstractDoubleRangeFilter<T> extends Filter<T>
|
||||
|
||||
@Override
|
||||
public boolean passesFilter(T t) {
|
||||
if (lowerBoundField.getFilterStatus() == FilterEditingStatus.ERROR ||
|
||||
upperBoundField.getFilterStatus() == FilterEditingStatus.ERROR) {
|
||||
if (getLowerBoundStatus() == FilterEditingStatus.ERROR ||
|
||||
getUpperBoundStatus() == FilterEditingStatus.ERROR) {
|
||||
return true; // for an invalid filter state, we let all values through
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -27,6 +27,8 @@ import javax.swing.border.Border;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.numberformat.IntegerFormatterFactory;
|
||||
import docking.widgets.textfield.GFormattedTextField;
|
||||
import ghidra.feature.vt.api.main.VTMatch;
|
||||
import ghidra.feature.vt.gui.filters.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
@ -39,7 +41,7 @@ public class LengthFilter extends Filter<VTMatch> {
|
||||
private static final Integer DEFAULT_FILTER_VALUE = 0;
|
||||
|
||||
private JComponent component;
|
||||
private FilterFormattedTextField textField;
|
||||
private GFormattedTextField textField;
|
||||
|
||||
public LengthFilter() {
|
||||
component = createComponent();
|
||||
@ -49,7 +51,7 @@ public class LengthFilter extends Filter<VTMatch> {
|
||||
final JLabel label = new GDLabel("Length Filter: ");
|
||||
|
||||
Integer defaultValue = DEFAULT_FILTER_VALUE;
|
||||
textField = new FilterFormattedTextField(new IntegerFormatterFactory(false), defaultValue);
|
||||
textField = new GFormattedTextField(new IntegerFormatterFactory(false), defaultValue);
|
||||
textField.setName("Length Filter Field"); // for debugging
|
||||
textField.setInputVerifier(new IntegerInputVerifier());
|
||||
textField.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
@ -67,8 +69,11 @@ public class LengthFilter extends Filter<VTMatch> {
|
||||
final JLayeredPane layeredPane = new JLayeredPane();
|
||||
|
||||
StatusLabel statusLabel = new StatusLabel(textField, defaultValue);
|
||||
textField.addFilterStatusListener(statusLabel);
|
||||
textField.addFilterStatusListener(status -> fireStatusChanged(status));
|
||||
textField.addTextEntryStatusListener(statusLabel);
|
||||
textField.addTextEntryStatusListener(s -> {
|
||||
FilterEditingStatus status = FilterEditingStatus.getFilterStatus(s);
|
||||
fireStatusChanged(status);
|
||||
});
|
||||
layeredPane.add(panel, BASE_COMPONENT_LAYER);
|
||||
layeredPane.add(statusLabel, HOVER_COMPONENT_LAYER);
|
||||
layeredPane.setPreferredSize(panel.getPreferredSize());
|
||||
@ -91,7 +96,7 @@ public class LengthFilter extends Filter<VTMatch> {
|
||||
|
||||
@Override
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
return textField.getFilterStatus();
|
||||
return FilterEditingStatus.getFilterStatus(textField);
|
||||
}
|
||||
|
||||
@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.
|
||||
@ -28,9 +28,11 @@ import javax.swing.text.DefaultFormatterFactory;
|
||||
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.table.GTable;
|
||||
import docking.widgets.textfield.GFormattedTextField;
|
||||
import ghidra.feature.vt.api.main.VTAssociation;
|
||||
import ghidra.feature.vt.api.main.VTSession;
|
||||
import ghidra.feature.vt.gui.filters.*;
|
||||
import ghidra.feature.vt.gui.filters.Filter;
|
||||
import ghidra.feature.vt.gui.filters.StatusLabel;
|
||||
import ghidra.feature.vt.gui.plugin.VTController;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.program.model.address.Address;
|
||||
@ -44,7 +46,7 @@ public abstract class AbstractTextFilter<T> extends Filter<T> {
|
||||
private static final Integer HOVER_COMPONENT_LAYER = 2;
|
||||
|
||||
private JComponent component;
|
||||
private FilterFormattedTextField textField;
|
||||
private GFormattedTextField textField;
|
||||
private String defaultValue = "";
|
||||
protected VTController controller;
|
||||
protected final GTable table;
|
||||
@ -62,7 +64,7 @@ public abstract class AbstractTextFilter<T> extends Filter<T> {
|
||||
panel.setBorder(BorderFactory.createCompoundBorder(outsideBorder, paddingBorder));
|
||||
|
||||
DefaultFormatterFactory factory = new DefaultFormatterFactory(new DefaultFormatter());
|
||||
textField = new FilterFormattedTextField(factory, defaultValue);
|
||||
textField = new GFormattedTextField(factory, defaultValue);
|
||||
textField.setName(filterName + " Field"); // for debugging
|
||||
textField.setColumns(20);
|
||||
textField.setMinimumSize(textField.getPreferredSize());
|
||||
@ -76,8 +78,11 @@ public abstract class AbstractTextFilter<T> extends Filter<T> {
|
||||
panel.add(textField, BorderLayout.CENTER);
|
||||
|
||||
StatusLabel nameFieldStatusLabel = new StatusLabel(textField, defaultValue);
|
||||
textField.addFilterStatusListener(nameFieldStatusLabel);
|
||||
textField.addFilterStatusListener(status -> fireStatusChanged(status));
|
||||
textField.addTextEntryStatusListener(nameFieldStatusLabel);
|
||||
textField.addTextEntryStatusListener(s -> {
|
||||
FilterEditingStatus status = FilterEditingStatus.getFilterStatus(s);
|
||||
fireStatusChanged(status);
|
||||
});
|
||||
|
||||
final JLayeredPane layeredPane = new JLayeredPane();
|
||||
layeredPane.add(panel, BASE_COMPONENT_LAYER);
|
||||
@ -127,7 +132,7 @@ public abstract class AbstractTextFilter<T> extends Filter<T> {
|
||||
|
||||
@Override
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
return textField.getFilterStatus();
|
||||
return FilterEditingStatus.getFilterStatus(textField);
|
||||
}
|
||||
|
||||
protected String getTextFieldText() {
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -532,6 +532,16 @@ public class DBHandle {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a means of detecting changes to the underlying database buffers
|
||||
* during a transaction.
|
||||
*
|
||||
* @return current modification count
|
||||
*/
|
||||
public long getModCount() {
|
||||
return bufferMgr.getModCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are uncommitted changes to the database.
|
||||
* @return true if there are uncommitted changes to the database.
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -68,6 +68,7 @@ public class BufferMgr {
|
||||
private Object snapshotLock = new Object(); // Used to prevent BufferNode modifications during snapshot
|
||||
private boolean modifiedSinceSnapshot = false;
|
||||
private boolean hasNonUndoableChanges = false;
|
||||
private long modCount;
|
||||
|
||||
private int bufferSize;
|
||||
|
||||
@ -238,7 +239,7 @@ public class BufferMgr {
|
||||
if (lockCount != 0) {
|
||||
throw new IOException("Unable to re-initialize buffer cache while in-use");
|
||||
}
|
||||
|
||||
|
||||
if (cacheFile != null) {
|
||||
cacheFile.delete();
|
||||
}
|
||||
@ -248,7 +249,7 @@ public class BufferMgr {
|
||||
cacheTail = new BufferNode(TAIL, -1);
|
||||
cacheHead.nextCached = cacheTail;
|
||||
cacheTail.prevCached = cacheHead;
|
||||
|
||||
|
||||
cacheSize = 0;
|
||||
buffersOnHand = 0;
|
||||
|
||||
@ -264,7 +265,7 @@ public class BufferMgr {
|
||||
cacheFile.setParameter(name, sourceFile.getParameter(name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resetCacheStatistics();
|
||||
|
||||
if (alwaysPreCache) {
|
||||
@ -1093,6 +1094,7 @@ public class BufferMgr {
|
||||
throw new AssertException();
|
||||
}
|
||||
|
||||
++modCount;
|
||||
modifiedSinceSnapshot = true;
|
||||
|
||||
// Establish current checkpoint if necessary
|
||||
@ -1199,6 +1201,14 @@ public class BufferMgr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a means of detecting changes to the underlying database during a transaction.
|
||||
* @return current modification count
|
||||
*/
|
||||
public synchronized long getModCount() {
|
||||
return modCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if unsaved "buffer" changes exist.
|
||||
* If no changes have been made, or all changes have been
|
||||
|
@ -54,6 +54,10 @@ color.bg.widget.tabs.more.tabs.hover = color.bg.widget.tabs.selected
|
||||
|
||||
color.fg.widget.tabs.list = color.fg
|
||||
|
||||
color.bg.formatted.field.error = color.palette.lightcoral
|
||||
color.bg.formatted.field.editing = color.bg.filterfield
|
||||
color.fg.formatted.field.editing = color.fg.filterfield
|
||||
|
||||
|
||||
icon.folder.new = folder_add.png
|
||||
icon.toggle.expand = expand.gif
|
||||
|
@ -4,16 +4,16 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.widgets.textfield;
|
||||
package docking.widgets.numberformat;
|
||||
|
||||
import java.text.Format;
|
||||
import java.text.ParseException;
|
@ -1,20 +1,19 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.widgets.textfield;
|
||||
package docking.widgets.numberformat;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
import java.text.*;
|
@ -1,27 +1,24 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.feature.vt.gui.filters;
|
||||
package docking.widgets.numberformat;
|
||||
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JFormattedTextField.AbstractFormatter;
|
||||
import javax.swing.text.DefaultFormatterFactory;
|
||||
|
||||
import docking.widgets.textfield.IntegerFormatter;
|
||||
|
||||
public class IntegerFormatterFactory extends DefaultFormatterFactory {
|
||||
|
||||
private AbstractFormatter formatter = new IntegerFormatter();
|
@ -4,18 +4,16 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.feature.vt.gui.filters;
|
||||
|
||||
import static ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus.*;
|
||||
package docking.widgets.textfield;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.event.FocusEvent;
|
||||
@ -30,34 +28,42 @@ import javax.swing.event.DocumentListener;
|
||||
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
public class FilterFormattedTextField extends JFormattedTextField {
|
||||
/**
|
||||
* {@link GFormattedTextField} provides an implementation of {@link JFormattedTextField}
|
||||
* which facilitates entry validation with an indication of its current status.
|
||||
* <br>
|
||||
* When modified from its default value the field background will reflect its
|
||||
* current status.
|
||||
*/
|
||||
public class GFormattedTextField extends JFormattedTextField {
|
||||
private static final Color ERROR_BACKGROUND_COLOR =
|
||||
new GColor("color.bg.version.tracking.filter.formatted.field.error");
|
||||
new GColor("color.bg.formatted.field.error");
|
||||
private static final Color EDITING_BACKGROUND_COLOR =
|
||||
new GColor("color.bg.version.tracking.filter.formatted.field.editing");
|
||||
new GColor("color.bg.formatted.field.editing");
|
||||
private static final Color EDITING_FOREGROUND_COLOR =
|
||||
new GColor("color.fg.version.tracking.filter.formatted.field.editing");
|
||||
new GColor("color.fg.formatted.field.editing");
|
||||
|
||||
private Set<FilterStatusListener> listeners = new HashSet<>();
|
||||
public static enum Status {
|
||||
UNCHANGED, CHANGED, INVALID;
|
||||
}
|
||||
|
||||
private FilterEditingStatus currentStatus = NONE;
|
||||
private final Object defaultValue;
|
||||
private final String defaultText;
|
||||
private Set<TextEntryStatusListener> listeners = new HashSet<>();
|
||||
|
||||
private Status currentStatus = Status.UNCHANGED;
|
||||
private Object defaultValue;
|
||||
private String defaultText;
|
||||
private boolean isError;
|
||||
private boolean ignoreFocusEditChanges;
|
||||
|
||||
/** A flag to let us know when we can ignore focus updates */
|
||||
private boolean isProcessingFocusEvent;
|
||||
|
||||
public FilterFormattedTextField(AbstractFormatterFactory factory, Object defaultValue) {
|
||||
public GFormattedTextField(AbstractFormatterFactory factory, Object defaultValue) {
|
||||
super(factory);
|
||||
|
||||
setValue(defaultValue);
|
||||
this.defaultValue = defaultValue;
|
||||
this.defaultText = getText(); // get the formatted text
|
||||
this.currentStatus = NONE;
|
||||
|
||||
getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
@ -76,8 +82,18 @@ public class FilterFormattedTextField extends JFormattedTextField {
|
||||
}
|
||||
});
|
||||
|
||||
addPropertyChangeListener("value", evt -> editingFinished());
|
||||
setDefaultValue(defaultValue);
|
||||
|
||||
addPropertyChangeListener("value", evt -> editingFinished());
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish default value. Text field value should be set before invoking this method.
|
||||
* @param defaultValue default value
|
||||
*/
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
this.defaultText = getText(); // get the formatted text
|
||||
update();
|
||||
}
|
||||
|
||||
@ -100,22 +116,22 @@ public class FilterFormattedTextField extends JFormattedTextField {
|
||||
isProcessingFocusEvent = false;
|
||||
}
|
||||
|
||||
public FilterEditingStatus getFilterStatus() {
|
||||
public Status getTextEntryStatus() {
|
||||
return currentStatus;
|
||||
}
|
||||
|
||||
public void addFilterStatusListener(FilterStatusListener listener) {
|
||||
public void addTextEntryStatusListener(TextEntryStatusListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
private void filterStatusChanged(FilterEditingStatus status) {
|
||||
private void textEntryStatusChanged(Status status) {
|
||||
currentStatus = status;
|
||||
if (listeners == null) {
|
||||
return; // happens during construction
|
||||
}
|
||||
|
||||
for (FilterStatusListener listener : listeners) {
|
||||
listener.filterStatusChanged(status);
|
||||
for (TextEntryStatusListener listener : listeners) {
|
||||
listener.statusChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,24 +211,24 @@ public class FilterFormattedTextField extends JFormattedTextField {
|
||||
setBackground(Colors.BACKGROUND);
|
||||
}
|
||||
|
||||
filterStatusChanged(currentStatus);
|
||||
textEntryStatusChanged(currentStatus);
|
||||
}
|
||||
|
||||
private void updateStatus() {
|
||||
FilterEditingStatus oldStatus = currentStatus;
|
||||
Status oldStatus = currentStatus;
|
||||
if (isError) {
|
||||
currentStatus = FilterEditingStatus.ERROR;
|
||||
currentStatus = Status.INVALID;
|
||||
}
|
||||
|
||||
else if (hasNonDefaultValue()) {
|
||||
currentStatus = APPLIED;
|
||||
currentStatus = Status.CHANGED;
|
||||
}
|
||||
else {
|
||||
currentStatus = NONE;
|
||||
currentStatus = Status.UNCHANGED;
|
||||
}
|
||||
|
||||
if (oldStatus != currentStatus) {
|
||||
filterStatusChanged(currentStatus);
|
||||
textEntryStatusChanged(currentStatus);
|
||||
}
|
||||
}
|
||||
|
@ -4,28 +4,18 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.compositeeditor;
|
||||
package docking.widgets.textfield;
|
||||
|
||||
public interface EditorAction extends CompositeEditorModelListener {
|
||||
|
||||
static final String BASIC_ACTION_GROUP = "1_BASIC_EDITOR_ACTION";
|
||||
static final String DATA_ACTION_GROUP = "2_DATA_EDITOR_ACTION";
|
||||
static final String COMPONENT_ACTION_GROUP = "3_COMPONENT_EDITOR_ACTION";
|
||||
static final String BITFIELD_ACTION_GROUP = "4_COMPONENT_EDITOR_ACTION";
|
||||
|
||||
/**
|
||||
* Method to set the action's enablement based on the associated editor
|
||||
* model's current state.
|
||||
*/
|
||||
public void adjustEnablement();
|
||||
public interface TextEntryStatusListener {
|
||||
|
||||
public void statusChanged(GFormattedTextField textField);
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
package ghidra.program.database.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.docking.settings.Settings;
|
||||
@ -212,6 +213,9 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
if (Objects.equals(desc, record.getString(CompositeDBAdapter.COMPOSITE_COMMENT_COL))) {
|
||||
return;
|
||||
}
|
||||
record.setString(CompositeDBAdapter.COMPOSITE_COMMENT_COL, desc);
|
||||
compositeAdapter.updateRecord(record, true);
|
||||
dataMgr.dataTypeChanged(this, false);
|
||||
@ -391,13 +395,17 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
|
||||
return record.getLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL);
|
||||
}
|
||||
|
||||
void doSetLastChangeTime(long lastChangeTime) throws IOException {
|
||||
record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime);
|
||||
compositeAdapter.updateRecord(record, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastChangeTime(long lastChangeTime) {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime);
|
||||
compositeAdapter.updateRecord(record, false);
|
||||
doSetLastChangeTime(lastChangeTime);
|
||||
dataMgr.dataTypeChanged(this, false);
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -138,7 +138,11 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
|
||||
if (id == -1) {
|
||||
return DataType.DEFAULT;
|
||||
}
|
||||
return dataMgr.getDataType(id);
|
||||
DataType dt = dataMgr.getDataType(id);
|
||||
if (dt == null) {
|
||||
return BadDataType.dataType;
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -191,6 +195,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
|
||||
|
||||
@Override
|
||||
public Settings getDefaultSettings() {
|
||||
|
||||
if (!hasSettings()) {
|
||||
return SettingsImpl.NO_SETTINGS;
|
||||
}
|
||||
|
@ -1693,8 +1693,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
// (preference is given to similar kind of datatype when checking existing conflict types)
|
||||
DataType existingDataType = findDataTypeSameLocation(dataType);
|
||||
if (existingDataType == null) {
|
||||
return createDataType(dataType, getUnusedConflictName(dataType), sourceArchive,
|
||||
currentHandler);
|
||||
// create non-existing datatype - keep original name unless it is already used
|
||||
String name = dataType.getName();
|
||||
if (getDataType(dataType.getCategoryPath(), name) != null) {
|
||||
name = getUnusedConflictName(dataType);
|
||||
}
|
||||
return createDataType(dataType, name, sourceArchive, currentHandler);
|
||||
}
|
||||
|
||||
// So we have a dataType with the same path and name, but not equivalent, so use
|
||||
@ -2310,7 +2314,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
if (id <= 0) { // removal of certain special types not permitted
|
||||
return false;
|
||||
}
|
||||
idsToDelete.add(Long.valueOf(id));
|
||||
idsToDelete.add(id);
|
||||
removeQueuedDataTypes();
|
||||
return true;
|
||||
}
|
||||
@ -3130,8 +3134,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
|
||||
structDB.doReplaceWith(struct, false);
|
||||
|
||||
// doReplaceWith may have updated the last change time so set it back to what we want.
|
||||
structDB.setLastChangeTime(struct.getLastChangeTime());
|
||||
// doReplaceWith may have updated the last change time so set it back to what we want
|
||||
// without triggering change notification
|
||||
structDB.doSetLastChangeTime(struct.getLastChangeTime());
|
||||
|
||||
return structDB;
|
||||
}
|
||||
@ -3198,8 +3203,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
|
||||
unionDB.doReplaceWith(union, false);
|
||||
|
||||
// doReplaceWith updated the last change time so set it back to what we want.
|
||||
unionDB.setLastChangeTime(union.getLastChangeTime());
|
||||
// doReplaceWith may have updated the last change time so set it back to what we want
|
||||
// without triggering change notification
|
||||
unionDB.doSetLastChangeTime(union.getLastChangeTime());
|
||||
|
||||
return unionDB;
|
||||
}
|
||||
@ -3717,7 +3723,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
}
|
||||
|
||||
void removeParentChildRecord(long parentID, long childID) {
|
||||
protected void removeParentChildRecord(long parentID, long childID) {
|
||||
|
||||
if (isBulkRemoving) {
|
||||
// we are in the process of bulk removing the given child; no need to call
|
||||
@ -3733,6 +3739,26 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<Long> getChildIds(long parentID) {
|
||||
try {
|
||||
return parentChildAdapter.getChildIds(parentID);
|
||||
}
|
||||
catch (IOException e) {
|
||||
dbError(e);
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
protected boolean hasParent(long childID) {
|
||||
try {
|
||||
return parentChildAdapter.hasParent(childID);
|
||||
}
|
||||
catch (IOException e) {
|
||||
dbError(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<DataType> getParentDataTypes(long dataTypeId) {
|
||||
lock.acquire();
|
||||
try {
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -80,6 +80,16 @@ abstract class ParentChildAdapter {
|
||||
|
||||
abstract void removeRecord(long parentID, long childID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the unique set of child IDs associated with the specified parent ID.
|
||||
* Since a parent may have duplicate parent-child records, this method
|
||||
* avoids returning the same child more than once.
|
||||
* @param parentID parent datatype ID
|
||||
* @return set of child datatype IDs
|
||||
* @throws IOException if a DB IO error occurs
|
||||
*/
|
||||
abstract Set<Long> getChildIds(long parentID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the unique set of parent ID associated with the specified childID.
|
||||
* Since composite parents may have duplicate parent-child records, this method
|
||||
@ -90,6 +100,14 @@ abstract class ParentChildAdapter {
|
||||
*/
|
||||
abstract Set<Long> getParentIds(long childID) throws IOException;
|
||||
|
||||
/**
|
||||
* Determine if there is one or more parents associated with the specified childID.
|
||||
* @param childID child datatype ID
|
||||
* @return true if a parent was identified, else false
|
||||
* @throws IOException if a DB IO error occurs
|
||||
*/
|
||||
abstract boolean hasParent(long childID) throws IOException;
|
||||
|
||||
abstract void removeAllRecordsForParent(long parentID) throws IOException;
|
||||
|
||||
abstract void removeAllRecordsForChild(long childID) throws IOException;
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -43,11 +43,21 @@ class ParentChildDBAdapterNoTable extends ParentChildAdapter {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<Long> getChildIds(long parentID) throws IOException {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<Long> getParentIds(long childID) throws IOException {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasParent(long childID) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean needsInitializing() {
|
||||
return false;
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -88,17 +88,33 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<Long> getChildIds(long parentID) throws IOException {
|
||||
Field[] ids = table.findRecords(new LongField(parentID), PARENT_COL);
|
||||
Set<Long> childIds = new HashSet<>(ids.length);
|
||||
for (Field id : ids) {
|
||||
DBRecord rec = table.getRecord(id);
|
||||
childIds.add(rec.getLongValue(CHILD_COL));
|
||||
}
|
||||
return childIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<Long> getParentIds(long childID) throws IOException {
|
||||
Field[] ids = table.findRecords(new LongField(childID), CHILD_COL);
|
||||
Set<Long> parentIds = new HashSet<>(ids.length);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
DBRecord rec = table.getRecord(ids[i]);
|
||||
for (Field id : ids) {
|
||||
DBRecord rec = table.getRecord(id);
|
||||
parentIds.add(rec.getLongValue(PARENT_COL));
|
||||
}
|
||||
return parentIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasParent(long childID) throws IOException {
|
||||
return table.hasRecord(new LongField(childID), CHILD_COL);
|
||||
}
|
||||
|
||||
public void setNeedsInitializing() {
|
||||
needsInitializing = true;
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -111,9 +111,6 @@ abstract class PointerDBAdapter implements RecordTranslator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
abstract void deleteTable(DBHandle handle) throws IOException;
|
||||
|
||||
/**
|
||||
|
@ -2301,7 +2301,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
|
||||
checkAncestry(replacementDt);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// TODO: should we flag bad replacement
|
||||
// Handle bad replacement with use of undefined component
|
||||
replacementDt = isPackingEnabled() ? Undefined1DataType.dataType : DataType.DEFAULT;
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
@ -36,7 +38,7 @@ public final class DataUtilities {
|
||||
* @return true if name is valid, else false
|
||||
*/
|
||||
public static boolean isValidDataTypeName(String name) {
|
||||
if (name == null || name.length() == 0) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -872,6 +872,14 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
||||
return transaction.intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of active transactions
|
||||
* @return number of active transactions
|
||||
*/
|
||||
protected int getTransactionCount() {
|
||||
return transactionCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransaction(int transactionID, boolean commit) {
|
||||
boolean restored = false;
|
||||
@ -953,6 +961,8 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
||||
protected synchronized void clearUndo() {
|
||||
undoList.clear();
|
||||
redoList.clear();
|
||||
|
||||
// Flatten all checkpoints then restore undo stack size
|
||||
dbHandle.setMaxUndos(0);
|
||||
dbHandle.setMaxUndos(NUM_UNDOS);
|
||||
}
|
||||
|
@ -1640,13 +1640,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
* @param dataType the data type of the new component
|
||||
* @param newOffset offset of replacement component which must fall within origComponents bounds
|
||||
* @param length the length of the new component
|
||||
* @param name the field name of the new component
|
||||
* @param fieldName the field name of the new component
|
||||
* @param comment the comment for the new component
|
||||
* @return the new component or null if only a clear operation was performed.
|
||||
* @throws IllegalArgumentException if unable to identify/make sufficient space
|
||||
*/
|
||||
private DataTypeComponent replaceComponents(LinkedList<DataTypeComponentImpl> origComponents,
|
||||
DataType dataType, int newOffset, int length, String name, String comment)
|
||||
DataType dataType, int newOffset, int length, String fieldName, String comment)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
boolean clearOnly = false;
|
||||
@ -1721,8 +1721,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
DataTypeComponentImpl newDtc = null;
|
||||
if (!clearOnly) {
|
||||
// insert new component
|
||||
newDtc = new DataTypeComponentImpl(dataType, this, length, newOrdinal, newOffset, name,
|
||||
comment);
|
||||
newDtc = new DataTypeComponentImpl(dataType, this, length, newOrdinal, newOffset,
|
||||
fieldName, comment);
|
||||
components.add(index, newDtc);
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -32,7 +32,8 @@ import ghidra.util.task.TaskMonitorAdapter;
|
||||
public class StructureDBTest extends AbstractGenericTest {
|
||||
|
||||
private StructureDB struct;
|
||||
private DataTypeManagerDB dataMgr;
|
||||
private StandAloneDataTypeManager dataMgr;
|
||||
private int txId;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@ -42,7 +43,7 @@ public class StructureDBTest extends AbstractGenericTest {
|
||||
// default data organization is little-endian
|
||||
// default BitFieldPackingImpl uses gcc conventions with type alignment enabled
|
||||
|
||||
dataMgr.startTransaction("Test");
|
||||
txId = dataMgr.startTransaction("Test");
|
||||
|
||||
struct = createStructure("Test", 0);
|
||||
struct.add(new ByteDataType(), "field1", "Comment1");
|
||||
@ -52,6 +53,14 @@ public class StructureDBTest extends AbstractGenericTest {
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (dataMgr != null) {
|
||||
dataMgr.endTransaction(txId, true);
|
||||
dataMgr.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void transitionToBigEndian() {
|
||||
|
||||
Structure structClone = struct.clone(null);
|
||||
@ -1442,7 +1451,45 @@ public class StructureDBTest extends AbstractGenericTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteMany() throws InvalidDataTypeException {
|
||||
public void testDeleteMany() {
|
||||
|
||||
struct.growStructure(20);
|
||||
struct.insertAtOffset(12, WordDataType.dataType, -1, "A", null);
|
||||
struct.insertAtOffset(16, WordDataType.dataType, -1, "B", null);
|
||||
|
||||
assertEquals(32, struct.getLength());
|
||||
assertEquals(26, struct.getNumComponents());
|
||||
assertEquals(6, struct.getNumDefinedComponents());
|
||||
|
||||
struct.delete(Sets.newHashSet(1, 4, 5));
|
||||
|
||||
assertEquals(28, struct.getLength());
|
||||
assertEquals(23, struct.getNumComponents());
|
||||
assertEquals(5, struct.getNumDefinedComponents());
|
||||
|
||||
DataTypeComponent[] comps = struct.getDefinedComponents();
|
||||
assertEquals(WordDataType.class, comps[3].getDataType().getClass());
|
||||
assertEquals(5, comps[3].getOrdinal());
|
||||
assertEquals(8, comps[3].getOffset());
|
||||
|
||||
// Verify that records were properly updated by comitting and performing an undo/redo
|
||||
dataMgr.endTransaction(txId, true);
|
||||
dataMgr.undo();
|
||||
dataMgr.redo();
|
||||
txId = dataMgr.startTransaction("Continue Test");
|
||||
|
||||
assertEquals(28, struct.getLength());
|
||||
assertEquals(23, struct.getNumComponents());
|
||||
assertEquals(5, struct.getNumDefinedComponents());
|
||||
|
||||
comps = struct.getDefinedComponents();
|
||||
assertEquals(WordDataType.class, comps[3].getDataType().getClass());
|
||||
assertEquals(5, comps[3].getOrdinal());
|
||||
assertEquals(8, comps[3].getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteManyBF() throws InvalidDataTypeException {
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
|
||||
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -30,8 +30,9 @@ import ghidra.util.task.TaskMonitor;
|
||||
*/
|
||||
public class UnionDBTest extends AbstractGenericTest {
|
||||
|
||||
private DataTypeManager dataMgr;
|
||||
private StandAloneDataTypeManager dataMgr;
|
||||
private UnionDB union;
|
||||
private int txId;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@ -41,7 +42,7 @@ public class UnionDBTest extends AbstractGenericTest {
|
||||
// default data organization is little-endian
|
||||
// default BitFieldPackingImpl uses gcc conventions
|
||||
|
||||
dataMgr.startTransaction("Test");
|
||||
txId = dataMgr.startTransaction("Test");
|
||||
|
||||
union = createUnion("TestUnion");
|
||||
union.add(new ByteDataType(), "field1", "Comment1");
|
||||
@ -50,6 +51,14 @@ public class UnionDBTest extends AbstractGenericTest {
|
||||
union.add(new ByteDataType(), "field4", "Comment4");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (dataMgr != null) {
|
||||
dataMgr.endTransaction(txId, true);
|
||||
dataMgr.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void transitionToBigEndian() {
|
||||
|
||||
Union unionClone = union.clone(null);
|
||||
@ -380,6 +389,7 @@ public class UnionDBTest extends AbstractGenericTest {
|
||||
union.delete(Sets.newHashSet(2, 4));
|
||||
|
||||
assertEquals(2, union.getLength());
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
|
||||
"pack(disabled)\n" +
|
||||
@ -390,6 +400,33 @@ public class UnionDBTest extends AbstractGenericTest {
|
||||
"}\n" +
|
||||
"Length: 2 Alignment: 1", union);
|
||||
//@formatter:on
|
||||
|
||||
DataTypeComponent[] comps = union.getDefinedComponents();
|
||||
assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
|
||||
assertEquals(2, comps[2].getOrdinal());
|
||||
|
||||
// Verify that records were properly updated by comitting and performing an undo/redo
|
||||
dataMgr.endTransaction(txId, true);
|
||||
dataMgr.undo();
|
||||
dataMgr.redo();
|
||||
txId = dataMgr.startTransaction("Continue Test");
|
||||
|
||||
assertEquals(2, union.getLength());
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Union TestUnion {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
" 0 word 2 \"Comment2\"\n" +
|
||||
" 0 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 2 Alignment: 1", union);
|
||||
//@formatter:on
|
||||
|
||||
comps = union.getDefinedComponents();
|
||||
assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
|
||||
assertEquals(2, comps[2].getOrdinal());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -26,8 +26,8 @@ import javax.swing.JFormattedTextField;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.widgets.textfield.HexIntegerFormatter;
|
||||
import ghidra.feature.vt.gui.filters.IntegerFormatterFactory;
|
||||
import docking.widgets.numberformat.HexIntegerFormatter;
|
||||
import docking.widgets.numberformat.IntegerFormatterFactory;
|
||||
|
||||
public class HexIntegerFormatterTest {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user