mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-4154 - Theming - Fixed font issues; updated font usage with attributes
This commit is contained in:
parent
c5bad0a88f
commit
b586d65a3b
91 changed files with 1309 additions and 1191 deletions
|
@ -133,10 +133,18 @@ icon.task.progress.hourglass.11 = hourglass24_11.png
|
|||
|
||||
// Fonts
|
||||
|
||||
font.splash.header.default = Serif-BOLD-35
|
||||
font.splash.status = Serif-BOLD-12
|
||||
font.table.header.number = arial-BOLD-12
|
||||
font.input.hint = monospaced-PLAIN-10
|
||||
font.splash.header.default = serif-bold-35
|
||||
font.splash.status = serif-bold-12
|
||||
|
||||
// default table renderer uses the JLabel font, which is mapped to system.font.control
|
||||
font.table.base = [font]system.font.control
|
||||
font.table.header.number = arial-bold-12
|
||||
|
||||
font.input.hint = monospaced-plain-10
|
||||
|
||||
font.task.monitor.label.message = sansserif-plain-10
|
||||
|
||||
font.wizard.border.title = sansserif-plain-10
|
||||
|
||||
|
||||
|
||||
|
@ -144,7 +152,6 @@ font.input.hint = monospaced-PLAIN-10
|
|||
[Dark Defaults]
|
||||
|
||||
|
||||
|
||||
color.fg.filterfield = color.palette.darkslategray
|
||||
|
||||
color.bg.highlight = #703401 // orangish
|
||||
|
|
|
@ -59,9 +59,9 @@
|
|||
<tocdef id="Root" sortgroup="a" text="Welcome to Help">
|
||||
<tocdef id="Theming" text="Theming" sortgroup="t" target="help/topics/Theming/ThemingOverview.html">
|
||||
<tocdef id="Overview" sortgroup="1" text="Overview" target="help/topics/Theming/ThemingOverview.html"/>
|
||||
<tocdef id="Editing Themes" sortgroup="2" text="Editing Themes" target="help/topics/Theming/ThemingUserDocs.html"/>
|
||||
<tocdef id="Architecture" sortgroup="3" text="Architecture" target="help/topics/Theming/ThemingInternals.html"/>
|
||||
<tocdef id="Developer Documentation" sortgroup="4" text="Developer's Guide" target="help/topics/Theming/ThemingDeveloperDocs.html"/>
|
||||
<tocdef id="Editing Themes" sortgroup="2" text="User's Guide" target="help/topics/Theming/ThemingUserDocs.html"/>
|
||||
<tocdef id="Developer Documentation" sortgroup="3" text="Developer's Guide" target="help/topics/Theming/ThemingDeveloperDocs.html"/>
|
||||
<tocdef id="Architecture" sortgroup="4" text="Architecture" target="help/topics/Theming/ThemingInternals.html"/>
|
||||
</tocdef>
|
||||
</tocdef>
|
||||
</tocroot>
|
||||
|
|
|
@ -13,6 +13,14 @@
|
|||
plugins, actions, scripts, etc., that use colors, fonts, or icons. By following these guidelines,
|
||||
developers can easily make use of Ghidra's theming capabilities.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG border="0" src="help/shared/tip.png" alt="Tip">Most classes referenced in this document
|
||||
live in the <CODE>generic.theme</CODE> package.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Theme Resource Types</H2>
|
||||
|
||||
<P>When developing application code for Ghidra such as plugins, actions, etc., developers often
|
||||
|
@ -122,8 +130,58 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<CODE>Gui.registerFont(myLabel, "font.xyz");</CODE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
<H3>Font Usage in Tables, Lists and Custom Painting</H3>
|
||||
<P>
|
||||
Ghidra makes great use of tables and to a lesser extent, lists. Both tables and lists
|
||||
use renderers to paint cell values.
|
||||
</P>
|
||||
<UL>
|
||||
<LI>
|
||||
Java - By default, Java will use the font of the table/list as the font used during rendering.
|
||||
</LI>
|
||||
<LI>
|
||||
Ghidra Tables - Ghidra does <U>not</U> use the table's font for rendering by default. Instead,
|
||||
the renderer is initialized with the fonts used by <CODE>JLabel</CODE>, with additional
|
||||
fonts for bold and monospaced text.
|
||||
</LI>
|
||||
<LI>
|
||||
Ghidra Lists - Ghidra does not currently use custom rendering for lists. Thus, list cell
|
||||
rendering will make use of the list's font, which is Java's default behavior.
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
<P>
|
||||
We point out this difference between Java and Ghidra here so that developers understand
|
||||
that changing fonts used for tables differs from Java. Specifically, calling
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<PRE>
|
||||
<CODE>
|
||||
table.setFont(newFont);
|
||||
</CODE>
|
||||
</PRE>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
<P>
|
||||
will not affect the font used when rendering the table. To programmatically change the
|
||||
fonts used for tables, you can set the font directly on each cell renderer. As with a
|
||||
any custom fonts used, be sure to define theme properties file and register then with the
|
||||
Gui class as outlined above.
|
||||
</P>
|
||||
<P>
|
||||
The fonts used for any painting operations, including table and list cell rendering, as well
|
||||
as any code that overrides <CODE>paint</CODE> will work correctly within the theming
|
||||
environment as long as the fonts are derived from the default Look and Feel values or are
|
||||
obtained from the <CODE>Gui</CODE> class. In other words, as long as the
|
||||
fonts used in custom painting are not hard-coded, any changes to the fonts via the theming
|
||||
API will appear on the next call to paint the UI.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
|
|
@ -26,7 +26,9 @@ import docking.action.DockingActionIf;
|
|||
import docking.action.KeyBindingData;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import generic.theme.GAttributes;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.Icons;
|
||||
|
||||
|
@ -43,7 +45,8 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
private KeyEntryTextField keyEntryField;
|
||||
private JTextPane collisionPane;
|
||||
private StyledDocument doc;
|
||||
private SimpleAttributeSet textAttrSet;
|
||||
|
||||
private SimpleAttributeSet textAttrs;
|
||||
private Color bgColor;
|
||||
|
||||
public KeyEntryDialog(Tool tool, DockingActionIf action) {
|
||||
|
@ -172,10 +175,8 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private void setUpAttributes() {
|
||||
textAttrSet = new SimpleAttributeSet();
|
||||
textAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
textAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
textAttrSet.addAttribute(StyleConstants.Foreground, Messages.NORMAL);
|
||||
Font font = Gui.getFont("font.standard");
|
||||
textAttrs = new GAttributes(font, Messages.NORMAL);
|
||||
}
|
||||
|
||||
private void updateCollisionPane(KeyStroke ks) {
|
||||
|
@ -194,7 +195,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
String ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||
String text = keyBindings.getActionsForKeyStrokeText(ksName);
|
||||
try {
|
||||
doc.insertString(0, text, textAttrSet);
|
||||
doc.insertString(0, text, textAttrs);
|
||||
collisionPane.setCaretPosition(0);
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.swing.*;
|
|||
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
|
@ -133,7 +134,7 @@ public class FontPropertyEditor extends PropertyEditorSupport {
|
|||
JPanel panel = new JPanel(new GridLayout(2, 1));
|
||||
|
||||
GDLabel styleLabel = new GDLabel("Styles");
|
||||
styleLabel.setFont(getFont().deriveFont(1));
|
||||
Gui.registerFont(styleLabel, Font.BOLD);
|
||||
styleLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
panel.add(styleLabel);
|
||||
|
||||
|
@ -150,7 +151,7 @@ public class FontPropertyEditor extends PropertyEditorSupport {
|
|||
JPanel panel = new JPanel(new GridLayout(2, 1));
|
||||
|
||||
GDLabel sizeLabel = new GDLabel("Sizes");
|
||||
sizeLabel.setFont(getFont().deriveFont(1));
|
||||
Gui.registerFont(sizeLabel, Font.BOLD);
|
||||
sizeLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
panel.add(sizeLabel);
|
||||
|
||||
|
@ -168,7 +169,7 @@ public class FontPropertyEditor extends PropertyEditorSupport {
|
|||
JPanel panel = new JPanel(new GridLayout(2, 1));
|
||||
|
||||
GDLabel fontLabel = new GDLabel("Fonts");
|
||||
fontLabel.setFont(getFont().deriveFont(1));
|
||||
Gui.registerFont(fontLabel, Font.BOLD);
|
||||
fontLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
panel.add(fontLabel);
|
||||
|
||||
|
|
|
@ -25,22 +25,32 @@ import javax.swing.plaf.basic.BasicHTML;
|
|||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GColorUIResource;
|
||||
import generic.theme.*;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import generic.theme.GThemeDefaults.Colors.Tables;
|
||||
import ghidra.util.Msg;
|
||||
import util.CollectionUtils;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
/**
|
||||
* A common base class for list and table renderer objects, unifying the Ghidra look and feel.
|
||||
* <p>
|
||||
* It allows (but default-disables) HTML content, automatically paints alternating row
|
||||
* background colors, and highlights the drop target in a drag-n-drop operation.
|
||||
*
|
||||
* It allows (but default-disables) HTML content, automatically paints alternating row background
|
||||
* colors, and highlights the drop target in a drag-n-drop operation.
|
||||
* <p>
|
||||
* The preferred method to change the font used by this renderer is {@link #setBaseFontId(String)}.
|
||||
* If you would like this renderer to use a monospaced font, then, as an alternative to creating a
|
||||
* font ID, you can instead override {@link #getDefaultFont()} to return this
|
||||
* class's {@link #fixedWidthFont}. Also, the fixed width font of this class is based on the
|
||||
* default font set when calling {@link #setBaseFontId(String)}, so it stays up-to-date with theme
|
||||
* changes.
|
||||
*/
|
||||
public abstract class AbstractGCellRenderer extends GDHtmlLabel {
|
||||
private static final Color BACKGROUND_COLOR = new GColor("color.bg.table.row");
|
||||
private static final Color ALT_BACKGROUND_COLOR = new GColor("color.bg.table.row.alt");
|
||||
|
||||
private static final String BASE_FONT_ID = "font.table.base";
|
||||
|
||||
/** Allows the user to disable alternating row colors on JLists and JTables */
|
||||
private static final String DISABLE_ALTERNATING_ROW_COLORS_PROPERTY =
|
||||
"disable.alternating.row.colors";
|
||||
|
@ -61,6 +71,9 @@ public abstract class AbstractGCellRenderer extends GDHtmlLabel {
|
|||
private boolean instanceAlternateRowColors = true;
|
||||
|
||||
public AbstractGCellRenderer() {
|
||||
|
||||
setBaseFontId(BASE_FONT_ID);
|
||||
|
||||
noFocusBorder = BorderFactory.createEmptyBorder(0, 5, 0, 5);
|
||||
Border innerBorder = BorderFactory.createEmptyBorder(0, 4, 0, 4);
|
||||
Border outerBorder = BorderFactory.createLineBorder(Palette.YELLOW, 1);
|
||||
|
@ -114,34 +127,48 @@ public abstract class AbstractGCellRenderer extends GDHtmlLabel {
|
|||
return getBackgroundColorForRow(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this renderer's theme font id. This will be used to load the base font and to create
|
||||
* the derived fonts, such as bold and fixed width.
|
||||
* @param fontId the font id
|
||||
* @see Gui#registerFont(Component, String)
|
||||
*/
|
||||
public void setBaseFontId(String fontId) {
|
||||
Font f = Gui.getFont(fontId);
|
||||
defaultFont = f;
|
||||
fixedWidthFont = new Font("monospaced", f.getStyle(), f.getSize());
|
||||
boldFont = f.deriveFont(Font.BOLD);
|
||||
|
||||
Gui.registerFont(this, fontId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFont(Font f) {
|
||||
super.setFont(f);
|
||||
defaultFont = f;
|
||||
fixedWidthFont = new Font("monospaced", defaultFont.getStyle(), defaultFont.getSize());
|
||||
boldFont = f.deriveFont(Font.BOLD);
|
||||
}
|
||||
|
||||
protected void superSetFont(Font font) {
|
||||
super.setFont(font);
|
||||
}
|
||||
//
|
||||
// Due to the nature of how setFont() is typically used (external client setup vs internal
|
||||
// rendering), we created setBaseFontId() to allow external clients to set the base font in
|
||||
// a way that is consistent with theming. Ignore any request to use one of our existing
|
||||
// fonts, as some clients may do that from the getTableCellRendererComponent() method.
|
||||
//
|
||||
if (defaultFont != null &&
|
||||
!CollectionUtils.isOneOf(f, defaultFont, fixedWidthFont, boldFont)) {
|
||||
|
||||
// sets the font of this renderer to be bold until the next time that
|
||||
// getTableCellRenderer() is called, as it resets the font to the default font on each pass
|
||||
protected void setBold() {
|
||||
super.setFont(boldFont);
|
||||
String caller =
|
||||
ReflectionUtilities.getClassNameOlderThan(getClass().getName(), "generic.theme");
|
||||
Msg.debug(this, "Calling setFont() on the renderer is discouraged. " +
|
||||
"To change the font, call setBaseFontId(). Called from " + caller);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the row where DnD would perform drop operation.
|
||||
* @param dropRow the drop row
|
||||
* Sets the font of this renderer to be bold until the next time that getTableCellRenderer() is
|
||||
* called, as it resets the font to the default font on each pass.
|
||||
* @see #getDefaultFont()
|
||||
*/
|
||||
public void setDropRow(int dropRow) {
|
||||
this.dropRow = dropRow;
|
||||
}
|
||||
|
||||
protected Border getNoFocusBorder() {
|
||||
return noFocusBorder;
|
||||
protected void setBold() {
|
||||
super.setFont(boldFont);
|
||||
}
|
||||
|
||||
protected Font getDefaultFont() {
|
||||
|
@ -156,6 +183,18 @@ public abstract class AbstractGCellRenderer extends GDHtmlLabel {
|
|||
return boldFont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the row where DnD would perform drop operation.
|
||||
* @param dropRow the drop row
|
||||
*/
|
||||
public void setDropRow(int dropRow) {
|
||||
this.dropRow = dropRow;
|
||||
}
|
||||
|
||||
protected Border getNoFocusBorder() {
|
||||
return noFocusBorder;
|
||||
}
|
||||
|
||||
protected Color getDefaultBackgroundColor() {
|
||||
return BACKGROUND_COLOR;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,8 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
new DropDownWindowVisibilityListener<>();
|
||||
|
||||
private GDHtmlLabel previewLabel;
|
||||
protected GList<T> list = new GList<>();
|
||||
protected DropDownList list = new DropDownList();
|
||||
|
||||
private WeakSet<DropDownSelectionChoiceListener<T>> choiceListeners =
|
||||
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
|
||||
private Collection<CellEditorListener> cellEditorListeners = new HashSet<>();
|
||||
|
@ -82,7 +83,6 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
private WindowComponentListener parentWindowListener = new WindowComponentListener();
|
||||
private T selectedValue;
|
||||
|
||||
private int cellHeight;
|
||||
private int matchingWindowHeight = MIN_HEIGHT;
|
||||
private Point lastLocation;
|
||||
protected final DropDownTextFieldDataModel<T> dataModel;
|
||||
|
@ -278,15 +278,6 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
|
||||
private void initDataList() {
|
||||
|
||||
Font font = list.getFont();
|
||||
FontMetrics fontMetrics = list.getFontMetrics(font);
|
||||
int padding = 2; // top and bottom border height
|
||||
int lineHeight = fontMetrics.getHeight() + padding;
|
||||
int iconAndPaddingHeight = 16 + padding;
|
||||
cellHeight = Math.max(lineHeight, iconAndPaddingHeight);
|
||||
|
||||
list.setFixedCellHeight(cellHeight);
|
||||
list.setFixedCellWidth(MIN_WIDTH - 20); // add some fudge for scrollbars
|
||||
list.setCellRenderer(dataModel.getListRenderer());
|
||||
|
||||
list.addKeyListener(keyListener);
|
||||
|
@ -654,7 +645,7 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
* signalling to use the clicked item. When pressing Enter, they may have been typing and
|
||||
* ignoring the list, so we have to do some validation.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // for the cast to T
|
||||
@SuppressWarnings("unchecked") // the item better be our type
|
||||
private void setTextFromListOnEnterPress() {
|
||||
Object selectedItem = list.getSelectedValue();
|
||||
if (selectedItem == null) {
|
||||
|
@ -747,6 +738,30 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
// Inner Classes
|
||||
//=================================================================================================
|
||||
|
||||
protected class DropDownList extends GList<T> {
|
||||
@Override
|
||||
public void setFont(Font f) {
|
||||
super.setFont(f);
|
||||
updateCellDimensions(f);
|
||||
}
|
||||
|
||||
private void updateCellDimensions(Font font) {
|
||||
|
||||
if (font == null || list == null) {
|
||||
return; // UI is initializing
|
||||
}
|
||||
|
||||
FontMetrics fontMetrics = list.getFontMetrics(font);
|
||||
int padding = 2; // top and bottom border height
|
||||
int lineHeight = fontMetrics.getHeight() + padding;
|
||||
int iconAndPaddingHeight = 16 + padding;
|
||||
int cellHeight = Math.max(lineHeight, iconAndPaddingHeight);
|
||||
|
||||
list.setFixedCellHeight(cellHeight);
|
||||
list.setFixedCellWidth(MIN_WIDTH - 20); // add some fudge for scrollbars
|
||||
}
|
||||
}
|
||||
|
||||
private class HideWindowFocusListener extends FocusAdapter {
|
||||
@Override
|
||||
public void focusLost(FocusEvent event) {
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
package docking.widgets.dialogs;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
|
@ -26,6 +27,7 @@ import docking.DockingUtils;
|
|||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
|
||||
|
@ -34,6 +36,8 @@ public class MultiLineInputDialog extends DialogComponentProvider {
|
|||
private static final KeyStroke SUBMIT_KEYSTROKE =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, DockingUtils.CONTROL_KEY_MODIFIER_MASK);
|
||||
|
||||
private static final String FONT_ID = "font.input.hint";
|
||||
|
||||
private boolean isCanceled;
|
||||
private JTextArea inputTextArea;
|
||||
|
||||
|
@ -84,10 +88,7 @@ public class MultiLineInputDialog extends DialogComponentProvider {
|
|||
}
|
||||
JLabel hintLabel = new GLabel("(" + metaKeyText + "-Enter to accept)");
|
||||
hintLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
Font font = hintLabel.getFont();
|
||||
Font smallerFont = font.deriveFont(12F);
|
||||
Font smallItalicFont = smallerFont.deriveFont(Font.ITALIC);
|
||||
hintLabel.setFont(smallItalicFont);
|
||||
Gui.registerFont(hintLabel, FONT_ID);
|
||||
hintLabel.setForeground(Messages.HINT);
|
||||
|
||||
dataPanel.add(messageLabel, BorderLayout.NORTH);
|
||||
|
|
|
@ -49,39 +49,54 @@ class DirectoryList extends GList<File> implements GhidraFileChooserDirectoryMod
|
|||
|
||||
/**
|
||||
* Create a new DirectoryList instance.
|
||||
*
|
||||
*
|
||||
* @param chooser the {@link GhidraFileChooser} this instance is nested in
|
||||
* @param model the {@link DirectoryListModel}
|
||||
* @param font the parent component's font, used to calculate row height in the list once
|
||||
*/
|
||||
DirectoryList(GhidraFileChooser chooser, DirectoryListModel model, Font font) {
|
||||
DirectoryList(GhidraFileChooser chooser, DirectoryListModel model) {
|
||||
super(model);
|
||||
this.chooser = chooser;
|
||||
this.model = model;
|
||||
build(font);
|
||||
build();
|
||||
}
|
||||
|
||||
private void build(Font font) {
|
||||
@Override
|
||||
public void setFont(Font font) {
|
||||
super.setFont(font);
|
||||
updateCellDimensions(font);
|
||||
}
|
||||
|
||||
private void updateCellDimensions(Font font) {
|
||||
|
||||
if (font == null) {
|
||||
return; // UI is being updated
|
||||
}
|
||||
|
||||
FileListCellRenderer cellRenderer = (FileListCellRenderer) getCellRenderer();
|
||||
if (cellRenderer == null) {
|
||||
return; // initializing
|
||||
}
|
||||
|
||||
// Enable the list to calculate the width of the cells on its own, but manually specify the
|
||||
// height to ensure some padding between rows.
|
||||
//
|
||||
// Use 1/3 of the line height of the font to ensure visually consistent padding between
|
||||
// rows. (Historically, 5px was used as the padding between the default 12pt (15px line
|
||||
// height) rows, so 15px line height/5px padding equals .333 ratio.)
|
||||
FontMetrics metrics = cellRenderer.getFontMetrics(font);
|
||||
setFixedCellHeight(Math.max(metrics.getHeight(), DEFAULT_ICON_SIZE) +
|
||||
Math.max(metrics.getHeight() / 3, MIN_HEIGHT_PADDING));
|
||||
setFixedCellWidth(-1);
|
||||
}
|
||||
|
||||
private void build() {
|
||||
|
||||
setLayoutOrientation(JList.VERTICAL_WRAP);
|
||||
|
||||
FileListCellRenderer cellRenderer = new FileListCellRenderer(chooser);
|
||||
setCellRenderer(cellRenderer);
|
||||
|
||||
// Enable the list to calculate the width of the cells on its own, but manually
|
||||
// specify the height to ensure some padding between rows.
|
||||
// We need the parent component's Font instead of using our
|
||||
// own #getFont() because we are not a child of the parent yet and
|
||||
// the font may be set to something other than the default.
|
||||
// Use 1/3 of the line height of the font to ensure visually consistent
|
||||
// padding between rows. (historically, 5px was used as the padding
|
||||
// between the default 12pt (15px lineht) rows, so 15px lineht/5px padding
|
||||
// equals .333 ratio.)
|
||||
FontMetrics metrics = cellRenderer.getFontMetrics(font);
|
||||
setFixedCellHeight(
|
||||
Math.max(metrics.getHeight(), DEFAULT_ICON_SIZE) +
|
||||
Math.max(metrics.getHeight() / 3, MIN_HEIGHT_PADDING));
|
||||
setFixedCellWidth(-1);
|
||||
updateCellDimensions(getFont());
|
||||
|
||||
addMouseListener(new GMouseListenerAdapter() {
|
||||
@Override
|
||||
|
|
|
@ -558,7 +558,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
|||
|
||||
private JScrollPane buildDirectoryList() {
|
||||
directoryListModel = new DirectoryListModel();
|
||||
directoryList = new DirectoryList(this, directoryListModel, rootPanel.getFont());
|
||||
directoryList = new DirectoryList(this, directoryListModel);
|
||||
directoryList.setName("LIST");
|
||||
directoryList.setBackground(BACKGROUND_COLOR);
|
||||
|
||||
|
|
|
@ -51,18 +51,17 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
|||
private static final Color BG_DRAG = new GColor("color.bg.table.row.drag");
|
||||
|
||||
/*
|
||||
* The map uses thread local variables to ensure that rendering and background model
|
||||
* The map uses thread local variables to ensure that rendering and background model
|
||||
* manipulation are thread safe.
|
||||
*/
|
||||
private static Map<Integer, ThreadLocal<DecimalFormat>> decimalFormatCache =
|
||||
new HashMap<>();
|
||||
private static Map<Integer, ThreadLocal<DecimalFormat>> decimalFormatCache = new HashMap<>();
|
||||
static {
|
||||
|
||||
int n = FloatingPointPrecisionSettingsDefinition.MAX_PRECISION;
|
||||
for (int i = 0; i <= n; i++) {
|
||||
int precision = i;
|
||||
ThreadLocal<DecimalFormat> localFormatter = ThreadLocal.withInitial(
|
||||
() -> new DecimalFormat(createDecimalFormat(precision)));
|
||||
ThreadLocal<DecimalFormat> localFormatter =
|
||||
ThreadLocal.withInitial(() -> new DecimalFormat(createDecimalFormat(precision)));
|
||||
decimalFormatCache.put(precision, localFormatter);
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +83,7 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
|||
|
||||
/**
|
||||
* Constructs a new GTableCellRenderer using the specified font.
|
||||
*
|
||||
*
|
||||
* @param f the font to use when rendering text in the table cells
|
||||
*/
|
||||
public GTableCellRenderer(Font f) {
|
||||
|
@ -94,7 +93,7 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
|||
|
||||
/**
|
||||
* Return the cell renderer text
|
||||
*
|
||||
*
|
||||
* @param value Cell object value
|
||||
* @return A string interpretation of value; generated by calling value.toString()
|
||||
*/
|
||||
|
@ -149,7 +148,7 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
|||
* Provide basic cell rendering -- setting foreground and background colors, font, text,
|
||||
* alignment, drop color, and border. Additional data that may be of use to the renderer is
|
||||
* passed through the {@link docking.widgets.table.GTableCellRenderingData} object.
|
||||
*
|
||||
*
|
||||
* @param data Context data used in the rendering of a data cell.
|
||||
* @return The component used for drawing the table cell.
|
||||
*/
|
||||
|
@ -158,7 +157,6 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
|||
Object value = data.getValue();
|
||||
JTable table = data.getTable();
|
||||
int row = data.getRowViewIndex();
|
||||
int column = data.getColumnViewIndex();
|
||||
boolean isSelected = data.isSelected();
|
||||
boolean hasFocus = data.hasFocus();
|
||||
Settings settings = data.getColumnSettings();
|
||||
|
@ -173,7 +171,7 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
|||
}
|
||||
|
||||
TableModel model = table.getModel();
|
||||
configureFont(table, model, column);
|
||||
setFont(getDefaultFont());
|
||||
|
||||
if (isSelected) {
|
||||
setForeground(table.getSelectionForeground());
|
||||
|
@ -199,8 +197,14 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
|||
setForeground(table.getForeground());
|
||||
}
|
||||
|
||||
protected void configureFont(JTable table, TableModel model, int column) {
|
||||
setFont(defaultFont);
|
||||
/**
|
||||
* Override to change the font that will be used each time the renderer is initialized inside
|
||||
* of {@link #getTableCellRendererComponent(GTableCellRenderingData)}
|
||||
* @return the font
|
||||
*/
|
||||
@Override
|
||||
protected Font getDefaultFont() {
|
||||
return defaultFont;
|
||||
}
|
||||
|
||||
protected int getRadix(Settings settings) {
|
||||
|
@ -217,7 +221,7 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
|||
|
||||
/**
|
||||
* Format a Number per the Settings parameters.
|
||||
*
|
||||
*
|
||||
* @param value the number to format
|
||||
* @param settings settings controlling the display of the Number parameter
|
||||
* @return a formatted representation of the Number value
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/* ###
|
||||
* 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.widgets.textarea;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.JTextArea;
|
||||
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
|
||||
/**
|
||||
* Simple text area that shows a text hint when the field is empty.
|
||||
*
|
||||
* Hint text will be shown in light grey, italicized, and in angle brackets. Normal text will
|
||||
* be plain black.
|
||||
*/
|
||||
public class HintTextArea extends JTextArea {
|
||||
|
||||
private String hint;
|
||||
|
||||
/**
|
||||
* Constructs the class with the hint text to be shown.
|
||||
*
|
||||
* @param hint the hint
|
||||
*/
|
||||
public HintTextArea(String hint) {
|
||||
this.hint = hint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Need to override the setText method so we can set font attributes.
|
||||
*
|
||||
* @param text the text
|
||||
*/
|
||||
@Override
|
||||
public void setText(String text) {
|
||||
super.setText(text);
|
||||
setAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
if (getText().isEmpty()) {
|
||||
if (g instanceof Graphics2D) {
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
g2.setColor(Messages.HINT);
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
if (hint != null) {
|
||||
g2.drawString(hint, 5, 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text attributes to be used when NOT viewing the hint.
|
||||
*/
|
||||
protected void setAttributes() {
|
||||
this.setFont(getFont().deriveFont(Font.PLAIN));
|
||||
setForeground(Colors.FOREGROUND);
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ public class HintTextField extends JTextField {
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*
|
||||
* @param hint the hint text
|
||||
* @param required true, if the field should be marked as required
|
||||
* @param verifier input verifier, or null if none needed
|
||||
|
@ -78,7 +78,6 @@ public class HintTextField extends JTextField {
|
|||
this.verifier = verifier;
|
||||
|
||||
addListeners();
|
||||
setFont(getFont().deriveFont(Font.PLAIN));
|
||||
validateField();
|
||||
}
|
||||
|
||||
|
@ -143,7 +142,7 @@ public class HintTextField extends JTextField {
|
|||
/**
|
||||
* Sets whether the field is required or not. If so, it will be rendered
|
||||
* differently to indicate that to the user.
|
||||
*
|
||||
*
|
||||
* @param required true if required, false otherwise
|
||||
*/
|
||||
public void setRequired(boolean required) {
|
||||
|
@ -161,7 +160,7 @@ public class HintTextField extends JTextField {
|
|||
|
||||
/**
|
||||
* Returns true if the field contains valid input.
|
||||
*
|
||||
*
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
public boolean isFieldValid() {
|
||||
|
@ -179,7 +178,7 @@ public class HintTextField extends JTextField {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks the validity of the field and sets the appropriate
|
||||
* Checks the validity of the field and sets the appropriate
|
||||
* field attributes.
|
||||
*/
|
||||
private void validateField() {
|
||||
|
|
|
@ -28,6 +28,7 @@ import docking.ReusableDialogComponentProvider;
|
|||
import docking.widgets.EmptyBorderButton;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.util.*;
|
||||
import help.Help;
|
||||
import help.HelpService;
|
||||
|
@ -47,6 +48,8 @@ public class WizardManager extends ReusableDialogComponentProvider implements Wi
|
|||
|
||||
private final static String INIT_TITLE = "<< untitled >>";
|
||||
|
||||
private static final String FONT_ID = "font.wizard.border.title";
|
||||
|
||||
private PanelManager panelMgr;
|
||||
private WizardPanel currWizPanel;
|
||||
private JButton backButton;
|
||||
|
@ -91,7 +94,7 @@ public class WizardManager extends ReusableDialogComponentProvider implements Wi
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @see docking.wizard.WizardPanelListener#validityChanged()
|
||||
*/
|
||||
@Override
|
||||
|
@ -108,7 +111,7 @@ public class WizardManager extends ReusableDialogComponentProvider implements Wi
|
|||
return getStatusText();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @see docking.wizard.WizardPanelListener#setStatusMessage(String)
|
||||
*/
|
||||
@Override
|
||||
|
@ -220,8 +223,7 @@ public class WizardManager extends ReusableDialogComponentProvider implements Wi
|
|||
titleLabel = (wizardIcon == null ? new GDLabel(INIT_TITLE)
|
||||
: new GDLabel(INIT_TITLE, wizardIcon, SwingConstants.TRAILING));
|
||||
|
||||
EmptyBorderButton helpButton =
|
||||
new EmptyBorderButton(Icons.INFO_ICON);
|
||||
EmptyBorderButton helpButton = new EmptyBorderButton(Icons.INFO_ICON);
|
||||
helpButton.setToolTipText("Help (F1)");
|
||||
helpButton.addActionListener(
|
||||
e -> DockingWindowManager.getHelpService().showHelp(rootPanel, false, rootPanel));
|
||||
|
@ -438,7 +440,7 @@ if (!visitedMap.containsKey(currWizPanel)) {
|
|||
return; // nothing to do
|
||||
}
|
||||
|
||||
// this will have no effect if we are not showing, but the above call will handle that
|
||||
// this will have no effect if we are not showing, but the above call will handle that
|
||||
// case
|
||||
defaultFocusComponent.requestFocusInWindow();
|
||||
}
|
||||
|
@ -465,14 +467,12 @@ if (!visitedMap.containsKey(currWizPanel)) {
|
|||
if (scrollPane.getVerticalScrollBar().isShowing()) {
|
||||
TitledBorder titledBorder =
|
||||
new TitledBorder(BorderFactory.createEmptyBorder(), "(scroll for more options)");
|
||||
|
||||
Font font = titledBorder.getTitleFont();
|
||||
if (font == null) {
|
||||
// workaround for bug on Java 7
|
||||
font = titleLabel.getFont();
|
||||
}
|
||||
|
||||
titledBorder.setTitleFont(font.deriveFont(10f));
|
||||
Gui.addThemeListener(e -> {
|
||||
if (e.isFontChanged(FONT_ID)) {
|
||||
titledBorder.setTitleFont(Gui.getFont(FONT_ID));
|
||||
}
|
||||
});
|
||||
titledBorder.setTitleFont(Gui.getFont(FONT_ID));
|
||||
titledBorder.setTitleColor(Messages.NORMAL);
|
||||
titledBorder.setTitlePosition(TitledBorder.BOTTOM);
|
||||
titledBorder.setTitleJustification(TitledBorder.TRAILING);
|
||||
|
|
|
@ -15,20 +15,14 @@
|
|||
*/
|
||||
package ghidra.docking.util;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Taskbar;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
|
||||
import docking.framework.ApplicationInformationDisplayFactory;
|
||||
import generic.theme.LafType;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
/**
|
||||
* A utility class to manage LookAndFeel (LaF) settings.
|
||||
|
@ -40,38 +34,12 @@ public class LookAndFeelUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Loads settings from {@link Preferences}.
|
||||
* This method does nothing. This is not handled by the theming system in the look and feel
|
||||
* manager.
|
||||
*/
|
||||
@Deprecated(since = "11.1", forRemoval = true)
|
||||
public static void installGlobalOverrides() {
|
||||
|
||||
//
|
||||
// Users can change this via the SystemUtilities.FONT_SIZE_OVERRIDE_PROPERTY_NAME
|
||||
// system property.
|
||||
//
|
||||
Integer fontOverride = SystemUtilities.getFontSizeOverrideValue();
|
||||
if (fontOverride != null) {
|
||||
setGlobalFontSizeOverride(fontOverride);
|
||||
}
|
||||
}
|
||||
|
||||
/** Allows you to globally set the font size (don't use this method!) */
|
||||
private static void setGlobalFontSizeOverride(int fontSize) {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
|
||||
Set<Entry<Object, Object>> set = defaults.entrySet();
|
||||
Iterator<Entry<Object, Object>> iterator = set.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Object, Object> entry = iterator.next();
|
||||
Object key = entry.getKey();
|
||||
|
||||
if (key.toString().toLowerCase().indexOf("font") != -1) {
|
||||
Font currentFont = defaults.getFont(key);
|
||||
if (currentFont != null) {
|
||||
Font newFont = currentFont.deriveFont((float) fontSize);
|
||||
UIManager.put(key, newFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void performPlatformSpecificFixups() {
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import docking.widgets.EmptyBorderButton;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
@ -43,6 +44,8 @@ import resources.Icons;
|
|||
*/
|
||||
public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
|
||||
private static final String MESSAGE_FONT_ID = "font.task.monitor.label.message";
|
||||
|
||||
private WeakSet<CancelledListener> listeners =
|
||||
WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
|
||||
|
@ -458,7 +461,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
// don't care
|
||||
}
|
||||
};
|
||||
messageLabel.setFont(messageLabel.getFont().deriveFont((float) 10.0));
|
||||
Gui.registerFont(messageLabel, MESSAGE_FONT_ID);
|
||||
Dimension d = messageLabel.getPreferredSize();
|
||||
d.width = 180;
|
||||
messageLabel.setPreferredSize(d);
|
||||
|
|
|
@ -35,7 +35,7 @@ public class ReflectionUtilitiesTest {
|
|||
@Test
|
||||
public void testGetClassNameAfter_NoClasses() {
|
||||
|
||||
String caller = ReflectionUtilities.getClassNameOlderThan();
|
||||
String caller = ReflectionUtilities.getClassNameOlderThan(new String[0]);
|
||||
assertThat(caller, is(equalTo(ReflectionUtilitiesTest.class.getName())));
|
||||
}
|
||||
|
||||
|
|
|
@ -23,12 +23,13 @@ color.visualgraph.view.satellite.edge.focused = color.palette.green
|
|||
color.visualgraph.view.satellite.edge.selected = color.palette.lime
|
||||
color.visualgraph.view.satellite.edge.hovered = color.palette.lime
|
||||
|
||||
|
||||
color.graphdisplay.vertex.default = color.palette.green
|
||||
color.graphdisplay.edge.default = color.palette.green
|
||||
color.graphdisplay.vertex.selected = color.palette.blue
|
||||
color.graphdisplay.edge.selected = color.palette.blue
|
||||
|
||||
font.visualgraph.view.label.message = SansSerif-PLAIN-22 // bigger for legibility in the graph
|
||||
|
||||
icon.graph.satellite = network-wireless-16.png
|
||||
icon.graph.satellite.large = network-wireless.png
|
||||
icon.graph.layout.default = color_swatch.png
|
||||
|
|
|
@ -27,6 +27,7 @@ import docking.widgets.label.GDLabel;
|
|||
import edu.uci.ics.jung.visualization.RenderContext;
|
||||
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
||||
import edu.uci.ics.jung.visualization.control.ScalingControl;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.graph.VisualGraph;
|
||||
import ghidra.graph.viewer.event.mouse.VertexTooltipProvider;
|
||||
import ghidra.graph.viewer.event.mouse.VisualGraphMousePlugin;
|
||||
|
@ -38,18 +39,18 @@ import ghidra.graph.viewer.vertex.VertexFocusListener;
|
|||
* A view object, where 'view' is used in the sense of the Model-View-Controller (MVC) pattern.
|
||||
* This class will contain all UI widgets need to display and interact with a graph.
|
||||
*
|
||||
* <p><b><u>Implementation Note:</u></b>
|
||||
* <p><b><u>Implementation Note:</u></b>
|
||||
* <ol>
|
||||
* <li>The graph of this component can be null, changing to non-null values over the
|
||||
* lifetime of this view. This allows this view to be installed in a UI component, with the
|
||||
* contents changing as needed.
|
||||
* <li>The graph of this component can be null, changing to non-null values over the
|
||||
* lifetime of this view. This allows this view to be installed in a UI component, with the
|
||||
* contents changing as needed.
|
||||
* </li>
|
||||
* <li>
|
||||
* When the graph is {@link #setGraph(VisualGraph) set}, the view portion of the class is
|
||||
* recreated.
|
||||
* </li>
|
||||
* <li>
|
||||
* At any given point in time there may not be a {@link #graphComponent}. This means that
|
||||
* At any given point in time there may not be a {@link #graphComponent}. This means that
|
||||
* this class must maintain settings state that it will apply when the component is created.
|
||||
* This state is atypical and makes this class a bit harder to understand.
|
||||
* </li>
|
||||
|
@ -60,14 +61,16 @@ import ghidra.graph.viewer.vertex.VertexFocusListener;
|
|||
* @param <G> the graph type
|
||||
*/
|
||||
//@formatter:off
|
||||
public class VisualGraphView<V extends VisualVertex,
|
||||
E extends VisualEdge<V>,
|
||||
public class VisualGraphView<V extends VisualVertex,
|
||||
E extends VisualEdge<V>,
|
||||
G extends VisualGraph<V, E>> {
|
||||
//@formatter:on
|
||||
//@formatter:on
|
||||
|
||||
private static final float ZOOM_OUT_AMOUNT = .9f;
|
||||
private static final float ZOOM_IN_AMOUNT = 1.1f;
|
||||
|
||||
private static final String MESSAGE_FONT_ID = "font.visualgraph.view.label.message";
|
||||
|
||||
private JPanel viewPanel;
|
||||
private JPanel viewContentPanel;
|
||||
|
||||
|
@ -76,7 +79,7 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
* As graph data is updated, we set and clear the contents of this panel as needed. This
|
||||
* allows the client to initialize the satellite window once, with updates controlled by
|
||||
* this class.
|
||||
*
|
||||
*
|
||||
* Note: this panel will be empty when docked and when the viewer is not yet built
|
||||
*/
|
||||
private JPanel undockedSatelliteContentPanel;
|
||||
|
@ -99,7 +102,7 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
|
||||
private Optional<GraphSatelliteListener> clientSatelliteListener = Optional.empty();
|
||||
|
||||
// this internal listener is the way we manage keeping our state in sync with the
|
||||
// this internal listener is the way we manage keeping our state in sync with the
|
||||
// graph component, as well as how we notify the client listener
|
||||
private GraphSatelliteListener internalSatelliteListener = (docked, visible) -> {
|
||||
|
||||
|
@ -190,10 +193,10 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets a listener that allows clients to be notified of vertex double-clicks. Normal
|
||||
* Sets a listener that allows clients to be notified of vertex double-clicks. Normal
|
||||
* mouse processing is handled by the {@link VisualGraphMousePlugin} class. This is a
|
||||
* convenience method so that clients do not have to deal with the mouse plugin.
|
||||
*
|
||||
*
|
||||
* @param l the listener
|
||||
*/
|
||||
public void setVertexClickListener(VertexClickListener<V, E> l) {
|
||||
|
@ -290,9 +293,9 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the primary viewer of the graph (as opposed to the satellite viewer). The
|
||||
* Returns the primary viewer of the graph (as opposed to the satellite viewer). The
|
||||
* viewer returned is responsible for maintaining view information for a given graph.
|
||||
*
|
||||
*
|
||||
* @return the primary viewer
|
||||
*/
|
||||
public GraphViewer<V, E> getPrimaryGraphViewer() {
|
||||
|
@ -308,7 +311,7 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
|
||||
/**
|
||||
* Sets the perspective for this view
|
||||
*
|
||||
*
|
||||
* @param newPerspective the new perspective
|
||||
*/
|
||||
public void setGraphPerspective(GraphPerspectiveInfo<V, E> newPerspective) {
|
||||
|
@ -350,8 +353,7 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
viewContentPanel.removeAll();
|
||||
viewContentPanel.paintImmediately(viewContentPanel.getBounds());
|
||||
JLabel messageLabel = new GDLabel(errorMessage);
|
||||
Font font = messageLabel.getFont();
|
||||
messageLabel.setFont(font.deriveFont(22f)); // make a bit bigger for readability
|
||||
Gui.registerFont(messageLabel, MESSAGE_FONT_ID);
|
||||
messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
messageLabel.setFocusable(true); // we have to have something focusable in our provider
|
||||
viewContentPanel.add(messageLabel, BorderLayout.NORTH);
|
||||
|
@ -362,8 +364,8 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
/**
|
||||
* Sets a message to be painted on the viewer. This is useful to show a text message to the
|
||||
* user. Passing null will clear the message.
|
||||
*
|
||||
* @param message the status message
|
||||
*
|
||||
* @param message the status message
|
||||
*/
|
||||
public void setStatusMessage(String message) {
|
||||
if (graphComponent != null) {
|
||||
|
@ -377,10 +379,10 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
|
||||
/**
|
||||
* Returns whether the satellite intended to be visible. If this component is built, then
|
||||
* a result of true means that the satellite is showing. If the component is not yet
|
||||
* built, then a result of true means that the satellite will be made visible when the
|
||||
* a result of true means that the satellite is showing. If the component is not yet
|
||||
* built, then a result of true means that the satellite will be made visible when the
|
||||
* component is built.
|
||||
*
|
||||
*
|
||||
* @return true if visible
|
||||
*/
|
||||
public boolean isSatelliteVisible() {
|
||||
|
@ -421,10 +423,10 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
|
||||
/**
|
||||
* Returns whether the satellite intended to be docked. If this component is built, then
|
||||
* a result of true means that the satellite is docked. If the component is not yet
|
||||
* built, then a result of true means that the satellite will be made docked when the
|
||||
* a result of true means that the satellite is docked. If the component is not yet
|
||||
* built, then a result of true means that the satellite will be made docked when the
|
||||
* component is built.
|
||||
*
|
||||
*
|
||||
* @return true if visible
|
||||
*/
|
||||
public boolean isSatelliteDocked() {
|
||||
|
@ -517,8 +519,8 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
}
|
||||
|
||||
public Point translatePointFromVertexToViewSpace(V v, Point p) {
|
||||
return GraphViewerUtils.translatePointFromVertexRelativeSpaceToViewSpace(
|
||||
getPrimaryGraphViewer(), v, p);
|
||||
return GraphViewerUtils
|
||||
.translatePointFromVertexRelativeSpaceToViewSpace(getPrimaryGraphViewer(), v, p);
|
||||
}
|
||||
|
||||
public Rectangle translateRectangleFromVertexToViewSpace(V v, Rectangle r) {
|
||||
|
@ -577,7 +579,7 @@ public class VisualGraphView<V extends VisualVertex,
|
|||
}
|
||||
|
||||
/**
|
||||
* Effectively clears this display. This method is not called dispose, as that implies
|
||||
* Effectively clears this display. This method is not called dispose, as that implies
|
||||
* the end of an object's lifecycle. This object can be re-used after this method is
|
||||
* called.
|
||||
*/
|
||||
|
|
|
@ -34,7 +34,8 @@ color.fg.tree.selected = [color]system.color.fg.selected.view
|
|||
|
||||
// Fonts
|
||||
font.standard = [font]system.font.control
|
||||
font.monospaced = monospaced-PLAIN-12
|
||||
font.standard.bold = font.standard[bold]
|
||||
font.monospaced = monospaced-plain-12
|
||||
|
||||
|
||||
//
|
||||
|
|
|
@ -298,6 +298,11 @@ public class ApplicationThemeManager extends ThemeManager {
|
|||
lookAndFeelManager.registerFont(component, fontId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerFont(Component component, String fontId, int fontStyle) {
|
||||
lookAndFeelManager.registerFont(component, fontId, fontStyle);
|
||||
}
|
||||
|
||||
private void installFlatLookAndFeels() {
|
||||
UIManager.installLookAndFeel(LafType.FLAT_LIGHT.getName(), FlatLightLaf.class.getName());
|
||||
UIManager.installLookAndFeel(LafType.FLAT_DARK.getName(), FlatDarkLaf.class.getName());
|
||||
|
|
|
@ -68,7 +68,7 @@ public class FontModifier {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the font stle modifier. This can be called multiple times to bold and italicize.
|
||||
* Sets the font style modifier. This can be called multiple times to bold and italicize.
|
||||
* @param newStyle the style to use for the font.
|
||||
*/
|
||||
public void addStyleModifier(int newStyle) {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/* ###
|
||||
* 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 generic.theme;
|
||||
|
||||
import java.awt.Font;
|
||||
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
|
||||
import ghidra.util.HTMLUtilities;
|
||||
|
||||
/**
|
||||
* A drop-in replacement for clients using {@link SimpleAttributeSet}s. This class will apply a
|
||||
* default set of font attributes based on the given font and optional color.
|
||||
*/
|
||||
public class GAttributes extends SimpleAttributeSet {
|
||||
|
||||
public GAttributes(Font f) {
|
||||
this(f, null);
|
||||
}
|
||||
|
||||
public GAttributes(Font f, GColor c) {
|
||||
addAttribute(StyleConstants.FontFamily, f.getFamily());
|
||||
addAttribute(StyleConstants.FontSize, f.getSize());
|
||||
addAttribute(StyleConstants.Bold, f.isBold());
|
||||
addAttribute(StyleConstants.Italic, f.isItalic());
|
||||
|
||||
if (c != null) {
|
||||
addAttribute(StyleConstants.Foreground, c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to style the given text in HTML using the font and color attributes
|
||||
* defined in this attribute set. The text will be HTML escaped.
|
||||
*
|
||||
* @param content the content
|
||||
* @return the styled content
|
||||
* @see HTMLUtilities#styleText(SimpleAttributeSet, String)
|
||||
*/
|
||||
public String toStyledHtml(String content) {
|
||||
return HTMLUtilities.styleText(this, content);
|
||||
}
|
||||
}
|
|
@ -17,8 +17,9 @@ package generic.theme;
|
|||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.*;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Provides a static set of methods for globally managing application themes and their values.
|
||||
|
@ -36,6 +37,8 @@ import javax.swing.LookAndFeel;
|
|||
*
|
||||
*/
|
||||
public class Gui {
|
||||
private static final String FONT_SUFFIX = ".font";
|
||||
|
||||
// Start with an StubThemeManager so that simple tests can operate without having
|
||||
// to initialize the theme system. Applications and integration tests will
|
||||
// called ThemeManager.initialize() which will replace this with a fully initialized version.
|
||||
|
@ -146,6 +149,9 @@ public class Gui {
|
|||
/**
|
||||
* Binds the component to the font identified by the given font id. Whenever the font for
|
||||
* the font id changes, the component will updated with the new font.
|
||||
* <p>
|
||||
* Calling this method will trigger a call to {@link JComponent#setFont(Font)}.
|
||||
*
|
||||
* @param component the component to set/update the font
|
||||
* @param fontId the id of the font to register with the given component
|
||||
*/
|
||||
|
@ -153,6 +159,37 @@ public class Gui {
|
|||
themeManager.registerFont(component, fontId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given component with the given font style. This method allows clients to not
|
||||
* define a font id in the theme system, but instead to signal that they want the default font
|
||||
* for the given component, modified with the given style. As the underlying font is changed,
|
||||
* the client will be updated with that new font with the given style applied.
|
||||
* <P>
|
||||
* Most clients should <b>not</b> be using this method. Instead, use
|
||||
* {@link #registerFont(JComponent, int)}.
|
||||
* <P>
|
||||
* The downside of using this method is that the end user cannot modify the style of the font.
|
||||
* By using the standard theming mechanism for registering fonts, the end user has full control.
|
||||
*
|
||||
* @param component the component to set/update the font
|
||||
* @param fontStyle the font style, one of Font.BOLD, Font.ITALIC,
|
||||
*/
|
||||
public static void registerFont(JComponent component, int fontStyle) {
|
||||
|
||||
if (fontStyle == Font.PLAIN) {
|
||||
Msg.warn(Gui.class,
|
||||
"Gui.registerFont(Component, int) may only be used for a non-plain font style. " +
|
||||
"Use registerFont(Component, String) instead.");
|
||||
return;
|
||||
}
|
||||
|
||||
String id = component.getUIClassID(); // e.g., ButtonUI
|
||||
String name = id.substring(0, id.length() - 2); // strip off "UI"
|
||||
String fontId = FontValue.LAF_ID_PREFIX + name + FONT_SUFFIX; // e.g., laf.font.Button.font
|
||||
|
||||
themeManager.registerFont(component, fontId, fontStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the active theme is using dark defaults
|
||||
* @return true if the active theme is using dark defaults
|
||||
|
|
|
@ -575,6 +575,21 @@ public abstract class ThemeManager {
|
|||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the component to the font identified by the given font id. Whenever the font for
|
||||
* the font id changes, the component will updated with the new font.
|
||||
* <p>
|
||||
* This method is fairly niche and should not be called by most clients. Instead, call
|
||||
* {@link #registerFont(Component, String)}.
|
||||
*
|
||||
* @param component the component to set/update the font
|
||||
* @param fontId the id of the font to register with the given component
|
||||
* @param fontStyle the font style
|
||||
*/
|
||||
public void registerFont(Component component, String fontId, int fontStyle) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current theme use dark default values.
|
||||
* @return true if the current theme use dark default values.
|
||||
|
|
|
@ -28,7 +28,8 @@ import ghidra.util.datastruct.WeakSet;
|
|||
* for the font id, this class will update the component's font to the new value.
|
||||
*/
|
||||
public class ComponentFontRegistry {
|
||||
private WeakSet<Component> components = WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
private WeakSet<StyledComponent> components =
|
||||
WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
private String fontId;
|
||||
|
||||
/**
|
||||
|
@ -45,8 +46,18 @@ public class ComponentFontRegistry {
|
|||
* @param component the component to add
|
||||
*/
|
||||
public void addComponent(Component component) {
|
||||
component.setFont(Gui.getFont(fontId));
|
||||
components.add(component);
|
||||
addComponent(component, Font.PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows clients to update the default font being used for a component to use the given style.
|
||||
* @param component the component
|
||||
* @param fontStyle the font style (e.g., {@link Font#BOLD})
|
||||
*/
|
||||
public void addComponent(Component component, int fontStyle) {
|
||||
StyledComponent sc = new StyledComponent(component, fontStyle);
|
||||
sc.setFont(Gui.getFont(fontId));
|
||||
components.add(sc);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,10 +65,26 @@ public class ComponentFontRegistry {
|
|||
*/
|
||||
public void updateComponentFonts() {
|
||||
Font font = Gui.getFont(fontId);
|
||||
for (Component component : components) {
|
||||
for (StyledComponent c : components) {
|
||||
c.setFont(font);
|
||||
}
|
||||
}
|
||||
|
||||
private record StyledComponent(Component component, int fontStyle) {
|
||||
|
||||
void setFont(Font font) {
|
||||
Font existingFont = component.getFont();
|
||||
if (!Objects.equals(existingFont, font)) {
|
||||
component.setFont(font);
|
||||
Font styledFont = font;
|
||||
int style = fontStyle();
|
||||
if (style != Font.PLAIN) {
|
||||
// Only style the font when it is not plain. Doing this means that clients cannot
|
||||
// override a non-plain font to be plain. If clients need that behavior, they must
|
||||
// create their own custom font id and register their component with Gui.
|
||||
styledFont = font.deriveFont(style);
|
||||
}
|
||||
|
||||
if (!Objects.equals(existingFont, styledFont)) {
|
||||
component.setFont(styledFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import generic.theme.*;
|
|||
import generic.util.action.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
/**
|
||||
* Manages installing and updating a {@link LookAndFeel}
|
||||
|
@ -38,6 +39,7 @@ public abstract class LookAndFeelManager {
|
|||
|
||||
private LafType laf;
|
||||
private Map<String, ComponentFontRegistry> fontRegistryMap = new HashMap<>();
|
||||
private Map<Component, String> componentToIdMap = new WeakHashMap<>();
|
||||
protected ApplicationThemeManager themeManager;
|
||||
protected Map<String, String> normalizedIdToLafIdMap;
|
||||
|
||||
|
@ -158,7 +160,7 @@ public abstract class LookAndFeelManager {
|
|||
* Called when one or more fonts have changed.
|
||||
* <p>
|
||||
* This will update the Java {@link UIManager} and trigger a reload of the UIs.
|
||||
*
|
||||
*
|
||||
* @param changedFontIds the set of Java Font ids that are affected by this change; these are
|
||||
* the normalized ids
|
||||
*/
|
||||
|
@ -202,12 +204,49 @@ public abstract class LookAndFeelManager {
|
|||
* @param fontId the id of the font to register with the given component
|
||||
*/
|
||||
public void registerFont(Component component, String fontId) {
|
||||
|
||||
checkForAlreadyRegistered(component, fontId);
|
||||
componentToIdMap.put(component, fontId);
|
||||
|
||||
ComponentFontRegistry register =
|
||||
fontRegistryMap.computeIfAbsent(fontId, id -> new ComponentFontRegistry(id));
|
||||
|
||||
register.addComponent(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the component to the font identified by the given font id. Whenever the font for
|
||||
* the font id changes, the component will be updated with the new font.
|
||||
* <p>
|
||||
* This method is fairly niche and should not be called by most clients. Instead, call
|
||||
* {@link #registerFont(Component, String)}.
|
||||
*
|
||||
* @param component the component to set/update the font
|
||||
* @param fontId the id of the font to register with the given component
|
||||
* @param fontStyle the font style
|
||||
*/
|
||||
public void registerFont(Component component, String fontId, int fontStyle) {
|
||||
|
||||
checkForAlreadyRegistered(component, fontId);
|
||||
componentToIdMap.put(component, fontId);
|
||||
|
||||
ComponentFontRegistry register =
|
||||
fontRegistryMap.computeIfAbsent(fontId, id -> new ComponentFontRegistry(id));
|
||||
|
||||
register.addComponent(component, fontStyle);
|
||||
}
|
||||
|
||||
private void checkForAlreadyRegistered(Component component, String newFontId) {
|
||||
String existingFontId = componentToIdMap.get(component);
|
||||
if (existingFontId != null) {
|
||||
Msg.warn(this, """
|
||||
Component has a Font ID registered more than once. \
|
||||
Previously registered ID: '%s'. Newly registered ID: '%s'.
|
||||
""".formatted(existingFontId, newFontId),
|
||||
ReflectionUtilities.createJavaFilteredThrowable());
|
||||
}
|
||||
}
|
||||
|
||||
private Font toUiResource(Font font) {
|
||||
if (!(font instanceof UIResource)) {
|
||||
return new FontUIResource(font);
|
||||
|
@ -292,8 +331,7 @@ public abstract class LookAndFeelManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected void setKeyBinding(String existingKsText, String newKsText,
|
||||
String[] prefixValues) {
|
||||
protected void setKeyBinding(String existingKsText, String newKsText, String[] prefixValues) {
|
||||
|
||||
KeyStroke existingKs = KeyStroke.getKeyStroke(existingKsText);
|
||||
KeyStroke newKs = KeyStroke.getKeyStroke(newKsText);
|
||||
|
|
|
@ -153,8 +153,8 @@ public class UiDefaultsMapper {
|
|||
* the user changeable values for affecting the Java LookAndFeel colors, fonts, and icons.
|
||||
* <p>
|
||||
* The keys in the returned map have been normalized and all start with 'laf.'
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @return a map of changeable values that affect java LookAndFeel values
|
||||
*/
|
||||
public GThemeValueMap getNormalizedJavaDefaults() {
|
||||
|
@ -184,7 +184,7 @@ public class UiDefaultsMapper {
|
|||
* Returns a mapping of normalized LaF Ids so that when fonts and icons get changed using the
|
||||
* normalized ids that are presented to the user, we know which LaF ids need to be updated in
|
||||
* the UiDefaults so that the LookAndFeel will pick up and use the changes.
|
||||
*
|
||||
*
|
||||
* @return a mapping of normalized LaF ids to original LaF ids.
|
||||
*/
|
||||
public Map<String, String> getNormalizedIdToLafIdMap() {
|
||||
|
@ -281,7 +281,7 @@ public class UiDefaultsMapper {
|
|||
|
||||
/**
|
||||
* This allows clients to hard-code a chosen color for a group
|
||||
*
|
||||
*
|
||||
* @param group the system color id to assign the given color
|
||||
* @param color the color to be assigned to the system color id
|
||||
*/
|
||||
|
@ -291,7 +291,7 @@ public class UiDefaultsMapper {
|
|||
|
||||
/**
|
||||
* This allows clients to hard-code a chosen font for a group
|
||||
*
|
||||
*
|
||||
* @param group the system font id to assign the given font
|
||||
* @param font the font to be assigned to the system font id
|
||||
*/
|
||||
|
@ -693,7 +693,7 @@ public class UiDefaultsMapper {
|
|||
* Groups allow us to use the same group id for many components that by default have the same
|
||||
* value (Color or Font). This grouper allows us to specify the precedence to use when
|
||||
* searching for the best group.
|
||||
*
|
||||
*
|
||||
* @param <T> The theme value type (Color or Font)
|
||||
*/
|
||||
private abstract class ValueGrouper<T> {
|
||||
|
|
|
@ -22,9 +22,10 @@ import java.util.regex.Pattern;
|
|||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.plaf.basic.BasicHTML;
|
||||
import javax.swing.text.View;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import generic.text.TextLayoutGraphics;
|
||||
import generic.theme.GAttributes;
|
||||
import ghidra.util.html.HtmlLineSplitter;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
|
@ -343,6 +344,67 @@ public class HTMLUtilities {
|
|||
return buffy.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes and wraps the given text in {@code SPAN} tag with font attributes specified in the
|
||||
* given attributes. Specifically, these attributes are used:
|
||||
*
|
||||
* <UL>
|
||||
* <LI>{@link StyleConstants#Foreground} - {@link Color} object</LI>
|
||||
* <LI>{@link StyleConstants#FontFamily} - font name</LI>
|
||||
* <LI>{@link StyleConstants#FontSize} - size in pixels</LI>
|
||||
* <LI>{@link StyleConstants#Italic} - true if italic</LI>
|
||||
* <LI>{@link StyleConstants#Bold} - true if bold</LI>
|
||||
* </UL>
|
||||
* <P>
|
||||
* See {@link GAttributes} for a convenient way to create the correct attributes for a font and
|
||||
* color.
|
||||
*
|
||||
* @param attributes the attributes
|
||||
* @param text the content to style
|
||||
* @return the styled content
|
||||
* @see GAttributes
|
||||
*/
|
||||
public static String styleText(SimpleAttributeSet attributes, String text) {
|
||||
|
||||
// StyleConstants.Foreground color: #00FF00;
|
||||
// StyleConstants.FontFamily font-family: "Tahoma";
|
||||
// StyleConstants.FontSize font-size: 40px;
|
||||
// StyleConstants.Italic font-style: italic;
|
||||
// StyleConstants.Bold font-weight: bold;
|
||||
|
||||
String family = attributes.getAttribute(StyleConstants.FontFamily).toString();
|
||||
String size = attributes.getAttribute(StyleConstants.FontSize).toString();
|
||||
String style = "plain";
|
||||
String weight = "plain";
|
||||
Boolean isItalic = (Boolean) attributes.getAttribute(StyleConstants.Italic);
|
||||
Boolean isBold = (Boolean) attributes.getAttribute(StyleConstants.Bold);
|
||||
if (Boolean.TRUE.equals(isItalic)) {
|
||||
style = "italic";
|
||||
}
|
||||
|
||||
if (Boolean.TRUE.equals(isBold)) {
|
||||
weight = "bold";
|
||||
}
|
||||
|
||||
// color is optional and defaults to the containing component's color
|
||||
String color = "";
|
||||
Object colorAttribute = attributes.getAttribute(StyleConstants.Foreground);
|
||||
if (colorAttribute instanceof Color fgColor) {
|
||||
String hexColor = HTMLUtilities.toHexString(fgColor);
|
||||
color = "color: % s;".formatted(hexColor);
|
||||
}
|
||||
|
||||
String escaped = escapeHTML(text);
|
||||
|
||||
//@formatter:off
|
||||
return """
|
||||
<SPAN STYLE=\"%s font-family: '%s'; font-size: %spx; font-style: %s; font-weight: %s;\">\
|
||||
%s\
|
||||
</SPAN>
|
||||
""".formatted(color, family, size, style, weight, escaped);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given text wrapped in {@link #LINK_PLACEHOLDER_OPEN} and close tags.
|
||||
* If <code>foo</code> is passed for the HTML text, with a content value of <code>123456</code>, then
|
||||
|
@ -510,7 +572,7 @@ public class HTMLUtilities {
|
|||
|
||||
/**
|
||||
* See {@link #friendlyEncodeHTML(String)}
|
||||
*
|
||||
*
|
||||
* @param text string to be encoded
|
||||
* @param skipLeadingWhitespace true signals to ignore any leading whitespace characters.
|
||||
* This is useful when line wrapping to force wrapped lines to the left
|
||||
|
@ -593,8 +655,7 @@ public class HTMLUtilities {
|
|||
* Calling this twice will result in text being double-escaped, which will not display correctly.
|
||||
* <p>
|
||||
* See also <code>StringEscapeUtils#escapeHtml3(String)</code> if you need quote-safe html encoding.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* @param text plain-text that might have some characters that should NOT be interpreted as HTML
|
||||
* @param makeSpacesNonBreaking true to convert spaces into {@value #HTML_SPACE}
|
||||
* @return string with any html characters replaced with equivalents
|
||||
|
@ -634,7 +695,7 @@ public class HTMLUtilities {
|
|||
|
||||
/**
|
||||
* Escapes any HTML special characters in the specified text.
|
||||
*
|
||||
*
|
||||
* @param text plain-text that might have some characters that should NOT be interpreted as HTML
|
||||
* @return string with any html characters replaced with equivalents
|
||||
* @see #escapeHTML(String, boolean)
|
||||
|
@ -647,7 +708,7 @@ public class HTMLUtilities {
|
|||
* Tests a unicode code point (i.e., 32 bit character) to see if it needs to be escaped before
|
||||
* being added to a HTML document because it is non-printable or a non-standard control
|
||||
* character
|
||||
*
|
||||
*
|
||||
* @param codePoint character to test
|
||||
* @return boolean true if character should be escaped
|
||||
*/
|
||||
|
|
|
@ -84,8 +84,12 @@ icon.plugin.manager.default = plasma.png
|
|||
font.help.about = font.monospaced
|
||||
font.keybindings.status = sansserif-plain-11
|
||||
font.task.viewer = sansserif-bold-36
|
||||
font.user.agreement = sansserif-plain-16
|
||||
font.task.progress.label.message = sansserif-plain-12
|
||||
font.user.agreement = sansserif-italic-22
|
||||
|
||||
font.panel.details = font.standard
|
||||
font.panel.details.monospaced = font.monospaced[bold]
|
||||
font.pluginpanel.name = sansserif-plain-18
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
package ghidra.framework.main;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Insets;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.swing.*;
|
||||
|
@ -57,11 +58,10 @@ public class UserAgreementDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private JComponent buildWorkPanel() {
|
||||
Font font = Gui.getFont(FONT_ID);
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
JLabel label = new GDLabel("Ghidra User Agreement", SwingConstants.CENTER);
|
||||
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
|
||||
label.setFont(font.deriveFont(Font.ITALIC, 22f));
|
||||
Gui.registerFont(label, FONT_ID);
|
||||
panel.add(label, BorderLayout.NORTH);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 40, 40, 40));
|
||||
JEditorPane editorPane = new JEditorPane();
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import javax.swing.*;
|
||||
|
||||
import docking.widgets.label.GDLabel;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.framework.client.RepositoryAdapter;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.remote.User;
|
||||
|
@ -33,9 +34,9 @@ import ghidra.framework.remote.User;
|
|||
*/
|
||||
public class ViewProjectAccessPanel extends ProjectAccessPanel {
|
||||
|
||||
/**
|
||||
/**
|
||||
* Construct a new panel.
|
||||
*
|
||||
*
|
||||
* @param repository handle to the repository adapter
|
||||
* @param tool the plugin tool
|
||||
* @throws IOException if there's an error processing repository information
|
||||
|
@ -47,13 +48,13 @@ public class ViewProjectAccessPanel extends ProjectAccessPanel {
|
|||
|
||||
/**
|
||||
* Constructs a new panel.
|
||||
*
|
||||
*
|
||||
* @param knownUsers names of the users that are known to the remote server
|
||||
* @param currentUser the current user
|
||||
* @param allUsers all users known to the repository
|
||||
* @param repositoryName the name of the repository
|
||||
* @param anonymousServerAccessAllowed true if the server allows anonymous access
|
||||
* @param anonymousAccessEnabled true if the repository allows anonymous access
|
||||
* @param anonymousAccessEnabled true if the repository allows anonymous access
|
||||
* (ignored if anonymousServerAccessAllowed is false)
|
||||
* @param tool the current tool
|
||||
*/
|
||||
|
@ -66,7 +67,7 @@ public class ViewProjectAccessPanel extends ProjectAccessPanel {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates the main gui panel, containing the known users, button, and user access
|
||||
* Creates the main gui panel, containing the known users, button, and user access
|
||||
* panels.
|
||||
*/
|
||||
@Override
|
||||
|
@ -82,9 +83,7 @@ public class ViewProjectAccessPanel extends ProjectAccessPanel {
|
|||
if (anonymousServerAccessAllowed && origAnonymousAccessEnabled) {
|
||||
JLabel anonymousAccessLabel = new GDLabel("Anonymous Read-Only Access Enabled");
|
||||
anonymousAccessLabel.setBorder(BorderFactory.createEmptyBorder(5, 2, 0, 0));
|
||||
Font f = anonymousAccessLabel.getFont().deriveFont(Font.ITALIC);
|
||||
anonymousAccessLabel.setFont(f);
|
||||
|
||||
Gui.registerFont(anonymousAccessLabel, Font.ITALIC);
|
||||
mainPanel.add(anonymousAccessLabel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,79 +17,57 @@ package ghidra.framework.plugintool.dialog;
|
|||
|
||||
import static ghidra.util.HTMLUtilities.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
import generic.theme.GColor;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import generic.theme.*;
|
||||
|
||||
/**
|
||||
* Abstract class that defines a panel for displaying name/value pairs with html-formatting.
|
||||
*/
|
||||
public abstract class AbstractDetailsPanel extends JPanel {
|
||||
|
||||
protected static final String FONT_DEFAULT = "font.panel.details";
|
||||
protected static final String FONT_MONOSPACED = "font.panel.details.monospaced";
|
||||
|
||||
private static final int MIN_WIDTH = 700;
|
||||
protected static final int LEFT_COLUMN_WIDTH = 150;
|
||||
protected static final int RIGHT_MARGIN = 30;
|
||||
|
||||
// Font attributes for the title of each row.
|
||||
protected static SimpleAttributeSet titleAttrSet;
|
||||
protected static GAttributes titleAttrs;
|
||||
|
||||
protected JLabel textLabel;
|
||||
protected JScrollPane sp;
|
||||
|
||||
private ThemeListener themeListener = e -> {
|
||||
|
||||
if (e.isFontChanged(FONT_DEFAULT) || e.isFontChanged(FONT_MONOSPACED)) {
|
||||
updateFieldAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
protected AbstractDetailsPanel() {
|
||||
createFieldAttributes();
|
||||
Gui.addThemeListener(themeListener);
|
||||
}
|
||||
|
||||
private void updateFieldAttributes() {
|
||||
createFieldAttributes();
|
||||
refresh();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets attributes for the different pieces of information being displayed in this
|
||||
* Sets attributes for the different pieces of information being displayed in this
|
||||
* panel.
|
||||
*/
|
||||
protected abstract void createFieldAttributes();
|
||||
|
||||
/**
|
||||
* Returns a new {@link SimpleAttributeSet} with all attributes set by the caller.
|
||||
*
|
||||
* @param fontFamily the font to use
|
||||
* @param fontSize the font size
|
||||
* @param bold if true, render text bold
|
||||
* @param color the foreground text color
|
||||
* @return a new attribute set
|
||||
*/
|
||||
protected SimpleAttributeSet createAttributeSet(String fontFamily, int fontSize, boolean bold,
|
||||
Color color) {
|
||||
|
||||
SimpleAttributeSet attrSet = new SimpleAttributeSet();
|
||||
attrSet.addAttribute(StyleConstants.FontFamily, fontFamily);
|
||||
attrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(fontSize));
|
||||
attrSet.addAttribute(StyleConstants.Bold, bold);
|
||||
attrSet.addAttribute(StyleConstants.Foreground, color);
|
||||
|
||||
return attrSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link SimpleAttributeSet} with the following default attributes set:
|
||||
* <ul>
|
||||
* <li>FontFamily: "Tahoma"</li>
|
||||
* <li>FontSize: 11</li>
|
||||
* <li>Bold: True</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param color the foreground text color
|
||||
* @return a new attribute set
|
||||
*/
|
||||
protected SimpleAttributeSet createAttributeSet(Color color) {
|
||||
|
||||
SimpleAttributeSet attrSet = new SimpleAttributeSet();
|
||||
attrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
attrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
attrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
attrSet.addAttribute(StyleConstants.Foreground, color);
|
||||
|
||||
return attrSet;
|
||||
}
|
||||
protected abstract void refresh();
|
||||
|
||||
/**
|
||||
* Clears the text in the details pane.
|
||||
|
@ -127,27 +105,26 @@ public abstract class AbstractDetailsPanel extends JPanel {
|
|||
/**
|
||||
* Inserts an html-formatted string into the given buffer. This is meant to be used
|
||||
* for inserting the name of each row in the description text.
|
||||
*
|
||||
*
|
||||
* @param buffer the string buffer to add to
|
||||
* @param rowName the name of the row to add
|
||||
*/
|
||||
protected void insertRowTitle(StringBuilder buffer, String rowName) {
|
||||
buffer.append("<TR>");
|
||||
buffer.append("<TD VALIGN=\"TOP\">");
|
||||
insertHTMLLine(buffer, rowName + ":", titleAttrSet);
|
||||
insertHTMLLine(buffer, rowName + ":", titleAttrs);
|
||||
buffer.append("</TD>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an html-formatted string into the given buffer. This is meant to be used
|
||||
* for inserting the value of each row in the description text.
|
||||
*
|
||||
*
|
||||
* @param buffer the string buffer to add to
|
||||
* @param value the text to add
|
||||
* @param attributes the structure containing formatting information
|
||||
* @param attributes the structure containing formatting information
|
||||
*/
|
||||
protected void insertRowValue(StringBuilder buffer, String value,
|
||||
SimpleAttributeSet attributes) {
|
||||
protected void insertRowValue(StringBuilder buffer, String value, GAttributes attributes) {
|
||||
buffer.append("<TD VALIGN=\"TOP\" WIDTH=\"80%\">");
|
||||
insertHTMLLine(buffer, value, attributes);
|
||||
buffer.append("</TD>");
|
||||
|
@ -161,33 +138,13 @@ public abstract class AbstractDetailsPanel extends JPanel {
|
|||
* @param string the string to add
|
||||
* @param attributes the formatting instructions
|
||||
*/
|
||||
protected void insertHTMLString(StringBuilder buffer, String string,
|
||||
SimpleAttributeSet attributes) {
|
||||
protected void insertHTMLString(StringBuilder buffer, String string, GAttributes attributes) {
|
||||
|
||||
if (string == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.append("<FONT COLOR=\"");
|
||||
|
||||
Color foregroundColor = (Color) attributes.getAttribute(StyleConstants.Foreground);
|
||||
buffer.append(HTMLUtilities.toHexString(foregroundColor));
|
||||
|
||||
buffer.append("\" FACE=\"");
|
||||
buffer.append(attributes.getAttribute(StyleConstants.FontFamily).toString());
|
||||
|
||||
buffer.append("\">");
|
||||
|
||||
Boolean isBold = (Boolean) attributes.getAttribute(StyleConstants.Bold);
|
||||
isBold = (isBold == null) ? Boolean.FALSE : isBold;
|
||||
String text = HTMLUtilities.escapeHTML(string);
|
||||
if (isBold) {
|
||||
text = HTMLUtilities.bold(text);
|
||||
}
|
||||
|
||||
buffer.append(text);
|
||||
|
||||
buffer.append("</FONT>");
|
||||
buffer.append(attributes.toStyledHtml(string));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,8 +153,7 @@ public abstract class AbstractDetailsPanel extends JPanel {
|
|||
* @param string the string to insert
|
||||
* @param attributes the attributes to apply
|
||||
*/
|
||||
protected void insertHTMLLine(StringBuilder buffer, String string,
|
||||
SimpleAttributeSet attributes) {
|
||||
protected void insertHTMLLine(StringBuilder buffer, String string, GAttributes attributes) {
|
||||
if (string == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -15,17 +15,16 @@
|
|||
*/
|
||||
package ghidra.framework.plugintool.dialog;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Point;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.MenuData;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.*;
|
||||
import ghidra.framework.plugintool.PluginConfigurationModel;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginDescription;
|
||||
|
@ -37,29 +36,52 @@ import ghidra.util.HTMLUtilities;
|
|||
*/
|
||||
class PluginDetailsPanel extends AbstractDetailsPanel {
|
||||
|
||||
private SimpleAttributeSet nameAttrSet;
|
||||
private SimpleAttributeSet depNameAttrSet;
|
||||
private SimpleAttributeSet descrAttrSet;
|
||||
private SimpleAttributeSet categoriesAttrSet;
|
||||
private SimpleAttributeSet classAttrSet;
|
||||
private SimpleAttributeSet locAttrSet;
|
||||
private SimpleAttributeSet developerAttrSet;
|
||||
private SimpleAttributeSet dependencyAttrSet;
|
||||
private SimpleAttributeSet noValueAttrSet;
|
||||
private static final GColor NO_VALUE_COLOR = new GColor("color.fg.pluginpanel.details.novalue");
|
||||
private static final GColor DEPENDENCY_COLOR =
|
||||
new GColor("color.fg.pluginpanel.details.dependency");
|
||||
private static final GColor LOCATION_COLOR = new GColor("color.fg.pluginpanel.details.loc");
|
||||
private static final GColor DEVELOPER_COLOR =
|
||||
new GColor("color.fg.pluginpanel.details.developer");
|
||||
private static final GColor CLASS_COLOR = new GColor("color.fg.pluginpanel.details.class");
|
||||
private static final GColor CATEGORIES_COLOR =
|
||||
new GColor("color.fg.pluginpanel.details.category");
|
||||
private static final GColor TITLE_COLOR = new GColor("color.fg.pluginpanel.details.title");
|
||||
private static final GColor DESCRIPTION_COLOR =
|
||||
new GColor("color.fg.pluginpanel.details.description");
|
||||
private static final GColor NAME_NO_DEPENDENTS_COLOR =
|
||||
new GColor("color.fg.pluginpanel.details.name.no.dependents");
|
||||
private static final GColor NAME_DEPENDENTS_COLOR =
|
||||
new GColor("color.fg.pluginpanel.details.name.has.dependents");
|
||||
|
||||
private GAttributes nameAttrs;
|
||||
private GAttributes dependenciesNameAttrs;
|
||||
private GAttributes descriptionAttrs;
|
||||
private GAttributes categoriesAttrs;
|
||||
private GAttributes classAttrs;
|
||||
private GAttributes locationAttrs;
|
||||
private GAttributes developerAttrs;
|
||||
private GAttributes dependencyAttrs;
|
||||
private GAttributes noValueAttrs;
|
||||
|
||||
private final PluginConfigurationModel model;
|
||||
private PluginTool tool;
|
||||
private PluginDescription currentDescriptor;
|
||||
|
||||
PluginDetailsPanel(PluginTool tool, PluginConfigurationModel model) {
|
||||
super();
|
||||
this.tool = tool;
|
||||
this.model = model;
|
||||
createFieldAttributes();
|
||||
createMainPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refresh() {
|
||||
setPluginDescription(currentDescriptor);
|
||||
}
|
||||
|
||||
void setPluginDescription(PluginDescription descriptor) {
|
||||
|
||||
this.currentDescriptor = descriptor;
|
||||
textLabel.setText("");
|
||||
if (descriptor == null) {
|
||||
return;
|
||||
|
@ -74,43 +96,43 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
|
||||
insertRowTitle(buffer, "Name");
|
||||
insertRowValue(buffer, descriptor.getName(),
|
||||
!dependencies.isEmpty() ? depNameAttrSet : nameAttrSet);
|
||||
!dependencies.isEmpty() ? dependenciesNameAttrs : nameAttrs);
|
||||
|
||||
insertRowTitle(buffer, "Description");
|
||||
insertRowValue(buffer, descriptor.getDescription(), descrAttrSet);
|
||||
insertRowValue(buffer, descriptor.getDescription(), descriptionAttrs);
|
||||
|
||||
insertRowTitle(buffer, "Status");
|
||||
insertRowValue(buffer, descriptor.getStatus().getDescription(),
|
||||
(descriptor.getStatus() == PluginStatus.RELEASED) ? titleAttrSet : developerAttrSet);
|
||||
(descriptor.getStatus() == PluginStatus.RELEASED) ? titleAttrs : developerAttrs);
|
||||
|
||||
insertRowTitle(buffer, "Package");
|
||||
insertRowValue(buffer, descriptor.getPluginPackage().getName(), categoriesAttrSet);
|
||||
insertRowValue(buffer, descriptor.getPluginPackage().getName(), categoriesAttrs);
|
||||
|
||||
insertRowTitle(buffer, "Category");
|
||||
insertRowValue(buffer, descriptor.getCategory(), categoriesAttrSet);
|
||||
insertRowValue(buffer, descriptor.getCategory(), categoriesAttrs);
|
||||
|
||||
insertRowTitle(buffer, "Plugin Class");
|
||||
insertRowValue(buffer, descriptor.getPluginClass().getName(), classAttrSet);
|
||||
insertRowValue(buffer, descriptor.getPluginClass().getName(), classAttrs);
|
||||
|
||||
insertRowTitle(buffer, "Class Location");
|
||||
insertRowValue(buffer, descriptor.getSourceLocation(), locAttrSet);
|
||||
insertRowValue(buffer, descriptor.getSourceLocation(), locationAttrs);
|
||||
|
||||
insertRowTitle(buffer, "Used By");
|
||||
|
||||
buffer.append("<TD VALIGN=\"TOP\">");
|
||||
|
||||
if (dependencies.isEmpty()) {
|
||||
insertHTMLLine(buffer, "None", noValueAttrSet);
|
||||
insertHTMLLine(buffer, "None", noValueAttrs);
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < dependencies.size(); i++) {
|
||||
insertHTMLString(buffer, dependencies.get(i).getPluginClass().getName(),
|
||||
dependencyAttrSet);
|
||||
dependencyAttrs);
|
||||
if (i < dependencies.size() - 1) {
|
||||
buffer.append(HTMLUtilities.BR);
|
||||
}
|
||||
}
|
||||
insertHTMLLine(buffer, "", titleAttrSet); // add a newline
|
||||
insertHTMLLine(buffer, "", titleAttrs); // add a newline
|
||||
}
|
||||
buffer.append("</TD>");
|
||||
buffer.append("</TR>");
|
||||
|
@ -121,16 +143,16 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
|
||||
List<Class<?>> servicesRequired = descriptor.getServicesRequired();
|
||||
if (servicesRequired.isEmpty()) {
|
||||
insertHTMLLine(buffer, "None", noValueAttrSet);
|
||||
insertHTMLLine(buffer, "None", noValueAttrs);
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < servicesRequired.size(); i++) {
|
||||
insertHTMLString(buffer, servicesRequired.get(i).getName(), dependencyAttrSet);
|
||||
insertHTMLString(buffer, servicesRequired.get(i).getName(), dependencyAttrs);
|
||||
if (i < servicesRequired.size() - 1) {
|
||||
buffer.append(HTMLUtilities.BR);
|
||||
}
|
||||
}
|
||||
insertHTMLLine(buffer, "", titleAttrSet); // add a newline
|
||||
insertHTMLLine(buffer, "", titleAttrs); // add a newline
|
||||
}
|
||||
buffer.append("</TD>");
|
||||
buffer.append("</TR>");
|
||||
|
@ -158,7 +180,7 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
|
||||
buffer.append("<TR>");
|
||||
buffer.append("<TD VALIGN=\"TOP\">");
|
||||
insertHTMLLine(buffer, "Loaded Actions:", titleAttrSet);
|
||||
insertHTMLLine(buffer, "Loaded Actions:", titleAttrs);
|
||||
buffer.append("</TD>");
|
||||
|
||||
Set<DockingActionIf> actions = Collections.emptySet();
|
||||
|
@ -169,7 +191,7 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
|
||||
if (actions.isEmpty()) {
|
||||
buffer.append("<TD VALIGN=\"TOP\">");
|
||||
insertHTMLLine(buffer, "No actions for plugin", noValueAttrSet);
|
||||
insertHTMLLine(buffer, "No actions for plugin", noValueAttrs);
|
||||
buffer.append("</TD>");
|
||||
buffer.append("</TR>");
|
||||
return;
|
||||
|
@ -182,7 +204,7 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
|
||||
for (DockingActionIf dockableAction : actions) {
|
||||
buffer.append("<TR><TD WIDTH=\"200\">");
|
||||
insertHTMLString(buffer, dockableAction.getName(), locAttrSet);
|
||||
insertHTMLString(buffer, dockableAction.getName(), locationAttrs);
|
||||
buffer.append("</TD>");
|
||||
|
||||
buffer.append("<TD WIDTH=\"300\">");
|
||||
|
@ -190,17 +212,17 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
String[] menuPath = menuBarData == null ? null : menuBarData.getMenuPath();
|
||||
String menuPathString = createStringForMenuPath(menuPath);
|
||||
if (menuPathString != null) {
|
||||
insertHTMLString(buffer, menuPathString, locAttrSet);
|
||||
insertHTMLString(buffer, menuPathString, locationAttrs);
|
||||
}
|
||||
else {
|
||||
MenuData popupMenuData = dockableAction.getPopupMenuData();
|
||||
String[] popupPath = popupMenuData == null ? null : popupMenuData.getMenuPath();
|
||||
|
||||
if (popupPath != null) {
|
||||
insertHTMLString(buffer, "(in a context popup menu)", noValueAttrSet);
|
||||
insertHTMLString(buffer, "(in a context popup menu)", noValueAttrs);
|
||||
}
|
||||
else {
|
||||
insertHTMLString(buffer, "Not in a menu", noValueAttrSet);
|
||||
insertHTMLString(buffer, "Not in a menu", noValueAttrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,10 +232,10 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
KeyStroke keyBinding = dockableAction.getKeyBinding();
|
||||
if (keyBinding != null) {
|
||||
String keyStrokeString = KeyBindingUtils.parseKeyStroke(keyBinding);
|
||||
insertHTMLString(buffer, keyStrokeString, locAttrSet);
|
||||
insertHTMLString(buffer, keyStrokeString, locationAttrs);
|
||||
}
|
||||
else {
|
||||
insertHTMLString(buffer, "No keybinding", noValueAttrSet);
|
||||
insertHTMLString(buffer, "No keybinding", noValueAttrs);
|
||||
}
|
||||
|
||||
buffer.append("</TD></TR>");
|
||||
|
@ -242,74 +264,19 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
@Override
|
||||
protected void createFieldAttributes() {
|
||||
|
||||
titleAttrSet = new SimpleAttributeSet();
|
||||
titleAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
titleAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
titleAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
titleAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.title"));
|
||||
Font font = Gui.getFont(FONT_DEFAULT);
|
||||
titleAttrs = new GAttributes(font, TITLE_COLOR);
|
||||
nameAttrs = new GAttributes(font, NAME_NO_DEPENDENTS_COLOR);
|
||||
dependenciesNameAttrs = new GAttributes(font, NAME_DEPENDENTS_COLOR);
|
||||
descriptionAttrs = new GAttributes(font, DESCRIPTION_COLOR);
|
||||
categoriesAttrs = new GAttributes(font, CATEGORIES_COLOR);
|
||||
locationAttrs = new GAttributes(font, LOCATION_COLOR);
|
||||
developerAttrs = new GAttributes(font, DEVELOPER_COLOR);
|
||||
|
||||
nameAttrSet = new SimpleAttributeSet();
|
||||
nameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
nameAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
nameAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
nameAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.name.no.dependents"));
|
||||
Font fontMonospaced = Gui.getFont(FONT_MONOSPACED);
|
||||
classAttrs = new GAttributes(fontMonospaced, CLASS_COLOR);
|
||||
dependencyAttrs = new GAttributes(fontMonospaced, DEPENDENCY_COLOR);
|
||||
|
||||
depNameAttrSet = new SimpleAttributeSet();
|
||||
depNameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
depNameAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
depNameAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
depNameAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.name.has.dependents"));
|
||||
|
||||
descrAttrSet = new SimpleAttributeSet();
|
||||
descrAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
descrAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
descrAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
descrAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.description"));
|
||||
|
||||
categoriesAttrSet = new SimpleAttributeSet();
|
||||
categoriesAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
categoriesAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
categoriesAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
categoriesAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.category"));
|
||||
|
||||
classAttrSet = new SimpleAttributeSet();
|
||||
classAttrSet.addAttribute(StyleConstants.FontFamily, "monospaced");
|
||||
classAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
classAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
classAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.class"));
|
||||
|
||||
locAttrSet = new SimpleAttributeSet();
|
||||
locAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
locAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
locAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
locAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.loc"));
|
||||
|
||||
developerAttrSet = new SimpleAttributeSet();
|
||||
developerAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
developerAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
developerAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
developerAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.developer"));
|
||||
|
||||
dependencyAttrSet = new SimpleAttributeSet();
|
||||
dependencyAttrSet.addAttribute(StyleConstants.FontFamily, "monospaced");
|
||||
dependencyAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
dependencyAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||
dependencyAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.dependency"));
|
||||
|
||||
noValueAttrSet = new SimpleAttributeSet();
|
||||
noValueAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
noValueAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
noValueAttrSet.addAttribute(StyleConstants.Italic, Boolean.TRUE);
|
||||
noValueAttrSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.pluginpanel.details.novalue"));
|
||||
noValueAttrs = new GAttributes(font, NO_VALUE_COLOR);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,14 @@
|
|||
*/
|
||||
package ghidra.framework.plugintool.dialog;
|
||||
|
||||
import static ghidra.framework.plugintool.dialog.PluginInstallerTableModel.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.table.*;
|
||||
|
@ -149,30 +152,23 @@ public class PluginInstallerDialog extends DialogComponentProvider {
|
|||
tableFilterPanel = new GTableFilterPanel<>(table, tableModel);
|
||||
|
||||
JScrollPane sp = new JScrollPane(table);
|
||||
|
||||
pluginTablePanel.add(sp, BorderLayout.CENTER);
|
||||
pluginTablePanel.add(tableFilterPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Restrict the size of the first couple columns - the default size is
|
||||
// way too large. This is annoying but our table column classes don't have a nice
|
||||
// way to restrict column width.
|
||||
TableColumn inst_col =
|
||||
table.getColumnModel().getColumn(PluginInstallerTableModel.INSTALLED_COL);
|
||||
inst_col.setMaxWidth(30);
|
||||
TableColumn status_col =
|
||||
table.getColumnModel().getColumn(PluginInstallerTableModel.STATUS_COL);
|
||||
status_col.setMaxWidth(24);
|
||||
TableColumnModel columnModel = table.getColumnModel();
|
||||
TableColumn installedColumn = columnModel.getColumn(INSTALLED_COL);
|
||||
installedColumn.setMaxWidth(30);
|
||||
TableColumn statusColumn = columnModel.getColumn(STATUS_COL);
|
||||
statusColumn.setMaxWidth(24);
|
||||
|
||||
tableModel.setTableSortState(
|
||||
TableSortState.createDefaultSortState(PluginInstallerTableModel.NAME_COL));
|
||||
tableModel.setTableSortState(TableSortState.createDefaultSortState(NAME_COL));
|
||||
tableModel.refresh();
|
||||
|
||||
table.getColumnModel()
|
||||
.getColumn(PluginInstallerTableModel.NAME_COL)
|
||||
.setCellRenderer(new NameCellRenderer());
|
||||
table.getColumnModel()
|
||||
.getColumn(PluginInstallerTableModel.STATUS_COL)
|
||||
.setCellRenderer(new StatusCellRenderer());
|
||||
columnModel.getColumn(NAME_COL).setCellRenderer(new NameCellRenderer());
|
||||
columnModel.getColumn(STATUS_COL).setCellRenderer(new StatusCellRenderer());
|
||||
|
||||
HelpService help = Help.getHelpService();
|
||||
help.registerHelp(table, new HelpLocation(GenericHelpTopics.TOOL, "PluginDialog"));
|
||||
|
@ -214,10 +210,10 @@ public class PluginInstallerDialog extends DialogComponentProvider {
|
|||
|
||||
renderer.setIcon((value instanceof Icon) ? (Icon) value : null);
|
||||
String toolTipText = "";
|
||||
if (value == PluginInstallerTableModel.EXPERIMENTAL_ICON) {
|
||||
if (value == EXPERIMENTAL_ICON) {
|
||||
toolTipText = "This plugin is usable, but not fully tested or documented";
|
||||
}
|
||||
else if (value == PluginInstallerTableModel.DEV_ICON) {
|
||||
else if (value == DEV_ICON) {
|
||||
toolTipText =
|
||||
"This plugin is under development and not intended for general use.\n" +
|
||||
"It could cause Ghidra to become unstable!";
|
||||
|
|
|
@ -176,7 +176,7 @@ public class PluginManagerComponent extends JPanel implements Scrollable {
|
|||
labelPanel.setBackground(BG);
|
||||
|
||||
GLabel nameLabel = new GLabel(pluginPackage.getName());
|
||||
nameLabel.setFont(nameLabel.getFont().deriveFont(18f));
|
||||
Gui.registerFont(nameLabel, "font.pluginpanel.name");
|
||||
nameLabel.setForeground(new GColor("color.fg.pluginpanel.name"));
|
||||
labelPanel.add(nameLabel);
|
||||
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
*/
|
||||
package ghidra.framework.project.extensions;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Point;
|
||||
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
|
||||
import docking.widgets.table.threaded.ThreadedTableModelListener;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.*;
|
||||
import ghidra.framework.plugintool.dialog.AbstractDetailsPanel;
|
||||
import ghidra.util.extensions.ExtensionDetails;
|
||||
|
||||
|
@ -33,27 +31,28 @@ import ghidra.util.extensions.ExtensionDetails;
|
|||
*/
|
||||
class ExtensionDetailsPanel extends AbstractDetailsPanel {
|
||||
|
||||
private static final Color FG_COLOR_AUTHOR =
|
||||
private static final GColor FG_COLOR_AUTHOR =
|
||||
new GColor("color.fg.extensionpanel.details.author");
|
||||
private static final Color FG_COLOR_DATE = new GColor("color.fg.extensionpanel.details.date");
|
||||
private static final Color FG_COLOR_DESCRIPTION =
|
||||
private static final GColor FG_COLOR_DATE = new GColor("color.fg.extensionpanel.details.date");
|
||||
private static final GColor FG_COLOR_DESCRIPTION =
|
||||
new GColor("color.fg.extensionpanel.details.description");
|
||||
private static final Color FG_COLOR_NAME = new GColor("color.fg.extensionpanel.details.name");
|
||||
private static final Color FG_COLOR_PATH = new GColor("color.fg.extensionpanel.path");
|
||||
private static final Color FG_COLOR_TITLE = new GColor("color.fg.extensionpanel.details.title");
|
||||
private static final Color FG_COLOR_VERSION =
|
||||
private static final GColor FG_COLOR_NAME = new GColor("color.fg.extensionpanel.details.name");
|
||||
private static final GColor FG_COLOR_PATH = new GColor("color.fg.extensionpanel.path");
|
||||
private static final GColor FG_COLOR_TITLE =
|
||||
new GColor("color.fg.extensionpanel.details.title");
|
||||
private static final GColor FG_COLOR_VERSION =
|
||||
new GColor("color.fg.extensionpanel.details.version");
|
||||
|
||||
/** Attribute sets define the visual characteristics for each field */
|
||||
private SimpleAttributeSet nameAttrSet;
|
||||
private SimpleAttributeSet descrAttrSet;
|
||||
private SimpleAttributeSet authorAttrSet;
|
||||
private SimpleAttributeSet createdOnAttrSet;
|
||||
private SimpleAttributeSet versionAttrSet;
|
||||
private SimpleAttributeSet pathAttrSet;
|
||||
private GAttributes nameAttrSet;
|
||||
private GAttributes descrAttrSet;
|
||||
private GAttributes authorAttrSet;
|
||||
private GAttributes createdOnAttrSet;
|
||||
private GAttributes versionAttrSet;
|
||||
private GAttributes pathAttrSet;
|
||||
private ExtensionDetails currentDetails;
|
||||
|
||||
ExtensionDetailsPanel(ExtensionTablePanel tablePanel) {
|
||||
super();
|
||||
createFieldAttributes();
|
||||
createMainPanel();
|
||||
|
||||
|
@ -82,13 +81,19 @@ class ExtensionDetailsPanel extends AbstractDetailsPanel {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refresh() {
|
||||
setDescription(currentDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this panel with the given extension.
|
||||
*
|
||||
*
|
||||
* @param details the extension to display
|
||||
*/
|
||||
public void setDescription(ExtensionDetails details) {
|
||||
|
||||
this.currentDetails = details;
|
||||
clear();
|
||||
if (details == null) {
|
||||
return;
|
||||
|
@ -134,12 +139,14 @@ class ExtensionDetailsPanel extends AbstractDetailsPanel {
|
|||
|
||||
@Override
|
||||
protected void createFieldAttributes() {
|
||||
titleAttrSet = createAttributeSet(FG_COLOR_TITLE);
|
||||
nameAttrSet = createAttributeSet(FG_COLOR_NAME);
|
||||
descrAttrSet = createAttributeSet(FG_COLOR_DESCRIPTION);
|
||||
authorAttrSet = createAttributeSet(FG_COLOR_AUTHOR);
|
||||
createdOnAttrSet = createAttributeSet(FG_COLOR_DATE);
|
||||
versionAttrSet = createAttributeSet(FG_COLOR_VERSION);
|
||||
pathAttrSet = createAttributeSet(FG_COLOR_PATH);
|
||||
|
||||
Font font = Gui.getFont(FONT_DEFAULT);
|
||||
titleAttrs = new GAttributes(font, FG_COLOR_TITLE);
|
||||
nameAttrSet = new GAttributes(font, FG_COLOR_NAME);
|
||||
descrAttrSet = new GAttributes(font, FG_COLOR_DESCRIPTION);
|
||||
authorAttrSet = new GAttributes(font, FG_COLOR_AUTHOR);
|
||||
createdOnAttrSet = new GAttributes(font, FG_COLOR_DATE);
|
||||
versionAttrSet = new GAttributes(font, FG_COLOR_VERSION);
|
||||
pathAttrSet = new GAttributes(font, FG_COLOR_PATH);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import docking.util.AnimatedIcon;
|
|||
import docking.widgets.EmptyBorderButton;
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
|
@ -36,6 +37,8 @@ import resources.ResourceManager;
|
|||
public class GProgressBar extends JPanel {
|
||||
private static final NumberFormat PERCENT_FORMAT = NumberFormat.getPercentInstance();
|
||||
|
||||
private static final String MESSAGE_FONT_ID = "font.task.progress.label.message";
|
||||
|
||||
private volatile long lastProgress = -1;
|
||||
private volatile long progress;
|
||||
private volatile long scaleFactor = 1;
|
||||
|
@ -47,7 +50,6 @@ public class GProgressBar extends JPanel {
|
|||
private volatile boolean paintProgressValue = true;
|
||||
private boolean showingIcon = true;
|
||||
|
||||
private final float fontSize;
|
||||
private JProgressBar progressBar;
|
||||
private JLabel messageLabel;
|
||||
private JLabel imageLabel;
|
||||
|
@ -61,10 +63,9 @@ public class GProgressBar extends JPanel {
|
|||
private CancelledListener cancelledListener;
|
||||
|
||||
public GProgressBar(CancelledListener cancelledListener, boolean includeTextField,
|
||||
boolean includeCancelButton, boolean includeAnimatedIcon, float fontSize) {
|
||||
boolean includeCancelButton, boolean includeAnimatedIcon) {
|
||||
super(new BorderLayout(5, 1));
|
||||
this.cancelledListener = cancelledListener;
|
||||
this.fontSize = fontSize;
|
||||
|
||||
buildProgressPanel(includeTextField, includeCancelButton, includeAnimatedIcon);
|
||||
|
||||
|
@ -217,7 +218,7 @@ public class GProgressBar extends JPanel {
|
|||
// don't care
|
||||
}
|
||||
};
|
||||
messageLabel.setFont(messageLabel.getFont().deriveFont(fontSize));
|
||||
Gui.registerFont(messageLabel, MESSAGE_FONT_ID);
|
||||
Dimension d = messageLabel.getPreferredSize();
|
||||
d.width = 180;
|
||||
messageLabel.setPreferredSize(d);
|
||||
|
|
|
@ -31,7 +31,6 @@ public class ScheduledTaskPanel extends JPanel {
|
|||
private ScheduledElementLayout layout;
|
||||
|
||||
public ScheduledTaskPanel(String labelText, int indention) {
|
||||
super();
|
||||
this.indention = indention;
|
||||
|
||||
setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
|
||||
|
@ -44,7 +43,7 @@ public class ScheduledTaskPanel extends JPanel {
|
|||
}
|
||||
|
||||
void addProgressBar() {
|
||||
progressBar = new GProgressBar(null, true, true, false, 12);
|
||||
progressBar = new GProgressBar(null, true, true, false);
|
||||
progressBar.setBackgroundColor(Colors.BACKGROUND);
|
||||
add(progressBar);
|
||||
layout.clearPreferredSize();
|
||||
|
@ -71,7 +70,7 @@ public class ScheduledTaskPanel extends JPanel {
|
|||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
// This layout handles the scrolling based on the scrollOffset as set by the setHiddenViewAmount()
|
||||
// It also optionally shows the scrollbar for the task or group.
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.framework.task;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
@ -46,11 +45,11 @@ public class GProgressBarTest extends AbstractDockingTest {
|
|||
cancelled = true;
|
||||
}
|
||||
};
|
||||
progressBar = new GProgressBar(cancelledListener, true, true, true, 10.0f);
|
||||
progressBar = new GProgressBar(cancelledListener, true, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicProgress() {
|
||||
public void testBasicProgress() {
|
||||
progressBar.initialize(100);
|
||||
assertEquals(0, progressBar.getProgress());
|
||||
assertEquals(100, progressBar.getMax());
|
||||
|
@ -62,7 +61,7 @@ public class GProgressBarTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testLongValues() {
|
||||
public void testLongValues() {
|
||||
progressBar.initialize(0x400000000L);
|
||||
progressBar.setProgress(10);
|
||||
assertEquals(10, progressBar.getProgress());
|
||||
|
@ -73,7 +72,7 @@ public class GProgressBarTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMessage() {
|
||||
public void testMessage() {
|
||||
progressBar.initialize(100);
|
||||
progressBar.setMessage("Hey");
|
||||
assertEquals("Hey", progressBar.getMessage());
|
||||
|
@ -91,7 +90,7 @@ public class GProgressBarTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCancel() {
|
||||
public void testCancel() {
|
||||
progressBar.initialize(100);
|
||||
progressBar.setProgress(50);
|
||||
assertTrue(!cancelled);
|
||||
|
|
|
@ -100,7 +100,7 @@ public class SystemUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Clean the specified user name to eliminate any spaces or leading domain name
|
||||
* Clean the specified user name to eliminate any spaces or leading domain name
|
||||
* which may be present (e.g., "MyDomain\John Doe" becomes "JohnDoe").
|
||||
* @param name user name string to be cleaned-up
|
||||
* @return the clean user name
|
||||
|
@ -127,8 +127,8 @@ public class SystemUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the user that is running the application. This name may be modified to
|
||||
* eliminate any spaces or leading domain name which may be present in Java's
|
||||
* Get the user that is running the application. This name may be modified to
|
||||
* eliminate any spaces or leading domain name which may be present in Java's
|
||||
* {@code user.name} system property (see {@link #getCleanUserName(String)}).
|
||||
* @return the user name
|
||||
*/
|
||||
|
@ -173,20 +173,15 @@ public class SystemUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the font size override setting is enabled and adjusts
|
||||
* the given font as necessary to match the override setting. If the setting
|
||||
* is not enabled, then <code>font</code> is returned.
|
||||
* No longer supported. Use the theming system for fonts
|
||||
*
|
||||
* @param font
|
||||
* The current font to adjust, if necessary.
|
||||
* @return a font object with the proper size.
|
||||
* @param font the font
|
||||
* @return the same font passed in
|
||||
* @deprecated Use the theming system for fonts
|
||||
*/
|
||||
@Deprecated(since = "11.1", forRemoval = true)
|
||||
public static Font adjustForFontSizeOverride(Font font) {
|
||||
if (FONT_SIZE_OVERRIDE_VALUE == null) {
|
||||
return font;
|
||||
}
|
||||
|
||||
return font.deriveFont((float) FONT_SIZE_OVERRIDE_VALUE.intValue());
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -350,10 +345,10 @@ public class SystemUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a file that contains the given class. If the class is in a jar file, then
|
||||
* the jar file will be returned. If the file is in a .class file, then the directory
|
||||
* Returns a file that contains the given class. If the class is in a jar file, then
|
||||
* the jar file will be returned. If the file is in a .class file, then the directory
|
||||
* containing the package root will be returned (i.e. the "bin" directory).
|
||||
*
|
||||
*
|
||||
* @param classObject the class for which to get the location
|
||||
* @return the containing location
|
||||
*/
|
||||
|
|
|
@ -42,12 +42,12 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Locates the field of the name <code>fieldName</code> on the given
|
||||
* class. If the given class does not contain the field, then this
|
||||
* method will recursively call up <code>containingClass</code>'s
|
||||
* implementation tree looking for a parent implementation of the
|
||||
* Locates the field of the name <code>fieldName</code> on the given
|
||||
* class. If the given class does not contain the field, then this
|
||||
* method will recursively call up <code>containingClass</code>'s
|
||||
* implementation tree looking for a parent implementation of the
|
||||
* requested field.
|
||||
*
|
||||
*
|
||||
* @param fieldName The name of the field to locate.
|
||||
* @param containingClass The class that contains the desired field.
|
||||
* @return The Field object that matches the given name, or null if not
|
||||
|
@ -73,12 +73,12 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Locates the field of the name <code>fieldName</code> on the given
|
||||
* class. If the given class does not contain the field, then this
|
||||
* method will recursively call up <code>containingClass</code>'s
|
||||
* implementation tree looking for a parent implementation of the
|
||||
* Locates the field of the name <code>fieldName</code> on the given
|
||||
* class. If the given class does not contain the field, then this
|
||||
* method will recursively call up <code>containingClass</code>'s
|
||||
* implementation tree looking for a parent implementation of the
|
||||
* requested field.
|
||||
*
|
||||
*
|
||||
* @param fieldName The name of the field to locate.
|
||||
* @param containingClass The class that contains the desired field.
|
||||
* @return The Field object that matches the given name, or null if not
|
||||
|
@ -104,12 +104,12 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Locates the method of the name <code>methodName</code> on the given
|
||||
* class. If the given class does not contain the method, then this
|
||||
* method will recursively call up <code>containingClass</code>'s
|
||||
* implementation tree looking for a parent implementation of the
|
||||
* Locates the method of the name <code>methodName</code> on the given
|
||||
* class. If the given class does not contain the method, then this
|
||||
* method will recursively call up <code>containingClass</code>'s
|
||||
* implementation tree looking for a parent implementation of the
|
||||
* requested method.
|
||||
*
|
||||
*
|
||||
* @param methodName The name of the method to locate.
|
||||
* @param containingClass The class that contains the desired method.
|
||||
* @param parameterTypes The parameters of the desired method (may be null).
|
||||
|
@ -158,7 +158,7 @@ public class ReflectionUtilities {
|
|||
|
||||
/**
|
||||
* Get the first field specification contained within containingClass which has the type classType.
|
||||
* This method is only really useful if it is known that only a single field of
|
||||
* This method is only really useful if it is known that only a single field of
|
||||
* classType exists within the containingClass hierarchy.
|
||||
* @param classType the class
|
||||
* @param containingClass the class that contains a field of the given type
|
||||
|
@ -184,40 +184,52 @@ public class ReflectionUtilities {
|
|||
/**
|
||||
* Returns the class name of the entry in the stack that comes before all references to the
|
||||
* given classes. This is useful for figuring out at runtime who is calling a particular
|
||||
* method.
|
||||
* method.
|
||||
* <p>
|
||||
* This method can take multiple classes, but you really only need to pass the oldest
|
||||
* This method can take multiple classes, but you really only need to pass the oldest
|
||||
* class of disinterest.
|
||||
*
|
||||
*
|
||||
* @param classes the classes to ignore
|
||||
* @return the desired class name
|
||||
*/
|
||||
public static String getClassNameOlderThan(Class<?>... classes) {
|
||||
|
||||
Throwable t = createThrowableWithStackOlderThan(classes);
|
||||
StackTraceElement[] stackTrace = t.getStackTrace();
|
||||
return stackTrace[0].getClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a throwable whose stack trace is based upon the current call stack, with any
|
||||
* information coming before, and including, the given classes removed.
|
||||
* <p>
|
||||
* This method can take multiple classes, but you really only need to pass the oldest
|
||||
* class of disinterest.
|
||||
*
|
||||
* @param classes the classes to ignore
|
||||
* @return the new throwable
|
||||
* Returns the class name of the entry in the stack that comes before all references to the
|
||||
* given patterns. This is useful for figuring out at runtime who is calling a particular
|
||||
* method.
|
||||
*
|
||||
* @param patterns the patterns to ignore
|
||||
* @return the desired class name
|
||||
*/
|
||||
public static Throwable createThrowableWithStackOlderThan(Class<?>... classes) {
|
||||
public static String getClassNameOlderThan(String... patterns) {
|
||||
Throwable t = createThrowableWithStackOlderThan(patterns);
|
||||
StackTraceElement[] stackTrace = t.getStackTrace();
|
||||
return stackTrace[0].getClassName();
|
||||
}
|
||||
|
||||
List<String> toFind =
|
||||
Arrays.stream(classes).map(c -> c.getName()).collect(Collectors.toList());
|
||||
/**
|
||||
* Creates a throwable whose stack trace is based upon the current call stack, with any
|
||||
* information coming before, and including, the given patterns removed.
|
||||
*
|
||||
* @param patterns the strings to ignore (e.g., class or package names)
|
||||
* @return the new throwable
|
||||
* @see #createThrowableWithStackOlderThan(Class...)
|
||||
*/
|
||||
public static Throwable createThrowableWithStackOlderThan(String... patterns) {
|
||||
return createThrowableWithStackOlderThan(List.of(patterns));
|
||||
}
|
||||
|
||||
if (toFind.isEmpty()) {
|
||||
// Always ignore our class. We get this for free if the client passes in any
|
||||
// classes.
|
||||
toFind.add(0, ReflectionUtilities.class.getName());
|
||||
private static Throwable createThrowableWithStackOlderThan(List<String> patterns) {
|
||||
|
||||
if (patterns.isEmpty()) {
|
||||
// always ignore our class. We get this for free if the client passes in any classes
|
||||
patterns = new ArrayList<>();
|
||||
patterns.add(0, ReflectionUtilities.class.getName());
|
||||
}
|
||||
|
||||
Throwable t = new Throwable();
|
||||
|
@ -227,7 +239,7 @@ public class ReflectionUtilities {
|
|||
|
||||
StackTraceElement element = trace[i];
|
||||
String className = element.getClassName();
|
||||
int nameIndex = toFind.indexOf(className);
|
||||
int nameIndex = patterns.indexOf(className);
|
||||
if (nameIndex != -1) {
|
||||
lastIgnoreIndex = i;
|
||||
}
|
||||
|
@ -242,13 +254,13 @@ public class ReflectionUtilities {
|
|||
if (lastIgnoreIndex == -1) {
|
||||
Msg.error(ReflectionUtilities.class,
|
||||
"Change call to ReflectionUtils. Did not find the " +
|
||||
"following classes in the call stack: " + Arrays.toString(classes));
|
||||
"following patterns in the call stack: " + patterns);
|
||||
}
|
||||
|
||||
if (lastIgnoreIndex == trace.length - 1) {
|
||||
Msg.error(ReflectionUtilities.class,
|
||||
"Change call to ReflectionUtils. Call stack only contains the classes to ignore: " +
|
||||
Arrays.toString(classes));
|
||||
"Change call to ReflectionUtils. Call stack contains only ignored patterns: " +
|
||||
patterns);
|
||||
}
|
||||
|
||||
int startIndex = lastIgnoreIndex + 1;
|
||||
|
@ -258,11 +270,27 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Finds the first occurrence of the given pattern and then stops filtering when it finds
|
||||
* Creates a throwable whose stack trace is based upon the current call stack, with any
|
||||
* information coming before, and including, the given classes removed.
|
||||
* <p>
|
||||
* This method can take multiple classes, but you really only need to pass the oldest
|
||||
* class of disinterest.
|
||||
*
|
||||
* @param classes the classes to ignore
|
||||
* @return the new throwable
|
||||
*/
|
||||
public static Throwable createThrowableWithStackOlderThan(Class<?>... classes) {
|
||||
List<String> patterns =
|
||||
Arrays.stream(classes).map(c -> c.getName()).collect(Collectors.toList());
|
||||
return createThrowableWithStackOlderThan(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first occurrence of the given pattern and then stops filtering when it finds
|
||||
* something that is not that pattern
|
||||
*
|
||||
*
|
||||
* @param trace the trace to update
|
||||
* @param pattern the non-regex patterns used to perform a
|
||||
* @param pattern the non-regex patterns used to perform a
|
||||
* {@link String#contains(CharSequence)} on each {@link StackTraceElement} line
|
||||
* @return the updated trace
|
||||
*/
|
||||
|
@ -296,12 +324,12 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Uses the given <code>patterns</code> to remove elements from the given stack trace.
|
||||
* Uses the given <code>patterns</code> to remove elements from the given stack trace.
|
||||
* The current implementation will simply perform a <code>toString()</code> on each element and
|
||||
* then check to see if that string contains any of the <code>patterns</code>.
|
||||
*
|
||||
*
|
||||
* @param trace the trace to filter
|
||||
* @param patterns the non-regex patterns used to perform a
|
||||
* @param patterns the non-regex patterns used to perform a
|
||||
* {@link String#contains(CharSequence)} on each {@link StackTraceElement}
|
||||
* line.
|
||||
* @return the filtered trace
|
||||
|
@ -325,15 +353,15 @@ public class ReflectionUtilities {
|
|||
/**
|
||||
* A convenience method to create a throwable, filtering any lines that contain the given
|
||||
* non-regex patterns. This can be useful for emitting diagnostic stack traces.
|
||||
*
|
||||
* @param patterns the non-regex patterns used to perform a
|
||||
*
|
||||
* @param patterns the non-regex patterns used to perform a
|
||||
* {@link String#contains(CharSequence)} on each {@link StackTraceElement}
|
||||
* line.
|
||||
* @return the new throwable
|
||||
*/
|
||||
public static Throwable createFilteredThrowable(String... patterns) {
|
||||
|
||||
Throwable t = createThrowableWithStackOlderThan();
|
||||
Throwable t = createThrowableWithStackOlderThan(new ArrayList<>());
|
||||
StackTraceElement[] trace = t.getStackTrace();
|
||||
StackTraceElement[] filtered = filterStackTrace(trace, patterns);
|
||||
t.setStackTrace(filtered);
|
||||
|
@ -341,40 +369,39 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* A convenience method to create a throwable, filtering boiler-plate Java-related
|
||||
* lines (e.g., AWT, Swing, Security, etc).
|
||||
* A convenience method to create a throwable, filtering boiler-plate Java-related
|
||||
* lines (e.g., AWT, Swing, Security, etc).
|
||||
* This can be useful for emitting diagnostic stack traces with reduced noise.
|
||||
*
|
||||
*
|
||||
* @return the new throwable
|
||||
*/
|
||||
public static Throwable createJavaFilteredThrowable() {
|
||||
|
||||
Throwable t = createThrowableWithStackOlderThan();
|
||||
Throwable t = createThrowableWithStackOlderThan(List.of());
|
||||
return filterJavaThrowable(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to create a throwable, filtering boiler-plate Java-related
|
||||
* lines (e.g., AWT, Swing, Security, etc).
|
||||
* This can be useful for emitting diagnostic stack traces with reduced noise.
|
||||
*
|
||||
* A convenience method to create a throwable, filtering boiler-plate Java-related
|
||||
* lines (e.g., AWT, Swing, Security, etc).
|
||||
* This can be useful for emitting diagnostic stack traces with reduced noise.
|
||||
*
|
||||
* <p>This method differs from {@link #createJavaFilteredThrowable()} in that this method
|
||||
* returns a String, which is useful when printing log messages without having to directly
|
||||
* print the stack trace.
|
||||
*
|
||||
*
|
||||
* @return the new throwable
|
||||
*/
|
||||
public static String createJavaFilteredThrowableString() {
|
||||
Throwable t = createThrowableWithStackOlderThan();
|
||||
Throwable t = createThrowableWithStackOlderThan(List.of());
|
||||
Throwable filtered = filterJavaThrowable(t);
|
||||
return stackTraceToString(filtered);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to take a throwable, filter boiler-plate Java-related
|
||||
* lines (e.g., AWT, Swing, Security, etc).
|
||||
* A convenience method to take a throwable, filter boiler-plate Java-related
|
||||
* lines (e.g., AWT, Swing, Security, etc).
|
||||
* This can be useful for emitting diagnostic stack traces with reduced noise.
|
||||
*
|
||||
*
|
||||
* @param t the throwable to filter
|
||||
* @return the throwable
|
||||
*/
|
||||
|
@ -418,15 +445,15 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns an ordered set of interfaces and classes that are shared amongst the items in
|
||||
* Returns an ordered set of interfaces and classes that are shared amongst the items in
|
||||
* the list.
|
||||
* <p>
|
||||
* The order of the items is as they are first encountered, favoring interfaces before
|
||||
* The order of the items is as they are first encountered, favoring interfaces before
|
||||
* classes. Further, interface hierarchies are examined before concrete parent extensions.
|
||||
* <p>
|
||||
* If the given items have no parents in common, then the result will be a list with
|
||||
* only <code>Object.class</code>.
|
||||
*
|
||||
*
|
||||
* @param list the items to examine
|
||||
* @return the set of items
|
||||
*/
|
||||
|
@ -461,15 +488,15 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns an ordered set of parent interfaces and classes that are shared
|
||||
* Returns an ordered set of parent interfaces and classes that are shared
|
||||
* amongst the items in the list.
|
||||
* <p>
|
||||
* The order of the items is as they are first encountered, favoring interfaces before
|
||||
* The order of the items is as they are first encountered, favoring interfaces before
|
||||
* classes. Further, interface hierarchies are examined before concrete parent extensions.
|
||||
* <p>
|
||||
* If the given items have no parents in common, then the result will be a list with
|
||||
* only <code>Object.class</code>.
|
||||
*
|
||||
*
|
||||
* @param list the items to examine
|
||||
* @return the set of items
|
||||
*/
|
||||
|
@ -494,9 +521,9 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Turns the given {@link Throwable} into a String version of its
|
||||
* Turns the given {@link Throwable} into a String version of its
|
||||
* {@link Throwable#printStackTrace()} method.
|
||||
*
|
||||
*
|
||||
* @param t the throwable
|
||||
* @return the string
|
||||
*/
|
||||
|
@ -505,9 +532,9 @@ public class ReflectionUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Turns the given {@link Throwable} into a String version of its
|
||||
* Turns the given {@link Throwable} into a String version of its
|
||||
* {@link Throwable#printStackTrace()} method.
|
||||
*
|
||||
*
|
||||
* @param message the preferred message to use. If null, the throwable message will be used
|
||||
* @param t the throwable
|
||||
* @return the string
|
||||
|
@ -543,11 +570,11 @@ public class ReflectionUtilities {
|
|||
|
||||
/**
|
||||
* Returns an order set of all interfaces implemented and classes extended for the entire
|
||||
* type structure of the given class.
|
||||
* type structure of the given class.
|
||||
* <p>
|
||||
* If <code>Object.class</code> is passed to this method, then it will be returned in the
|
||||
* If <code>Object.class</code> is passed to this method, then it will be returned in the
|
||||
* result of this method.
|
||||
*
|
||||
*
|
||||
* @param c the class to introspect
|
||||
* @return the set of parents
|
||||
*/
|
||||
|
@ -581,7 +608,7 @@ public class ReflectionUtilities {
|
|||
|
||||
/**
|
||||
* Returns the type arguments for the given base class and extension.
|
||||
*
|
||||
*
|
||||
* <p>Caveat: this lookup will only work if the given child class is a concrete class that
|
||||
* has its type arguments specified. For example, these cases will work:
|
||||
* <pre>
|
||||
|
@ -592,17 +619,17 @@ public class ReflectionUtilities {
|
|||
*
|
||||
* // class definition
|
||||
* public class MyList implements List<String> {
|
||||
* </pre>
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* Whereas this case will not work:
|
||||
* <pre>
|
||||
* // local variable with the type specified
|
||||
* List<String> myList = new ArrayList<String>();
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>Note: a null entry in the result list will exist for any type that was unrecoverable
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param <T> the type of the base and child class
|
||||
* @param baseClass the base class
|
||||
* @param childClass the child class
|
||||
|
@ -618,7 +645,7 @@ public class ReflectionUtilities {
|
|||
Type baseClassAsType =
|
||||
walkClassHierarchyAndResolveTypes(baseClass, resolvedTypesDictionary, childClass);
|
||||
|
||||
// try to resolve type arguments defined by 'baseClass' to the raw runtime class
|
||||
// try to resolve type arguments defined by 'baseClass' to the raw runtime class
|
||||
Type[] baseClassDeclaredTypeArguments = getDeclaredTypeArguments(baseClassAsType);
|
||||
return resolveBaseClassTypeArguments(resolvedTypesDictionary,
|
||||
baseClassDeclaredTypeArguments);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue