| /* | |
| * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.awt; | |
| import java.applet.Applet; | |
| import java.awt.AWTKeyStroke; | |
| import java.awt.Component; | |
| import java.awt.Container; | |
| import java.awt.Cursor; | |
| import java.awt.Dialog; | |
| import java.awt.Dimension; | |
| import java.awt.Frame; | |
| import java.awt.Image; | |
| import java.awt.KeyEventDispatcher; | |
| import java.awt.KeyboardFocusManager; | |
| import java.awt.MenuBar; | |
| import java.awt.MenuComponent; | |
| import java.awt.Point; | |
| import java.awt.Rectangle; | |
| import java.awt.event.KeyEvent; | |
| import java.awt.peer.ComponentPeer; | |
| import java.awt.peer.FramePeer; | |
| import java.beans.PropertyChangeEvent; | |
| import java.beans.PropertyChangeListener; | |
| import java.io.Serial; | |
| import java.util.Set; | |
| /** | |
| * A generic container used for embedding Java components, usually applets. | |
| * An EmbeddedFrame has two related uses: | |
| * | |
| * . Within a Java-based application, an EmbeddedFrame serves as a sort of | |
| * firewall, preventing the contained components or applets from using | |
| * getParent() to find parent components, such as menubars. | |
| * | |
| * . Within a C-based application, an EmbeddedFrame contains a window handle | |
| * which was created by the application, which serves as the top-level | |
| * Java window. EmbeddedFrames created for this purpose are passed-in a | |
| * handle of an existing window created by the application. The window | |
| * handle should be of the appropriate native type for a specific | |
| * platform, as stored in the pData field of the ComponentPeer. | |
| * | |
| * @author Thomas Ball | |
| */ | |
| public abstract class EmbeddedFrame extends Frame | |
| implements KeyEventDispatcher, PropertyChangeListener { | |
| private boolean isCursorAllowed = true; | |
| private boolean supportsXEmbed = false; | |
| @SuppressWarnings("serial") // Not statically typed as Serializable | |
| private KeyboardFocusManager appletKFM; | |
| /** | |
| * Use serialVersionUID from JDK 1.1 for interoperability. | |
| */ | |
| @Serial | |
| private static final long serialVersionUID = 2967042741780317130L; | |
| /* | |
| * The constants define focus traversal directions. | |
| * Use them in {@code traverseIn}, {@code traverseOut} methods. | |
| */ | |
| protected static final boolean FORWARD = true; | |
| protected static final boolean BACKWARD = false; | |
| public boolean supportsXEmbed() { | |
| return supportsXEmbed && SunToolkit.needsXEmbed(); | |
| } | |
| protected EmbeddedFrame(boolean supportsXEmbed) { | |
| this((long)0, supportsXEmbed); | |
| } | |
| protected EmbeddedFrame() { | |
| this((long)0); | |
| } | |
| /** | |
| * @deprecated This constructor will be removed in 1.5 | |
| */ | |
| @Deprecated | |
| protected EmbeddedFrame(int handle) { | |
| this((long)handle); | |
| } | |
| protected EmbeddedFrame(long handle) { | |
| this(handle, false); | |
| } | |
| protected EmbeddedFrame(long handle, boolean supportsXEmbed) { | |
| this.supportsXEmbed = supportsXEmbed; | |
| registerListeners(); | |
| } | |
| /** | |
| * Block introspection of a parent window by this child. | |
| */ | |
| public Container getParent() { | |
| return null; | |
| } | |
| /** | |
| * Needed to track which KeyboardFocusManager is current. We want to avoid memory | |
| * leaks, so when KFM stops being current, we remove ourselves as listeners. | |
| */ | |
| public void propertyChange(PropertyChangeEvent evt) { | |
| // We don't handle any other properties. Skip it. | |
| if (!evt.getPropertyName().equals("managingFocus")) { | |
| return; | |
| } | |
| // We only do it if it stops being current. Technically, we should | |
| // never get an event about KFM starting being current. | |
| if (evt.getNewValue() == Boolean.TRUE) { | |
| return; | |
| } | |
| // should be the same as appletKFM | |
| removeTraversingOutListeners((KeyboardFocusManager)evt.getSource()); | |
| appletKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager(); | |
| if (isVisible()) { | |
| addTraversingOutListeners(appletKFM); | |
| } | |
| } | |
| /** | |
| * Register us as KeyEventDispatcher and property "managingFocus" listeners. | |
| */ | |
| private void addTraversingOutListeners(KeyboardFocusManager kfm) { | |
| kfm.addKeyEventDispatcher(this); | |
| kfm.addPropertyChangeListener("managingFocus", this); | |
| } | |
| /** | |
| * Deregister us as KeyEventDispatcher and property "managingFocus" listeners. | |
| */ | |
| private void removeTraversingOutListeners(KeyboardFocusManager kfm) { | |
| kfm.removeKeyEventDispatcher(this); | |
| kfm.removePropertyChangeListener("managingFocus", this); | |
| } | |
| /** | |
| * Because there may be many AppContexts, and we can't be sure where this | |
| * EmbeddedFrame is first created or shown, we can't automatically determine | |
| * the correct KeyboardFocusManager to attach to as KeyEventDispatcher. | |
| * Those who want to use the functionality of traversing out of the EmbeddedFrame | |
| * must call this method on the Applet's AppContext. After that, all the changes | |
| * can be handled automatically, including possible replacement of | |
| * KeyboardFocusManager. | |
| */ | |
| public void registerListeners() { | |
| if (appletKFM != null) { | |
| removeTraversingOutListeners(appletKFM); | |
| } | |
| appletKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager(); | |
| if (isVisible()) { | |
| addTraversingOutListeners(appletKFM); | |
| } | |
| } | |
| /** | |
| * Needed to avoid memory leak: we register this EmbeddedFrame as a listener with | |
| * KeyboardFocusManager of applet's AppContext. We don't want the KFM to keep | |
| * reference to our EmbeddedFrame forever if the Frame is no longer in use, so we | |
| * add listeners in show() and remove them in hide(). | |
| */ | |
| @SuppressWarnings("deprecation") | |
| public void show() { | |
| if (appletKFM != null) { | |
| addTraversingOutListeners(appletKFM); | |
| } | |
| super.show(); | |
| } | |
| /** | |
| * Needed to avoid memory leak: we register this EmbeddedFrame as a listener with | |
| * KeyboardFocusManager of applet's AppContext. We don't want the KFM to keep | |
| * reference to our EmbeddedFrame forever if the Frame is no longer in use, so we | |
| * add listeners in show() and remove them in hide(). | |
| */ | |
| @SuppressWarnings("deprecation") | |
| public void hide() { | |
| if (appletKFM != null) { | |
| removeTraversingOutListeners(appletKFM); | |
| } | |
| super.hide(); | |
| } | |
| /** | |
| * Need this method to detect when the focus may have chance to leave the | |
| * focus cycle root which is EmbeddedFrame. Mostly, the code here is copied | |
| * from DefaultKeyboardFocusManager.processKeyEvent with some minor | |
| * modifications. | |
| */ | |
| public boolean dispatchKeyEvent(KeyEvent e) { | |
| Container currentRoot = AWTAccessor.getKeyboardFocusManagerAccessor() | |
| .getCurrentFocusCycleRoot(); | |
| // if we are not in EmbeddedFrame's cycle, we should not try to leave. | |
| if (this != currentRoot) { | |
| return false; | |
| } | |
| // KEY_TYPED events cannot be focus traversal keys | |
| if (e.getID() == KeyEvent.KEY_TYPED) { | |
| return false; | |
| } | |
| if (!getFocusTraversalKeysEnabled() || e.isConsumed()) { | |
| return false; | |
| } | |
| AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e); | |
| Set<AWTKeyStroke> toTest; | |
| Component currentFocused = e.getComponent(); | |
| toTest = getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); | |
| if (toTest.contains(stroke)) { | |
| // 6581899: performance improvement for SortingFocusTraversalPolicy | |
| Component last = getFocusTraversalPolicy().getLastComponent(this); | |
| if (currentFocused == last || last == null) { | |
| if (traverseOut(FORWARD)) { | |
| e.consume(); | |
| return true; | |
| } | |
| } | |
| } | |
| toTest = getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); | |
| if (toTest.contains(stroke)) { | |
| // 6581899: performance improvement for SortingFocusTraversalPolicy | |
| Component first = getFocusTraversalPolicy().getFirstComponent(this); | |
| if (currentFocused == first || first == null) { | |
| if (traverseOut(BACKWARD)) { | |
| e.consume(); | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * This method is called by the embedder when we should receive focus as element | |
| * of the traversal chain. The method requests focus on: | |
| * 1. the first Component of this EmbeddedFrame if user moves focus forward | |
| * in the focus traversal cycle. | |
| * 2. the last Component of this EmbeddedFrame if user moves focus backward | |
| * in the focus traversal cycle. | |
| * | |
| * The direction parameter specifies which of the two mentioned cases is | |
| * happening. Use FORWARD and BACKWARD constants defined in the EmbeddedFrame class | |
| * to avoid confusing boolean values. | |
| * | |
| * A concrete implementation of this method is defined in the platform-dependent | |
| * subclasses. | |
| * | |
| * @param direction FORWARD or BACKWARD | |
| * @return true, if the EmbeddedFrame wants to get focus, false otherwise. | |
| */ | |
| public boolean traverseIn(boolean direction) { | |
| Component comp = null; | |
| if (direction == FORWARD) { | |
| comp = getFocusTraversalPolicy().getFirstComponent(this); | |
| } else { | |
| comp = getFocusTraversalPolicy().getLastComponent(this); | |
| } | |
| if (comp != null) { | |
| // comp.requestFocus(); - Leads to a hung. | |
| AWTAccessor.getKeyboardFocusManagerAccessor().setMostRecentFocusOwner(this, comp); | |
| synthesizeWindowActivation(true); | |
| } | |
| return (null != comp); | |
| } | |
| /** | |
| * This method is called from dispatchKeyEvent in the following two cases: | |
| * 1. The focus is on the first Component of this EmbeddedFrame and we are | |
| * about to transfer the focus backward. | |
| * 2. The focus in on the last Component of this EmbeddedFrame and we are | |
| * about to transfer the focus forward. | |
| * This is needed to give the opportunity for keyboard focus to leave the | |
| * EmbeddedFrame. Override this method, initiate focus transfer in it and | |
| * return true if you want the focus to leave EmbeddedFrame's cycle. | |
| * The direction parameter specifies which of the two mentioned cases is | |
| * happening. Use FORWARD and BACKWARD constants defined in EmbeddedFrame | |
| * to avoid confusing boolean values. | |
| * | |
| * @param direction FORWARD or BACKWARD | |
| * @return true, if EmbeddedFrame wants the focus to leave it, | |
| * false otherwise. | |
| */ | |
| protected boolean traverseOut(boolean direction) { | |
| return false; | |
| } | |
| /** | |
| * Block modifying any frame attributes, since they aren't applicable | |
| * for EmbeddedFrames. | |
| */ | |
| public void setTitle(String title) {} | |
| public void setIconImage(Image image) {} | |
| public void setIconImages(java.util.List<? extends Image> icons) {} | |
| public void setMenuBar(MenuBar mb) {} | |
| public void setResizable(boolean resizable) {} | |
| public void remove(MenuComponent m) {} | |
| public boolean isResizable() { | |
| return true; | |
| } | |
| public void addNotify() { | |
| synchronized (getTreeLock()) { | |
| if (!isDisplayable()) { | |
| setPeer(new NullEmbeddedFramePeer()); | |
| } | |
| super.addNotify(); | |
| } | |
| } | |
| // These three functions constitute RFE 4100710. Do not remove. | |
| public void setCursorAllowed(boolean isCursorAllowed) { | |
| this.isCursorAllowed = isCursorAllowed; | |
| final FramePeer peer = AWTAccessor.getComponentAccessor().getPeer(this); | |
| peer.updateCursorImmediately(); | |
| } | |
| public boolean isCursorAllowed() { | |
| return isCursorAllowed; | |
| } | |
| public Cursor getCursor() { | |
| return (isCursorAllowed) | |
| ? super.getCursor() | |
| : Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); | |
| } | |
| protected void setPeer(final ComponentPeer p){ | |
| AWTAccessor.getComponentAccessor().setPeer(EmbeddedFrame.this, p); | |
| } | |
| /** | |
| * Synthesize native message to activate or deactivate EmbeddedFrame window | |
| * depending on the value of parameter {@code b}. | |
| * Peers should override this method if they are to implement | |
| * this functionality. | |
| * @param doActivate if {@code true}, activates the window; | |
| * otherwise, deactivates the window | |
| */ | |
| public void synthesizeWindowActivation(boolean doActivate) {} | |
| /** | |
| * Moves this embedded frame to a new location. The top-left corner of | |
| * the new location is specified by the {@code x} and {@code y} | |
| * parameters relative to the native parent component. | |
| * <p> | |
| * setLocation() and setBounds() for EmbeddedFrame really don't move it | |
| * within the native parent. These methods always put embedded frame to | |
| * (0, 0) for backward compatibility. To allow moving embedded frame | |
| * setLocationPrivate() and setBoundsPrivate() were introduced, and they | |
| * work just the same way as setLocation() and setBounds() for usual, | |
| * non-embedded components. | |
| * </p> | |
| * <p> | |
| * Using usual get/setLocation() and get/setBounds() together with new | |
| * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended. | |
| * For example, calling getBoundsPrivate() after setLocation() works fine, | |
| * but getBounds() after setBoundsPrivate() may return unpredictable value. | |
| * </p> | |
| * @param x the new <i>x</i>-coordinate relative to the parent component | |
| * @param y the new <i>y</i>-coordinate relative to the parent component | |
| * @see java.awt.Component#setLocation | |
| * @see #getLocationPrivate | |
| * @see #setBoundsPrivate | |
| * @see #getBoundsPrivate | |
| * @since 1.5 | |
| */ | |
| protected void setLocationPrivate(int x, int y) { | |
| Dimension size = getSize(); | |
| setBoundsPrivate(x, y, size.width, size.height); | |
| } | |
| /** | |
| * Gets the location of this embedded frame as a point specifying the | |
| * top-left corner relative to parent component. | |
| * <p> | |
| * setLocation() and setBounds() for EmbeddedFrame really don't move it | |
| * within the native parent. These methods always put embedded frame to | |
| * (0, 0) for backward compatibility. To allow getting location and size | |
| * of embedded frame getLocationPrivate() and getBoundsPrivate() were | |
| * introduced, and they work just the same way as getLocation() and getBounds() | |
| * for usual, non-embedded components. | |
| * </p> | |
| * <p> | |
| * Using usual get/setLocation() and get/setBounds() together with new | |
| * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended. | |
| * For example, calling getBoundsPrivate() after setLocation() works fine, | |
| * but getBounds() after setBoundsPrivate() may return unpredictable value. | |
| * </p> | |
| * @return a point indicating this embedded frame's top-left corner | |
| * @see java.awt.Component#getLocation | |
| * @see #setLocationPrivate | |
| * @see #setBoundsPrivate | |
| * @see #getBoundsPrivate | |
| * @since 1.6 | |
| */ | |
| protected Point getLocationPrivate() { | |
| Rectangle bounds = getBoundsPrivate(); | |
| return new Point(bounds.x, bounds.y); | |
| } | |
| /** | |
| * Moves and resizes this embedded frame. The new location of the top-left | |
| * corner is specified by {@code x} and {@code y} parameters | |
| * relative to the native parent component. The new size is specified by | |
| * {@code width} and {@code height}. | |
| * <p> | |
| * setLocation() and setBounds() for EmbeddedFrame really don't move it | |
| * within the native parent. These methods always put embedded frame to | |
| * (0, 0) for backward compatibility. To allow moving embedded frames | |
| * setLocationPrivate() and setBoundsPrivate() were introduced, and they | |
| * work just the same way as setLocation() and setBounds() for usual, | |
| * non-embedded components. | |
| * </p> | |
| * <p> | |
| * Using usual get/setLocation() and get/setBounds() together with new | |
| * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended. | |
| * For example, calling getBoundsPrivate() after setLocation() works fine, | |
| * but getBounds() after setBoundsPrivate() may return unpredictable value. | |
| * </p> | |
| * @param x the new <i>x</i>-coordinate relative to the parent component | |
| * @param y the new <i>y</i>-coordinate relative to the parent component | |
| * @param width the new {@code width} of this embedded frame | |
| * @param height the new {@code height} of this embedded frame | |
| * @see java.awt.Component#setBounds | |
| * @see #setLocationPrivate | |
| * @see #getLocationPrivate | |
| * @see #getBoundsPrivate | |
| * @since 1.5 | |
| */ | |
| protected void setBoundsPrivate(int x, int y, int width, int height) { | |
| final FramePeer peer = AWTAccessor.getComponentAccessor().getPeer(this); | |
| if (peer != null) { | |
| peer.setBoundsPrivate(x, y, width, height); | |
| } | |
| } | |
| /** | |
| * Gets the bounds of this embedded frame as a rectangle specifying the | |
| * width, height and location relative to the native parent component. | |
| * <p> | |
| * setLocation() and setBounds() for EmbeddedFrame really don't move it | |
| * within the native parent. These methods always put embedded frame to | |
| * (0, 0) for backward compatibility. To allow getting location and size | |
| * of embedded frames getLocationPrivate() and getBoundsPrivate() were | |
| * introduced, and they work just the same way as getLocation() and getBounds() | |
| * for usual, non-embedded components. | |
| * </p> | |
| * <p> | |
| * Using usual get/setLocation() and get/setBounds() together with new | |
| * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended. | |
| * For example, calling getBoundsPrivate() after setLocation() works fine, | |
| * but getBounds() after setBoundsPrivate() may return unpredictable value. | |
| * </p> | |
| * @return a rectangle indicating this embedded frame's bounds | |
| * @see java.awt.Component#getBounds | |
| * @see #setLocationPrivate | |
| * @see #getLocationPrivate | |
| * @see #setBoundsPrivate | |
| * @since 1.6 | |
| */ | |
| protected Rectangle getBoundsPrivate() { | |
| final FramePeer peer = AWTAccessor.getComponentAccessor().getPeer(this); | |
| if (peer != null) { | |
| return peer.getBoundsPrivate(); | |
| } | |
| else { | |
| return getBounds(); | |
| } | |
| } | |
| public void toFront() {} | |
| public void toBack() {} | |
| public abstract void registerAccelerator(AWTKeyStroke stroke); | |
| public abstract void unregisterAccelerator(AWTKeyStroke stroke); | |
| /** | |
| * Checks if the component is in an EmbeddedFrame. If so, | |
| * returns the applet found in the hierarchy or null if | |
| * not found. | |
| * @return the parent applet or {@code null} | |
| * @since 1.6 | |
| * | |
| * @deprecated The Applet API is deprecated. See the | |
| * <a href="../../java/applet/package-summary.html"> java.applet package | |
| * documentation</a> for further information. | |
| */ | |
| @Deprecated(since = "9", forRemoval = true) | |
| @SuppressWarnings("removal") | |
| public static Applet getAppletIfAncestorOf(Component comp) { | |
| Container parent = comp.getParent(); | |
| Applet applet = null; | |
| while (parent != null && !(parent instanceof EmbeddedFrame)) { | |
| if (parent instanceof Applet) { | |
| applet = (Applet)parent; | |
| } | |
| parent = parent.getParent(); | |
| } | |
| return parent == null ? null : applet; | |
| } | |
| /** | |
| * This method should be overridden in subclasses. It is | |
| * called when window this frame is within should be blocked | |
| * by some modal dialog. | |
| */ | |
| public void notifyModalBlocked(Dialog blocker, boolean blocked) { | |
| } | |
| private static class NullEmbeddedFramePeer | |
| extends NullComponentPeer implements FramePeer { | |
| public void setTitle(String title) {} | |
| public void setIconImage(Image im) {} | |
| public void updateIconImages() {} | |
| public void setMenuBar(MenuBar mb) {} | |
| public void setResizable(boolean resizeable) {} | |
| public void setState(int state) {} | |
| public int getState() { return Frame.NORMAL; } | |
| public void setMaximizedBounds(Rectangle b) {} | |
| public void toFront() {} | |
| public void toBack() {} | |
| public void updateFocusableWindowState() {} | |
| public void updateAlwaysOnTop() {} | |
| public void updateAlwaysOnTopState() {} | |
| public Component getGlobalHeavyweightFocusOwner() { return null; } | |
| public void setBoundsPrivate(int x, int y, int width, int height) { | |
| setBounds(x, y, width, height, SET_BOUNDS); | |
| } | |
| public Rectangle getBoundsPrivate() { | |
| return getBounds(); | |
| } | |
| public void setModalBlocked(Dialog blocker, boolean blocked) {} | |
| public void restack() { | |
| throw new UnsupportedOperationException(); | |
| } | |
| public boolean isRestackSupported() { | |
| return false; | |
| } | |
| public boolean requestWindowFocus() { | |
| return false; | |
| } | |
| public void updateMinimumSize() { | |
| } | |
| public void setOpacity(float opacity) { | |
| } | |
| public void setOpaque(boolean isOpaque) { | |
| } | |
| public void updateWindow() { | |
| } | |
| public void emulateActivation(boolean activate) { | |
| } | |
| } | |
| } // class EmbeddedFrame |