1 /*
   2  * Copyright (c) 2011, 2012, 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 com.apple.laf;
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.beans.PropertyChangeEvent;
  31 
  32 import javax.swing.*;
  33 import javax.swing.border.Border;
  34 import javax.swing.event.*;
  35 import javax.swing.plaf.*;
  36 import javax.swing.plaf.basic.*;
  37 import javax.swing.text.View;
  38 
  39 import sun.swing.SwingUtilities2;
  40 
  41 import apple.laf.JRSUIConstants.Size;
  42 
  43 import com.apple.laf.AquaButtonExtendedTypes.TypeSpecifier;
  44 import com.apple.laf.AquaUtilControlSize.Sizeable;
  45 import com.apple.laf.AquaUtils.*;
  46 
  47 public class AquaButtonUI extends BasicButtonUI implements Sizeable {
  48     private static final String BUTTON_TYPE = "JButton.buttonType";
  49     private static final String SEGMENTED_BUTTON_POSITION = "JButton.segmentPosition";
  50 
  51     protected static final RecyclableSingleton<AquaButtonUI> buttonUI = new RecyclableSingletonFromDefaultConstructor<AquaButtonUI>(AquaButtonUI.class);
  52     public static ComponentUI createUI(final JComponent c) {
  53         return buttonUI.get();
  54     }
  55 
  56     // Has the shared instance defaults been initialized?
  57     private boolean defaults_initialized = false;
  58     private Color defaultDisabledTextColor = null;
  59 
  60     protected void installDefaults(final AbstractButton b) {
  61         // load shared instance defaults
  62         final String pp = getPropertyPrefix();
  63 
  64         if (!defaults_initialized) {
  65             defaultDisabledTextColor = UIManager.getColor(pp + "disabledText");
  66             defaults_initialized = true;
  67         }
  68 
  69         setButtonMarginIfNeeded(b, UIManager.getInsets(pp + "margin"));
  70 
  71         LookAndFeel.installColorsAndFont(b, pp + "background", pp + "foreground", pp + "font");
  72         LookAndFeel.installProperty(b, "opaque", UIManager.getBoolean(pp + "opaque"));
  73 
  74         final Object borderProp = b.getClientProperty(BUTTON_TYPE);
  75         boolean hasBorder = false;
  76 
  77         if (borderProp != null) {
  78             hasBorder = setButtonType(b, borderProp);
  79         }
  80         if (!hasBorder) setThemeBorder(b);
  81 
  82         final Object segmentProp = b.getClientProperty(SEGMENTED_BUTTON_POSITION);
  83         if (segmentProp != null) {
  84             final Border border = b.getBorder();
  85             if (!(border instanceof AquaBorder)) return;
  86 
  87             b.setBorder(AquaButtonExtendedTypes.getBorderForPosition(b, b.getClientProperty(BUTTON_TYPE), segmentProp));
  88         }
  89     }
  90 
  91     public void applySizeFor(final JComponent c, final Size size) {
  92         // this space intentionally left blank
  93         // (subclasses need to do work here)
  94      }
  95 
  96     protected void setThemeBorder(final AbstractButton b) {
  97         // Set the correct border
  98         final ButtonUI genericUI = b.getUI();
  99         if (!(genericUI instanceof AquaButtonUI)) return;
 100         final AquaButtonUI ui = (AquaButtonUI)genericUI;
 101 
 102         Border border = b.getBorder();
 103         if (!ui.isBorderFromProperty(b) && (border == null || border instanceof UIResource || border instanceof AquaButtonBorder)) {
 104             // See BasicGraphicsUtils.getPreferredButtonSize - it returns null for preferred size,
 105             // causing it to use the subcomponent's size, which doesn't allow space for Aqua pushbuttons
 106             boolean iconFont = true;
 107             if (isOnToolbar(b)) {
 108                 if (b instanceof JToggleButton) {
 109                     border = AquaButtonBorder.getToolBarButtonBorder();
 110                 } else {
 111                     border = AquaButtonBorder.getBevelButtonBorder();
 112                 }
 113             } else if (b.getIcon() != null || b.getComponentCount() > 0) {
 114                 // radar 3308129 && (b.getText() == null || b.getText().equals("")))
 115                 // we used to only do this for buttons that had images and no text
 116                 // now we do it for all buttons that have any images - they cannot
 117                 // be a default button.
 118                 border = AquaButtonBorder.getToggleButtonBorder();
 119             } else {
 120                 border = UIManager.getBorder(getPropertyPrefix() + "border");
 121                 iconFont = false;
 122             }
 123 
 124             b.setBorder(border);
 125 
 126             final Font currentFont = b.getFont();
 127             if (iconFont && (currentFont == null || currentFont instanceof UIResource)) {
 128                 b.setFont(UIManager.getFont("IconButton.font"));
 129             }
 130         }
 131     }
 132 
 133     protected static boolean isOnToolbar(final AbstractButton b) {
 134         Component parent = b.getParent();
 135         while (parent != null) {
 136             if (parent instanceof JToolBar) return true;
 137             parent = parent.getParent();
 138         }
 139         return false;
 140     }
 141 
 142     // A state that affects border has changed.  Make sure we have the right one
 143     protected static void updateBorder(final AbstractButton b) {
 144         // See if the button has overridden the automatic button type
 145         final Object prop = b.getClientProperty(BUTTON_TYPE);
 146         if (prop != null) return;
 147 
 148         final ButtonUI ui = b.getUI();
 149         if (!(ui instanceof AquaButtonUI)) return;
 150         if (b.getBorder() != null) ((AquaButtonUI)ui).setThemeBorder(b);
 151     }
 152 
 153     protected void setButtonMarginIfNeeded(final AbstractButton b, final Insets insets) {
 154         final Insets margin = b.getMargin();
 155         if (margin == null || (margin instanceof UIResource)) {
 156             b.setMargin(insets);
 157         }
 158     }
 159 
 160     public boolean isBorderFromProperty(final AbstractButton button) {
 161         return button.getClientProperty(BUTTON_TYPE) != null;
 162     }
 163 
 164     protected boolean setButtonType(final AbstractButton b, final Object prop) {
 165         if (!(prop instanceof String)) {
 166             b.putClientProperty(BUTTON_TYPE, null); // so we know to use the automatic button type
 167             return false;
 168         }
 169 
 170         final String buttonType = (String)prop;
 171         boolean iconFont = true;
 172 
 173         final TypeSpecifier specifier = AquaButtonExtendedTypes.getSpecifierByName(buttonType);
 174         if (specifier != null) {
 175             b.setBorder(specifier.getBorder());
 176             iconFont = specifier.setIconFont;
 177         }
 178 
 179         final Font currentFont = b.getFont();
 180         if (currentFont == null || currentFont instanceof UIResource) {
 181             b.setFont(UIManager.getFont(iconFont ? "IconButton.font" : "Button.font"));
 182         }
 183 
 184         return true;
 185     }
 186 
 187     protected void installListeners(final AbstractButton b) {
 188         final AquaButtonListener listener = createButtonListener(b);
 189         if (listener != null) {
 190             // put the listener in the button's client properties so that
 191             // we can get at it later
 192             b.putClientProperty(this, listener);
 193 
 194             b.addMouseListener(listener);
 195             b.addMouseMotionListener(listener);
 196             b.addFocusListener(listener);
 197             b.addPropertyChangeListener(listener);
 198             b.addChangeListener(listener);
 199             b.addAncestorListener(listener);
 200         }
 201         installHierListener(b);
 202         AquaUtilControlSize.addSizePropertyListener(b);
 203     }
 204 
 205     protected void installKeyboardActions(final AbstractButton b) {
 206         final BasicButtonListener listener = (BasicButtonListener)b.getClientProperty(this);
 207         if (listener != null) listener.installKeyboardActions(b);
 208     }
 209 
 210     // Uninstall PLAF
 211     public void uninstallUI(final JComponent c) {
 212         uninstallKeyboardActions((AbstractButton)c);
 213         uninstallListeners((AbstractButton)c);
 214         uninstallDefaults((AbstractButton)c);
 215         //BasicHTML.updateRenderer(c, "");
 216     }
 217 
 218     protected void uninstallKeyboardActions(final AbstractButton b) {
 219         final BasicButtonListener listener = (BasicButtonListener)b.getClientProperty(this);
 220         if (listener != null) listener.uninstallKeyboardActions(b);
 221     }
 222 
 223     protected void uninstallListeners(final AbstractButton b) {
 224         final AquaButtonListener listener = (AquaButtonListener)b.getClientProperty(this);
 225         b.putClientProperty(this, null);
 226         if (listener != null) {
 227             b.removeMouseListener(listener);
 228             b.removeMouseListener(listener);
 229             b.removeMouseMotionListener(listener);
 230             b.removeFocusListener(listener);
 231             b.removeChangeListener(listener);
 232             b.removePropertyChangeListener(listener);
 233             b.removeAncestorListener(listener);
 234         }
 235         uninstallHierListener(b);
 236         AquaUtilControlSize.addSizePropertyListener(b);
 237     }
 238 
 239     protected void uninstallDefaults(final AbstractButton b) {
 240         LookAndFeel.uninstallBorder(b);
 241         defaults_initialized = false;
 242     }
 243 
 244     // Create Listeners
 245     protected AquaButtonListener createButtonListener(final AbstractButton b) {
 246         return new AquaButtonListener(b);
 247     }
 248 
 249     // Paint Methods
 250     public void paint(final Graphics g, final JComponent c) {
 251         final AbstractButton b = (AbstractButton)c;
 252         final ButtonModel model = b.getModel();
 253 
 254         final Insets i = c.getInsets();
 255 
 256         Rectangle viewRect = new Rectangle(b.getWidth(), b.getHeight());
 257         Rectangle iconRect = new Rectangle();
 258         Rectangle textRect = new Rectangle();
 259 
 260         // we are overdrawing here with translucent colors so we get
 261         // a darkening effect. How can we avoid it. Try clear rect?
 262         if (b.isOpaque()) {
 263             g.setColor(c.getBackground());
 264             g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
 265         }
 266 
 267         AquaButtonBorder aquaBorder = null;
 268         if (((AbstractButton)c).isBorderPainted()) {
 269             final Border border = c.getBorder();
 270 
 271             if (border instanceof AquaButtonBorder) {
 272                 // only do this if borders are on!
 273                 // this also takes care of focus painting.
 274                 aquaBorder = (AquaButtonBorder)border;
 275                 aquaBorder.paintButton(c, g, viewRect.x, viewRect.y, viewRect.width, viewRect.height);
 276             }
 277         } else {
 278             if (b.isOpaque()) {
 279                 viewRect.x = i.left - 2;
 280                 viewRect.y = i.top - 2;
 281                 viewRect.width = b.getWidth() - (i.right + viewRect.x) + 4;
 282                 viewRect.height = b.getHeight() - (i.bottom + viewRect.y) + 4;
 283                 if (b.isContentAreaFilled() || model.isSelected()) {
 284                     if (model.isSelected()) // Toggle buttons
 285                     g.setColor(c.getBackground().darker());
 286                     else g.setColor(c.getBackground());
 287                     g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
 288                 }
 289             }
 290 
 291             // needs focus to be painted
 292             // for now we don't know exactly what to do...we'll see!
 293             if (b.isFocusPainted() && b.hasFocus()) {
 294                 // paint UI specific focus
 295                 paintFocus(g, b, viewRect, textRect, iconRect);
 296             }
 297         }
 298 
 299         // performs icon and text rect calculations
 300         final String text = layoutAndGetText(g, b, aquaBorder, i, viewRect, iconRect, textRect);
 301 
 302         // Paint the Icon
 303         if (b.getIcon() != null) {
 304             paintIcon(g, b, iconRect);
 305         }
 306 
 307         if (textRect.width == 0) {
 308             textRect.width = 50;
 309         }
 310 
 311         if (text != null && !text.equals("")) {
 312             final View v = (View)c.getClientProperty(BasicHTML.propertyKey);
 313             if (v != null) {
 314                 v.paint(g, textRect);
 315             } else {
 316                 paintText(g, b, textRect, text);
 317             }
 318         }
 319     }
 320 
 321     protected String layoutAndGetText(final Graphics g, final AbstractButton b, final AquaButtonBorder aquaBorder, final Insets i, Rectangle viewRect, Rectangle iconRect, Rectangle textRect) {
 322         // re-initialize the view rect to the selected insets
 323         viewRect.x = i.left;
 324         viewRect.y = i.top;
 325         viewRect.width = b.getWidth() - (i.right + viewRect.x);
 326         viewRect.height = b.getHeight() - (i.bottom + viewRect.y);
 327 
 328         // reset the text and icon rects
 329         textRect.x = textRect.y = textRect.width = textRect.height = 0;
 330         iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
 331 
 332         // setup the font
 333         g.setFont(b.getFont());
 334         final FontMetrics fm = g.getFontMetrics();
 335 
 336         // layout the text and icon
 337         final String originalText = b.getText();
 338         final String text = SwingUtilities.layoutCompoundLabel(b, fm, originalText, b.getIcon(), b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), viewRect, iconRect, textRect, originalText == null ? 0 : b.getIconTextGap());
 339         if (text == originalText || aquaBorder == null) return text; // everything fits
 340 
 341         // if the text didn't fit - check if the aqua border has alternate Insets that are more adhering
 342         final Insets alternateContentInsets = aquaBorder.getContentInsets(b, b.getWidth(), b.getHeight());
 343         if (alternateContentInsets != null) {
 344             // recursively call and don't pass AquaBorder
 345             return layoutAndGetText(g, b, null, alternateContentInsets, viewRect, iconRect, textRect);
 346         }
 347 
 348         // there is no Aqua border, go with what we've got
 349         return text;
 350     }
 351 
 352     protected void paintIcon(final Graphics g, final AbstractButton b, final Rectangle localIconRect) {
 353         final ButtonModel model = b.getModel();
 354         Icon icon = b.getIcon();
 355         Icon tmpIcon = null;
 356 
 357         if (icon == null) return;
 358 
 359         if (!model.isEnabled()) {
 360             if (model.isSelected()) {
 361                 tmpIcon = b.getDisabledSelectedIcon();
 362             } else {
 363                 tmpIcon = b.getDisabledIcon();
 364             }
 365         } else if (model.isPressed() && model.isArmed()) {
 366             tmpIcon = b.getPressedIcon();
 367             if (tmpIcon == null) {
 368                 if (icon instanceof ImageIcon) {
 369                     tmpIcon = new ImageIcon(AquaUtils.generateSelectedDarkImage(((ImageIcon)icon).getImage()));
 370                 }
 371             }
 372         } else if (b.isRolloverEnabled() && model.isRollover()) {
 373             if (model.isSelected()) {
 374                 tmpIcon = b.getRolloverSelectedIcon();
 375             } else {
 376                 tmpIcon = b.getRolloverIcon();
 377             }
 378         } else if (model.isSelected()) {
 379             tmpIcon = b.getSelectedIcon();
 380         }
 381 
 382         if (model.isEnabled() && b.isFocusOwner() && b.getBorder() instanceof AquaButtonBorder.Toolbar) {
 383             if (tmpIcon == null) tmpIcon = icon;
 384             if (tmpIcon instanceof ImageIcon) {
 385                 tmpIcon = AquaFocus.createFocusedIcon(tmpIcon, b, 3);
 386                 tmpIcon.paintIcon(b, g, localIconRect.x - 3, localIconRect.y - 3);
 387                 return;
 388             }
 389         }
 390 
 391         if (tmpIcon != null) {
 392             icon = tmpIcon;
 393         }
 394 
 395         icon.paintIcon(b, g, localIconRect.x, localIconRect.y);
 396     }
 397 
 398     /**
 399      * As of Java 2 platform v 1.4 this method should not be used or overriden.
 400      * Use the paintText method which takes the AbstractButton argument.
 401      */
 402     protected void paintText(final Graphics g, final JComponent c, final Rectangle localTextRect, final String text) {
 403         final Graphics2D g2d = g instanceof Graphics2D ? (Graphics2D)g : null;
 404 
 405         final AbstractButton b = (AbstractButton)c;
 406         final ButtonModel model = b.getModel();
 407         final FontMetrics fm = g.getFontMetrics();
 408         final int mnemonicIndex = AquaMnemonicHandler.isMnemonicHidden() ? -1 : b.getDisplayedMnemonicIndex();
 409 
 410         /* Draw the Text */
 411         if (model.isEnabled()) {
 412             /*** paint the text normally */
 413             g.setColor(b.getForeground());
 414         } else {
 415             /*** paint the text disabled ***/
 416             g.setColor(defaultDisabledTextColor);
 417         }
 418         getTextUIDrawing().drawStringUnderlineCharAt(c, g, text, mnemonicIndex,
 419                                                      localTextRect.x,
 420                                                      localTextRect.y + fm.getAscent());
 421     }
 422 
 423     protected void paintText(final Graphics g, final AbstractButton b, final Rectangle localTextRect, final String text) {
 424         paintText(g, (JComponent)b, localTextRect, text);
 425     }
 426 
 427     protected void paintButtonPressed(final Graphics g, final AbstractButton b) {
 428         paint(g, b);
 429     }
 430 
 431     // Layout Methods
 432     public Dimension getMinimumSize(final JComponent c) {
 433         final Dimension d = getPreferredSize(c);
 434         final View v = (View)c.getClientProperty(BasicHTML.propertyKey);
 435         if (v != null) {
 436             d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
 437         }
 438         return d;
 439     }
 440 
 441     public Dimension getPreferredSize(final JComponent c) {
 442         final AbstractButton b = (AbstractButton)c;
 443 
 444         // fix for Radar #3134273
 445         final Dimension d = BasicGraphicsUtils.getPreferredButtonSize(b, b.getIconTextGap());
 446         if (d == null) return null;
 447 
 448         final Border border = b.getBorder();
 449         if (border instanceof AquaButtonBorder) {
 450             ((AquaButtonBorder)border).alterPreferredSize(d);
 451         }
 452 
 453         return d;
 454     }
 455 
 456     public Dimension getMaximumSize(final JComponent c) {
 457         final Dimension d = getPreferredSize(c);
 458 
 459         final View v = (View)c.getClientProperty(BasicHTML.propertyKey);
 460         if (v != null) {
 461             d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
 462         }
 463 
 464         return d;
 465     }
 466 
 467     static final RecyclableSingleton<AquaHierarchyButtonListener> fHierListener = new RecyclableSingletonFromDefaultConstructor<AquaHierarchyButtonListener>(AquaHierarchyButtonListener.class);
 468     static AquaHierarchyButtonListener getAquaHierarchyButtonListener() {
 469         return fHierListener.get();
 470     }
 471 
 472     // We need to know when ordinary JButtons are put on JToolbars, but not JComboBoxButtons
 473     // JToggleButtons always have the same border
 474 
 475     private boolean shouldInstallHierListener(final AbstractButton b) {
 476         return  (b instanceof JButton || b instanceof JToggleButton && !(b instanceof AquaComboBoxButton) && !(b instanceof JCheckBox) && !(b instanceof JRadioButton));
 477     }
 478 
 479     protected void installHierListener(final AbstractButton b) {
 480         if (shouldInstallHierListener(b)) {
 481             // super put the listener in the button's client properties
 482             b.addHierarchyListener(getAquaHierarchyButtonListener());
 483         }
 484     }
 485 
 486     protected void uninstallHierListener(final AbstractButton b) {
 487         if (shouldInstallHierListener(b)) {
 488             b.removeHierarchyListener(getAquaHierarchyButtonListener());
 489         }
 490     }
 491 
 492     static class AquaHierarchyButtonListener implements HierarchyListener {
 493         // Everytime a hierarchy is change we need to check if the button if moved on or from
 494         // a toolbar. If that is the case, we need to re-set the border of the button.
 495         public void hierarchyChanged(final HierarchyEvent e) {
 496             if ((e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) == 0) return;
 497 
 498             final Object o = e.getSource();
 499             if (!(o instanceof AbstractButton)) return;
 500 
 501             final AbstractButton b = (AbstractButton)o;
 502             final ButtonUI ui = b.getUI();
 503             if (!(ui instanceof AquaButtonUI)) return;
 504 
 505             if (!(b.getBorder() instanceof UIResource)) return; // if the border is not one of ours, or null
 506             ((AquaButtonUI)ui).setThemeBorder(b);
 507         }
 508     }
 509 
 510     class AquaButtonListener extends BasicButtonListener implements AncestorListener {
 511         protected final AbstractButton b;
 512 
 513         public AquaButtonListener(final AbstractButton b) {
 514             super(b);
 515             this.b = b;
 516         }
 517 
 518         public void focusGained(final FocusEvent e) {
 519             ((Component)e.getSource()).repaint();
 520         }
 521 
 522         public void focusLost(final FocusEvent e) {
 523             // 10-06-03 VL: [Radar 3187049]
 524             // If focusLost arrives while the button has been left-clicked this would disarm the button,
 525             // causing actionPerformed not to fire on mouse release!
 526             //b.getModel().setArmed(false);
 527             ((Component)e.getSource()).repaint();
 528         }
 529 
 530         public void propertyChange(final PropertyChangeEvent e) {
 531             super.propertyChange(e);
 532 
 533             final String propertyName = e.getPropertyName();
 534 
 535             // Repaint the button, since its border needs to handle the new state.
 536             if (AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(propertyName)) {
 537                 b.repaint();
 538                 return;
 539             }
 540 
 541             if ("icon".equals(propertyName) || "text".equals(propertyName)) {
 542                 setThemeBorder(b);
 543                 return;
 544             }
 545 
 546             if (BUTTON_TYPE.equals(propertyName)) {
 547                 // Forced border types
 548                 final String value = (String)e.getNewValue();
 549 
 550                 final Border border = AquaButtonExtendedTypes.getBorderForPosition(b, value, b.getClientProperty(SEGMENTED_BUTTON_POSITION));
 551                 if (border != null) {
 552                     b.setBorder(border);
 553                 }
 554 
 555                 return;
 556             }
 557 
 558             if (SEGMENTED_BUTTON_POSITION.equals(propertyName)) {
 559                 final Border border = b.getBorder();
 560                 if (!(border instanceof AquaBorder)) return;
 561 
 562                 b.setBorder(AquaButtonExtendedTypes.getBorderForPosition(b, b.getClientProperty(BUTTON_TYPE), e.getNewValue()));
 563             }
 564 
 565             if ("componentOrientation".equals(propertyName)) {
 566                 final Border border = b.getBorder();
 567                 if (!(border instanceof AquaBorder)) return;
 568 
 569                 Object buttonType = b.getClientProperty(BUTTON_TYPE);
 570                 Object buttonPosition = b.getClientProperty(SEGMENTED_BUTTON_POSITION);
 571                 if (buttonType != null && buttonPosition != null) {
 572                     b.setBorder(AquaButtonExtendedTypes.getBorderForPosition(b, buttonType, buttonPosition));
 573                 }
 574             }
 575         }
 576 
 577         public void ancestorMoved(final AncestorEvent e) {}
 578 
 579         public void ancestorAdded(final AncestorEvent e) {
 580             updateDefaultButton();
 581         }
 582 
 583         public void ancestorRemoved(final AncestorEvent e) {
 584             updateDefaultButton();
 585         }
 586 
 587         protected void updateDefaultButton() {
 588             if (!(b instanceof JButton)) return;
 589             if (!((JButton)b).isDefaultButton()) return;
 590 
 591             final JRootPane rootPane = b.getRootPane();
 592             if (rootPane == null) return;
 593 
 594             final RootPaneUI ui = rootPane.getUI();
 595             if (!(ui instanceof AquaRootPaneUI)) return;
 596             ((AquaRootPaneUI)ui).updateDefaultButton(rootPane);
 597         }
 598     }
 599 }