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(Object frame, long handle, Runnable closeWindow) { 150 JLightweightFrame lwFrame = (JLightweightFrame)frame; 151 jlfOverrideNativeWindowHandle.invoke(lwFrame, handle, closeWindow); 152 } 153 154 public void notifyDisplayChanged(Object frame, double scaleX, double scaleY) { 155 JLightweightFrame lwFrame = (JLightweightFrame)frame; 156 if (jlfNotifyDisplayChanged.isIntegerApi()) { 157 jlfNotifyDisplayChanged.invoke(lwFrame, (int) scaleX); 158 } else { 159 jlfNotifyDisplayChanged.invoke(lwFrame, scaleX, scaleY); 160 } 161 } 162 163 public void setHostBounds(Object frame, int windowX, int windowY, int windowW, int windowH) { 164 JLightweightFrame lwFrame = (JLightweightFrame)frame; 165 jlfSetHostBounds.invoke(lwFrame, windowX, windowY, windowW, windowH); 166 } 167 168 public void setContent(Object frame, Object cnt) { 169 JLightweightFrame lwFrame = (JLightweightFrame)frame; 170 LightweightContent content = (LightweightContent)cnt; 171 lwFrame.setContent(content); 172 } 173 174 public void setVisible(Object frame, boolean visible) { 175 JLightweightFrame lwFrame = (JLightweightFrame)frame; 176 lwFrame.setVisible(visible); 177 } 178 179 public void setBounds(Object frame, int frameX, int frameY, int frameW, int frameH) { 180 JLightweightFrame lwFrame = (JLightweightFrame)frame; 181 lwFrame.setBounds(frameX, frameY, frameW, frameH); 182 } 183 184 public SwingNodeContent createSwingNodeContent(JComponent content, SwingNode node) { 185 return new SwingNodeContent(content, node); 186 } 187 188 public DisposerRecord createSwingNodeDisposer(Object frame) { 189 JLightweightFrame lwFrame = (JLightweightFrame)frame; 190 return new SwingNodeDisposer(lwFrame); 191 } 192 193 private static final class OptionalMethod<T> { 194 private final Method method; 195 private final boolean isIntegerAPI; 196 197 OptionalMethod(Class<T> cls, String name, Class<?>... args) { 198 Method m; 199 try { 200 m = cls.getMethod(name, args); 201 } catch (NoSuchMethodException ignored) { 202 // This means we're running with older JDK, simply skip the call 203 m = null; 204 } catch (Throwable ex) { 205 throw new RuntimeException("Error when calling " + cls.getName() + ".getMethod('" + name + "').", ex); 206 } 207 method = m; 208 isIntegerAPI = args != null && args.length > 0 && 209 args[0] == Integer.TYPE; 210 } 211 212 public boolean isSupported() { 213 return method != null; 214 } 215 216 public boolean isIntegerApi() { 217 return isIntegerAPI; 218 } 219 220 public Object invoke(T object, Object... args) { 221 if (method != null) { 222 try { 223 return method.invoke(object, args); 224 } catch (Throwable ex) { 225 throw new RuntimeException("Error when calling " + object.getClass().getName() + "." + method.getName() + "().", ex); 226 } 227 } else { 228 return null; 229 } 230 } 231 } 232 233 /** 234 * Calls LightweightFrameWrapper.setHostBounds. 235 * Must be called on EDT only. 236 */ 237 private static final OptionalMethod<JLightweightFrame> jlfSetHostBounds = 238 new OptionalMethod<>(JLightweightFrame.class, "setHostBounds", 239 Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE); 240 241 public void emulateActivation(Object frame, boolean activate) { 242 JLightweightFrame lwFrame = (JLightweightFrame)frame; 243 lwFrame.emulateActivation(activate); 244 } 245 246 public void disposeFrame(Object frame) { 247 JLightweightFrame lwFrame = (JLightweightFrame)frame; 248 lwFrame.dispose(); 249 } 250 251 public void addWindowFocusListener(Object frame, WindowFocusListener l) { 252 JLightweightFrame lwFrame = (JLightweightFrame)frame; 253 lwFrame.addWindowFocusListener(l); 254 } 255 256 private static class SwingNodeDisposer implements DisposerRecord { 257 JLightweightFrame lwFrame; 258 259 SwingNodeDisposer(JLightweightFrame ref) { 260 this.lwFrame = ref; 261 } 262 public void dispose() { 263 if (lwFrame != null) { 264 lwFrame.dispose(); 265 lwFrame = null; 266 } 267 } 268 } 269 270 private static class SwingNodeContent implements LightweightContent { 271 private JComponent comp; 272 private volatile FXDnD dnd; 273 private WeakReference<SwingNode> swingNodeRef; 274 275 SwingNodeContent(JComponent comp, SwingNode swingNode) { 276 this.comp = comp; 277 this.swingNodeRef = new WeakReference<SwingNode>(swingNode); 278 } 279 @Override 280 public JComponent getComponent() { 281 return comp; 282 } 283 @Override 284 public void paintLock() { 285 SwingNode swingNode = swingNodeRef.get(); 286 if (swingNode != null) { 287 SwingNodeHelper.getPaintLock(swingNode).lock(); 288 } 289 } 290 @Override 291 public void paintUnlock() { 292 SwingNode swingNode = swingNodeRef.get(); 293 if (swingNode != null) { 294 SwingNodeHelper.getPaintLock(swingNode).unlock(); 295 } 296 } 297 298 @Override 299 public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) { 300 imageBufferReset(data, x, y, width, height, linestride, 1); 301 } 302 //@Override 303 public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) { 304 SwingNode swingNode = swingNodeRef.get(); 305 if (swingNode != null) { 306 SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scale, scale); 307 } 308 } 309 @Override 310 public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) { 311 SwingNode swingNode = swingNodeRef.get(); 312 if (swingNode != null) { 313 SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scaleX, scaleY); 314 } 315 } 316 @Override 317 public void imageReshaped(int x, int y, int width, int height) { 318 SwingNode swingNode = swingNodeRef.get(); 319 if (swingNode != null) { 320 SwingNodeHelper.setImageBounds(swingNode, x, y, width, height); 321 } 322 } 323 @Override 324 public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) { 325 SwingNode swingNode = swingNodeRef.get(); 326 if (swingNode != null) { 327 SwingNodeHelper.repaintDirtyRegion(swingNode, dirtyX, dirtyY, dirtyWidth, dirtyHeight); 328 } 329 } 330 @Override 331 public void focusGrabbed() { 332 SwingNodeHelper.runOnFxThread(() -> { 333 // On X11 grab is limited to a single XDisplay connection, 334 // so we can't delegate it to another GUI toolkit. 335 if (PlatformUtil.isLinux()) return; 336 337 SwingNode swingNode = swingNodeRef.get(); 338 if (swingNode != null) { 339 Scene scene = swingNode.getScene(); 340 if (scene != null && 341 scene.getWindow() != null && 342 WindowHelper.getPeer(scene.getWindow()) != null) { 343 WindowHelper.getPeer(scene.getWindow()).grabFocus(); 344 SwingNodeHelper.setGrabbed(swingNode, true); 345 } 346 } 347 }); 348 } 349 @Override 350 public void focusUngrabbed() { 351 SwingNodeHelper.runOnFxThread(() -> { 352 SwingNode swingNode = swingNodeRef.get(); 353 if (swingNode != null) { 354 Scene scene = swingNode.getScene(); 355 SwingNodeHelper.ungrabFocus(swingNode, false); 356 } 357 }); 358 } 359 @Override 360 public void preferredSizeChanged(final int width, final int height) { 361 SwingNodeHelper.runOnFxThread(() -> { 362 SwingNode swingNode = swingNodeRef.get(); 363 if (swingNode != null) { 364 SwingNodeHelper.setSwingPrefWidth(swingNode, width); 365 SwingNodeHelper.setSwingPrefHeight(swingNode, height); 366 NodeHelper.notifyLayoutBoundsChanged(swingNode); 367 } 368 }); 369 } 370 @Override 371 public void maximumSizeChanged(final int width, final int height) { 372 SwingNodeHelper.runOnFxThread(() -> { 373 SwingNode swingNode = swingNodeRef.get(); 374 if (swingNode != null) { 375 SwingNodeHelper.setSwingMaxWidth(swingNode, width); 376 SwingNodeHelper.setSwingMaxHeight(swingNode, height); 377 NodeHelper.notifyLayoutBoundsChanged(swingNode); 378 } 379 }); 380 } 381 @Override 382 public void minimumSizeChanged(final int width, final int height) { 383 SwingNodeHelper.runOnFxThread(() -> { 384 SwingNode swingNode = swingNodeRef.get(); 385 if (swingNode != null) { 386 SwingNodeHelper.setSwingMinWidth(swingNode, width); 387 SwingNodeHelper.setSwingMinHeight(swingNode, height); 388 NodeHelper.notifyLayoutBoundsChanged(swingNode); 389 } 390 }); 391 } 392 393 //@Override 394 public void setCursor(Cursor cursor) { 395 SwingNodeHelper.runOnFxThread(() -> { 396 SwingNode swingNode = swingNodeRef.get(); 397 if (swingNode != null) { 398 swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor)); 399 } 400 }); 401 } 402 403 private void initDnD() { 404 // This is a part of AWT API, so the method may be invoked on any thread 405 synchronized (SwingNodeContent.this) { 406 if (this.dnd == null) { 407 SwingNode swingNode = swingNodeRef.get(); 408 if (swingNode != null) { 409 this.dnd = new FXDnD(swingNode); 410 } 411 } 412 } 413 } 414 415 //@Override 416 public synchronized <T extends DragGestureRecognizer> T createDragGestureRecognizer( 417 Class<T> abstractRecognizerClass, 418 DragSource ds, Component c, int srcActions, 419 DragGestureListener dgl) 420 { 421 initDnD(); 422 return dnd.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl); 423 } 424 425 //@Override 426 public DragSourceContextPeer createDragSourceContext(DragGestureEvent dge) throws InvalidDnDOperationException 427 { 428 initDnD(); 429 return (DragSourceContextPeer)dnd.createDragSourceContext(dge); 430 } 431 432 //@Override 433 public void addDropTarget(DropTarget dt) { 434 initDnD(); 435 dnd.addDropTarget(dt); 436 } 437 438 //@Override 439 public void removeDropTarget(DropTarget dt) { 440 initDnD(); 441 dnd.removeDropTarget(dt); 442 } 443 } 444 445 }