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 public void show() {
184 if (appletKFM != null) {
185 addTraversingOutListeners(appletKFM);
186 }
187 super.show();
188 }
189
190 /**
191 * Needed to avoid memory leak: we register this EmbeddedFrame as a listener with
192 * KeyboardFocusManager of applet's AppContext. We don't want the KFM to keep
193 * reference to our EmbeddedFrame forever if the Frame is no longer in use, so we
194 * add listeners in show() and remove them in hide().
195 */
196 public void hide() {
197 if (appletKFM != null) {
198 removeTraversingOutListeners(appletKFM);
199 }
200 super.hide();
201 }
202
203 /**
204 * Need this method to detect when the focus may have chance to leave the
205 * focus cycle root which is EmbeddedFrame. Mostly, the code here is copied
206 * from DefaultKeyboardFocusManager.processKeyEvent with some minor
207 * modifications.
208 */
209 public boolean dispatchKeyEvent(KeyEvent e) {
210
211 // We can't guarantee that this is called on the same AppContext as EmbeddedFrame
212 // belongs to. That's why we can't use public methods to find current focus cycle
213 // root. Instead, we access KFM's private field directly.
214 if (currentCycleRoot == null) {
215 currentCycleRoot = (Field)AccessController.doPrivileged(new PrivilegedAction() {
216 public Object run() {
217 try {
218 Field unaccessibleRoot = KeyboardFocusManager.class.
219 getDeclaredField("currentFocusCycleRoot");
220 if (unaccessibleRoot != null) {
221 unaccessibleRoot.setAccessible(true);
222 }
223 return unaccessibleRoot;
224 } catch (NoSuchFieldException e1) {
225 assert false;
226 } catch (SecurityException e2) {
227 assert false;
228 }
229 return null;
230 }
231 });
232 }
233
234 Container currentRoot = null;
235 if (currentCycleRoot != null) {
236 try {
237 // The field is static, so we can pass null to Field.get() as the argument.
238 currentRoot = (Container)currentCycleRoot.get(null);
239 } catch (IllegalAccessException e3) {
240 // This is impossible: currentCycleRoot would be null if setAccessible failed.
241 assert false;
242 }
243 }
244
245 // if we are not in EmbeddedFrame's cycle, we should not try to leave.
246 if (this != currentRoot) {
247 return false;
248 }
249
250 // KEY_TYPED events cannot be focus traversal keys
251 if (e.getID() == KeyEvent.KEY_TYPED) {
252 return false;
253 }
254
255 if (!getFocusTraversalKeysEnabled() || e.isConsumed()) {
256 return false;
257 }
258
259 AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
260 Set toTest;
261 Component currentFocused = e.getComponent();
262
263 toTest = getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
264 if (toTest.contains(stroke)) {
265 // 6581899: performance improvement for SortingFocusTraversalPolicy
266 Component last = getFocusTraversalPolicy().getLastComponent(this);
267 if (currentFocused == last || last == null) {
268 if (traverseOut(FORWARD)) {
269 e.consume();
270 return true;
271 }
272 }
273 }
274
275 toTest = getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
276 if (toTest.contains(stroke)) {
277 // 6581899: performance improvement for SortingFocusTraversalPolicy
278 Component first = getFocusTraversalPolicy().getFirstComponent(this);
279 if (currentFocused == first || first == null) {
280 if (traverseOut(BACKWARD)) {
281 e.consume();
282 return true;
283 }
284 }
285 }
286 return false;
287 }
288
289 /**
290 * This method is called by the embedder when we should receive focus as element
291 * of the traversal chain. The method requests focus on:
292 * 1. the first Component of this EmbeddedFrame if user moves focus forward
293 * in the focus traversal cycle.
294 * 2. the last Component of this EmbeddedFrame if user moves focus backward
295 * in the focus traversal cycle.
296 *
297 * The direction parameter specifies which of the two mentioned cases is
298 * happening. Use FORWARD and BACKWARD constants defined in the EmbeddedFrame class
299 * to avoid confusing boolean values.
300 *
301 * A concrete implementation of this method is defined in the platform-dependent
302 * subclasses.
303 *
304 * @param direction FORWARD or BACKWARD
305 * @return true, if the EmbeddedFrame wants to get focus, false otherwise.
306 */
307 public boolean traverseIn(boolean direction) {
308 Component comp = null;
309
310 if (direction == FORWARD) {
311 comp = getFocusTraversalPolicy().getFirstComponent(this);
312 } else {
313 comp = getFocusTraversalPolicy().getLastComponent(this);
314 }
315 if (comp != null) {
316 // comp.requestFocus(); - Leads to a hung.
317
318 AWTAccessor.getKeyboardFocusManagerAccessor().setMostRecentFocusOwner(this, comp);
319 synthesizeWindowActivation(true);
320 }
321 return (null != comp);
322 }
323
324 /**
325 * This method is called from dispatchKeyEvent in the following two cases:
326 * 1. The focus is on the first Component of this EmbeddedFrame and we are
327 * about to transfer the focus backward.
328 * 2. The focus in on the last Component of this EmbeddedFrame and we are
329 * about to transfer the focus forward.
330 * This is needed to give the opportuity for keyboard focus to leave the
331 * EmbeddedFrame. Override this method, initiate focus transfer in it and
332 * return true if you want the focus to leave EmbeddedFrame's cycle.
333 * The direction parameter specifies which of the two mentioned cases is
334 * happening. Use FORWARD and BACKWARD constants defined in EmbeddedFrame
335 * to avoid confusing boolean values.
336 *
337 * @param direction FORWARD or BACKWARD
338 * @return true, if EmbeddedFrame wants the focus to leave it,
339 * false otherwise.
340 */
341 protected boolean traverseOut(boolean direction) {
342 return false;
343 }
344
345 /**
346 * Block modifying any frame attributes, since they aren't applicable
347 * for EmbeddedFrames.
348 */
349 public void setTitle(String title) {}
350 public void setIconImage(Image image) {}
351 public void setIconImages(java.util.List<? extends Image> icons) {}
352 public void setMenuBar(MenuBar mb) {}
353 public void setResizable(boolean resizable) {}
354 public void remove(MenuComponent m) {}
355
356 public boolean isResizable() {
357 return true;
358 }
359
360 public void addNotify() {
361 synchronized (getTreeLock()) {
362 if (getPeer() == null) {
363 setPeer(new NullEmbeddedFramePeer());
364 }
365 super.addNotify();
366 }
367 }
368
369 // These three functions consitute RFE 4100710. Do not remove.
370 public void setCursorAllowed(boolean isCursorAllowed) {
371 this.isCursorAllowed = isCursorAllowed;
372 getPeer().updateCursorImmediately();
373 }
374 public boolean isCursorAllowed() {
375 return isCursorAllowed;
376 }
377 public Cursor getCursor() {
378 return (isCursorAllowed)
379 ? super.getCursor()
380 : Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
381 }
382
383 protected void setPeer(final ComponentPeer p){
384 if (fieldPeer == null) {
385 fieldPeer = (Field)AccessController.doPrivileged(new PrivilegedAction() {
386 public Object run() {
387 try {
388 Field lnkPeer = Component.class.getDeclaredField("peer");
389 if (lnkPeer != null) {
390 lnkPeer.setAccessible(true);
391 }
392 return lnkPeer;
393 } catch (NoSuchFieldException e) {
394 assert false;
395 } catch (SecurityException e) {
396 assert false;
397 }
398 return null;
399 }//run
400 });
401 }
402 try{
403 if (fieldPeer !=null){
404 fieldPeer.set(EmbeddedFrame.this, p);
405 }
406 } catch (IllegalAccessException e) {
407 assert false;
408 }
409 }; //setPeer method ends
410
411 /**
412 * Synthesize native message to activate or deactivate EmbeddedFrame window
413 * depending on the value of parameter <code>b</code>.
414 * Peers should override this method if they are to implement
415 * this functionality.
416 * @param doActivate if <code>true</code>, activates the window;
417 * otherwise, deactivates the window
418 */
419 public void synthesizeWindowActivation(boolean doActivate) {}
420
421 /**
422 * Moves this embedded frame to a new location. The top-left corner of
423 * the new location is specified by the <code>x</code> and <code>y</code>
424 * parameters relative to the native parent component.
425 * <p>
426 * setLocation() and setBounds() for EmbeddedFrame really don't move it
427 * within the native parent. These methods always put embedded frame to
428 * (0, 0) for backward compatibility. To allow moving embedded frame
429 * setLocationPrivate() and setBoundsPrivate() were introduced, and they
430 * work just the same way as setLocation() and setBounds() for usual,
431 * non-embedded components.
432 * </p>
433 * <p>
434 * Using usual get/setLocation() and get/setBounds() together with new
435 * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended.
436 * For example, calling getBoundsPrivate() after setLocation() works fine,
437 * but getBounds() after setBoundsPrivate() may return unpredictable value.
438 * </p>
439 * @param x the new <i>x</i>-coordinate relative to the parent component
440 * @param y the new <i>y</i>-coordinate relative to the parent component
441 * @see java.awt.Component#setLocation
442 * @see #getLocationPrivate
443 * @see #setBoundsPrivate
444 * @see #getBoundsPrivate
445 * @since 1.5
446 */
447 protected void setLocationPrivate(int x, int y) {
448 Dimension size = getSize();
449 setBoundsPrivate(x, y, size.width, size.height);
450 }
451
452 /**
453 * Gets the location of this embedded frame as a point specifying the
454 * top-left corner relative to parent component.
455 * <p>
456 * setLocation() and setBounds() for EmbeddedFrame really don't move it
457 * within the native parent. These methods always put embedded frame to
458 * (0, 0) for backward compatibility. To allow getting location and size
459 * of embedded frame getLocationPrivate() and getBoundsPrivate() were
460 * introduced, and they work just the same way as getLocation() and getBounds()
461 * for ususal, non-embedded components.
462 * </p>
463 * <p>
464 * Using usual get/setLocation() and get/setBounds() together with new
465 * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended.
466 * For example, calling getBoundsPrivate() after setLocation() works fine,
467 * but getBounds() after setBoundsPrivate() may return unpredictable value.
468 * </p>
469 * @return a point indicating this embedded frame's top-left corner
470 * @see java.awt.Component#getLocation
471 * @see #setLocationPrivate
472 * @see #setBoundsPrivate
473 * @see #getBoundsPrivate
474 * @since 1.6
475 */
476 protected Point getLocationPrivate() {
477 Rectangle bounds = getBoundsPrivate();
478 return new Point(bounds.x, bounds.y);
479 }
480
481 /**
482 * Moves and resizes this embedded frame. The new location of the top-left
483 * corner is specified by <code>x</code> and <code>y</code> parameters
484 * relative to the native parent component. The new size is specified by
485 * <code>width</code> and <code>height</code>.
486 * <p>
487 * setLocation() and setBounds() for EmbeddedFrame really don't move it
488 * within the native parent. These methods always put embedded frame to
489 * (0, 0) for backward compatibility. To allow moving embedded frames
490 * setLocationPrivate() and setBoundsPrivate() were introduced, and they
491 * work just the same way as setLocation() and setBounds() for usual,
492 * non-embedded components.
493 * </p>
494 * <p>
495 * Using usual get/setLocation() and get/setBounds() together with new
496 * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended.
497 * For example, calling getBoundsPrivate() after setLocation() works fine,
498 * but getBounds() after setBoundsPrivate() may return unpredictable value.
499 * </p>
500 * @param x the new <i>x</i>-coordinate relative to the parent component
501 * @param y the new <i>y</i>-coordinate relative to the parent component
502 * @param width the new <code>width</code> of this embedded frame
503 * @param height the new <code>height</code> of this embedded frame
504 * @see java.awt.Component#setBounds
505 * @see #setLocationPrivate
506 * @see #getLocationPrivate
507 * @see #getBoundsPrivate
508 * @since 1.5
509 */
510 protected void setBoundsPrivate(int x, int y, int width, int height) {
511 final FramePeer peer = (FramePeer)getPeer();
512 if (peer != null) {
513 peer.setBoundsPrivate(x, y, width, height);
514 }
515 }
516
517 /**
518 * Gets the bounds of this embedded frame as a rectangle specifying the
519 * width, height and location relative to the native parent component.
520 * <p>
521 * setLocation() and setBounds() for EmbeddedFrame really don't move it
522 * within the native parent. These methods always put embedded frame to
523 * (0, 0) for backward compatibility. To allow getting location and size
524 * of embedded frames getLocationPrivate() and getBoundsPrivate() were
525 * introduced, and they work just the same way as getLocation() and getBounds()
526 * for ususal, non-embedded components.
527 * </p>
528 * <p>
529 * Using usual get/setLocation() and get/setBounds() together with new
530 * get/setLocationPrivate() and get/setBoundsPrivate() is not recommended.
531 * For example, calling getBoundsPrivate() after setLocation() works fine,
532 * but getBounds() after setBoundsPrivate() may return unpredictable value.
533 * </p>
534 * @return a rectangle indicating this embedded frame's bounds
535 * @see java.awt.Component#getBounds
536 * @see #setLocationPrivate
537 * @see #getLocationPrivate
538 * @see #setBoundsPrivate
539 * @since 1.6
540 */
541 protected Rectangle getBoundsPrivate() {
542 final FramePeer peer = (FramePeer)getPeer();
543 if (peer != null) {
544 return peer.getBoundsPrivate();
545 }
546 else {
547 return getBounds();
548 }
549 }
550
551 public void toFront() {}
552 public void toBack() {}
553
554 public abstract void registerAccelerator(AWTKeyStroke stroke);
555 public abstract void unregisterAccelerator(AWTKeyStroke stroke);
556
557 /**
558 * Checks if the component is in an EmbeddedFrame. If so,
559 * returns the applet found in the hierarchy or null if
560 * not found.
561 * @return the parent applet or {@ null}
562 * @since 1.6
563 */
564 public static Applet getAppletIfAncestorOf(Component comp) {
565 Container parent = comp.getParent();
566 Applet applet = null;
567 while (parent != null && !(parent instanceof EmbeddedFrame)) {
568 if (parent instanceof Applet) {
569 applet = (Applet)parent;
570 }
571 parent = parent.getParent();
572 }
573 return parent == null ? null : applet;
574 }
575
576 /**
577 * This method should be overriden in subclasses. It is
578 * called when window this frame is within should be blocked
579 * by some modal dialog.
580 */
581 public void notifyModalBlocked(Dialog blocker, boolean blocked) {
582 }
583
584 private static class NullEmbeddedFramePeer
585 extends NullComponentPeer implements FramePeer {
586 public void setTitle(String title) {}
587 public void setIconImage(Image im) {}
588 public void updateIconImages() {}
589 public void setMenuBar(MenuBar mb) {}
590 public void setResizable(boolean resizeable) {}
591 public void setState(int state) {}
592 public int getState() { return Frame.NORMAL; }
593 public void setMaximizedBounds(Rectangle b) {}
594 public void toFront() {}
595 public void toBack() {}
596 public void updateFocusableWindowState() {}
597 public void updateAlwaysOnTop() {}
598 public void setAlwaysOnTop(boolean alwaysOnTop) {}
599 public Component getGlobalHeavyweightFocusOwner() { return null; }
600 public void setBoundsPrivate(int x, int y, int width, int height) {
601 setBounds(x, y, width, height, SET_BOUNDS);
602 }
603 public Rectangle getBoundsPrivate() {
604 return getBounds();
605 }
606 public void setModalBlocked(Dialog blocker, boolean blocked) {}
607
608 /**
609 * @see java.awt.peer.ContainerPeer#restack
610 */
611 public void restack() {
612 throw new UnsupportedOperationException();
613 }
614
615 /**
616 * @see java.awt.peer.ContainerPeer#isRestackSupported
617 */
618 public boolean isRestackSupported() {
619 return false;
620 }
621 public boolean requestWindowFocus() {
622 return false;
623 }
624 public void updateMinimumSize() {
625 }
626
627 public void setOpacity(float opacity) {
628 }
629
630 public void setOpaque(boolean isOpaque) {
631 }
632
633 public void updateWindow() {
634 }
635
636 public void repositionSecurityWarning() {
637 }
638 }
639 } // class EmbeddedFrame