GNU Classpath (0.91) | |
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.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: if (b.getMargin() == null || b.getMargin() instanceof UIResource) 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: } 165: 166: /** 167: * Removes the defaults added by {@link #installDefaults(AbstractButton)}. 168: * 169: * @param b the button (<code>null</code> not permitted). 170: */ 171: protected void uninstallDefaults(AbstractButton b) 172: { 173: if (b.getFont() instanceof UIResource) 174: b.setFont(null); 175: if (b.getForeground() instanceof UIResource) 176: b.setForeground(null); 177: if (b.getBackground() instanceof UIResource) 178: b.setBackground(null); 179: if (b.getBorder() instanceof UIResource) 180: b.setBorder(null); 181: b.setIconTextGap(defaultTextIconGap); 182: if (b.getMargin() instanceof UIResource) 183: b.setMargin(null); 184: } 185: 186: protected BasicButtonListener listener; 187: 188: /** 189: * Creates and returns a new instance of {@link BasicButtonListener}. This 190: * method provides a hook to make it easy for subclasses to install a 191: * different listener. 192: * 193: * @param b the button. 194: * 195: * @return A new listener. 196: */ 197: protected BasicButtonListener createButtonListener(AbstractButton b) 198: { 199: return new BasicButtonListener(b); 200: } 201: 202: /** 203: * Installs listeners for the button. 204: * 205: * @param b the button (<code>null</code> not permitted). 206: */ 207: protected void installListeners(AbstractButton b) 208: { 209: listener = createButtonListener(b); 210: b.addChangeListener(listener); 211: b.addPropertyChangeListener(listener); 212: b.addFocusListener(listener); 213: b.addMouseListener(listener); 214: b.addMouseMotionListener(listener); 215: } 216: 217: /** 218: * Uninstalls listeners for the button. 219: * 220: * @param b the button (<code>null</code> not permitted). 221: */ 222: protected void uninstallListeners(AbstractButton b) 223: { 224: b.removeChangeListener(listener); 225: b.removePropertyChangeListener(listener); 226: b.removeFocusListener(listener); 227: b.removeMouseListener(listener); 228: b.removeMouseMotionListener(listener); 229: } 230: 231: protected void installKeyboardActions(AbstractButton b) 232: { 233: listener.installKeyboardActions(b); 234: } 235: 236: protected void uninstallKeyboardActions(AbstractButton b) 237: { 238: listener.uninstallKeyboardActions(b); 239: } 240: 241: /** 242: * Install the BasicButtonUI as the UI for a particular component. 243: * This means registering all the UI's listeners with the component, 244: * and setting any properties of the button which are particular to 245: * this look and feel. 246: * 247: * @param c The component to install the UI into 248: */ 249: public void installUI(final JComponent c) 250: { 251: super.installUI(c); 252: if (c instanceof AbstractButton) 253: { 254: AbstractButton b = (AbstractButton) c; 255: installDefaults(b); 256: installListeners(b); 257: installKeyboardActions(b); 258: } 259: } 260: 261: /** 262: * Calculate the preferred size of this component, by delegating to 263: * {@link BasicGraphicsUtils#getPreferredButtonSize}. 264: * 265: * @param c The component to measure 266: * 267: * @return The preferred dimensions of the component 268: */ 269: public Dimension getPreferredSize(JComponent c) 270: { 271: AbstractButton b = (AbstractButton)c; 272: Dimension d = 273: BasicGraphicsUtils.getPreferredButtonSize 274: (b, defaultTextIconGap + defaultTextShiftOffset); 275: return d; 276: } 277: 278: static Icon currentIcon(AbstractButton b) 279: { 280: Icon i = b.getIcon(); 281: ButtonModel model = b.getModel(); 282: 283: if (model.isPressed() && b.getPressedIcon() != null && b.isEnabled()) 284: i = b.getPressedIcon(); 285: 286: else if (model.isRollover()) 287: { 288: if (b.isSelected() && b.getRolloverSelectedIcon() != null) 289: i = b.getRolloverSelectedIcon(); 290: else if (b.getRolloverIcon() != null) 291: i = b.getRolloverIcon(); 292: } 293: 294: else if (b.isSelected() && b.isEnabled()) 295: { 296: if (b.isEnabled() && b.getSelectedIcon() != null) 297: i = b.getSelectedIcon(); 298: else if (b.getDisabledSelectedIcon() != null) 299: i = b.getDisabledSelectedIcon(); 300: } 301: 302: else if (! b.isEnabled() && b.getDisabledIcon() != null) 303: i = b.getDisabledIcon(); 304: 305: return i; 306: } 307: 308: /** 309: * Paint the component, which is an {@link AbstractButton}, according to 310: * its current state. 311: * 312: * @param g The graphics context to paint with 313: * @param c The component to paint the state of 314: */ 315: public void paint(Graphics g, JComponent c) 316: { 317: AbstractButton b = (AbstractButton) c; 318: 319: Rectangle tr = new Rectangle(); 320: Rectangle ir = new Rectangle(); 321: Rectangle vr = new Rectangle(); 322: 323: Font f = c.getFont(); 324: 325: g.setFont(f); 326: 327: if (b.isBorderPainted()) 328: SwingUtilities.calculateInnerArea(b, vr); 329: else 330: vr = SwingUtilities.getLocalBounds(b); 331: String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), 332: b.getText(), 333: currentIcon(b), 334: b.getVerticalAlignment(), 335: b.getHorizontalAlignment(), 336: b.getVerticalTextPosition(), 337: b.getHorizontalTextPosition(), 338: vr, ir, tr, 339: b.getIconTextGap() 340: + defaultTextShiftOffset); 341: 342: if ((b.getModel().isArmed() && b.getModel().isPressed()) 343: || b.isSelected()) 344: paintButtonPressed(g, b); 345: 346: paintIcon(g, c, ir); 347: if (text != null) 348: paintText(g, b, tr, text); 349: if (b.isFocusOwner() && b.isFocusPainted()) 350: paintFocus(g, b, vr, tr, ir); 351: } 352: 353: /** 354: * Paint any focus decoration this {@link JComponent} might have. The 355: * component, which in this case will be an {@link AbstractButton}, 356: * should only have focus decoration painted if it has the focus, and its 357: * "focusPainted" property is <code>true</code>. 358: * 359: * @param g Graphics context to paint with 360: * @param b Button to paint the focus of 361: * @param vr Visible rectangle, the area in which to paint 362: * @param tr Text rectangle, contained in visible rectangle 363: * @param ir Icon rectangle, contained in visible rectangle 364: * 365: * @see AbstractButton#isFocusPainted() 366: * @see JComponent#hasFocus() 367: */ 368: protected void paintFocus(Graphics g, AbstractButton b, Rectangle vr, 369: Rectangle tr, Rectangle ir) 370: { 371: // In the BasicLookAndFeel no focus border is drawn. This can be 372: // overridden in subclasses to implement such behaviour. 373: } 374: 375: /** 376: * Paint the icon for this component. Depending on the state of the 377: * component and the availability of the button's various icon 378: * properties, this might mean painting one of several different icons. 379: * 380: * @param g Graphics context to paint with 381: * @param c Component to paint the icon of 382: * @param iconRect Rectangle in which the icon should be painted 383: */ 384: protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect) 385: { 386: AbstractButton b = (AbstractButton) c; 387: Icon i = currentIcon(b); 388: 389: if (i != null) 390: i.paintIcon(c, g, iconRect.x, iconRect.y); 391: } 392: 393: /** 394: * Paints the background area of an {@link AbstractButton} in the pressed 395: * state. This means filling the supplied area with a darker than normal 396: * background. 397: * 398: * @param g The graphics context to paint with 399: * @param b The button to paint the state of 400: */ 401: protected void paintButtonPressed(Graphics g, AbstractButton b) 402: { 403: if (b.isContentAreaFilled() && b.isOpaque()) 404: { 405: Rectangle area = new Rectangle(); 406: SwingUtilities.calculateInnerArea(b, area); 407: g.setColor(UIManager.getColor(getPropertyPrefix() + "shadow")); 408: g.fillRect(area.x, area.y, area.width, area.height); 409: } 410: } 411: 412: /** 413: * Paints the "text" property of an {@link AbstractButton}. 414: * 415: * @param g The graphics context to paint with 416: * @param c The component to paint the state of 417: * @param textRect The area in which to paint the text 418: * @param text The text to paint 419: */ 420: protected void paintText(Graphics g, JComponent c, Rectangle textRect, 421: String text) 422: { 423: paintText(g, (AbstractButton) c, textRect, text); 424: } 425: 426: /** 427: * Paints the "text" property of an {@link AbstractButton}. 428: * 429: * @param g The graphics context to paint with 430: * @param b The button to paint the state of 431: * @param textRect The area in which to paint the text 432: * @param text The text to paint 433: * 434: * @since 1.4 435: */ 436: protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, 437: String text) 438: { 439: Font f = b.getFont(); 440: g.setFont(f); 441: FontMetrics fm = g.getFontMetrics(f); 442: 443: if (b.isEnabled()) 444: { 445: g.setColor(b.getForeground()); 446: g.drawString(text, textRect.x, textRect.y + fm.getAscent()); 447: } 448: else 449: { 450: String prefix = getPropertyPrefix(); 451: g.setColor(UIManager.getColor(prefix + "disabledText")); 452: g.drawString(text, textRect.x, textRect.y + fm.getAscent()); 453: } 454: } 455: }
GNU Classpath (0.91) |