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