1 /*
   2  * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.plaf.basic;
  27 
  28 import sun.swing.DefaultLookup;
  29 import sun.swing.UIAction;
  30 import java.awt.*;
  31 import java.awt.event.*;
  32 import java.beans.*;
  33 import javax.swing.*;
  34 import javax.swing.event.*;
  35 import javax.swing.plaf.ActionMapUIResource;
  36 import javax.swing.plaf.ButtonUI;
  37 import javax.swing.plaf.ComponentInputMapUIResource;
  38 
  39 /**
  40  * Button Listener
  41  *
  42  * @author Jeff Dinkins
  43  * @author Arnaud Weber (keyboard UI support)
  44  */
  45 
  46 public class BasicButtonListener implements MouseListener, MouseMotionListener,
  47                                    FocusListener, ChangeListener, PropertyChangeListener
  48 {
  49     private long lastPressedTimestamp = -1;
  50     private boolean shouldDiscardRelease = false;
  51 
  52     /**
  53      * Populates Buttons actions.
  54      */
  55     static void loadActionMap(LazyActionMap map) {
  56         map.put(new Actions(Actions.PRESS));
  57         map.put(new Actions(Actions.RELEASE));
  58     }
  59 
  60 
  61     /**
  62      * Constructs a new instance of {@code BasicButtonListener}.
  63      *
  64      * @param b an abstract button
  65      */
  66     public BasicButtonListener(AbstractButton b) {
  67     }
  68 
  69     public void propertyChange(PropertyChangeEvent e) {
  70         String prop = e.getPropertyName();
  71         if(prop == AbstractButton.MNEMONIC_CHANGED_PROPERTY) {
  72             updateMnemonicBinding((AbstractButton)e.getSource());
  73         }
  74         else if(prop == AbstractButton.CONTENT_AREA_FILLED_CHANGED_PROPERTY) {
  75             checkOpacity((AbstractButton) e.getSource() );
  76         }
  77         else if(prop == AbstractButton.TEXT_CHANGED_PROPERTY ||
  78                 "font" == prop || "foreground" == prop ||
  79                 "ancestor" == prop || "graphicsConfig" == prop) {
  80             AbstractButton b = (AbstractButton) e.getSource();
  81             BasicHTML.updateRenderer(b, b.getText());
  82         }
  83     }
  84 
  85     /**
  86      * Checks the opacity of the {@code AbstractButton}.
  87      *
  88      * @param b an abstract button
  89      */
  90     protected void checkOpacity(AbstractButton b) {
  91         b.setOpaque( b.isContentAreaFilled() );
  92     }
  93 
  94     /**
  95      * Register default key actions: pressing space to "click" a
  96      * button and registering the keyboard mnemonic (if any).
  97      *
  98      * @param c a component
  99      */
 100     public void installKeyboardActions(JComponent c) {
 101         AbstractButton b = (AbstractButton)c;
 102         // Update the mnemonic binding.
 103         updateMnemonicBinding(b);
 104 
 105         LazyActionMap.installLazyActionMap(c, BasicButtonListener.class,
 106                                            "Button.actionMap");
 107 
 108         InputMap km = getInputMap(JComponent.WHEN_FOCUSED, c);
 109 
 110         SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, km);
 111     }
 112 
 113     /**
 114      * Unregister default key actions.
 115      *
 116      * @param c a component
 117      */
 118     public void uninstallKeyboardActions(JComponent c) {
 119         SwingUtilities.replaceUIInputMap(c, JComponent.
 120                                          WHEN_IN_FOCUSED_WINDOW, null);
 121         SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
 122         SwingUtilities.replaceUIActionMap(c, null);
 123     }
 124 
 125     /**
 126      * Returns the InputMap for condition <code>condition</code>. Called as
 127      * part of <code>installKeyboardActions</code>.
 128      */
 129     InputMap getInputMap(int condition, JComponent c) {
 130         if (condition == JComponent.WHEN_FOCUSED) {
 131             BasicButtonUI ui = (BasicButtonUI)BasicLookAndFeel.getUIOfType(
 132                          ((AbstractButton)c).getUI(), BasicButtonUI.class);
 133             if (ui != null) {
 134                 return (InputMap)DefaultLookup.get(
 135                              c, ui, ui.getPropertyPrefix() + "focusInputMap");
 136             }
 137         }
 138         return null;
 139     }
 140 
 141     /**
 142      * Resets the binding for the mnemonic in the WHEN_IN_FOCUSED_WINDOW
 143      * UI InputMap.
 144      */
 145     void updateMnemonicBinding(AbstractButton b) {
 146         int m = b.getMnemonic();
 147         if(m != 0) {
 148             InputMap map = SwingUtilities.getUIInputMap(
 149                                 b, JComponent.WHEN_IN_FOCUSED_WINDOW);
 150 
 151             if (map == null) {
 152                 map = new ComponentInputMapUIResource(b);
 153                 SwingUtilities.replaceUIInputMap(b,
 154                                JComponent.WHEN_IN_FOCUSED_WINDOW, map);
 155             }
 156             map.clear();
 157             map.put(KeyStroke.getKeyStroke(m, BasicLookAndFeel.getFocusAcceleratorKeyMask(), false),
 158                     "pressed");
 159             map.put(KeyStroke.getKeyStroke(m, BasicLookAndFeel.getFocusAcceleratorKeyMask(), true),
 160                     "released");
 161             map.put(KeyStroke.getKeyStroke(m, 0, true), "released");
 162         }
 163         else {
 164             InputMap map = SwingUtilities.getUIInputMap(b, JComponent.
 165                                              WHEN_IN_FOCUSED_WINDOW);
 166             if (map != null) {
 167                 map.clear();
 168             }
 169         }
 170     }
 171 
 172     public void stateChanged(ChangeEvent e) {
 173         AbstractButton b = (AbstractButton) e.getSource();
 174         b.repaint();
 175     }
 176 
 177     public void focusGained(FocusEvent e) {
 178         AbstractButton b = (AbstractButton) e.getSource();
 179         if (b instanceof JButton && ((JButton)b).isDefaultCapable()) {
 180             JRootPane root = b.getRootPane();
 181             if (root != null) {
 182                BasicButtonUI ui = (BasicButtonUI)BasicLookAndFeel.getUIOfType(
 183                          b.getUI(), BasicButtonUI.class);
 184                if (ui != null && DefaultLookup.getBoolean(b, ui,
 185                                    ui.getPropertyPrefix() +
 186                                    "defaultButtonFollowsFocus", true)) {
 187                    root.putClientProperty("temporaryDefaultButton", b);
 188                    root.setDefaultButton((JButton)b);
 189                    root.putClientProperty("temporaryDefaultButton", null);
 190                }
 191             }
 192         }
 193         b.repaint();
 194     }
 195 
 196     public void focusLost(FocusEvent e) {
 197         AbstractButton b = (AbstractButton) e.getSource();
 198         JRootPane root = b.getRootPane();
 199         if (root != null) {
 200            JButton initialDefault = (JButton)root.getClientProperty("initialDefaultButton");
 201            if (b != initialDefault) {
 202                BasicButtonUI ui = (BasicButtonUI)BasicLookAndFeel.getUIOfType(
 203                          b.getUI(), BasicButtonUI.class);
 204                if (ui != null && DefaultLookup.getBoolean(b, ui,
 205                                    ui.getPropertyPrefix() +
 206                                    "defaultButtonFollowsFocus", true)) {
 207                    root.setDefaultButton(initialDefault);
 208                }
 209            }
 210         }
 211 
 212         ButtonModel model = b.getModel();
 213         model.setPressed(false);
 214         model.setArmed(false);
 215         b.repaint();
 216     }
 217 
 218     public void mouseMoved(MouseEvent e) {
 219     }
 220 
 221 
 222     public void mouseDragged(MouseEvent e) {
 223     }
 224 
 225     public void mouseClicked(MouseEvent e) {
 226     }
 227 
 228     public void mousePressed(MouseEvent e) {
 229        if (SwingUtilities.isLeftMouseButton(e) ) {
 230           AbstractButton b = (AbstractButton) e.getSource();
 231 
 232           if(b.contains(e.getX(), e.getY())) {
 233               long lastTime = lastPressedTimestamp;
 234               lastPressedTimestamp = e.getWhen();
 235               long timeSinceLastClick = lastPressedTimestamp - lastTime;
 236               if (lastTime != -1 &&
 237                   timeSinceLastClick > 0 &&
 238                   timeSinceLastClick < b.getMultiClickThreshhold()) {
 239 
 240                   shouldDiscardRelease = true;
 241                   return;
 242               }
 243 
 244              ButtonModel model = b.getModel();
 245              if (!model.isEnabled()) {
 246                 // Disabled buttons ignore all input...
 247                 return;
 248              }
 249              if (!model.isArmed()) {
 250                 // button not armed, should be
 251                 model.setArmed(true);
 252              }
 253              model.setPressed(true);
 254              if(!b.hasFocus() && b.isRequestFocusEnabled()) {
 255                 b.requestFocus();
 256              }
 257           }
 258        }
 259     }
 260 
 261     public void mouseReleased(MouseEvent e) {
 262         if (SwingUtilities.isLeftMouseButton(e)) {
 263             // Support for multiClickThreshhold
 264             if (shouldDiscardRelease) {
 265                 shouldDiscardRelease = false;
 266                 return;
 267             }
 268             AbstractButton b = (AbstractButton) e.getSource();
 269             ButtonModel model = b.getModel();
 270             model.setPressed(false);
 271             model.setArmed(false);
 272         }
 273     }
 274 
 275     public void mouseEntered(MouseEvent e) {
 276         AbstractButton b = (AbstractButton) e.getSource();
 277         ButtonModel model = b.getModel();
 278         if (b.isRolloverEnabled() && !SwingUtilities.isLeftMouseButton(e)) {
 279             model.setRollover(true);
 280         }
 281         if (model.isPressed())
 282                 model.setArmed(true);
 283     }
 284 
 285     public void mouseExited(MouseEvent e) {
 286         AbstractButton b = (AbstractButton) e.getSource();
 287         ButtonModel model = b.getModel();
 288         if(b.isRolloverEnabled()) {
 289             model.setRollover(false);
 290         }
 291         model.setArmed(false);
 292     }
 293 
 294 
 295     /**
 296      * Actions for Buttons. Two types of action are supported:
 297      * pressed: Moves the button to a pressed state
 298      * released: Disarms the button.
 299      */
 300     private static class Actions extends UIAction {
 301         private static final String PRESS = "pressed";
 302         private static final String RELEASE = "released";
 303 
 304         Actions(String name) {
 305             super(name);
 306         }
 307 
 308         public void actionPerformed(ActionEvent e) {
 309             AbstractButton b = (AbstractButton)e.getSource();
 310             String key = getName();
 311             if (key == PRESS) {
 312                 ButtonModel model = b.getModel();
 313                 model.setArmed(true);
 314                 model.setPressed(true);
 315                 if(!b.hasFocus()) {
 316                     b.requestFocus();
 317                 }
 318             }
 319             else if (key == RELEASE) {
 320                 ButtonModel model = b.getModel();
 321                 model.setPressed(false);
 322                 model.setArmed(false);
 323             }
 324         }
 325 
 326         @Override
 327         public boolean accept(Object sender) {
 328             return !((sender instanceof AbstractButton) &&
 329                     !((AbstractButton)sender).getModel().isEnabled());
 330         }
 331     }
 332 }