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 }