       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 || "graphicsConfiguration" == 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,
     160                     BasicLookAndFeel.getFocusAcceleratorKeyMask() |
     161                             InputEvent.ALT_GRAPH_DOWN_MASK,false),
     162                     "pressed");
     163
     164             map.put(KeyStroke.getKeyStroke(m, BasicLookAndFeel.getFocusAcceleratorKeyMask(), true),
     165                     "released");
     166             map.put(KeyStroke.getKeyStroke(m,
     167                     BasicLookAndFeel.getFocusAcceleratorKeyMask() |
     168                             InputEvent.ALT_GRAPH_DOWN_MASK, true),
     169                     "released");
     170             map.put(KeyStroke.getKeyStroke(m, 0, true), "released");
     171         }
     172         else {
     173             InputMap map = SwingUtilities.getUIInputMap(b, JComponent.
     174                                              WHEN_IN_FOCUSED_WINDOW);
     175             if (map != null) {
     176                 map.clear();
     177             }
     178         }
     179     }
     180
     181     public void stateChanged(ChangeEvent e) {
     182         AbstractButton b = (AbstractButton) e.getSource();
     183         b.repaint();
     184     }
     185
     186     public void focusGained(FocusEvent e) {
     187         AbstractButton b = (AbstractButton) e.getSource();
     188         if (b instanceof JButton && ((JButton)b).isDefaultCapable()) {
     189             JRootPane root = b.getRootPane();
     190             if (root != null) {
     191                BasicButtonUI ui = (BasicButtonUI)BasicLookAndFeel.getUIOfType(
     192                          b.getUI(), BasicButtonUI.class);
     193                if (ui != null && DefaultLookup.getBoolean(b, ui,
     194                                    ui.getPropertyPrefix() +
     195                                    "defaultButtonFollowsFocus", true)) {
     196                    root.putClientProperty("temporaryDefaultButton", b);
     197                    root.setDefaultButton((JButton)b);
     198                    root.putClientProperty("temporaryDefaultButton", null);
     199                }
     200             }
     201         }
     202         b.repaint();
     203     }
     204
     205     public void focusLost(FocusEvent e) {
     206         AbstractButton b = (AbstractButton) e.getSource();
     207         JRootPane root = b.getRootPane();
     208         if (root != null) {
     209            JButton initialDefault = (JButton)root.getClientProperty("initialDefaultButton");
     210            if (b != initialDefault) {
     211                BasicButtonUI ui = (BasicButtonUI)BasicLookAndFeel.getUIOfType(
     212                          b.getUI(), BasicButtonUI.class);
     213                if (ui != null && DefaultLookup.getBoolean(b, ui,
     214                                    ui.getPropertyPrefix() +
     215                                    "defaultButtonFollowsFocus", true)) {
     216                    root.setDefaultButton(initialDefault);
     217                }
     218            }
     219         }
     220
     221         ButtonModel model = b.getModel();
     222         model.setPressed(false);
     223         model.setArmed(false);
     224         b.repaint();
     225     }
     226
     227     public void mouseMoved(MouseEvent e) {
     228     }
     229
     230
     231     public void mouseDragged(MouseEvent e) {
     232     }
     233
     234     public void mouseClicked(MouseEvent e) {
     235     }
     236
     237     public void mousePressed(MouseEvent e) {
     238        if (SwingUtilities.isLeftMouseButton(e) ) {
     239           AbstractButton b = (AbstractButton) e.getSource();
     240
     241           if(b.contains(e.getX(), e.getY())) {
     242               long lastTime = lastPressedTimestamp;
     243               lastPressedTimestamp = e.getWhen();
     244               long timeSinceLastClick = lastPressedTimestamp - lastTime;
     245               if (lastTime != -1 &&
     246                   timeSinceLastClick > 0 &&
     247                   timeSinceLastClick < b.getMultiClickThreshhold()) {
     248
     249                   shouldDiscardRelease = true;
     250                   return;
     251               }
     252
     253              ButtonModel model = b.getModel();
     254              if (!model.isEnabled()) {
     255                 // Disabled buttons ignore all input...
     256                 return;
     257              }
     258              if (!model.isArmed()) {
     259                 // button not armed, should be
     260                 model.setArmed(true);
     261              }
     262              model.setPressed(true);
     263              if(!b.hasFocus() && b.isRequestFocusEnabled()) {
     264                 b.requestFocus();
     265              }
     266           }
     267        }
     268     }
     269
     270     public void mouseReleased(MouseEvent e) {
     271         if (SwingUtilities.isLeftMouseButton(e)) {
     272             // Support for multiClickThreshhold
     273             if (shouldDiscardRelease) {
     274                 shouldDiscardRelease = false;
     275                 return;
     276             }
     277             AbstractButton b = (AbstractButton) e.getSource();
     278             ButtonModel model = b.getModel();
     279             model.setPressed(false);
     280             model.setArmed(false);
     281         }
     282     }
     283
     284     public void mouseEntered(MouseEvent e) {
     285         AbstractButton b = (AbstractButton) e.getSource();
     286         ButtonModel model = b.getModel();
     287         if (b.isRolloverEnabled() && !SwingUtilities.isLeftMouseButton(e)) {
     288             model.setRollover(true);
     289         }
     290         if (model.isPressed())
     291                 model.setArmed(true);
     292     }
     293
     294     public void mouseExited(MouseEvent e) {
     295         AbstractButton b = (AbstractButton) e.getSource();
     296         ButtonModel model = b.getModel();
     297         if(b.isRolloverEnabled()) {
     298             model.setRollover(false);
     299         }
     300         model.setArmed(false);
     301     }
     302
     303
     304     /**
     305      * Actions for Buttons. Two types of action are supported:
     306      * pressed: Moves the button to a pressed state
     307      * released: Disarms the button.
     308      */
     309     private static class Actions extends UIAction {
     310         private static final String PRESS = "pressed";
     311         private static final String RELEASE = "released";
     312
     313         Actions(String name) {
     314             super(name);
     315         }
     316
     317         public void actionPerformed(ActionEvent e) {
     318             AbstractButton b = (AbstractButton)e.getSource();
     319             String key = getName();
     320             if (key == PRESS) {
     321                 ButtonModel model = b.getModel();
     322                 model.setArmed(true);
     323                 model.setPressed(true);
     324                 if(!b.hasFocus()) {
     325                     b.requestFocus();
     326                 }
     327             }
     328             else if (key == RELEASE) {
     329                 ButtonModel model = b.getModel();
     330                 model.setPressed(false);
     331                 model.setArmed(false);
     332             }
     333         }
     334
     335         @Override
     336         public boolean accept(Object sender) {
     337             return !((sender instanceof AbstractButton) &&
     338                     !((AbstractButton)sender).getModel().isEnabled());
     339         }
     340     }
     341 }