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