diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java index 6fa1209f69..d39226f30d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java @@ -17,7 +17,6 @@ package ghidra.app.plugin.core.calltree; import java.awt.*; import java.awt.event.*; -import java.awt.geom.Rectangle2D; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -27,7 +26,7 @@ import javax.swing.tree.TreePath; import docking.ActionContext; import docking.WindowPosition; import docking.action.*; -import docking.util.GraphicsUtils; +import docking.resources.icons.NumberIcon; import docking.widgets.dialogs.NumberInputDialog; import docking.widgets.label.GLabel; import docking.widgets.tree.*; @@ -1283,97 +1282,6 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain } } - private class NumberIcon implements Icon { - private String number; - private float bestFontSize = -1; - - NumberIcon(int number) { - this.number = Integer.toString(number); - } - - void setNumber(int number) { - this.number = Integer.toString(number); - bestFontSize = -1; - } - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - g.setColor(Color.WHITE); - g.fillRect(x, y, getIconWidth(), getIconHeight()); - g.setColor(new Color(0xb5d5ff)); - g.drawRect(x, y, getIconWidth(), getIconHeight()); - - float fontSize = getMaxFontSize(g, getIconWidth() - 1, getIconHeight()); - Font originalFont = g.getFont(); - Font textFont = originalFont.deriveFont(fontSize).deriveFont(Font.BOLD); - g.setFont(textFont); - - FontMetrics fontMetrics = g.getFontMetrics(textFont); - Rectangle2D stringBounds = fontMetrics.getStringBounds(number, g); - int textHeight = (int) stringBounds.getHeight(); - int iconHeight = getIconHeight(); - int space = y + iconHeight - textHeight; - int halfSpace = space >> 1; - int baselineY = y + iconHeight - halfSpace;// - halfTextHeight;// + halfTextHeight; - - int textWidth = (int) stringBounds.getWidth(); - int iconWidth = getIconWidth(); - int halfWidth = iconWidth >> 1; - int halfTextWidth = textWidth >> 1; - int baselineX = x + halfWidth - halfTextWidth; - - g.setColor(Color.BLACK); - JComponent jc = null; - if (c instanceof JComponent) { - jc = (JComponent) c; - } - GraphicsUtils.drawString(jc, g, number, baselineX, baselineY); - } - - private float getMaxFontSize(Graphics g, int width, int height) { - if (bestFontSize > 0) { - return bestFontSize; - } - - float size = 12f; - Font font = g.getFont().deriveFont(size); // reasonable default - if (textFitsInFont(g, font, width, height)) { - bestFontSize = size; - return bestFontSize; - } - - do { - size--; - font = g.getFont().deriveFont(size); - } - while (!textFitsInFont(g, font, width, height)); - - bestFontSize = Math.max(1f, size); - return bestFontSize; - } - - private boolean textFitsInFont(Graphics g, Font font, int width, int height) { - FontMetrics fontMetrics = g.getFontMetrics(font); - int textWidth = fontMetrics.stringWidth(number); - if (textWidth > width) { - return false; - } - - int textHeight = fontMetrics.getHeight(); - return textHeight < height; - } - - @Override - public int getIconHeight() { - return 16; - } - - @Override - public int getIconWidth() { - return 16; - } - } - private class PendingRootNode extends GTreeNode { @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/resources/icons/NumberIcon.java b/Ghidra/Framework/Docking/src/main/java/docking/resources/icons/NumberIcon.java new file mode 100644 index 0000000000..d219d6e16a --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/resources/icons/NumberIcon.java @@ -0,0 +1,122 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking.resources.icons; + +import java.awt.*; +import java.awt.geom.Rectangle2D; + +import javax.swing.Icon; +import javax.swing.JComponent; + +import docking.util.GraphicsUtils; + +/** + * An icon that paints the given number + */ +public class NumberIcon implements Icon { + + private String number; + private float bestFontSize = -1; + + public NumberIcon(int number) { + this.number = Integer.toString(number); + } + + public void setNumber(int number) { + this.number = Integer.toString(number); + bestFontSize = -1; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + g.setColor(Color.WHITE); + g.fillRect(x, y, getIconWidth(), getIconHeight()); + g.setColor(new Color(0xb5d5ff)); + g.drawRect(x, y, getIconWidth(), getIconHeight()); + + float fontSize = getMaxFontSize(g, getIconWidth() - 1, getIconHeight()); + Font originalFont = g.getFont(); + Font textFont = originalFont.deriveFont(fontSize).deriveFont(Font.BOLD); + g.setFont(textFont); + + FontMetrics fontMetrics = g.getFontMetrics(textFont); + Rectangle2D stringBounds = fontMetrics.getStringBounds(number, g); + int textHeight = (int) stringBounds.getHeight(); + int iconHeight = getIconHeight(); + int space = y + iconHeight - textHeight; + int halfSpace = space >> 1; + int baselineY = y + iconHeight - halfSpace;// - halfTextHeight;// + halfTextHeight; + + int textWidth = (int) stringBounds.getWidth(); + int iconWidth = getIconWidth(); + int halfWidth = iconWidth >> 1; + int halfTextWidth = textWidth >> 1; + int baselineX = x + (halfWidth - halfTextWidth); + + g.setColor(Color.BLACK); + JComponent jc = null; + if (c instanceof JComponent) { + jc = (JComponent) c; + } + GraphicsUtils.drawString(jc, g, number, baselineX, baselineY); + } + + private float getMaxFontSize(Graphics g, int width, int height) { + if (bestFontSize > 0) { + return bestFontSize; + } + + float size = 12f; + Font font = g.getFont().deriveFont(size); // reasonable default + if (textFitsInFont(g, font, width, height)) { + bestFontSize = size; + return bestFontSize; + } + + do { + size--; + font = g.getFont().deriveFont(size); + } + while (!textFitsInFont(g, font, width, height)); + + bestFontSize = Math.max(1f, size); + return bestFontSize; + } + + private boolean textFitsInFont(Graphics g, Font font, int width, int height) { + + // padding so the text does not touch the border + int padding = 2; + FontMetrics fontMetrics = g.getFontMetrics(font); + int textWidth = fontMetrics.stringWidth(number) + padding; + if (textWidth > width) { + return false; + } + + int textHeight = fontMetrics.getHeight(); + return textHeight < height; + } + + @Override + public int getIconHeight() { + return 16; + } + + @Override + public int getIconWidth() { + return 16; + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GFilterTable.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GFilterTable.java index 32414f6ff6..49c52d80fd 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GFilterTable.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GFilterTable.java @@ -70,7 +70,7 @@ public class GFilterTable extends JPanel { private void addTableSelectionListener(GTable gTable) { gTable.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { - rowSelected(); + rowSelectionChanged(); } }); } @@ -116,6 +116,7 @@ public class GFilterTable extends JPanel { public void clearSelection() { table.clearSelection(); + table.getSelectionManager().clearSavedSelection(); } /** @@ -163,19 +164,31 @@ public class GFilterTable extends JPanel { listeners.remove(l); } - /** - * Notifies listeners that an item was selected. - */ - protected void rowSelected() { + private void rowSelectionChanged() { ROW_OBJECT selectedObject = null; if (table.getSelectedRow() >= 0) { selectedObject = getSelectedRowObject(); } if (selectedObject == null) { + rowSelectionCleared(); return; // can happen for transient events } + rowSelected(selectedObject); + } + + protected void rowSelectionCleared() { + for (ObjectSelectedListener l : listeners) { + l.objectSelected(null); + } + } + + /** + * Notifies listeners that an item was selected + * @param selectedObject the selected row object + */ + protected void rowSelected(ROW_OBJECT selectedObject) { for (ObjectSelectedListener l : listeners) { l.objectSelected(selectedObject); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/ObjectSelectedListener.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/ObjectSelectedListener.java index bb3cc63a5d..e2921d056c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/ObjectSelectedListener.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/ObjectSelectedListener.java @@ -15,6 +15,16 @@ */ package docking.widgets.table; +/** + * An interface for clients to know when an object is selected and when the selection is cleared + * + * @param the object type + */ public interface ObjectSelectedListener { + + /** + * When an object is select; null if the selection is cleared + * @param t the object selected or null + */ public void objectSelected(T t); } 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 3274cc6989..45206a0ead 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/HTMLUtilities.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/HTMLUtilities.java @@ -270,7 +270,7 @@ public class HTMLUtilities { * @param text the text to check * @return true if the text cannot be correctly broken into lines */ - private static boolean isUnbreakableHTML(String text) { + public static boolean isUnbreakableHTML(String text) { if (text.contains(HTML_SPACE) && !text.contains(" ")) { // this can happen if the client has called a method on this class that turns spaces // to the HTML_SPACE