001    /* MetalButtonUI.java
002       Copyright (C) 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.metal;
040    
041    import java.awt.Color;
042    import java.awt.Component;
043    import java.awt.Font;
044    import java.awt.FontMetrics;
045    import java.awt.Graphics;
046    import java.awt.Rectangle;
047    
048    import javax.swing.AbstractButton;
049    import javax.swing.ButtonModel;
050    import javax.swing.JButton;
051    import javax.swing.JComponent;
052    import javax.swing.JToolBar;
053    import javax.swing.SwingConstants;
054    import javax.swing.UIManager;
055    import javax.swing.plaf.ComponentUI;
056    import javax.swing.plaf.UIResource;
057    import javax.swing.plaf.basic.BasicButtonUI;
058    
059    /**
060     * A UI delegate for the {@link JButton} component.
061     *
062     * @author Roman Kennke (roman@kennke.org)
063     */
064    public class MetalButtonUI
065      extends BasicButtonUI
066    {
067    
068      /**
069       * The shared button UI.
070       */
071      private static MetalButtonUI sharedUI;
072    
073      /**
074       * The color used to draw the focus rectangle around the text and/or icon.
075       */
076      protected Color focusColor;
077        
078      /**
079       * The background color for the button when it is pressed.
080       */
081      protected Color selectColor;
082    
083      /**
084       * The color for disabled button labels.
085       */
086      protected Color disabledTextColor;
087    
088      /**
089       * Returns a UI delegate for the specified component.
090       * 
091       * @param c  the component (should be a subclass of {@link AbstractButton}).
092       * 
093       * @return A new instance of <code>MetalButtonUI</code>.
094       */
095      public static ComponentUI createUI(JComponent c) 
096      {
097        if (sharedUI == null)
098          sharedUI = new MetalButtonUI();
099        return sharedUI;
100      }
101    
102      /**
103       * Creates a new instance.
104       */
105      public MetalButtonUI()
106      {
107        super();
108      }
109    
110      /**
111       * Returns the color for the focus border.
112       *
113       * @return the color for the focus border
114       */
115      protected Color getFocusColor()
116      {
117        focusColor = UIManager.getColor(getPropertyPrefix() + "focus");
118        return focusColor;
119      }
120    
121      /**
122       * Returns the color that indicates a selected button.
123       *
124       * @return the color that indicates a selected button
125       */
126      protected Color getSelectColor()
127      {
128        selectColor = UIManager.getColor(getPropertyPrefix() + "select");
129        return selectColor;
130      }
131    
132      /**
133       * Returns the color for the text label of disabled buttons.
134       *
135       * @return the color for the text label of disabled buttons
136       */
137      protected Color getDisabledTextColor()
138      {
139        disabledTextColor = UIManager.getColor(getPropertyPrefix()
140                                               + "disabledText");
141        return disabledTextColor;
142      }
143    
144      /**
145       * Installs the default settings for the specified button.
146       * 
147       * @param button  the button.
148       * 
149       * @see #uninstallDefaults(AbstractButton)
150       */
151      public void installDefaults(AbstractButton button)
152      {
153        // This is overridden to be public, for whatever reason.
154        super.installDefaults(button);
155      }
156    
157      /**
158       * Removes the defaults added by {@link #installDefaults(AbstractButton)}.
159       */
160      public void uninstallDefaults(AbstractButton button) 
161      {
162        // This is overridden to be public, for whatever reason.
163        super.uninstallDefaults(button);
164      }
165    
166      /**
167       * Paints the background of the button to indicate that it is in the
168       * "pressed" state.
169       * 
170       * @param g  the graphics context.
171       * @param b  the button.
172       */
173      protected void paintButtonPressed(Graphics g, AbstractButton b) 
174      { 
175        if (b.isContentAreaFilled())
176        {
177          g.setColor(getSelectColor());
178          g.fillRect(0, 0, b.getWidth(), b.getHeight());
179        }
180      }
181        
182      /** 
183       * Paints the focus rectangle around the button text and/or icon.
184       * 
185       * @param g  the graphics context.
186       * @param b  the button.
187       * @param viewRect  the button bounds.
188       * @param textRect  the text bounds.
189       * @param iconRect  the icon bounds.
190       */
191      protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect,
192              Rectangle textRect, Rectangle iconRect) 
193      {
194        if (b.isEnabled() && b.hasFocus() && b.isFocusPainted())
195        {
196          Color savedColor = g.getColor();
197          g.setColor(getFocusColor());
198          Rectangle focusRect = iconRect.union(textRect);
199          g.drawRect(focusRect.x - 1, focusRect.y,
200                     focusRect.width + 1, focusRect.height);
201          g.setColor(savedColor);
202        }
203      }
204        
205      /**
206       * Paints the button text.
207       * 
208       * @param g  the graphics context.
209       * @param c  the button.
210       * @param textRect  the text bounds.
211       * @param text  the text to display.
212       */
213      protected void paintText(Graphics g, JComponent c, Rectangle textRect,
214              String text) 
215      {
216        AbstractButton b = (AbstractButton) c;
217        Font f = b.getFont();
218        g.setFont(f);
219        FontMetrics fm = g.getFontMetrics(f);
220    
221        if (b.isEnabled())
222          {
223            g.setColor(b.getForeground());
224            g.drawString(text, textRect.x, textRect.y + fm.getAscent());
225          }
226        else
227          {
228            g.setColor(getDisabledTextColor());
229            g.drawString(text, textRect.x, textRect.y + fm.getAscent());
230          }  
231      }
232    
233      /**
234       * If the property <code>Button.gradient</code> is set, then a gradient is
235       * painted as background, otherwise the normal superclass behaviour is
236       * called.
237       */
238      public void update(Graphics g, JComponent c)
239      {
240        AbstractButton b = (AbstractButton) c;
241        if ((b.getBackground() instanceof UIResource)
242            && b.isContentAreaFilled() && b.isEnabled())
243          {
244            ButtonModel m = b.getModel();
245            String uiKey = "Button.gradient";
246            if (! isToolbarButton(b))
247              {
248                if (! m.isArmed() && ! m.isPressed() && isDrawingGradient(uiKey))
249                  {
250                    MetalUtils.paintGradient(g, 0, 0, b.getWidth(), b.getHeight(),
251                                             SwingConstants.VERTICAL,
252                                             uiKey);
253                    paint(g, c);
254                    return;
255                  }
256              }
257            else if (m.isRollover() && isDrawingGradient(uiKey))
258              {
259                MetalUtils.paintGradient(g, 0, 0, b.getWidth(), b.getHeight(),
260                                         SwingConstants.VERTICAL,
261                                         uiKey);
262                paint(g, c);
263                return;
264              }
265          }
266        // Fallback if we didn't have any of the two above cases.
267        super.update(g, c);
268      }
269    
270      /**
271       * Returns <code>true</code> when the button is a toolbar button,
272       * <code>false</code> otherwise.
273       *
274       * @param b the button component to test
275       *
276       * @return <code>true</code> when the button is a toolbar button,
277       *         <code>false</code> otherwise
278       */
279      private boolean isToolbarButton(Component b)
280      {
281        Component parent = b.getParent();
282        return parent instanceof JToolBar;
283      }
284    
285      /**
286       * Returns <code>true</code> if we should draw the button gradient,
287       * <code>false</code> otherwise.
288       *
289       * @param uiKey the UIManager key for the gradient
290       *
291       * @return <code>true</code> if we should draw the button gradient,
292       *         <code>false</code> otherwise
293       */
294      private boolean isDrawingGradient(String uiKey)
295      {
296        return (UIManager.get(uiKey) != null);
297      }
298    }