001    /* BasicButtonListener.java --
002       Copyright (C) 2004, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.plaf.basic;
040    
041    import gnu.classpath.SystemProperties;
042    
043    import java.awt.event.ActionEvent;
044    import java.awt.event.FocusEvent;
045    import java.awt.event.FocusListener;
046    import java.awt.event.MouseEvent;
047    import java.awt.event.MouseListener;
048    import java.awt.event.MouseMotionListener;
049    import java.awt.font.FontRenderContext;
050    import java.awt.font.TextLayout;
051    import java.awt.geom.AffineTransform;
052    import java.beans.PropertyChangeEvent;
053    import java.beans.PropertyChangeListener;
054    
055    import javax.swing.AbstractAction;
056    import javax.swing.AbstractButton;
057    import javax.swing.Action;
058    import javax.swing.ActionMap;
059    import javax.swing.ButtonModel;
060    import javax.swing.InputMap;
061    import javax.swing.JComponent;
062    import javax.swing.SwingUtilities;
063    import javax.swing.UIManager;
064    import javax.swing.event.ChangeEvent;
065    import javax.swing.event.ChangeListener;
066    import javax.swing.plaf.ActionMapUIResource;
067    import javax.swing.plaf.ButtonUI;
068    
069    public class BasicButtonListener
070      implements MouseListener, MouseMotionListener, FocusListener, ChangeListener,
071                 PropertyChangeListener
072    {
073      /**
074       * Implements the keyboard action for Swing buttons.
075       */
076      private class ButtonAction
077        extends AbstractAction
078      {
079        /**
080         * The key for pressed action.
081         */
082        static final String PRESSED = "pressed";
083    
084        /**
085         * The key for released action.
086         */
087        static final String RELEASED = "released";
088    
089        /**
090         * Performs the action.
091         */
092        public void actionPerformed(ActionEvent event)
093        {
094          Object cmd = getValue("__command__");
095          AbstractButton b = (AbstractButton) event.getSource();
096          ButtonModel m = b.getModel();
097          if (PRESSED.equals(cmd))
098            {
099              m.setArmed(true);
100              m.setPressed(true);
101              if (! b.isFocusOwner())
102                b.requestFocus();
103            }
104          else if (RELEASED.equals(cmd))
105            {
106              m.setPressed(false);
107              m.setArmed(false);
108            }
109        }
110    
111        /**
112         * Indicates if this action is enabled.
113         *
114         * @param source the source of the action
115         *
116         * @return <code>true</code> when enabled, <code>false</code> otherwise
117         */
118        public boolean isEnabled(Object source)
119        {
120          boolean enabled = true;
121          if (source instanceof AbstractButton)
122            {
123              AbstractButton b = (AbstractButton) source;
124              enabled = b.isEnabled();
125            }
126          return enabled;
127        }
128      }
129    
130      public BasicButtonListener(AbstractButton b)
131      {
132        // Do nothing here.
133      }
134      
135      public void propertyChange(PropertyChangeEvent e)
136      {
137        // Store the TextLayout for this in a client property for speed-up
138        // painting of the label.
139        String property = e.getPropertyName();
140        AbstractButton b = (AbstractButton) e.getSource();
141        if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
142             || property.equals("font"))
143            && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
144            == null)
145          {
146            String text = b.getText();
147            if (text == null)
148              text = "";
149            FontRenderContext frc = new FontRenderContext(new AffineTransform(),
150                                                          false, false);
151            TextLayout layout = new TextLayout(text, b.getFont(), frc);
152            b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
153    
154            // Update HTML renderer.
155            BasicHTML.updateRenderer(b, b.getText());
156          }
157        else if (property.equals(AbstractButton.CONTENT_AREA_FILLED_CHANGED_PROPERTY))
158          {
159            checkOpacity(b);
160          }
161      }
162    
163      /**
164       * Checks the <code>contentAreaFilled</code> property and updates the
165       * opaque property of the button.
166       *
167       * @param b the button to check
168       */
169      protected void checkOpacity(AbstractButton b) 
170      {    
171        b.setOpaque(b.isContentAreaFilled());
172      }
173      
174      public void focusGained(FocusEvent e) 
175      {    
176        if (e.getSource() instanceof AbstractButton)
177          {
178            AbstractButton button = (AbstractButton) e.getSource();
179            if (button.isFocusPainted())
180              button.repaint();   
181          }
182      }
183      
184      public void focusLost(FocusEvent e)
185      {
186        if (e.getSource() instanceof AbstractButton)
187          {
188            AbstractButton button = (AbstractButton) e.getSource();
189            if (button.isFocusPainted())
190              button.repaint();   
191          }
192      }
193      
194      public void installKeyboardActions(JComponent c)
195      {
196        ButtonUI ui = ((AbstractButton) c).getUI();
197        if (ui instanceof BasicButtonUI)
198          {
199            // Install InputMap.
200            BasicButtonUI basicUI = (BasicButtonUI) ui;
201            String prefix = basicUI.getPropertyPrefix(); 
202            InputMap focusInputMap =
203              (InputMap) UIManager.get(prefix + "focusInputMap");
204            SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED,
205                                             focusInputMap);
206    
207            ActionMap am = (ActionMap) UIManager.get(prefix + "actionMap");
208            if (am == null)
209              {
210                am = createDefaultActionMap();
211                UIManager.put(prefix + "actionMap", am);
212              }
213            SwingUtilities.replaceUIActionMap(c, am);
214          }
215        
216        c.getActionMap().put("pressed", 
217                             new AbstractAction() 
218                             {
219                               public void actionPerformed(ActionEvent e)          
220                               {
221                                 AbstractButton button = (AbstractButton) e.getSource();
222                                 ButtonModel model = button.getModel();
223                                 // It is important that these transitions happen in this order.
224                                 model.setArmed(true);
225                                 model.setPressed(true);
226                               }
227                             });
228        
229        c.getActionMap().put("released", 
230                             new AbstractAction() 
231                             {
232                               public void actionPerformed(ActionEvent e)          
233                               {
234                                 AbstractButton button = (AbstractButton) e.getSource();
235                                 ButtonModel model = button.getModel();
236                                 // It is important that these transitions happen in this order.
237                                 model.setPressed(false);
238                                 model.setArmed(false);
239                               }
240                           });    
241      }
242    
243      /**
244       * Creates and returns the default action map for Swing buttons.
245       *
246       * @return the default action map for Swing buttons
247       */
248      private ActionMap createDefaultActionMap()
249      {
250        Action action = new ButtonAction();
251        ActionMapUIResource am = new ActionMapUIResource();
252        am.put(ButtonAction.PRESSED, action);
253        am.put(ButtonAction.RELEASED, action);
254        return am;
255      }
256    
257      public void uninstallKeyboardActions(JComponent c)
258      {
259        SwingUtilities.replaceUIActionMap(c, null);
260        SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
261      }
262      
263      public void stateChanged(ChangeEvent e)
264      {
265        // Need to repaint when the button state changes.
266        ((AbstractButton) e.getSource()).repaint();
267      }
268      
269      public void mouseMoved(MouseEvent e)
270      {
271        // Nothing to do here.
272      }
273      
274      public void mouseDragged(MouseEvent e)
275      {
276        // Nothing to do here.
277      }
278      
279      public void mouseClicked(MouseEvent e)
280      {
281        // Nothing to do here.
282      }
283    
284      /**
285       * Accept a mouse press event and arm the button.
286       *
287       * @param e The mouse press event to accept
288       */
289      public void mousePressed(MouseEvent e)
290      {
291        if (e.getSource() instanceof AbstractButton)
292          {
293            AbstractButton button = (AbstractButton) e.getSource();
294            ButtonModel model = button.getModel();
295            if (SwingUtilities.isLeftMouseButton(e))
296              {
297                // It is important that these transitions happen in this order.
298                model.setArmed(true);
299                model.setPressed(true);
300    
301                if (! button.isFocusOwner() && button.isRequestFocusEnabled())
302                  button.requestFocus();
303              }
304          }
305      }
306    
307      /**
308       * Accept a mouse release event and set the button's 
309       * "pressed" property to <code>true</code>, if the model
310       * is armed. If the model is not armed, ignore the event.
311       *
312       * @param e The mouse release event to accept
313       */
314      public void mouseReleased(MouseEvent e)
315      {
316        if (e.getSource() instanceof AbstractButton)
317          {
318            AbstractButton button = (AbstractButton) e.getSource();
319            ButtonModel model = button.getModel();
320            if (e.getButton() == MouseEvent.BUTTON1)
321              {
322                // It is important that these transitions happen in this order.
323                model.setPressed(false);
324                model.setArmed(false);
325              }
326          }
327      }
328    
329      /**
330       * Accept a mouse enter event and set the button's "rollover" property to
331       * <code>true</code>, if the button's "rolloverEnabled" property is
332       * <code>true</code>. If the button is currently armed and the mouse
333       * button is not held down, this enter event will also disarm the model.
334       *
335       * @param e The mouse enter event to accept
336       */
337      public void mouseEntered(MouseEvent e)
338      {
339        if (e.getSource() instanceof AbstractButton)
340          {
341            AbstractButton button = (AbstractButton) e.getSource();
342            ButtonModel model = button.getModel();
343            if (button.isRolloverEnabled()
344                && ! SwingUtilities.isLeftMouseButton(e))
345              model.setRollover(true);
346    
347            if (model.isPressed())
348              model.setArmed(true);
349          }
350      }
351    
352      /**
353       * Accept a mouse exit event and set the button's model's "rollover"
354       * property to <code>false</code>, if it's "rolloverEnabled" property is
355       * <code>true</code>. Also disarm the button.
356       *
357       * @param e The mouse exit event to accept
358       */
359      public void mouseExited(MouseEvent e)
360      {
361        if (e.getSource() instanceof AbstractButton)
362          {
363            AbstractButton button = (AbstractButton) e.getSource();
364            ButtonModel model = button.getModel();
365            if (button.isRolloverEnabled())
366              model.setRollover(false);
367            model.setArmed(false);
368          }
369      }
370    }