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