Merge remote-tracking branch 'origin/GP-0-dragonmacher-xref-merge-test-fixes--SQUASHED'

This commit is contained in:
Ryan Kurtz 2021-09-21 12:59:01 -04:00
commit e3f5e9a061
16 changed files with 440 additions and 294 deletions

View file

@ -60,7 +60,8 @@ public class CommentFieldSearcher extends ProgramDatabaseFieldSearcher {
return nextAddress; return nextAddress;
} }
private void findMatchesForCurrentAddress(Address address, List<ProgramLocation> currentMatches) { private void findMatchesForCurrentAddress(Address address,
List<ProgramLocation> currentMatches) {
String comment = program.getListing().getComment(commentType, address); String comment = program.getListing().getComment(commentType, address);
if (comment == null) { if (comment == null) {
return; return;
@ -84,7 +85,7 @@ public class CommentFieldSearcher extends ProgramDatabaseFieldSearcher {
charOffset, rowIndex); charOffset, rowIndex);
case CodeUnit.PLATE_COMMENT: case CodeUnit.PLATE_COMMENT:
return new PlateFieldLocation(program, address, dataPath, rowIndex, charOffset, return new PlateFieldLocation(program, address, dataPath, rowIndex, charOffset,
comments, rowIndex - 1); comments, rowIndex);
case CodeUnit.REPEATABLE_COMMENT: case CodeUnit.REPEATABLE_COMMENT:
return new RepeatableCommentFieldLocation(program, address, dataPath, comments, return new RepeatableCommentFieldLocation(program, address, dataPath, comments,
rowIndex, charOffset, rowIndex); // TODO One of searchStrIndex parameters is wrong. rowIndex, charOffset, rowIndex); // TODO One of searchStrIndex parameters is wrong.

View file

@ -427,11 +427,9 @@ public class BytesFieldFactory extends FieldFactory {
ListingTextField btf = (ListingTextField) bf; ListingTextField btf = (ListingTextField) bf;
RowColLocation rcl = btf.dataToScreenLocation(tokenIndex, tokenOffset); RowColLocation rcl = btf.dataToScreenLocation(tokenIndex, tokenOffset);
if (hasSamePath(bf, loc)) { if (hasSamePath(bf, loc)) {
return new FieldLocation(index, fieldNum, rcl.row(), rcl.col()); return new FieldLocation(index, fieldNum, rcl.row(), rcl.col());
} }
return null; return null;
} }

View file

@ -20,6 +20,8 @@ import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.field.*;
import docking.widgets.fieldpanel.support.*; import docking.widgets.fieldpanel.support.*;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
@ -32,6 +34,7 @@ import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.util.*; import ghidra.program.util.*;
@ -141,21 +144,18 @@ public class PlateFieldFactory extends FieldFactory {
} }
CodeUnit cu = (CodeUnit) proxy.getObject(); CodeUnit cu = (CodeUnit) proxy.getObject();
List<FieldElement> elements = new ArrayList<>(10);
boolean isClipped = false; boolean isClipped = false;
List<FieldElement> elements = new ArrayList<>();
String commentText = getCommentText(cu); String commentText = getCommentText(cu);
if ((commentText == null) || (commentText.isEmpty())) { if (StringUtils.isBlank(commentText)) {
generateDefaultPlate(elements, cu); getDefaultFieldElements(cu, elements);
} }
else { else {
isClipped = generateFormattedPlateComment(elements, cu); isClipped = getFormattedFieldElements(cu, elements);
} }
addBlankLines(elements, cu); if (elements.isEmpty()) {
return null; // no real or default comments
if (elements.size() == 0) {
// no real or default comment
return null;
} }
if (isNestedDataAtSameAddressAsParent(proxy)) { if (isNestedDataAtSameAddressAsParent(proxy)) {
@ -170,6 +170,27 @@ public class PlateFieldFactory extends FieldFactory {
return new PlateListingTextField(proxy, textField); return new PlateListingTextField(proxy, textField);
} }
private boolean getFormattedFieldElements(CodeUnit cu, List<FieldElement> elements) {
int numberBlankLines = getNumberBlankLines(cu, true);
addBlankLines(elements, numberBlankLines, cu);
String[] comments = cu.getCommentAsArray(CodeUnit.PLATE_COMMENT);
return generateFormattedPlateComment(elements, comments, cu.getProgram());
}
private void getDefaultFieldElements(CodeUnit cu, List<FieldElement> elements) {
int numberBlankLines = getNumberBlankLines(cu, true);
addBlankLines(elements, numberBlankLines, cu);
String defaultComment = getDefaultComment(cu);
if (defaultComment != null) {
generateDefaultPlate(elements, defaultComment);
}
}
private boolean isNestedDataAtSameAddressAsParent(ProxyObj<?> proxy) { private boolean isNestedDataAtSameAddressAsParent(ProxyObj<?> proxy) {
if (proxy instanceof DataProxy) { if (proxy instanceof DataProxy) {
DataProxy dp = (DataProxy) proxy; DataProxy dp = (DataProxy) proxy;
@ -200,67 +221,39 @@ public class PlateFieldFactory extends FieldFactory {
return buffy.toString(); return buffy.toString();
} }
/** /*
* Creates desired FieldElements and puts them in the given list. Returns true if any of the * Creates desired FieldElements and puts them in the given list. Returns true if any of the
* data is clipped because it is too long to display. * data is clipped because it is too long to display.
*
* @param elementList the list into which the created data will be placed.
* @param cu the code unit for which a plate comment will be generated
* @return true if the data is clipped
*/ */
private boolean generateFormattedPlateComment(List<FieldElement> elementList, CodeUnit cu) { private boolean generateFormattedPlateComment(List<FieldElement> elements, String[] comments,
String[] comments = cu.getCommentAsArray(CodeUnit.PLATE_COMMENT); Program p) {
if ((comments == null) || (comments.length == 0)) { if (comments == null || comments.length == 0) {
return false; return false;
} }
Program program = cu.getProgram();
AttributedString prototype = new AttributedString(EMPTY_STRING, color, getMetrics()); AttributedString prototype = new AttributedString(EMPTY_STRING, color, getMetrics());
for (int i = 0; i < comments.length; i++) { AttributedString asteriscs = getStarsString();
elementList int row = elements.size();
.add(CommentUtils.parseTextForAnnotations(comments[i], program, prototype, i));
// add top border
elements.add(new TextFieldElement(asteriscs, row++, 0));
int commentsStart = row;
for (String c : comments) {
FieldElement element = CommentUtils.parseTextForAnnotations(c, p, prototype, row++);
elements.add(element);
} }
if (isWordWrap) { if (isWordWrap) {
elementList = FieldUtils.wordWrapList(new CompositeFieldElement(elementList), width); elements = FieldUtils.wordWrapList(new CompositeFieldElement(elements), width);
} }
return addBorder(elementList); boolean isClipped = addSideBorders(elements, commentsStart);
}
private void addBlankLines(List<FieldElement> elementList, CodeUnit cu) { // add bottom border
AttributedString prototype = new AttributedString(EMPTY_STRING, color, getMetrics()); elements.add(new TextFieldElement(asteriscs, row++, 0));
FieldElement blankLine = new TextFieldElement(prototype, 0, 0);
int numBlankLines = getNumberBlankLines(cu, elementList.size() > 0);
for (int i = 0; i < numBlankLines; i++) {
elementList.add(0, blankLine);
}
}
private int getNumberBlankLines(CodeUnit cu, boolean hasPlate) {
if (cu.getProgram().getListing().getFunctionAt(cu.getMinAddress()) != null) {
if (nLinesBeforeFunctions != 0) {
return nLinesBeforeFunctions;
}
}
if (hasPlate && nLinesBeforePlates != 0) {
return nLinesBeforePlates;
}
if (cu.getLabel() != null) {
return nLinesBeforeLabels;
}
return 0;
}
private boolean addBorder(List<FieldElement> elements) {
AttributedString asteriscs = getStarsString();
boolean isClipped = addSideBorders(elements);
elements.add(0, new TextFieldElement(asteriscs, 0, 0));
elements.add(new TextFieldElement(asteriscs, elements.size(), 0));
return isClipped; return isClipped;
} }
@ -269,13 +262,17 @@ public class PlateFieldFactory extends FieldFactory {
* Text will be left justified between two '*' and padded based upon the * Text will be left justified between two '*' and padded based upon the
* available field width. * available field width.
* @param elements the field elements that may get updated * @param elements the field elements that may get updated
* @param commentsStart the start index of comment elements in the element list
* @return formatted plate text line * @return formatted plate text line
*/ */
private boolean addSideBorders(List<FieldElement> elements) { private boolean addSideBorders(List<FieldElement> elements, int commentsStart) {
boolean isClipped = false; boolean isClipped = false;
if (elements.size() == 1) {
// If there is only a single line comment, then center it
int n = elements.size() - commentsStart;
if (n == 1) {
FieldElement element = elements.get(0); FieldElement element = elements.get(0);
if (element.length() > 1 && element.charAt(0) == ' ') { if (element.length() > 1 && element.charAt(0) == ' ') { // not sure why the space matters
FieldElementResult result = addSideBorder(element.substring(1), 1, true); FieldElementResult result = addSideBorder(element.substring(1), 1, true);
isClipped = result.isClipped(); isClipped = result.isClipped();
elements.set(0, result.getFieldElement()); elements.set(0, result.getFieldElement());
@ -283,8 +280,8 @@ public class PlateFieldFactory extends FieldFactory {
} }
} }
for (int i = 0; i < elements.size(); i++) { for (int i = commentsStart; i < elements.size(); i++) {
FieldElementResult result = addSideBorder(elements.get(i), i + 1, false); FieldElementResult result = addSideBorder(elements.get(i), i, false);
isClipped |= result.isClipped(); isClipped |= result.isClipped();
elements.set(i, result.getFieldElement()); elements.set(i, result.getFieldElement());
} }
@ -311,7 +308,7 @@ public class PlateFieldFactory extends FieldFactory {
int prePadding = center ? totalPadding / 2 : 0; int prePadding = center ? totalPadding / 2 : 0;
int postPadding = center ? (totalPadding + 1) / 2 : totalPadding; int postPadding = center ? (totalPadding + 1) / 2 : totalPadding;
StringBuffer buffy = new StringBuffer(); StringBuilder buffy = new StringBuilder();
buffy.append('*').append(' '); buffy.append('*').append(' ');
addPadding(buffy, prePadding); addPadding(buffy, prePadding);
@ -335,21 +332,56 @@ public class PlateFieldFactory extends FieldFactory {
ellipsisLength > 0); ellipsisLength > 0);
} }
private void addPadding(StringBuffer buf, int count) { private void addPadding(StringBuilder buf, int count) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
buf.append(' '); buf.append(' ');
} }
} }
private void generateDefaultPlate(List<FieldElement> elementList, CodeUnit cu) { private void addBlankLines(List<FieldElement> elements, int numberBlankLines, CodeUnit cu) {
String defaultComment = getDefaultComment(cu); AttributedString prototype = new AttributedString(EMPTY_STRING, color, getMetrics());
if (defaultComment != null) { for (int row = 0; row < numberBlankLines; row++) {
AttributedString as = new AttributedString(defaultComment, color, getMetrics()); elements.add(new TextFieldElement(prototype, row, 0));
elementList.add(new TextFieldElement(as, 0, 0));
addBorder(elementList);
} }
} }
private int getNumberBlankLines(CodeUnit cu, boolean hasPlate) {
if (cu.getProgram().getListing().getFunctionAt(cu.getMinAddress()) != null) {
if (nLinesBeforeFunctions != 0) {
return nLinesBeforeFunctions;
}
}
if (hasPlate && nLinesBeforePlates != 0) {
return nLinesBeforePlates;
}
if (cu.getLabel() != null) {
return nLinesBeforeLabels;
}
return 0;
}
private void generateDefaultPlate(List<FieldElement> elements, String defaultComment) {
if (defaultComment == null) {
return;
}
AttributedString asteriscs = getStarsString();
int row = elements.size();
// top border
elements.add(0, new TextFieldElement(asteriscs, row++, 0));
AttributedString as = new AttributedString(defaultComment, color, getMetrics());
elements.add(new TextFieldElement(as, row++, 0));
addSideBorders(elements, 1);
// bottom border
elements.add(new TextFieldElement(asteriscs, row++, 0));
}
private String getDefaultComment(CodeUnit cu) { private String getDefaultComment(CodeUnit cu) {
if (showFunctionPlates) { if (showFunctionPlates) {
@ -367,11 +399,6 @@ public class PlateFieldFactory extends FieldFactory {
return SUBROUTINE_PLATE_COMMENT; return SUBROUTINE_PLATE_COMMENT;
} }
// TODO handle case statements?
// if (showCasePlates) {
// return CASE_PLATE_COMMENT;
// }
if (showTransitionPlates) { if (showTransitionPlates) {
if (isDeadCode(cu)) { if (isDeadCode(cu)) {
return DEAD_CODE_PLATE_COMMENT; return DEAD_CODE_PLATE_COMMENT;
@ -432,7 +459,7 @@ public class PlateFieldFactory extends FieldFactory {
int n = width / starWidth; int n = width / starWidth;
if (stars.length() != n) { if (stars.length() != n) {
StringBuffer buf = new StringBuffer(); StringBuilder buf = new StringBuilder();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
buf.append('*'); buf.append('*');
} }
@ -465,18 +492,18 @@ public class PlateFieldFactory extends FieldFactory {
((ListingTextField) listingField).screenToDataLocation(row, col); ((ListingTextField) listingField).screenToDataLocation(row, col);
// //
// The 'row' value is an offset from the top of the plate comment, which has 0 or // The 'row' value includes blank lines and header decoration lines. The 'commentRow' used
// more blank lines at the top, followed by a line of asterisks. // below is the index into the list of comments. Calculate the comment beginning by
// removing the non-comment lines.
// //
int fillerLineCount = getNumberOfLeadingFillerLines(listingField); int fillerLineCount = getNumberOfLeadingFillerLines(listingField);
int commentRow = row - fillerLineCount; int commentRow = row - fillerLineCount;
if (commentRow >= comments.length) { if (commentRow >= comments.length || commentRow < 0) {
commentRow = -1; // clicked the bottom decoration line commentRow = -1; // clicked above the comment or the bottom decoration line
} }
return new PlateFieldLocation(cu.getProgram(), ((CodeUnit) proxyObject).getMinAddress(), return new PlateFieldLocation(cu.getProgram(), ((CodeUnit) proxyObject).getMinAddress(),
cpath, dataLocation.row(), dataLocation.col(), comments, commentRow); cpath, commentRow, dataLocation.col(), comments, commentRow);
} }
private int getNumberOfLeadingFillerLines(ListingField listingField) { private int getNumberOfLeadingFillerLines(ListingField listingField) {
@ -491,6 +518,7 @@ public class PlateFieldFactory extends FieldFactory {
@Override @Override
public FieldLocation getFieldLocation(ListingField listingField, BigInteger index, int fieldNum, public FieldLocation getFieldLocation(ListingField listingField, BigInteger index, int fieldNum,
ProgramLocation programLoc) { ProgramLocation programLoc) {
if (!(programLoc instanceof CommentFieldLocation)) { if (!(programLoc instanceof CommentFieldLocation)) {
return null; return null;
} }
@ -507,8 +535,40 @@ public class PlateFieldFactory extends FieldFactory {
return null; return null;
} }
/*
Calculate the data row using the model row provided in the location, along with
compensating for any spacing and plate comment decorations. For example, for this
comment,
This is line one
This is line two
the plate comment may look like this
(blank line)
****************************
* This is line one
* This is line two
*****************************
*/
CodeUnit cu = (CodeUnit) obj;
String commentText = getCommentText(cu);
boolean hasComment = true;
if (StringUtils.isBlank(commentText)) {
String defaultComment = getDefaultComment(cu);
if (defaultComment == null) {
hasComment = false;
}
}
int commentRow = commentLocation.getRow();
int numberBlankLines = getNumberBlankLines(cu, hasComment);
int headerCount = hasComment ? 1 : 0;
int dataRow = commentRow + numberBlankLines + headerCount;
ListingTextField listingTextField = (ListingTextField) listingField; ListingTextField listingTextField = (ListingTextField) listingField;
RowColLocation location = listingTextField.dataToScreenLocation(commentLocation.getRow(), RowColLocation location = listingTextField.dataToScreenLocation(dataRow,
commentLocation.getCharOffset()); commentLocation.getCharOffset());
return new FieldLocation(index, fieldNum, location.row(), location.col()); return new FieldLocation(index, fieldNum, location.row(), location.col());
} }
@ -614,7 +674,7 @@ public class PlateFieldFactory extends FieldFactory {
Address prevAddr = cu.getMinAddress().subtractNoWrap(1); Address prevAddr = cu.getMinAddress().subtractNoWrap(1);
return cu.getProgram().getListing().getCodeUnitContaining(prevAddr); return cu.getProgram().getListing().getCodeUnitContaining(prevAddr);
} }
catch (Exception e) { catch (AddressOverflowException e) {
// we are just being lazy and not validating before doing the subtract--SOCK! // we are just being lazy and not validating before doing the subtract--SOCK!
} }
return null; return null;
@ -638,7 +698,7 @@ public class PlateFieldFactory extends FieldFactory {
} }
initialized = true; initialized = true;
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
sb.append("\n"); sb.append("\n");
for (int i = 0; i < 19; i++) { for (int i = 0; i < 19; i++) {
sb.append("|"); sb.append("|");
@ -725,14 +785,13 @@ public class PlateFieldFactory extends FieldFactory {
int getLeadingFillerLineCount() { int getLeadingFillerLineCount() {
int count = 0; int count = 0;
for (Field field : subFields) { for (String line : getLines()) {
String text = field.getText().trim();
count++; count++;
if (text.isEmpty()) { if (line.isEmpty()) {
continue; // skip leading blank lines continue; // skip leading blank lines
} }
if (text.startsWith("*")) { if (line.startsWith("*")) {
break; break;
} }
} }

View file

@ -551,7 +551,7 @@ public class XRefFieldFactory extends FieldFactory {
List<XrefFieldElement> elements = new ArrayList<>(); List<XrefFieldElement> elements = new ArrayList<>();
FunctionManager functionManager = program.getFunctionManager(); FunctionManager functionManager = program.getFunctionManager();
Function currentFunction = functionManager.getFunctionContaining(cu.getMinAddress()); Function currentFunction = functionManager.getFunctionContaining(cu.getMinAddress());
int n = tooMany ? maxXRefs + 1 : totalXrefs; int n = tooMany ? maxXRefs : totalXrefs;
int count = 0; int count = 0;
for (; count < xrefs.size() && count < n; count++) { for (; count < xrefs.size() && count < n; count++) {
Reference ref = xrefs.get(count); Reference ref = xrefs.get(count);

View file

@ -896,7 +896,8 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
assertEquals("XREFs Field.Display Local Block", names.get(1)); assertEquals("XREFs Field.Display Local Block", names.get(1));
assertEquals("XREFs Field.Display Namespace", names.get(2)); assertEquals("XREFs Field.Display Namespace", names.get(2));
assertEquals("XREFs Field.Display Reference Type", names.get(3)); assertEquals("XREFs Field.Display Reference Type", names.get(3));
assertEquals("XREFs Field.Maximum Number of XREFs to Display", names.get(4)); assertEquals("XREFs Field.Group by Function", names.get(4));
assertEquals("XREFs Field.Maximum Number of XREFs to Display", names.get(5));
assertTrue(cb.goToField(addr("0x1003d9f"), "XRef", 0, 0)); assertTrue(cb.goToField(addr("0x1003d9f"), "XRef", 0, 0));
@ -968,7 +969,9 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
btf = (ListingTextField) cb.getCurrentField(); btf = (ListingTextField) cb.getCurrentField();
assertEquals(9, btf.getNumRows()); assertEquals(9, btf.getNumRows());
options.setInt(names.get(4), 3); // note: the 'group by function' option is tested in the XrefFieldFactoryTest
options.setInt(names.get(5), 3);
cb.updateNow(); cb.updateNow();
assertTrue(cb.goToField(addr("0x1003d9f"), "XRef", 0, 0)); assertTrue(cb.goToField(addr("0x1003d9f"), "XRef", 0, 0));
btf = (ListingTextField) cb.getCurrentField(); btf = (ListingTextField) cb.getCurrentField();

View file

@ -212,25 +212,25 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
for (int i = 0; i < 22; i++) { for (int i = 0; i < 22; i++) {
scrollbar.getUnitIncrement(1); scrollbar.getUnitIncrement(1);
} }
waitForPostedSwingRunnables(); waitForSwing();
assertEquals(addr("0x1001027"), codeBrowser.getAddressTopOfScreen()); assertEquals(addr("0x1001027"), codeBrowser.getAddressTopOfScreen());
for (int i = 0; i < 18; i++) { for (int i = 0; i < 18; i++) {
scrollbar.getUnitIncrement(-1); scrollbar.getUnitIncrement(-1);
} }
waitForPostedSwingRunnables(); waitForSwing();
assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen()); assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen());
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
scrollbar.getBlockIncrement(1); scrollbar.getBlockIncrement(1);
} }
waitForPostedSwingRunnables(); waitForSwing();
assertEquals(addr("0x1001136"), codeBrowser.getAddressTopOfScreen()); assertEquals(addr("0x1001136"), codeBrowser.getAddressTopOfScreen());
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
scrollbar.getBlockIncrement(-1); scrollbar.getBlockIncrement(-1);
} }
waitForPostedSwingRunnables(); waitForSwing();
assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen()); assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen());
} }
@ -333,25 +333,25 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
scrollbar.getUnitIncrement(1); scrollbar.getUnitIncrement(1);
} }
waitForPostedSwingRunnables(); waitForSwing();
assertEquals(addr("0x1001025"), codeBrowser.getAddressTopOfScreen()); assertEquals(addr("0x1001025"), codeBrowser.getAddressTopOfScreen());
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
scrollbar.getUnitIncrement(-1); scrollbar.getUnitIncrement(-1);
} }
waitForPostedSwingRunnables(); waitForSwing();
assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen()); assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen());
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
scrollbar.getBlockIncrement(1); scrollbar.getBlockIncrement(1);
} }
waitForPostedSwingRunnables(); waitForSwing();
assertEquals(addr("0x1001136"), codeBrowser.getAddressTopOfScreen()); assertEquals(addr("0x1001136"), codeBrowser.getAddressTopOfScreen());
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
scrollbar.getBlockIncrement(-1); scrollbar.getBlockIncrement(-1);
} }
waitForPostedSwingRunnables(); waitForSwing();
assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen()); assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen());
} }
@ -412,7 +412,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0); codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x1007001"), "Address", 0, 0); codeBrowser.goToField(addr("0x1007001"), "Address", 0, 0);
assertEquals("01007001", codeBrowser.getCurrentFieldText()); assertEquals("01007001", codeBrowser.getCurrentFieldText());
@ -423,7 +423,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0); codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x1007000"), "Address", 0, 0); codeBrowser.goToField(addr("0x1007000"), "Address", 0, 0);
for (int i = 0; i < 7; i++) { for (int i = 0; i < 7; i++) {
@ -450,7 +450,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0); codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x1007008"), "Address", 0, 0); codeBrowser.goToField(addr("0x1007008"), "Address", 0, 0);
assertEquals("01007008", codeBrowser.getCurrentFieldText()); assertEquals("01007008", codeBrowser.getCurrentFieldText());
codeBrowser.goToField(addr("0x1007010"), "Address", 0, 0); codeBrowser.goToField(addr("0x1007010"), "Address", 0, 0);
@ -458,7 +458,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0); codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x1007000"), "Address", 0, 0); codeBrowser.goToField(addr("0x1007000"), "Address", 0, 0);
codeBrowser.goToField(addr("0x1007008"), "Address", 0, 0); codeBrowser.goToField(addr("0x1007008"), "Address", 0, 0);
assertEquals("01007000", codeBrowser.getCurrentFieldText()); assertEquals("01007000", codeBrowser.getCurrentFieldText());
@ -477,14 +477,14 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0); codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x1007023"), "+", 0, 0); codeBrowser.goToField(addr("0x1007023"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x100702a"), "+", 0, 0); codeBrowser.goToField(addr("0x100702a"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x100702b"), "Address", 0, 0); codeBrowser.goToField(addr("0x100702b"), "Address", 0, 0);
assertEquals("0100702b", codeBrowser.getCurrentFieldText()); assertEquals("0100702b", codeBrowser.getCurrentFieldText());
@ -525,6 +525,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
goTo(codeBrowser, addr("0x1007000")); goTo(codeBrowser, addr("0x1007000"));
for (int i = 0; i < 300; i++) { for (int i = 0; i < 300; i++) {
cursorRight(fp); cursorRight(fp);
if (!codeBrowser.getCurrentFieldText().equals(cb2.getCurrentFieldText())) { if (!codeBrowser.getCurrentFieldText().equals(cb2.getCurrentFieldText())) {
@ -533,11 +534,11 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
assertEquals(codeBrowser.getCurrentFieldText(), cb2.getCurrentFieldText()); assertEquals(codeBrowser.getCurrentFieldText(), cb2.getCurrentFieldText());
if (!codeBrowser.getCurrentFieldLoction().equals(cb2.getCurrentFieldLoction())) { if (!codeBrowser.getCurrentFieldLoction().equals(cb2.getCurrentFieldLoction())) {
System.err.println("location no equal at cursor move: " + i); System.err.println("location not equal at cursor move: " + i);
} }
assertEquals(codeBrowser.getCurrentFieldLoction(), cb2.getCurrentFieldLoction()); assertEquals(codeBrowser.getCurrentFieldLoction(), cb2.getCurrentFieldLoction());
} }
} }
@Test @Test
@ -561,13 +562,13 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0); codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x1007023"), "+", 0, 0); codeBrowser.goToField(addr("0x1007023"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
codeBrowser.goToField(addr("0x100702a"), "+", 0, 0); codeBrowser.goToField(addr("0x100702a"), "+", 0, 0);
click(codeBrowser, 1); click(codeBrowser, 1);
waitForPostedSwingRunnables(); waitForSwing();
// this should open the other structure // this should open the other structure
codeBrowser.goToField(addr("0x1007023"), "Address", 0, 0); codeBrowser.goToField(addr("0x1007023"), "Address", 0, 0);
@ -809,7 +810,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
fieldOptions.setInt(name, 0); fieldOptions.setInt(name, 0);
} }
} }
waitForPostedSwingRunnables(); waitForSwing();
plugin.updateNow(); plugin.updateNow();
} }

