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