Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.embed.swing;
027
028import java.awt.AlphaComposite;
029import java.awt.AWTEvent;
030import java.awt.Cursor;
031import java.awt.Dimension;
032import java.awt.Graphics;
033import java.awt.Graphics2D;
034import java.awt.KeyboardFocusManager;
035import java.awt.Point;
036import java.awt.Window;
037import java.awt.event.AWTEventListener;
038import java.awt.event.ComponentEvent;
039import java.awt.event.FocusEvent;
040import java.awt.event.HierarchyEvent;
041import java.awt.event.InputEvent;
042import java.awt.event.KeyEvent;
043import java.awt.event.MouseEvent;
044import java.awt.event.MouseWheelEvent;
045import java.awt.image.BufferedImage;
046import java.awt.image.DataBufferInt;
047import java.nio.IntBuffer;
048import java.security.AccessController;
049import java.security.PrivilegedAction;
050import java.util.concurrent.CountDownLatch;
051
052import javafx.application.Platform;
053import javafx.scene.Scene;
054
055import javax.swing.JComponent;
056import javax.swing.SwingUtilities;
057
058import com.sun.javafx.application.PlatformImpl;
059import com.sun.javafx.cursor.CursorFrame;
060import com.sun.javafx.embed.AbstractEvents;
061import com.sun.javafx.embed.EmbeddedSceneInterface;
062import com.sun.javafx.embed.EmbeddedStageInterface;
063import com.sun.javafx.embed.HostInterface;
064import com.sun.javafx.stage.EmbeddedWindow;
065import com.sun.javafx.tk.Toolkit;
066
067import java.util.concurrent.atomic.AtomicInteger;
068import sun.awt.CausedFocusEvent;
069import sun.awt.SunToolkit;
070
071/**
072* {@code JFXPanel} is a component to embed JavaFX content into
073 * Swing applications. The content to be displayed is specified
074 * with the {@link #setScene} method that accepts an instance of
075 * JavaFX {@code Scene}. After the scene is assigned, it gets
076 * repainted automatically. All the input and focus events are
077 * forwarded to the scene transparently to the developer.
078 * <p>
079 * There are some restrictions related to {@code JFXPanel}. As a
080 * Swing component, it should only be accessed from the event
081 * dispatch thread, except the {@link #setScene} method, which can
082 * be called either on the event dispatch thread or on the JavaFX
083 * application thread.
084 * <p>
085 * Here is a typical pattern how {@code JFXPanel} can used:
086 * <pre>
087 *     public class Test {
088 *
089 *         private static void initAndShowGUI() {
090 *             // This method is invoked on Swing thread
091 *             JFrame frame = new JFrame("FX");
092 *             final JFXPanel fxPanel = new JFXPanel();
093 *             frame.add(fxPanel);
094 *             frame.setVisible(true);
095 *
096 *             Platform.runLater(new Runnable() {
097 *                 &#064;Override
098 *                 public void run() {
099 *                     initFX(fxPanel);
100 *                 }
101 *             });
102 *         }
103 *
104 *         private static void initFX(JFXPanel fxPanel) {
105 *             // This method is invoked on JavaFX thread
106 *             Scene scene = createScene();
107 *             fxPanel.setScene(scene);
108 *         }
109 *
110 *         public static void main(String[] args) {
111 *             SwingUtilities.invokeLater(new Runnable() {
112 *                 &#064;Override
113 *                 public void run() {
114 *                     initAndShowGUI();
115 *                 }
116 *             });
117 *         }
118 *     }
119 * </pre>
120 *
121 */
122public class JFXPanel extends JComponent {
123
124    private static PlatformImpl.FinishListener finishListener;
125    private static boolean firstPanelShown = false;
126
127    private HostContainer hostContainer;
128
129    private volatile EmbeddedWindow stage;
130    private volatile Scene scene;
131
132    private final SwingDnD dnd;
133
134    private EmbeddedStageInterface stagePeer;
135    private EmbeddedSceneInterface scenePeer;
136
137    // Dimensions of back buffer used to draw FX content
138    private int pWidth;
139    private int pHeight;
140
141    // Preferred size set from FX
142    private volatile int pPreferredWidth = -1;
143    private volatile int pPreferredHeight = -1;
144
145    // Cached copy of this component's location on screen to avoid
146    // calling getLocationOnScreen() under the tree lock on FX thread
147    private volatile int screenX = 0;
148    private volatile int screenY = 0;
149
150    // accessed on EDT only
151    private BufferedImage pixelsIm;
152
153    private volatile float opacity = 1.0f;
154
155    // Indicates how many times setFxEnabled(false) has been called.
156    // A value of 0 means the component is enabled.
157    private AtomicInteger disableCount = new AtomicInteger(0);
158
159    private boolean isCapturingMouse = false;
160
161    // Initialize FX runtime when the JFXPanel instance is constructed
162    private synchronized static void initFx() {
163        if (finishListener != null) {
164            // Already registered
165            return;
166        }
167        // Need to install a finish listener to catch calls to Platform.exit
168        finishListener = new PlatformImpl.FinishListener() {
169            @Override public void idle(boolean implicitExit) {
170                if (!firstPanelShown) {
171                    return;
172                }
173                PlatformImpl.removeListener(finishListener);
174                finishListener = null;
175                if (implicitExit) {
176                    Platform.exit();
177                }
178            }
179            @Override public void exitCalled() {
180            }
181        };
182        PlatformImpl.addListener(finishListener);
183        // Note that calling PlatformImpl.startup more than once is OK
184        PlatformImpl.startup(new Runnable() {
185            @Override public void run() {
186                // No need to do anything here
187            }
188        });
189    }
190
191    /**
192     * Creates a new {@code JFXPanel} object.
193     * <p>
194     * <b>Implementation note</b>: when the first {@code JFXPanel} object
195     * is created, it implicitly initializes the JavaFX runtime. This is the
196     * preferred way to initialize JavaFX in Swing.
197     */
198    public JFXPanel() {
199        super();
200
201        initFx();
202
203        hostContainer = new HostContainer();
204
205        enableEvents(InputEvent.COMPONENT_EVENT_MASK |
206                     InputEvent.FOCUS_EVENT_MASK |
207                     InputEvent.HIERARCHY_BOUNDS_EVENT_MASK |
208                     InputEvent.HIERARCHY_EVENT_MASK |
209                     InputEvent.MOUSE_EVENT_MASK |
210                     InputEvent.MOUSE_MOTION_EVENT_MASK |
211                     InputEvent.MOUSE_WHEEL_EVENT_MASK |
212                     InputEvent.KEY_EVENT_MASK);
213
214        setFocusable(true);
215        setFocusTraversalKeysEnabled(false);
216
217        this.dnd = new SwingDnD(this, new SwingDnD.JFXPanelFacade() {
218
219            @Override
220            public EmbeddedSceneInterface getScene() {
221                return isFxEnabled() ? scenePeer : null;
222            }
223        });
224    }
225
226    /**
227     * Returns the JavaFX scene attached to this {@code JFXPanel}.
228     *
229     * @return the {@code Scene} attached to this {@code JFXPanel}
230     */
231    public Scene getScene() {
232        return scene;
233    }
234
235    /**
236     * Attaches a {@code Scene} object to display in this {@code
237     * JFXPanel}. This method can be called either on the event
238     * dispatch thread or the JavaFX application thread.
239     *
240     * @param newScene a scene to display in this {@code JFXpanel}
241     *
242     * @see java.awt.EventQueue#isDispatchThread()
243     * @see javafx.application.Platform#isFxApplicationThread()
244     */
245    public void setScene(final Scene newScene) {
246        if (Toolkit.getToolkit().isFxUserThread()) {
247            setSceneImpl(newScene);
248        } else {
249            final CountDownLatch initLatch = new CountDownLatch(1);
250            Platform.runLater(new Runnable() {
251                @Override
252                public void run() {
253                    setSceneImpl(newScene);
254                    initLatch.countDown();
255                }
256            });
257            try {
258                initLatch.await();
259            } catch (InterruptedException z) {
260                z.printStackTrace(System.err);
261            }
262        }
263    }
264
265    /*
266     * Called on JavaFX app thread.
267     */
268    private void setSceneImpl(Scene newScene) {
269        if ((stage != null) && (newScene == null)) {
270            stage.hide();
271            stage = null;
272        }
273        scene = newScene;
274        if ((stage == null) && (newScene != null)) {
275            stage = new EmbeddedWindow(hostContainer);
276        }
277        if (stage != null) {
278            stage.setScene(newScene);
279            if (!stage.isShowing()) {
280                stage.show();
281                firstPanelShown = true;
282            }
283        }
284    }
285
286    /**
287     * {@code JFXPanel}'s opacity is controlled by the JavaFX content
288     * which is displayed in this component, so this method overrides
289     * {@link javax.swing.JComponent#setOpaque(boolean)} to only accept a
290     * {@code false} value. If this method is called with a {@code true}
291     * value, no action is performed.
292     *
293     * @param opaque must be {@code false}
294     */
295    @Override
296    public final void setOpaque(boolean opaque) {
297        // Don't let user control opacity
298        if (!opaque) {
299            super.setOpaque(opaque);
300        }
301    }
302
303    /**
304     * {@code JFXPanel}'s opacity is controlled by the JavaFX content
305     * which is displayed in this component, so this method overrides
306     * {@link javax.swing.JComponent#isOpaque()} to always return a
307     * {@code false} value.
308     *
309     * @return a {@code false} value
310     */
311    @Override
312    public final boolean isOpaque() {
313        return false;
314    }
315
316    private void sendMouseEventToFX(MouseEvent e) {
317        if (scenePeer == null || !isFxEnabled()) {
318            return;
319        }
320
321        int extModifiers = e.getModifiersEx();
322        // Fix for RT-15457: we should report no mouse button upon mouse release, so
323        // *BtnDown values are calculated based on extMofifiers, not e.getButton()
324        boolean primaryBtnDown = (extModifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0;
325        boolean middleBtnDown = (extModifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0;
326        boolean secondaryBtnDown = (extModifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0;
327        // Fix for RT-16558: if a PRESSED event is consumed, e.g. by a Swing Popup,
328        // subsequent DRAGGED and RELEASED events should not be sent to FX as well
329        if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
330            if (!isCapturingMouse) {
331                return;
332            }
333        } else if (e.getID() == MouseEvent.MOUSE_PRESSED) {
334            isCapturingMouse = true;
335        } else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
336            if (!isCapturingMouse) {
337                return;
338            }
339            isCapturingMouse = primaryBtnDown || middleBtnDown || secondaryBtnDown;
340        }
341        scenePeer.mouseEvent(
342                SwingEvents.mouseIDToEmbedMouseType(e.getID()),
343                SwingEvents.mouseButtonToEmbedMouseButton(e.getButton(), extModifiers),
344                primaryBtnDown, middleBtnDown, secondaryBtnDown,
345                e.getClickCount(),
346                e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(),
347                (extModifiers & MouseEvent.SHIFT_DOWN_MASK) != 0,
348                (extModifiers & MouseEvent.CTRL_DOWN_MASK) != 0,
349                (extModifiers & MouseEvent.ALT_DOWN_MASK) != 0,
350                (extModifiers & MouseEvent.META_DOWN_MASK) != 0,
351                SwingEvents.getWheelRotation(e), e.isPopupTrigger());
352        if (e.isPopupTrigger()) {
353            scenePeer.menuEvent(e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), false);
354        }
355    }
356
357    /**
358     * Overrides the {@link java.awt.Component#processMouseEvent(MouseEvent)}
359     * method to dispatch the mouse event to the JavaFX scene attached to this
360     * {@code JFXPanel}.
361     *
362     * @param e the mouse event to dispatch to the JavaFX scene
363     */
364    @Override
365    protected void processMouseEvent(MouseEvent e) {
366        if ((e.getID() == MouseEvent.MOUSE_PRESSED) &&
367            (e.getButton() == MouseEvent.BUTTON1)) {
368            if (!hasFocus()) {
369                requestFocus();
370            }
371        }
372
373        sendMouseEventToFX(e);
374        super.processMouseEvent(e);
375    }
376
377    /**
378     * Overrides the {@link java.awt.Component#processMouseMotionEvent(MouseEvent)}
379     * method to dispatch the mouse motion event to the JavaFX scene attached to
380     * this {@code JFXPanel}.
381     *
382     * @param e the mouse motion event to dispatch to the JavaFX scene
383     */
384    @Override
385    protected void processMouseMotionEvent(MouseEvent e) {
386        sendMouseEventToFX(e);
387        super.processMouseMotionEvent(e);
388    }
389
390    /**
391     * Overrides the
392     * {@link java.awt.Component#processMouseWheelEvent(MouseWheelEvent)}
393     * method to dispatch the mouse wheel event to the JavaFX scene attached
394     * to this {@code JFXPanel}.
395     *
396     * @param e the mouse wheel event to dispatch to the JavaFX scene
397     */
398    @Override
399    protected void processMouseWheelEvent(MouseWheelEvent e) {
400        sendMouseEventToFX(e);
401        super.processMouseWheelEvent(e);
402    }
403
404    private void sendKeyEventToFX(final KeyEvent e) {
405        if (scenePeer == null || !isFxEnabled()) {
406            return;
407        }
408
409        char[] chars = (e.getKeyChar() == KeyEvent.CHAR_UNDEFINED)
410                       ? new char[] {}
411                       : new char[] { e.getKeyChar() };
412
413        scenePeer.keyEvent(
414                SwingEvents.keyIDToEmbedKeyType(e.getID()),
415                e.getKeyCode(), chars,
416                SwingEvents.keyModifiersToEmbedKeyModifiers(e.getModifiersEx()));
417    }
418
419    /**
420     * Overrides the {@link java.awt.Component#processKeyEvent(KeyEvent)}
421     * method to dispatch the key event to the JavaFX scene attached to this
422     * {@code JFXPanel}.
423     *
424     * @param e the key event to dispatch to the JavaFX scene
425     */
426    @Override
427    protected void processKeyEvent(KeyEvent e) {
428        sendKeyEventToFX(e);
429        super.processKeyEvent(e);
430    }
431
432    private void sendResizeEventToFX() {
433        if (stagePeer != null) {
434            stagePeer.setSize(pWidth, pHeight);
435        }
436        if (scenePeer != null) {
437            scenePeer.setSize(pWidth, pHeight);
438        }
439    }
440
441    /**
442     * Overrides the
443     * {@link java.awt.Component#processComponentEvent(ComponentEvent)}
444     * method to dispatch {@link java.awt.event.ComponentEvent#COMPONENT_RESIZED}
445     * events to the JavaFX scene attached to this {@code JFXPanel}. The JavaFX
446     * scene object is then resized to match the {@code JFXPanel} size.
447     *
448     * @param e the component event to dispatch to the JavaFX scene
449     */
450    @Override
451    protected void processComponentEvent(ComponentEvent e) {
452        switch (e.getID()) {
453            case ComponentEvent.COMPONENT_RESIZED: {
454                updateComponentSize();
455                break;
456            }
457            case ComponentEvent.COMPONENT_MOVED: {
458                if (updateScreenLocation()) {
459                    sendMoveEventToFX();
460                }
461                break;
462            }
463            default: {
464                break;
465            }
466        }
467        super.processComponentEvent(e);
468    }
469
470    // called on EDT only
471    private void updateComponentSize() {
472        int oldWidth = pWidth;
473        int oldHeight = pHeight;
474        // It's quite possible to get negative values here, this is not
475        // what JavaFX embedded scenes/stages are ready to
476        pWidth = Math.max(0, getWidth());
477        pHeight = Math.max(0, getHeight());
478        if (oldWidth != pWidth || oldHeight != pHeight) {
479            resizePixels();
480            sendResizeEventToFX();
481        }
482    }
483
484    // This methods should only be called on EDT
485    private boolean updateScreenLocation() {
486        synchronized (getTreeLock()) {
487            if (isShowing()) {
488                Point p = getLocationOnScreen();
489                screenX = p.x;
490                screenY = p.y;
491                return true;
492            }
493        }
494        return false;
495    }
496
497    private void sendMoveEventToFX() {
498        if (stagePeer == null) {
499            return;
500        }
501
502        stagePeer.setLocation(screenX, screenY);
503    }
504
505    /**
506     * Overrides the
507     * {@link java.awt.Component#processHierarchyBoundsEvent(HierarchyEvent)}
508     * method to process {@link java.awt.event.HierarchyEvent#ANCESTOR_MOVED}
509     * events and update the JavaFX scene location to match the {@code
510     * JFXPanel} location on the screen.
511     *
512     * @param e the hierarchy bounds event to process
513     */
514    @Override
515    protected void processHierarchyBoundsEvent(HierarchyEvent e) {
516        if (e.getID() == HierarchyEvent.ANCESTOR_MOVED) {
517            if (updateScreenLocation()) {
518                sendMoveEventToFX();
519            }
520        }
521        super.processHierarchyBoundsEvent(e);
522    }
523
524    @Override
525    protected void processHierarchyEvent(HierarchyEvent e) {
526        if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
527            if (updateScreenLocation()) {
528                sendMoveEventToFX();
529            }
530        }
531        super.processHierarchyEvent(e);
532    }
533
534    private void sendFocusEventToFX(final FocusEvent e) {
535        if ((stage == null) || (stagePeer == null) || !isFxEnabled()) {
536            return;
537        }
538
539        boolean focused = (e.getID() == FocusEvent.FOCUS_GAINED);
540        int focusCause = (focused ? AbstractEvents.FOCUSEVENT_ACTIVATED :
541                                      AbstractEvents.FOCUSEVENT_DEACTIVATED);
542
543        if (focused && (e instanceof CausedFocusEvent)) {
544            CausedFocusEvent ce = (CausedFocusEvent)e;
545            if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_FORWARD) {
546                focusCause = AbstractEvents.FOCUSEVENT_TRAVERSED_FORWARD;
547                        } else if (ce.getCause() == sun.awt.CausedFocusEvent.Cause.TRAVERSAL_BACKWARD) {
548                focusCause = AbstractEvents.FOCUSEVENT_TRAVERSED_BACKWARD;
549                        }
550                    }
551        stagePeer.setFocused(focused, focusCause);
552    }
553
554    /**
555     * Overrides the
556     * {@link java.awt.Component#processFocusEvent(FocusEvent)}
557     * method to dispatch focus events to the JavaFX scene attached to this
558     * {@code JFXPanel}.
559     *
560     * @param e the focus event to dispatch to the JavaFX scene
561     */
562    @Override
563    protected void processFocusEvent(FocusEvent e) {
564        sendFocusEventToFX(e);
565        super.processFocusEvent(e);
566    }
567
568    // called on EDT only
569    private void resizePixels() {
570        if ((pWidth <= 0) || (pHeight <= 0)) {
571            pixelsIm = null;
572        } else {
573            BufferedImage oldIm = pixelsIm;
574            pixelsIm = new BufferedImage(pWidth, pHeight, BufferedImage.TYPE_INT_ARGB);
575            if (oldIm != null) {
576                Graphics g = pixelsIm.getGraphics();
577                try {
578                    g.drawImage(oldIm, 0, 0, null);
579                } finally {
580                    g.dispose();
581                }
582            }
583        }
584    }
585
586    /**
587     * Overrides the {@link javax.swing.JComponent#paintComponent(Graphics)}
588     * method to paint the content of the JavaFX scene attached to this
589     * {@code JFXpanel}.
590     *
591     * @param g the Graphics context in which to paint
592     *
593     * @see #isOpaque()
594     */
595    @Override
596    protected void paintComponent(Graphics g) {
597        if ((scenePeer == null) || (pixelsIm == null)) {
598            return;
599        }
600
601        DataBufferInt dataBuf = (DataBufferInt)pixelsIm.getRaster().getDataBuffer();
602        int[] pixelsData = dataBuf.getData();
603        IntBuffer buf = IntBuffer.wrap(pixelsData);
604        if (!scenePeer.getPixels(buf, pWidth, pHeight)) {
605            // May happen during early Quantum initialization
606            return;
607        }
608
609        Graphics gg = null;
610        try {
611            gg = g.create();
612            if ((opacity < 1.0f) && (gg instanceof Graphics2D)) {
613                Graphics2D g2d = (Graphics2D)gg;
614                AlphaComposite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
615                g2d.setComposite(c);
616            }
617            gg.drawImage(pixelsIm, 0, 0, null);
618        } catch (Throwable th) {
619            th.printStackTrace();
620        } finally {
621            if (gg != null) {
622                gg.dispose();
623            }
624        }
625    }
626
627    /**
628     * Returns the preferred size of this {@code JFXPanel}, either
629     * previously set with {@link #setPreferredSize(Dimension)} or
630     * based on the content of the JavaFX scene attached to this {@code
631     * JFXPanel}.
632     *
633     * @return prefSize this {@code JFXPanel} preferred size
634     */
635    @Override
636    public Dimension getPreferredSize() {
637        if (isPreferredSizeSet() || scenePeer == null) {
638            return super.getPreferredSize();
639        }
640        return new Dimension(pPreferredWidth, pPreferredHeight);
641    }
642
643    private boolean isFxEnabled() {
644        return this.disableCount.get() == 0;
645    }
646
647    private void setFxEnabled(boolean enabled) {
648        if (!enabled) {
649            disableCount.incrementAndGet();
650        } else {
651            if (disableCount.get() == 0) {
652                //should report a warning about an extra enable call ?
653                return;
654            }
655            disableCount.decrementAndGet();
656        }
657    }
658
659    private final AWTEventListener ungrabListener = new AWTEventListener() {
660        @Override
661        public void eventDispatched(AWTEvent event) {
662            if (event instanceof sun.awt.UngrabEvent) {
663                Platform.runLater(new Runnable() {
664                    @Override
665                    public void run() {
666                        if (JFXPanel.this.stagePeer != null) {
667                            JFXPanel.this.stagePeer.focusUngrab();
668                        }
669                    }
670                });
671            }
672        }
673    };
674
675    /**
676     * Notifies this component that it now has a parent component. When this
677     * method is invoked, the chain of parent components is set up with
678     * KeyboardAction event listeners.
679     */
680    @Override
681    public void addNotify() {
682        super.addNotify();
683
684        dnd.addNotify();
685
686        AccessController.doPrivileged(new PrivilegedAction<Void>() {
687            public Void run() {
688                JFXPanel.this.getToolkit().addAWTEventListener(ungrabListener,
689                    sun.awt.SunToolkit.GRAB_EVENT_MASK);
690                return null;
691            }
692        });
693
694        updateComponentSize(); // see RT-23603
695
696        Platform.runLater(new Runnable() {
697            @Override
698            public void run() {
699                if ((stage != null) && !stage.isShowing()) {
700                    stage.show();
701                    firstPanelShown = true;
702                    sendMoveEventToFX();
703                }
704            }
705        });
706    }
707
708    /**
709     * Notifies this component that it no longer has a parent component.
710     * When this method is invoked, any KeyboardActions set up in the the
711     * chain of parent components are removed.
712     */
713    @Override public void removeNotify() {
714        Platform.runLater(new Runnable() {
715            @Override
716            public void run() {
717                if ((stage != null) && stage.isShowing()) {
718                    stage.hide();
719                }
720            }
721        });
722
723        pixelsIm = null;
724        pWidth = 0;
725        pHeight = 0;
726
727        super.removeNotify();
728
729        AccessController.doPrivileged(new PrivilegedAction<Void>() {
730            public Void run() {
731                JFXPanel.this.getToolkit().removeAWTEventListener(ungrabListener);
732                return null;
733            }
734        });
735
736        dnd.removeNotify();
737
738        /* see CR 4867453 */
739        getInputContext().removeNotify(this);
740    }
741
742    private class HostContainer implements HostInterface {
743
744        @Override
745        public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
746            stagePeer = embeddedStage;
747            if (stagePeer == null) {
748                return;
749            }
750            if (pWidth > 0 && pHeight > 0) {
751                stagePeer.setSize(pWidth, pHeight);
752            }
753            if (JFXPanel.this.isFocusOwner()) {
754                stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED);
755            }
756            sendMoveEventToFX();
757        }
758
759        @Override
760        public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) {
761            scenePeer = embeddedScene;
762            if (scenePeer == null) {
763                return;
764            }
765            if (pWidth > 0 && pHeight > 0) {
766                scenePeer.setSize(pWidth, pHeight);
767            }
768
769            // DnD-related calls on 'scenePeer' should go from AWT EDT.
770            SwingUtilities.invokeLater(new Runnable() {
771
772                @Override
773                public void run() {
774                    if (scenePeer != null) {
775                        scenePeer.setDragStartListener(dnd.getDragStartListener());
776                    }
777                }
778            });
779        }
780
781        @Override
782        public boolean requestFocus() {
783            return requestFocusInWindow();
784        }
785
786        @Override
787        public boolean traverseFocusOut(boolean forward) {
788            KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
789            if (forward) {
790                kfm.focusNextComponent(JFXPanel.this);
791            } else {
792                kfm.focusPreviousComponent(JFXPanel.this);
793            }
794            return true;
795        }
796
797        @Override
798        public void setPreferredSize(final int width, final int height) {
799            JFXPanel.this.pPreferredWidth = width;
800            JFXPanel.this.pPreferredHeight = height;
801            JFXPanel.this.revalidate();
802        }
803
804        @Override
805        public void repaint() {
806            JFXPanel.this.repaint();
807        }
808
809        @Override
810        public void setEnabled(final boolean enabled) {
811            JFXPanel.this.setFxEnabled(enabled);
812        }
813
814        @Override
815        public void setCursor(CursorFrame cursorFrame) {
816            final Cursor cursor = getPlatformCursor(cursorFrame);
817            SwingUtilities.invokeLater(new Runnable() {
818                @Override
819                public void run() {
820                    JFXPanel.this.setCursor(cursor);
821                }
822            });
823        }
824
825        private Cursor getPlatformCursor(final CursorFrame cursorFrame) {
826            final Cursor cachedPlatformCursor =
827                    cursorFrame.getPlatformCursor(Cursor.class);
828            if (cachedPlatformCursor != null) {
829                // platform cursor already cached
830                return cachedPlatformCursor;
831            }
832
833            // platform cursor not cached yet
834            final Cursor platformCursor =
835                    SwingCursors.embedCursorToCursor(cursorFrame);
836            cursorFrame.setPlatforCursor(Cursor.class, platformCursor);
837
838            return platformCursor;
839        }
840
841        @Override
842        public boolean grabFocus() {
843            SwingUtilities.invokeLater(new Runnable() {
844                @Override
845                public void run() {
846                    Window window = SwingUtilities.getWindowAncestor(JFXPanel.this);
847                    if (window != null) {
848                        if (JFXPanel.this.getToolkit() instanceof SunToolkit) {
849                            ((SunToolkit)JFXPanel.this.getToolkit()).grab(window);
850                        }
851                    }
852                }
853            });
854
855            return true; // Oh, well...
856        }
857
858        @Override
859        public void ungrabFocus() {
860            SwingUtilities.invokeLater(new Runnable() {
861                @Override
862                public void run() {
863                    Window window = SwingUtilities.getWindowAncestor(JFXPanel.this);
864                    if (window != null) {
865                        if (JFXPanel.this.getToolkit() instanceof SunToolkit) {
866                            ((SunToolkit)JFXPanel.this.getToolkit()).ungrab(window);
867                        }
868                    }
869                }
870            });
871        }
872    }
873}