Source for javax.swing.plaf.basic.BasicButtonUI

   1: /* BasicButtonUI.java --
   2:    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.basic;
  40: 
  41: import java.awt.Dimension;
  42: import java.awt.Font;
  43: import java.awt.FontMetrics;
  44: import java.awt.Graphics;
  45: import java.awt.Rectangle;
  46: 
  47: import javax.swing.AbstractButton;
  48: import javax.swing.ButtonModel;
  49: import javax.swing.Icon;
  50: import javax.swing.InputMap;
  51: import javax.swing.JButton;
  52: import javax.swing.JComponent;
  53: import javax.swing.LookAndFeel;
  54: import javax.swing.SwingUtilities;
  55: import javax.swing.UIDefaults;
  56: import javax.swing.UIManager;
  57: import javax.swing.plaf.ButtonUI;
  58: import javax.swing.plaf.ComponentUI;
  59: import javax.swing.plaf.UIResource;
  60: 
  61: /**
  62:  * A UI delegate for the {@link JButton} component.
  63:  */
  64: public class BasicButtonUI extends ButtonUI
  65: {
  66:   /**
  67:    * A constant used to pad out elements in the button's layout and
  68:    * preferred size calculations.
  69:    */
  70:   protected int defaultTextIconGap = 4;
  71: 
  72:   /**
  73:    * A constant added to the defaultTextIconGap to adjust the text
  74:    * within this particular button.
  75:    */
  76:   protected int defaultTextShiftOffset = 0;
  77: 
  78:   private int textShiftOffset;
  79: 
  80:   /**
  81:    * Factory method to create an instance of BasicButtonUI for a given
  82:    * {@link JComponent}, which should be an {@link AbstractButton}.
  83:    *
  84:    * @param c The component.
  85:    *
  86:    * @return A new UI capable of drawing the component
  87:    */
  88:   public static ComponentUI createUI(final JComponent c) 
  89:   {
  90:     return new BasicButtonUI();
  91:   }
  92: 
  93:   /**
  94:    * Returns the default gap between the button's text and icon (in pixels).
  95:    * 
  96:    * @param b  the button (ignored).
  97:    * 
  98:    * @return The gap.
  99:    */
 100:   public int getDefaultTextIconGap(AbstractButton b)
 101:   {
 102:     return defaultTextIconGap;
 103:   }
 104: 
 105:   /**
 106:    * Sets the text shift offset to zero.
 107:    * 
 108:    * @see #setTextShiftOffset()
 109:    */
 110:   protected void clearTextShiftOffset()
 111:   {
 112:     textShiftOffset = 0;
 113:   }
 114:   
 115:   /**
 116:    * Returns the text shift offset.
 117:    * 
 118:    * @return The text shift offset.
 119:    * 
 120:    * @see #clearTextShiftOffset()
 121:    * @see #setTextShiftOffset()
 122:    */
 123:   protected int getTextShiftOffset()
 124:   {
 125:     return textShiftOffset;
 126:   }
 127: 
 128:   /**
 129:    * Sets the text shift offset to the value in {@link #defaultTextShiftOffset}.
 130:    * 
 131:    * @see #clearTextShiftOffset()
 132:    */
 133:   protected void setTextShiftOffset()
 134:   {
 135:     textShiftOffset = defaultTextShiftOffset;
 136:   }
 137: 
 138:   /**
 139:    * Returns the prefix for the UI defaults property for this UI class.
 140:    * This is 'Button' for this class.
 141:    *
 142:    * @return the prefix for the UI defaults property
 143:    */
 144:   protected String getPropertyPrefix()
 145:   {
 146:     return "Button.";
 147:   }
 148: 
 149:   /**
 150:    * Installs the default settings.
 151:    * 
 152:    * @param b  the button (<code>null</code> not permitted).
 153:    */
 154:   protected void installDefaults(AbstractButton b)
 155:   {
 156:     String prefix = getPropertyPrefix();
 157:     LookAndFeel.installColorsAndFont(b, prefix + "background",
 158:                                      prefix + "foreground", prefix + "font");
 159:     LookAndFeel.installBorder(b, prefix + "border");
 160:     b.setMargin(UIManager.getInsets(prefix + "margin"));
 161:     b.setIconTextGap(UIManager.getInt(prefix + "textIconGap"));
 162:     b.setInputMap(JComponent.WHEN_FOCUSED, 
 163:                   (InputMap) UIManager.get(prefix + "focusInputMap"));
 164:     b.setRolloverEnabled(UIManager.getBoolean(prefix + "rollover"));
 165:   }
 166: 
 167:   /**
 168:    * Removes the defaults added by {@link #installDefaults(AbstractButton)}.
 169:    * 
 170:    * @param b  the button (<code>null</code> not permitted).
 171:    */
 172:   protected void uninstallDefaults(AbstractButton b)
 173:   {
 174:     if (b.getFont() instanceof UIResource)
 175:       b.setFont(null);
 176:     b.setForeground(null);
 177:     b.setBackground(null);
 178:     b.setBorder(null);
 179:     b.setIconTextGap(defaultTextIconGap);
 180:     b.setMargin(null);
 181:   }
 182: 
 183:   protected BasicButtonListener listener;
 184: 
 185:   /**
 186:    * Creates and returns a new instance of {@link BasicButtonListener}.  This
 187:    * method provides a hook to make it easy for subclasses to install a 
 188:    * different listener.
 189:    * 
 190:    * @param b  the button.
 191:    * 
 192:    * @return A new listener.
 193:    */
 194:   protected BasicButtonListener createButtonListener(AbstractButton b)
 195:   {
 196:     return new BasicButtonListener(b);
 197:   }
 198: 
 199:   /**
 200:    * Installs listeners for the button.
 201:    * 
 202:    * @param b  the button (<code>null</code> not permitted).
 203:    */
 204:   protected void installListeners(AbstractButton b)
 205:   {
 206:     listener = createButtonListener(b);
 207:     b.addChangeListener(listener);
 208:     b.addPropertyChangeListener(listener);
 209:     b.addFocusListener(listener);    
 210:     b.addMouseListener(listener);
 211:     b.addMouseMotionListener(listener);
 212:   }
 213: 
 214:   /**
 215:    * Uninstalls listeners for the button.
 216:    * 
 217:    * @param b  the button (<code>null</code> not permitted).
 218:    */
 219:   protected void uninstallListeners(AbstractButton b)
 220:   {
 221:     b.removeChangeListener(listener);
 222:     b.removePropertyChangeListener(listener);
 223:     b.removeFocusListener(listener);    
 224:     b.removeMouseListener(listener);
 225:     b.removeMouseMotionListener(listener);
 226:   }
 227: 
 228:   protected void installKeyboardActions(AbstractButton b)
 229:   {
 230:     listener.installKeyboardActions(b);
 231:   }
 232: 
 233:   protected void uninstallKeyboardActions(AbstractButton b)
 234:   {
 235:     listener.uninstallKeyboardActions(b);
 236:   }
 237: 
 238:   /**
 239:    * Install the BasicButtonUI as the UI for a particular component.
 240:    * This means registering all the UI's listeners with the component,
 241:    * and setting any properties of the button which are particular to 
 242:    * this look and feel.
 243:    *
 244:    * @param c The component to install the UI into
 245:    */
 246:   public void installUI(final JComponent c) 
 247:   {
 248:     super.installUI(c);
 249:     if (c instanceof AbstractButton)
 250:       {
 251:         AbstractButton b = (AbstractButton) c;
 252:         installDefaults(b);
 253:         installListeners(b);
 254:         installKeyboardActions(b);
 255:       }
 256:   }
 257: 
 258:   /**
 259:    * Calculate the preferred size of this component, by delegating to
 260:    * {@link BasicGraphicsUtils#getPreferredButtonSize}.
 261:    *
 262:    * @param c The component to measure
 263:    *
 264:    * @return The preferred dimensions of the component
 265:    */
 266:   public Dimension getPreferredSize(JComponent c) 
 267:   {
 268:     AbstractButton b = (AbstractButton)c;
 269:     Dimension d = 
 270:       BasicGraphicsUtils.getPreferredButtonSize
 271:       (b, defaultTextIconGap + defaultTextShiftOffset);
 272:     return d;
 273:   }
 274: 
 275:   static Icon currentIcon(AbstractButton b)
 276:   {
 277:     Icon i = b.getIcon();
 278:     ButtonModel model = b.getModel();
 279: 
 280:     if (model.isPressed() && b.getPressedIcon() != null && b.isEnabled())
 281:       i = b.getPressedIcon();
 282: 
 283:     else if (model.isRollover())
 284:       {
 285:         if (b.isSelected() && b.getRolloverSelectedIcon() != null)
 286:           i = b.getRolloverSelectedIcon();
 287:         else if (b.getRolloverIcon() != null)
 288:           i = b.getRolloverIcon();
 289:       }    
 290: 
 291:     else if (b.isSelected() && b.isEnabled())
 292:       {
 293:         if (b.isEnabled() && b.getSelectedIcon() != null)
 294:           i = b.getSelectedIcon();
 295:         else if (b.getDisabledSelectedIcon() != null)
 296:           i = b.getDisabledSelectedIcon();
 297:       }
 298: 
 299:     else if (! b.isEnabled() && b.getDisabledIcon() != null)
 300:       i = b.getDisabledIcon();
 301: 
 302:     return i;
 303:   }
 304: 
 305:   /**
 306:    * Paint the component, which is an {@link AbstractButton}, according to 
 307:    * its current state.
 308:    *
 309:    * @param g The graphics context to paint with
 310:    * @param c The component to paint the state of
 311:    */
 312:   public void paint(Graphics g, JComponent c)
 313:   {      
 314:     AbstractButton b = (AbstractButton) c;
 315: 
 316:     Rectangle tr = new Rectangle();
 317:     Rectangle ir = new Rectangle();
 318:     Rectangle vr = new Rectangle();
 319: 
 320:     Font f = c.getFont();
 321: 
 322:     g.setFont(f);
 323: 
 324:     if (b.isBorderPainted())
 325:       SwingUtilities.calculateInnerArea(b, vr);
 326:     else
 327:       vr = SwingUtilities.getLocalBounds(b);
 328:     String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), 
 329:                                                      b.getText(),
 330:                                                      currentIcon(b),
 331:                                                      b.getVerticalAlignment(), 
 332:                                                      b.getHorizontalAlignment(),
 333:                                                      b.getVerticalTextPosition(), 
 334:                                                      b.getHorizontalTextPosition(),
 335:                                                      vr, ir, tr, 
 336:                                                      b.getIconTextGap() 
 337:                                                      + defaultTextShiftOffset);
 338:     
 339:     if ((b.getModel().isArmed() && b.getModel().isPressed()) 
 340:         || b.isSelected())
 341:       paintButtonPressed(g, b);
 342:     
 343:     paintIcon(g, c, ir);
 344:     if (text != null)
 345:       paintText(g, b, tr, text);
 346:     if (b.isFocusOwner() && b.isFocusPainted())
 347:       paintFocus(g, b, vr, tr, ir);
 348:   }
 349: 
 350:   /**
 351:    * Paint any focus decoration this {@link JComponent} might have.  The
 352:    * component, which in this case will be an {@link AbstractButton},
 353:    * should only have focus decoration painted if it has the focus, and its
 354:    * "focusPainted" property is <code>true</code>.
 355:    *
 356:    * @param g Graphics context to paint with
 357:    * @param b Button to paint the focus of
 358:    * @param vr Visible rectangle, the area in which to paint
 359:    * @param tr Text rectangle, contained in visible rectangle
 360:    * @param ir Icon rectangle, contained in visible rectangle
 361:    *
 362:    * @see AbstractButton#isFocusPainted()
 363:    * @see JComponent#hasFocus()
 364:    */
 365:   protected void paintFocus(Graphics g, AbstractButton b, Rectangle vr,
 366:                             Rectangle tr, Rectangle ir)
 367:   {
 368:     // In the BasicLookAndFeel no focus border is drawn. This can be
 369:     // overridden in subclasses to implement such behaviour.
 370:   }
 371: 
 372:   /**
 373:    * Paint the icon for this component. Depending on the state of the
 374:    * component and the availability of the button's various icon
 375:    * properties, this might mean painting one of several different icons.
 376:    *
 377:    * @param g Graphics context to paint with
 378:    * @param c Component to paint the icon of
 379:    * @param iconRect Rectangle in which the icon should be painted
 380:    */
 381:   protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect)
 382:   {
 383:     AbstractButton b = (AbstractButton) c;
 384:     Icon i = currentIcon(b);
 385: 
 386:     if (i != null)
 387:       i.paintIcon(c, g, iconRect.x, iconRect.y);
 388:   }
 389: 
 390:   /**
 391:    * Paints the background area of an {@link AbstractButton} in the pressed
 392:    * state.  This means filling the supplied area with a darker than normal 
 393:    * background.
 394:    *
 395:    * @param g The graphics context to paint with
 396:    * @param b The button to paint the state of
 397:    */
 398:   protected void paintButtonPressed(Graphics g, AbstractButton b)
 399:   {
 400:     if (b.isContentAreaFilled() && b.isOpaque())
 401:       {
 402:         Rectangle area = new Rectangle();
 403:         SwingUtilities.calculateInnerArea(b, area);
 404:         g.setColor(UIManager.getColor(getPropertyPrefix() + "shadow"));
 405:         g.fillRect(area.x, area.y, area.width, area.height);
 406:       }
 407:   }
 408:     
 409:   /**
 410:    * Paints the "text" property of an {@link AbstractButton}.
 411:    *
 412:    * @param g The graphics context to paint with
 413:    * @param c The component to paint the state of
 414:    * @param textRect The area in which to paint the text
 415:    * @param text The text to paint
 416:    */
 417:   protected void paintText(Graphics g, JComponent c, Rectangle textRect,
 418:                            String text) 
 419:   {    
 420:     paintText(g, (AbstractButton) c, textRect, text);
 421:   }
 422: 
 423:   /**
 424:    * Paints the "text" property of an {@link AbstractButton}.
 425:    *
 426:    * @param g The graphics context to paint with
 427:    * @param b The button to paint the state of
 428:    * @param textRect The area in which to paint the text
 429:    * @param text The text to paint
 430:    *
 431:    * @since 1.4
 432:    */
 433:   protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
 434:                String text)
 435:   {
 436:     Font f = b.getFont();
 437:     g.setFont(f);
 438:     FontMetrics fm = g.getFontMetrics(f);
 439: 
 440:     if (b.isEnabled())
 441:       {
 442:         g.setColor(b.getForeground());
 443:         g.drawString(text, textRect.x, textRect.y + fm.getAscent());
 444:       }
 445:     else
 446:       {
 447:         UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 448:         String prefix = getPropertyPrefix();
 449:         g.setColor(defaults.getColor(prefix + "disabledText"));
 450:         g.drawString(text, textRect.x, textRect.y + fm.getAscent());
 451:       }
 452:   } 
 453: }