View file

@ -187,7 +187,7 @@ public class SearchTextPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
assertNotNull(commentsCB); assertNotNull(commentsCB);
assertSelected(commentsCB); assertSelected(commentsCB);
// verify that the case sensitive checkbox is not selected // verify that the case sensitive checkbox is not selected
JCheckBox csCB = (JCheckBox) findButton(dialog.getComponent(), "Case Sensitive"); JCheckBox csCB = (JCheckBox) findButton(dialog.getComponent(), "Case Sensitive");
assertNotNull(csCB); assertNotNull(csCB);
assertNotSelected(csCB); assertNotSelected(csCB);
@ -216,7 +216,7 @@ public class SearchTextPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testSearchOptions() throws Exception { public void testSearchOptions() throws Exception {
// verify that the search dialog allows for searching Functions, // verify that the search dialog allows for searching Functions,
// Comments, labels, instruction mnemonics and operands, defined data // Comments, labels, instruction mnemonics and operands, defined data
// mnemonics and values. // mnemonics and values.
SearchTextDialog dialog = getDialog(); SearchTextDialog dialog = getDialog();
JCheckBox cb = (JCheckBox) findButton(dialog.getComponent(), "Functions"); JCheckBox cb = (JCheckBox) findButton(dialog.getComponent(), "Functions");
@ -419,6 +419,8 @@ public class SearchTextPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
loc = cbPlugin.getCurrentLocation(); loc = cbPlugin.getCurrentLocation();
assertEquals(getAddr(0x01004192), loc.getAddress()); assertEquals(getAddr(0x01004192), loc.getAddress());
assertTrue(loc instanceof CommentFieldLocation); assertTrue(loc instanceof CommentFieldLocation);
assertEquals("Search result not placed at the matching character position", 15,
((CommentFieldLocation) loc).getCharOffset());
assertEquals(CodeUnit.PLATE_COMMENT, ((CommentFieldLocation) loc).getCommentType()); assertEquals(CodeUnit.PLATE_COMMENT, ((CommentFieldLocation) loc).getCommentType());
pressButton(searchButton); pressButton(searchButton);
@ -667,7 +669,7 @@ public class SearchTextPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
// //
// test marker stuff // test marker stuff
// //
AddressSet set = getAddressesFromModel(model); AddressSet set = getAddressesFromModel(model);
MarkerService markerService = tool.getService(MarkerService.class); MarkerService markerService = tool.getService(MarkerService.class);
MarkerSet markerSet = markerService.getMarkerSet("Search", program); MarkerSet markerSet = markerService.getMarkerSet("Search", program);

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.app.util.viewer.field; package ghidra.app.util.viewer.field;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -57,10 +56,6 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
private Program program; private Program program;
private GoToService goToService; private GoToService goToService;
public PlateFieldFactoryTest() {
super();
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@ -151,8 +146,9 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
cu.setCommentAsArray(CodeUnit.PLATE_COMMENT, cu.setCommentAsArray(CodeUnit.PLATE_COMMENT,
new String[] { "this is", "a plate comment" }); new String[] { "this is", "a plate comment" });
// create a reference to addr // create a reference to addr
program.getReferenceManager().addMemoryReference(getAddr(0x010023ee), addr, program.getReferenceManager()
RefType.DATA, SourceType.USER_DEFINED, 0); .addMemoryReference(getAddr(0x010023ee), addr,
RefType.DATA, SourceType.USER_DEFINED, 0);
} }
finally { finally {
program.endTransaction(transactionID, true); program.endTransaction(transactionID, true);
@ -189,8 +185,9 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
CodeUnit cu = program.getListing().getCodeUnitAt(addr); CodeUnit cu = program.getListing().getCodeUnitAt(addr);
cu.setCommentAsArray(CodeUnit.PLATE_COMMENT, new String[] { originalText }); cu.setCommentAsArray(CodeUnit.PLATE_COMMENT, new String[] { originalText });
// create a reference to addr // create a reference to addr
program.getReferenceManager().addMemoryReference(getAddr(0x010023ee), addr, program.getReferenceManager()
RefType.DATA, SourceType.USER_DEFINED, 0); .addMemoryReference(getAddr(0x010023ee), addr,
RefType.DATA, SourceType.USER_DEFINED, 0);
} }
finally { finally {
program.endTransaction(transactionID, true); program.endTransaction(transactionID, true);
@ -247,7 +244,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
public void testShowTransitionPlates() throws Exception { public void testShowTransitionPlates() throws Exception {
// no plate comment // no plate comment
assertTrue(!cb.goToField(getAddr(0x1001100), PlateFieldFactory.FIELD_NAME, 1, 1)); assertFalse(cb.goToField(getAddr(0x1001100), PlateFieldFactory.FIELD_NAME, 1, 1));
setBooleanOption(PlateFieldFactory.SHOW_TRANSITION_PLATES_OPTION, true); setBooleanOption(PlateFieldFactory.SHOW_TRANSITION_PLATES_OPTION, true);
@ -282,7 +279,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
public void testShowSubroutinePlates() throws Exception { public void testShowSubroutinePlates() throws Exception {
// no subroutine plate comment // no subroutine plate comment
assertTrue(!cb.goToField(getAddr(0x1001200), PlateFieldFactory.FIELD_NAME, 1, 1)); assertFalse(cb.goToField(getAddr(0x1001200), PlateFieldFactory.FIELD_NAME, 1, 1));
setBooleanOption(PlateFieldFactory.SHOW_SUBROUTINE_PLATES_OPTION, true); setBooleanOption(PlateFieldFactory.SHOW_SUBROUTINE_PLATES_OPTION, true);
@ -297,28 +294,13 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testLinesBeforeFunction() throws Exception { public void testLinesBeforeFunction() throws Exception {
assertTrue(!cb.goToField(getAddr(0x1001300), PlateFieldFactory.FIELD_NAME, 1, 1)); assertFalse(cb.goToField(getAddr(0x1001300), PlateFieldFactory.FIELD_NAME, 1, 1));
setIntOption(PlateFieldFactory.LINES_BEFORE_FUNCTIONS_OPTION, 2); setIntOption(PlateFieldFactory.LINES_BEFORE_FUNCTIONS_OPTION, 2);
assertTrue(cb.goToField(getAddr(0x1001300), PlateFieldFactory.FIELD_NAME, 1, 1)); assertTrue(cb.goToField(getAddr(0x1001300), PlateFieldFactory.FIELD_NAME, 1, 1));
ListingTextField tf = (ListingTextField) cb.getCurrentField(); ListingTextField tf = (ListingTextField) cb.getCurrentField();
assertEquals(2, tf.getNumRows()); assertEquals(2, tf.getNumRows());
// list.add(cu.getMinAddress());
//
// ArrayList<Address> list = new ArrayList<Address>();
// Listing listing = program.getListing();
//
// FunctionIterator iter = listing.getFunctions(true);
// while (iter.hasNext()) {
// Function f = iter.next();
// CodeUnit cu = listing.getCodeUnitAt(f.getEntryPoint());
// assertTrue(cbPlugin.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 1, 1));
// ListingTextField tf = (ListingTextField) cbPlugin.getCurrentField();
// assertEquals(5, tf.getNumRows());
// list.add(cu.getMinAddress());
// }
// assertEquals(66, list.size());
} }
@Test @Test
@ -354,7 +336,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
Listing listing = program.getListing(); Listing listing = program.getListing();
CodeUnit cu = listing.getCodeUnitAt(getAddr(0x1001500)); CodeUnit cu = listing.getCodeUnitAt(getAddr(0x1001500));
assertTrue(!cb.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 1, 1)); assertFalse(cb.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 1, 1));
createPlateComment(cu, "This is a plate comment"); createPlateComment(cu, "This is a plate comment");
@ -465,8 +447,9 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
int transactionID = program.startTransaction("test"); int transactionID = program.startTransaction("test");
CodeUnit cu = program.getListing().getCodeUnitAt(addr); CodeUnit cu = program.getListing().getCodeUnitAt(addr);
try { try {
program.getSymbolTable().createLabel(addr, testName.getMethodName(), program.getSymbolTable()
SourceType.USER_DEFINED); .createLabel(addr, testName.getMethodName(),
SourceType.USER_DEFINED);
cu.setComment(CodeUnit.PLATE_COMMENT, cu.setComment(CodeUnit.PLATE_COMMENT,
"this is a comment\ngo to the address 0x010028de"); "this is a comment\ngo to the address 0x010028de");
} }

View file

@ -39,6 +39,7 @@ public class ClippingTextField implements TextField {
protected int startX; protected int startX;
private int width; private int width;
private int preferredWidth; private int preferredWidth;
private int numDataRows;
private String fullText; private String fullText;
private boolean isClipped; private boolean isClipped;
@ -49,28 +50,40 @@ public class ClippingTextField implements TextField {
/** /**
* Constructs a new ClippingTextField that allows the cursor beyond the end * Constructs a new ClippingTextField that allows the cursor beyond the end
* of the line. This is just a pass through constructor that makes the call: * of the line.
* *
* <pre> * @param startX The x position of the field
* this(startX, width, new AttributedString[] { textElement }, hlFactory, true); * @param width The width of the field
* </pre> * @param textElement The AttributedStrings to display in the field.
* * @param hlFactory The HighlightFactory object used to paint highlights.
* @param startX
* The x position of the field
* @param width
* The width of the field
* @param textElement
* The AttributedStrings to display in the field.
* @param hlFactory
* The HighlightFactory object used to paint highlights.
*/ */
public ClippingTextField(int startX, int width, FieldElement textElement, public ClippingTextField(int startX, int width, FieldElement textElement,
HighlightFactory hlFactory) { HighlightFactory hlFactory) {
// default to one row
this(startX, width, textElement, 1, hlFactory);
}
/**
* Constructs a new ClippingTextField that allows the cursor beyond the end
* of the line.
*
* <p>This constructor allows clients to specify the number of data rows that have been
* converted into a single screen row.
*
* @param startX The x position of the field
* @param width The width of the field
* @param textElement The AttributedStrings to display in the field.
* @param numDataRows the number of data rows represented by this single screen row field
* @param hlFactory The HighlightFactory object used to paint highlights.
*/
public ClippingTextField(int startX, int width, FieldElement textElement, int numDataRows,
HighlightFactory hlFactory) {
this.textElement = textElement;
this.hlFactory = hlFactory;
this.startX = startX; this.startX = startX;
this.width = width; this.width = width;
this.numDataRows = numDataRows;
this.textElement = textElement;
this.hlFactory = hlFactory;
this.preferredWidth = textElement.getStringWidth(); this.preferredWidth = textElement.getStringWidth();
clip(width); clip(width);
@ -139,7 +152,7 @@ public class ClippingTextField implements TextField {
@Override @Override
public int getNumDataRows() { public int getNumDataRows() {
return 1; return numDataRows;
} }
@Override @Override
@ -336,18 +349,13 @@ public class ClippingTextField implements TextField {
@Override @Override
public RowColLocation screenToDataLocation(int screenRow, int screenColumn) { public RowColLocation screenToDataLocation(int screenRow, int screenColumn) {
return originalElement.getDataLocationForCharacterIndex(screenColumn); return originalElement.getDataLocationForCharacterIndex(screenColumn);
} }
@Override @Override
public RowColLocation dataToScreenLocation(int dataRow, int dataColumn) { public RowColLocation dataToScreenLocation(int dataRow, int dataColumn) {
int column = textElement.getCharacterIndexForDataLocation(dataRow, dataColumn); int column = textElement.getCharacterIndexForDataLocation(dataRow, dataColumn);
if (column < 0) { if (column < 0) {
// place at the end if past the end return new DefaultRowColLocation(0, textElement.length());
if (dataColumn >= textElement.length()) {
return new DefaultRowColLocation(0, textElement.length());
}
return new DefaultRowColLocation();
} }
return new RowColLocation(0, column); return new RowColLocation(0, column);
} }

View file

@ -22,6 +22,7 @@ import java.util.List;
import javax.swing.JComponent; import javax.swing.JComponent;
import docking.widgets.fieldpanel.support.RowColLocation; import docking.widgets.fieldpanel.support.RowColLocation;
import generic.json.Json;
/** /**
* A FieldElement that is composed of other FieldElements. The elements are laid out horizontally. * A FieldElement that is composed of other FieldElements. The elements are laid out horizontally.
@ -107,46 +108,6 @@ public class CompositeFieldElement implements FieldElement {
return heightBelow; return heightBelow;
} }
//==================================================================================================
// FontMetrics methods
//==================================================================================================
@Override
public int getStringWidth() {
if (textWidth == -1) {
textWidth = 0;
for (FieldElement fieldElement : fieldElements) {
textWidth += fieldElement.getStringWidth();
}
}
return textWidth;
}
@Override
public String getText() {
if (fullText == null) {
StringBuilder buffer = new StringBuilder();
for (FieldElement fieldElement : fieldElements) {
buffer.append(fieldElement.getText());
}
fullText = buffer.toString();
}
return fullText;
}
//==================================================================================================
// Paint methods
//==================================================================================================
@Override
public void paint(JComponent c, Graphics g, int x, int y) {
int xPos = x;
for (FieldElement fieldElement : fieldElements) {
fieldElement.paint(c, g, xPos, y);
xPos += fieldElement.getStringWidth();
}
}
@Override @Override
public FieldElement replaceAll(char[] targets, char repacement) { public FieldElement replaceAll(char[] targets, char repacement) {
FieldElement[] newStrings = new FieldElement[fieldElements.length]; FieldElement[] newStrings = new FieldElement[fieldElements.length];
@ -191,16 +152,6 @@ public class CompositeFieldElement implements FieldElement {
return new CompositeFieldElement(newStrings); return new CompositeFieldElement(newStrings);
} }
private static class IndexedOffset {
int index;
int offset;
IndexedOffset(int index, int offset) {
this.index = index;
this.offset = offset;
}
}
@Override @Override
public FieldElement getFieldElement(int column) { public FieldElement getFieldElement(int column) {
IndexedOffset startPos = getIndexedOffsetForCharPosition(column); IndexedOffset startPos = getIndexedOffsetForCharPosition(column);
@ -217,6 +168,46 @@ public class CompositeFieldElement implements FieldElement {
return getText(); return getText();
} }
@Override
public int getStringWidth() {
if (textWidth == -1) {
textWidth = 0;
for (FieldElement fieldElement : fieldElements) {
textWidth += fieldElement.getStringWidth();
}
}
return textWidth;
}
@Override
public String getText() {
if (fullText == null) {
StringBuilder buffer = new StringBuilder();
for (FieldElement fieldElement : fieldElements) {
buffer.append(fieldElement.getText());
}
fullText = buffer.toString();
}
return fullText;
}
@Override
public void paint(JComponent c, Graphics g, int x, int y) {
int xPos = x;
for (FieldElement fieldElement : fieldElements) {
fieldElement.paint(c, g, xPos, y);
xPos += fieldElement.getStringWidth();
}
}
/**
* Returns the number of sub-elements contained in this field
* @return the number of sub-elements contained in this field
*/
public int getNumElements() {
return fieldElements.length;
}
//================================================================================================== //==================================================================================================
// Location Info // Location Info
//================================================================================================== //==================================================================================================
@ -229,6 +220,7 @@ public class CompositeFieldElement implements FieldElement {
@Override @Override
public int getCharacterIndexForDataLocation(int dataRow, int dataColumn) { public int getCharacterIndexForDataLocation(int dataRow, int dataColumn) {
int columnsSoFar = 0; int columnsSoFar = 0;
for (int i = fieldElements.length - 1; i >= 0; i--) { for (int i = fieldElements.length - 1; i >= 0; i--) {
columnsSoFar += fieldElements[i].length(); columnsSoFar += fieldElements[i].length();
@ -242,4 +234,20 @@ public class CompositeFieldElement implements FieldElement {
return -1; return -1;
} }
private static class IndexedOffset {
int index;
int offset;
IndexedOffset(int index, int offset) {
this.index = index;
this.offset = offset;
}
@Override
public String toString() {
return Json.toString(this);
}
}
} }

View file

@ -342,12 +342,15 @@ public class CompositeVerticalLayoutTextField implements TextField {
} }
private FieldRow getFieldRowFromDataRow(int dataRow) { private FieldRow getFieldRowFromDataRow(int dataRow) {
int currentRow = 0; int currentRow = 0;
for (FieldRow row : fieldRows) { for (FieldRow row : fieldRows) {
if (currentRow >= dataRow) { int length = row.field.getNumDataRows();
if (currentRow + length > dataRow) {
return row; return row;
} }
currentRow += row.field.getNumDataRows(); currentRow += length;
} }
return fieldRows.get(fieldRows.size() - 1); return fieldRows.get(fieldRows.size() - 1);
} }

View file

@ -20,8 +20,9 @@ import java.util.*;
import docking.widgets.fieldpanel.support.HighlightFactory; import docking.widgets.fieldpanel.support.HighlightFactory;
/** /**
* This class provides a TextField implementation that takes multiple AttributedStrings and places * This class provides a TextField implementation that takes multiple AttributedString field
* as many that will fit on a line without clipping before continuing to the next line. * elements and places as many that will fit on a line without clipping before continuing to the
* next line.
*/ */
public class FlowLayoutTextField extends VerticalLayoutTextField { public class FlowLayoutTextField extends VerticalLayoutTextField {
@ -67,27 +68,35 @@ public class FlowLayoutTextField extends VerticalLayoutTextField {
int currentIndex = 0; int currentIndex = 0;
while (currentIndex < elements.size()) { while (currentIndex < elements.size()) {
int numberPerLine = getNumberOfElementsPerLine(elements, currentIndex, width); int numberPerLine = getNumberOfElementsPerLine(elements, currentIndex, width);
subFields.add(createLine(elements, currentIndex, numberPerLine)); subFields.add(createLineFromElements(elements, currentIndex, numberPerLine));
currentIndex += numberPerLine; currentIndex += numberPerLine;
} }
return subFields; return subFields;
} }
private static CompositeFieldElement createLine(List<FieldElement> elements, int from, @Override
int length) { protected TextField createFieldForLine(FieldElement element) {
return new CompositeFieldElement(elements.subList(from, from + length)); CompositeFieldElement composite = (CompositeFieldElement) element;
int numDataRows = composite.getNumElements();
return new ClippingTextField(startX, width, element, numDataRows, hlFactory);
}
private static CompositeFieldElement createLineFromElements(List<FieldElement> elements,
int start, int length) {
return new CompositeFieldElement(elements.subList(start, start + length));
} }
private static int getNumberOfElementsPerLine(List<FieldElement> elements, int start, private static int getNumberOfElementsPerLine(List<FieldElement> elements, int start,
int width) { int width) {
int currentWidth = 0; int currentWidth = 0;
int count = 0; for (int i = start; i < elements.size(); i++) {
for (FieldElement element : elements) { FieldElement element = elements.get(i);
currentWidth += element.getStringWidth(); currentWidth += element.getStringWidth();
count++;
if (currentWidth > width) { if (currentWidth > width) {
return Math.max(count - 1, 1); int count = i - start;
return Math.max(count, 1);
} }
} }
return elements.size() - start; return elements.size() - start;

View file

@ -16,7 +16,8 @@
package docking.widgets.fieldpanel.field; package docking.widgets.fieldpanel.field;
import java.awt.*; import java.awt.*;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.swing.JComponent; import javax.swing.JComponent;
@ -26,6 +27,7 @@ import org.apache.commons.lang3.StringUtils;
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager; import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
import docking.widgets.fieldpanel.internal.PaintContext; import docking.widgets.fieldpanel.internal.PaintContext;
import docking.widgets.fieldpanel.support.*; import docking.widgets.fieldpanel.support.*;
import generic.json.Json;
/** /**
* This class provides a TextField implementation that takes multiple FieldElements and places * This class provides a TextField implementation that takes multiple FieldElements and places
@ -33,7 +35,10 @@ import docking.widgets.fieldpanel.support.*;
*/ */
public class VerticalLayoutTextField implements TextField { public class VerticalLayoutTextField implements TextField {
protected List<TextField> subFields; // list of fields for FieldElements //
// The sub-fields of this text field. Each FieldRow has a Field, a screen row and a data row.
//
protected List<FieldRow> subFields;
protected int startX; protected int startX;
protected int width; protected int width;
protected int preferredWidth; protected int preferredWidth;
@ -136,16 +141,16 @@ public class VerticalLayoutTextField implements TextField {
} }
protected void calculateHeight() { protected void calculateHeight() {
heightAbove = (subFields.get(0)).getHeightAbove(); heightAbove = (subFields.get(0)).field.getHeightAbove();
for (Field field : subFields) { for (FieldRow fieldRow : subFields) {
height += field.getHeight(); height += fieldRow.field.getHeight();
} }
} }
private int calculatePreferredWidth() { private int calculatePreferredWidth() {
int widest = 0; int widest = 0;
for (Field field : subFields) { for (FieldRow fieldRow : subFields) {
widest = Math.max(widest, field.getPreferredWidth()); widest = Math.max(widest, fieldRow.field.getPreferredWidth());
} }
return widest; return widest;
} }
@ -197,20 +202,21 @@ public class VerticalLayoutTextField implements TextField {
@Override @Override
public int getNumCols(int row) { public int getNumCols(int row) {
Field f = subFields.get(row); Field f = getField(row);
return f.getNumCols(0); return f.getNumCols(0);
} }
@Override @Override
public int getRow(int y) { public int getRow(int y) {
if (y < 0) { if (y < -heightAbove) {
return 0; return 0;
} }
int heightSoFar = 0; int heightSoFar = -heightAbove;
int n = subFields.size(); int n = subFields.size();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
Field f = subFields.get(i); Field f = getField(i);
heightSoFar += f.getHeight(); heightSoFar += f.getHeight();
if (heightSoFar > y) { if (heightSoFar > y) {
return i; return i;
@ -221,7 +227,7 @@ public class VerticalLayoutTextField implements TextField {
@Override @Override
public int getCol(int row, int x) { public int getCol(int row, int x) {
Field f = subFields.get(row); Field f = getField(row);
return f.getCol(0, x); return f.getCol(0, x);
} }
@ -230,7 +236,7 @@ public class VerticalLayoutTextField implements TextField {
int y = -heightAbove; int y = -heightAbove;
for (int i = 0; i < row; i++) { for (int i = 0; i < row; i++) {
Field f = subFields.get(row); Field f = getField(i);
y += f.getHeight(); y += f.getHeight();
} }
return y; return y;
@ -238,7 +244,7 @@ public class VerticalLayoutTextField implements TextField {
@Override @Override
public int getX(int row, int col) { public int getX(int row, int col) {
Field f = subFields.get(row); Field f = getField(row);
return f.getX(0, col); return f.getX(0, col);
} }
@ -248,7 +254,7 @@ public class VerticalLayoutTextField implements TextField {
if ((row < 0) || (row >= subFields.size())) { if ((row < 0) || (row >= subFields.size())) {
return false; return false;
} }
Field f = subFields.get(row); Field f = getField(row);
return f.isValid(0, col); return f.isValid(0, col);
} }
@ -295,7 +301,7 @@ public class VerticalLayoutTextField implements TextField {
int translatedY = 0; int translatedY = 0;
int extraSpace = rowSeparator.length(); int extraSpace = rowSeparator.length();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
ClippingTextField subField = (ClippingTextField) subFields.get(i); ClippingTextField subField = (ClippingTextField) getField(i);
int subFieldHeight = subField.getHeight(); int subFieldHeight = subField.getHeight();
int endY = startY + subFieldHeight; int endY = startY + subFieldHeight;
@ -332,7 +338,7 @@ public class VerticalLayoutTextField implements TextField {
private void print(Graphics g, PaintContext context) { private void print(Graphics g, PaintContext context) {
int n = subFields.size(); int n = subFields.size();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
ClippingTextField clippingField = (ClippingTextField) subFields.get(i); ClippingTextField clippingField = (ClippingTextField) getField(i);
clippingField.print(g, context); clippingField.print(g, context);
@ -346,10 +352,10 @@ public class VerticalLayoutTextField implements TextField {
if ((row < 0) || (row >= subFields.size())) { if ((row < 0) || (row >= subFields.size())) {
return null; return null;
} }
Field f = subFields.get(row); Field f = getField(row);
Rectangle r = f.getCursorBounds(0, col); Rectangle r = f.getCursorBounds(0, col);
for (int i = 0; i < row; i++) { for (int i = 0; i < row; i++) {
f = subFields.get(row); f = getField(i);
r.y += f.getHeight(); r.y += f.getHeight();
} }
return r; return r;
@ -370,10 +376,11 @@ public class VerticalLayoutTextField implements TextField {
if ((topOfScreen < -heightAbove) || (topOfScreen > height - heightAbove)) { if ((topOfScreen < -heightAbove) || (topOfScreen > height - heightAbove)) {
return max; return max;
} }
int row = getRow(topOfScreen); int row = getRow(topOfScreen);
int y = getY(row); int y = getY(row);
int rowOffset = topOfScreen - y; int rowOffset = topOfScreen - y;
int rowHeight = (subFields.get(row)).getHeight(); int rowHeight = getField(row).getHeight();
if (direction > 0) { // if scrolling down if (direction > 0) { // if scrolling down
return rowHeight - rowOffset; return rowHeight - rowOffset;
} }
@ -399,14 +406,6 @@ public class VerticalLayoutTextField implements TextField {
isPrimary = state; isPrimary = state;
} }
/**
* Returns the list of subfields in this field.
* @return the list of subfields in this field.
*/
public List<Field> getSubfields() {
return Collections.unmodifiableList(subFields);
}
@Override @Override
public int getHeightAbove() { public int getHeightAbove() {
return heightAbove; return heightAbove;
@ -425,17 +424,17 @@ public class VerticalLayoutTextField implements TextField {
@Override @Override
public FieldElement getFieldElement(int screenRow, int screenColumn) { public FieldElement getFieldElement(int screenRow, int screenColumn) {
TextField f = subFields.get(screenRow); TextField f = getField(screenRow);
int fieldRow = 0; // each field is on a single row int fieldRow = 0; // each field is on a single row
return f.getFieldElement(fieldRow, screenColumn); return f.getFieldElement(fieldRow, screenColumn);
} }
protected List<TextField> layoutElements(List<FieldElement> textElements, int maxLines) { protected List<FieldRow> layoutElements(List<FieldElement> textElements, int maxLines) {
List<TextField> newSubFields = new ArrayList<>(); List<FieldRow> newSubFields = new ArrayList<>();
boolean tooManyLines = textElements.size() > maxLines; boolean tooManyLines = textElements.size() > maxLines;
int currentRow = 0;
for (int i = 0; i < textElements.size() && i < maxLines; i++) { for (int i = 0; i < textElements.size() && i < maxLines; i++) {
FieldElement element = textElements.get(i); FieldElement element = textElements.get(i);
if (tooManyLines && (i == maxLines - 1)) { if (tooManyLines && (i == maxLines - 1)) {
@ -444,9 +443,13 @@ public class VerticalLayoutTextField implements TextField {
elements[1] = new StrutFieldElement(500); elements[1] = new StrutFieldElement(500);
element = new CompositeFieldElement(elements); element = new CompositeFieldElement(elements);
} }
TextField field = new ClippingTextField(startX, width, element, hlFactory); TextField field = createFieldForLine(element);
newSubFields.add(field); int modelRow = currentRow;
int screenRow = newSubFields.size();
newSubFields.add(new FieldRow(field, modelRow, screenRow));
isClipped |= field.isClipped(); isClipped |= field.isClipped();
currentRow += field.getNumRows();
} }
isClipped |= tooManyLines; isClipped |= tooManyLines;
@ -455,38 +458,36 @@ public class VerticalLayoutTextField implements TextField {
} }
/** /**
* Translates the row and column to a String index and character offset into * Create the text field for given field element
* that string. * @param element the element
* @param screenRow the row containing the location. * @return the field
* @param screenColumn the character position in the row of the location
* @return a MultiStringLocation containing the string index and position
* within that string.
*/ */
protected TextField createFieldForLine(FieldElement element) {
return new ClippingTextField(startX, width, element, hlFactory);
}
@Override @Override
public RowColLocation screenToDataLocation(int screenRow, int screenColumn) { public RowColLocation screenToDataLocation(int screenRow, int screenColumn) {
screenRow = Math.min(screenRow, subFields.size() - 1); screenRow = Math.min(screenRow, subFields.size() - 1);
screenRow = Math.max(screenRow, 0); screenRow = Math.max(screenRow, 0);
TextField field = subFields.get(screenRow); TextField field = getField(screenRow);
screenColumn = Math.min(screenColumn, field.getText().length()); screenColumn = Math.min(screenColumn, field.getText().length());
screenColumn = Math.max(screenColumn, 0); screenColumn = Math.max(screenColumn, 0);
int fieldRow = 0; // each field is on a single row int dataRow = getDataRow(field);
return field.screenToDataLocation(fieldRow, screenColumn); return field.screenToDataLocation(dataRow, screenColumn);
} }
@Override @Override
public RowColLocation dataToScreenLocation(int dataRow, int dataColumn) { public RowColLocation dataToScreenLocation(int dataRow, int dataColumn) {
if (dataRow >= getNumRows()) { FieldRow fieldRow = getFieldRowFromDataRow(dataRow);
TextField lastField = subFields.get(subFields.size()); TextField field = fieldRow.field;
return new DefaultRowColLocation(lastField.getText().length(), subFields.size() - 1);
}
TextField field = subFields.get(dataRow);
RowColLocation location = field.dataToScreenLocation(dataRow, dataColumn); RowColLocation location = field.dataToScreenLocation(dataRow, dataColumn);
return location.withRow(dataRow); int screenRow = fieldRow.screenRow;
return location.withRow(screenRow);
} }
@Override @Override
@ -517,7 +518,8 @@ public class VerticalLayoutTextField implements TextField {
} }
int lastRow = n - 1; int lastRow = n - 1;
int lastColumn = subFields.get(lastRow).getText().length(); TextField field = getField(lastRow);
int lastColumn = field.getText().length();
return new DefaultRowColLocation(lastRow, lastColumn); return new DefaultRowColLocation(lastRow, lastColumn);
} }
@ -525,4 +527,54 @@ public class VerticalLayoutTextField implements TextField {
public boolean isClipped() { public boolean isClipped() {
return isClipped; return isClipped;
} }
/**
* Returns the view's text lines of this field
* @return the lines
*/
protected List<String> getLines() {
return lines;
}
private TextField getField(int screenRow) {
return subFields.get(screenRow).field;
}
private FieldRow getFieldRowFromDataRow(int dataRow) {
int currentRow = 0;
for (FieldRow row : subFields) {
int length = row.field.getNumDataRows();
if (currentRow + length > dataRow) {
return row;
}
currentRow += length;
}
return subFields.get(subFields.size() - 1);
}
private int getDataRow(TextField field) {
for (FieldRow fieldRow : subFields) {
if (fieldRow.field == field) {
return fieldRow.dataRow;
}
}
return 0;
}
private class FieldRow {
private TextField field;
private int dataRow;
private int screenRow;
FieldRow(TextField field, int dataRow, int screenRow) {
this.field = field;
this.dataRow = dataRow;
this.screenRow = screenRow;
}
@Override
public String toString() {
return Json.toString(this);
}
}
} }

View file

@ -47,6 +47,22 @@ public class FlowLayoutTextFieldTest extends AbstractGenericTest {
FontMetrics fm = tk.getFontMetrics(font); FontMetrics fm = tk.getFontMetrics(font);
List<FieldElement> elements = new ArrayList<>(); List<FieldElement> elements = new ArrayList<>();
/*
Data Rows:
Hello
World
Supercalifragilisticexpialidocious
Wow!
Screen Rows:
Hello World
Supercalifra...
Wow
*/
elements.add(new TextFieldElement(new AttributedString("Hello ", Color.BLUE, fm), 0, 0)); elements.add(new TextFieldElement(new AttributedString("Hello ", Color.BLUE, fm), 0, 0));
elements.add(new TextFieldElement( elements.add(new TextFieldElement(
new AttributedString("World ", Color.RED, fm, true, Color.BLUE), 1, 0)); new AttributedString("World ", Color.RED, fm, true, Color.BLUE), 1, 0));
@ -95,17 +111,19 @@ public class FlowLayoutTextFieldTest extends AbstractGenericTest {
assertEquals(new RowColLocation(1, 0), textField.dataToScreenLocation(2, 0)); assertEquals(new RowColLocation(1, 0), textField.dataToScreenLocation(2, 0));
assertEquals(new RowColLocation(1, 4), textField.dataToScreenLocation(2, 4)); assertEquals(new RowColLocation(1, 4), textField.dataToScreenLocation(2, 4));
assertEquals(new RowColLocation(1, 15), textField.dataToScreenLocation(2, 15));
// Supercalifra (12 chars); ... (3 chars); Supercalifra... (15 chars)
assertEquals(new DefaultRowColLocation(1, 12), textField.dataToScreenLocation(2, 15));
assertEquals(new RowColLocation(2, 0), textField.dataToScreenLocation(3, 0)); assertEquals(new RowColLocation(2, 0), textField.dataToScreenLocation(3, 0));
assertEquals(new RowColLocation(2, 4), textField.dataToScreenLocation(3, 4)); assertEquals(new RowColLocation(2, 4), textField.dataToScreenLocation(3, 4));
assertEquals(new RowColLocation(0, 0), textField.dataToScreenLocation(0, 12)); assertEquals(new DefaultRowColLocation(0, 12), textField.dataToScreenLocation(0, 12));
assertEquals(new RowColLocation(0, 0), textField.dataToScreenLocation(0, 75)); assertEquals(new DefaultRowColLocation(0, 12), textField.dataToScreenLocation(0, 75));
} }
@Test @Test
public void testGetRowColumn() { public void testTextOffsetToScreenLocation() {
assertEquals(new RowColLocation(0, 0), textField.textOffsetToScreenLocation(0)); assertEquals(new RowColLocation(0, 0), textField.textOffsetToScreenLocation(0));
assertEquals(new RowColLocation(0, 5), textField.textOffsetToScreenLocation(5)); assertEquals(new RowColLocation(0, 5), textField.textOffsetToScreenLocation(5));
assertEquals(new RowColLocation(0, 6), textField.textOffsetToScreenLocation(6)); assertEquals(new RowColLocation(0, 6), textField.textOffsetToScreenLocation(6));
@ -116,7 +134,8 @@ public class FlowLayoutTextFieldTest extends AbstractGenericTest {
assertEquals(new RowColLocation(1, 3), textField.textOffsetToScreenLocation(15)); assertEquals(new RowColLocation(1, 3), textField.textOffsetToScreenLocation(15));
assertEquals(new RowColLocation(1, 18), textField.textOffsetToScreenLocation(30)); assertEquals(new RowColLocation(1, 18), textField.textOffsetToScreenLocation(30));
assertEquals(new RowColLocation(2, 0), textField.textOffsetToScreenLocation(47));
assertEquals(new RowColLocation(2, 5), textField.textOffsetToScreenLocation(1000)); assertEquals(new DefaultRowColLocation(2, 5), textField.textOffsetToScreenLocation(1000));
} }
} }

View file

@ -324,7 +324,7 @@ public class CompositeVerticalLayoutTextFieldTest extends AbstractGenericTest {
assertRowCol(1, 5, field.dataToScreenLocation(1, 5)); assertRowCol(1, 5, field.dataToScreenLocation(1, 5));
// try accessing clipped rows // try accessing clipped rows
assertRowCol(1, 0, field.dataToScreenLocation(2, 0)); assertRowCol(1, 5, field.dataToScreenLocation(2, 0));
assertRowCol(1, 5, field.dataToScreenLocation(2, 5)); assertRowCol(1, 5, field.dataToScreenLocation(2, 5));
assertRowCol(1, 5, field.dataToScreenLocation(20, 50)); assertRowCol(1, 5, field.dataToScreenLocation(20, 50));
} }

View file

@ -435,7 +435,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
String operandPrefix = "dword ptr [EBP + "; String operandPrefix = "dword ptr [EBP + ";
String operandReferenceName = "destStr]"; String operandReferenceName = "destStr]";
OperandFieldLocation variableOperandReferenceLocation = new OperandFieldLocation(program, OperandFieldLocation variableOperandReferenceLocation = new OperandFieldLocation(program,
addr("0100416c"), null, addr("0x8"), operandPrefix + operandReferenceName, 1, 9); addr("0100416c"), null, addr("0x8"), operandPrefix + operandReferenceName, 0, 9);
codeBrowserPlugin.goTo(variableOperandReferenceLocation); codeBrowserPlugin.goTo(variableOperandReferenceLocation);
DockingAction pasteAction = getAction(codeBrowserClipboardProvider, PASTE_ACTION_NAME); DockingAction pasteAction = getAction(codeBrowserClipboardProvider, PASTE_ACTION_NAME);
@ -1551,8 +1551,8 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
/* /*
* We remove the FieldPanel focus listeners for these tests, as when they lose focus, * We remove the FieldPanel focus listeners for these tests, as when they lose focus,
* the selection mechanism does not work as expected. Focus changes can happen * the selection mechanism does not work as expected. Focus changes can happen
* indeterminately during parallel batch testing. * indeterminately during parallel batch testing.
*/ */
private void removeFieldPanelFocusListeners(Container c) { private void removeFieldPanelFocusListeners(Container c) {