You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
594 lines
20 KiB
594 lines
20 KiB
/*
|
|
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.swing.plaf.basic;
|
|
|
|
import sun.swing.SwingUtilities2;
|
|
import sun.swing.DefaultLookup;
|
|
import sun.swing.UIAction;
|
|
import sun.awt.AppContext;
|
|
|
|
import javax.swing.*;
|
|
import javax.swing.plaf.*;
|
|
import javax.swing.text.View;
|
|
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.Component;
|
|
import java.awt.Container;
|
|
import java.awt.Dimension;
|
|
import java.awt.Rectangle;
|
|
import java.awt.Insets;
|
|
import java.awt.Color;
|
|
import java.awt.Graphics;
|
|
import java.awt.Font;
|
|
import java.awt.FontMetrics;
|
|
import java.beans.PropertyChangeEvent;
|
|
import java.beans.PropertyChangeListener;
|
|
|
|
/**
|
|
* A Windows L&F implementation of LabelUI. This implementation
|
|
* is completely static, i.e. there's only one UIView implementation
|
|
* that's shared by all JLabel objects.
|
|
*
|
|
* @author Hans Muller
|
|
*/
|
|
public class BasicLabelUI extends LabelUI implements PropertyChangeListener
|
|
{
|
|
/**
|
|
* The default <code>BasicLabelUI</code> instance. This field might
|
|
* not be used. To change the default instance use a subclass which
|
|
* overrides the <code>createUI</code> method, and place that class
|
|
* name in defaults table under the key "LabelUI".
|
|
*/
|
|
protected static BasicLabelUI labelUI = new BasicLabelUI();
|
|
private static final Object BASIC_LABEL_UI_KEY = new Object();
|
|
|
|
private Rectangle paintIconR = new Rectangle();
|
|
private Rectangle paintTextR = new Rectangle();
|
|
|
|
static void loadActionMap(LazyActionMap map) {
|
|
map.put(new Actions(Actions.PRESS));
|
|
map.put(new Actions(Actions.RELEASE));
|
|
}
|
|
|
|
/**
|
|
* Forwards the call to SwingUtilities.layoutCompoundLabel().
|
|
* This method is here so that a subclass could do Label specific
|
|
* layout and to shorten the method name a little.
|
|
*
|
|
* @param label an instance of {@code JLabel}
|
|
* @param fontMetrics a font metrics
|
|
* @param text a text
|
|
* @param icon an icon
|
|
* @param viewR a bounding rectangle to lay out label
|
|
* @param iconR a bounding rectangle to lay out icon
|
|
* @param textR a bounding rectangle to lay out text
|
|
* @return a possibly clipped version of the compound labels string
|
|
* @see SwingUtilities#layoutCompoundLabel
|
|
*/
|
|
protected String layoutCL(
|
|
JLabel label,
|
|
FontMetrics fontMetrics,
|
|
String text,
|
|
Icon icon,
|
|
Rectangle viewR,
|
|
Rectangle iconR,
|
|
Rectangle textR)
|
|
{
|
|
return SwingUtilities.layoutCompoundLabel(
|
|
(JComponent) label,
|
|
fontMetrics,
|
|
text,
|
|
icon,
|
|
label.getVerticalAlignment(),
|
|
label.getHorizontalAlignment(),
|
|
label.getVerticalTextPosition(),
|
|
label.getHorizontalTextPosition(),
|
|
viewR,
|
|
iconR,
|
|
textR,
|
|
label.getIconTextGap());
|
|
}
|
|
|
|
/**
|
|
* Paint clippedText at textX, textY with the labels foreground color.
|
|
*
|
|
* @param l an instance of {@code JLabel}
|
|
* @param g an instance of {@code Graphics}
|
|
* @param s a text
|
|
* @param textX an X coordinate
|
|
* @param textY an Y coordinate
|
|
* @see #paint
|
|
* @see #paintDisabledText
|
|
*/
|
|
protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY)
|
|
{
|
|
int mnemIndex = l.getDisplayedMnemonicIndex();
|
|
g.setColor(l.getForeground());
|
|
SwingUtilities2.drawStringUnderlineCharAt(l, g, s, mnemIndex,
|
|
textX, textY);
|
|
}
|
|
|
|
|
|
/**
|
|
* Paint clippedText at textX, textY with background.lighter() and then
|
|
* shifted down and to the right by one pixel with background.darker().
|
|
*
|
|
* @param l an instance of {@code JLabel}
|
|
* @param g an instance of {@code Graphics}
|
|
* @param s a text
|
|
* @param textX an X coordinate
|
|
* @param textY an Y coordinate
|
|
* @see #paint
|
|
* @see #paintEnabledText
|
|
*/
|
|
protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY)
|
|
{
|
|
int accChar = l.getDisplayedMnemonicIndex();
|
|
Color background = l.getBackground();
|
|
g.setColor(background.brighter());
|
|
SwingUtilities2.drawStringUnderlineCharAt(l, g, s, accChar,
|
|
textX + 1, textY + 1);
|
|
g.setColor(background.darker());
|
|
SwingUtilities2.drawStringUnderlineCharAt(l, g, s, accChar,
|
|
textX, textY);
|
|
}
|
|
|
|
/**
|
|
* Paints the label text with the foreground color, if the label is opaque
|
|
* then paints the entire background with the background color. The Label
|
|
* text is drawn by {@link #paintEnabledText} or {@link #paintDisabledText}.
|
|
* The locations of the label parts are computed by {@link #layoutCL}.
|
|
*
|
|
* @see #paintEnabledText
|
|
* @see #paintDisabledText
|
|
* @see #layoutCL
|
|
*/
|
|
public void paint(Graphics g, JComponent c)
|
|
{
|
|
JLabel label = (JLabel)c;
|
|
String text = label.getText();
|
|
Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon();
|
|
|
|
if ((icon == null) && (text == null)) {
|
|
return;
|
|
}
|
|
|
|
FontMetrics fm = SwingUtilities2.getFontMetrics(label, g);
|
|
String clippedText = layout(label, fm, c.getWidth(), c.getHeight());
|
|
|
|
if (icon != null) {
|
|
icon.paintIcon(c, g, paintIconR.x, paintIconR.y);
|
|
}
|
|
|
|
if (text != null) {
|
|
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
|
|
if (v != null) {
|
|
v.paint(g, paintTextR);
|
|
} else {
|
|
int textX = paintTextR.x;
|
|
int textY = paintTextR.y + fm.getAscent();
|
|
|
|
if (label.isEnabled()) {
|
|
paintEnabledText(label, g, clippedText, textX, textY);
|
|
}
|
|
else {
|
|
paintDisabledText(label, g, clippedText, textX, textY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private String layout(JLabel label, FontMetrics fm,
|
|
int width, int height) {
|
|
Insets insets = label.getInsets(null);
|
|
String text = label.getText();
|
|
Icon icon = (label.isEnabled()) ? label.getIcon() :
|
|
label.getDisabledIcon();
|
|
Rectangle paintViewR = new Rectangle();
|
|
paintViewR.x = insets.left;
|
|
paintViewR.y = insets.top;
|
|
paintViewR.width = width - (insets.left + insets.right);
|
|
paintViewR.height = height - (insets.top + insets.bottom);
|
|
paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
|
|
paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
|
|
return layoutCL(label, fm, text, icon, paintViewR, paintIconR,
|
|
paintTextR);
|
|
}
|
|
|
|
public Dimension getPreferredSize(JComponent c)
|
|
{
|
|
JLabel label = (JLabel)c;
|
|
String text = label.getText();
|
|
Icon icon = (label.isEnabled()) ? label.getIcon() :
|
|
label.getDisabledIcon();
|
|
Insets insets = label.getInsets(null);
|
|
Font font = label.getFont();
|
|
|
|
int dx = insets.left + insets.right;
|
|
int dy = insets.top + insets.bottom;
|
|
|
|
if ((icon == null) &&
|
|
((text == null) ||
|
|
((text != null) && (font == null)))) {
|
|
return new Dimension(dx, dy);
|
|
}
|
|
else if ((text == null) || ((icon != null) && (font == null))) {
|
|
return new Dimension(icon.getIconWidth() + dx,
|
|
icon.getIconHeight() + dy);
|
|
}
|
|
else {
|
|
FontMetrics fm = label.getFontMetrics(font);
|
|
Rectangle iconR = new Rectangle();
|
|
Rectangle textR = new Rectangle();
|
|
Rectangle viewR = new Rectangle();
|
|
|
|
iconR.x = iconR.y = iconR.width = iconR.height = 0;
|
|
textR.x = textR.y = textR.width = textR.height = 0;
|
|
viewR.x = dx;
|
|
viewR.y = dy;
|
|
viewR.width = viewR.height = Short.MAX_VALUE;
|
|
|
|
layoutCL(label, fm, text, icon, viewR, iconR, textR);
|
|
int x1 = Math.min(iconR.x, textR.x);
|
|
int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width);
|
|
int y1 = Math.min(iconR.y, textR.y);
|
|
int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height);
|
|
Dimension rv = new Dimension(x2 - x1, y2 - y1);
|
|
|
|
rv.width += dx;
|
|
rv.height += dy;
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @return getPreferredSize(c)
|
|
*/
|
|
public Dimension getMinimumSize(JComponent c) {
|
|
Dimension d = getPreferredSize(c);
|
|
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
|
|
if (v != null) {
|
|
d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
|
|
}
|
|
return d;
|
|
}
|
|
|
|
/**
|
|
* @return getPreferredSize(c)
|
|
*/
|
|
public Dimension getMaximumSize(JComponent c) {
|
|
Dimension d = getPreferredSize(c);
|
|
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
|
|
if (v != null) {
|
|
d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
|
|
}
|
|
return d;
|
|
}
|
|
|
|
/**
|
|
* Returns the baseline.
|
|
*
|
|
* @throws NullPointerException {@inheritDoc}
|
|
* @throws IllegalArgumentException {@inheritDoc}
|
|
* @see javax.swing.JComponent#getBaseline(int, int)
|
|
* @since 1.6
|
|
*/
|
|
public int getBaseline(JComponent c, int width, int height) {
|
|
super.getBaseline(c, width, height);
|
|
JLabel label = (JLabel)c;
|
|
String text = label.getText();
|
|
if (text == null || "".equals(text) || label.getFont() == null) {
|
|
return -1;
|
|
}
|
|
FontMetrics fm = label.getFontMetrics(label.getFont());
|
|
layout(label, fm, width, height);
|
|
return BasicHTML.getBaseline(label, paintTextR.y, fm.getAscent(),
|
|
paintTextR.width, paintTextR.height);
|
|
}
|
|
|
|
/**
|
|
* Returns an enum indicating how the baseline of the component
|
|
* changes as the size changes.
|
|
*
|
|
* @throws NullPointerException {@inheritDoc}
|
|
* @see javax.swing.JComponent#getBaseline(int, int)
|
|
* @since 1.6
|
|
*/
|
|
public Component.BaselineResizeBehavior getBaselineResizeBehavior(
|
|
JComponent c) {
|
|
super.getBaselineResizeBehavior(c);
|
|
if (c.getClientProperty(BasicHTML.propertyKey) != null) {
|
|
return Component.BaselineResizeBehavior.OTHER;
|
|
}
|
|
switch(((JLabel)c).getVerticalAlignment()) {
|
|
case JLabel.TOP:
|
|
return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
|
|
case JLabel.BOTTOM:
|
|
return Component.BaselineResizeBehavior.CONSTANT_DESCENT;
|
|
case JLabel.CENTER:
|
|
return Component.BaselineResizeBehavior.CENTER_OFFSET;
|
|
}
|
|
return Component.BaselineResizeBehavior.OTHER;
|
|
}
|
|
|
|
|
|
public void installUI(JComponent c) {
|
|
installDefaults((JLabel)c);
|
|
installComponents((JLabel)c);
|
|
installListeners((JLabel)c);
|
|
installKeyboardActions((JLabel)c);
|
|
}
|
|
|
|
|
|
public void uninstallUI(JComponent c) {
|
|
uninstallDefaults((JLabel) c);
|
|
uninstallComponents((JLabel) c);
|
|
uninstallListeners((JLabel) c);
|
|
uninstallKeyboardActions((JLabel) c);
|
|
}
|
|
|
|
/**
|
|
* Installs default properties.
|
|
*
|
|
* @param c an instance of {@code JLabel}
|
|
*/
|
|
protected void installDefaults(JLabel c){
|
|
LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground", "Label.font");
|
|
LookAndFeel.installProperty(c, "opaque", Boolean.FALSE);
|
|
}
|
|
|
|
/**
|
|
* Registers listeners.
|
|
*
|
|
* @param c an instance of {@code JLabel}
|
|
*/
|
|
protected void installListeners(JLabel c){
|
|
c.addPropertyChangeListener(this);
|
|
}
|
|
|
|
/**
|
|
* Registers components.
|
|
*
|
|
* @param c an instance of {@code JLabel}
|
|
*/
|
|
protected void installComponents(JLabel c){
|
|
BasicHTML.updateRenderer(c, c.getText());
|
|
c.setInheritsPopupMenu(true);
|
|
}
|
|
|
|
/**
|
|
* Registers keyboard actions.
|
|
*
|
|
* @param l an instance of {@code JLabel}
|
|
*/
|
|
protected void installKeyboardActions(JLabel l) {
|
|
int dka = l.getDisplayedMnemonic();
|
|
Component lf = l.getLabelFor();
|
|
if ((dka != 0) && (lf != null)) {
|
|
LazyActionMap.installLazyActionMap(l, BasicLabelUI.class,
|
|
"Label.actionMap");
|
|
InputMap inputMap = SwingUtilities.getUIInputMap
|
|
(l, JComponent.WHEN_IN_FOCUSED_WINDOW);
|
|
if (inputMap == null) {
|
|
inputMap = new ComponentInputMapUIResource(l);
|
|
SwingUtilities.replaceUIInputMap(l,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap);
|
|
}
|
|
inputMap.clear();
|
|
inputMap.put(KeyStroke.getKeyStroke(dka, BasicLookAndFeel.getFocusAcceleratorKeyMask(), false), "press");
|
|
}
|
|
else {
|
|
InputMap inputMap = SwingUtilities.getUIInputMap
|
|
(l, JComponent.WHEN_IN_FOCUSED_WINDOW);
|
|
if (inputMap != null) {
|
|
inputMap.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Uninstalls default properties.
|
|
*
|
|
* @param c an instance of {@code JLabel}
|
|
*/
|
|
protected void uninstallDefaults(JLabel c){
|
|
}
|
|
|
|
/**
|
|
* Unregisters listeners.
|
|
*
|
|
* @param c an instance of {@code JLabel}
|
|
*/
|
|
protected void uninstallListeners(JLabel c){
|
|
c.removePropertyChangeListener(this);
|
|
}
|
|
|
|
/**
|
|
* Unregisters components.
|
|
*
|
|
* @param c an instance of {@code JLabel}
|
|
*/
|
|
protected void uninstallComponents(JLabel c){
|
|
BasicHTML.updateRenderer(c, "");
|
|
}
|
|
|
|
/**
|
|
* Unregisters keyboard actions.
|
|
*
|
|
* @param c an instance of {@code JLabel}
|
|
*/
|
|
protected void uninstallKeyboardActions(JLabel c) {
|
|
SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
|
|
SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_IN_FOCUSED_WINDOW,
|
|
null);
|
|
SwingUtilities.replaceUIActionMap(c, null);
|
|
}
|
|
|
|
/**
|
|
* Returns an instance of {@code BasicLabelUI}.
|
|
*
|
|
* @param c a component
|
|
* @return an instance of {@code BasicLabelUI}
|
|
*/
|
|
public static ComponentUI createUI(JComponent c) {
|
|
if (System.getSecurityManager() != null) {
|
|
AppContext appContext = AppContext.getAppContext();
|
|
BasicLabelUI safeBasicLabelUI =
|
|
(BasicLabelUI) appContext.get(BASIC_LABEL_UI_KEY);
|
|
if (safeBasicLabelUI == null) {
|
|
safeBasicLabelUI = new BasicLabelUI();
|
|
appContext.put(BASIC_LABEL_UI_KEY, safeBasicLabelUI);
|
|
}
|
|
return safeBasicLabelUI;
|
|
}
|
|
return labelUI;
|
|
}
|
|
|
|
public void propertyChange(PropertyChangeEvent e) {
|
|
String name = e.getPropertyName();
|
|
if (name == "text" || "font" == name || "foreground" == name) {
|
|
// remove the old html view client property if one
|
|
// existed, and install a new one if the text installed
|
|
// into the JLabel is html source.
|
|
JLabel lbl = ((JLabel) e.getSource());
|
|
String text = lbl.getText();
|
|
BasicHTML.updateRenderer(lbl, text);
|
|
}
|
|
else if (name == "labelFor" || name == "displayedMnemonic") {
|
|
installKeyboardActions((JLabel) e.getSource());
|
|
}
|
|
}
|
|
|
|
// When the accelerator is pressed, temporarily make the JLabel
|
|
// focusTraversable by registering a WHEN_FOCUSED action for the
|
|
// release of the accelerator. Then give it focus so it can
|
|
// prevent unwanted keyTyped events from getting to other components.
|
|
private static class Actions extends UIAction {
|
|
private static final String PRESS = "press";
|
|
private static final String RELEASE = "release";
|
|
|
|
Actions(String key) {
|
|
super(key);
|
|
}
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
JLabel label = (JLabel)e.getSource();
|
|
String key = getName();
|
|
if (key == PRESS) {
|
|
doPress(label);
|
|
}
|
|
else if (key == RELEASE) {
|
|
doRelease(label, e.getActionCommand() != null);
|
|
}
|
|
}
|
|
|
|
private void doPress(JLabel label) {
|
|
Component labelFor = label.getLabelFor();
|
|
if (labelFor != null && labelFor.isEnabled()) {
|
|
InputMap inputMap = SwingUtilities.getUIInputMap(label, JComponent.WHEN_FOCUSED);
|
|
if (inputMap == null) {
|
|
inputMap = new InputMapUIResource();
|
|
SwingUtilities.replaceUIInputMap(label, JComponent.WHEN_FOCUSED, inputMap);
|
|
}
|
|
int dka = label.getDisplayedMnemonic();
|
|
putOnRelease(inputMap, dka, BasicLookAndFeel
|
|
.getFocusAcceleratorKeyMask());
|
|
// Need this when the sticky keys are enabled
|
|
putOnRelease(inputMap, dka, 0);
|
|
// Need this if ALT is released before the accelerator
|
|
putOnRelease(inputMap, KeyEvent.VK_ALT, 0);
|
|
label.requestFocus();
|
|
}
|
|
}
|
|
|
|
private void doRelease(JLabel label, boolean isCommand) {
|
|
Component labelFor = label.getLabelFor();
|
|
if (labelFor != null && labelFor.isEnabled()) {
|
|
if (label.hasFocus()) {
|
|
InputMap inputMap = SwingUtilities.getUIInputMap(label,
|
|
JComponent.WHEN_FOCUSED);
|
|
if (inputMap != null) {
|
|
// inputMap should never be null.
|
|
int dka = label.getDisplayedMnemonic();
|
|
removeOnRelease(inputMap, dka, BasicLookAndFeel
|
|
.getFocusAcceleratorKeyMask());
|
|
removeOnRelease(inputMap, dka, 0);
|
|
removeOnRelease(inputMap, KeyEvent.VK_ALT, 0);
|
|
}
|
|
inputMap = SwingUtilities.getUIInputMap(label,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
|
if (inputMap == null) {
|
|
inputMap = new InputMapUIResource();
|
|
SwingUtilities.replaceUIInputMap(label,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap);
|
|
}
|
|
int dka = label.getDisplayedMnemonic();
|
|
if (isCommand) {
|
|
putOnRelease(inputMap, KeyEvent.VK_ALT, 0);
|
|
} else {
|
|
putOnRelease(inputMap, dka, BasicLookAndFeel
|
|
.getFocusAcceleratorKeyMask());
|
|
// Need this when the sticky keys are enabled
|
|
putOnRelease(inputMap, dka, 0);
|
|
}
|
|
if (labelFor instanceof Container &&
|
|
((Container) labelFor).isFocusCycleRoot()) {
|
|
labelFor.requestFocus();
|
|
} else {
|
|
SwingUtilities2.compositeRequestFocus(labelFor);
|
|
}
|
|
} else {
|
|
InputMap inputMap = SwingUtilities.getUIInputMap(label,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
|
int dka = label.getDisplayedMnemonic();
|
|
if (inputMap != null) {
|
|
if (isCommand) {
|
|
removeOnRelease(inputMap, dka, BasicLookAndFeel
|
|
.getFocusAcceleratorKeyMask());
|
|
removeOnRelease(inputMap, dka, 0);
|
|
} else {
|
|
removeOnRelease(inputMap, KeyEvent.VK_ALT, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void putOnRelease(InputMap inputMap, int keyCode, int modifiers) {
|
|
inputMap.put(KeyStroke.getKeyStroke(keyCode, modifiers, true),
|
|
RELEASE);
|
|
}
|
|
|
|
private void removeOnRelease(InputMap inputMap, int keyCode, int modifiers) {
|
|
inputMap.remove(KeyStroke.getKeyStroke(keyCode, modifiers, true));
|
|
}
|
|
|
|
}
|
|
}
|