GNU Classpath (0.19) | ||
Frames | No Frames |
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: }
GNU Classpath (0.19) |