1 /*
   2  * Copyright (c) 2018, 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 com.sun.javafx.embed.swing.oldimpl;
  27 
  28 import java.lang.ref.WeakReference;
  29 import java.lang.reflect.Method;
  30 import java.awt.Component;
  31 import java.awt.AWTEvent;
  32 import java.awt.Cursor;
  33 import java.awt.event.WindowFocusListener;
  34 import java.awt.event.MouseEvent;
  35 import java.awt.event.MouseWheelEvent;
  36 import java.awt.event.KeyEvent;
  37 import java.awt.dnd.DragGestureEvent;
  38 import java.awt.dnd.DragGestureRecognizer;
  39 import java.awt.dnd.DragGestureListener;
  40 import java.awt.dnd.DragSource;
  41 import java.awt.dnd.DropTarget;
  42 import java.awt.dnd.InvalidDnDOperationException;
  43 import java.awt.dnd.peer.DragSourceContextPeer;
  44 import sun.awt.UngrabEvent;
  45 import sun.swing.JLightweightFrame;
  46 import sun.swing.LightweightContent;
  47 import javax.swing.JComponent;
  48 import javafx.scene.Scene;
  49 import com.sun.javafx.stage.WindowHelper;
  50 import javafx.embed.swing.SwingNode;
  51 import com.sun.javafx.PlatformUtil;
  52 import com.sun.javafx.scene.NodeHelper;
  53 
  54 import com.sun.javafx.embed.swing.DisposerRecord;
  55 import com.sun.javafx.embed.swing.SwingNodeInterop;
  56 import com.sun.javafx.embed.swing.FXDnD;
  57 import com.sun.javafx.embed.swing.SwingCursors;
  58 import com.sun.javafx.embed.swing.SwingEvents;
  59 import com.sun.javafx.embed.swing.SwingNodeHelper;
  60 
  61 public class SwingNodeInteropO extends SwingNodeInterop {
  62 
  63     private volatile JLightweightFrame lwFrame;
  64 
  65     /**
  66      * Calls LightweightFrameWrapper.notifyDisplayChanged.
  67      * Must be called on EDT only.
  68      */
  69     private static OptionalMethod<JLightweightFrame> jlfNotifyDisplayChanged;
  70     private static OptionalMethod<JLightweightFrame> jlfOverrideNativeWindowHandle;
  71 
  72     static {
  73         jlfNotifyDisplayChanged = new OptionalMethod<>(JLightweightFrame.class,
  74                 "notifyDisplayChanged", Double.TYPE, Double.TYPE);
  75         if (!jlfNotifyDisplayChanged.isSupported()) {
  76             jlfNotifyDisplayChanged = new OptionalMethod<>(
  77                   JLightweightFrame.class,"notifyDisplayChanged", Integer.TYPE);
  78         }
  79 
  80         jlfOverrideNativeWindowHandle = new OptionalMethod<>(JLightweightFrame.class,
  81             "overrideNativeWindowHandle", Long.TYPE, Runnable.class);
  82     }
  83 
  84     public Object createLightweightFrame() {
  85         lwFrame = new JLightweightFrame();
  86         return lwFrame;
  87     }
  88 
  89     public JLightweightFrame getLightweightFrame() { return lwFrame; }
  90 
  91     public MouseEvent createMouseEvent(Object frame,
  92                             int swingID, long swingWhen, int swingModifiers,
  93                             int relX, int relY, int absX, int absY,
  94                             int clickCount, boolean swingPopupTrigger,
  95                             int swingButton) {
  96         JLightweightFrame lwFrame = (JLightweightFrame)frame;
  97         return new java.awt.event.MouseEvent(lwFrame, swingID,
  98                 swingWhen, swingModifiers, relX, relY, absX, absY,
  99                 clickCount, swingPopupTrigger, swingButton);
 100     }
 101 
 102     public MouseWheelEvent createMouseWheelEvent(Object frame,
 103                             int swingModifiers, int x, int y, int wheelRotation) {
 104         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 105         return new MouseWheelEvent(lwFrame, java.awt.event.MouseEvent.MOUSE_WHEEL,
 106                        System.currentTimeMillis(), swingModifiers,
 107                    x, y, 0, 0, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL,
 108                    1, wheelRotation);
 109     }
 110 
 111     public KeyEvent createKeyEvent(Object frame,
 112                                    int swingID, long swingWhen,
 113                                    int swingModifiers,
 114                                    int swingKeyCode, char swingChar) {
 115         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 116         return new java.awt.event.KeyEvent(lwFrame, swingID,
 117                     swingWhen, swingModifiers, swingKeyCode,
 118                     swingChar);
 119     }
 120 
 121     public AWTEvent createUngrabEvent(Object frame) {
 122         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 123         return new UngrabEvent(lwFrame);
 124     }
 125 
 126     public void overrideNativeWindowHandle(Object frame, long handle, Runnable closeWindow) {
 127         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 128         jlfOverrideNativeWindowHandle.invoke(lwFrame, handle, closeWindow);
 129     }
 130 
 131     public void notifyDisplayChanged(Object frame, double scaleX, double scaleY) {
 132         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 133         if (jlfNotifyDisplayChanged.isIntegerApi()) {
 134             jlfNotifyDisplayChanged.invoke(lwFrame, (int) scaleX);
 135         } else {
 136             jlfNotifyDisplayChanged.invoke(lwFrame, scaleX, scaleY);
 137         }
 138     }
 139 
 140     public void setHostBounds(Object frame, int windowX, int windowY, int windowW, int windowH) {
 141         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 142         jlfSetHostBounds.invoke(lwFrame, windowX, windowY, windowW, windowH);
 143     }
 144 
 145     public void setContent(Object frame, Object cnt) {
 146         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 147         LightweightContent content = (LightweightContent)cnt;
 148         lwFrame.setContent(content);
 149     }
 150 
 151     public void setVisible(Object frame, boolean visible) {
 152         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 153         lwFrame.setVisible(visible);
 154     }
 155 
 156     public void setBounds(Object frame, int frameX, int frameY, int frameW, int frameH) {
 157         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 158         lwFrame.setBounds(frameX, frameY, frameW, frameH);
 159     }
 160 
 161     public SwingNodeContent createSwingNodeContent(JComponent content, SwingNode node) {
 162         return new SwingNodeContent(content, node);
 163     }
 164 
 165     public DisposerRecord createSwingNodeDisposer(Object frame) {
 166         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 167         return new SwingNodeDisposer(lwFrame);
 168     }
 169 
 170     private static final class OptionalMethod<T> {
 171         private final Method method;
 172         private final boolean isIntegerAPI;
 173 
 174         OptionalMethod(Class<T> cls, String name, Class<?>... args) {
 175             Method m;
 176             try {
 177                 m = cls.getMethod(name, args);
 178             } catch (NoSuchMethodException ignored) {
 179                 // This means we're running with older JDK, simply skip the call
 180                 m = null;
 181             } catch (Throwable ex) {
 182                 throw new RuntimeException("Error when calling " + cls.getName() + ".getMethod('" + name + "').", ex);
 183             }
 184             method = m;
 185             isIntegerAPI = args != null && args.length > 0 &&
 186                                                         args[0] == Integer.TYPE;
 187         }
 188 
 189         public boolean isSupported() {
 190             return method != null;
 191         }
 192 
 193         public boolean isIntegerApi() {
 194             return isIntegerAPI;
 195         }
 196 
 197         public Object invoke(T object, Object... args) {
 198             if (method != null) {
 199                 try {
 200                     return method.invoke(object, args);
 201                 } catch (Throwable ex) {
 202                     throw new RuntimeException("Error when calling " + object.getClass().getName() + "." + method.getName() + "().", ex);
 203                 }
 204             } else {
 205                 return null;
 206             }
 207         }
 208     }
 209 
 210     /**
 211      * Calls LightweightFrameWrapper.setHostBounds.
 212      * Must be called on EDT only.
 213      */
 214     private static final OptionalMethod<JLightweightFrame> jlfSetHostBounds =
 215         new OptionalMethod<>(JLightweightFrame.class, "setHostBounds",
 216                 Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE);
 217 
 218     public void emulateActivation(Object frame, boolean activate) {
 219         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 220         lwFrame.emulateActivation(activate);
 221     }
 222 
 223     public void disposeFrame(Object frame) {
 224         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 225         lwFrame.dispose();
 226     }
 227 
 228     public void addWindowFocusListener(Object frame, WindowFocusListener l) {
 229         JLightweightFrame lwFrame = (JLightweightFrame)frame;
 230         lwFrame.addWindowFocusListener(l);
 231     }
 232 
 233     private static class SwingNodeDisposer implements DisposerRecord {
 234          JLightweightFrame lwFrame;
 235 
 236          SwingNodeDisposer(JLightweightFrame ref) {
 237              this.lwFrame = ref;
 238          }
 239          public void dispose() {
 240              if (lwFrame != null) {
 241                  lwFrame.dispose();
 242                  lwFrame = null;
 243              }
 244          }
 245     }
 246 
 247     private static class SwingNodeContent implements LightweightContent {
 248         private JComponent comp;
 249         private volatile FXDnD dnd;
 250         private WeakReference<SwingNode> swingNodeRef;
 251 
 252         SwingNodeContent(JComponent comp, SwingNode swingNode) {
 253             this.comp = comp;
 254             this.swingNodeRef = new WeakReference<SwingNode>(swingNode);
 255         }
 256         @Override
 257         public JComponent getComponent() {
 258             return comp;
 259         }
 260         @Override
 261         public void paintLock() {
 262             SwingNode swingNode = swingNodeRef.get();
 263             if (swingNode != null) {
 264                 SwingNodeHelper.getPaintLock(swingNode).lock();
 265             }
 266         }
 267         @Override
 268         public void paintUnlock() {
 269             SwingNode swingNode = swingNodeRef.get();
 270             if (swingNode != null) {
 271                 SwingNodeHelper.getPaintLock(swingNode).unlock();
 272             }
 273         }
 274 
 275         @Override
 276         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) {
 277             imageBufferReset(data, x, y, width, height, linestride, 1);
 278         }
 279         //@Override
 280         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) {
 281             SwingNode swingNode = swingNodeRef.get();
 282             if (swingNode != null) {
 283                 SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scale, scale);
 284             }
 285         }
 286         @Override
 287         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) {
 288             SwingNode swingNode = swingNodeRef.get();
 289             if (swingNode != null) {
 290                 SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scaleX, scaleY);
 291             }
 292         }
 293         @Override
 294         public void imageReshaped(int x, int y, int width, int height) {
 295             SwingNode swingNode = swingNodeRef.get();
 296             if (swingNode != null) {
 297                 SwingNodeHelper.setImageBounds(swingNode, x, y, width, height);
 298             }
 299         }
 300         @Override
 301         public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) {
 302             SwingNode swingNode = swingNodeRef.get();
 303             if (swingNode != null) {
 304                 SwingNodeHelper.repaintDirtyRegion(swingNode, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
 305             }
 306         }
 307         @Override
 308         public void focusGrabbed() {
 309             SwingNodeHelper.runOnFxThread(() -> {
 310                 // On X11 grab is limited to a single XDisplay connection,
 311                 // so we can't delegate it to another GUI toolkit.
 312                 if (PlatformUtil.isLinux()) return;
 313 
 314                 SwingNode swingNode = swingNodeRef.get();
 315                 if (swingNode != null) {
 316                     Scene scene = swingNode.getScene();
 317                     if (scene != null &&
 318                             scene.getWindow() != null &&
 319                             WindowHelper.getPeer(scene.getWindow()) != null) {
 320                         WindowHelper.getPeer(scene.getWindow()).grabFocus();
 321                         SwingNodeHelper.setGrabbed(swingNode, true);
 322                     }
 323                 }
 324             });
 325         }
 326         @Override
 327         public void focusUngrabbed() {
 328             SwingNodeHelper.runOnFxThread(() -> {
 329                 SwingNode swingNode = swingNodeRef.get();
 330                 if (swingNode != null) {
 331                     Scene scene = swingNode.getScene();
 332                     SwingNodeHelper.ungrabFocus(swingNode, false);
 333                 }
 334             });
 335         }
 336         @Override
 337         public void preferredSizeChanged(final int width, final int height) {
 338             SwingNodeHelper.runOnFxThread(() -> {
 339                 SwingNode swingNode = swingNodeRef.get();
 340                 if (swingNode != null) {
 341                     SwingNodeHelper.setSwingPrefWidth(swingNode, width);
 342                     SwingNodeHelper.setSwingPrefHeight(swingNode, height);
 343                     NodeHelper.notifyLayoutBoundsChanged(swingNode);
 344                 }
 345             });
 346         }
 347         @Override
 348         public void maximumSizeChanged(final int width, final int height) {
 349             SwingNodeHelper.runOnFxThread(() -> {
 350                 SwingNode swingNode = swingNodeRef.get();
 351                 if (swingNode != null) {
 352                     SwingNodeHelper.setSwingMaxWidth(swingNode, width);
 353                     SwingNodeHelper.setSwingMaxHeight(swingNode, height);
 354                     NodeHelper.notifyLayoutBoundsChanged(swingNode);
 355                 }
 356             });
 357         }
 358         @Override
 359         public void minimumSizeChanged(final int width, final int height) {
 360             SwingNodeHelper.runOnFxThread(() -> {
 361                 SwingNode swingNode = swingNodeRef.get();
 362                 if (swingNode != null) {
 363                     SwingNodeHelper.setSwingMinWidth(swingNode, width);
 364                     SwingNodeHelper.setSwingMinHeight(swingNode, height);
 365                     NodeHelper.notifyLayoutBoundsChanged(swingNode);
 366                 }
 367             });
 368         }
 369 
 370         //@Override
 371         public void setCursor(Cursor cursor) {
 372             SwingNodeHelper.runOnFxThread(() -> {
 373                 SwingNode swingNode = swingNodeRef.get();
 374                 if (swingNode != null) {
 375                     swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor));
 376                 }
 377             });
 378         }
 379 
 380         private void initDnD() {
 381             // This is a part of AWT API, so the method may be invoked on any thread
 382             synchronized (SwingNodeContent.this) {
 383                 if (this.dnd == null) {
 384                     SwingNode swingNode = swingNodeRef.get();
 385                     if (swingNode != null) {
 386                         this.dnd = new FXDnD(swingNode);
 387                     }
 388                 }
 389             }
 390         }
 391 
 392         //@Override
 393         public synchronized <T extends DragGestureRecognizer> T createDragGestureRecognizer(
 394                 Class<T> abstractRecognizerClass,
 395                 DragSource ds, Component c, int srcActions,
 396                 DragGestureListener dgl)
 397         {
 398             initDnD();
 399             return dnd.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl);
 400         }
 401 
 402         //@Override
 403         public DragSourceContextPeer createDragSourceContext(DragGestureEvent dge) throws InvalidDnDOperationException
 404         {
 405             initDnD();
 406             return (DragSourceContextPeer)dnd.createDragSourceContext(dge);
 407         }
 408 
 409         //@Override
 410         public void addDropTarget(DropTarget dt) {
 411             initDnD();
 412             dnd.addDropTarget(dt);
 413         }
 414 
 415         //@Override
 416         public void removeDropTarget(DropTarget dt) {
 417             initDnD();
 418             dnd.removeDropTarget(dt);
 419         }
 420     }
 421 
 422 }