mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-02 16:33:49 +00:00
Merge remote-tracking branch 'origin/GP-0-dragonmacher-xref-merge-test-fixes--SQUASHED'
This commit is contained in:
commit
e3f5e9a061
|
@ -60,7 +60,8 @@ public class CommentFieldSearcher extends ProgramDatabaseFieldSearcher {
|
|||
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);
|
||||
if (comment == null) {
|
||||
return;
|
||||
|
@ -84,7 +85,7 @@ public class CommentFieldSearcher extends ProgramDatabaseFieldSearcher {
|
|||
charOffset, rowIndex);
|
||||
case CodeUnit.PLATE_COMMENT:
|
||||
return new PlateFieldLocation(program, address, dataPath, rowIndex, charOffset,
|
||||
comments, rowIndex - 1);
|
||||
comments, rowIndex);
|
||||
case CodeUnit.REPEATABLE_COMMENT:
|
||||
return new RepeatableCommentFieldLocation(program, address, dataPath, comments,
|
||||
rowIndex, charOffset, rowIndex); // TODO One of searchStrIndex parameters is wrong.
|
||||
|
|
|
@ -427,11 +427,9 @@ public class BytesFieldFactory extends FieldFactory {
|
|||
|
||||
ListingTextField btf = (ListingTextField) bf;
|
||||
RowColLocation rcl = btf.dataToScreenLocation(tokenIndex, tokenOffset);
|
||||
|
||||
if (hasSamePath(bf, loc)) {
|
||||
return new FieldLocation(index, fieldNum, rcl.row(), rcl.col());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import java.math.BigInteger;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
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.ToolOptions;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.*;
|
||||
|
@ -141,21 +144,18 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
}
|
||||
|
||||
CodeUnit cu = (CodeUnit) proxy.getObject();
|
||||
List<FieldElement> elements = new ArrayList<>(10);
|
||||
boolean isClipped = false;
|
||||
List<FieldElement> elements = new ArrayList<>();
|
||||
String commentText = getCommentText(cu);
|
||||
if ((commentText == null) || (commentText.isEmpty())) {
|
||||
generateDefaultPlate(elements, cu);
|
||||
if (StringUtils.isBlank(commentText)) {
|
||||
getDefaultFieldElements(cu, elements);
|
||||
}
|
||||
else {
|
||||
isClipped = generateFormattedPlateComment(elements, cu);
|
||||
isClipped = getFormattedFieldElements(cu, elements);
|
||||
}
|
||||
|
||||
addBlankLines(elements, cu);
|
||||
|
||||
if (elements.size() == 0) {
|
||||
// no real or default comment
|
||||
return null;
|
||||
if (elements.isEmpty()) {
|
||||
return null; // no real or default comments
|
||||
}
|
||||
|
||||
if (isNestedDataAtSameAddressAsParent(proxy)) {
|
||||
|
@ -170,6 +170,27 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
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) {
|
||||
if (proxy instanceof DataProxy) {
|
||||
DataProxy dp = (DataProxy) proxy;
|
||||
|
@ -200,67 +221,39 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
return buffy.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* @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) {
|
||||
String[] comments = cu.getCommentAsArray(CodeUnit.PLATE_COMMENT);
|
||||
if ((comments == null) || (comments.length == 0)) {
|
||||
private boolean generateFormattedPlateComment(List<FieldElement> elements, String[] comments,
|
||||
Program p) {
|
||||
if (comments == null || comments.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Program program = cu.getProgram();
|
||||
AttributedString prototype = new AttributedString(EMPTY_STRING, color, getMetrics());
|
||||
|
||||
for (int i = 0; i < comments.length; i++) {
|
||||
elementList
|
||||
.add(CommentUtils.parseTextForAnnotations(comments[i], program, prototype, i));
|
||||
AttributedString asteriscs = getStarsString();
|
||||
int row = elements.size();
|
||||
|
||||
// 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) {
|
||||
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) {
|
||||
AttributedString prototype = new AttributedString(EMPTY_STRING, color, getMetrics());
|
||||
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);
|
||||
}
|
||||
}
|
||||
// add bottom border
|
||||
elements.add(new TextFieldElement(asteriscs, row++, 0));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -269,13 +262,17 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
* Text will be left justified between two '*' and padded based upon the
|
||||
* available field width.
|
||||
* @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
|
||||
*/
|
||||
private boolean addSideBorders(List<FieldElement> elements) {
|
||||
private boolean addSideBorders(List<FieldElement> elements, int commentsStart) {
|
||||
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);
|
||||
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);
|
||||
isClipped = result.isClipped();
|
||||
elements.set(0, result.getFieldElement());
|
||||
|
@ -283,8 +280,8 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
FieldElementResult result = addSideBorder(elements.get(i), i + 1, false);
|
||||
for (int i = commentsStart; i < elements.size(); i++) {
|
||||
FieldElementResult result = addSideBorder(elements.get(i), i, false);
|
||||
isClipped |= result.isClipped();
|
||||
elements.set(i, result.getFieldElement());
|
||||
}
|
||||
|
@ -311,7 +308,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
int prePadding = center ? totalPadding / 2 : 0;
|
||||
int postPadding = center ? (totalPadding + 1) / 2 : totalPadding;
|
||||
|
||||
StringBuffer buffy = new StringBuffer();
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
buffy.append('*').append(' ');
|
||||
addPadding(buffy, prePadding);
|
||||
|
||||
|
@ -335,21 +332,56 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
ellipsisLength > 0);
|
||||
}
|
||||
|
||||
private void addPadding(StringBuffer buf, int count) {
|
||||
private void addPadding(StringBuilder buf, int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
buf.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
private void generateDefaultPlate(List<FieldElement> elementList, CodeUnit cu) {
|
||||
String defaultComment = getDefaultComment(cu);
|
||||
if (defaultComment != null) {
|
||||
AttributedString as = new AttributedString(defaultComment, color, getMetrics());
|
||||
elementList.add(new TextFieldElement(as, 0, 0));
|
||||
addBorder(elementList);
|
||||
private void addBlankLines(List<FieldElement> elements, int numberBlankLines, CodeUnit cu) {
|
||||
AttributedString prototype = new AttributedString(EMPTY_STRING, color, getMetrics());
|
||||
for (int row = 0; row < numberBlankLines; row++) {
|
||||
elements.add(new TextFieldElement(prototype, row, 0));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
if (showFunctionPlates) {
|
||||
|
@ -367,11 +399,6 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
return SUBROUTINE_PLATE_COMMENT;
|
||||
}
|
||||
|
||||
// TODO handle case statements?
|
||||
// if (showCasePlates) {
|
||||
// return CASE_PLATE_COMMENT;
|
||||
// }
|
||||
|
||||
if (showTransitionPlates) {
|
||||
if (isDeadCode(cu)) {
|
||||
return DEAD_CODE_PLATE_COMMENT;
|
||||
|
@ -432,7 +459,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
int n = width / starWidth;
|
||||
|
||||
if (stars.length() != n) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = 0; i < n; i++) {
|
||||
buf.append('*');
|
||||
}
|
||||
|
@ -465,18 +492,18 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
((ListingTextField) listingField).screenToDataLocation(row, col);
|
||||
|
||||
//
|
||||
// The 'row' value is an offset from the top of the plate comment, which has 0 or
|
||||
// more blank lines at the top, followed by a line of asterisks.
|
||||
// The 'row' value includes blank lines and header decoration lines. The 'commentRow' used
|
||||
// below is the index into the list of comments. Calculate the comment beginning by
|
||||
// removing the non-comment lines.
|
||||
//
|
||||
int fillerLineCount = getNumberOfLeadingFillerLines(listingField);
|
||||
|
||||
int commentRow = row - fillerLineCount;
|
||||
if (commentRow >= comments.length) {
|
||||
commentRow = -1; // clicked the bottom decoration line
|
||||
if (commentRow >= comments.length || commentRow < 0) {
|
||||
commentRow = -1; // clicked above the comment or the bottom decoration line
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -491,6 +518,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
@Override
|
||||
public FieldLocation getFieldLocation(ListingField listingField, BigInteger index, int fieldNum,
|
||||
ProgramLocation programLoc) {
|
||||
|
||||
if (!(programLoc instanceof CommentFieldLocation)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -507,8 +535,40 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
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;
|
||||
RowColLocation location = listingTextField.dataToScreenLocation(commentLocation.getRow(),
|
||||
RowColLocation location = listingTextField.dataToScreenLocation(dataRow,
|
||||
commentLocation.getCharOffset());
|
||||
return new FieldLocation(index, fieldNum, location.row(), location.col());
|
||||
}
|
||||
|
@ -614,7 +674,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
Address prevAddr = cu.getMinAddress().subtractNoWrap(1);
|
||||
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!
|
||||
}
|
||||
return null;
|
||||
|
@ -638,7 +698,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
}
|
||||
initialized = true;
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\n");
|
||||
for (int i = 0; i < 19; i++) {
|
||||
sb.append("|");
|
||||
|
@ -725,14 +785,13 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
int getLeadingFillerLineCount() {
|
||||
int count = 0;
|
||||
|
||||
for (Field field : subFields) {
|
||||
String text = field.getText().trim();
|
||||
for (String line : getLines()) {
|
||||
count++;
|
||||
if (text.isEmpty()) {
|
||||
if (line.isEmpty()) {
|
||||
continue; // skip leading blank lines
|
||||
}
|
||||
|
||||
if (text.startsWith("*")) {
|
||||
if (line.startsWith("*")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -551,7 +551,7 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
List<XrefFieldElement> elements = new ArrayList<>();
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function currentFunction = functionManager.getFunctionContaining(cu.getMinAddress());
|
||||
int n = tooMany ? maxXRefs + 1 : totalXrefs;
|
||||
int n = tooMany ? maxXRefs : totalXrefs;
|
||||
int count = 0;
|
||||
for (; count < xrefs.size() && count < n; count++) {
|
||||
Reference ref = xrefs.get(count);
|
||||
|
|
|
@ -896,7 +896,8 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
|
|||
assertEquals("XREFs Field.Display Local Block", names.get(1));
|
||||
assertEquals("XREFs Field.Display Namespace", names.get(2));
|
||||
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));
|
||||
|
||||
|
@ -968,7 +969,9 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
|
|||
btf = (ListingTextField) cb.getCurrentField();
|
||||
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();
|
||||
assertTrue(cb.goToField(addr("0x1003d9f"), "XRef", 0, 0));
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
|
|
|
@ -212,25 +212,25 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
for (int i = 0; i < 22; i++) {
|
||||
scrollbar.getUnitIncrement(1);
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x1001027"), codeBrowser.getAddressTopOfScreen());
|
||||
|
||||
for (int i = 0; i < 18; i++) {
|
||||
scrollbar.getUnitIncrement(-1);
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen());
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
scrollbar.getBlockIncrement(1);
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x1001136"), codeBrowser.getAddressTopOfScreen());
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
scrollbar.getBlockIncrement(-1);
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen());
|
||||
|
||||
}
|
||||
|
@ -333,25 +333,25 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
for (int i = 0; i < 20; i++) {
|
||||
scrollbar.getUnitIncrement(1);
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x1001025"), codeBrowser.getAddressTopOfScreen());
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
scrollbar.getUnitIncrement(-1);
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen());
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
scrollbar.getBlockIncrement(1);
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x1001136"), codeBrowser.getAddressTopOfScreen());
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
scrollbar.getBlockIncrement(-1);
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x1001000"), codeBrowser.getAddressTopOfScreen());
|
||||
|
||||
}
|
||||
|
@ -412,7 +412,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
|
||||
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
|
||||
codeBrowser.goToField(addr("0x1007001"), "Address", 0, 0);
|
||||
assertEquals("01007001", codeBrowser.getCurrentFieldText());
|
||||
|
@ -423,7 +423,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
|
||||
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
|
||||
codeBrowser.goToField(addr("0x1007000"), "Address", 0, 0);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
|
@ -450,7 +450,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
|
||||
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
codeBrowser.goToField(addr("0x1007008"), "Address", 0, 0);
|
||||
assertEquals("01007008", codeBrowser.getCurrentFieldText());
|
||||
codeBrowser.goToField(addr("0x1007010"), "Address", 0, 0);
|
||||
|
@ -458,7 +458,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
|
||||
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
codeBrowser.goToField(addr("0x1007000"), "Address", 0, 0);
|
||||
codeBrowser.goToField(addr("0x1007008"), "Address", 0, 0);
|
||||
assertEquals("01007000", codeBrowser.getCurrentFieldText());
|
||||
|
@ -477,14 +477,14 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
|
||||
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
codeBrowser.goToField(addr("0x1007023"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
codeBrowser.goToField(addr("0x100702a"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
waitForSwing();
|
||||
|
||||
codeBrowser.goToField(addr("0x100702b"), "Address", 0, 0);
|
||||
assertEquals("0100702b", codeBrowser.getCurrentFieldText());
|
||||
|
@ -525,6 +525,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
goTo(codeBrowser, addr("0x1007000"));
|
||||
|
||||
for (int i = 0; i < 300; i++) {
|
||||
|
||||
cursorRight(fp);
|
||||
|
||||
if (!codeBrowser.getCurrentFieldText().equals(cb2.getCurrentFieldText())) {
|
||||
|
@ -533,11 +534,11 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
assertEquals(codeBrowser.getCurrentFieldText(), cb2.getCurrentFieldText());
|
||||
|
||||
if (!codeBrowser.getCurrentFieldLoction().equals(cb2.getCurrentFieldLoction())) {
|
||||
System.err.println("location no equal at cursor move: " + i);
|
||||
}
|
||||
assertEquals(codeBrowser.getCurrentFieldLoction(), cb2.getCurrentFieldLoction());
|
||||
System.err.println("location not equal at cursor move: " + i);
|
||||
}
|
||||
|
||||
assertEquals(codeBrowser.getCurrentFieldLoction(), cb2.getCurrentFieldLoction());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -561,13 +562,13 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
|
||||
codeBrowser.goToField(addr("0x1007000"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
codeBrowser.goToField(addr("0x1007023"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
codeBrowser.goToField(addr("0x100702a"), "+", 0, 0);
|
||||
click(codeBrowser, 1);
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
|
||||
// this should open the other structure
|
||||
codeBrowser.goToField(addr("0x1007023"), "Address", 0, 0);
|
||||
|
@ -809,7 +810,7 @@ public class CodeBrowserScreenMovementTest extends AbstractProgramBasedTest {
|
|||
fieldOptions.setInt(name, 0);
|
||||
}
|
||||
}
|
||||
waitForPostedSwingRunnables();
|
||||
waitForSwing();
|
||||
plugin.updateNow();
|
||||
}
|
||||
|
||||
|
|
|
@ -419,6 +419,8 @@ public class SearchTextPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
loc = cbPlugin.getCurrentLocation();
|
||||
assertEquals(getAddr(0x01004192), loc.getAddress());
|
||||
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());
|
||||
|
||||
pressButton(searchButton);
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -57,10 +56,6 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private Program program;
|
||||
private GoToService goToService;
|
||||
|
||||
public PlateFieldFactoryTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
|
@ -151,7 +146,8 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
cu.setCommentAsArray(CodeUnit.PLATE_COMMENT,
|
||||
new String[] { "this is", "a plate comment" });
|
||||
// create a reference to addr
|
||||
program.getReferenceManager().addMemoryReference(getAddr(0x010023ee), addr,
|
||||
program.getReferenceManager()
|
||||
.addMemoryReference(getAddr(0x010023ee), addr,
|
||||
RefType.DATA, SourceType.USER_DEFINED, 0);
|
||||
}
|
||||
finally {
|
||||
|
@ -189,7 +185,8 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
CodeUnit cu = program.getListing().getCodeUnitAt(addr);
|
||||
cu.setCommentAsArray(CodeUnit.PLATE_COMMENT, new String[] { originalText });
|
||||
// create a reference to addr
|
||||
program.getReferenceManager().addMemoryReference(getAddr(0x010023ee), addr,
|
||||
program.getReferenceManager()
|
||||
.addMemoryReference(getAddr(0x010023ee), addr,
|
||||
RefType.DATA, SourceType.USER_DEFINED, 0);
|
||||
}
|
||||
finally {
|
||||
|
@ -247,7 +244,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testShowTransitionPlates() throws Exception {
|
||||
|
||||
// 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);
|
||||
|
||||
|
@ -282,7 +279,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testShowSubroutinePlates() throws Exception {
|
||||
|
||||
// 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);
|
||||
|
||||
|
@ -297,28 +294,13 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
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);
|
||||
|
||||
assertTrue(cb.goToField(getAddr(0x1001300), PlateFieldFactory.FIELD_NAME, 1, 1));
|
||||
ListingTextField tf = (ListingTextField) cb.getCurrentField();
|
||||
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
|
||||
|
@ -354,7 +336,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
Listing listing = program.getListing();
|
||||
|
||||
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");
|
||||
|
||||
|
@ -465,7 +447,8 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
int transactionID = program.startTransaction("test");
|
||||
CodeUnit cu = program.getListing().getCodeUnitAt(addr);
|
||||
try {
|
||||
program.getSymbolTable().createLabel(addr, testName.getMethodName(),
|
||||
program.getSymbolTable()
|
||||
.createLabel(addr, testName.getMethodName(),
|
||||
SourceType.USER_DEFINED);
|
||||
cu.setComment(CodeUnit.PLATE_COMMENT,
|
||||
"this is a comment\ngo to the address 0x010028de");
|
||||
|
|
|
@ -39,6 +39,7 @@ public class ClippingTextField implements TextField {
|
|||
protected int startX;
|
||||
private int width;
|
||||
private int preferredWidth;
|
||||
private int numDataRows;
|
||||
|
||||
private String fullText;
|
||||
private boolean isClipped;
|
||||
|
@ -49,28 +50,40 @@ public class ClippingTextField implements TextField {
|
|||
|
||||
/**
|
||||
* 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>
|
||||
* this(startX, width, new AttributedString[] { textElement }, hlFactory, true);
|
||||
* </pre>
|
||||
*
|
||||
* @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.
|
||||
* @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,
|
||||
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.width = width;
|
||||
this.numDataRows = numDataRows;
|
||||
this.textElement = textElement;
|
||||
this.hlFactory = hlFactory;
|
||||
this.preferredWidth = textElement.getStringWidth();
|
||||
|
||||
clip(width);
|
||||
|
@ -139,7 +152,7 @@ public class ClippingTextField implements TextField {
|
|||
|
||||
@Override
|
||||
public int getNumDataRows() {
|
||||
return 1;
|
||||
return numDataRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -336,19 +349,14 @@ public class ClippingTextField implements TextField {
|
|||
@Override
|
||||
public RowColLocation screenToDataLocation(int screenRow, int screenColumn) {
|
||||
return originalElement.getDataLocationForCharacterIndex(screenColumn);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowColLocation dataToScreenLocation(int dataRow, int dataColumn) {
|
||||
int column = textElement.getCharacterIndexForDataLocation(dataRow, dataColumn);
|
||||
if (column < 0) {
|
||||
// place at the end if past the end
|
||||
if (dataColumn >= textElement.length()) {
|
||||
return new DefaultRowColLocation(0, textElement.length());
|
||||
}
|
||||
return new DefaultRowColLocation();
|
||||
}
|
||||
return new RowColLocation(0, column);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
|||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import generic.json.Json;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// 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
|
||||
public FieldElement replaceAll(char[] targets, char repacement) {
|
||||
FieldElement[] newStrings = new FieldElement[fieldElements.length];
|
||||
|
@ -191,16 +152,6 @@ public class CompositeFieldElement implements FieldElement {
|
|||
return new CompositeFieldElement(newStrings);
|
||||
}
|
||||
|
||||
private static class IndexedOffset {
|
||||
int index;
|
||||
int offset;
|
||||
|
||||
IndexedOffset(int index, int offset) {
|
||||
this.index = index;
|
||||
this.offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldElement getFieldElement(int column) {
|
||||
IndexedOffset startPos = getIndexedOffsetForCharPosition(column);
|
||||
|
@ -217,6 +168,46 @@ public class CompositeFieldElement implements FieldElement {
|
|||
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
|
||||
//==================================================================================================
|
||||
|
@ -229,6 +220,7 @@ public class CompositeFieldElement implements FieldElement {
|
|||
|
||||
@Override
|
||||
public int getCharacterIndexForDataLocation(int dataRow, int dataColumn) {
|
||||
|
||||
int columnsSoFar = 0;
|
||||
for (int i = fieldElements.length - 1; i >= 0; i--) {
|
||||
columnsSoFar += fieldElements[i].length();
|
||||
|
@ -242,4 +234,20 @@ public class CompositeFieldElement implements FieldElement {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -342,12 +342,15 @@ public class CompositeVerticalLayoutTextField implements TextField {
|
|||
}
|
||||
|
||||
private FieldRow getFieldRowFromDataRow(int dataRow) {
|
||||
|
||||
int currentRow = 0;
|
||||
for (FieldRow row : fieldRows) {
|
||||
if (currentRow >= dataRow) {
|
||||
int length = row.field.getNumDataRows();
|
||||
|
||||
if (currentRow + length > dataRow) {
|
||||
return row;
|
||||
}
|
||||
currentRow += row.field.getNumDataRows();
|
||||
currentRow += length;
|
||||
}
|
||||
return fieldRows.get(fieldRows.size() - 1);
|
||||
}
|
||||
|
|
|
@ -20,8 +20,9 @@ import java.util.*;
|
|||
import docking.widgets.fieldpanel.support.HighlightFactory;
|
||||
|
||||
/**
|
||||
* This class provides a TextField implementation that takes multiple AttributedStrings and places
|
||||
* as many that will fit on a line without clipping before continuing to the next line.
|
||||
* This class provides a TextField implementation that takes multiple AttributedString field
|
||||
* elements and places as many that will fit on a line without clipping before continuing to the
|
||||
* next line.
|
||||
*/
|
||||
public class FlowLayoutTextField extends VerticalLayoutTextField {
|
||||
|
||||
|
@ -67,27 +68,35 @@ public class FlowLayoutTextField extends VerticalLayoutTextField {
|
|||
int currentIndex = 0;
|
||||
while (currentIndex < elements.size()) {
|
||||
int numberPerLine = getNumberOfElementsPerLine(elements, currentIndex, width);
|
||||
subFields.add(createLine(elements, currentIndex, numberPerLine));
|
||||
subFields.add(createLineFromElements(elements, currentIndex, numberPerLine));
|
||||
currentIndex += numberPerLine;
|
||||
}
|
||||
|
||||
return subFields;
|
||||
}
|
||||
|
||||
private static CompositeFieldElement createLine(List<FieldElement> elements, int from,
|
||||
int length) {
|
||||
return new CompositeFieldElement(elements.subList(from, from + length));
|
||||
@Override
|
||||
protected TextField createFieldForLine(FieldElement element) {
|
||||
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,
|
||||
int width) {
|
||||
|
||||
int currentWidth = 0;
|
||||
int count = 0;
|
||||
for (FieldElement element : elements) {
|
||||
for (int i = start; i < elements.size(); i++) {
|
||||
FieldElement element = elements.get(i);
|
||||
currentWidth += element.getStringWidth();
|
||||
count++;
|
||||
if (currentWidth > width) {
|
||||
return Math.max(count - 1, 1);
|
||||
int count = i - start;
|
||||
return Math.max(count, 1);
|
||||
}
|
||||
}
|
||||
return elements.size() - start;
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
package docking.widgets.fieldpanel.field;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
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.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import generic.json.Json;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
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 width;
|
||||
protected int preferredWidth;
|
||||
|
@ -136,16 +141,16 @@ public class VerticalLayoutTextField implements TextField {
|
|||
}
|
||||
|
||||
protected void calculateHeight() {
|
||||
heightAbove = (subFields.get(0)).getHeightAbove();
|
||||
for (Field field : subFields) {
|
||||
height += field.getHeight();
|
||||
heightAbove = (subFields.get(0)).field.getHeightAbove();
|
||||
for (FieldRow fieldRow : subFields) {
|
||||
height += fieldRow.field.getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
private int calculatePreferredWidth() {
|
||||
int widest = 0;
|
||||
for (Field field : subFields) {
|
||||
widest = Math.max(widest, field.getPreferredWidth());
|
||||
for (FieldRow fieldRow : subFields) {
|
||||
widest = Math.max(widest, fieldRow.field.getPreferredWidth());
|
||||
}
|
||||
return widest;
|
||||
}
|
||||
|
@ -197,20 +202,21 @@ public class VerticalLayoutTextField implements TextField {
|
|||
|
||||
@Override
|
||||
public int getNumCols(int row) {
|
||||
Field f = subFields.get(row);
|
||||
Field f = getField(row);
|
||||
return f.getNumCols(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRow(int y) {
|
||||
if (y < 0) {
|
||||
if (y < -heightAbove) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int heightSoFar = 0;
|
||||
int heightSoFar = -heightAbove;
|
||||
|
||||
int n = subFields.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Field f = subFields.get(i);
|
||||
Field f = getField(i);
|
||||
heightSoFar += f.getHeight();
|
||||
if (heightSoFar > y) {
|
||||
return i;
|
||||
|
@ -221,7 +227,7 @@ public class VerticalLayoutTextField implements TextField {
|
|||
|
||||
@Override
|
||||
public int getCol(int row, int x) {
|
||||
Field f = subFields.get(row);
|
||||
Field f = getField(row);
|
||||
return f.getCol(0, x);
|
||||
}
|
||||
|
||||
|
@ -230,7 +236,7 @@ public class VerticalLayoutTextField implements TextField {
|
|||
|
||||
int y = -heightAbove;
|
||||
for (int i = 0; i < row; i++) {
|
||||
Field f = subFields.get(row);
|
||||
Field f = getField(i);
|
||||
y += f.getHeight();
|
||||
}
|
||||
return y;
|
||||
|
@ -238,7 +244,7 @@ public class VerticalLayoutTextField implements TextField {
|
|||
|
||||
@Override
|
||||
public int getX(int row, int col) {
|
||||
Field f = subFields.get(row);
|
||||
Field f = getField(row);
|
||||
return f.getX(0, col);
|
||||
}
|
||||
|
||||
|
@ -248,7 +254,7 @@ public class VerticalLayoutTextField implements TextField {
|
|||
if ((row < 0) || (row >= subFields.size())) {
|
||||
return false;
|
||||
}
|
||||
Field f = subFields.get(row);
|
||||
Field f = getField(row);
|
||||
return f.isValid(0, col);
|
||||
}
|
||||
|
||||
|
@ -295,7 +301,7 @@ public class VerticalLayoutTextField implements TextField {
|
|||
int translatedY = 0;
|
||||
int extraSpace = rowSeparator.length();
|
||||
for (int i = 0; i < n; i++) {
|
||||
ClippingTextField subField = (ClippingTextField) subFields.get(i);
|
||||
ClippingTextField subField = (ClippingTextField) getField(i);
|
||||
int subFieldHeight = subField.getHeight();
|
||||
int endY = startY + subFieldHeight;
|
||||
|
||||
|
@ -332,7 +338,7 @@ public class VerticalLayoutTextField implements TextField {
|
|||
private void print(Graphics g, PaintContext context) {
|
||||
int n = subFields.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
ClippingTextField clippingField = (ClippingTextField) subFields.get(i);
|
||||
ClippingTextField clippingField = (ClippingTextField) getField(i);
|
||||
|
||||
clippingField.print(g, context);
|
||||
|
||||
|
@ -346,10 +352,10 @@ public class VerticalLayoutTextField implements TextField {
|
|||
if ((row < 0) || (row >= subFields.size())) {
|
||||
return null;
|
||||
}
|
||||
Field f = subFields.get(row);
|
||||
Field f = getField(row);
|
||||
Rectangle r = f.getCursorBounds(0, col);
|
||||
for (int i = 0; i < row; i++) {
|
||||
f = subFields.get(row);
|
||||
f = getField(i);
|
||||
r.y += f.getHeight();
|
||||
}
|
||||
return r;
|
||||
|
@ -370,10 +376,11 @@ public class VerticalLayoutTextField implements TextField {
|
|||
if ((topOfScreen < -heightAbove) || (topOfScreen > height - heightAbove)) {
|
||||
return max;
|
||||
}
|
||||
|
||||
int row = getRow(topOfScreen);
|
||||
int y = getY(row);
|
||||
int rowOffset = topOfScreen - y;
|
||||
int rowHeight = (subFields.get(row)).getHeight();
|
||||
int rowHeight = getField(row).getHeight();
|
||||
if (direction > 0) { // if scrolling down
|
||||
return rowHeight - rowOffset;
|
||||
}
|
||||
|
@ -399,14 +406,6 @@ public class VerticalLayoutTextField implements TextField {
|
|||
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
|
||||
public int getHeightAbove() {
|
||||
return heightAbove;
|
||||
|
@ -425,17 +424,17 @@ public class VerticalLayoutTextField implements TextField {
|
|||
@Override
|
||||
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
|
||||
return f.getFieldElement(fieldRow, screenColumn);
|
||||
}
|
||||
|
||||
protected List<TextField> layoutElements(List<FieldElement> textElements, int maxLines) {
|
||||
List<TextField> newSubFields = new ArrayList<>();
|
||||
protected List<FieldRow> layoutElements(List<FieldElement> textElements, int maxLines) {
|
||||
List<FieldRow> newSubFields = new ArrayList<>();
|
||||
|
||||
boolean tooManyLines = textElements.size() > maxLines;
|
||||
|
||||
int currentRow = 0;
|
||||
for (int i = 0; i < textElements.size() && i < maxLines; i++) {
|
||||
FieldElement element = textElements.get(i);
|
||||
if (tooManyLines && (i == maxLines - 1)) {
|
||||
|
@ -444,9 +443,13 @@ public class VerticalLayoutTextField implements TextField {
|
|||
elements[1] = new StrutFieldElement(500);
|
||||
element = new CompositeFieldElement(elements);
|
||||
}
|
||||
TextField field = new ClippingTextField(startX, width, element, hlFactory);
|
||||
newSubFields.add(field);
|
||||
TextField field = createFieldForLine(element);
|
||||
int modelRow = currentRow;
|
||||
int screenRow = newSubFields.size();
|
||||
newSubFields.add(new FieldRow(field, modelRow, screenRow));
|
||||
isClipped |= field.isClipped();
|
||||
|
||||
currentRow += field.getNumRows();
|
||||
}
|
||||
|
||||
isClipped |= tooManyLines;
|
||||
|
@ -455,38 +458,36 @@ public class VerticalLayoutTextField implements TextField {
|
|||
}
|
||||
|
||||
/**
|
||||
* Translates the row and column to a String index and character offset into
|
||||
* that string.
|
||||
* @param screenRow the row containing the location.
|
||||
* @param screenColumn the character position in the row of the location
|
||||
* @return a MultiStringLocation containing the string index and position
|
||||
* within that string.
|
||||
* Create the text field for given field element
|
||||
* @param element the element
|
||||
* @return the field
|
||||
*/
|
||||
protected TextField createFieldForLine(FieldElement element) {
|
||||
return new ClippingTextField(startX, width, element, hlFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowColLocation screenToDataLocation(int screenRow, int screenColumn) {
|
||||
|
||||
screenRow = Math.min(screenRow, subFields.size() - 1);
|
||||
screenRow = Math.max(screenRow, 0);
|
||||
|
||||
TextField field = subFields.get(screenRow);
|
||||
TextField field = getField(screenRow);
|
||||
screenColumn = Math.min(screenColumn, field.getText().length());
|
||||
screenColumn = Math.max(screenColumn, 0);
|
||||
|
||||
int fieldRow = 0; // each field is on a single row
|
||||
return field.screenToDataLocation(fieldRow, screenColumn);
|
||||
int dataRow = getDataRow(field);
|
||||
return field.screenToDataLocation(dataRow, screenColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowColLocation dataToScreenLocation(int dataRow, int dataColumn) {
|
||||
|
||||
if (dataRow >= getNumRows()) {
|
||||
TextField lastField = subFields.get(subFields.size());
|
||||
return new DefaultRowColLocation(lastField.getText().length(), subFields.size() - 1);
|
||||
}
|
||||
|
||||
TextField field = subFields.get(dataRow);
|
||||
FieldRow fieldRow = getFieldRowFromDataRow(dataRow);
|
||||
TextField field = fieldRow.field;
|
||||
RowColLocation location = field.dataToScreenLocation(dataRow, dataColumn);
|
||||
return location.withRow(dataRow);
|
||||
int screenRow = fieldRow.screenRow;
|
||||
return location.withRow(screenRow);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -517,7 +518,8 @@ public class VerticalLayoutTextField implements TextField {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -525,4 +527,54 @@ public class VerticalLayoutTextField implements TextField {
|
|||
public boolean 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,22 @@ public class FlowLayoutTextFieldTest extends AbstractGenericTest {
|
|||
FontMetrics fm = tk.getFontMetrics(font);
|
||||
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("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, 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, 4), textField.dataToScreenLocation(3, 4));
|
||||
|
||||
assertEquals(new RowColLocation(0, 0), textField.dataToScreenLocation(0, 12));
|
||||
assertEquals(new RowColLocation(0, 0), textField.dataToScreenLocation(0, 75));
|
||||
assertEquals(new DefaultRowColLocation(0, 12), textField.dataToScreenLocation(0, 12));
|
||||
assertEquals(new DefaultRowColLocation(0, 12), textField.dataToScreenLocation(0, 75));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRowColumn() {
|
||||
public void testTextOffsetToScreenLocation() {
|
||||
assertEquals(new RowColLocation(0, 0), textField.textOffsetToScreenLocation(0));
|
||||
assertEquals(new RowColLocation(0, 5), textField.textOffsetToScreenLocation(5));
|
||||
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, 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -324,7 +324,7 @@ public class CompositeVerticalLayoutTextFieldTest extends AbstractGenericTest {
|
|||
assertRowCol(1, 5, field.dataToScreenLocation(1, 5));
|
||||
|
||||
// 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(20, 50));
|
||||
}
|
||||
|
|
|
@ -435,7 +435,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
String operandPrefix = "dword ptr [EBP + ";
|
||||
String operandReferenceName = "destStr]";
|
||||
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);
|
||||
|
||||
DockingAction pasteAction = getAction(codeBrowserClipboardProvider, PASTE_ACTION_NAME);
|
||||
|
|
Loading…
Reference in a new issue