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);
}
}