From ef46024fb56db94c1c4b5496fa00bb7b117804ae Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Tue, 22 Aug 2006 11:23:56 +0000 Subject: 2006-08-22 Roman Kennke * javax/swing/AbstractButton.java (ButtonChangeListener.stateChanged): Delegate to combined handler. (EventHandler): New inner class. Handles all three types of events on the model. (eventHandler): New field. Stores the combined event handler. (AbstractButton): Moved listener initialization to setModel(). (createActionListener): Return combined handler. (createChangeListener): Return combined handler. (createItemListener): Return combined handler. (getEventHandler): New helper method for creating the combined handler. (setModel): Initialize listeners here. * javax/swing/plaf/basic/BasicButtonListener.java (ButtonAction): New class. Implements the keyboard action for buttons. (checkOpacity): Implemented. (createDefaultActionMap): New helper method. (installKeyboardActions): Rewritten to install InputMap and ActionMap according to 'new' keyboard input method. (mouseClicked): Commented as no-op. (mouseDragged): Commented as no-op. (mouseMoved): Commented as no-op. (propertyChange): Check for contentAreaFilled change and update opacity. Pull handling of HTLM in font and text handler. (stateChanged): Repaint button. (uninstallKeyboardActions): Properly uninstall keyboard actions. * javax/swing/plaf/basic/BasicButtonUI.java (listener): Removed. (sharedListener): New static field. Stores the shared listener. (sharedUI): New static field. Stores the shared UI. (createButtonListener): Return shared instance here. (createUI): Return shared instance here. (getButtonListener): New helper method. Looks for the BasicButtonListener installed on a button and returns it. (installDefaults): Correctly install rollover property here. Fetch defaultTextShiftOffset. Initialize opaqueness correctly. (installKeyboardActions): Fetch listener with new helper method. (installListeners): Don't use removed field. Check for null. (installUI): Added comment about order of method invocations. (uninstallDefaults): Don't uninstall non-uninstallable properties. (uninstallKeyboardActions): Fetch listener with new helper method. (uninstallListeners): Fetch listener with new helper method. (paintIcon): Paint icon offset when pressed and armed. * javax/swing/plaf/metal/MetalButtonListener.java: Removed. * javax/swing/plaf/metal/MetalButtonUI.java (sharedUI): New field. Stores the shared UI. (MetalButtonUI): Don't initialize fields here. (createButtonListener): Removed method. Use super impl. (createUI): Return shared instance. (getDisabledTextColor): Update field here. (getFocusColor): Update field here. (getSelectColor): Update field here. (installDefaults): Don't handle rollover property here. (uninstallDefaults): Don't handle rollover property here. (paintButtonPressed): Use accessor method to update the field value. --- javax/swing/plaf/basic/BasicButtonListener.java | 134 ++++++++++++++++++++--- javax/swing/plaf/basic/BasicButtonUI.java | 138 ++++++++++++++++++------ 2 files changed, 226 insertions(+), 46 deletions(-) (limited to 'javax/swing/plaf/basic') diff --git a/javax/swing/plaf/basic/BasicButtonListener.java b/javax/swing/plaf/basic/BasicButtonListener.java index 042192b62..c99de2c70 100644 --- a/javax/swing/plaf/basic/BasicButtonListener.java +++ b/javax/swing/plaf/basic/BasicButtonListener.java @@ -54,15 +54,79 @@ import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.AbstractButton; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.ButtonModel; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ButtonUI; -public class BasicButtonListener implements MouseListener, MouseMotionListener, - FocusListener, ChangeListener, PropertyChangeListener +public class BasicButtonListener + implements MouseListener, MouseMotionListener, FocusListener, ChangeListener, + PropertyChangeListener { + /** + * Implements the keyboard action for Swing buttons. + */ + private class ButtonAction + extends AbstractAction + { + /** + * The key for pressed action. + */ + static final String PRESSED = "pressed"; + + /** + * The key for released action. + */ + static final String RELEASED = "released"; + + /** + * Performs the action. + */ + public void actionPerformed(ActionEvent event) + { + Object cmd = getValue("__command__"); + AbstractButton b = (AbstractButton) event.getSource(); + ButtonModel m = b.getModel(); + if (PRESSED.equals(cmd)) + { + m.setArmed(true); + m.setPressed(true); + if (! b.isFocusOwner()) + b.requestFocus(); + } + else if (RELEASED.equals(cmd)) + { + m.setPressed(false); + m.setArmed(false); + } + } + + /** + * Indicates if this action is enabled. + * + * @param source the source of the action + * + * @return true when enabled, false otherwise + */ + public boolean isEnabled(Object source) + { + boolean enabled = true; + if (source instanceof AbstractButton) + { + AbstractButton b = (AbstractButton) source; + enabled = b.isEnabled(); + } + return enabled; + } + } + public BasicButtonListener(AbstractButton b) { // Do nothing here. @@ -86,16 +150,25 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, false, false); TextLayout layout = new TextLayout(text, b.getFont(), frc); b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout); + + // Update HTML renderer. + BasicHTML.updateRenderer(b, b.getText()); } - if (property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)) + else if (property.equals(AbstractButton.CONTENT_AREA_FILLED_CHANGED_PROPERTY)) { - BasicHTML.updateRenderer(b, b.getText()); + checkOpacity(b); } } - + + /** + * Checks the contentAreaFilled property and updates the + * opaque property of the button. + * + * @param b the button to check + */ protected void checkOpacity(AbstractButton b) { - // TODO: What should be done here? + b.setOpaque(b.isContentAreaFilled()); } public void focusGained(FocusEvent e) @@ -120,6 +193,26 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, public void installKeyboardActions(JComponent c) { + ButtonUI ui = ((AbstractButton) c).getUI(); + if (ui instanceof BasicButtonUI) + { + // Install InputMap. + BasicButtonUI basicUI = (BasicButtonUI) ui; + String prefix = basicUI.getPropertyPrefix(); + InputMap focusInputMap = + (InputMap) UIManager.get(prefix + "focusInputMap"); + SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, + focusInputMap); + + ActionMap am = (ActionMap) UIManager.get(prefix + "actionMap"); + if (am == null) + { + am = createDefaultActionMap(); + UIManager.put(prefix + "actionMap", am); + } + SwingUtilities.replaceUIActionMap(c, am); + } + c.getActionMap().put("pressed", new AbstractAction() { @@ -146,31 +239,46 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, } }); } - + + /** + * Creates and returns the default action map for Swing buttons. + * + * @return the default action map for Swing buttons + */ + private ActionMap createDefaultActionMap() + { + Action action = new ButtonAction(); + ActionMapUIResource am = new ActionMapUIResource(); + am.put(ButtonAction.PRESSED, action); + am.put(ButtonAction.RELEASED, action); + return am; + } + public void uninstallKeyboardActions(JComponent c) { - c.getActionMap().put("pressed", null); - c.getActionMap().put("released", null); + SwingUtilities.replaceUIActionMap(c, null); + SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null); } public void stateChanged(ChangeEvent e) { - // TODO: What should be done here, if anything? + // Need to repaint when the button state changes. + ((AbstractButton) e.getSource()).repaint(); } public void mouseMoved(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } public void mouseDragged(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } public void mouseClicked(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** diff --git a/javax/swing/plaf/basic/BasicButtonUI.java b/javax/swing/plaf/basic/BasicButtonUI.java index 96b11a00d..e2493d156 100644 --- a/javax/swing/plaf/basic/BasicButtonUI.java +++ b/javax/swing/plaf/basic/BasicButtonUI.java @@ -44,11 +44,11 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; +import java.beans.PropertyChangeListener; import javax.swing.AbstractButton; import javax.swing.ButtonModel; import javax.swing.Icon; -import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.LookAndFeel; @@ -82,6 +82,16 @@ public class BasicButtonUI extends ButtonUI */ static Rectangle textR = new Rectangle(); + /** + * 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. @@ -106,7 +116,9 @@ public class BasicButtonUI extends ButtonUI */ public static ComponentUI createUI(final JComponent c) { - return new BasicButtonUI(); + if (sharedUI == null) + sharedUI = new BasicButtonUI(); + return sharedUI; } /** @@ -173,14 +185,29 @@ public class BasicButtonUI extends ButtonUI 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")); - b.setIconTextGap(UIManager.getInt(prefix + "textIconGap")); - b.setInputMap(JComponent.WHEN_FOCUSED, - (InputMap) UIManager.get(prefix + "focusInputMap")); + + // 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); } /** @@ -190,21 +217,10 @@ public class BasicButtonUI extends ButtonUI */ protected void uninstallDefaults(AbstractButton b) { - if (b.getFont() instanceof UIResource) - b.setFont(null); - if (b.getForeground() instanceof UIResource) - b.setForeground(null); - if (b.getBackground() instanceof UIResource) - b.setBackground(null); - if (b.getBorder() instanceof UIResource) - b.setBorder(null); - b.setIconTextGap(defaultTextIconGap); - if (b.getMargin() instanceof UIResource) - b.setMargin(null); + // The other properties aren't uninstallable. + LookAndFeel.uninstallBorder(b); } - protected BasicButtonListener listener; - /** * Creates and returns a new instance of {@link BasicButtonListener}. This * method provides a hook to make it easy for subclasses to install a @@ -216,7 +232,13 @@ public class BasicButtonUI extends ButtonUI */ protected BasicButtonListener createButtonListener(AbstractButton b) { - return new BasicButtonListener(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; } /** @@ -226,12 +248,15 @@ public class BasicButtonUI extends ButtonUI */ protected void installListeners(AbstractButton b) { - listener = createButtonListener(b); - b.addChangeListener(listener); - b.addPropertyChangeListener(listener); - b.addFocusListener(listener); - b.addMouseListener(listener); - b.addMouseMotionListener(listener); + BasicButtonListener listener = createButtonListener(b); + if (listener != null) + { + b.addChangeListener(listener); + b.addPropertyChangeListener(listener); + b.addFocusListener(listener); + b.addMouseListener(listener); + b.addMouseMotionListener(listener); + } } /** @@ -241,21 +266,29 @@ public class BasicButtonUI extends ButtonUI */ protected void uninstallListeners(AbstractButton b) { - b.removeChangeListener(listener); - b.removePropertyChangeListener(listener); - b.removeFocusListener(listener); - b.removeMouseListener(listener); - b.removeMouseMotionListener(listener); + 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) { - listener.installKeyboardActions(b); + BasicButtonListener listener = getButtonListener(b); + if (listener != null) + listener.installKeyboardActions(b); } protected void uninstallKeyboardActions(AbstractButton b) { - listener.uninstallKeyboardActions(b); + BasicButtonListener listener = getButtonListener(b); + if (listener != null) + listener.uninstallKeyboardActions(b); } /** @@ -273,6 +306,9 @@ public class BasicButtonUI extends ButtonUI { 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()); @@ -480,7 +516,16 @@ public class BasicButtonUI extends ButtonUI Icon i = currentIcon(b); if (i != null) - i.paintIcon(c, g, iconRect.x, iconRect.y); + { + 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); + } } /** @@ -549,4 +594,31 @@ public class BasicButtonUI extends ButtonUI textRect.y + fm.getAscent()); } } + + /** + * 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; + } } -- cgit v1.2.1