GP-1943 - Structure Editor - added the 'Offset' column

This commit is contained in:
dragonmacher 2022-04-21 11:40:38 -04:00
parent 47f76c78d6
commit acf8a9e1bf
6 changed files with 187 additions and 145 deletions

View File

@ -74,6 +74,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
* *
* @param dataType the new composite data type. * @param dataType the new composite data type.
*/ */
@Override
public void load(Composite dataType) { public void load(Composite dataType) {
if (dataType == null) { // TODO: Why is this needed? Use case? if (dataType == null) { // TODO: Why is this needed? Use case?
return; return;
@ -104,7 +105,8 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
// Listen so we can update editor if name changes for this structure. // Listen so we can update editor if name changes for this structure.
originalCompositeId = DataTypeManager.NULL_DATATYPE_ID; originalCompositeId = DataTypeManager.NULL_DATATYPE_ID;
if (originalDTM.contains(dataType)) { if (originalDTM.contains(dataType)) {
originalCompositeId = originalDTM.getID(dataType); // Get the id if editing an existing data type. // Get the id if editing an existing data type.
originalCompositeId = originalDTM.getID(dataType);
} }
originalDTM.addDataTypeManagerListener(this); originalDTM.addDataTypeManagerListener(this);
@ -117,7 +119,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
editorStateChanged(CompositeEditorModelListener.COMPOSITE_LOADED); editorStateChanged(CompositeEditorModelListener.COMPOSITE_LOADED);
} }
/** /**
* Create view composite with the appropriate datatype manager and * Create view composite with the appropriate datatype manager and
* changes listener(s) if required. * changes listener(s) if required.
@ -393,7 +395,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
if (newDt == null) { if (newDt == null) {
return; // Was nothing and is nothing. return; // Was nothing and is nothing.
} }
if (DataTypeComponent.usesZeroLengthComponent(newDt)) { if (DataTypeComponent.usesZeroLengthComponent(newDt)) {
newLength = 0; newLength = 0;
} }
@ -401,7 +403,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
checkIsAllowableDataType(newDt); checkIsAllowableDataType(newDt);
newDt = resolveDataType(newDt, viewDTM, DataTypeConflictHandler.DEFAULT_HANDLER); newDt = resolveDataType(newDt, viewDTM, DataTypeConflictHandler.DEFAULT_HANDLER);
if (newLength < 0) { if (newLength < 0) {
// prefer previous size first // prefer previous size first
int suggestedLength = (previousLength <= 0) ? lastNumBytes : previousLength; int suggestedLength = (previousLength <= 0) ? lastNumBytes : previousLength;
@ -410,7 +412,8 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
if (sizedDataType == null) { if (sizedDataType == null) {
return; return;
} }
newDt = resolveDataType(sizedDataType.getDataType(), viewDTM, DataTypeConflictHandler.DEFAULT_HANDLER); newDt = resolveDataType(sizedDataType.getDataType(), viewDTM,
DataTypeConflictHandler.DEFAULT_HANDLER);
newLength = sizedDataType.getLength(); newLength = sizedDataType.getLength();
if (newLength <= 0) { if (newLength <= 0) {
throw new UsrException("Can't currently add this data type."); throw new UsrException("Can't currently add this data type.");
@ -430,9 +433,10 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
} }
/** /**
* Resolves the data type against the indicated data type manager using the specified conflictHandler. * Resolves the data type against the indicated data type manager using the specified
* Transactions should have already been initiated prior to calling this method. * conflictHandler. Transactions should have already been initiated prior to calling this
* If not then override this method to perform the transaction code around the resolve. * method. If not then override this method to perform the transaction code around the
* resolve.
* *
* @param dt the data type to be resolved * @param dt the data type to be resolved
* @param resolveDtm the data type manager to resolve the data type against * @param resolveDtm the data type manager to resolve the data type against

View File

@ -23,6 +23,7 @@ import java.awt.event.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays; import java.util.Arrays;
import java.util.EventObject; import java.util.EventObject;
import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
@ -42,8 +43,7 @@ import docking.widgets.fieldpanel.support.FieldRange;
import docking.widgets.fieldpanel.support.FieldSelection; import docking.widgets.fieldpanel.support.FieldSelection;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import docking.widgets.label.GLabel; import docking.widgets.label.GLabel;
import docking.widgets.table.GTable; import docking.widgets.table.*;
import docking.widgets.table.GTableCellRenderer;
import docking.widgets.textfield.GValidatedTextField; import docking.widgets.textfield.GValidatedTextField;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.datatype.DataTypeSelectionEditor; import ghidra.app.util.datatype.DataTypeSelectionEditor;
@ -555,6 +555,16 @@ public abstract class CompositeEditorPanel extends JPanel
private void createTable() { private void createTable() {
table = new CompositeTable(model); table = new CompositeTable(model);
TableColumnModel columnModel = table.getColumnModel();
if (columnModel instanceof GTableColumnModel) {
GTableColumnModel gColumnModel = (GTableColumnModel) columnModel;
List<TableColumn> hiddenColumns = model.getHiddenColumns();
for (TableColumn column : hiddenColumns) {
gColumnModel.addHiddenColumn(column);
}
}
table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
table.addMouseListener(new CompositeTableMouseListener()); table.addMouseListener(new CompositeTableMouseListener());

View File

@ -16,11 +16,11 @@
package ghidra.app.plugin.core.compositeeditor; package ghidra.app.plugin.core.compositeeditor;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import docking.widgets.fieldpanel.support.FieldRange; import docking.widgets.fieldpanel.support.FieldRange;
import docking.widgets.fieldpanel.support.FieldSelection; import docking.widgets.fieldpanel.support.FieldSelection;
@ -34,9 +34,10 @@ import utility.function.Callback;
abstract class CompositeViewerModel extends AbstractTableModel abstract class CompositeViewerModel extends AbstractTableModel
implements DataTypeManagerChangeListener { implements DataTypeManagerChangeListener {
/** Flag indicating that the model is updating the selection and /**
* should ignore any attempts to set the selection until it is no * Flag indicating that the model is updating the selection and should ignore any attempts to
* longer updating. */ * set the selection until it is no longer updating.
*/
protected boolean updatingSelection = false; protected boolean updatingSelection = false;
protected Composite originalComposite; protected Composite originalComposite;
@ -68,7 +69,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
/** Offset of each component field. */ /** Offset of each component field. */
protected int[] columnOffsets = new int[headers.length]; protected int[] columnOffsets = new int[headers.length];
/** Width of each component field. */ /** Width of each component field. */
protected int[] columnWidths = { 75, 75, 100, 100, 100, 150 }; // Initial default column widths. protected int[] columnWidths = { 75, 75, 100, 100, 100, 150 }; // Initial default column widths
/** Total component area width. */ /** Total component area width. */
protected int width = 0; protected int width = 0;
/** Width of left margin in pixels for the component area. */ /** Width of left margin in pixels for the component area. */
@ -88,10 +89,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns <code>String.class</code> regardless of <code>columnIndex</code>. * Returns <code>String.class</code> regardless of <code>columnIndex</code>.
* *
* @param columnIndex the column being queried * @param columnIndex the column being queried
* @return the String.class * @return the String.class
*/ */
@Override @Override
public Class<?> getColumnClass(int columnIndex) { public Class<?> getColumnClass(int columnIndex) {
@ -125,6 +126,11 @@ abstract class CompositeViewerModel extends AbstractTableModel
return COMMENT; return COMMENT;
} }
// subclasses may supply columns
protected List<TableColumn> getHiddenColumns() {
return Collections.emptyList();
}
/** /**
* Terminates listening for category change events within the model. * Terminates listening for category change events within the model.
*/ */
@ -135,19 +141,18 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns whether or not the editor has a structure loaded. * Returns whether or not the editor has a structure loaded. If no structure is loaded then
* If no structure is loaded then only unload() or dispose() methods * only unload() or dispose() methods should be called.
* should be called. *
* @return true if an editable structure is currently loaded in the model. * @return true if an editable structure is currently loaded in the model.
*/ */
public boolean isLoaded() { public boolean isLoaded() {
return (viewComposite != null); return (viewComposite != null);
} }
/** /**
* Updates the model to now view the indicated data structure. * Updates the model to now view the indicated data structure. This method should cleanup from
* This method should cleanup from a previous load if neccessary * a previous load if necessary and must initialize the following model state:
* and must initilize the following model state:
* <ul> * <ul>
* <li>originalComposite</li> * <li>originalComposite</li>
* <li>originalDataTypePath</li> * <li>originalDataTypePath</li>
@ -188,8 +193,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Resolves the indicated data type against the working copy in the * Resolves the indicated data type against the working copy in the viewer's data type manager.
* viewer's data type manager.
* @param dataType the data type * @param dataType the data type
* @return the working copy of the data type. * @return the working copy of the data type.
*/ */
@ -328,8 +332,8 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Return the size of the structure being viewed in bytes as a hex or decimal string * Return the size of the structure being viewed in bytes as a hex or decimal string depending
* depending on the model's current display setting for numbers * on the model's current display setting for numbers
* @return the length * @return the length
*/ */
public String getLengthAsString() { public String getLengthAsString() {
@ -354,10 +358,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Return a header name for the indicated column. * Return a header name for the indicated column.
* *
* @param columnIndex the index number indicating the component field (column) * @param columnIndex the index number indicating the component field (column) to get the
* to get the header for. * header for.
*/ */
@Override @Override
public String getColumnName(int columnIndex) { public String getColumnName(int columnIndex) {
@ -365,25 +369,36 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Return a header name for the indicated field (column) * Return a header name for the indicated field (column)
* *
* @param columnIndex the index number indicating the component field (column) * @param columnIndex the index number indicating the component field (column) to get the
* to get the header for * header for
* @return the name * @return the name
*/ */
public String getFieldName(int columnIndex) { public String getFieldName(int columnIndex) {
if (columnIndex < 0 || columnIndex > getColumnCount()) { if (columnIndex < 0) {
return "UNKNOWN"; return "UNKNOWN";
} }
return headers[columnIndex]; if (columnIndex < headers.length) {
return headers[columnIndex];
}
List<TableColumn> hiddenColumns = getHiddenColumns();
for (TableColumn c : hiddenColumns) {
int modelIndex = c.getModelIndex();
if (modelIndex == columnIndex) {
return Objects.toString(c.getHeaderValue());
}
}
return "UNKNOWN";
} }
/** /**
* Returns whether or not a particular component row and field in this * Returns whether or not a particular component row and field in this structure is an editable
* structure is an editable type of cell. However the cell still * type of cell. However the cell still may not be editable currently. To check if the cell can
* may not be editable currently. To check if the cell can actually be * actually be edited call isCellEditable().
* edited call isCellEditable().
* *
* @param rowIndex the row index of the component * @param rowIndex the row index of the component
* @param columnIndex the index for the field of the component * @param columnIndex the index for the field of the component
@ -394,12 +409,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns whether or not a particular component row and field in this * Returns whether or not a particular component row and field in this structure is editable
* structure is editable
* *
* @param rowIndex index for the row (component within this structure). * @param rowIndex index for the row (component within this structure).
* @param columnIndex index for the column (field of the component within * @param columnIndex index for the column (field of the component within this structure).
* this structure).
*/ */
@Override @Override
public boolean isCellEditable(int rowIndex, int columnIndex) { public boolean isCellEditable(int rowIndex, int columnIndex) {
@ -407,7 +420,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Gets the display offset of the component field at the specified column index * Gets the display offset of the component field at the specified column index.
* *
* @param columnIndex the field index within the component. * @param columnIndex the field index within the component.
* @return the offset in pixels of the component field. * @return the offset in pixels of the component field.
@ -460,10 +473,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Gets the display width of the component field at the specified column index * Gets the display width of the component field at the specified column index.
* *
* @param columnIndex the field index within the component. * @param columnIndex the field index within the component
* @return the width of the component field. * @return the width of the component field
*/ */
public int getFieldWidth(int columnIndex) { public int getFieldWidth(int columnIndex) {
return ((columnIndex < 0 || columnIndex >= getColumnCount()) ? 0 return ((columnIndex < 0 || columnIndex >= getColumnCount()) ? 0
@ -471,10 +484,9 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns the number of component rows in the viewer. There may be a * Returns the number of component rows in the viewer. There may be a blank row at the end for
* blank row at the end for selecting. Therefore this number can be * selecting. Therefore this number can be different than the actual number of components
* different than the actual number of components currently in the * currently in the structure being viewed.
* structure being viewed.
* *
* @return the number of rows in the model * @return the number of rows in the model
*/ */
@ -492,11 +504,11 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Return the nth component for the structure being viewed. Since the number of rows * Return the nth component for the structure being viewed. Since the number of rows can exceed
* can exceed the number of components defined within the composite * the number of components defined within the composite ({@link Composite#getNumComponents()})
* ({@link Composite#getNumComponents()}) this method will return null for a blank * this method will return null for a blank row.
* row. *
* @param rowIndex the index of the component to return. First component is index of 0. * @param rowIndex the index of the component to return. First component is index of 0
* @return the component * @return the component
*/ */
public DataTypeComponent getComponent(int rowIndex) { public DataTypeComponent getComponent(int rowIndex) {
@ -507,10 +519,10 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns the number of columns (display fields) for each component in * Returns the number of columns (display fields) for each component in this structure or
* this structure or union. * union.
* *
* @return the number of display fields for each component. * @return the number of display fields for each component
*/ */
@Override @Override
public int getColumnCount() { public int getColumnCount() {
@ -520,7 +532,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
/** /**
* Returns the number of display fields for this structure or union. * Returns the number of display fields for this structure or union.
* *
* @return the number of display fields for each component. * @return the number of display fields for each component
*/ */
public int getNumFields() { public int getNumFields() {
return getColumnCount(); return getColumnCount();
@ -573,7 +585,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns the current dataType name (Structure or Union) as a string * Returns the current dataType name (Structure or Union) as a string.
* @return the name * @return the name
*/ */
protected String getTypeName() { protected String getTypeName() {
@ -581,7 +593,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns the current status string * Returns the current status string.
* @return the status * @return the status
*/ */
public String getStatus() { public String getStatus() {
@ -597,10 +609,9 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Sets the current status string and performs notification to all * Sets the current status string and performs notification to all listeners.
* CompositeModelStatusListeners.
* @param status the status message * @param status the status message
* @param beep true indicates an audible beep should sound when the message is displayed. * @param beep true indicates an audible beep should sound when the message is displayed
*/ */
public void setStatus(String status, boolean beep) { public void setStatus(String status, boolean beep) {
if (status == null) { if (status == null) {
@ -623,9 +634,9 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Fixes up the original name and category because a program restoration may * Fixes up the original name and category because a program restoration may have changed the
* have changed the original composite. * original composite.
* @param composite the restored copy of our original composite. * @param composite the restored copy of our original composite
*/ */
protected void fixupOriginalPath(Composite composite) { protected void fixupOriginalPath(Composite composite) {
String newName = composite.getName(); String newName = composite.getName();
@ -659,8 +670,8 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Called whenever the composite's non-component information changes. * Called whenever the composite's non-component information changes. For example, the name,
* For example, the name, or description change. * or description change.
*/ */
protected void compositeInfoChanged() { protected void compositeInfoChanged() {
notify(modelListeners, CompositeViewerModelListener::compositeInfoChanged); notify(modelListeners, CompositeViewerModelListener::compositeInfoChanged);
@ -674,8 +685,8 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Determines the full path name for the composite data type based on * Determines the full path name for the composite data type based on the original composite
* the original composite and original category. * and original category.
* @return the full path name * @return the full path name
*/ */
public final DataTypePath getOriginalDataTypePath() { public final DataTypePath getOriginalDataTypePath() {
@ -837,10 +848,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
fireTableDataChanged(); fireTableDataChanged();
componentDataChanged(); componentDataChanged();
} }
catch (InvalidNameException e) { catch (InvalidNameException | DuplicateNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
catch (DuplicateNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
} }
} }
@ -947,22 +955,23 @@ abstract class CompositeViewerModel extends AbstractTableModel
@Override @Override
public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) { public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) {
// Don't care. // Don't care.
} }
//=================================================================================================
// Helper methods for CategoryChangeListener methods.
//=================================================================================================
/*******************************************************************
* Helper methods for CategoryChangeListener methods.
********************************************************************/
/** /**
* Determines whether the indicated composite data type has any * Determines whether the indicated composite data type has any sub-components that are within
* sub-components that are within the indicated category or one * the indicated category or one of its sub-categories.
* of its sub-categories.
* @param parentDt the composite data type * @param parentDt the composite data type
* @param catPath the category's path. * @param catPath the category's path
* @return true if a sub-component is in the indicated category. * @return true if a sub-component is in the indicated category
*/ */
boolean hasSubDtInCategory(Composite parentDt, String catPath) { boolean hasSubDtInCategory(Composite parentDt, String catPath) {
DataTypeComponent components[] = parentDt.getDefinedComponents(); DataTypeComponent components[] = parentDt.getDefinedComponents();
// FUTURE Add a structure to keep track of which composites were searched so they aren't searched multiple times. // FUTURE Add a structure to keep track of which composites were searched so they aren't
// searched multiple times.
for (DataTypeComponent component : components) { for (DataTypeComponent component : components) {
DataType subDt = component.getDataType(); DataType subDt = component.getDataType();
String subCatPath = subDt.getCategoryPath().getPath(); String subCatPath = subDt.getCategoryPath().getPath();
@ -979,11 +988,11 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Determines whether the indicated composite data type has any * Determines whether the indicated composite data type has any sub-components that are the
* sub-components that are the indicated data type. * indicated data type.
* @param parentDt the composite data type * @param parentDt the composite data type
* @param dtPath the data type to be detected. * @param dtPath the data type to be detected
* @return true if the composite data type has the data type as a sub-component. * @return true if the composite data type has the data type as a sub-component
*/ */
protected boolean hasSubDt(Composite parentDt, DataTypePath dtPath) { protected boolean hasSubDt(Composite parentDt, DataTypePath dtPath) {
DataTypeComponent components[] = parentDt.getDefinedComponents(); DataTypeComponent components[] = parentDt.getDefinedComponents();
@ -1032,6 +1041,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* Returns the number of rows currently selected. * Returns the number of rows currently selected.
* *
* <p>Note: In unlocked mode this can include the additional blank line. * <p>Note: In unlocked mode this can include the additional blank line.
*
* @return the selected row count * @return the selected row count
*/ */
public int getNumSelectedRows() { public int getNumSelectedRows() {
@ -1048,6 +1058,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
* Returns the number of component rows currently selected. * Returns the number of component rows currently selected.
* *
* <p>Note: This only includes rows that are actually components. * <p>Note: This only includes rows that are actually components.
*
* @return the selected row count * @return the selected row count
*/ */
public int getNumSelectedComponentRows() { public int getNumSelectedComponentRows() {
@ -1065,7 +1076,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns true if the component list selection is contiguous * Returns true if the component list selection is contiguous.
* @return true if contiguous * @return true if contiguous
*/ */
public boolean isContiguousSelection() { public boolean isContiguousSelection() {
@ -1073,7 +1084,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns true if the component list selection is a single component * Returns true if the component list selection is a single component.
* @return true if the component list selection is a single component * @return true if the component list selection is a single component
*/ */
public boolean isSingleComponentRowSelection() { public boolean isSingleComponentRowSelection() {
@ -1086,7 +1097,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns true if the selection is a single row * Returns true if the selection is a single row.
* @return true if the selection is a single row * @return true if the selection is a single row
*/ */
public boolean isSingleRowSelection() { public boolean isSingleRowSelection() {
@ -1098,7 +1109,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns true if the list selection is contiguous and only contains component rows * 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 * @return true if the list selection is contiguous and only contains component rows
*/ */
public boolean isContiguousComponentSelection() { public boolean isContiguousComponentSelection() {
@ -1107,7 +1118,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Get an array of the indices for all the selected rows * Get an array of the indices for all the selected rows.
* @return the selected rows * @return the selected rows
*/ */
public int[] getSelectedRows() { public int[] getSelectedRows() {
@ -1123,7 +1134,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Get an array of the row indices for all the selected components * Get an array of the row indices for all the selected components.
* @return the selected rows * @return the selected rows
*/ */
public int[] getSelectedComponentRows() { public int[] getSelectedComponentRows() {
@ -1140,8 +1151,8 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Returns the selection range containing the specified row index * Returns the selection range containing the specified row index if there is one that contains
* if there is one that contains it. Otherwise, returns null. * it. Otherwise, returns null.
* *
* @param rowIndex the row index * @param rowIndex the row index
* @return the range or null * @return the range or null
@ -1164,7 +1175,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Gets the minimum row index that is selected or -1 if no index is selected * Gets the minimum row index that is selected or -1 if no index is selected.
* @return the index * @return the index
*/ */
public int getMinIndexSelected() { public int getMinIndexSelected() {
@ -1176,7 +1187,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Saves the current selection in the structure components viewing area * Saves the current selection in the structure components viewing area.
* *
* @param rows the indices for the selected rows. * @param rows the indices for the selected rows.
*/ */
@ -1202,8 +1213,8 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Sets the model's current selection to the indicated selection. * Sets the model's current selection to the indicated selection. If the selection is empty,
* If the selection is empty, it gets adjusted to the empty last line when in unlocked mode. * it gets adjusted to the empty last line when in unlocked mode.
* @param selection the new selection * @param selection the new selection
*/ */
public void setSelection(FieldSelection selection) { public void setSelection(FieldSelection selection) {
@ -1269,7 +1280,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 now if swing or later if not.
* @param r the runnable * @param r the runnable
*/ */
protected void swing(Runnable r) { protected void swing(Runnable r) {
@ -1277,8 +1288,8 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* A notify method to take the listens to notify, along with the method that should be * A notify method to take the listens to notify, along with the method that should be called
* called on each listener * on each listener.
* *
* @param <T> the type of the listener * @param <T> the type of the listener
* @param listeners the listeners * @param listeners the listeners
@ -1293,8 +1304,8 @@ abstract class CompositeViewerModel extends AbstractTableModel
} }
/** /**
* Sets whether or not the editor displays numeric values in hexadecimal. * Sets whether or not the editor displays numeric values in hexadecimal.
* @param showHex true means show in hexadecimal. false means show in decimal. * @param showHex true means show in hexadecimal. false means show in decimal
*/ */
public void displayNumbersInHex(boolean showHex) { public void displayNumbersInHex(boolean showHex) {
if (this.showHexNumbers != showHex) { if (this.showHexNumbers != showHex) {

View File

@ -16,14 +16,16 @@
package ghidra.app.plugin.core.compositeeditor; package ghidra.app.plugin.core.compositeeditor;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays; import java.util.*;
import java.util.NoSuchElementException;
import javax.swing.table.TableColumn;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import docking.widgets.dialogs.InputDialog; import docking.widgets.dialogs.InputDialog;
import docking.widgets.dialogs.InputDialogListener; import docking.widgets.dialogs.InputDialogListener;
import docking.widgets.fieldpanel.support.FieldRange; import docking.widgets.fieldpanel.support.FieldRange;
import docking.widgets.fieldpanel.support.FieldSelection; import docking.widgets.fieldpanel.support.FieldSelection;
import docking.widgets.table.GTableHeaderRenderer;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.InsufficientBytesException; import ghidra.program.model.lang.InsufficientBytesException;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -39,6 +41,9 @@ class StructureEditorModel extends CompEditorModel {
private static final int DATATYPE = 3; private static final int DATATYPE = 3;
private static final int FIELDNAME = 4; private static final int FIELDNAME = 4;
private static final int COMMENT = 5; private static final int COMMENT = 5;
private static final int ORDINAL = 6;
private List<TableColumn> hiddenColumns;
StructureEditorModel(StructureEditorProvider provider, boolean showHexNumbers) { StructureEditorModel(StructureEditorProvider provider, boolean showHexNumbers) {
super(provider); super(provider);
@ -47,6 +52,18 @@ class StructureEditorModel extends CompEditorModel {
columnOffsets = new int[headers.length]; columnOffsets = new int[headers.length];
adjustOffsets(); adjustOffsets();
this.showHexNumbers = showHexNumbers; this.showHexNumbers = showHexNumbers;
List<TableColumn> additionalColumns = new ArrayList<>();
TableColumn ordinalColumn = new TableColumn(ORDINAL, 75);
ordinalColumn.setHeaderRenderer(new GTableHeaderRenderer());
ordinalColumn.setHeaderValue("Ordinal");
additionalColumns.add(ordinalColumn);
hiddenColumns = Collections.unmodifiableList(additionalColumns);
}
@Override
protected List<TableColumn> getHiddenColumns() {
return hiddenColumns;
} }
@Override @Override
@ -110,11 +127,7 @@ class StructureEditorModel extends CompEditorModel {
@Override @Override
public Object getValueAt(int rowIndex, int columnIndex) { public Object getValueAt(int rowIndex, int columnIndex) {
if ((viewComposite == null) || (rowIndex < 0) || (columnIndex < 0) || if ((viewComposite == null) || (rowIndex < 0) || (columnIndex < 0)) {
(columnIndex >= getColumnCount())) {
if (columnIndex == getDataTypeColumn()) {
return null;
}
return ""; return "";
} }
@ -155,6 +168,10 @@ class StructureEditorModel extends CompEditorModel {
else if (columnIndex == getCommentColumn()) { else if (columnIndex == getCommentColumn()) {
value = dtc.getComment(); value = dtc.getComment();
} }
else if (columnIndex == ORDINAL) {
int ordinal = dtc.getOrdinal();
value = showHexNumbers ? getHexString(ordinal, true) : Integer.toString(ordinal);
}
return (value == null) ? "" : value; return (value == null) ? "" : value;
} }
@ -671,7 +688,6 @@ class StructureEditorModel extends CompEditorModel {
1 == currentRange.getEnd().getIndex().intValue()); 1 == currentRange.getEnd().getIndex().intValue());
if (isOneComponent) { if (isOneComponent) {
// TODO
if (!isShowingUndefinedBytes() || isAtEnd(currentIndex) || if (!isShowingUndefinedBytes() || isAtEnd(currentIndex) ||
onlyUndefinedsUntilEnd(currentIndex + 1)) { onlyUndefinedsUntilEnd(currentIndex + 1)) {
return true; // allow replace of component when aligning. return true; // allow replace of component when aligning.

View File

@ -116,7 +116,6 @@ public class GTable extends JTable {
* Constructs a new GTable * Constructs a new GTable
*/ */
public GTable() { public GTable() {
super();
init(); init();
} }
@ -721,9 +720,6 @@ public class GTable extends JTable {
} }
} }
/**
* @see javax.swing.JComponent#getToolTipText(java.awt.event.MouseEvent)
*/
@Override @Override
public String getToolTipText(MouseEvent e) { public String getToolTipText(MouseEvent e) {
String str = super.getToolTipText(e); String str = super.getToolTipText(e);
@ -873,10 +869,10 @@ public class GTable extends JTable {
} }
/** /**
* Performs custom work to locate renderers for special table model types. This method * Performs custom work to locate renderers for special table model types. This method allows
* allows clients to bypass the {@link #getCellRenderer(int, int)}, which is sometimes * clients to bypass the {@link #getCellRenderer(int, int)}, which is sometimes overridden by
* overridden by subclasses to return a hard-coded renderer. In that case, some clients * subclasses to return a hard-coded renderer. In that case, some clients still want a way to
* still want a way to perform normal cell renderer lookup. * perform normal cell renderer lookup.
* *
* @param row the row * @param row the row
* @param col the column * @param col the column
@ -895,11 +891,9 @@ public class GTable extends JTable {
} }
/** /**
* If you just begin typing into an editable cell in * If you just begin typing into an editable cell in a JTable, then the cell editor will be
* a JTable, then the cell editor will be displayed. However, * displayed. However, the editor component will not have a focus. This method has been
* the editor component will not have a focus. This * overridden to request focus on the editor component.
* method has been overridden to request
* focus on the editor component.
* *
* @see javax.swing.JTable#editCellAt(int, int) * @see javax.swing.JTable#editCellAt(int, int)
*/ */
@ -1057,18 +1051,16 @@ public class GTable extends JTable {
} }
/** /**
* Maintain a {@link docking.widgets.table.GTableCellRenderingData} object * Maintain a {@link docking.widgets.table.GTableCellRenderingData} object associated with each
* associated with each column that maintains some state and references to * column that maintains some state and references to useful data. These objects are created as
* useful data. These objects are created as needed, stored by the table for * needed, stored by the table for convenient re-use and to prevent per-cell creation, and
* convenient re-use and to prevent per-cell creation, and cleared when columns * cleared when columns are removed from the table.
* are removed from the table.
* <p> * <p>
* Row and cell state is cleared before returning to the caller to ensure * Row and cell state is cleared before returning to the caller to ensure consistent state;
* consistent state; when the client is done rendering a cell, row and cell * when the client is done rendering a cell, row and cell state should also be cleared to
* state should also be cleared to minimize references. * minimize references.
* *
* @param viewColumn * @param viewColumn the columns' view index
* The columns' view index
* @return Data specific to the column. Row state is cleared before returning. * @return Data specific to the column. Row state is cleared before returning.
*/ */
GTableCellRenderingData getRenderingData(int viewColumn) { GTableCellRenderingData getRenderingData(int viewColumn) {

View File

@ -151,10 +151,7 @@ public class GTableColumnModel
@Override @Override
public void addColumn(TableColumn aColumn) { public void addColumn(TableColumn aColumn) {
if (aColumn == null) { Objects.requireNonNull(aColumn);
throw new IllegalArgumentException("Object is null");
}
removeColumnWithModelIndex(aColumn.getModelIndex()); // dedup removeColumnWithModelIndex(aColumn.getModelIndex()); // dedup
completeList.add(aColumn); completeList.add(aColumn);
@ -169,6 +166,18 @@ public class GTableColumnModel
columnModelState.restoreState(); columnModelState.restoreState();
} }
/**
* Adds a table column to this model that is not visible default. This column may be made
* visible later by the user or by the system restoring a previously used visible column state.
* @param aColumn the column
*/
public void addHiddenColumn(TableColumn aColumn) {
Objects.requireNonNull(aColumn);
removeColumnWithModelIndex(aColumn.getModelIndex()); // dedup
completeList.add(aColumn);
aColumn.addPropertyChangeListener(this);
}
/** Finds the table's column with the given model index */ /** Finds the table's column with the given model index */
private TableColumn getColumnFromModelIndex(int modelIndex) { private TableColumn getColumnFromModelIndex(int modelIndex) {
for (TableColumn tableColumn : completeList) { for (TableColumn tableColumn : completeList) {