diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java index 216a85bbd6..dae9bddd46 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java @@ -25,6 +25,7 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import docking.ActionContext; import docking.ComponentProvider; @@ -346,12 +347,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier return createStringTransferable(g.getBuffer().toString()); } catch (Exception e) { - String msg = e.getMessage(); - if (msg == null) { - msg = e.toString(); - } - - String message = "Copy failed: " + msg; + String message = "Copy failed: " + ExceptionUtils.getMessage(e); Msg.error(this, message, e); tool.setStatusInfo(message, true); } diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGClipboardProvider.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGClipboardProvider.java index f1f733145e..7df039ba46 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGClipboardProvider.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGClipboardProvider.java @@ -18,6 +18,8 @@ package ghidra.app.plugin.core.functiongraph; import java.awt.Rectangle; import java.awt.datatransfer.Transferable; +import org.apache.commons.lang3.exception.ExceptionUtils; + import docking.ActionContext; import docking.widgets.fieldpanel.Layout; import docking.widgets.fieldpanel.internal.EmptyLayoutBackgroundColorManager; @@ -32,6 +34,7 @@ import ghidra.app.plugin.core.functiongraph.mvc.FGData; import ghidra.app.util.viewer.listingpanel.ListingModel; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; +import ghidra.util.Msg; import ghidra.util.task.TaskMonitor; public class FGClipboardProvider extends CodeBrowserClipboardProvider { @@ -82,11 +85,9 @@ public class FGClipboardProvider extends CodeBrowserClipboardProvider { return createStringTransferable(g.getBuffer().toString()); } catch (Exception e) { - String msg = e.getMessage(); - if (msg == null) { - msg = e.toString(); - } - tool.setStatusInfo("Copy failed: " + msg, true); + String message = "Copy failed: " + ExceptionUtils.getMessage(e); + Msg.error(this, message, e); + tool.setStatusInfo(message, true); } return null; diff --git a/Ghidra/Framework/Generic/src/main/java/generic/text/TextLayoutGraphics.java b/Ghidra/Framework/Generic/src/main/java/generic/text/TextLayoutGraphics.java index 299b560126..f15a9f9d37 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/text/TextLayoutGraphics.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/text/TextLayoutGraphics.java @@ -26,28 +26,55 @@ import java.text.AttributedCharacterIterator; import java.util.*; import java.util.List; -import javax.swing.JPanel; - /** * Graphics used to render copied text data. This class is not a true graphics object, but is * instead used to grab text being painted so that clients can later use that text. */ public class TextLayoutGraphics extends Graphics2D { - private static final Component COMPONENT = new JPanel(); - private int transX; private int transY; private Shape clip; private StringBuilder buffer = new StringBuilder(); private Font lastFont = new Font("SansSerif", Font.PLAIN, 12); - private FontMetrics fontMetrics = getFontMetricsForFont(lastFont); + private FontMetrics fontMetrics = createFontMetrics(lastFont); private List textInfos = new ArrayList<>(); - @SuppressWarnings("deprecation") // Java still uses it, so we still use it - private static FontMetrics getFontMetricsForFont(Font font) { - return Toolkit.getDefaultToolkit().getFontMetrics(font); + private Comparator pointComparator = (o1, o2) -> { + TextInfo t1 = o1; + TextInfo t2 = o2; + + int diff = t1.point.y - t2.point.y; + if (diff != 0) { + return diff; + } + + diff = t1.point.x - t2.point.x; + + return diff; + }; + + private Comparator rowComparator = (o1, o2) -> { + TextInfo t1 = o1; + TextInfo t2 = o2; + + int diff = t1.row - t2.row; + if (diff != 0) { + return diff; + } + + diff = t1.point.x - t2.point.x; + + return diff; + }; + + private static FontMetrics createFontMetrics(Font font) { + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + FontMetrics fm = g.getFontMetrics(font); + g.dispose(); + return fm; } @Override @@ -71,13 +98,14 @@ public class TextLayoutGraphics extends Graphics2D { newTextInfo.point = new Point(x + transX, y + transY); newTextInfo.text = str; newTextInfo.font = lastFont; + newTextInfo.fontMetrics = fontMetrics; textInfos.add(newTextInfo); } @Override public void setFont(Font font) { lastFont = font; - fontMetrics = getFontMetrics(font); + fontMetrics = createFontMetrics(font); } /** @@ -89,101 +117,79 @@ public class TextLayoutGraphics extends Graphics2D { return; } - Comparator pointComparator = (o1, o2) -> { - TextInfo t1 = o1; - TextInfo t2 = o2; + sortDataAndAssignRows(); - int diff = t1.point.y - t2.point.y; - if (diff != 0) { - return diff; - } - - diff = t1.point.x - t2.point.x; - - return diff; - }; - - Comparator rowComparator = (o1, o2) -> { - TextInfo t1 = o1; - TextInfo t2 = o2; - - int diff = t1.row - t2.row; - if (diff != 0) { - return diff; - } - - diff = t1.point.x - t2.point.x; - - return diff; - }; - - TextInfo[] sortedTextInfos = new TextInfo[textInfos.size()]; - textInfos.toArray(sortedTextInfos); - - //Sort the text by y position, then by x position - Arrays.sort(sortedTextInfos, pointComparator); - - //Group the text into rows based on font height and y position - //TODO - Ideally, it would be nice if there was a good way to group text that - // varied in height in a nice way - int lastPos = sortedTextInfos[0].point.y; - int curRow = 0; - for (int i = 0; i < sortedTextInfos.length; i++) { - if (sortedTextInfos[i].point.y != lastPos) { - curRow++; - lastPos = sortedTextInfos[i].point.y; - } - - sortedTextInfos[i].row = curRow; - } - - //Sort the text by row, then by x position - Arrays.sort(sortedTextInfos, rowComparator); - - //Render the text into a string + // render the text into a string int lastRow = 0; - int lastXPos = 0; //The X co-ordinate of the end of the last string - for (TextInfo sortedTextInfo : sortedTextInfos) { - //Insert newlines as appropriate - for (int j = lastRow; j < sortedTextInfo.row; j++) { + int currentX = 0; //The x coordinate of the end of the last string + for (TextInfo info : textInfos) { + // insert newlines as appropriate + for (int i = lastRow; i < info.row; i++) { buffer.append('\n'); } - //If we started a new row, reset the X position - if (lastRow != sortedTextInfo.row) { - lastXPos = 0; + // if we started a new row, reset the x position + if (lastRow != info.row) { + currentX = 0; } - lastRow = sortedTextInfo.row; + lastRow = info.row; - //Insert spaces to account for distance past last field in row - FontMetrics metrics = COMPONENT.getFontMetrics(sortedTextInfo.font); + // insert spaces to account for distance past last field in row + FontMetrics metrics = info.fontMetrics; int spaceWidth = metrics.charWidth(' '); + if (spaceWidth == 0) { // some environments report 0 for some fonts spaceWidth = 4; } - int fillSpaces = - Math.round((float) (sortedTextInfo.point.x - lastXPos) / (float) spaceWidth); - //Account for the case where there's a very small amount of space between fields - if (fillSpaces == 0 && sortedTextInfo.point.x > lastXPos) { + float spaceBetween = info.point.x - currentX; + int fillSpaces = Math.round(spaceBetween / spaceWidth); + + // account for the case where there's a very small amount of space between fields + if (fillSpaces == 0 && info.point.x > currentX) { fillSpaces = 1; } - for (int j = 0; j < fillSpaces; j++) { + for (int i = 0; i < fillSpaces; i++) { buffer.append(' '); } - lastXPos = sortedTextInfo.point.x + metrics.stringWidth(sortedTextInfo.text); + int stringWidth = metrics.stringWidth(info.text); + currentX = info.point.x + stringWidth; - //Append the text - buffer.append(sortedTextInfo.text); + // append the text + buffer.append(info.text); } buffer.append('\n'); textInfos.clear(); } + private void sortDataAndAssignRows() { + + // sort the text by y position, then by x position + textInfos.sort(pointComparator); + + // Group the text into rows based on font height and y position + //TODO - Ideally, it would be nice if there was a good way to group text that + // varied in height in a nice way + int lastPos = textInfos.get(0).point.y; + int row = 0; + for (TextInfo info : textInfos) { + if (info.point.y != lastPos) { + row++; + lastPos = info.point.y; + } + + info.row = row; + } + + // sort the text by row, then by x position + textInfos.sort(rowComparator); + + } + public String getBuffer() { return buffer.toString(); } @@ -231,7 +237,7 @@ public class TextLayoutGraphics extends Graphics2D { //================================================================================================== // Stubs -//================================================================================================== +//================================================================================================== @Override public void dispose() { @@ -387,7 +393,7 @@ public class TextLayoutGraphics extends Graphics2D { @Override public void drawRenderableImage(RenderableImage img, AffineTransform xform) { - // stub + // stub } @Override @@ -419,7 +425,7 @@ public class TextLayoutGraphics extends Graphics2D { @Override public void setPaint(Paint paint) { - // stub + // stub } @Override @@ -440,7 +446,7 @@ public class TextLayoutGraphics extends Graphics2D { @Override public void setRenderingHints(Map hints) { - // stub + // stub } @Override @@ -456,22 +462,22 @@ public class TextLayoutGraphics extends Graphics2D { @Override public void translate(double tx, double ty) { - // stub + // stub } @Override public void rotate(double theta) { - // stub + // stub } @Override public void rotate(double theta, double x, double y) { - // stub + // stub } @Override public void scale(double sx, double sy) { - // stub + // stub } @Override @@ -486,7 +492,7 @@ public class TextLayoutGraphics extends Graphics2D { @Override public void setTransform(AffineTransform Tx) { - // stub + // stub } @Override @@ -538,11 +544,11 @@ public class TextLayoutGraphics extends Graphics2D { } class TextInfo { - public String text; - public Point point; - public Font font; - - public int row; + String text; + Font font; + FontMetrics fontMetrics; + Point point; + int row; @Override public String toString() { diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/HTMLUtilities.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/HTMLUtilities.java index 45206a0ead..f8733adf4d 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/HTMLUtilities.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/HTMLUtilities.java @@ -759,6 +759,7 @@ public class HTMLUtilities { // // Use the label's builtin handling of HTML text via the HTMLEditorKit // + Swing.assertSwingThread("This method must be called on the Swing thread"); JLabel label = new JLabel(text) { @Override public void paint(Graphics g) { diff --git a/Ghidra/Framework/Generic/src/test/java/ghidra/util/HTMLUtilitiesTest.java b/Ghidra/Framework/Generic/src/test/java/ghidra/util/HTMLUtilitiesTest.java index c95425fd1e..d9fda2ff81 100644 --- a/Ghidra/Framework/Generic/src/test/java/ghidra/util/HTMLUtilitiesTest.java +++ b/Ghidra/Framework/Generic/src/test/java/ghidra/util/HTMLUtilitiesTest.java @@ -15,8 +15,8 @@ */ package ghidra.util; -import static ghidra.util.HTMLUtilities.HTML; -import static org.junit.Assert.assertEquals; +import static ghidra.util.HTMLUtilities.*; +import static org.junit.Assert.*; import java.awt.Color; @@ -122,7 +122,7 @@ public class HTMLUtilitiesTest { @Test public void testFromHTML() { String s = "Bold, italics, sized font!"; - String text = HTMLUtilities.fromHTML(s); + String text = Swing.runNow(() -> HTMLUtilities.fromHTML(s)); assertEquals("Bold, italics, sized font!", text); }