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