summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java')
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java636
1 files changed, 636 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
new file mode 100644
index 000000000..1697c24d9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
@@ -0,0 +1,636 @@
+/* BasicButtonUI.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ButtonUI;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.text.View;
+
+/**
+ * A UI delegate for the {@link JButton} component.
+ */
+public class BasicButtonUI extends ButtonUI
+{
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle viewR = new Rectangle();
+
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle iconR = new Rectangle();
+
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle textR = new Rectangle();
+
+ /**
+ * Cached Insets instance, used in paint().
+ */
+ static Insets cachedInsets;
+
+ /**
+ * The shared button UI.
+ */
+ private static BasicButtonUI sharedUI;
+
+ /**
+ * The shared BasicButtonListener.
+ */
+ private static BasicButtonListener sharedListener;
+
+ /**
+ * A constant used to pad out elements in the button's layout and
+ * preferred size calculations.
+ */
+ protected int defaultTextIconGap = 4;
+
+ /**
+ * A constant added to the defaultTextIconGap to adjust the text
+ * within this particular button.
+ */
+ protected int defaultTextShiftOffset;
+
+ private int textShiftOffset;
+
+ /**
+ * Factory method to create an instance of BasicButtonUI for a given
+ * {@link JComponent}, which should be an {@link AbstractButton}.
+ *
+ * @param c The component.
+ *
+ * @return A new UI capable of drawing the component
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ if (sharedUI == null)
+ sharedUI = new BasicButtonUI();
+ return sharedUI;
+ }
+
+ /**
+ * Returns the default gap between the button's text and icon (in pixels).
+ *
+ * @param b the button (ignored).
+ *
+ * @return The gap.
+ */
+ public int getDefaultTextIconGap(AbstractButton b)
+ {
+ return defaultTextIconGap;
+ }
+
+ /**
+ * Sets the text shift offset to zero.
+ *
+ * @see #setTextShiftOffset()
+ */
+ protected void clearTextShiftOffset()
+ {
+ textShiftOffset = 0;
+ }
+
+ /**
+ * Returns the text shift offset.
+ *
+ * @return The text shift offset.
+ *
+ * @see #clearTextShiftOffset()
+ * @see #setTextShiftOffset()
+ */
+ protected int getTextShiftOffset()
+ {
+ return textShiftOffset;
+ }
+
+ /**
+ * Sets the text shift offset to the value in {@link #defaultTextShiftOffset}.
+ *
+ * @see #clearTextShiftOffset()
+ */
+ protected void setTextShiftOffset()
+ {
+ textShiftOffset = defaultTextShiftOffset;
+ }
+
+ /**
+ * Returns the prefix for the UI defaults property for this UI class.
+ * This is 'Button' for this class.
+ *
+ * @return the prefix for the UI defaults property
+ */
+ protected String getPropertyPrefix()
+ {
+ return "Button.";
+ }
+
+ /**
+ * Installs the default settings.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void installDefaults(AbstractButton b)
+ {
+ String prefix = getPropertyPrefix();
+ // Install colors and font.
+ LookAndFeel.installColorsAndFont(b, prefix + "background",
+ prefix + "foreground", prefix + "font");
+ // Install border.
+ LookAndFeel.installBorder(b, prefix + "border");
+
+ // Install margin property.
+ if (b.getMargin() == null || b.getMargin() instanceof UIResource)
+ b.setMargin(UIManager.getInsets(prefix + "margin"));
+
+ // Install rollover property.
+ Object rollover = UIManager.get(prefix + "rollover");
+ if (rollover != null)
+ LookAndFeel.installProperty(b, "rolloverEnabled", rollover);
+
+ // Fetch default textShiftOffset.
+ defaultTextShiftOffset = UIManager.getInt(prefix + "textShiftOffset");
+
+ // Make button opaque if needed.
+ if (b.isContentAreaFilled())
+ LookAndFeel.installProperty(b, "opaque", Boolean.TRUE);
+ else
+ LookAndFeel.installProperty(b, "opaque", Boolean.FALSE);
+ }
+
+ /**
+ * Removes the defaults added by {@link #installDefaults(AbstractButton)}.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void uninstallDefaults(AbstractButton b)
+ {
+ // The other properties aren't uninstallable.
+ LookAndFeel.uninstallBorder(b);
+ }
+
+ /**
+ * Creates and returns a new instance of {@link BasicButtonListener}. This
+ * method provides a hook to make it easy for subclasses to install a
+ * different listener.
+ *
+ * @param b the button.
+ *
+ * @return A new listener.
+ */
+ protected BasicButtonListener createButtonListener(AbstractButton b)
+ {
+ // Note: The RI always returns a new instance here. However,
+ // the BasicButtonListener class is perfectly suitable to be shared
+ // between multiple buttons, so we return a shared instance here
+ // for efficiency.
+ if (sharedListener == null)
+ sharedListener = new BasicButtonListener(b);
+ return sharedListener;
+ }
+
+ /**
+ * Installs listeners for the button.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void installListeners(AbstractButton b)
+ {
+ BasicButtonListener listener = createButtonListener(b);
+ if (listener != null)
+ {
+ b.addChangeListener(listener);
+ b.addPropertyChangeListener(listener);
+ b.addFocusListener(listener);
+ b.addMouseListener(listener);
+ b.addMouseMotionListener(listener);
+ }
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ listener.propertyChange(new PropertyChangeEvent(b, "font", null,
+ b.getFont()));
+ }
+
+ /**
+ * Uninstalls listeners for the button.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void uninstallListeners(AbstractButton b)
+ {
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ {
+ b.removeChangeListener(listener);
+ b.removePropertyChangeListener(listener);
+ b.removeFocusListener(listener);
+ b.removeMouseListener(listener);
+ b.removeMouseMotionListener(listener);
+ }
+ }
+
+ protected void installKeyboardActions(AbstractButton b)
+ {
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.installKeyboardActions(b);
+ }
+
+ protected void uninstallKeyboardActions(AbstractButton b)
+ {
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.uninstallKeyboardActions(b);
+ }
+
+ /**
+ * Install the BasicButtonUI as the UI for a particular component.
+ * This means registering all the UI's listeners with the component,
+ * and setting any properties of the button which are particular to
+ * this look and feel.
+ *
+ * @param c The component to install the UI into
+ */
+ public void installUI(final JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ installDefaults(b);
+ // It is important to install the listeners before installing
+ // the keyboard actions, because the keyboard actions
+ // are actually installed on the listener instance.
+ installListeners(b);
+ installKeyboardActions(b);
+ BasicHTML.updateRenderer(b, b.getText());
+ }
+ }
+
+ /**
+ * Uninstalls the UI from the component.
+ *
+ * @param c the component from which to uninstall the UI
+ */
+ public void uninstallUI(JComponent c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ uninstallKeyboardActions(b);
+ uninstallListeners(b);
+ uninstallDefaults(b);
+ BasicHTML.updateRenderer(b, "");
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
+ }
+ }
+
+ /**
+ * Calculates the minimum size for the specified component.
+ *
+ * @param c the component for which to compute the minimum size
+ *
+ * @return the minimum size for the specified component
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension size = getPreferredSize(c);
+ // When the HTML view has a minimum width different from the preferred
+ // width, then substract this here accordingly. The height is not
+ // affected by that.
+ View html = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ size.width -= html.getPreferredSpan(View.X_AXIS)
+ - html.getPreferredSpan(View.X_AXIS);
+ }
+ return size;
+ }
+
+ /**
+ * Calculates the maximum size for the specified component.
+ *
+ * @param c the component for which to compute the maximum size
+ *
+ * @return the maximum size for the specified component
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension size = getPreferredSize(c);
+ // When the HTML view has a maximum width different from the preferred
+ // width, then add this here accordingly. The height is not
+ // affected by that.
+ View html = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ size.width += html.getMaximumSpan(View.X_AXIS)
+ - html.getPreferredSpan(View.X_AXIS);
+ }
+ return size;
+ }
+
+ /**
+ * Calculate the preferred size of this component, by delegating to
+ * {@link BasicGraphicsUtils#getPreferredButtonSize}.
+ *
+ * @param c The component to measure
+ *
+ * @return The preferred dimensions of the component
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Dimension d = BasicGraphicsUtils.getPreferredButtonSize(b,
+ b.getIconTextGap());
+ return d;
+ }
+
+ static Icon currentIcon(AbstractButton b)
+ {
+ Icon i = b.getIcon();
+ ButtonModel model = b.getModel();
+
+ if (model.isPressed() && b.getPressedIcon() != null && b.isEnabled())
+ i = b.getPressedIcon();
+
+ else if (model.isRollover())
+ {
+ if (b.isSelected() && b.getRolloverSelectedIcon() != null)
+ i = b.getRolloverSelectedIcon();
+ else if (b.getRolloverIcon() != null)
+ i = b.getRolloverIcon();
+ }
+
+ else if (b.isSelected() && b.isEnabled())
+ {
+ if (b.isEnabled() && b.getSelectedIcon() != null)
+ i = b.getSelectedIcon();
+ else if (b.getDisabledSelectedIcon() != null)
+ i = b.getDisabledSelectedIcon();
+ }
+
+ else if (! b.isEnabled() && b.getDisabledIcon() != null)
+ i = b.getDisabledIcon();
+
+ return i;
+ }
+
+ /**
+ * Paint the component, which is an {@link AbstractButton}, according to
+ * its current state.
+ *
+ * @param g The graphics context to paint with
+ * @param c The component to paint the state of
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+
+ Insets i = c.getInsets(cachedInsets);
+ viewR.x = i.left;
+ viewR.y = i.top;
+ viewR.width = c.getWidth() - i.left - i.right;
+ viewR.height = c.getHeight() - i.top - i.bottom;
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+
+ Font f = c.getFont();
+ g.setFont(f);
+ Icon icon = b.getIcon();
+ String text = b.getText();
+ text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
+ text, icon,
+ b.getVerticalAlignment(),
+ b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(),
+ viewR, iconR, textR,
+ text == null ? 0
+ : b.getIconTextGap());
+
+ ButtonModel model = b.getModel();
+ if (model.isArmed() && model.isPressed())
+ paintButtonPressed(g, b);
+
+ if (icon != null)
+ paintIcon(g, c, iconR);
+ if (text != null)
+ {
+ View html = (View) b.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ html.paint(g, textR);
+ else
+ paintText(g, b, textR, text);
+ }
+ if (b.isFocusOwner() && b.isFocusPainted())
+ paintFocus(g, b, viewR, textR, iconR);
+ }
+
+ /**
+ * Paint any focus decoration this {@link JComponent} might have. The
+ * component, which in this case will be an {@link AbstractButton},
+ * should only have focus decoration painted if it has the focus, and its
+ * "focusPainted" property is <code>true</code>.
+ *
+ * @param g Graphics context to paint with
+ * @param b Button to paint the focus of
+ * @param vr Visible rectangle, the area in which to paint
+ * @param tr Text rectangle, contained in visible rectangle
+ * @param ir Icon rectangle, contained in visible rectangle
+ *
+ * @see AbstractButton#isFocusPainted()
+ * @see JComponent#hasFocus()
+ */
+ protected void paintFocus(Graphics g, AbstractButton b, Rectangle vr,
+ Rectangle tr, Rectangle ir)
+ {
+ // In the BasicLookAndFeel no focus border is drawn. This can be
+ // overridden in subclasses to implement such behaviour.
+ }
+
+ /**
+ * Paint the icon for this component. Depending on the state of the
+ * component and the availability of the button's various icon
+ * properties, this might mean painting one of several different icons.
+ *
+ * @param g Graphics context to paint with
+ * @param c Component to paint the icon of
+ * @param iconRect Rectangle in which the icon should be painted
+ */
+ protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Icon i = currentIcon(b);
+
+ if (i != null)
+ {
+ ButtonModel m = b.getModel();
+ if (m.isPressed() && m.isArmed())
+ {
+ int offs = getTextShiftOffset();
+ i.paintIcon(c, g, iconRect.x + offs, iconRect.y + offs);
+ }
+ else
+ i.paintIcon(c, g, iconRect.x, iconRect.y);
+ }
+ }
+
+ /**
+ * Paints the background area of an {@link AbstractButton} in the pressed
+ * state. This means filling the supplied area with a darker than normal
+ * background.
+ *
+ * @param g The graphics context to paint with
+ * @param b The button to paint the state of
+ */
+ protected void paintButtonPressed(Graphics g, AbstractButton b)
+ {
+ if (b.isContentAreaFilled() && b.isOpaque())
+ {
+ Rectangle area = new Rectangle();
+ SwingUtilities.calculateInnerArea(b, area);
+ g.setColor(UIManager.getColor(getPropertyPrefix() + "shadow"));
+ g.fillRect(area.x, area.y, area.width, area.height);
+ }
+ }
+
+ /**
+ * Paints the "text" property of an {@link AbstractButton}.
+ *
+ * @param g The graphics context to paint with
+ * @param c The component to paint the state of
+ * @param textRect The area in which to paint the text
+ * @param text The text to paint
+ */
+ protected void paintText(Graphics g, JComponent c, Rectangle textRect,
+ String text)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Font f = b.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+
+ if (b.isEnabled())
+ {
+ g.setColor(b.getForeground());
+ // FIXME: Underline mnemonic.
+ BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
+ textRect.y + fm.getAscent());
+ }
+ else
+ {
+ String prefix = getPropertyPrefix();
+ g.setColor(UIManager.getColor(prefix + "disabledText"));
+ // FIXME: Underline mnemonic.
+ BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
+ textRect.y + fm.getAscent());
+ }
+ }
+
+ /**
+ * Paints the "text" property of an {@link AbstractButton}.
+ *
+ * @param g The graphics context to paint with
+ * @param b The button to paint the state of
+ * @param textRect The area in which to paint the text
+ * @param text The text to paint
+ *
+ * @since 1.4
+ */
+ protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
+ String text)
+ {
+ paintText(g, (JComponent) b, textRect, text);
+ }
+
+ /**
+ * A helper method that finds the BasicButtonListener for the specified
+ * button. This is there because this UI class is stateless and
+ * shared for all buttons, and thus can't store the listener
+ * as instance field. (We store our shared instance in sharedListener,
+ * however, subclasses may override createButtonListener() and we would
+ * be lost in this case).
+ *
+ * @param b the button
+ *
+ * @return the UI event listener
+ */
+ private BasicButtonListener getButtonListener(AbstractButton b)
+ {
+ // The listener gets installed as PropertyChangeListener,
+ // so look for it in the list of property change listeners.
+ PropertyChangeListener[] listeners = b.getPropertyChangeListeners();
+ BasicButtonListener l = null;
+ for (int i = 0; listeners != null && l == null && i < listeners.length;
+ i++)
+ {
+ if (listeners[i] instanceof BasicButtonListener)
+ l = (BasicButtonListener) listeners[i];
+ }
+ return l;
+ }
+}