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