4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/Apackage com.apple.laf;
4632N/A
4632N/Aimport java.awt.*;
4632N/Aimport java.awt.event.*;
4632N/Aimport java.beans.PropertyChangeEvent;
4632N/A
4632N/Aimport javax.swing.*;
4632N/Aimport javax.swing.event.*;
4632N/Aimport javax.swing.plaf.*;
4632N/Aimport javax.swing.plaf.basic.BasicRootPaneUI;
4632N/A
4632N/Aimport com.apple.laf.AquaUtils.RecyclableSingleton;
4632N/Aimport com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor;
4632N/A
4632N/A/**
4632N/A * From AquaRootPaneUI.java
4632N/A *
4632N/A * The JRootPane manages the default button. There can be only one active rootpane,
4632N/A * and one default button, so we need only one timer
4632N/A *
4632N/A * AquaRootPaneUI is a singleton object
4632N/A */
4632N/Apublic class AquaRootPaneUI extends BasicRootPaneUI implements AncestorListener, WindowListener, ContainerListener {
4632N/A private static final RecyclableSingleton<AquaRootPaneUI> sRootPaneUI = new RecyclableSingletonFromDefaultConstructor<AquaRootPaneUI>(AquaRootPaneUI.class);
4632N/A
4632N/A final static int kDefaultButtonPaintDelayBetweenFrames = 50;
4632N/A JButton fCurrentDefaultButton = null;
4632N/A Timer fTimer = null;
4632N/A static final boolean sUseScreenMenuBar = AquaMenuBarUI.getScreenMenuBarProperty();
4632N/A
4632N/A public static ComponentUI createUI(final JComponent c) {
4632N/A return sRootPaneUI.get();
4632N/A }
4632N/A
4632N/A public void installUI(final JComponent c) {
4632N/A super.installUI(c);
4632N/A c.addAncestorListener(this);
4632N/A
4632N/A if (c.isShowing() && c.isEnabled()) {
4632N/A updateDefaultButton((JRootPane)c);
4632N/A }
4632N/A
4632N/A // for <rdar://problem/3689020> REGR: Realtime LAF updates no longer work
4632N/A //
4632N/A // because the JFrame parent has a LAF background set (why without a UI element I don't know!)
4632N/A // we have to set it from the root pane so when we are coming from metal we will set it to
4632N/A // the aqua color.
4632N/A // This is because the aqua color is a magical color that gets the background of the window,
4632N/A // so for most other LAFs the root pane changing is enough since it would be opaque, but for us
4632N/A // it is not since we are going to grab the one that was set on the JFrame. :(
4632N/A final Component parent = c.getParent();
4632N/A
4632N/A if (parent != null && parent instanceof JFrame) {
4632N/A final JFrame frameParent = (JFrame)parent;
4632N/A final Color bg = frameParent.getBackground();
4632N/A if (bg == null || bg instanceof UIResource) {
4632N/A frameParent.setBackground(UIManager.getColor("Panel.background"));
4632N/A }
4632N/A }
4632N/A
4632N/A // for <rdar://problem/3750909> OutOfMemoryError swapping menus.
4632N/A // Listen for layered pane/JMenuBar updates if the screen menu bar is active.
4632N/A if (sUseScreenMenuBar) {
4632N/A final JRootPane root = (JRootPane)c;
4632N/A root.addContainerListener(this);
4632N/A root.getLayeredPane().addContainerListener(this);
4632N/A }
4632N/A }
4632N/A
4632N/A public void uninstallUI(final JComponent c) {
4632N/A stopTimer();
4632N/A c.removeAncestorListener(this);
4632N/A
4632N/A if (sUseScreenMenuBar) {
4632N/A final JRootPane root = (JRootPane)c;
4632N/A root.removeContainerListener(this);
4632N/A root.getLayeredPane().removeContainerListener(this);
4632N/A }
4632N/A
4632N/A super.uninstallUI(c);
4632N/A }
4632N/A
4632N/A /**
4632N/A * If the screen menu bar is active we need to listen to the layered pane of the root pane
4632N/A * because it holds the JMenuBar. So, if a new layered pane was added, listen to it.
4632N/A * If a new JMenuBar was added, tell the menu bar UI, because it will need to update the menu bar.
4632N/A */
4632N/A public void componentAdded(final ContainerEvent e) {
4632N/A if (e.getContainer() instanceof JRootPane) {
4632N/A final JRootPane root = (JRootPane)e.getContainer();
4632N/A if (e.getChild() == root.getLayeredPane()) {
4632N/A final JLayeredPane layered = root.getLayeredPane();
4632N/A layered.addContainerListener(this);
4632N/A }
4632N/A } else {
4632N/A if (e.getChild() instanceof JMenuBar) {
4632N/A final JMenuBar jmb = (JMenuBar)e.getChild();
4632N/A final MenuBarUI mbui = jmb.getUI();
4632N/A
4632N/A if (mbui instanceof AquaMenuBarUI) {
4632N/A final Window owningWindow = SwingUtilities.getWindowAncestor(jmb);
4632N/A
4632N/A // Could be a JDialog, and may have been added to a JRootPane not yet in a window.
4632N/A if (owningWindow != null && owningWindow instanceof JFrame) {
4632N/A ((AquaMenuBarUI)mbui).setScreenMenuBar((JFrame)owningWindow);
4632N/A }
4632N/A }
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A /**
4632N/A * Likewise, when the layered pane is removed from the root pane, stop listening to it.
4632N/A * If the JMenuBar is removed, tell the menu bar UI to clear the menu bar.
4632N/A */
4632N/A public void componentRemoved(final ContainerEvent e) {
4632N/A if (e.getContainer() instanceof JRootPane) {
4632N/A final JRootPane root = (JRootPane)e.getContainer();
4632N/A if (e.getChild() == root.getLayeredPane()) {
4632N/A final JLayeredPane layered = root.getLayeredPane();
4632N/A layered.removeContainerListener(this);
4632N/A }
4632N/A } else {
4632N/A if (e.getChild() instanceof JMenuBar) {
4632N/A final JMenuBar jmb = (JMenuBar)e.getChild();
4632N/A final MenuBarUI mbui = jmb.getUI();
4632N/A
4632N/A if (mbui instanceof AquaMenuBarUI) {
4632N/A final Window owningWindow = SwingUtilities.getWindowAncestor(jmb);
4632N/A
4632N/A // Could be a JDialog, and may have been added to a JRootPane not yet in a window.
4632N/A if (owningWindow != null && owningWindow instanceof JFrame) {
4632N/A ((AquaMenuBarUI)mbui).clearScreenMenuBar((JFrame)owningWindow);
4632N/A }
4632N/A }
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A /**
4632N/A * Invoked when a property changes on the root pane. If the event
4632N/A * indicates the <code>defaultButton</code> has changed, this will
4632N/A * update the animation.
4632N/A * If the enabled state changed, it will start or stop the animation
4632N/A */
4632N/A public void propertyChange(final PropertyChangeEvent e) {
4632N/A super.propertyChange(e);
4632N/A
4632N/A final String prop = e.getPropertyName();
4632N/A if ("defaultButton".equals(prop) || "temporaryDefaultButton".equals(prop)) {
4632N/A // Change the animating button if this root is showing and enabled
4632N/A // otherwise do nothing - someone else may be active
4632N/A final JRootPane root = (JRootPane)e.getSource();
4632N/A
4632N/A if (root.isShowing() && root.isEnabled()) {
4632N/A updateDefaultButton(root);
4632N/A }
4632N/A } else if ("enabled".equals(prop) || AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(prop)) {
4632N/A final JRootPane root = (JRootPane)e.getSource();
4632N/A if (root.isShowing()) {
4632N/A if (((Boolean)e.getNewValue()).booleanValue()) {
4632N/A updateDefaultButton((JRootPane)e.getSource());
4632N/A } else {
4632N/A stopTimer();
4632N/A }
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A synchronized void stopTimer() {
4632N/A if (fTimer != null) {
4632N/A fTimer.stop();
4632N/A fTimer = null;
4632N/A }
4632N/A }
4632N/A
4632N/A synchronized void updateDefaultButton(final JRootPane root) {
4632N/A final JButton button = root.getDefaultButton();
4632N/A //System.err.println("in updateDefaultButton button = " + button);
4632N/A fCurrentDefaultButton = button;
4632N/A stopTimer();
4632N/A if (button != null) {
4632N/A fTimer = new Timer(kDefaultButtonPaintDelayBetweenFrames, new DefaultButtonPainter(root));
4632N/A fTimer.start();
4632N/A }
4632N/A }
4632N/A
4632N/A class DefaultButtonPainter implements ActionListener {
4632N/A JRootPane root;
4632N/A
4632N/A public DefaultButtonPainter(final JRootPane root) {
4632N/A this.root = root;
4632N/A }
4632N/A
4632N/A public void actionPerformed(final ActionEvent e) {
4632N/A final JButton defaultButton = root.getDefaultButton();
4632N/A if ((defaultButton != null) && defaultButton.isShowing()) {
4632N/A if (defaultButton.isEnabled()) {
4632N/A defaultButton.repaint();
4632N/A }
4632N/A } else {
4632N/A stopTimer();
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A /**
4632N/A * This is sort of like viewDidMoveToWindow:. When the root pane is put into a window
4632N/A * this method gets called for the notification.
4632N/A * We need to set up the listener relationship so we can pick up activation events.
4632N/A * And, if a JMenuBar was added before the root pane was added to the window, we now need
4632N/A * to notify the menu bar UI.
4632N/A */
4632N/A public void ancestorAdded(final AncestorEvent event) {
4632N/A // this is so we can handle window activated and deactivated events so
4632N/A // our swing controls can color/enable/disable/focus draw correctly
4632N/A final Container ancestor = event.getComponent();
4632N/A final Window owningWindow = SwingUtilities.getWindowAncestor(ancestor);
4632N/A
4632N/A if (owningWindow != null) {
4632N/A // We get this message even when a dialog is opened and the owning window is a window
4632N/A // that could already be listened to. We should only be a listener once.
4632N/A // adding multiple listeners was the cause of <rdar://problem/3534047>
4632N/A // but the incorrect removal of them caused <rdar://problem/3617848>
4632N/A owningWindow.removeWindowListener(this);
4632N/A owningWindow.addWindowListener(this);
4632N/A }
4632N/A
4632N/A // The root pane has been added to the hierarchy. If it's enabled update the default
4632N/A // button to start the throbbing. Since the UI is a singleton make sure the root pane
4632N/A // we are checking has a default button before calling update otherwise we will stop
4632N/A // throbbing the current default button.
4632N/A final JComponent comp = event.getComponent();
4632N/A if (comp instanceof JRootPane) {
4632N/A final JRootPane rp = (JRootPane)comp;
4632N/A if (rp.isEnabled() && rp.getDefaultButton() != null) {
4632N/A updateDefaultButton((JRootPane)comp);
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A /**
4632N/A * If the JRootPane was removed from the window we should clear the screen menu bar.
4632N/A * That's a non-trivial problem, because you need to know which window the JRootPane was in
4632N/A * before it was removed. By the time ancestorRemoved was called, the JRootPane has already been removed
4632N/A */
4632N/A
4632N/A public void ancestorRemoved(final AncestorEvent event) { }
4632N/A public void ancestorMoved(final AncestorEvent event) { }
4632N/A
4632N/A public void windowActivated(final WindowEvent e) {
4632N/A updateComponentTreeUIActivation((Component)e.getSource(), Boolean.TRUE);
4632N/A }
4632N/A
4632N/A public void windowDeactivated(final WindowEvent e) {
4632N/A updateComponentTreeUIActivation((Component)e.getSource(), Boolean.FALSE);
4632N/A }
4632N/A
4632N/A public void windowOpened(final WindowEvent e) { }
4632N/A public void windowClosing(final WindowEvent e) { }
4632N/A
4632N/A public void windowClosed(final WindowEvent e) {
4632N/A // We know the window is closed so remove the listener.
4632N/A final Window w = e.getWindow();
4632N/A w.removeWindowListener(this);
4632N/A }
4632N/A
4632N/A public void windowIconified(final WindowEvent e) { }
4632N/A public void windowDeiconified(final WindowEvent e) { }
4632N/A public void windowStateChanged(final WindowEvent e) { }
4632N/A public void windowGainedFocus(final WindowEvent e) { }
4632N/A public void windowLostFocus(final WindowEvent e) { }
4632N/A
4632N/A private static void updateComponentTreeUIActivation(final Component c, Object active) {
4632N/A if (c instanceof javax.swing.JInternalFrame) {
4632N/A active = (((JInternalFrame)c).isSelected() ? Boolean.TRUE : Boolean.FALSE);
4632N/A }
4632N/A
4632N/A if (c instanceof javax.swing.JComponent) {
4632N/A ((javax.swing.JComponent)c).putClientProperty(AquaFocusHandler.FRAME_ACTIVE_PROPERTY, active);
4632N/A }
4632N/A
4632N/A Component[] children = null;
4632N/A
4632N/A if (c instanceof javax.swing.JMenu) {
4632N/A children = ((javax.swing.JMenu)c).getMenuComponents();
4632N/A } else if (c instanceof Container) {
4632N/A children = ((Container)c).getComponents();
4632N/A }
4632N/A
4632N/A if (children == null) return;
4632N/A
4632N/A for (final Component element : children) {
4632N/A updateComponentTreeUIActivation(element, active);
4632N/A }
4632N/A }
5233N/A
5233N/A @Override
5233N/A public final void update(final Graphics g, final JComponent c) {
5233N/A if (c.isOpaque()) {
5233N/A AquaUtils.fillRect(g, c);
5233N/A }
5233N/A paint(g, c);
5233N/A }
4632N/A}