1 /*
   2  * Copyright (c) 1996, 2015, 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 sun.awt;
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.awt.peer.*;
  31 import java.beans.PropertyChangeListener;
  32 import java.beans.PropertyChangeEvent;
  33 import java.util.Set;
  34 import java.awt.AWTKeyStroke;
  35 import java.applet.Applet;
  36 
  37 /**
  38  * A generic container used for embedding Java components, usually applets.
  39  * An EmbeddedFrame has two related uses:
  40  *
  41  * . Within a Java-based application, an EmbeddedFrame serves as a sort of
  42  *   firewall, preventing the contained components or applets from using
  43  *   getParent() to find parent components, such as menubars.
  44  *
  45  * . Within a C-based application, an EmbeddedFrame contains a window handle
  46  *   which was created by the application, which serves as the top-level
  47  *   Java window.  EmbeddedFrames created for this purpose are passed-in a
  48  *   handle of an existing window created by the application.  The window
  49  *   handle should be of the appropriate native type for a specific
  50  *   platform, as stored in the pData field of the ComponentPeer.
  51  *
  52  * @author      Thomas Ball
  53  */
  54 public abstract class EmbeddedFrame extends Frame
  55                           implements KeyEventDispatcher, PropertyChangeListener {
  56 
  57     private boolean isCursorAllowed = true;
  58     private boolean supportsXEmbed = false;
  59     @SuppressWarnings("serial") // Not statically typed as Serializable
  60     private KeyboardFocusManager appletKFM;
  61     // JDK 1.1 compatibility
  62     private static final long serialVersionUID = 2967042741780317130L;
  63 
  64     /*
  65      * The constants define focus traversal directions.
  66      * Use them in {@code traverseIn}, {@code traverseOut} methods.
  67      */
  68     protected static final boolean FORWARD = true;
  69     protected static final boolean BACKWARD = false;
  70 
  71     public boolean supportsXEmbed() {
  72         return supportsXEmbed && SunToolkit.needsXEmbed();
  73     }
  74 
  75     protected EmbeddedFrame(boolean supportsXEmbed) {
  76         this((long)0, supportsXEmbed);
  77     }
  78 
  79 
  80     protected EmbeddedFrame() {
  81         this((long)0);
  82     }
  83 
  84     /**
  85      * @deprecated This constructor will be removed in 1.5
  86      */
  87     @Deprecated
  88     protected EmbeddedFrame(int handle) {
  89         this((long)handle);
  90     }
  91 
  92     protected EmbeddedFrame(long handle) {
  93         this(handle, false);
  94     }
  95 
  96     protected EmbeddedFrame(long handle, boolean supportsXEmbed) {
  97         this.supportsXEmbed = supportsXEmbed;
  98         registerListeners();
  99     }
 100 
 101     /**
 102      * Block introspection of a parent window by this child.
 103      */
 104     public Container getParent() {
 105         return null;
 106     }
 107 
 108     /**
 109      * Needed to track which KeyboardFocusManager is current. We want to avoid memory
 110      * leaks, so when KFM stops being current, we remove ourselves as listeners.
 111      */
 112     public void propertyChange(PropertyChangeEvent evt) {
 113         // We don't handle any other properties. Skip it.
 114         if (!evt.getPropertyName().equals("managingFocus")) {
 115             return;
 116         }
 117 
 118         // We only do it if it stops being current. Technically, we should
 119         // never get an event about KFM starting being current.
 120         if (evt.getNewValue() == Boolean.TRUE) {
 121             return;
 122         }
 123 
 124         // should be the same as appletKFM
 125         removeTraversingOutListeners((KeyboardFocusManager)evt.getSource());
 126 
 127         appletKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager();
 128         if (isVisible()) {
 129             addTraversingOutListeners(appletKFM);
 130         }
 131     }
 132 
 133     /**
 134      * Register us as KeyEventDispatcher and property "managingFocus" listeners.
 135      */
 136     private void addTraversingOutListeners(KeyboardFocusManager kfm) {
 137         kfm.addKeyEventDispatcher(this);
 138         kfm.addPropertyChangeListener("managingFocus", this);
 139     }
 140 
 141     /**
 142      * Deregister us as KeyEventDispatcher and property "managingFocus" listeners.
 143      */
 144     private void removeTraversingOutListeners(KeyboardFocusManager kfm) {
 145         kfm.removeKeyEventDispatcher(this);
 146         kfm.removePropertyChangeListener("managingFocus", this);
 147     }
 148 
 149     /**
 150      * Because there may be many AppContexts, and we can't be sure where this
 151      * EmbeddedFrame is first created or shown, we can't automatically determine
 152      * the correct KeyboardFocusManager to attach to as KeyEventDispatcher.
 153      * Those who want to use the functionality of traversing out of the EmbeddedFrame
 154      * must call this method on the Applet's AppContext. After that, all the changes
 155      * can be handled automatically, including possible replacement of
 156      * KeyboardFocusManager.
 157      */
 158     public void registerListeners() {
 159         if (appletKFM != null) {
 160             removeTraversingOutListeners(appletKFM);
 161         }
 162         appletKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager();
 163         if (isVisible()) {
 164             addTraversingOutListeners(appletKFM);
 165         }
 166     }
 167 
 168     /**
 169      * Needed to avoid memory leak: we register this EmbeddedFrame as a listener with
 170      * KeyboardFocusManager of applet's AppContext. We don't want the KFM to keep
 171      * reference to our EmbeddedFrame forever if the Frame is no longer in use, so we
 172      * add listeners in show() and remove them in hide().
 173      */
 174     @SuppressWarnings("deprecation")
 175     public void show() {
 176         if (appletKFM != null) {
 177             addTraversingOutListeners(appletKFM);
 178         }
 179         super.show();
 180     }
 181 
 182     /**
 183      * Needed to avoid memory leak: we register this EmbeddedFrame as a listener with
 184      * KeyboardFocusManager of applet's AppContext. We don't want the KFM to keep
 185      * reference to our EmbeddedFrame forever if the Frame is no longer in use, so we
 186      * add listeners in show() and remove them in hide().
 187      */
 188     @SuppressWarnings("deprecation")
 189     public void hide() {
 190         if (appletKFM != null) {
 191             removeTraversingOutListeners(appletKFM);
 192         }
 193         super.hide();
 194     }
 195 
 196     /**
 197      * Need this method to detect when the focus may have chance to leave the
 198      * focus cycle root which is EmbeddedFrame. Mostly, the code here is copied
 199      * from DefaultKeyboardFocusManager.processKeyEvent with some minor
 200      * modifications.
 201      */
 202     public boolean dispatchKeyEvent(KeyEvent e) {
 203 
 204         Container currentRoot = AWTAccessor.getKeyboardFocusManagerAccessor()
 205                                     .getCurrentFocusCycleRoot();
 206 
 207         // if we are not in EmbeddedFrame's cycle, we should not try to leave.
 208         if (this != currentRoot) {
 209             return false;
 210         }
 211 
 212         // KEY_TYPED events cannot be focus traversal keys
 213         if (e.getID() == KeyEvent.KEY_TYPED) {
 214             return false;
 215         }
 216 
 217         if (!getFocusTraversalKeysEnabled() || e.isConsumed()) {
 218             return false;
 219         }
 220 
 221         AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
 222         Set<AWTKeyStroke> toTest;
 223         Component currentFocused = e.getComponent();
 224 
 225         toTest = getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
 226         if (toTest.contains(stroke)) {
 227             // 6581899: performance improvement for SortingFocusTraversalPolicy
 228             Component last = getFocusTraversalPolicy().getLastComponent(this);
 229             if (currentFocused == last || last == null) {
 230                 if (traverseOut(FORWARD)) {
 231                     e.consume();
 232                     return true;
 233                 }
 234             }
 235         }
 236 
 237         toTest = getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
 238         if (toTest.contains(stroke)) {
 239             // 6581899: performance improvement for SortingFocusTraversalPolicy
 240             Component first = getFocusTraversalPolicy().getFirstComponent(this);
 241             if (currentFocused == first || first == null) {
 242                 if (traverseOut(BACKWARD)) {
 243                     e.consume();
 244                     return true;
 245                 }
 246             }
 247         }
 248         return false;
 249     }
 250 
 251     /**
 252      * This method is called by the embedder when we should receive focus as element
 253      * of the traversal chain.  The method requests focus on:
 254      * 1. the first Component of this EmbeddedFrame if user moves focus forward
 255      *    in the focus traversal cycle.
 256      * 2. the last Component of this EmbeddedFrame if user moves focus backward
 257      *    in the focus traversal cycle.
 258      *
 259      * The direction parameter specifies which of the two mentioned cases is
 260      * happening. Use FORWARD and BACKWARD constants defined in the EmbeddedFrame class
 261      * to avoid confusing boolean values.
 262      *
 263      * A concrete implementation of this method is defined in the platform-dependent
 264      * subclasses.
 265      *
 266      * @param direction FORWARD or BACKWARD
 267      * @return true, if the EmbeddedFrame wants to get focus, false otherwise.
 268      */
 269     public boolean traverseIn(boolean direction) {
 270         Component comp = null;
 271 
 272         if (direction == FORWARD) {
 273             comp = getFocusTraversalPolicy().getFirstComponent(this);
 274         } else {
 275             comp = getFocusTraversalPolicy().getLastComponent(this);
 276         }
 277         if (comp != null) {
 278             // comp.requestFocus(); - Leads to a hung.
 279 
 280             AWTAccessor.getKeyboardFocusManagerAccessor().setMostRecentFocusOwner(this, comp);
 281             synthesizeWindowActivation(true);
 282         }
 283         return (null != comp);
 284     }
 285 
 286     /**
 287      * This method is called from dispatchKeyEvent in the following two cases:
 288      * 1. The focus is on the first Component of this EmbeddedFrame and we are
 289      *    about to transfer the focus backward.
 290      * 2. The focus in on the last Component of this EmbeddedFrame and we are
 291      *    about to transfer the focus forward.
 292      * This is needed to give the opportuity for keyboard focus to leave the
 293      * EmbeddedFrame. Override this method, initiate focus transfer in it and
 294      * return true if you want the focus to leave EmbeddedFrame's cycle.
 295      * The direction parameter specifies which of the two mentioned cases is
 296      * happening. Use FORWARD and BACKWARD constants defined in EmbeddedFrame
 297      * to avoid confusing boolean values.
 298      *
 299      * @param direction FORWARD or BACKWARD
 300      * @return true, if EmbeddedFrame wants the focus to leave it,
 301      *         false otherwise.
 302      */
 303     protected boolean traverseOut(boolean direction) {
 304         return false;
 305     }
 306 
 307     /**
 308      * Block modifying any frame attributes, since they aren't applicable
 309      * for EmbeddedFrames.
 310      */
 311     public void setTitle(String title) {}
 312     public void setIconImage(Image image) {}
 313     public void setIconImages(java.util.List<? extends Image> icons) {}
 314     public void setMenuBar(MenuBar mb) {}
 315     public void setResizable(boolean resizable) {}
 316     public void remove(MenuComponent m) {}
 317 
 318     public boolean isResizable() {
 319         return true;
 320     }
 321 
 322     public void addNotify() {
 323         synchronized (getTreeLock()) {
 324             if (!isDisplayable()) {
 325                 setPeer(new NullEmbeddedFramePeer());
 326             }
 327             super.addNotify();
 328         }
 329     }
 330 
 331     // These three functions consitute RFE 4100710. Do not remove.
 332     public void setCursorAllowed(boolean isCursorAllowed) {
 333         this.isCursorAllowed = isCursorAllowed;
 334         final FramePeer peer = AWTAccessor.getComponentAccessor().getPeer(this);
 335         peer.updateCursorImmediately();
 336     }
 337     public boolean isCursorAllowed() {
 338         return isCursorAllowed;
 339     }
 340     public Cursor getCursor() {
 341         return (isCursorAllowed)
 342             ? super.getCursor()
 343             : Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
 344     }
 345 
 346     protected void setPeer(final ComponentPeer p){
 347         AWTAccessor.getComponentAccessor().setPeer(EmbeddedFrame.this, p);
 348     };
 349 
 350     /**
 351      * Synthesize native message to activate or deactivate EmbeddedFrame window
 352      * depending on the value of parameter {@code b}.
 353      * Peers should override this method if they are to implement
 354      * this functionality.
 355      * @param doActivate  if {@code true}, activates the window;
 356      * otherwise, deactivates the window
 357      */
 358     public void synthesizeWindowActivation(boolean doActivate) {}
 359 
 360     /**
 361      * Moves this embedded frame to a new location. The top-left corner of
 362      * the new location is specified by the {@code x} and {@code y}
 363      * parameters relative to the native parent component.
 364      * <p>
 365      * setLocation() and setBounds() for EmbeddedFrame really don't move it
 366      * within the native parent. These methods always put embedded frame to
 367      * (0, 0) for backward compatibility. To allow moving embedded frame
 368      * setLocationPrivate() and setBoundsPrivate() were introduced, and they
 369      * work just the same way as setLocation() and setBounds() for usual,
 370      * non-embedded components.
 371      * </p>
 372      * <p>
 373      * Using usual get/setLocation() and get/setBounds() together with new
 374      * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended.
 375      * For example, calling getBoundsPrivate() after setLocation() works fine,
 376      * but getBounds() after setBoundsPrivate() may return unpredictable value.
 377      * </p>
 378      * @param x the new <i>x</i>-coordinate relative to the parent component
 379      * @param y the new <i>y</i>-coordinate relative to the parent component
 380      * @see java.awt.Component#setLocation
 381      * @see #getLocationPrivate
 382      * @see #setBoundsPrivate
 383      * @see #getBoundsPrivate
 384      * @since 1.5
 385      */
 386     protected void setLocationPrivate(int x, int y) {
 387         Dimension size = getSize();
 388         setBoundsPrivate(x, y, size.width, size.height);
 389     }
 390 
 391     /**
 392      * Gets the location of this embedded frame as a point specifying the
 393      * top-left corner relative to parent component.
 394      * <p>
 395      * setLocation() and setBounds() for EmbeddedFrame really don't move it
 396      * within the native parent. These methods always put embedded frame to
 397      * (0, 0) for backward compatibility. To allow getting location and size
 398      * of embedded frame getLocationPrivate() and getBoundsPrivate() were
 399      * introduced, and they work just the same way as getLocation() and getBounds()
 400      * for ususal, non-embedded components.
 401      * </p>
 402      * <p>
 403      * Using usual get/setLocation() and get/setBounds() together with new
 404      * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended.
 405      * For example, calling getBoundsPrivate() after setLocation() works fine,
 406      * but getBounds() after setBoundsPrivate() may return unpredictable value.
 407      * </p>
 408      * @return a point indicating this embedded frame's top-left corner
 409      * @see java.awt.Component#getLocation
 410      * @see #setLocationPrivate
 411      * @see #setBoundsPrivate
 412      * @see #getBoundsPrivate
 413      * @since 1.6
 414      */
 415     protected Point getLocationPrivate() {
 416         Rectangle bounds = getBoundsPrivate();
 417         return new Point(bounds.x, bounds.y);
 418     }
 419 
 420     /**
 421      * Moves and resizes this embedded frame. The new location of the top-left
 422      * corner is specified by {@code x} and {@code y} parameters
 423      * relative to the native parent component. The new size is specified by
 424      * {@code width} and {@code height}.
 425      * <p>
 426      * setLocation() and setBounds() for EmbeddedFrame really don't move it
 427      * within the native parent. These methods always put embedded frame to
 428      * (0, 0) for backward compatibility. To allow moving embedded frames
 429      * setLocationPrivate() and setBoundsPrivate() were introduced, and they
 430      * work just the same way as setLocation() and setBounds() for usual,
 431      * non-embedded components.
 432      * </p>
 433      * <p>
 434      * Using usual get/setLocation() and get/setBounds() together with new
 435      * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended.
 436      * For example, calling getBoundsPrivate() after setLocation() works fine,
 437      * but getBounds() after setBoundsPrivate() may return unpredictable value.
 438      * </p>
 439      * @param x the new <i>x</i>-coordinate relative to the parent component
 440      * @param y the new <i>y</i>-coordinate relative to the parent component
 441      * @param width the new {@code width} of this embedded frame
 442      * @param height the new {@code height} of this embedded frame
 443      * @see java.awt.Component#setBounds
 444      * @see #setLocationPrivate
 445      * @see #getLocationPrivate
 446      * @see #getBoundsPrivate
 447      * @since 1.5
 448      */
 449     protected void setBoundsPrivate(int x, int y, int width, int height) {
 450         final FramePeer peer = AWTAccessor.getComponentAccessor().getPeer(this);
 451         if (peer != null) {
 452             peer.setBoundsPrivate(x, y, width, height);
 453         }
 454     }
 455 
 456     /**
 457      * Gets the bounds of this embedded frame as a rectangle specifying the
 458      * width, height and location relative to the native parent component.
 459      * <p>
 460      * setLocation() and setBounds() for EmbeddedFrame really don't move it
 461      * within the native parent. These methods always put embedded frame to
 462      * (0, 0) for backward compatibility. To allow getting location and size
 463      * of embedded frames getLocationPrivate() and getBoundsPrivate() were
 464      * introduced, and they work just the same way as getLocation() and getBounds()
 465      * for ususal, non-embedded components.
 466      * </p>
 467      * <p>
 468      * Using usual get/setLocation() and get/setBounds() together with new
 469      * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended.
 470      * For example, calling getBoundsPrivate() after setLocation() works fine,
 471      * but getBounds() after setBoundsPrivate() may return unpredictable value.
 472      * </p>
 473      * @return a rectangle indicating this embedded frame's bounds
 474      * @see java.awt.Component#getBounds
 475      * @see #setLocationPrivate
 476      * @see #getLocationPrivate
 477      * @see #setBoundsPrivate
 478      * @since 1.6
 479      */
 480     protected Rectangle getBoundsPrivate() {
 481         final FramePeer peer = AWTAccessor.getComponentAccessor().getPeer(this);
 482         if (peer != null) {
 483             return peer.getBoundsPrivate();
 484         }
 485         else {
 486             return getBounds();
 487         }
 488     }
 489 
 490     public void toFront() {}
 491     public void toBack() {}
 492 
 493     public abstract void registerAccelerator(AWTKeyStroke stroke);
 494     public abstract void unregisterAccelerator(AWTKeyStroke stroke);
 495 
 496     /**
 497      * Checks if the component is in an EmbeddedFrame. If so,
 498      * returns the applet found in the hierarchy or null if
 499      * not found.
 500      * @return the parent applet or {@code null}
 501      * @since 1.6
 502      *
 503      * @deprecated The Applet API is deprecated. See the
 504      * <a href="../../java/applet/package-summary.html"> java.applet package
 505      * documentation</a> for further information.
 506      */
 507     @Deprecated(since = "9")
 508     public static Applet getAppletIfAncestorOf(Component comp) {
 509         Container parent = comp.getParent();
 510         Applet applet = null;
 511         while (parent != null && !(parent instanceof EmbeddedFrame)) {
 512             if (parent instanceof Applet) {
 513                 applet = (Applet)parent;
 514             }
 515             parent = parent.getParent();
 516         }
 517         return parent == null ? null : applet;
 518     }
 519 
 520     /**
 521      * This method should be overriden in subclasses. It is
 522      * called when window this frame is within should be blocked
 523      * by some modal dialog.
 524      */
 525     public void notifyModalBlocked(Dialog blocker, boolean blocked) {
 526     }
 527 
 528     private static class NullEmbeddedFramePeer
 529         extends NullComponentPeer implements FramePeer {
 530         public void setTitle(String title) {}
 531         public void setIconImage(Image im) {}
 532         public void updateIconImages() {}
 533         public void setMenuBar(MenuBar mb) {}
 534         public void setResizable(boolean resizeable) {}
 535         public void setState(int state) {}
 536         public int getState() { return Frame.NORMAL; }
 537         public void setMaximizedBounds(Rectangle b) {}
 538         public void toFront() {}
 539         public void toBack() {}
 540         public void updateFocusableWindowState() {}
 541         public void updateAlwaysOnTop() {}
 542         public void updateAlwaysOnTopState() {}
 543         public Component getGlobalHeavyweightFocusOwner() { return null; }
 544         public void setBoundsPrivate(int x, int y, int width, int height) {
 545             setBounds(x, y, width, height, SET_BOUNDS);
 546         }
 547         public Rectangle getBoundsPrivate() {
 548             return getBounds();
 549         }
 550         public void setModalBlocked(Dialog blocker, boolean blocked) {}
 551 
 552         public void restack() {
 553             throw new UnsupportedOperationException();
 554         }
 555 
 556         public boolean isRestackSupported() {
 557             return false;
 558         }
 559         public boolean requestWindowFocus() {
 560             return false;
 561         }
 562         public void updateMinimumSize() {
 563         }
 564 
 565         public void setOpacity(float opacity) {
 566         }
 567 
 568         public void setOpaque(boolean isOpaque) {
 569         }
 570 
 571         public void updateWindow() {
 572         }
 573 
 574         public void repositionSecurityWarning() {
 575         }
 576 
 577         public void emulateActivation(boolean activate) {
 578         }
 579     }
 580 } // class EmbeddedFrame