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