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 }