1 /* 2 * Copyright (c) 2010, 2015, 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.tk; 27 28 import javafx.application.ConditionalFeature; 29 import javafx.beans.property.ReadOnlyObjectProperty; 30 import javafx.geometry.Dimension2D; 31 import javafx.scene.Scene; 32 import javafx.scene.effect.BlurType; 33 import javafx.scene.image.Image; 34 import javafx.scene.image.PixelFormat; 35 import javafx.scene.image.WritableImage; 36 import javafx.scene.input.Dragboard; 37 import javafx.scene.input.InputMethodRequests; 38 import javafx.scene.input.KeyCode; 39 import javafx.scene.input.KeyEvent; 40 import javafx.scene.input.TransferMode; 41 import javafx.scene.paint.Color; 42 import javafx.scene.paint.ImagePattern; 43 import javafx.scene.paint.LinearGradient; 44 import javafx.scene.paint.Paint; 45 import javafx.scene.paint.RadialGradient; 46 import javafx.scene.paint.Stop; 47 import javafx.scene.shape.PathElement; 48 import javafx.scene.shape.SVGPath; 49 import javafx.scene.shape.StrokeLineCap; 50 import javafx.scene.shape.StrokeLineJoin; 51 import javafx.scene.shape.StrokeType; 52 import javafx.stage.FileChooser.ExtensionFilter; 53 import javafx.stage.Modality; 54 import javafx.stage.Stage; 55 import javafx.stage.StageStyle; 56 import javafx.stage.Window; 57 import java.io.File; 58 import java.io.IOException; 59 import java.io.InputStream; 60 import java.nio.ByteBuffer; 61 import java.security.AccessControlContext; 62 import java.security.AccessController; 63 import java.security.PrivilegedAction; 64 import java.util.ArrayList; 65 import java.util.HashSet; 66 import java.util.Iterator; 67 import java.util.List; 68 import java.util.Map; 69 import java.util.Set; 70 import java.util.WeakHashMap; 71 import java.util.concurrent.CountDownLatch; 72 import java.util.concurrent.Future; 73 import com.sun.glass.ui.CommonDialogs.FileChooserResult; 74 import com.sun.javafx.PlatformUtil; 75 import com.sun.javafx.beans.event.AbstractNotifyListener; 76 import com.sun.javafx.embed.HostInterface; 77 import com.sun.javafx.geom.Path2D; 78 import com.sun.javafx.geom.transform.BaseTransform; 79 import com.sun.javafx.jmx.HighlightRegion; 80 import com.sun.javafx.perf.PerformanceTracker; 81 import com.sun.javafx.runtime.VersionInfo; 82 import com.sun.javafx.runtime.async.AsyncOperation; 83 import com.sun.javafx.runtime.async.AsyncOperationListener; 84 import com.sun.javafx.scene.SceneHelper; 85 import com.sun.javafx.scene.text.HitInfo; 86 import com.sun.javafx.scene.text.TextLayoutFactory; 87 import com.sun.javafx.sg.prism.NGCamera; 88 import com.sun.javafx.sg.prism.NGLightBase; 89 import com.sun.javafx.sg.prism.NGNode; 90 import com.sun.scenario.DelayedRunnable; 91 import com.sun.scenario.animation.AbstractMasterTimer; 92 import com.sun.scenario.effect.AbstractShadow.ShadowMode; 93 import com.sun.scenario.effect.Color4f; 94 import com.sun.scenario.effect.FilterContext; 95 import com.sun.scenario.effect.Filterable; 96 97 98 public abstract class Toolkit { 99 private static String tk; 100 private static Toolkit TOOLKIT; 101 private static Thread fxUserThread = null; 102 103 private static final String QUANTUM_TOOLKIT = "com.sun.javafx.tk.quantum.QuantumToolkit"; 104 private static final String DEFAULT_TOOLKIT = QUANTUM_TOOLKIT; 105 106 private static final Map gradientMap = new WeakHashMap(); 107 108 private static String lookupToolkitClass(String name) { 109 if ("prism".equalsIgnoreCase(name)) { 110 return QUANTUM_TOOLKIT; 111 } else if ("quantum".equalsIgnoreCase(name)) { 112 return QUANTUM_TOOLKIT; 113 } 114 return name; 115 } 116 117 private static String getDefaultToolkit() { 118 if (PlatformUtil.isWindows()) { 119 return DEFAULT_TOOLKIT; 120 } else if (PlatformUtil.isMac()) { 121 return DEFAULT_TOOLKIT; 122 } else if (PlatformUtil.isLinux()) { 123 return DEFAULT_TOOLKIT; 124 } else if (PlatformUtil.isIOS()) { 125 return DEFAULT_TOOLKIT; 126 } else if (PlatformUtil.isAndroid()) { 127 return DEFAULT_TOOLKIT; 128 } 129 130 throw new UnsupportedOperationException(System.getProperty("os.name") + " is not supported"); 131 } 132 133 public static synchronized Toolkit getToolkit() { 134 if (TOOLKIT != null) { 135 return TOOLKIT; 136 } 137 138 final boolean verbose = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.verbose")); 139 AccessController.doPrivileged((PrivilegedAction<Object>) () -> { 140 // Get the javafx.version and javafx.runtime.version from a preconstructed 141 // java class, VersionInfo, created at build time. 142 VersionInfo.setupSystemProperties(); 143 return null; 144 }); 145 146 boolean userSpecifiedToolkit = true; 147 148 // Check a system property to see if there is a specific toolkit to use. 149 // This is not a doPriviledged check so that applets cannot use this. 150 String forcedToolkit = null; 151 try { 152 forcedToolkit = System.getProperty("javafx.toolkit"); 153 } catch (SecurityException ex) {} 154 155 if (forcedToolkit == null) { 156 forcedToolkit = tk; 157 } 158 if (forcedToolkit == null) { 159 userSpecifiedToolkit = false; 160 forcedToolkit = getDefaultToolkit(); 161 } 162 163 if (forcedToolkit.indexOf('.') == -1) { 164 // Turn a short name into a fully qualified classname 165 forcedToolkit = lookupToolkitClass(forcedToolkit); 166 } 167 168 boolean printToolkit = verbose 169 || (userSpecifiedToolkit && !forcedToolkit.endsWith("StubToolkit")); 170 171 try { 172 TOOLKIT = (Toolkit) Class.forName(forcedToolkit).newInstance(); 173 if (TOOLKIT.init()) { 174 if (printToolkit) { 175 System.err.println("JavaFX: using " + forcedToolkit); 176 } 177 return TOOLKIT; 178 } 179 TOOLKIT = null; 180 } catch (Exception any) { 181 TOOLKIT = null; 182 any.printStackTrace(); 183 } 184 185 throw new RuntimeException("No toolkit found"); 186 } 187 188 protected static Thread getFxUserThread() { 189 return fxUserThread; 190 } 191 192 protected static void setFxUserThread(Thread t) { 193 if (fxUserThread != null) { 194 throw new IllegalStateException("Error: FX User Thread already initialized"); 195 } 196 197 fxUserThread = t; 198 } 199 200 public void checkFxUserThread() { 201 // Throw exception if not on FX user thread 202 if (!isFxUserThread()) { 203 throw new IllegalStateException("Not on FX application thread; currentThread = " 204 + Thread.currentThread().getName()); 205 } 206 } 207 208 // Toolkit can override this if needed 209 public boolean isFxUserThread() { 210 return Thread.currentThread() == fxUserThread; 211 } 212 213 protected Toolkit() { 214 } 215 216 public abstract boolean init(); 217 218 /** 219 * Enter a nested event loop and block until the corresponding 220 * exitNestedEventLoop call is made. 221 * The key passed into this method is used to 222 * uniquely identify the matched enter/exit pair. This method creates a 223 * new nested event loop and blocks until the corresponding 224 * exitNestedEventLoop method is called with the same key. 225 * The return value of this method will be the {@code rval} 226 * object supplied to the exitNestedEventLoop method call that unblocks it. 227 * 228 * @param key the Object that identifies the nested event loop, which 229 * must not be null 230 * 231 * @throws IllegalArgumentException if the specified key is associated 232 * with a nested event loop that has not yet returned 233 * 234 * @throws NullPointerException if the key is null 235 * 236 * @throws IllegalStateException if this method is called on a thread 237 * other than the FX Application thread 238 * 239 * @return the value passed into the corresponding call to exitEventLoop 240 */ 241 public abstract Object enterNestedEventLoop(Object key); 242 243 /** 244 * Exit a nested event loop and unblock the caller of the 245 * corresponding enterNestedEventLoop. 246 * The key passed into this method is used to 247 * uniquely identify the matched enter/exit pair. This method causes the 248 * nested event loop that was previously created with the key to exit and 249 * return control to the caller. If the specified nested event loop is not 250 * the inner-most loop then it will not return until all other inner loops 251 * also exit. 252 * 253 * @param key the Object that identifies the nested event loop, which 254 * must not be null 255 * 256 * @param rval an Object that is returned to the caller of the 257 * corresponding enterNestedEventLoop. This may be null. 258 * 259 * @throws IllegalArgumentException if the specified key is not associated 260 * with an active nested event loop 261 * 262 * @throws NullPointerException if the key is null 263 * 264 * @throws IllegalStateException if this method is called on a thread 265 * other than the FX Application thread 266 */ 267 public abstract void exitNestedEventLoop(Object key, Object rval); 268 269 public abstract boolean isNestedLoopRunning(); 270 271 public abstract TKStage createTKStage(Window peerWindow, boolean securityDialog, StageStyle stageStyle, boolean primary, Modality modality, TKStage owner, boolean rtl, AccessControlContext acc); 272 273 public abstract TKStage createTKPopupStage(Window peerWindow, StageStyle popupStyle, TKStage owner, AccessControlContext acc); 274 public abstract TKStage createTKEmbeddedStage(HostInterface host, AccessControlContext acc); 275 276 /** 277 * Creates an AppletWindow using the provided window pointer as the parent 278 * window. 279 * 280 * @param parent the native parent which will contain the primary stage 281 * window(s). Used on Windows/Linux platforms. 282 * 283 * @param serverName the name of CARemoteLayerServer which 284 * will be used to register native layer. Used on Mac platform. 285 */ 286 public abstract AppletWindow createAppletWindow(long parent, String serverName); 287 288 /** 289 * Perform cleanup in preparation for applet termination, including closing 290 * the applet window. 291 */ 292 public abstract void closeAppletWindow(); 293 294 private final Map<TKPulseListener,AccessControlContext> stagePulseListeners = 295 new WeakHashMap<TKPulseListener,AccessControlContext>(); 296 private final Map<TKPulseListener,AccessControlContext> scenePulseListeners = 297 new WeakHashMap<TKPulseListener,AccessControlContext>(); 298 private final Map<TKPulseListener,AccessControlContext> postScenePulseListeners = 299 new WeakHashMap<TKPulseListener,AccessControlContext>(); 300 private final Map<TKListener,AccessControlContext> toolkitListeners = 301 new WeakHashMap<TKListener,AccessControlContext>(); 302 303 // The set of shutdown hooks is strongly held to avoid premature GC. 304 private final Set<Runnable> shutdownHooks = new HashSet<Runnable>(); 305 306 private void runPulse(final TKPulseListener listener, 307 final AccessControlContext acc) { 308 309 if (acc == null) { 310 throw new IllegalStateException("Invalid AccessControlContext"); 311 } 312 313 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 314 listener.pulse(); 315 return null; 316 }, acc); 317 } 318 319 public void firePulse() { 320 // Stages need to be notified of pulses before scenes so the Stage can resized 321 // and those changes propogated to scene before it gets its pulse to update 322 323 // Copy of listener map 324 final Map<TKPulseListener,AccessControlContext> stagePulseList = 325 new WeakHashMap<TKPulseListener,AccessControlContext>(); 326 final Map<TKPulseListener,AccessControlContext> scenePulseList = 327 new WeakHashMap<TKPulseListener,AccessControlContext>(); 328 final Map<TKPulseListener,AccessControlContext> postScenePulseList = 329 new WeakHashMap<TKPulseListener,AccessControlContext>(); 330 331 synchronized (this) { 332 stagePulseList.putAll(stagePulseListeners); 333 scenePulseList.putAll(scenePulseListeners); 334 postScenePulseList.putAll(postScenePulseListeners); 335 } 336 for (Map.Entry<TKPulseListener,AccessControlContext> entry : stagePulseList.entrySet()) { 337 runPulse(entry.getKey(), entry.getValue()); 338 } 339 for (Map.Entry<TKPulseListener,AccessControlContext> entry : scenePulseList.entrySet()) { 340 runPulse(entry.getKey(), entry.getValue()); 341 } 342 for (Map.Entry<TKPulseListener,AccessControlContext> entry : postScenePulseList.entrySet()) { 343 runPulse(entry.getKey(), entry.getValue()); 344 } 345 346 if (lastTkPulseListener != null) { 347 runPulse(lastTkPulseListener, lastTkPulseAcc); 348 } 349 } 350 public void addStageTkPulseListener(TKPulseListener listener) { 351 if (listener == null) { 352 return; 353 } 354 synchronized (this) { 355 AccessControlContext acc = AccessController.getContext(); 356 stagePulseListeners.put(listener, acc); 357 } 358 } 359 public void removeStageTkPulseListener(TKPulseListener listener) { 360 synchronized (this) { 361 stagePulseListeners.remove(listener); 362 } 363 } 364 public void addSceneTkPulseListener(TKPulseListener listener) { 365 if (listener == null) { 366 return; 367 } 368 synchronized (this) { 369 AccessControlContext acc = AccessController.getContext(); 370 scenePulseListeners.put(listener, acc); 371 } 372 } 373 public void removeSceneTkPulseListener(TKPulseListener listener) { 374 synchronized (this) { 375 scenePulseListeners.remove(listener); 376 } 377 } 378 public void addPostSceneTkPulseListener(TKPulseListener listener) { 379 if (listener == null) { 380 return; 381 } 382 synchronized (this) { 383 AccessControlContext acc = AccessController.getContext(); 384 postScenePulseListeners.put(listener, acc); 385 } 386 } 387 public void removePostSceneTkPulseListener(TKPulseListener listener) { 388 synchronized (this) { 389 postScenePulseListeners.remove(listener); 390 } 391 } 392 393 public void addTkListener(TKListener listener) { 394 if (listener == null) { 395 return; 396 } 397 AccessControlContext acc = AccessController.getContext(); 398 toolkitListeners.put(listener, acc); 399 } 400 401 public void removeTkListener(TKListener listener) { 402 toolkitListeners.remove(listener); 403 } 404 405 private TKPulseListener lastTkPulseListener = null; 406 private AccessControlContext lastTkPulseAcc = null; 407 public void setLastTkPulseListener(TKPulseListener listener) { 408 lastTkPulseAcc = AccessController.getContext(); 409 lastTkPulseListener = listener; 410 } 411 412 public void addShutdownHook(Runnable hook) { 413 if (hook == null) { 414 return; 415 } 416 synchronized (shutdownHooks) { 417 shutdownHooks.add(hook); 418 } 419 } 420 421 public void removeShutdownHook(Runnable hook) { 422 synchronized (shutdownHooks) { 423 shutdownHooks.remove(hook); 424 } 425 } 426 427 protected void notifyShutdownHooks() { 428 List<Runnable> hooks; 429 synchronized (shutdownHooks) { 430 hooks = new ArrayList<Runnable>(shutdownHooks); 431 shutdownHooks.clear(); 432 } 433 434 for (Runnable hook : hooks) { 435 hook.run(); 436 } 437 } 438 439 public void notifyWindowListeners(final List<TKStage> windows) { 440 for (Map.Entry<TKListener,AccessControlContext> entry : toolkitListeners.entrySet()) { 441 final TKListener listener = entry.getKey(); 442 final AccessControlContext acc = entry.getValue(); 443 if (acc == null) { 444 throw new IllegalStateException("Invalid AccessControlContext"); 445 } 446 447 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 448 listener.changedTopLevelWindows(windows); 449 return null; 450 }, acc); 451 } 452 } 453 454 public void notifyLastNestedLoopExited() { 455 for (TKListener listener: toolkitListeners.keySet()) { 456 listener.exitedLastNestedLoop(); 457 } 458 } 459 460 // notify the pulse timer code that we need the next pulse to happen 461 // this flag is cleared each cycle so subsequent pulses must be requested 462 public abstract void requestNextPulse(); 463 464 public abstract Future addRenderJob(RenderJob rj); 465 466 public InputStream getInputStream(String url, Class base) 467 throws IOException { 468 return (url.startsWith("http:") 469 || url.startsWith("https:") 470 || url.startsWith("file:") 471 || url.startsWith("jar:")) 472 ? new java.net.URL(url).openStream() 473 : base.getResource(url).openStream(); 474 } 475 476 public abstract ImageLoader loadImage(String url, 477 int width, int height, 478 boolean preserveRatio, 479 boolean smooth); 480 public abstract ImageLoader loadImage(InputStream stream, 481 int width, int height, 482 boolean preserveRatio, 483 boolean smooth); 484 public abstract AsyncOperation loadImageAsync( 485 AsyncOperationListener<? extends ImageLoader> listener, 486 String url, 487 int width, int height, 488 boolean preserveRatio, 489 boolean smooth); 490 491 /* 492 * The loadPlatformImage method supports the following image types: 493 * - an object returned by the renderToImage method 494 * - an instance of com.sun.prism.Image (in case of prism) 495 * - an instance of an external image object, which can be a BufferedImage 496 * If JavaFX Image had one more constructor Image(ImageLoader), 497 * we could introduce a different method for external image loading support. 498 */ 499 500 public abstract ImageLoader loadPlatformImage(Object platformImage); 501 502 public abstract PlatformImage createPlatformImage(int w, int h); 503 504 // Indicates the default state of smooth for ImageView and MediaView 505 // Subclasses may override this to provide a platform-specific default 506 public boolean getDefaultImageSmooth() { return true; } 507 508 public abstract void startup(Runnable runnable); 509 public abstract void defer(Runnable runnable); 510 public void exit() { 511 fxUserThread = null; 512 } 513 514 public abstract Map<Object, Object> getContextMap(); 515 public abstract int getRefreshRate(); 516 public abstract void setAnimationRunnable(DelayedRunnable animationRunnable); 517 public abstract PerformanceTracker getPerformanceTracker(); 518 public abstract PerformanceTracker createPerformanceTracker(); 519 520 //to be used for testing only 521 public abstract void waitFor(Task t); 522 523 private Object checkSingleColor(List<Stop> stops) { 524 if (stops.size() == 2) { 525 Color c = stops.get(0).getColor(); 526 if (c.equals(stops.get(1).getColor())) { 527 return Toolkit.getPaintAccessor().getPlatformPaint(c); 528 } 529 } 530 return null; 531 } 532 533 private Object getPaint(LinearGradient paint) { 534 Object p = gradientMap.get(paint); 535 if (p != null) { 536 return p; 537 } 538 p = checkSingleColor(paint.getStops()); 539 if (p == null) { 540 p = createLinearGradientPaint(paint); 541 } 542 gradientMap.put(paint, p); 543 return p; 544 } 545 546 private Object getPaint(RadialGradient paint) { 547 Object p = gradientMap.get(paint); 548 if (p != null) { 549 return p; 550 } 551 p = checkSingleColor(paint.getStops()); 552 if (p == null) { 553 p = createRadialGradientPaint(paint); 554 } 555 gradientMap.put(paint, p); 556 return p; 557 } 558 559 public Object getPaint(Paint paint) { 560 if (paint instanceof Color) { 561 return createColorPaint((Color) paint); 562 } 563 564 if (paint instanceof LinearGradient) { 565 return getPaint((LinearGradient) paint); 566 } 567 568 if (paint instanceof RadialGradient) { 569 return getPaint((RadialGradient) paint); 570 } 571 572 if (paint instanceof ImagePattern) { 573 return createImagePatternPaint((ImagePattern) paint); 574 } 575 576 return null; 577 } 578 579 protected static final double clampStopOffset(double offset) { 580 return (offset > 1.0) ? 1.0 : 581 (offset < 0.0) ? 0.0 : offset; 582 } 583 584 protected abstract Object createColorPaint(Color paint); 585 protected abstract Object createLinearGradientPaint(LinearGradient paint); 586 protected abstract Object createRadialGradientPaint(RadialGradient paint); 587 protected abstract Object createImagePatternPaint(ImagePattern paint); 588 589 public abstract void 590 accumulateStrokeBounds(com.sun.javafx.geom.Shape shape, 591 float bbox[], 592 StrokeType type, 593 double strokewidth, 594 StrokeLineCap cap, 595 StrokeLineJoin join, 596 float miterLimit, 597 BaseTransform tx); 598 599 public abstract boolean 600 strokeContains(com.sun.javafx.geom.Shape shape, 601 double x, double y, 602 StrokeType type, 603 double strokewidth, 604 StrokeLineCap cap, 605 StrokeLineJoin join, 606 float miterLimit); 607 608 public abstract com.sun.javafx.geom.Shape 609 createStrokedShape(com.sun.javafx.geom.Shape shape, 610 StrokeType type, 611 double strokewidth, 612 StrokeLineCap cap, 613 StrokeLineJoin join, 614 float miterLimit, 615 float[] dashArray, 616 float dashOffset); 617 618 public abstract int getKeyCodeForChar(String character); 619 public abstract Dimension2D getBestCursorSize(int preferredWidth, int preferredHeight); 620 public abstract int getMaximumCursorColors(); 621 public abstract PathElement[] convertShapeToFXPath(Object shape); 622 public abstract HitInfo convertHitInfoToFX(Object hit); 623 624 public abstract Filterable toFilterable(Image img); 625 public abstract FilterContext getFilterContext(Object config); 626 627 public abstract boolean isForwardTraversalKey(KeyEvent e); 628 public abstract boolean isBackwardTraversalKey(KeyEvent e); 629 630 public abstract AbstractMasterTimer getMasterTimer(); 631 632 public abstract FontLoader getFontLoader(); 633 public abstract TextLayoutFactory getTextLayoutFactory(); 634 635 public abstract Object createSVGPathObject(SVGPath svgpath); 636 public abstract Path2D createSVGPath2D(SVGPath svgpath); 637 638 /** 639 * Tests whether the pixel on the given coordinates in the given image 640 * is non-empty (not fully transparent). Return value is not defined 641 * for pixels out of the image bounds. 642 */ 643 public abstract boolean imageContains(Object image, float x, float y); 644 645 public abstract TKClipboard getSystemClipboard(); 646 647 public TKClipboard createLocalClipboard() { 648 return new LocalClipboard(); 649 } 650 651 public abstract TKSystemMenu getSystemMenu(); 652 653 public abstract TKClipboard getNamedClipboard(String name); 654 655 public boolean isSupported(ConditionalFeature feature) { return false; } 656 657 public boolean isMSAASupported() { return false; } 658 659 public abstract ScreenConfigurationAccessor setScreenConfigurationListener(TKScreenConfigurationListener listener); 660 661 public abstract Object getPrimaryScreen(); 662 663 public abstract List<?> getScreens(); 664 665 public abstract ScreenConfigurationAccessor getScreenConfigurationAccessor(); 666 667 public abstract void registerDragGestureListener(TKScene s, Set<TransferMode> tm, TKDragGestureListener l); 668 669 /** 670 * This function is called when a drag originates within a JavaFX application. 671 * This means that drags that originate in other applications / from the OS 672 * do not call this function. 673 * The argument o represents an object used to identify a scene on which 674 * the drag has started. 675 */ 676 public abstract void startDrag(TKScene scene, Set<TransferMode> tm, TKDragSourceListener l, Dragboard dragboard); 677 678 // template function which can be implemented by toolkit impls such that they 679 // can be informed when a drag and drop operation has completed. This allows 680 // for any cleanup that may need to be done. 681 public void stopDrag(Dragboard dragboard) { 682 // no-op 683 } 684 685 public abstract void enableDrop(TKScene s, TKDropTargetListener l); 686 687 public interface Task { 688 boolean isFinished(); 689 } 690 691 public Color4f toColor4f(Color color) { 692 return new Color4f((float)color.getRed(), (float)color.getGreen(), (float)color.getBlue(), (float)color.getOpacity()); 693 } 694 695 696 public ShadowMode toShadowMode(BlurType blurType) { 697 switch (blurType) { 698 case ONE_PASS_BOX: 699 return ShadowMode.ONE_PASS_BOX; 700 case TWO_PASS_BOX: 701 return ShadowMode.TWO_PASS_BOX; 702 case THREE_PASS_BOX: 703 return ShadowMode.THREE_PASS_BOX; 704 default: 705 return ShadowMode.GAUSSIAN; 706 } 707 } 708 709 public abstract void installInputMethodRequests(TKScene scene, InputMethodRequests requests); 710 711 /* 712 * ImageRenderingContext holds the many parameters passed to 713 * the renderToImage method. 714 * The use of the parameters is specified by the renderToImage 715 * method. 716 * @see #renderToImage 717 */ 718 public static class ImageRenderingContext { 719 // Node to be rendered 720 public NGNode root; 721 722 // Viewport for rendering 723 public int x; 724 public int y; 725 public int width; 726 public int height; 727 728 // Initial transform for root node 729 public BaseTransform transform; 730 731 // Rendering parameters either from Scene or SnapShotParams 732 public boolean depthBuffer; 733 public Object platformPaint; 734 public NGCamera camera; 735 public NGLightBase[] lights; 736 737 // PlatformImage into which to render or null 738 public Object platformImage; 739 } 740 741 /* 742 * This method renders a PG-graph to a platform image object. 743 * The returned object can be turned into a useable 744 * scene graph image using the appropriate factor of the 745 * Image class. 746 * The scale specified in the params is used to scale the 747 * entire rendering before any transforms in the nodes are 748 * applied. 749 * The width and height specified in the params represent 750 * the user space dimensions to be rendered. The returned 751 * image will be large enough to hold these dimensions 752 * scaled by the scale parameter. 753 * The depthBuffer specified in the params is used to determine 754 * with or without depthBuffer rendering should be performed. 755 * The root node is the root of a tree of toolkit-specific 756 * scene graph peer nodes to be rendered and should have 757 * been previously created by this toolkit. 758 * The platformPaint specified in the params must be 759 * generated by the appropriate Toolkit.createPaint method 760 * and is used to fill the background of the image before 761 * rendering the scene graph. 762 * The platformImage specified in the params may be non-null 763 * and should be a previous return value from this method. 764 * If it is non-null then it may be reused as the return value 765 * of this method if it is still valid and large enough to 766 * hold the requested size. 767 * 768 * @param context a ImageRenderingContext instance specifying 769 * the various rendering parameters 770 * @return a platform specific image object 771 * @see javafx.scene.image.Image#impl_fromPlatformImage 772 */ 773 774 public abstract Object renderToImage(ImageRenderingContext context); 775 776 /** 777 * Returns the key code for the key which is commonly used on the 778 * corresponding platform as a modifier key in shortcuts. For example 779 * it is {@code KeyCode.CONTROL} on Windows (Ctrl + C, Ctrl + V ...) and 780 * {@code KeyCode.META} on MacOS (Cmd + C, Cmd + V ...). 781 * 782 * @return the key code for shortcut modifier key 783 */ 784 public KeyCode getPlatformShortcutKey() { 785 return PlatformUtil.isMac() ? KeyCode.META : KeyCode.CONTROL; 786 } 787 788 public abstract FileChooserResult showFileChooser( 789 TKStage ownerWindow, 790 String title, 791 File initialDirectory, 792 String initialFileName, 793 FileChooserType fileChooserType, 794 List<ExtensionFilter> extensionFilters, 795 ExtensionFilter selectedFilter); 796 797 public abstract File showDirectoryChooser( 798 TKStage ownerWindow, 799 String title, 800 File initialDirectory); 801 802 /* 803 * Methods for obtaining "double-click" speed value. 804 */ 805 public abstract long getMultiClickTime(); 806 public abstract int getMultiClickMaxX(); 807 public abstract int getMultiClickMaxY(); 808 809 private CountDownLatch pauseScenesLatch = null; 810 811 /* 812 * Causes all scenes to stop by removing its TKPulseListener from Toolkit. 813 * It is used by Scenegraph-JMX bean. 814 */ 815 public void pauseScenes() { 816 pauseScenesLatch = new CountDownLatch(1); 817 Iterator<Window> i = Window.impl_getWindows(); 818 while (i.hasNext()) { 819 final Window w = i.next(); 820 final Scene scene = w.getScene(); 821 if (scene != null) { 822 this.removeSceneTkPulseListener(scene.impl_getScenePulseListener()); 823 } 824 } 825 this.getMasterTimer().pause(); 826 SceneHelper.setPaused(true); 827 } 828 829 /* 830 * Resume all scenes by registering its TKPulseListener back to Toolkit. 831 * It is used by Scenegraph-JMX bean. 832 */ 833 public void resumeScenes() { 834 SceneHelper.setPaused(false); 835 this.getMasterTimer().resume(); 836 Iterator<Window> i = Window.impl_getWindows(); 837 while (i.hasNext()) { 838 final Window w = i.next(); 839 final Scene scene = w.getScene(); 840 if (scene != null) { 841 this.addSceneTkPulseListener(scene.impl_getScenePulseListener()); 842 } 843 } 844 pauseScenesLatch.countDown(); 845 pauseScenesLatch = null; 846 } 847 848 /** 849 * Used to pause current thread when the Scene-graph is in "PAUSED" mode. 850 * It is mainly used by {@link javafx.application.Platform#runLater(Runnable)} 851 * to block the thread and not to pass the runnable to FX event queue. 852 */ 853 public void pauseCurrentThread() { 854 final CountDownLatch cdl = pauseScenesLatch; 855 if (cdl == null) { 856 return; 857 } 858 try { 859 cdl.await(); 860 } catch (InterruptedException e) { } 861 } 862 863 private Set<HighlightRegion> highlightRegions; 864 865 /** 866 * Getter for the set of regions to be highlighted using the JMX tooling 867 * interface. 868 * 869 * @return the set of regions to be highlighted. 870 */ 871 public Set<HighlightRegion> getHighlightedRegions() { 872 if (highlightRegions == null) { 873 highlightRegions = new HashSet<HighlightRegion>(); 874 } 875 return highlightRegions; 876 } 877 878 public interface WritableImageAccessor { 879 public void loadTkImage(WritableImage wimg, Object loader); 880 public Object getTkImageLoader(WritableImage wimg); 881 } 882 883 private static WritableImageAccessor writableImageAccessor = null; 884 885 public static void setWritableImageAccessor(WritableImageAccessor accessor) { 886 writableImageAccessor = accessor; 887 } 888 889 public static WritableImageAccessor getWritableImageAccessor() { 890 return writableImageAccessor; 891 } 892 893 public interface PaintAccessor { 894 public boolean isMutable(Paint paint); 895 public Object getPlatformPaint(Paint paint); 896 public void addListener(Paint paint, AbstractNotifyListener platformChangeListener); 897 public void removeListener(Paint paint, AbstractNotifyListener platformChangeListener); 898 } 899 900 private static PaintAccessor paintAccessor = null; 901 902 public static void setPaintAccessor(PaintAccessor accessor) { 903 paintAccessor = accessor; 904 } 905 906 public static PaintAccessor getPaintAccessor() { 907 return paintAccessor; 908 } 909 910 public interface ImageAccessor { 911 public boolean isAnimation(Image image); 912 public ReadOnlyObjectProperty<PlatformImage>getImageProperty(Image image); 913 public int[] getPreColors(PixelFormat<ByteBuffer> pf); 914 public int[] getNonPreColors(PixelFormat<ByteBuffer> pf); 915 } 916 917 private static ImageAccessor imageAccessor = null; 918 919 public static void setImageAccessor(ImageAccessor accessor) { 920 imageAccessor = accessor; 921 } 922 923 public static ImageAccessor getImageAccessor() { 924 return imageAccessor; 925 } 926 927 public String getThemeName() { 928 return null; 929 } 930 }