package com.onaro.util.jfc.collapsible; import java.awt.Cursor; import java.net.URL; import java.util.ResourceBundle; import javax.swing.Action; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * A button who's text behaves like a hyperlink. Note that if the arrow is too narrow for the text, * it will be wrapped into multiple lines. *

* *

* Implemented by wrapping the text within HTML tags. The hyperlink effect is achieved by using the A tag. */ public class HyperlinkButton extends JButton { private static final long serialVersionUID = 1L; /** * HTML document which is used when the mouse isn't over the button. HTML is used to enable the text wrapping * that is done by the HTML of the rollover text. */ private String plainText; /** * HTML document which is used when the mouse is over the button. This HTML is using the A tag to create the * hyperlink effect. */ private String rolloverText; private boolean displayLinkAlways = false; /** * Helps preventing the text from flickering when the button changes from "normal" to "rollover" and the opposite. * Used to ignore calls to {@link HyperlinkButton#repaint()} and {@link HyperlinkButton#invalidate()} which are * invoked by {@link JButton#setText(String)}. */ private boolean textIsChanging = false; private final static Logger logger = LogManager.getLogger(HyperlinkButton.class); /** * Constructs a button with text and a single icon that will be used for normal, mouse-over and pressed modes. * Icons are resources and are searched for in the classpath, see {@link Class#getResource(String)}. * @param text button label * @param icon icon file-name for the normal, mouse-over and pressed modes */ public HyperlinkButton(String text, String icon) { this(text, getIcon(null, icon), null, null); } /** * Constructs a button with text and a single icon that will be used for normal, mouse-over and pressed modes. * Icons are resources and are searched for in the classpath, see {@link Class#getResource(String)}. * @param text button label * @param prefix when attached to the icon, it provides the icon file-name, may be null * @param icon icon file-name */ public HyperlinkButton(String text, String prefix, String icon) { this(text, getIcon(prefix, icon), null, null); } /** * Constructs a button with text and different icons for normal, mouse-over and pressed modes. * Icons are resources and are searched for in the classpath, see {@link Class#getResource(String)}. * @param text button label * @param prefix when attached to of the icons, provides the icon file-name, may be null * @param icon normal mode icon * @param rollover mouse-over icon * @param pressed button-pressed icon */ public HyperlinkButton(String text, String prefix, String icon, String rollover, String pressed) { this(text, getIcon(prefix, icon), getIcon(prefix, rollover), getIcon(prefix, pressed)); } /** * Constructs a button with text and different icons for normal, mouse-over and pressed modes. * @param text button label * @param icon normal mode icon * @param rollover mouse-over icon * @param pressed button-pressed icon */ public HyperlinkButton(String text, Icon icon, Icon rollover, Icon pressed) { super(text, icon); init(); if (rollover != null) setRolloverIcon(rollover); if (pressed != null) setPressedIcon(pressed); } /** * Constructs a button with text and different icons for normal, mouse-over and pressed modes. * * @param action to be performed when the button is clicked */ public HyperlinkButton(Action action){ super(action); init(); } private void init() { setHorizontalAlignment(JButton.LEFT); setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); setContentAreaFilled(false); setBorderPainted(false); setFocusPainted(false); setBorder(null); setIconTextGap(2); registerTextChange(); } /** * Changes the button's text according to its rollover state (mouse over). */ protected void registerTextChange() { getModel().addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (displayLinkAlways) { return; } if (getModel().isRollover()) setShownText(rolloverText); else setShownText(plainText); } }); } /** * Avoids the text from filckering when the button changes from "normal" to "rollover" and the oposite by ignoring * calls which are invoked by {@link JButton#setText(String)}. */ public void revalidate() { if (!textIsChanging) super.revalidate(); } /** * Avoids the text from filckering when the button changes from "normal" to "rollover" and the oposite by ignoring * calls which are invoked by {@link JButton#setText(String)}. */ public void repaint() { if (!textIsChanging) super.repaint(); } /** * Sets the text currently shown by the button. This text changes when the mouse rolls over the button * @param text the string to display */ protected void setShownText(String text){ textIsChanging = true; super.setText(text); textIsChanging = false; } /** * Sets the button's text label. Consructs the HTML documents providing the hyperlink and text-wrapp effects. * @param text the label */ public void setText(String text) { plainText = Messages.INSTANCE.getPlainText(text); rolloverText = toLink(text, false); setShownText(plainText); } public String toLink(String text, boolean bold) { return bold ? Messages.INSTANCE.getHRefBoldText(text): Messages.INSTANCE.getHRefNoBoldText(text); } public boolean isDisplayLinkAlways() { return displayLinkAlways; } public void setDisplayLinkAlways(boolean displayLinkAlways) { this.displayLinkAlways = displayLinkAlways; if (displayLinkAlways) { setShownText(rolloverText); } else { setShownText(plainText); } } /** * Gets an icon-file-name string from a resource-file where the key is the concatenation of the prefix and the suffix. * @param resources a resource file from which to fetch the string * @param prefix key prefix * @param suffix key suffix * @return the string from the resource file or null if no such resource was found */ public static String getIconFileName(ResourceBundle resources, String prefix, String suffix) { try { return resources.getString(prefix + suffix); } catch (Exception e) { return null; // No icon was specified } } /** * A utility for fetching an icon from the classpath. * @param prefix resource-name prefix * @param name resource-name suffix * @return the icon or null if the icon was found */ private static Icon getIcon(String prefix, String name) { if (name == null) return null; String iconName = prefix != null ? prefix + name : name; URL resource = HyperlinkButton.class.getResource(iconName); if (resource == null) { logger.error("Failed to find icon named: " + iconName, new IllegalArgumentException("Failed to find icon: prefix='" + prefix + "' ; name='" + name + '\'')); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return null; } else return new ImageIcon(resource); } }