001/* 002 * $Id: JXButton.java 4158 2012-02-03 18:29:40Z kschaefe $ 003 * 004 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021 022package org.jdesktop.swingx; 023 024import java.awt.Color; 025import java.awt.Dimension; 026import java.awt.Graphics; 027import java.awt.Graphics2D; 028import java.awt.Insets; 029import java.awt.Rectangle; 030import java.awt.image.BufferedImage; 031import java.awt.image.BufferedImageOp; 032 033import javax.swing.Action; 034import javax.swing.ButtonModel; 035import javax.swing.CellRendererPane; 036import javax.swing.Icon; 037import javax.swing.JButton; 038import javax.swing.SwingUtilities; 039import javax.swing.plaf.basic.BasicGraphicsUtils; 040 041import org.jdesktop.beans.JavaBean; 042import org.jdesktop.swingx.painter.AbstractPainter; 043import org.jdesktop.swingx.painter.Painter; 044import org.jdesktop.swingx.painter.PainterPaint; 045import org.jdesktop.swingx.util.GraphicsUtilities; 046import org.jdesktop.swingx.util.PaintUtils; 047 048/** 049 * <p>A {@link org.jdesktop.swingx.painter.Painter} enabled subclass of {@link javax.swing.JButton}. 050 * This class supports setting the foreground and background painters of the button separately.</p> 051 * 052 * <p>For example, if you wanted to blur <em>just the text</em> on the button, and let everything else be 053 * handled by the UI delegate for your look and feel, then you could: 054 * <pre><code> 055 * JXButton b = new JXButton("Execute"); 056 * AbstractPainter fgPainter = (AbstractPainter)b.getForegroundPainter(); 057 * StackBlurFilter filter = new StackBlurFilter(); 058 * fgPainter.setFilters(filter); 059 * </code></pre> 060 * 061 * <p>If <em>either</em> the foreground painter or the background painter is set, 062 * then super.paintComponent() is not called. By setting both the foreground and background 063 * painters to null, you get <em>exactly</em> the same painting behavior as JButton.</p> 064 * 065 * @author rbair 066 * @author rah003 067 * @author Jan Stola 068 * @author Karl George Schaefer 069 */ 070@JavaBean 071@SuppressWarnings({ "nls", "serial" }) 072public class JXButton extends JButton implements BackgroundPaintable { 073 private class BackgroundButton extends JButton { 074 075 /** 076 * {@inheritDoc} 077 */ 078 @Override 079 public boolean isDefaultButton() { 080 return JXButton.this.isDefaultButton(); 081 } 082 083 /** 084 * {@inheritDoc} 085 */ 086 @Override 087 public Icon getDisabledIcon() { 088 return null; 089 } 090 091 /** 092 * {@inheritDoc} 093 */ 094 @Override 095 public Icon getDisabledSelectedIcon() { 096 return null; 097 } 098 099 /** 100 * {@inheritDoc} 101 */ 102 @Override 103 public int getDisplayedMnemonicIndex() { 104 return -1; 105 } 106 107 /** 108 * {@inheritDoc} 109 */ 110 @Override 111 public int getHorizontalAlignment() { 112 return JXButton.this.getHorizontalAlignment(); 113 } 114 115 /** 116 * {@inheritDoc} 117 */ 118 @Override 119 public int getHorizontalTextPosition() { 120 return JXButton.this.getHorizontalTextPosition(); 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override 127 public Icon getIcon() { 128 return null; 129 } 130 131 /** 132 * {@inheritDoc} 133 */ 134 @Override 135 public int getIconTextGap() { 136 return JXButton.this.getIconTextGap(); 137 } 138 139 /** 140 * {@inheritDoc} 141 */ 142 @Override 143 public Insets getMargin() { 144 return JXButton.this.getMargin(); 145 } 146 147 /** 148 * {@inheritDoc} 149 */ 150 @Override 151 public int getMnemonic() { 152 return -1; 153 } 154 155 /** 156 * {@inheritDoc} 157 */ 158 @Override 159 public ButtonModel getModel() { 160 return JXButton.this.getModel(); 161 } 162 163 /** 164 * {@inheritDoc} 165 */ 166 @Override 167 public Icon getPressedIcon() { 168 return null; 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override 175 public Icon getRolloverIcon() { 176 return null; 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override 183 public Icon getRolloverSelectedIcon() { 184 return null; 185 } 186 187 /** 188 * {@inheritDoc} 189 */ 190 @Override 191 public Icon getSelectedIcon() { 192 return null; 193 } 194 195 /** 196 * {@inheritDoc} 197 */ 198 @Override 199 public String getText() { 200 return ""; 201 } 202 203 /** 204 * {@inheritDoc} 205 */ 206 @Override 207 public int getVerticalAlignment() { 208 return JXButton.this.getVerticalAlignment(); 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 @Override 215 public int getVerticalTextPosition() { 216 return JXButton.this.getVerticalTextPosition(); 217 } 218 219 /** 220 * {@inheritDoc} 221 */ 222 @Override 223 public boolean isBorderPainted() { 224 return JXButton.this.isBorderPainted(); 225 } 226 227 /** 228 * {@inheritDoc} 229 */ 230 @Override 231 public boolean isContentAreaFilled() { 232 return JXButton.this.isContentAreaFilled(); 233 } 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override 239 public boolean isFocusPainted() { 240 return false; 241 } 242 243 /** 244 * {@inheritDoc} 245 */ 246 @Override 247 public boolean isRolloverEnabled() { 248 return JXButton.this.isRolloverEnabled(); 249 } 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override 255 public boolean isSelected() { 256 return JXButton.this.isSelected(); 257 } 258 259 } 260 261 private class ForegroundButton extends JButton { 262 /** 263 * {@inheritDoc} 264 */ 265 @Override 266 public Color getForeground() { 267 if (fgPainter == null) { 268 return JXButton.this.getForeground(); 269 } 270 271 return PaintUtils.setAlpha(JXButton.this.getForeground(), 0); 272 } 273 /** 274 * {@inheritDoc} 275 */ 276 @Override 277 public boolean isDefaultButton() { 278 return JXButton.this.isDefaultButton(); 279 } 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override 285 public Icon getDisabledIcon() { 286 return JXButton.this.getDisabledIcon(); 287 } 288 289 /** 290 * {@inheritDoc} 291 */ 292 @Override 293 public Icon getDisabledSelectedIcon() { 294 return JXButton.this.getDisabledSelectedIcon(); 295 } 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override 301 public int getDisplayedMnemonicIndex() { 302 return JXButton.this.getDisplayedMnemonicIndex(); 303 } 304 305 /** 306 * {@inheritDoc} 307 */ 308 @Override 309 public int getHorizontalAlignment() { 310 return JXButton.this.getHorizontalAlignment(); 311 } 312 313 /** 314 * {@inheritDoc} 315 */ 316 @Override 317 public int getHorizontalTextPosition() { 318 return JXButton.this.getHorizontalTextPosition(); 319 } 320 321 /** 322 * {@inheritDoc} 323 */ 324 @Override 325 public Icon getIcon() { 326 return JXButton.this.getIcon(); 327 } 328 329 /** 330 * {@inheritDoc} 331 */ 332 @Override 333 public int getIconTextGap() { 334 return JXButton.this.getIconTextGap(); 335 } 336 337 /** 338 * {@inheritDoc} 339 */ 340 @Override 341 public Insets getMargin() { 342 return JXButton.this.getMargin(); 343 } 344 345 /** 346 * {@inheritDoc} 347 */ 348 @Override 349 public int getMnemonic() { 350 return JXButton.this.getMnemonic(); 351 } 352 353 /** 354 * {@inheritDoc} 355 */ 356 @Override 357 public ButtonModel getModel() { 358 return JXButton.this.getModel(); 359 } 360 361 /** 362 * {@inheritDoc} 363 */ 364 @Override 365 public Icon getPressedIcon() { 366 return JXButton.this.getPressedIcon(); 367 } 368 369 /** 370 * {@inheritDoc} 371 */ 372 @Override 373 public Icon getRolloverIcon() { 374 return JXButton.this.getRolloverIcon(); 375 } 376 377 /** 378 * {@inheritDoc} 379 */ 380 @Override 381 public Icon getRolloverSelectedIcon() { 382 return JXButton.this.getRolloverSelectedIcon(); 383 } 384 385 /** 386 * {@inheritDoc} 387 */ 388 @Override 389 public Icon getSelectedIcon() { 390 return JXButton.this.getSelectedIcon(); 391 } 392 393 /** 394 * {@inheritDoc} 395 */ 396 @Override 397 public String getText() { 398 return JXButton.this.getText(); 399 } 400 401 /** 402 * {@inheritDoc} 403 */ 404 @Override 405 public int getVerticalAlignment() { 406 return JXButton.this.getVerticalAlignment(); 407 } 408 409 /** 410 * {@inheritDoc} 411 */ 412 @Override 413 public int getVerticalTextPosition() { 414 return JXButton.this.getVerticalTextPosition(); 415 } 416 417 /** 418 * {@inheritDoc} 419 */ 420 @Override 421 public boolean isBorderPainted() { 422 return JXButton.this.isBorderPainted(); 423 } 424 425 /** 426 * {@inheritDoc} 427 */ 428 @Override 429 public boolean isContentAreaFilled() { 430 return false; 431 } 432 433 /** 434 * {@inheritDoc} 435 */ 436 @Override 437 public boolean hasFocus() { 438 return JXButton.this.hasFocus(); 439 } 440 441 /** 442 * {@inheritDoc} 443 */ 444 @Override 445 public boolean isFocusPainted() { 446 return JXButton.this.isFocusPainted(); 447 } 448 449 /** 450 * {@inheritDoc} 451 */ 452 @Override 453 public boolean isRolloverEnabled() { 454 return JXButton.this.isRolloverEnabled(); 455 } 456 457 /** 458 * {@inheritDoc} 459 */ 460 @Override 461 public boolean isSelected() { 462 return JXButton.this.isSelected(); 463 } 464 } 465 466 private ForegroundButton fgStamp; 467 private Painter fgPainter; 468 private PainterPaint fgPaint; 469 private BackgroundButton bgStamp; 470 private Painter bgPainter; 471 472 private boolean paintBorderInsets = true; 473 474 private Rectangle viewRect = new Rectangle(); 475 private Rectangle textRect = new Rectangle(); 476 private Rectangle iconRect = new Rectangle(); 477 478 /** 479 * Creates a button with no set text or icon. 480 */ 481 public JXButton() { 482 init(); 483 } 484 485 /** 486 * Creates a button with text. 487 * 488 * @param text 489 * the text of the button 490 */ 491 public JXButton(String text) { 492 super(text); 493 init(); 494 } 495 496 /** 497 * Creates a button where properties are taken from the {@code Action} supplied. 498 * 499 * @param a 500 * the {@code Action} used to specify the new button 501 */ 502 public JXButton(Action a) { 503 super(a); 504 init(); 505 } 506 507 /** 508 * Creates a button with an icon. 509 * 510 * @param icon 511 * the Icon image to display on the button 512 */ 513 public JXButton(Icon icon) { 514 super(icon); 515 init(); 516 } 517 518 /** 519 * Creates a button with initial text and an icon. 520 * 521 * @param text 522 * the text of the button 523 * @param icon 524 * the Icon image to display on the button 525 */ 526 public JXButton(String text, Icon icon) { 527 super(text, icon); 528 init(); 529 } 530 531 private void init() { 532 fgStamp = new ForegroundButton(); 533 } 534 535 /** 536 * Sets the background color for this component by 537 * 538 * @param bg 539 * the desired background <code>Color</code> 540 * @see javax.swing.JComponent#getBackground() 541 * @see #setOpaque 542 * 543 * @beaninfo 544 * preferred: true 545 * bound: true 546 * attribute: visualUpdate true 547 * description: The background color of the component. 548 */ 549 @Override 550 public void setBackground(Color bg) { 551 super.setBackground(bg); 552 553 SwingXUtilities.installBackground(this, bg); 554 } 555 556 /** 557 * {@inheritDoc} 558 */ 559 @Override 560 @SuppressWarnings("rawtypes") 561 public Painter getBackgroundPainter() { 562 return bgPainter; 563 } 564 565 /** 566 * {@inheritDoc} 567 */ 568 @Override 569 @SuppressWarnings("rawtypes") 570 public void setBackgroundPainter(Painter p) { 571 Painter old = getBackgroundPainter(); 572 this.bgPainter = p; 573 firePropertyChange("backgroundPainter", old, getBackgroundPainter()); 574 repaint(); 575 } 576 577 /** 578 * @return the foreground painter for this button 579 */ 580 @SuppressWarnings("rawtypes") 581 public Painter getForegroundPainter() { 582 return fgPainter; 583 } 584 585 @SuppressWarnings({ "unchecked", "rawtypes" }) 586 public void setForegroundPainter(Painter p) { 587 Painter old = getForegroundPainter(); 588 this.fgPainter = p; 589 590 if (fgPainter == null) { 591 fgPaint = null; 592 } else { 593 fgPaint = new PainterPaint(fgPainter, this); 594 595 if (bgStamp == null) { 596 bgStamp = new BackgroundButton(); 597 } 598 } 599 600 firePropertyChange("foregroundPainter", old, getForegroundPainter()); 601 repaint(); 602 } 603 604 /** 605 * Returns true if the background painter should paint where the border is 606 * or false if it should only paint inside the border. This property is 607 * true by default. This property affects the width, height, 608 * and initial transform passed to the background painter. 609 */ 610 @Override 611 public boolean isPaintBorderInsets() { 612 return paintBorderInsets; 613 } 614 615 /** 616 * Sets the paintBorderInsets property. 617 * Set to true if the background painter should paint where the border is 618 * or false if it should only paint inside the border. This property is true by default. 619 * This property affects the width, height, 620 * and initial transform passed to the background painter. 621 * 622 * This is a bound property. 623 */ 624 @Override 625 public void setPaintBorderInsets(boolean paintBorderInsets) { 626 boolean old = this.isPaintBorderInsets(); 627 this.paintBorderInsets = paintBorderInsets; 628 firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); 629 } 630 631 /** 632 * {@inheritDoc} 633 */ 634 @Override 635 public Dimension getPreferredSize() { 636 if (getComponentCount() == 1 && getComponent(0) instanceof CellRendererPane) { 637 return BasicGraphicsUtils.getPreferredButtonSize(fgStamp, getIconTextGap()); 638 } 639 640 return super.getPreferredSize(); 641 } 642 643 /** 644 * {@inheritDoc} 645 */ 646 @Override 647 protected void paintComponent(Graphics g) { 648 if (fgPainter == null && bgPainter == null) { 649 super.paintComponent(g); 650 } else { 651 if (fgPainter == null) { 652 Graphics2D g2d = (Graphics2D) g.create(); 653 654 try{ 655 paintWithoutForegroundPainter(g2d); 656 } finally { 657 g2d.dispose(); 658 } 659 } else if (fgPainter instanceof AbstractPainter && ((AbstractPainter<?>) fgPainter).getFilters().length > 0) { 660 paintWithForegroundPainterWithFilters(g); 661 } else { 662 Graphics2D g2d = (Graphics2D) g.create(); 663 664 try { 665 paintWithForegroundPainterWithoutFilters(g2d); 666 } finally { 667 g2d.dispose(); 668 } 669 } 670 } 671 } 672 673 private void paintWithoutForegroundPainter(Graphics2D g2d) { 674 if (bgPainter == null) { 675 SwingUtilities.paintComponent(g2d, bgStamp, this, 0, 0, getWidth(), getHeight()); 676 } else { 677 SwingXUtilities.paintBackground(this, g2d); 678 } 679 680 SwingUtilities.paintComponent(g2d, fgStamp, this, 0, 0, getWidth(), getHeight()); 681 } 682 683 private void paintWithForegroundPainterWithoutFilters(Graphics2D g2d) { 684 paintWithoutForegroundPainter(g2d); 685 686 if (getText() != null && !getText().isEmpty()) { 687 Insets i = getInsets(); 688 viewRect.x = i.left; 689 viewRect.y = i.top; 690 viewRect.width = getWidth() - (i.right + viewRect.x); 691 viewRect.height = getHeight() - (i.bottom + viewRect.y); 692 693 textRect.x = textRect.y = textRect.width = textRect.height = 0; 694 iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; 695 696 // layout the text and icon 697 String text = SwingUtilities.layoutCompoundLabel( 698 this, g2d.getFontMetrics(), getText(), getIcon(), 699 getVerticalAlignment(), getHorizontalAlignment(), 700 getVerticalTextPosition(), getHorizontalTextPosition(), 701 viewRect, iconRect, textRect, 702 getText() == null ? 0 : getIconTextGap()); 703 704 if (!isPaintBorderInsets()) { 705 g2d.translate(i.left, i.top); 706 } 707 708 g2d.setPaint(fgPaint); 709 BasicGraphicsUtils.drawStringUnderlineCharAt(g2d, text, getDisplayedMnemonicIndex(), 710 textRect.x, textRect.y + g2d.getFontMetrics().getAscent()); 711 } 712 } 713 714 private void paintWithForegroundPainterWithFilters(Graphics g) { 715 BufferedImage im = GraphicsUtilities.createCompatibleImage(getWidth(), getHeight()); 716 Graphics2D g2d = im.createGraphics(); 717 paintWithForegroundPainterWithoutFilters(g2d); 718 719 for (BufferedImageOp filter : ((AbstractPainter<?>) fgPainter).getFilters()) { 720 im = filter.filter(im, null); 721 } 722 723 g.drawImage(im, 0, 0, this); 724 } 725 726 /** 727 * Notification from the <code>UIManager</code> that the L&F has changed. 728 * Replaces the current UI object with the latest version from the <code>UIManager</code>. 729 * 730 * @see javax.swing.JComponent#updateUI 731 */ 732 @Override 733 public void updateUI() { 734 super.updateUI(); 735 736 if (bgStamp != null) { 737 bgStamp.updateUI(); 738 } 739 740 if (fgStamp != null) { 741 fgStamp.updateUI(); 742 } 743 } 744}