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 }