1 /*
   2  * Copyright (c) 1999, 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 java.awt.event.ActionEvent;
  29 import java.awt.KeyboardFocusManager;
  30 import java.awt.Component;
  31 import java.awt.Point;
  32 import java.awt.Rectangle;
  33 import java.beans.PropertyChangeEvent;
  34 import java.beans.PropertyChangeListener;
  35 import javax.swing.*;
  36 import javax.swing.plaf.*;
  37 import sun.swing.DefaultLookup;
  38 import sun.swing.UIAction;
  39 
  40 /**
  41  * Basic implementation of RootPaneUI, there is one shared between all
  42  * JRootPane instances.
  43  *
  44  * @author Scott Violet
  45  * @since 1.3
  46  */
  47 public class BasicRootPaneUI extends RootPaneUI implements
  48                   PropertyChangeListener {
  49     private static RootPaneUI rootPaneUI = new BasicRootPaneUI();
  50 
  51     public static ComponentUI createUI(JComponent c) {
  52         return rootPaneUI;
  53     }
  54 
  55     public void installUI(JComponent c) {
  56         installDefaults((JRootPane)c);
  57         installComponents((JRootPane)c);
  58         installListeners((JRootPane)c);
  59         installKeyboardActions((JRootPane)c);
  60     }
  61 
  62 
  63     public void uninstallUI(JComponent c) {
  64         uninstallDefaults((JRootPane)c);
  65         uninstallComponents((JRootPane)c);
  66         uninstallListeners((JRootPane)c);
  67         uninstallKeyboardActions((JRootPane)c);
  68     }
  69 
  70     protected void installDefaults(JRootPane c){
  71         LookAndFeel.installProperty(c, "opaque", Boolean.FALSE);
  72     }
  73 
  74     protected void installComponents(JRootPane root) {
  75     }
  76 
  77     protected void installListeners(JRootPane root) {
  78         root.addPropertyChangeListener(this);
  79     }
  80 
  81     protected void installKeyboardActions(JRootPane root) {
  82         InputMap km = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, root);
  83         SwingUtilities.replaceUIInputMap(root,
  84                 JComponent.WHEN_IN_FOCUSED_WINDOW, km);
  85         km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  86                 root);
  87         SwingUtilities.replaceUIInputMap(root,
  88                 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km);
  89 
  90         LazyActionMap.installLazyActionMap(root, BasicRootPaneUI.class,
  91                 "RootPane.actionMap");
  92         updateDefaultButtonBindings(root);
  93     }
  94 
  95     protected void uninstallDefaults(JRootPane root) {
  96     }
  97 
  98     protected void uninstallComponents(JRootPane root) {
  99     }
 100 
 101     protected void uninstallListeners(JRootPane root) {
 102         root.removePropertyChangeListener(this);
 103     }
 104 
 105     protected void uninstallKeyboardActions(JRootPane root) {
 106         SwingUtilities.replaceUIInputMap(root, JComponent.
 107                                        WHEN_IN_FOCUSED_WINDOW, null);
 108         SwingUtilities.replaceUIActionMap(root, null);
 109     }
 110 
 111     InputMap getInputMap(int condition, JComponent c) {
 112         if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
 113             return (InputMap)DefaultLookup.get(c, this,
 114                                        "RootPane.ancestorInputMap");
 115         }
 116 
 117         if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) {
 118             return createInputMap(condition, c);
 119         }
 120         return null;
 121     }
 122 
 123     ComponentInputMap createInputMap(int condition, JComponent c) {
 124         return new RootPaneInputMap(c);
 125     }
 126 
 127     static void loadActionMap(LazyActionMap map) {
 128         map.put(new Actions(Actions.PRESS));
 129         map.put(new Actions(Actions.RELEASE));
 130         map.put(new Actions(Actions.POST_POPUP));
 131     }
 132 
 133     /**
 134      * Invoked when the default button property has changed. This reloads
 135      * the bindings from the defaults table with name
 136      * <code>RootPane.defaultButtonWindowKeyBindings</code>.
 137      */
 138     void updateDefaultButtonBindings(JRootPane root) {
 139         InputMap km = SwingUtilities.getUIInputMap(root, JComponent.
 140                                                WHEN_IN_FOCUSED_WINDOW);
 141         while (km != null && !(km instanceof RootPaneInputMap)) {
 142             km = km.getParent();
 143         }
 144         if (km != null) {
 145             km.clear();
 146             if (root.getDefaultButton() != null) {
 147                 Object[] bindings = (Object[])DefaultLookup.get(root, this,
 148                            "RootPane.defaultButtonWindowKeyBindings");
 149                 if (bindings != null) {
 150                     LookAndFeel.loadKeyBindings(km, bindings);
 151                 }
 152             }
 153         }
 154     }
 155 
 156     /**
 157      * Invoked when a property changes on the root pane. If the event
 158      * indicates the <code>defaultButton</code> has changed, this will
 159      * reinstall the keyboard actions.
 160      */
 161     public void propertyChange(PropertyChangeEvent e) {
 162         if(e.getPropertyName().equals("defaultButton")) {
 163             JRootPane rootpane = (JRootPane)e.getSource();
 164             updateDefaultButtonBindings(rootpane);
 165             if (rootpane.getClientProperty("temporaryDefaultButton") == null) {
 166                 rootpane.putClientProperty("initialDefaultButton", e.getNewValue());
 167             }
 168         }
 169     }
 170 
 171 
 172     static class Actions extends UIAction {
 173         public static final String PRESS = "press";
 174         public static final String RELEASE = "release";
 175         public static final String POST_POPUP = "postPopup";
 176 
 177         Actions(String name) {
 178             super(name);
 179         }
 180 
 181         public void actionPerformed(ActionEvent evt) {
 182             JRootPane root = (JRootPane)evt.getSource();
 183             JButton owner = root.getDefaultButton();
 184             String key = getName();
 185 
 186             if (key == POST_POPUP) { // Action to post popup
 187                 Component c = KeyboardFocusManager
 188                         .getCurrentKeyboardFocusManager()
 189                          .getFocusOwner();
 190 
 191                 if(c instanceof JComponent) {
 192                     JComponent src = (JComponent) c;
 193                     JPopupMenu jpm = src.getComponentPopupMenu();
 194                     if(jpm != null) {
 195                         Point pt = src.getPopupLocation(null);
 196                         if(pt == null) {
 197                             Rectangle vis = src.getVisibleRect();
 198                             pt = new Point(vis.x+vis.width/2,
 199                                            vis.y+vis.height/2);
 200                         }
 201                         jpm.show(c, pt.x, pt.y);
 202                     }
 203                 }
 204             }
 205             else if (owner != null
 206                      && SwingUtilities.getRootPane(owner) == root) {
 207                 if (key == PRESS) {
 208                     owner.doClick(20);
 209                 }
 210             }
 211         }
 212 
 213         public boolean isEnabled(Object sender) {
 214             String key = getName();
 215             if(key == POST_POPUP) {
 216                 MenuElement[] elems = MenuSelectionManager
 217                         .defaultManager()
 218                         .getSelectedPath();
 219                 if(elems != null && elems.length != 0) {
 220                     return false;
 221                     // We shall not interfere with already opened menu
 222                 }
 223 
 224                 Component c = KeyboardFocusManager
 225                        .getCurrentKeyboardFocusManager()
 226                         .getFocusOwner();
 227                 if(c instanceof JComponent) {
 228                     JComponent src = (JComponent) c;
 229                     return src.getComponentPopupMenu() != null;
 230                 }
 231 
 232                 return false;
 233             }
 234 
 235             if (sender != null && sender instanceof JRootPane) {
 236                 JButton owner = ((JRootPane)sender).getDefaultButton();
 237                 return (owner != null && owner.getModel().isEnabled());
 238             }
 239             return true;
 240         }
 241     }
 242 
 243     private static class RootPaneInputMap extends ComponentInputMapUIResource {
 244         public RootPaneInputMap(JComponent c) {
 245             super(c);
 246         }
 247     }
 248 }