1 /* 2 * Copyright (c) 1999, 2016, 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 /** 52 * Returns a new instance of {@code BasicRootPaneUI}. 53 * 54 * @param c a component 55 * @return a new instance of {@code BasicRootPaneUI} 56 */ createUI(JComponent c)57 public static ComponentUI createUI(JComponent c) { 58 return rootPaneUI; 59 } 60 installUI(JComponent c)61 public void installUI(JComponent c) { 62 installDefaults((JRootPane)c); 63 installComponents((JRootPane)c); 64 installListeners((JRootPane)c); 65 installKeyboardActions((JRootPane)c); 66 } 67 68 uninstallUI(JComponent c)69 public void uninstallUI(JComponent c) { 70 uninstallDefaults((JRootPane)c); 71 uninstallComponents((JRootPane)c); 72 uninstallListeners((JRootPane)c); 73 uninstallKeyboardActions((JRootPane)c); 74 } 75 76 /** 77 * Installs default properties. 78 * 79 * @param c an instance of {@code JRootPane} 80 */ installDefaults(JRootPane c)81 protected void installDefaults(JRootPane c){ 82 LookAndFeel.installProperty(c, "opaque", Boolean.FALSE); 83 } 84 85 /** 86 * Installs components. 87 * 88 * @param root an instance of {@code JRootPane} 89 */ installComponents(JRootPane root)90 protected void installComponents(JRootPane root) { 91 } 92 93 /** 94 * Registers listeners. 95 * 96 * @param root an instance of {@code JRootPane} 97 */ installListeners(JRootPane root)98 protected void installListeners(JRootPane root) { 99 root.addPropertyChangeListener(this); 100 } 101 102 /** 103 * Registers keyboard actions. 104 * 105 * @param root an instance of {@code JRootPane} 106 */ installKeyboardActions(JRootPane root)107 protected void installKeyboardActions(JRootPane root) { 108 InputMap km = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, root); 109 SwingUtilities.replaceUIInputMap(root, 110 JComponent.WHEN_IN_FOCUSED_WINDOW, km); 111 km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, 112 root); 113 SwingUtilities.replaceUIInputMap(root, 114 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km); 115 116 LazyActionMap.installLazyActionMap(root, BasicRootPaneUI.class, 117 "RootPane.actionMap"); 118 updateDefaultButtonBindings(root); 119 } 120 121 /** 122 * Uninstalls default properties. 123 * 124 * @param root an instance of {@code JRootPane} 125 */ uninstallDefaults(JRootPane root)126 protected void uninstallDefaults(JRootPane root) { 127 } 128 129 /** 130 * Unregisters components. 131 * 132 * @param root an instance of {@code JRootPane} 133 */ uninstallComponents(JRootPane root)134 protected void uninstallComponents(JRootPane root) { 135 } 136 137 /** 138 * Unregisters listeners. 139 * 140 * @param root an instance of {@code JRootPane} 141 */ uninstallListeners(JRootPane root)142 protected void uninstallListeners(JRootPane root) { 143 root.removePropertyChangeListener(this); 144 } 145 146 /** 147 * Unregisters keyboard actions. 148 * 149 * @param root an instance of {@code JRootPane} 150 */ uninstallKeyboardActions(JRootPane root)151 protected void uninstallKeyboardActions(JRootPane root) { 152 SwingUtilities.replaceUIInputMap(root, JComponent. 153 WHEN_IN_FOCUSED_WINDOW, null); 154 SwingUtilities.replaceUIActionMap(root, null); 155 } 156 getInputMap(int condition, JComponent c)157 InputMap getInputMap(int condition, JComponent c) { 158 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { 159 return (InputMap)DefaultLookup.get(c, this, 160 "RootPane.ancestorInputMap"); 161 } 162 163 if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) { 164 return createInputMap(condition, c); 165 } 166 return null; 167 } 168 createInputMap(int condition, JComponent c)169 ComponentInputMap createInputMap(int condition, JComponent c) { 170 return new RootPaneInputMap(c); 171 } 172 loadActionMap(LazyActionMap map)173 static void loadActionMap(LazyActionMap map) { 174 map.put(new Actions(Actions.PRESS)); 175 map.put(new Actions(Actions.RELEASE)); 176 map.put(new Actions(Actions.POST_POPUP)); 177 } 178 179 /** 180 * Invoked when the default button property has changed. This reloads 181 * the bindings from the defaults table with name 182 * <code>RootPane.defaultButtonWindowKeyBindings</code>. 183 */ updateDefaultButtonBindings(JRootPane root)184 void updateDefaultButtonBindings(JRootPane root) { 185 InputMap km = SwingUtilities.getUIInputMap(root, JComponent. 186 WHEN_IN_FOCUSED_WINDOW); 187 while (km != null && !(km instanceof RootPaneInputMap)) { 188 km = km.getParent(); 189 } 190 if (km != null) { 191 km.clear(); 192 if (root.getDefaultButton() != null) { 193 Object[] bindings = (Object[])DefaultLookup.get(root, this, 194 "RootPane.defaultButtonWindowKeyBindings"); 195 if (bindings != null) { 196 LookAndFeel.loadKeyBindings(km, bindings); 197 } 198 } 199 } 200 } 201 202 /** 203 * Invoked when a property changes on the root pane. If the event 204 * indicates the <code>defaultButton</code> has changed, this will 205 * reinstall the keyboard actions. 206 */ propertyChange(PropertyChangeEvent e)207 public void propertyChange(PropertyChangeEvent e) { 208 if(e.getPropertyName().equals("defaultButton")) { 209 JRootPane rootpane = (JRootPane)e.getSource(); 210 updateDefaultButtonBindings(rootpane); 211 if (rootpane.getClientProperty("temporaryDefaultButton") == null) { 212 rootpane.putClientProperty("initialDefaultButton", e.getNewValue()); 213 } 214 } 215 } 216 217 218 static class Actions extends UIAction { 219 public static final String PRESS = "press"; 220 public static final String RELEASE = "release"; 221 public static final String POST_POPUP = "postPopup"; 222 Actions(String name)223 Actions(String name) { 224 super(name); 225 } 226 actionPerformed(ActionEvent evt)227 public void actionPerformed(ActionEvent evt) { 228 JRootPane root = (JRootPane)evt.getSource(); 229 JButton owner = root.getDefaultButton(); 230 String key = getName(); 231 232 if (key == POST_POPUP) { // Action to post popup 233 Component c = KeyboardFocusManager 234 .getCurrentKeyboardFocusManager() 235 .getFocusOwner(); 236 237 if(c instanceof JComponent) { 238 JComponent src = (JComponent) c; 239 JPopupMenu jpm = src.getComponentPopupMenu(); 240 if(jpm != null) { 241 Point pt = src.getPopupLocation(null); 242 if(pt == null) { 243 Rectangle vis = src.getVisibleRect(); 244 pt = new Point(vis.x+vis.width/2, 245 vis.y+vis.height/2); 246 } 247 jpm.show(c, pt.x, pt.y); 248 } 249 } 250 } 251 else if (owner != null 252 && SwingUtilities.getRootPane(owner) == root) { 253 if (key == PRESS) { 254 owner.doClick(20); 255 } 256 } 257 } 258 259 @Override accept(Object sender)260 public boolean accept(Object sender) { 261 String key = getName(); 262 if(key == POST_POPUP) { 263 MenuElement[] elems = MenuSelectionManager 264 .defaultManager() 265 .getSelectedPath(); 266 if(elems != null && elems.length != 0) { 267 return false; 268 // We shall not interfere with already opened menu 269 } 270 271 Component c = KeyboardFocusManager 272 .getCurrentKeyboardFocusManager() 273 .getFocusOwner(); 274 if(c instanceof JComponent) { 275 JComponent src = (JComponent) c; 276 return src.getComponentPopupMenu() != null; 277 } 278 279 return false; 280 } 281 282 if (sender instanceof JRootPane) { 283 JButton owner = ((JRootPane)sender).getDefaultButton(); 284 return (owner != null && owner.getModel().isEnabled() && owner.isShowing()); 285 } 286 return true; 287 } 288 } 289 290 @SuppressWarnings("serial") // JDK-implementation class 291 private static class RootPaneInputMap extends ComponentInputMapUIResource { RootPaneInputMap(JComponent c)292 public RootPaneInputMap(JComponent c) { 293 super(c); 294 } 295 } 296 } 297