1 /* 2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.plaf.basic; 27 28 import sun.swing.SwingUtilities2; 29 import sun.awt.AppContext; 30 31 import java.awt.*; 32 import java.awt.event.*; 33 import java.io.Serializable; 34 import javax.swing.*; 35 import javax.swing.border.*; 36 import java.awt.*; 37 import java.awt.event.*; 38 import javax.swing.plaf.ButtonUI; 39 import javax.swing.plaf.UIResource; 40 import javax.swing.plaf.ComponentUI; 41 import javax.swing.text.View; 42 43 /** 44 * BasicButton implementation 45 * 46 * @author Jeff Dinkins 47 */ 48 public class BasicButtonUI extends ButtonUI{ 49 // Visual constants 50 // NOTE: This is not used or set any where. Were we allowed to remove 51 // fields, this would be removed. 52 protected int defaultTextIconGap; 53 54 // Amount to offset text, the value of this comes from 55 // defaultTextShiftOffset once setTextShiftOffset has been invoked. 56 private int shiftOffset = 0; 57 // Value that is set in shiftOffset once setTextShiftOffset has been 58 // invoked. The value of this comes from the defaults table. 59 protected int defaultTextShiftOffset; 60 61 private final static String propertyPrefix = "Button" + "."; 62 63 private static final Object BASIC_BUTTON_UI_KEY = new Object(); 64 65 // ******************************** 66 // Create PLAF 67 // ******************************** 68 public static ComponentUI createUI(JComponent c) { 69 AppContext appContext = AppContext.getAppContext(); 70 BasicButtonUI buttonUI = 71 (BasicButtonUI) appContext.get(BASIC_BUTTON_UI_KEY); 72 if (buttonUI == null) { 73 buttonUI = new BasicButtonUI(); 74 appContext.put(BASIC_BUTTON_UI_KEY, buttonUI); 75 } 76 return buttonUI; 77 } 78 79 protected String getPropertyPrefix() { 80 return propertyPrefix; 81 } 82 83 84 // ******************************** 85 // Install PLAF 86 // ******************************** 87 public void installUI(JComponent c) { 88 installDefaults((AbstractButton) c); 89 installListeners((AbstractButton) c); 90 installKeyboardActions((AbstractButton) c); 91 BasicHTML.updateRenderer(c, ((AbstractButton) c).getText()); 92 } 93 94 protected void installDefaults(AbstractButton b) { 95 // load shared instance defaults 96 String pp = getPropertyPrefix(); 97 98 defaultTextShiftOffset = UIManager.getInt(pp + "textShiftOffset"); 99 100 // set the following defaults on the button 101 if (b.isContentAreaFilled()) { 102 LookAndFeel.installProperty(b, "opaque", Boolean.TRUE); 103 } else { 104 LookAndFeel.installProperty(b, "opaque", Boolean.FALSE); 105 } 106 107 if(b.getMargin() == null || (b.getMargin() instanceof UIResource)) { 108 b.setMargin(UIManager.getInsets(pp + "margin")); 109 } 110 111 LookAndFeel.installColorsAndFont(b, pp + "background", 112 pp + "foreground", pp + "font"); 113 LookAndFeel.installBorder(b, pp + "border"); 114 115 Object rollover = UIManager.get(pp + "rollover"); 116 if (rollover != null) { 117 LookAndFeel.installProperty(b, "rolloverEnabled", rollover); 118 } 119 120 LookAndFeel.installProperty(b, "iconTextGap", new Integer(4)); 121 } 122 123 protected void installListeners(AbstractButton b) { 124 BasicButtonListener listener = createButtonListener(b); 125 if(listener != null) { 126 b.addMouseListener(listener); 127 b.addMouseMotionListener(listener); 128 b.addFocusListener(listener); 129 b.addPropertyChangeListener(listener); 130 b.addChangeListener(listener); 131 } 132 } 133 134 protected void installKeyboardActions(AbstractButton b){ 135 BasicButtonListener listener = getButtonListener(b); 136 137 if(listener != null) { 138 listener.installKeyboardActions(b); 139 } 140 } 141 142 143 // ******************************** 144 // Uninstall PLAF 145 // ******************************** 146 public void uninstallUI(JComponent c) { 147 uninstallKeyboardActions((AbstractButton) c); 148 uninstallListeners((AbstractButton) c); 149 uninstallDefaults((AbstractButton) c); 150 BasicHTML.updateRenderer(c, ""); 151 } 152 153 protected void uninstallKeyboardActions(AbstractButton b) { 154 BasicButtonListener listener = getButtonListener(b); 155 if(listener != null) { 156 listener.uninstallKeyboardActions(b); 157 } 158 } 159 160 protected void uninstallListeners(AbstractButton b) { 161 BasicButtonListener listener = getButtonListener(b); 162 if(listener != null) { 163 b.removeMouseListener(listener); 164 b.removeMouseMotionListener(listener); 165 b.removeFocusListener(listener); 166 b.removeChangeListener(listener); 167 b.removePropertyChangeListener(listener); 168 } 169 } 170 171 protected void uninstallDefaults(AbstractButton b) { 172 LookAndFeel.uninstallBorder(b); 173 } 174 175 // ******************************** 176 // Create Listeners 177 // ******************************** 178 protected BasicButtonListener createButtonListener(AbstractButton b) { 179 return new BasicButtonListener(b); 180 } 181 182 public int getDefaultTextIconGap(AbstractButton b) { 183 return defaultTextIconGap; 184 } 185 186 /* These rectangles/insets are allocated once for all 187 * ButtonUI.paint() calls. Re-using rectangles rather than 188 * allocating them in each paint call substantially reduced the time 189 * it took paint to run. Obviously, this method can't be re-entered. 190 */ 191 private static Rectangle viewRect = new Rectangle(); 192 private static Rectangle textRect = new Rectangle(); 193 private static Rectangle iconRect = new Rectangle(); 194 195 // ******************************** 196 // Paint Methods 197 // ******************************** 198 199 public void paint(Graphics g, JComponent c) 200 { 201 AbstractButton b = (AbstractButton) c; 202 ButtonModel model = b.getModel(); 203 204 String text = layout(b, SwingUtilities2.getFontMetrics(b, g), 205 b.getWidth(), b.getHeight()); 206 207 clearTextShiftOffset(); 208 209 // perform UI specific press action, e.g. Windows L&F shifts text 210 if (model.isArmed() && model.isPressed()) { 211 paintButtonPressed(g,b); 212 } 213 214 // Paint the Icon 215 if(b.getIcon() != null) { 216 paintIcon(g,c,iconRect); 217 } 218 219 if (text != null && !text.equals("")){ 220 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 221 if (v != null) { 222 v.paint(g, textRect); 223 } else { 224 paintText(g, b, textRect, text); 225 } 226 } 227 228 if (b.isFocusPainted() && b.hasFocus()) { 229 // paint UI specific focus 230 paintFocus(g,b,viewRect,textRect,iconRect); 231 } 232 } 233 234 protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect){ 235 AbstractButton b = (AbstractButton) c; 236 ButtonModel model = b.getModel(); 237 Icon icon = b.getIcon(); 238 Icon tmpIcon = null; 239 240 if(icon == null) { 241 return; 242 } 243 244 Icon selectedIcon = null; 245 246 /* the fallback icon should be based on the selected state */ 247 if (model.isSelected()) { 248 selectedIcon = (Icon) b.getSelectedIcon(); 249 if (selectedIcon != null) { 250 icon = selectedIcon; 251 } 252 } 253 254 if(!model.isEnabled()) { 255 if(model.isSelected()) { 256 tmpIcon = (Icon) b.getDisabledSelectedIcon(); 257 if (tmpIcon == null) { 258 tmpIcon = selectedIcon; 259 } 260 } 261 262 if (tmpIcon == null) { 263 tmpIcon = (Icon) b.getDisabledIcon(); 264 } 265 } else if(model.isPressed() && model.isArmed()) { 266 tmpIcon = (Icon) b.getPressedIcon(); 267 if(tmpIcon != null) { 268 // revert back to 0 offset 269 clearTextShiftOffset(); 270 } 271 } else if(b.isRolloverEnabled() && model.isRollover()) { 272 if(model.isSelected()) { 273 tmpIcon = (Icon) b.getRolloverSelectedIcon(); 274 if (tmpIcon == null) { 275 tmpIcon = selectedIcon; 276 } 277 } 278 279 if (tmpIcon == null) { 280 tmpIcon = (Icon) b.getRolloverIcon(); 281 } 282 } 283 284 if(tmpIcon != null) { 285 icon = tmpIcon; 286 } 287 288 if(model.isPressed() && model.isArmed()) { 289 icon.paintIcon(c, g, iconRect.x + getTextShiftOffset(), 290 iconRect.y + getTextShiftOffset()); 291 } else { 292 icon.paintIcon(c, g, iconRect.x, iconRect.y); 293 } 294 295 } 296 297 /** 298 * As of Java 2 platform v 1.4 this method should not be used or overriden. 299 * Use the paintText method which takes the AbstractButton argument. 300 */ 301 protected void paintText(Graphics g, JComponent c, Rectangle textRect, String text) { 302 AbstractButton b = (AbstractButton) c; 303 ButtonModel model = b.getModel(); 304 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g); 305 int mnemonicIndex = b.getDisplayedMnemonicIndex(); 306 307 /* Draw the Text */ 308 if(model.isEnabled()) { 309 /*** paint the text normally */ 310 g.setColor(b.getForeground()); 311 SwingUtilities2.drawStringUnderlineCharAt(c, g,text, mnemonicIndex, 312 textRect.x + getTextShiftOffset(), 313 textRect.y + fm.getAscent() + getTextShiftOffset()); 314 } 315 else { 316 /*** paint the text disabled ***/ 317 g.setColor(b.getBackground().brighter()); 318 SwingUtilities2.drawStringUnderlineCharAt(c, g,text, mnemonicIndex, 319 textRect.x, textRect.y + fm.getAscent()); 320 g.setColor(b.getBackground().darker()); 321 SwingUtilities2.drawStringUnderlineCharAt(c, g,text, mnemonicIndex, 322 textRect.x - 1, textRect.y + fm.getAscent() - 1); 323 } 324 } 325 326 /** 327 * Method which renders the text of the current button. 328 * <p> 329 * @param g Graphics context 330 * @param b Current button to render 331 * @param textRect Bounding rectangle to render the text. 332 * @param text String to render 333 * @since 1.4 334 */ 335 protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) { 336 paintText(g, (JComponent)b, textRect, text); 337 } 338 339 // Method signature defined here overriden in subclasses. 340 // Perhaps this class should be abstract? 341 protected void paintFocus(Graphics g, AbstractButton b, 342 Rectangle viewRect, Rectangle textRect, Rectangle iconRect){ 343 } 344 345 346 347 protected void paintButtonPressed(Graphics g, AbstractButton b){ 348 } 349 350 protected void clearTextShiftOffset(){ 351 this.shiftOffset = 0; 352 } 353 354 protected void setTextShiftOffset(){ 355 this.shiftOffset = defaultTextShiftOffset; 356 } 357 358 protected int getTextShiftOffset() { 359 return shiftOffset; 360 } 361 362 // ******************************** 363 // Layout Methods 364 // ******************************** 365 public Dimension getMinimumSize(JComponent c) { 366 Dimension d = getPreferredSize(c); 367 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 368 if (v != null) { 369 d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS); 370 } 371 return d; 372 } 373 374 public Dimension getPreferredSize(JComponent c) { 375 AbstractButton b = (AbstractButton)c; 376 return BasicGraphicsUtils.getPreferredButtonSize(b, b.getIconTextGap()); 377 } 378 379 public Dimension getMaximumSize(JComponent c) { 380 Dimension d = getPreferredSize(c); 381 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 382 if (v != null) { 383 d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS); 384 } 385 return d; 386 } 387 388 /** 389 * Returns the baseline. 390 * 391 * @throws NullPointerException {@inheritDoc} 392 * @throws IllegalArgumentException {@inheritDoc} 393 * @see javax.swing.JComponent#getBaseline(int, int) 394 * @since 1.6 395 */ 396 public int getBaseline(JComponent c, int width, int height) { 397 super.getBaseline(c, width, height); 398 AbstractButton b = (AbstractButton)c; 399 String text = b.getText(); 400 if (text == null || "".equals(text)) { 401 return -1; 402 } 403 FontMetrics fm = b.getFontMetrics(b.getFont()); 404 layout(b, fm, width, height); 405 return BasicHTML.getBaseline(b, textRect.y, fm.getAscent(), 406 textRect.width, textRect.height); 407 } 408 409 /** 410 * Returns an enum indicating how the baseline of the component 411 * changes as the size changes. 412 * 413 * @throws NullPointerException {@inheritDoc} 414 * @see javax.swing.JComponent#getBaseline(int, int) 415 * @since 1.6 416 */ 417 public Component.BaselineResizeBehavior getBaselineResizeBehavior( 418 JComponent c) { 419 super.getBaselineResizeBehavior(c); 420 if (c.getClientProperty(BasicHTML.propertyKey) != null) { 421 return Component.BaselineResizeBehavior.OTHER; 422 } 423 switch(((AbstractButton)c).getVerticalAlignment()) { 424 case AbstractButton.TOP: 425 return Component.BaselineResizeBehavior.CONSTANT_ASCENT; 426 case AbstractButton.BOTTOM: 427 return Component.BaselineResizeBehavior.CONSTANT_DESCENT; 428 case AbstractButton.CENTER: 429 return Component.BaselineResizeBehavior.CENTER_OFFSET; 430 } 431 return Component.BaselineResizeBehavior.OTHER; 432 } 433 434 private String layout(AbstractButton b, FontMetrics fm, 435 int width, int height) { 436 Insets i = b.getInsets(); 437 viewRect.x = i.left; 438 viewRect.y = i.top; 439 viewRect.width = width - (i.right + viewRect.x); 440 viewRect.height = height - (i.bottom + viewRect.y); 441 442 textRect.x = textRect.y = textRect.width = textRect.height = 0; 443 iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; 444 445 // layout the text and icon 446 return SwingUtilities.layoutCompoundLabel( 447 b, fm, b.getText(), b.getIcon(), 448 b.getVerticalAlignment(), b.getHorizontalAlignment(), 449 b.getVerticalTextPosition(), b.getHorizontalTextPosition(), 450 viewRect, iconRect, textRect, 451 b.getText() == null ? 0 : b.getIconTextGap()); 452 } 453 454 /** 455 * Returns the ButtonListener for the passed in Button, or null if one 456 * could not be found. 457 */ 458 private BasicButtonListener getButtonListener(AbstractButton b) { 459 MouseMotionListener[] listeners = b.getMouseMotionListeners(); 460 461 if (listeners != null) { 462 for (int counter = 0; counter < listeners.length; counter++) { 463 if (listeners[counter] instanceof BasicButtonListener) { 464 return (BasicButtonListener)listeners[counter]; 465 } 466 } 467 } 468 return null; 469 } 470 471 }