diff --git a/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm b/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm index 59f73b54a7..37cb9db6cc 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm @@ -932,7 +932,12 @@ from the Listing Display options panel; select Underline from the Screen Element list.

- +

Wrap on Semicolons - Option to wrap operand fields on semicolons. Some processors + have multiple sub instructions encoded at the same address. Normally, these are shown on + one line and the additional instructions are all shown within the operand field and + separated by semicolons. With this option on, each follow on instruction will be displayed + on its own line within the operand field. +

PCode Field

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/processors/generic/PcodeFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/processors/generic/PcodeFieldFactory.java index bc2996c9bd..46e426ce1e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/processors/generic/PcodeFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/processors/generic/PcodeFieldFactory.java @@ -55,7 +55,8 @@ public class PcodeFieldFactory extends FieldFactory { } public PcodeFieldFactory(String name, FieldFormatModel model, - ListingHighlightProvider highlightProvider, Options displayOptions, Options fieldOptions) { + ListingHighlightProvider highlightProvider, Options displayOptions, + Options fieldOptions) { super(name, model, highlightProvider, displayOptions, fieldOptions); setWidth(300); @@ -67,7 +68,8 @@ public class PcodeFieldFactory extends FieldFactory { } @Override - public FieldFactory newInstance(FieldFormatModel myModel, ListingHighlightProvider highlightProvider, + public FieldFactory newInstance(FieldFormatModel myModel, + ListingHighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions) { return new PcodeFieldFactory(FIELD_NAME, myModel, highlightProvider, options, fieldOptions); } @@ -81,7 +83,7 @@ public class PcodeFieldFactory extends FieldFactory { } Instruction instr = (Instruction) obj; - ArrayList elements = new ArrayList<>(); + List elements = new ArrayList<>(); List pcodeListing = formatter.formatOps(instr.getProgram().getLanguage(), instr.getProgram().getAddressFactory(), Arrays.asList(instr.getPcode(true))); @@ -91,9 +93,8 @@ public class PcodeFieldFactory extends FieldFactory { } if (elements.size() > 0) { - FieldElement[] textElements = elements.toArray(new FieldElement[elements.size()]); - return ListingTextField.createMultilineTextField(this, proxy, textElements, - startX + varWidth, width, Integer.MAX_VALUE, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, elements, + startX + varWidth, width, hlProvider); } return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AssignedVariableFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AssignedVariableFieldFactory.java index 41e30d1c11..509be1e955 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AssignedVariableFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AssignedVariableFieldFactory.java @@ -50,7 +50,8 @@ public class AssignedVariableFieldFactory extends FieldFactory { * @param displayOptions the Options for display properties. * @param fieldOptions the Options for field specific properties. */ - private AssignedVariableFieldFactory(FieldFormatModel model, ListingHighlightProvider hsProvider, + private AssignedVariableFieldFactory(FieldFormatModel model, + ListingHighlightProvider hsProvider, Options displayOptions, Options fieldOptions) { super(FIELD_NAME, model, hsProvider, displayOptions, fieldOptions); } @@ -70,7 +71,7 @@ public class AssignedVariableFieldFactory extends FieldFactory { } CodeUnit cu = (CodeUnit) obj; - ArrayList elemenetList = new ArrayList<>(); + ArrayList elements = new ArrayList<>(); Function f = cu.getProgram().getFunctionManager().getFunctionContaining(cu.getMinAddress()); if (f != null) { @@ -89,19 +90,16 @@ public class AssignedVariableFieldFactory extends FieldFactory { buf.append(var.getName()); AttributedString as = new AttributedString(buf.toString(), FunctionColors.VARIABLE_ASSIGNED, getMetrics()); - elemenetList.add(new TextFieldElement(as, 0, 0)); + elements.add(new TextFieldElement(as, 0, 0)); } } } - if (elemenetList.size() == 0) { + if (elements.isEmpty()) { return null; } - FieldElement[] elements = new FieldElement[elemenetList.size()]; - elemenetList.toArray(elements); - return ListingTextField.createMultilineTextField(this, proxy, elements, startX + varWidth, - width, elements.length + 1, hlProvider); + width, hlProvider); } @Override @@ -134,7 +132,8 @@ public class AssignedVariableFieldFactory extends FieldFactory { } @Override - public FieldFactory newInstance(FieldFormatModel formatModel, ListingHighlightProvider hsProvider, + public FieldFactory newInstance(FieldFormatModel formatModel, + ListingHighlightProvider hsProvider, ToolOptions displayOptions, ToolOptions fieldOptions) { return new AssignedVariableFieldFactory(formatModel, hsProvider, displayOptions, fieldOptions); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/EolCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/EolCommentFieldFactory.java index 884f8a6c45..9a31a1bcd1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/EolCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/EolCommentFieldFactory.java @@ -292,11 +292,7 @@ public class EolCommentFieldFactory extends FieldFactory { elementList.addAll(elements); } - FieldElement[] fieldElements = elementList.toArray(new FieldElement[elementList.size()]); - if (fieldElements.length == 0) { - return null; - } - return ListingTextField.createMultilineTextField(this, proxy, fieldElements, x, width, + return ListingTextField.createMultilineTextField(this, proxy, elementList, x, width, maxDisplayLines, hlProvider); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionRepeatableCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionRepeatableCommentFieldFactory.java index 53d2c7bed4..6afa8383dc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionRepeatableCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionRepeatableCommentFieldFactory.java @@ -16,6 +16,8 @@ package ghidra.app.util.viewer.field; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; import docking.widgets.fieldpanel.field.AttributedString; import docking.widgets.fieldpanel.field.FieldElement; @@ -70,16 +72,16 @@ public class FunctionRepeatableCommentFieldFactory extends FieldFactory { Function f = (Function) obj; Program program = f.getProgram(); String[] commentArr = f.getRepeatableCommentAsArray(); - FieldElement[] fields = new FieldElement[commentArr.length]; + List fields = new ArrayList<>(); AttributedString prototype = new AttributedString("prototype", CommentColors.REPEATABLE, getMetrics()); for (int i = 0; i < commentArr.length; i++) { - fields[i] = CommentUtils.parseTextForAnnotations(commentArr[i], program, prototype, i); + fields.add(CommentUtils.parseTextForAnnotations(commentArr[i], program, prototype, i)); } if (commentArr.length > 0) { return ListingTextField.createMultilineTextField(this, proxy, fields, x, width, - Integer.MAX_VALUE, hlProvider); + hlProvider); } return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/InstructionMaskValueFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/InstructionMaskValueFieldFactory.java index 52f4c23d7f..b0efae3004 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/InstructionMaskValueFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/InstructionMaskValueFieldFactory.java @@ -17,6 +17,8 @@ package ghidra.app.util.viewer.field; import java.awt.Color; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.support.FieldLocation; @@ -52,7 +54,8 @@ public class InstructionMaskValueFieldFactory extends FieldFactory { * @param displayOptions the Options for display properties. * @param fieldOptions the Options for field specific properties. */ - private InstructionMaskValueFieldFactory(FieldFormatModel model, ListingHighlightProvider hsProvider, + private InstructionMaskValueFieldFactory(FieldFormatModel model, + ListingHighlightProvider hsProvider, Options displayOptions, Options fieldOptions) { super(FIELD_NAME, model, hsProvider, displayOptions, fieldOptions); } @@ -88,21 +91,21 @@ public class InstructionMaskValueFieldFactory extends FieldFactory { } try { - FieldElement[] fieldElements = new FieldElement[2 * (operandCount + 1)]; - fieldElements[0] = - getLine("M[m]: ", instructionMask.getBytes(), MaskColors.BITS, proxy, varWidth); - fieldElements[1] = - getLine("V[m]: ", instructionMask.applyMask(instr), MaskColors.VALUE, proxy, - varWidth); + List elements = new ArrayList<>(); + elements.add( + getLine("M[m]: ", instructionMask.getBytes(), MaskColors.BITS, proxy, varWidth)); + elements.add(getLine("V[m]: ", instructionMask.applyMask(instr), MaskColors.VALUE, + proxy, varWidth)); + for (int i = 0; i < operandCount; i++) { - fieldElements[2 * (i + 1)] = getLine("M[" + i + "]: ", operandMasks[i].getBytes(), - MaskColors.BITS, proxy, varWidth); - fieldElements[2 * (i + 1) + 1] = getLine("V[" + i + "]: ", - operandMasks[i].applyMask(instr), MaskColors.VALUE, proxy, varWidth); + elements.add(getLine("M[" + i + "]: ", operandMasks[i].getBytes(), MaskColors.BITS, + proxy, varWidth)); + elements.add(getLine("V[" + i + "]: ", operandMasks[i].applyMask(instr), + MaskColors.VALUE, proxy, varWidth)); } - return ListingTextField.createMultilineTextField(this, proxy, fieldElements, - startX + varWidth, width, fieldElements.length, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, elements, + startX + varWidth, width, hlProvider); } catch (MemoryAccessException e) { return null; @@ -165,7 +168,8 @@ public class InstructionMaskValueFieldFactory extends FieldFactory { } @Override - public FieldFactory newInstance(FieldFormatModel formatModel, ListingHighlightProvider hsProvider, + public FieldFactory newInstance(FieldFormatModel formatModel, + ListingHighlightProvider hsProvider, ToolOptions toolOptions, ToolOptions fieldOptions) { return new InstructionMaskValueFieldFactory(formatModel, hsProvider, toolOptions, fieldOptions); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java index cd5d7b0ee3..a615cfc28c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java @@ -181,8 +181,7 @@ public class LabelFieldFactory extends FieldFactory { return null; } - FieldElement[] textElements = new FieldElement[length]; - int nextPos = 0; + List elements = new ArrayList<>(length); if (hasOffcuts) { for (Address offcut : offcuts) { @@ -193,7 +192,7 @@ public class LabelFieldFactory extends FieldFactory { inspector.getOffcutSymbolColor(), getMetrics(inspector.getOffcutSymbolStyle()), false, null); } - textElements[nextPos++] = new TextFieldElement(as, nextPos, 0); + elements.add(new TextFieldElement(as, elements.size(), 0)); } } @@ -206,11 +205,11 @@ public class LabelFieldFactory extends FieldFactory { ColorAndStyle c = inspector.getColorAndStyle(symbol); AttributedString as = new AttributedString(icon, checkLabelString(symbol, prog), c.getColor(), getMetrics(c.getStyle()), false, null); - textElements[nextPos++] = new TextFieldElement(as, nextPos, 0); + elements.add(new TextFieldElement(as, elements.size(), 0)); } - return ListingTextField.createMultilineTextField(this, proxy, textElements, x, width, - Integer.MAX_VALUE, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, elements, x, width, + hlProvider); } private String getOffsetText(CodeUnit cu, Address currAddr, Address offcutAddress) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ListingTextField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ListingTextField.java index 777f8bc923..b2de120747 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ListingTextField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ListingTextField.java @@ -125,10 +125,10 @@ public class ListingTextField implements ListingField, TextField { } /** - * Displays the given array of text, each on its own line. + * Displays the given List of text elements, each on its own line. * @param factory the field factory that generated this field * @param proxy the object used to populate this field - * @param textElements the array of elements for the field. + * @param textElements the list of text elements * Each of these holds text, attributes and location information. * @param startX the starting X position of the field * @param width the width of the field @@ -137,18 +137,36 @@ public class ListingTextField implements ListingField, TextField { * @return the text field. */ public static ListingTextField createMultilineTextField(FieldFactory factory, ProxyObj proxy, - FieldElement[] textElements, int startX, int width, int maxLines, + List textElements, int startX, int width, int maxLines, ListingHighlightProvider provider) { ListingFieldHighlightFactoryAdapter hlFactory = new ListingFieldHighlightFactoryAdapter(provider); - List list = Arrays.asList(textElements); TextField field = - new VerticalLayoutTextField(list, startX, width, maxLines, hlFactory); + new VerticalLayoutTextField(textElements, startX, width, maxLines, hlFactory); ListingTextField listingField = new ListingTextField(factory, proxy, field, hlFactory); return listingField; } + /** + * Displays the given List of text elements, each on its own line with no max line restriction + * @param factory the field factory that generated this field + * @param proxy the object used to populate this field + * @param textElements the list of text elements + * Each of these holds text, attributes and location information. + * @param startX the starting X position of the field + * @param width the width of the field + * @param provider the highlight provider + * @return the text field. + */ + public static ListingTextField createMultilineTextField(FieldFactory factory, ProxyObj proxy, + List textElements, int startX, int width, + ListingHighlightProvider provider) { + + return ListingTextField.createMultilineTextField(factory, proxy, textElements, startX, + width, Integer.MAX_VALUE, provider); + } + protected ListingTextField(FieldFactory factory, ProxyObj proxy, TextField field, ListingFieldHighlightFactoryAdapter hlFactory) { this.factory = factory; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/MemoryBlockStartFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/MemoryBlockStartFieldFactory.java index f90778b0f0..457a6082be 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/MemoryBlockStartFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/MemoryBlockStartFieldFactory.java @@ -56,7 +56,8 @@ public class MemoryBlockStartFieldFactory extends FieldFactory { * @param displayOptions the Options for display properties. * @param fieldOptions the Options for field specific properties. */ - private MemoryBlockStartFieldFactory(FieldFormatModel model, ListingHighlightProvider hlProvider, + private MemoryBlockStartFieldFactory(FieldFormatModel model, + ListingHighlightProvider hlProvider, Options displayOptions, Options fieldOptions) { super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions); } @@ -85,7 +86,7 @@ public class MemoryBlockStartFieldFactory extends FieldFactory { } // Convert the text to field elements. - FieldElement[] elements = createFieldElements(attributedStrings); + List elements = createFieldElements(attributedStrings); // And put the elements in a text field. ListingTextField ltf = ListingTextField.createMultilineTextField(this, proxy, elements, @@ -227,7 +228,7 @@ public class MemoryBlockStartFieldFactory extends FieldFactory { return lines; } - private FieldElement[] createFieldElements(List attributedStrings) { + private List createFieldElements(List attributedStrings) { List elements = new ArrayList<>(); int lineNum = 0; for (AttributedString line : attributedStrings) { @@ -235,11 +236,6 @@ public class MemoryBlockStartFieldFactory extends FieldFactory { elements.add(blockElement); lineNum++; } - - // Convert to an array - FieldElement[] elementsArray = new FieldElement[elements.size()]; - elements.toArray(elementsArray); - - return elementsArray; + return elements; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldFactory.java index 10ad68c0c0..55739f8fc2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldFactory.java @@ -89,7 +89,8 @@ public class OperandFieldFactory extends OperandFieldHelper { } @Override - public FieldFactory newInstance(FieldFormatModel formatModel, ListingHighlightProvider hsProvider, + public FieldFactory newInstance(FieldFormatModel formatModel, + ListingHighlightProvider hsProvider, ToolOptions displayOptions, ToolOptions fieldOptions) { return new OperandFieldFactory(formatModel, hsProvider, displayOptions, fieldOptions); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java index c3d456b94f..c1bfcc69bf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java @@ -48,14 +48,17 @@ import ghidra.util.HelpLocation; */ abstract class OperandFieldHelper extends FieldFactory { - private final static String ENABLE_WORD_WRAP_MSG = + private final static String ENABLE_WORD_WRAP_OPTION = GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + FieldUtils.WORD_WRAP_OPTION_NAME; - private final static String MAX_DISPLAY_LINES_MSG = + private final static String MAX_DISPLAY_LINES_OPTION = GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Maximum Lines To Display"; private final static String UNDERLINE_OPTION = GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Underline References"; private final static String SPACE_AFTER_SEPARATOR_OPTION = GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Add Space After Separator"; + private final static String WRAP_ON_SEMICOLON_OPTION = + GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Wrap on Semicolons"; + private final static OperandFieldElement LINE_BREAK = new OperandFieldElement(null, 0, 0, 0); public enum UNDERLINE_CHOICE { Hidden, All, None @@ -75,6 +78,7 @@ abstract class OperandFieldHelper extends FieldFactory { private boolean isWordWrap = false; private int maxDisplayLines = 2; private boolean spaceAfterSeparator = false; + private boolean wrapOnSemicolon = false; protected BrowserCodeUnitFormat codeUnitFormat; private ChangeListener codeUnitFormatListener = e -> OperandFieldHelper.this.model.update(); @@ -102,9 +106,9 @@ abstract class OperandFieldHelper extends FieldFactory { setOptions(displayOptions); HelpLocation hl = new HelpLocation("CodeBrowserPlugin", "Operands_Field"); - fieldOptions.registerOption(ENABLE_WORD_WRAP_MSG, false, hl, + fieldOptions.registerOption(ENABLE_WORD_WRAP_OPTION, false, hl, "Enables word wrapping of strings in the operands field."); - fieldOptions.registerOption(MAX_DISPLAY_LINES_MSG, 2, hl, + fieldOptions.registerOption(MAX_DISPLAY_LINES_OPTION, 2, hl, "The maximum number of lines used to display the strings in the operands field."); fieldOptions.registerOption(UNDERLINE_OPTION, UNDERLINE_CHOICE.Hidden, hl, "Select 'All' to underline any operand field that has a reference; " + @@ -112,16 +116,16 @@ abstract class OperandFieldHelper extends FieldFactory { "select 'None' for no underlines."); fieldOptions.registerOption(SPACE_AFTER_SEPARATOR_OPTION, false, hl, "Add space between separator and next operand"); + fieldOptions.registerOption(WRAP_ON_SEMICOLON_OPTION, false, hl, + "Wrap operand field on semicolons"); - setMaximumLinesToDisplay(fieldOptions.getInt(MAX_DISPLAY_LINES_MSG, 2), fieldOptions); - isWordWrap = fieldOptions.getBoolean(ENABLE_WORD_WRAP_MSG, false); - + setMaximumLinesToDisplay(fieldOptions.getInt(MAX_DISPLAY_LINES_OPTION, 2), fieldOptions); + isWordWrap = fieldOptions.getBoolean(ENABLE_WORD_WRAP_OPTION, false); underlineChoice = fieldOptions.getEnum(UNDERLINE_OPTION, UNDERLINE_CHOICE.Hidden); - spaceAfterSeparator = fieldOptions.getBoolean(SPACE_AFTER_SEPARATOR_OPTION, false); + wrapOnSemicolon = fieldOptions.getBoolean(WRAP_ON_SEMICOLON_OPTION, false); inspector = new SymbolInspector(displayOptions, null); - fieldOptions.getOptions(GhidraOptions.OPERAND_GROUP_TITLE).setOptionsHelpLocation(hl); // Create code unit format and associated options - listen for changes codeUnitFormat = new BrowserCodeUnitFormat(fieldOptions, true); @@ -141,22 +145,29 @@ abstract class OperandFieldHelper extends FieldFactory { Object newValue) { boolean updateModel = false; - if (optionName.equals(MAX_DISPLAY_LINES_MSG)) { - setMaximumLinesToDisplay(((Integer) newValue).intValue(), options); - updateModel = true; - } - else if (optionName.equals(ENABLE_WORD_WRAP_MSG)) { - isWordWrap = ((Boolean) newValue).booleanValue(); - updateModel = true; - } - else if (optionName.equals(UNDERLINE_OPTION)) { - underlineChoice = (UNDERLINE_CHOICE) newValue; - updateModel = true; - } - else if (optionName.equals(SPACE_AFTER_SEPARATOR_OPTION)) { - spaceAfterSeparator = ((Boolean) newValue).booleanValue(); - updateModel = true; + switch (optionName) { + case MAX_DISPLAY_LINES_OPTION: + setMaximumLinesToDisplay(((Integer) newValue).intValue(), options); + updateModel = true; + break; + case ENABLE_WORD_WRAP_OPTION: + isWordWrap = ((Boolean) newValue).booleanValue(); + updateModel = true; + break; + case UNDERLINE_OPTION: + underlineChoice = (UNDERLINE_CHOICE) newValue; + updateModel = true; + break; + case SPACE_AFTER_SEPARATOR_OPTION: + spaceAfterSeparator = ((Boolean) newValue).booleanValue(); + updateModel = true; + break; + case WRAP_ON_SEMICOLON_OPTION: + wrapOnSemicolon = ((Boolean) newValue).booleanValue(); + updateModel = true; + break; } + if (updateModel) { model.update(); } @@ -165,19 +176,18 @@ abstract class OperandFieldHelper extends FieldFactory { private void setMaximumLinesToDisplay(int maxLines, Options options) { if (maxLines < 1) { maxLines = 1; - options.setInt(MAX_DISPLAY_LINES_MSG, maxLines); + options.setInt(MAX_DISPLAY_LINES_OPTION, maxLines); } this.maxDisplayLines = maxLines; } - FieldLocation getFieldLocation(BigInteger index, int fieldNum, ListingField bf, int opIndex, - int column) { - if (bf instanceof ListingTextField) { - ListingTextField btf = (ListingTextField) bf; - RowColLocation rcl = btf.dataToScreenLocation(opIndex, column); + FieldLocation getFieldLocation(BigInteger index, int fieldNum, ListingField field, + int opIndex, int column) { + if (field instanceof ListingTextField listingField) { + RowColLocation rcl = listingField.dataToScreenLocation(opIndex, column); return new FieldLocation(index, fieldNum, rcl.row(), rcl.col()); } - else if (bf instanceof ImageFactoryField) { + else if (field instanceof ImageFactoryField) { return new FieldLocation(index, fieldNum, 0, 0); } return null; @@ -435,14 +445,50 @@ abstract class OperandFieldHelper extends FieldFactory { characterOffset = 0; } - // There may be operands with no representation objects, so we don't want to create a composite field element. + // There may be operands with no representation objects, so we don't want to create a + // composite field element. if (elements.isEmpty()) { return null; } + if (wrapOnSemicolon) { + List lines = breakIntoLines(elements); + if (lines.size() == 1) { + return ListingTextField.createSingleLineTextField(this, proxy, + lines.get(0), startX + varWidth, width, hlProvider); + } + return ListingTextField.createMultilineTextField(this, proxy, lines, startX, width, + hlProvider); + } return ListingTextField.createSingleLineTextField(this, proxy, new CompositeFieldElement(elements), startX + varWidth, width, hlProvider); } + private List breakIntoLines(List elements) { + // This method groups all elements between LINE_BREAK elements into composite elements + // where each composite element will be display on its own line. + // + // It does this by collecting elements in the lineElements list until it find a LINE_BREAK + List fieldElements = new ArrayList<>(); + List lineElements = new ArrayList<>(); + + for (OperandFieldElement operandFieldElement : elements) { + if (operandFieldElement == LINE_BREAK) { + if (!lineElements.isEmpty()) { + fieldElements.add(new CompositeFieldElement(lineElements)); + lineElements.clear(); + } + } + else { + lineElements.add(operandFieldElement); + } + } + if (!lineElements.isEmpty()) { + fieldElements.add(new CompositeFieldElement(lineElements)); + lineElements.clear(); + } + return fieldElements; + } + private int addElementsForOperand(Instruction inst, List elements, int opIndex, OperandRepresentationList opRepList, int characterOffset) { int subOpIndex = 0; @@ -493,7 +539,11 @@ abstract class OperandFieldHelper extends FieldFactory { AttributedString as = new AttributedString(opElem.toString(), attributes.colorAttribute, getMetrics(attributes.styleAttribute), underline, ListingColors.UNDERLINE); + elements.add(new OperandFieldElement(as, opIndex, subOpIndex, characterOffset)); + if (wrapOnSemicolon && opElem instanceof Character c && c == ';') { + elements.add(LINE_BREAK); + } return characterOffset + as.length(); } @@ -742,5 +792,4 @@ abstract class OperandFieldHelper extends FieldFactory { operandSubIndex, column); } } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java index aeb541f8b0..f90ffda7bc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java @@ -449,26 +449,26 @@ public class PostCommentFieldFactory extends FieldFactory { (comments.length == 0 || alwaysShowAutomatic) ? autoComment.length : 0; AttributedString prototypeString = new AttributedString("prototype", color, getMetrics()); - FieldElement[] fields = - new FieldElement[comments.length + nLinesAfterBlocks + nLinesAutoComment]; - if (fields.length > 0) { + int commentLineCount = comments.length + nLinesAfterBlocks + nLinesAutoComment; + List elements = new ArrayList<>(commentLineCount); + if (commentLineCount > 0) { for (int i = 0; i < nLinesAutoComment; i++) { AttributedString as = new AttributedString(autoComment[i], CommentColors.AUTO, getMetrics(automaticCommentStyle), false, null); - fields[i] = new TextFieldElement(as, i, 0); + elements.add(new TextFieldElement(as, i, 0)); } for (int i = 0; i < comments.length; i++) { int index = nLinesAutoComment + i; - fields[index] = CommentUtils.parseTextForAnnotations(comments[i], - instr.getProgram(), prototypeString, index); + elements.add(CommentUtils.parseTextForAnnotations(comments[i], + instr.getProgram(), prototypeString, index)); } - for (int i = fields.length - nLinesAfterBlocks; i < fields.length; i++) { + for (int i = commentLineCount - nLinesAfterBlocks; i < commentLineCount; i++) { // add blank lines for end-of-block AttributedString as = new AttributedString("", color, getMetrics()); - fields[i] = new TextFieldElement(as, i, 0); + elements.add(new TextFieldElement(as, i, 0)); } - return ListingTextField.createMultilineTextField(this, proxy, fields, xStart, - width, Integer.MAX_VALUE, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, elements, xStart, + width, hlProvider); } } } @@ -516,10 +516,9 @@ public class PostCommentFieldFactory extends FieldFactory { fields.add(new TextFieldElement(as, fields.size(), 0)); } } - FieldElement[] elements = fields.toArray(new FieldElement[fields.size()]); - return ListingTextField.createMultilineTextField(this, proxy, elements, xStart, width, - Integer.MAX_VALUE, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, fields, xStart, width, + hlProvider); } private void init(Options options) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java index d651af5b2c..b6154b5318 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java @@ -386,10 +386,8 @@ public class PreCommentFieldFactory extends FieldFactory { fields = FieldUtils.wrap(fields, width); } - FieldElement[] elements = fields.toArray(new FieldElement[fields.size()]); - - return ListingTextField.createMultilineTextField(this, proxy, elements, xStart, width, - Integer.MAX_VALUE, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, fields, xStart, width, + hlProvider); } private void init(Options options) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/RegisterFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/RegisterFieldFactory.java index 117db23d66..f53d72e02a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/RegisterFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/RegisterFieldFactory.java @@ -199,14 +199,14 @@ public class RegisterFieldFactory extends FieldFactory { return setRegisters; } - private FieldElement[] getFieldElements(String[] registerStrings) { - FieldElement[] fieldElements = new FieldElement[registerStrings.length]; + private List getFieldElements(String[] registerStrings) { + List elements = new ArrayList<>(registerStrings.length); for (int i = 0; i < registerStrings.length; i++) { AttributedString str = new AttributedString(registerStrings[i], ListingColors.REGISTER, getMetrics()); - fieldElements[i] = new TextFieldElement(str, i, 0); + elements.add(new TextFieldElement(str, i, 0)); } - return fieldElements; + return elements; } private ListingTextField getTextField(String[] registerStrings, ProxyObj proxy, int xStart) { @@ -214,9 +214,9 @@ public class RegisterFieldFactory extends FieldFactory { return null; } - FieldElement[] fieldElements = getFieldElements(registerStrings); - return ListingTextField.createMultilineTextField(this, proxy, fieldElements, xStart, width, - Integer.MAX_VALUE, hlProvider); + List elements = getFieldElements(registerStrings); + return ListingTextField.createMultilineTextField(this, proxy, elements, xStart, width, + hlProvider); } private class RegComparator implements Comparator { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/RegisterTransitionFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/RegisterTransitionFieldFactory.java index b2714290b4..f780de1e4d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/RegisterTransitionFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/RegisterTransitionFieldFactory.java @@ -55,7 +55,8 @@ public class RegisterTransitionFieldFactory extends FieldFactory { * @param displayOptions the Options for display properties. * @param fieldOptions the Options for field specific properties. */ - private RegisterTransitionFieldFactory(FieldFormatModel model, ListingHighlightProvider hsProvider, + private RegisterTransitionFieldFactory(FieldFormatModel model, + ListingHighlightProvider hsProvider, Options displayOptions, Options fieldOptions) { super(FIELD_NAME, model, hsProvider, displayOptions, fieldOptions); initOptions(displayOptions, fieldOptions); @@ -118,22 +119,22 @@ public class RegisterTransitionFieldFactory extends FieldFactory { if (stackDepthStr != null) { numElements++; } - FieldElement[] fieldElements = new FieldElement[numElements]; + List elements = new ArrayList<>(numElements); for (int i = 0; i < numRegisters; i++) { Register register = transitionRegisters.get(i); AttributedString str = new AttributedString( "assume " + register.getName() + " = " + getValueString(register, context, curAddress), ListingColors.REGISTER, getMetrics()); - fieldElements[i] = new TextFieldElement(str, i, 0); + elements.add(new TextFieldElement(str, i, 0)); } if (stackDepthStr != null) { AttributedString str = new AttributedString(stackDepthStr, ListingColors.REGISTER, getMetrics()); - fieldElements[numRegisters] = new TextFieldElement(str, numRegisters, 0); + elements.add(new TextFieldElement(str, numRegisters, 0)); } - return ListingTextField.createMultilineTextField(this, proxy, fieldElements, - startX + varWidth, width, Integer.MAX_VALUE, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, elements, + startX + varWidth, width, hlProvider); } private String getValueString(Register register, ProgramContext context, Address curAddress) { @@ -242,7 +243,8 @@ public class RegisterTransitionFieldFactory extends FieldFactory { } @Override - public FieldFactory newInstance(FieldFormatModel fieldFormatModel, ListingHighlightProvider hsProvider, + public FieldFactory newInstance(FieldFormatModel fieldFormatModel, + ListingHighlightProvider hsProvider, ToolOptions displayOptions, ToolOptions fieldOptions) { return new RegisterTransitionFieldFactory(fieldFormatModel, hsProvider, displayOptions, fieldOptions); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/SpaceFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/SpaceFieldFactory.java index 039103994b..2e6cc4fd9e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/SpaceFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/SpaceFieldFactory.java @@ -16,6 +16,8 @@ package ghidra.app.util.viewer.field; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.support.FieldLocation; @@ -78,14 +80,15 @@ public class SpaceFieldFactory extends FieldFactory { else if (n < 0) { n = -n; } - FieldElement[] fes = new FieldElement[n]; + + List elements = new ArrayList<>(n); AttributedString as = new AttributedString("", Colors.FOREGROUND, getMetrics()); for (int i = 0; i < n; i++) { - fes[i] = new TextFieldElement(as, 0, 0); + elements.add(new TextFieldElement(as, 0, 0)); } - return ListingTextField.createMultilineTextField(this, proxy, fes, startX + varWidth, - width, n + 1, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, elements, + startX + varWidth, width, hlProvider); } return null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableCommentFieldFactory.java index d523cd4ad7..ff267a5fb9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableCommentFieldFactory.java @@ -16,6 +16,8 @@ package ghidra.app.util.viewer.field; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.support.FieldLocation; @@ -69,15 +71,15 @@ public class VariableCommentFieldFactory extends AbstractVariableFieldFactory { String comment = sv.getComment(); String[] comments = StringUtilities.toLines(comment); if ((comments != null) && (comments.length > 0)) { - FieldElement[] fields = new FieldElement[comments.length]; + List elements = new ArrayList<>(comments.length); for (int i = 0; i < comments.length; i++) { AttributedString as = new AttributedString(comments[i], getColor(sv), getMetrics(sv)); - fields[i] = new TextFieldElement(as, i, 0); + elements.add(new TextFieldElement(as, i, 0)); } - return ListingTextField.createMultilineTextField(this, proxy, fields, startX + varWidth, - width, Integer.MAX_VALUE, hlProvider); + return ListingTextField.createMultilineTextField(this, proxy, elements, + startX + varWidth, width, hlProvider); } return null; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java index 503137cc9d..02429d5a34 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java @@ -745,7 +745,7 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest loadProgram(); Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS); List names = getOptionNames(options, "Operands Field"); - assertEquals(15, names.size()); + assertEquals(16, names.size()); assertEquals("Operands Field.Add Space After Separator", names.get(0)); assertEquals("Operands Field.Always Show Primary Reference", names.get(1)); assertEquals("Operands Field.Display Abbreviated Default Label Names", names.get(2)); @@ -761,6 +761,7 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest assertEquals("Operands Field.Show Block Names", names.get(12)); assertEquals("Operands Field.Show Offcut Information", names.get(13)); assertEquals("Operands Field.Underline References", names.get(14)); + assertEquals("Operands Field.Wrap on Semicolons", names.get(15)); NamespaceWrappedOption namespaceOption = (NamespaceWrappedOption) options.getCustomOption(names.get(3), diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/field/VerticalLayoutTextField.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/field/VerticalLayoutTextField.java index 1001e70a1e..1bc36c28e2 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/field/VerticalLayoutTextField.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/field/VerticalLayoutTextField.java @@ -492,11 +492,19 @@ public class VerticalLayoutTextField implements TextField { @Override public RowColLocation dataToScreenLocation(int dataRow, int dataColumn) { - FieldRow fieldRow = getFieldRowFromDataRow(dataRow); - TextField field = fieldRow.field; - RowColLocation location = field.dataToScreenLocation(dataRow, dataColumn); - int screenRow = fieldRow.screenRow; - return location.withRow(screenRow); + // search each line looking for a match for the given row and column + for (int i = 0; i < subFields.size(); i++) { + FieldRow row = subFields.get(i); + RowColLocation loc = row.field.dataToScreenLocation(dataRow, dataColumn); + + // A DefaultRowColLocation means that the line did not have an exact match for + // the dataRow and dataColumn, so need to keep looking at each line. + if (!(loc instanceof DefaultRowColLocation)) { + return new RowColLocation(i, loc.col()); + } + } + // We did not find a match for the given row and column, so return a default location of 0,0 + return new DefaultRowColLocation(); } @Override diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/fieldpanel/VerticalLayoutTextFieldTest.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/fieldpanel/VerticalLayoutTextFieldTest.java index 9325668b2c..434a363133 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/widgets/fieldpanel/VerticalLayoutTextFieldTest.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/fieldpanel/VerticalLayoutTextFieldTest.java @@ -92,7 +92,7 @@ public class VerticalLayoutTextFieldTest extends AbstractGenericTest { assertEquals(new RowColLocation(2, 0), field.dataToScreenLocation(2, 0)); assertEquals(new RowColLocation(2, 4), field.dataToScreenLocation(2, 4)); assertEquals(new RowColLocation(2, 12), field.dataToScreenLocation(2, 12)); - assertEquals(new DefaultRowColLocation(2, 12), field.dataToScreenLocation(2, 15)); + assertEquals(new DefaultRowColLocation(0, 0), field.dataToScreenLocation(2, 15)); assertEquals(new RowColLocation(3, 0), field.dataToScreenLocation(3, 0)); assertEquals(new RowColLocation(3, 4), field.dataToScreenLocation(3, 4));