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.UIManager;
  56: import javax.swing.plaf.ButtonUI;
  57: import javax.swing.plaf.ComponentUI;
  58: import javax.swing.plaf.UIResource;
  59: 
  60: /**
  61:  * A UI delegate for the {@link JButton} component.
  62:  */
  63: public class BasicButtonUI extends ButtonUI
  64: {
  65:   /**
  66:    * A constant used to pad out elements in the button's layout and
  67:    * preferred size calculations.
  68:    */
  69:   protected int defaultTextIconGap = 4;
  70: 
  71:   /**
  72:    * A constant added to the defaultTextIconGap to adjust the text
  73:    * within this particular button.
  74:    */
  75:   protected int defaultTextShiftOffset = 0;
  76: 
  77:   private int textShiftOffset;
  78: 
  79:   /**
  80:    * Factory method to create an instance of BasicButtonUI for a given
  81:    * {@link JComponent}, which should be an {@link AbstractButton}.
  82:    *
  83:    * @param c The component.
  84:    *
  85:    * @return A new UI capable of drawing the component
  86:    */
  87:   public static ComponentUI createUI(final JComponent c) 
  88:   {
  89:     return new BasicButtonUI();
  90:   }
  91: 
  92:   /**
  93:    * Returns the default gap between the button's text and icon (in pixels).
  94:    * 
  95:    * @param b  the button (ignored).
  96:    * 
  97:    * @return The gap.
  98:    */
  99:   public int getDefaultTextIconGap(AbstractButton b)
 100:   {
 101:     return defaultTextIconGap;
 102:   }
 103: 
 104:   /**
 105:    * Sets the text shift offset to zero.
 106:    * 
 107:    * @see #setTextShiftOffset()
 108:    */
 109:   protected void clearTextShiftOffset()
 110:   {
 111:     textShiftOffset = 0;
 112:   }
 113:   
 114:   /**
 115:    * Returns the text shift offset.
 116:    * 
 117:    * @return The text shift offset.
 118:    * 
 119:    * @see #clearTextShiftOffset()
 120:    * @see #setTextShiftOffset()
 121:    */
 122:   protected int getTextShiftOffset()
 123:   {
 124:     return textShiftOffset;
 125:   }
 126: 
 127:   /**
 128:    * Sets the text shift offset to the value in {@link #defaultTextShiftOffset}.
 129:    * 
 130:    * @see #clearTextShiftOffset()
 131:    */
 132:   protected void setTextShiftOffset()
 133:   {
 134:     textShiftOffset = defaultTextShiftOffset;
 135:   }
 136: 
 137:   /**
 138:    * Returns the prefix for the UI defaults property for this UI class.
 139:    * This is 'Button' for this class.
 140:    *
 141:    * @return the prefix for the UI defaults property
 142:    */
 143:   protected String getPropertyPrefix()
 144:   {
 145:     return "Button.";
 146:   }
 147: 
 148:   /**
 149:    * Installs the default settings.
 150:    * 
 151:    * @param b  the button (<code>null</code> not permitted).
 152:    */
 153:   protected void installDefaults(AbstractButton b)
 154:   {
 155:     String prefix = getPropertyPrefix();
 156:     LookAndFeel.installColorsAndFont(b, prefix + "background",
 157:                                      prefix + "foreground", prefix + "font");
 158:     LookAndFeel.installBorder(b, prefix + "border");
 159:     b.setMargin(UIManager.getInsets(prefix + "margin"));
 160:     b.setIconTextGap(UIManager.getInt(prefix + "textIconGap"));
 161:     b.setInputMap(JComponent.WHEN_FOCUSED, 
 162:                   (InputMap) UIManager.get(prefix + "focusInputMap"));
 163:   }
 164: 
 165:   /**
 166:    * Removes the defaults added by {@link #installDefaults(AbstractButton)}.
 167:    * 
 168:    * @param b  the button (<code>null</code> not permitted).
 169:    */
 170:   protected void uninstallDefaults(AbstractButton b)
 171:   {
 172:     if (b.getFont() instanceof UIResource)
 173:       b.setFont(null);
 174:     b.setForeground(null);
 175:     b.setBackground(null);
 176:     b.setBorder(null);
 177:     b.setIconTextGap(defaultTextIconGap);
 178:     b.setMargin(null);
 179:   }
 180: 
 181:   protected BasicButtonListener listener;
 182: 
 183:   /**
 184:    * Creates and returns a new instance of {@link BasicButtonListener}.  This
 185:    * method provides a hook to make it easy for subclasses to install a 
 186:    * different listener.
 187:    * 
 188:    * @param b  the button.
 189:    * 
 190:    * @return A new listener.
 191:    */
 192:   protected BasicButtonListener createButtonListener(AbstractButton b)
 193:   {
 194:     return new BasicButtonListener(b);
 195:   }
 196: 
 197:   /**
 198:    * Installs listeners for the button.
 199:    * 
 200:    * @param b  the button (<code>null</code> not permitted).
 201:    */
 202:   protected void installListeners(AbstractButton b)
 203:   {
 204:     listener = createButtonListener(b);
 205:     b.addChangeListener(listener);
 206:     b.addPropertyChangeListener(listener);
 207:     b.addFocusListener(listener);    
 208:     b.addMouseListener(listener);
 209:     b.addMouseMotionListener(listener);
 210:   }
 211: 
 212:   /**
 213:    * Uninstalls listeners for the button.
 214:    * 
 215:    * @param b  the button (<code>null</code> not permitted).
 216:    */
 217:   protected void uninstallListeners(AbstractButton b)
 218:   {
 219:     b.removeChangeListener(listener);
 220:     b.removePropertyChangeListener(listener);
 221:     b.removeFocusListener(listener);    
 222:     b.removeMouseListener(listener);
 223:     b.removeMouseMotionListener(listener);
 224:   }
 225: 
 226:   protected void installKeyboardActions(AbstractButton b)
 227:   {
 228:     listener.installKeyboardActions(b);
 229:   }
 230: 
 231:   protected void uninstallKeyboardActions(AbstractButton b)
 232:   {
 233:     listener.uninstallKeyboardActions(b);
 234:   }
 235: 
 236:   /**
 237:    * Install the BasicButtonUI as the UI for a particular component.
 238:    * This means registering all the UI's listeners with the component,
 239:    * and setting any properties of the button which are particular to 
 240:    * this look and feel.
 241:    *
 242:    * @param c The component to install the UI into
 243:    */
 244:   public void installUI(final JComponent c) 
 245:   {
 246:     super.installUI(c);
 247:     if (c instanceof AbstractButton)
 248:       {
 249:         AbstractButton b = (AbstractButton) c;
 250:         installDefaults(b);
 251:         installListeners(b);
 252:         installKeyboardActions(b);
 253:       }
 254:   }
 255: 
 256:   /**
 257:    * Calculate the preferred size of this component, by delegating to
 258:    * {@link BasicGraphicsUtils#getPreferredButtonSize}.
 259:    *
 260:    * @param c The component to measure
 261:    *
 262:    * @return The preferred dimensions of the component
 263:    */
 264:   public Dimension getPreferredSize(JComponent c) 
 265:   {
 266:     AbstractButton b = (AbstractButton)c;
 267:     Dimension d = 
 268:       BasicGraphicsUtils.getPreferredButtonSize
 269:       (b, defaultTextIconGap + defaultTextShiftOffset);
 270:     return d;
 271:   }
 272: 
 273:   static Icon currentIcon(AbstractButton b)
 274:   {
 275:     Icon i = b.getIcon();
 276:     ButtonModel model = b.getModel();
 277: 
 278:     if (model.isPressed() && b.getPressedIcon() != null && b.isEnabled())
 279:       i = b.getPressedIcon();
 280: 
 281:     else if (model.isRollover())
 282:       {
 283:         if (b.isSelected() && b.getRolloverSelectedIcon() != null)
 284:           i = b.getRolloverSelectedIcon();
 285:         else if (b.getRolloverIcon() != null)
 286:           i = b.getRolloverIcon();
 287:       }    
 288: 
 289:     else if (b.isSelected() && b.isEnabled())
 290:       {
 291:         if (b.isEnabled() && b.getSelectedIcon() != null)
 292:           i = b.getSelectedIcon();
 293:         else if (b.getDisabledSelectedIcon() != null)
 294:           i = b.getDisabledSelectedIcon();
 295:       }
 296: 
 297:     else if (! b.isEnabled() && b.getDisabledIcon() != null)
 298:       i = b.getDisabledIcon();
 299: 
 300:     return i;
 301:   }
 302: 
 303:   /**
 304:    * Paint the component, which is an {@link AbstractButton}, according to 
 305:    * its current state.
 306:    *
 307:    * @param g The graphics context to paint with
 308:    * @param c The component to paint the state of
 309:    */
 310:   public void paint(Graphics g, JComponent c)
 311:   {      
 312:     AbstractButton b = (AbstractButton) c;
 313: 
 314:     Rectangle tr = new Rectangle();
 315:     Rectangle ir = new Rectangle();
 316:     Rectangle vr = new Rectangle();
 317: 
 318:     Font f = c.getFont();
 319: 
 320:     g.setFont(f);
 321: 
 322:     if (b.isBorderPainted())
 323:       SwingUtilities.calculateInnerArea(b, vr);
 324:     else
 325:       vr = SwingUtilities.getLocalBounds(b);
 326:     String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), 
 327:                                                      b.getText(),
 328:                                                      currentIcon(b),
 329:                                                      b.getVerticalAlignment(), 
 330:                                                      b.getHorizontalAlignment(),
 331:                                                      b.getVerticalTextPosition(), 
 332:                                                      b.getHorizontalTextPosition(),
 333:                                                      vr, ir, tr, 
 334:                                                      b.getIconTextGap() 
 335:                                                      + defaultTextShiftOffset);
 336:     
 337:     if ((b.getModel().isArmed() && b.getModel().isPressed()) 
 338:         || b.isSelected())
 339:       paintButtonPressed(g, b);
 340:     
 341:     paintIcon(g, c, ir);
 342:     if (text != null)
 343:       paintText(g, b, tr, text);
 344:     if (b.isFocusOwner() && b.isFocusPainted())
 345:       paintFocus(g, b, vr, tr, ir);
 346:   }
 347: 
 348:   /**
 349:    * Paint any focus decoration this {@link JComponent} might have.  The
 350:    * component, which in this case will be an {@link AbstractButton},
 351:    * should only have focus decoration painted if it has the focus, and its
 352:    * "focusPainted" property is <code>true</code>.
 353:    *
 354:    * @param g Graphics context to paint with
 355:    * @param b Button to paint the focus of
 356:    * @param vr Visible rectangle, the area in which to paint
 357:    * @param tr Text rectangle, contained in visible rectangle
 358:    * @param ir Icon rectangle, contained in visible rectangle
 359:    *
 360:    * @see AbstractButton#isFocusPainted()
 361:    * @see JComponent#hasFocus()
 362:    */
 363:   protected void paintFocus(Graphics g, AbstractButton b, Rectangle vr,
 364:                             Rectangle tr, Rectangle ir)
 365:   {
 366:     // In the BasicLookAndFeel no focus border is drawn. This can be
 367:     // overridden in subclasses to implement such behaviour.
 368:   }
 369: 
 370:   /**
 371:    * Paint the icon for this component. Depending on the state of the
 372:    * component and the availability of the button's various icon
 373:    * properties, this might mean painting one of several different icons.
 374:    *
 375:    * @param g Graphics context to paint with
 376:    * @param c Component to paint the icon of
 377:    * @param iconRect Rectangle in which the icon should be painted
 378:    */
 379:   protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect)
 380:   {
 381:     AbstractButton b = (AbstractButton) c;
 382:     Icon i = currentIcon(b);
 383: 
 384:     if (i != null)
 385:       i.paintIcon(c, g, iconRect.x, iconRect.y);
 386:   }
 387: 
 388:   /**
 389:    * Paints the background area of an {@link AbstractButton} in the pressed
 390:    * state.  This means filling the supplied area with a darker than normal 
 391:    * background.
 392:    *
 393:    * @param g The graphics context to paint with
 394:    * @param b The button to paint the state of
 395:    */
 396:   protected void paintButtonPressed(Graphics g, AbstractButton b)
 397:   {
 398:     if (b.isContentAreaFilled() && b.isOpaque())
 399:       {
 400:         Rectangle area = new Rectangle();
 401:         SwingUtilities.calculateInnerArea(b, area);
 402:         g.setColor(UIManager.getColor(getPropertyPrefix() + "shadow"));
 403:         g.fillRect(area.x, area.y, area.width, area.height);
 404:       }
 405:   }
 406:     
 407:   /**
 408:    * Paints the "text" property of an {@link AbstractButton}.
 409:    *
 410:    * @param g The graphics context to paint with
 411:    * @param c The component to paint the state of
 412:    * @param textRect The area in which to paint the text
 413:    * @param text The text to paint
 414:    */
 415:   protected void paintText(Graphics g, JComponent c, Rectangle textRect,
 416:                            String text) 
 417:   {    
 418:     paintText(g, (AbstractButton) c, textRect, text);
 419:   }
 420: 
 421:   /**
 422:    * Paints the "text" property of an {@link AbstractButton}.
 423:    *
 424:    * @param g The graphics context to paint with
 425:    * @param b The button to paint the state of
 426:    * @param textRect The area in which to paint the text
 427:    * @param text The text to paint
 428:    *
 429:    * @since 1.4
 430:    */
 431:   protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
 432:                String text)
 433:   {
 434:     Font f = b.getFont();
 435:     g.setFont(f);
 436:     FontMetrics fm = g.getFontMetrics(f);
 437: 
 438:     if (b.isEnabled())
 439:       {
 440:         g.setColor(b.getForeground());
 441:         g.drawString(text, textRect.x, textRect.y + fm.getAscent());
 442:       }
 443:     else
 444:       {
 445:         String prefix = getPropertyPrefix();
 446:         g.setColor(UIManager.getColor(prefix + "disabledText"));
 447:         g.drawString(text, textRect.x, textRect.y + fm.getAscent());
 448:       }
 449:   } 
 450: }