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