mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
Merge remote-tracking branch
'origin/GP-3425-dragonmacher-field-word-wrapping--SQUASHED' (Closes #5299, #5298)
This commit is contained in:
commit
e6ca9675cc
6 changed files with 83 additions and 113 deletions
|
@ -47,7 +47,7 @@ public class EolCommentFieldFactory extends FieldFactory {
|
|||
private static final String GROUP_TITLE = "EOL Comments Field";
|
||||
private static final String SEMICOLON_PREFIX = "; ";
|
||||
public static final String ENABLE_WORD_WRAP_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Enable Word Wrapping";
|
||||
GROUP_TITLE + Options.DELIMITER + FieldUtils.WORD_WRAP_OPTION_NAME;
|
||||
public static final String MAX_DISPLAY_LINES_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Maximum Lines To Display";
|
||||
public static final String ENABLE_SHOW_SEMICOLON_MSG =
|
||||
|
@ -106,11 +106,7 @@ public class EolCommentFieldFactory extends FieldFactory {
|
|||
fieldOptions.registerOption(MAX_DISPLAY_LINES_MSG, 6, hl,
|
||||
"The maximum number of lines used to display the end-of-line comment.");
|
||||
fieldOptions.registerOption(ENABLE_WORD_WRAP_MSG, false, hl,
|
||||
"Enables word wrapping in the end-of-line comments field. If word " +
|
||||
"wrapping is on, user enter new lines are ignored and the entire comment is" +
|
||||
" displayed in paragraph form. If word wrapping is off, comments are " +
|
||||
"displayed in line format however the user entered them. Lines that are too " +
|
||||
"long for the field, are truncated.");
|
||||
FieldUtils.WORD_WRAP_OPTION_DESCRIPTION);
|
||||
|
||||
fieldOptions.registerOption(ENABLE_SHOW_SEMICOLON_MSG, false, hl,
|
||||
"Displays a semi-colon before each line in the end-of-line comment. " +
|
||||
|
|
|
@ -23,8 +23,7 @@ import java.util.List;
|
|||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.viewer.field.ListingColors.FunctionColors;
|
||||
|
@ -50,7 +49,7 @@ import ghidra.util.HelpLocation;
|
|||
abstract class OperandFieldHelper extends FieldFactory {
|
||||
|
||||
private final static String ENABLE_WORD_WRAP_MSG =
|
||||
GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Enable Word Wrapping";
|
||||
GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + FieldUtils.WORD_WRAP_OPTION_NAME;
|
||||
private final static String MAX_DISPLAY_LINES_MSG =
|
||||
GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Maximum Lines To Display";
|
||||
private final static String UNDERLINE_OPTION =
|
||||
|
|
|
@ -51,7 +51,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
public static final Color DEFAULT_COLOR = Palette.BLUE;
|
||||
private final static String FIELD_GROUP_TITLE = "Plate Comments Field";
|
||||
public final static String ENABLE_WORD_WRAP_MSG =
|
||||
FIELD_GROUP_TITLE + Options.DELIMITER + "Enable Word Wrapping";
|
||||
FIELD_GROUP_TITLE + Options.DELIMITER + FieldUtils.WORD_WRAP_OPTION_NAME;
|
||||
|
||||
/**
|
||||
* This is the length of the padding, which is a '*' and a space on each side
|
||||
|
@ -245,13 +245,15 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
for (String c : comments) {
|
||||
commentsList.add(CommentUtils.parseTextForAnnotations(c, p, prototype, row++));
|
||||
}
|
||||
|
||||
if (isWordWrap) {
|
||||
int charWidth = getMetrics().charWidth(' ');
|
||||
int spaceWidth = getMetrics().charWidth(' ');
|
||||
int starWidth = getMetrics().charWidth('*');
|
||||
int charWidth = Math.max(spaceWidth, starWidth);
|
||||
int paddingWidth = CONTENT_PADDING * charWidth;
|
||||
commentsList = FieldUtils.wordWrapList(
|
||||
new CompositeFieldElement(commentsList),
|
||||
Math.max(width - paddingWidth, charWidth));
|
||||
commentsList = FieldUtils.wrap(commentsList, Math.max(width - paddingWidth, charWidth));
|
||||
}
|
||||
|
||||
boolean isClipped = addSideBorders(commentsList);
|
||||
elements.addAll(commentsList);
|
||||
|
||||
|
@ -274,27 +276,36 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
|
||||
private FieldElementResult addSideBorder(FieldElement element, int row, boolean center) {
|
||||
|
||||
int ellipsisLength = 0;
|
||||
int availableWidth = stars.length() - CONTENT_PADDING;
|
||||
boolean isClipped = false;
|
||||
int ellipsisWidth = 0;
|
||||
String ellipsisText = EMPTY_STRING;
|
||||
if (element.length() > availableWidth) {
|
||||
|
||||
int spaceWidth = getMetrics().charWidth(' ');
|
||||
int starWidth = getMetrics().charWidth('*');
|
||||
int fullStarWidth = stars.length() * starWidth;
|
||||
int sideStarWidth = 2 * starWidth;
|
||||
int sideSpaceWidth = 2 * spaceWidth;
|
||||
int availableWidth = fullStarWidth - sideStarWidth - sideSpaceWidth;
|
||||
if (availableWidth < element.getStringWidth()) {
|
||||
// not enough room; clip the text and add ellipses
|
||||
isClipped = true;
|
||||
ellipsisText = ELLIPSIS;
|
||||
ellipsisLength = ELLIPSIS.length();
|
||||
availableWidth = stars.length() - CONTENT_PADDING - ellipsisLength;
|
||||
element = element.substring(0, availableWidth); // clip
|
||||
ellipsisWidth = getMetrics().charWidth('.') * ELLIPSIS.length();
|
||||
availableWidth -= ellipsisWidth;
|
||||
int charsThatFit = element.getMaxCharactersForWidth(availableWidth);
|
||||
element = element.substring(0, charsThatFit); // clip
|
||||
}
|
||||
|
||||
int charWidth = getMetrics().charWidth(' ');
|
||||
int paddingWidth = (CONTENT_PADDING + ellipsisLength) * charWidth;
|
||||
int textWidth = paddingWidth + element.getStringWidth();
|
||||
int totalPadding = (width - textWidth) / charWidth;
|
||||
int prePadding = center ? totalPadding / 2 : 0;
|
||||
int postPadding = center ? (totalPadding + 1) / 2 : totalPadding;
|
||||
int paddingWidth = sideStarWidth + sideSpaceWidth;
|
||||
int currentTextWidth = paddingWidth + element.getStringWidth() + ellipsisWidth;
|
||||
int biggestCharWidth = Math.max(starWidth, spaceWidth);
|
||||
int paddingCharsNeeded = (width - currentTextWidth) / biggestCharWidth;
|
||||
int prePaddingCharCount = center ? paddingCharsNeeded / 2 : 0;
|
||||
int postPaddingCharCount = center ? (paddingCharsNeeded + 1) / 2 : paddingCharsNeeded;
|
||||
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
buffy.append('*').append(' ');
|
||||
addPadding(buffy, prePadding);
|
||||
addPaddingSpaces(buffy, prePaddingCharCount);
|
||||
|
||||
FieldElement prefix = new TextFieldElement(
|
||||
new AttributedString(buffy.toString(), CommentColors.PLATE, getMetrics()), row, 0);
|
||||
|
@ -304,7 +315,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
prefix.length() + element.length());
|
||||
|
||||
buffy.setLength(0);
|
||||
addPadding(buffy, postPadding);
|
||||
addPaddingSpaces(buffy, postPaddingCharCount);
|
||||
buffy.append(' ').append('*');
|
||||
|
||||
FieldElement suffix = new TextFieldElement(
|
||||
|
@ -313,10 +324,10 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
|
||||
return new FieldElementResult(
|
||||
new CompositeFieldElement(new FieldElement[] { prefix, element, ellipsis, suffix }),
|
||||
ellipsisLength > 0);
|
||||
isClipped);
|
||||
}
|
||||
|
||||
private void addPadding(StringBuilder buf, int count) {
|
||||
private void addPaddingSpaces(StringBuilder buf, int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
buf.append(' ');
|
||||
}
|
||||
|
@ -688,12 +699,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
options.getOptions(GROUP_TITLE).setOptionsHelpLocation(help);
|
||||
|
||||
options.registerOption(ENABLE_WORD_WRAP_MSG, false, null,
|
||||
"Enables word wrapping in the pre-comments field. If word " +
|
||||
"wrapping is on, user enter new lines are ignored and " +
|
||||
"the entire comment is displayed in paragraph form. If word " +
|
||||
"wrapping is off, comments are displayed in line format " +
|
||||
"however the user entered them. Lines that are too long " +
|
||||
"for the field, are truncated.");
|
||||
FieldUtils.WORD_WRAP_OPTION_DESCRIPTION);
|
||||
|
||||
options.registerOption(SHOW_SUBROUTINE_PLATES_OPTION, true, help,
|
||||
"Toggle for whether a plate comment should be displayed for subroutines.");
|
||||
|
|
|
@ -52,7 +52,7 @@ public class PostCommentFieldFactory extends FieldFactory {
|
|||
private final static String GROUP_TITLE = "Format Code";
|
||||
private final static String FIELD_GROUP_TITLE = "Post-comments Field";
|
||||
public final static String ENABLE_WORD_WRAP_MSG =
|
||||
FIELD_GROUP_TITLE + Options.DELIMITER + "Enable Word Wrapping";
|
||||
FIELD_GROUP_TITLE + Options.DELIMITER + FieldUtils.WORD_WRAP_OPTION_NAME;
|
||||
public final static String ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG =
|
||||
FIELD_GROUP_TITLE + Options.DELIMITER + "Always Show the Automatic Comment";
|
||||
|
||||
|
@ -516,11 +516,7 @@ public class PostCommentFieldFactory extends FieldFactory {
|
|||
|
||||
private void init(Options options) {
|
||||
options.registerOption(ENABLE_WORD_WRAP_MSG, false, null,
|
||||
"Enables word wrapping in the pre-comments field. " +
|
||||
"If word wrapping is on, user enter" + " new lines are ignored and the entire " +
|
||||
"comment is displayed in paragraph form. " + " If word wrapping is off, comments" +
|
||||
" are displayed in line format however the user entered " +
|
||||
"them. Lines that are too long for the field, are truncated.");
|
||||
FieldUtils.WORD_WRAP_OPTION_DESCRIPTION);
|
||||
|
||||
options.registerOption(FLAG_FUNCTION_EXIT_OPTION, false, null,
|
||||
"Toggle for whether a post comment should be displayed " +
|
||||
|
|
|
@ -50,7 +50,7 @@ public class PreCommentFieldFactory extends FieldFactory {
|
|||
private final static String GROUP_TITLE = "Format Code";
|
||||
private final static String FIELD_GROUP_TITLE = "Pre-comments Field";
|
||||
public final static String ENABLE_WORD_WRAP_MSG =
|
||||
FIELD_GROUP_TITLE + Options.DELIMITER + "Enable Word Wrapping";
|
||||
FIELD_GROUP_TITLE + Options.DELIMITER + FieldUtils.WORD_WRAP_OPTION_NAME;
|
||||
public final static String ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG =
|
||||
FIELD_GROUP_TITLE + Options.DELIMITER + "Always Show the Automatic Comment";
|
||||
|
||||
|
@ -394,12 +394,7 @@ public class PreCommentFieldFactory extends FieldFactory {
|
|||
|
||||
private void init(Options options) {
|
||||
options.registerOption(ENABLE_WORD_WRAP_MSG, false, null,
|
||||
"Enables word wrapping in the pre-comments field. If word " +
|
||||
"wrapping is on, user enter new lines are ignored and the " +
|
||||
"entire comment is displayed in paragraph form. If word " +
|
||||
"wrapping is off, comments are displayed in line format " +
|
||||
"however the user entered them. Lines that are too long " +
|
||||
"for the field, are truncated.");
|
||||
FieldUtils.WORD_WRAP_OPTION_DESCRIPTION);
|
||||
options.registerOption(ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG, true, null,
|
||||
"Toggles the display of the automatic pre-comment");
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
package docking.widgets.fieldpanel.support;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.fieldpanel.field.FieldElement;
|
||||
|
||||
|
@ -24,7 +25,11 @@ import docking.widgets.fieldpanel.field.FieldElement;
|
|||
*/
|
||||
public class FieldUtils {
|
||||
|
||||
private static final char[] WHITE_SPACE = new char[] { '\t', '\n', '\r', '\f' };
|
||||
public static final String WORD_WRAP_OPTION_NAME = "Enable Word Wrapping";
|
||||
public static final String WORD_WRAP_OPTION_DESCRIPTION =
|
||||
"Enables word wrapping. When on, each line of text is wrapped as needed to fit within " +
|
||||
"the current width. When off, comments are displayed as entered by the user. Lines " +
|
||||
"that are too long for the field are truncated.";
|
||||
|
||||
private FieldUtils() { // utility class
|
||||
}
|
||||
|
@ -32,7 +37,7 @@ public class FieldUtils {
|
|||
public static List<FieldElement> wrap(List<FieldElement> fieldElements, int width) {
|
||||
List<FieldElement> wrappedElements = new ArrayList<>();
|
||||
for (FieldElement fieldElement : fieldElements) {
|
||||
wrappedElements.addAll(wordWrapList(fieldElement, width));
|
||||
wrappedElements.addAll(wrap(fieldElement, width));
|
||||
}
|
||||
return wrappedElements;
|
||||
}
|
||||
|
@ -45,23 +50,20 @@ public class FieldUtils {
|
|||
* @return The wrapped elements
|
||||
*/
|
||||
public static List<FieldElement> wrap(FieldElement fieldElement, int width) {
|
||||
|
||||
FieldElement originalFieldElement = fieldElement.replaceAll(WHITE_SPACE, ' ');
|
||||
if (originalFieldElement.getStringWidth() <= width) {
|
||||
return Arrays.asList(originalFieldElement);
|
||||
}
|
||||
|
||||
List<FieldElement> lines = new ArrayList<>();
|
||||
int wordWrapPos = findWordWrapPosition(originalFieldElement, width);
|
||||
while (wordWrapPos > 0) {
|
||||
lines.add(originalFieldElement.substring(0, wordWrapPos));
|
||||
if (originalFieldElement.charAt(wordWrapPos) == ' ') {
|
||||
wordWrapPos++; // skip white space char
|
||||
}
|
||||
originalFieldElement = originalFieldElement.substring(wordWrapPos);
|
||||
wordWrapPos = findWordWrapPosition(originalFieldElement, width);
|
||||
if (fieldElement.getStringWidth() <= width) {
|
||||
lines.add(fieldElement);
|
||||
return lines;
|
||||
}
|
||||
lines.add(originalFieldElement);
|
||||
|
||||
FieldElement element = fieldElement;
|
||||
int wordWrapPos = findWordWrapPosition(element, width);
|
||||
while (wordWrapPos > 0) {
|
||||
lines.add(element.substring(0, wordWrapPos));
|
||||
element = element.substring(wordWrapPos);
|
||||
wordWrapPos = findWordWrapPosition(element, width);
|
||||
}
|
||||
lines.add(element);
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
@ -76,57 +78,32 @@ public class FieldUtils {
|
|||
*/
|
||||
public static List<FieldElement> wrap(FieldElement fieldElement, int width,
|
||||
boolean breakOnWhiteSpace) {
|
||||
|
||||
if (breakOnWhiteSpace) {
|
||||
return wrap(fieldElement, width);
|
||||
}
|
||||
FieldElement originalFieldElement = fieldElement.replaceAll(WHITE_SPACE, ' ');
|
||||
if (originalFieldElement.getStringWidth() <= width) {
|
||||
return Arrays.asList(originalFieldElement);
|
||||
}
|
||||
|
||||
List<FieldElement> lines = new ArrayList<>();
|
||||
int wordWrapPos = originalFieldElement.getMaxCharactersForWidth(width);
|
||||
if (wordWrapPos == originalFieldElement.length()) {
|
||||
wordWrapPos = 0;
|
||||
}
|
||||
while (wordWrapPos > 0) {
|
||||
lines.add(originalFieldElement.substring(0, wordWrapPos));
|
||||
originalFieldElement = originalFieldElement.substring(wordWrapPos);
|
||||
wordWrapPos = originalFieldElement.getMaxCharactersForWidth(width);
|
||||
if (wordWrapPos == originalFieldElement.length()) {
|
||||
wordWrapPos = 0;
|
||||
}
|
||||
}
|
||||
lines.add(originalFieldElement);
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the given FieldElement into sub-elements by wrapping the element on whitespace.
|
||||
*
|
||||
* @param fieldElement The element to wrap
|
||||
* @param width The maximum width to allow before wrapping
|
||||
* @return The wrapped elements
|
||||
*/
|
||||
public static List<FieldElement> wordWrapList(FieldElement fieldElement, int width) {
|
||||
List<FieldElement> lines = new ArrayList<>();
|
||||
|
||||
FieldElement originalFieldElement = fieldElement.replaceAll(WHITE_SPACE, ' ');
|
||||
if (originalFieldElement.getStringWidth() <= width) {
|
||||
lines.add(originalFieldElement);
|
||||
if (fieldElement.getStringWidth() <= width) {
|
||||
lines.add(fieldElement);
|
||||
return lines;
|
||||
}
|
||||
|
||||
int wordWrapPos = findWordWrapPosition(originalFieldElement, width);
|
||||
while (wordWrapPos > 0) {
|
||||
lines.add(originalFieldElement.substring(0, wordWrapPos));
|
||||
if (originalFieldElement.charAt(wordWrapPos) == ' ') {
|
||||
wordWrapPos++; // skip white space char
|
||||
}
|
||||
originalFieldElement = originalFieldElement.substring(wordWrapPos);
|
||||
wordWrapPos = findWordWrapPosition(originalFieldElement, width);
|
||||
FieldElement element = fieldElement;
|
||||
int wordWrapPos = element.getMaxCharactersForWidth(width);
|
||||
if (wordWrapPos == element.length()) {
|
||||
wordWrapPos = 0;
|
||||
}
|
||||
lines.add(originalFieldElement);
|
||||
|
||||
while (wordWrapPos > 0) {
|
||||
lines.add(element.substring(0, wordWrapPos));
|
||||
element = element.substring(wordWrapPos);
|
||||
wordWrapPos = element.getMaxCharactersForWidth(width);
|
||||
if (wordWrapPos == element.length()) {
|
||||
wordWrapPos = 0;
|
||||
}
|
||||
}
|
||||
lines.add(element);
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
@ -134,13 +111,13 @@ public class FieldUtils {
|
|||
* Finds the position within the given element at which to split the line for word wrapping.
|
||||
* This method finds the last whitespace character that completely fits within the given width.
|
||||
* If there is no whitespace character before the width break point, it finds the first
|
||||
* whitespace character after the width. If no whitespace can be found, then the text will
|
||||
* be split at a non-whitespace character.
|
||||
* whitespace character after the width. If no whitespace can be found, then 0 will be returned
|
||||
* to signal that there is no spot to break the line.
|
||||
*
|
||||
* @param element the element to split
|
||||
* @param width the max width to allow before looking for a word wrap positions
|
||||
* @return 0 if the element cannot be split, else the character position of the string
|
||||
* to be split off.
|
||||
* to be split, exclusive
|
||||
*/
|
||||
private static int findWordWrapPosition(FieldElement element, int width) {
|
||||
|
||||
|
@ -150,9 +127,10 @@ public class FieldUtils {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// inclusive
|
||||
int whiteSpacePosition = text.lastIndexOf(" ", wrapPosition - 1);
|
||||
if (whiteSpacePosition >= 0) {
|
||||
return whiteSpacePosition;
|
||||
return whiteSpacePosition + 1; // exclusive
|
||||
}
|
||||
|
||||
return wrapPosition;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue