1 /*
   2  * Copyright (c) 2011, 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 /*
  27  * StubToolkit.java
  28  */
  29 
  30 package test.com.sun.javafx.pgstub;
  31 
  32 import com.sun.glass.ui.CommonDialogs.FileChooserResult;
  33 import com.sun.javafx.application.PlatformImpl;
  34 import com.sun.javafx.embed.HostInterface;
  35 import com.sun.javafx.geom.Path2D;
  36 import com.sun.javafx.geom.Shape;
  37 import com.sun.javafx.geom.transform.BaseTransform;
  38 import com.sun.javafx.menu.MenuBase;
  39 import com.sun.javafx.perf.PerformanceTracker;
  40 import com.sun.javafx.runtime.async.AsyncOperation;
  41 import com.sun.javafx.runtime.async.AsyncOperationListener;
  42 import com.sun.javafx.scene.text.HitInfo;
  43 import com.sun.javafx.scene.text.TextLayoutFactory;
  44 import com.sun.javafx.tk.*;
  45 import com.sun.prism.BasicStroke;
  46 import com.sun.scenario.DelayedRunnable;
  47 import com.sun.scenario.animation.AbstractMasterTimer;
  48 import com.sun.scenario.effect.FilterContext;
  49 import com.sun.scenario.effect.Filterable;
  50 import javafx.application.ConditionalFeature;
  51 import javafx.geometry.Dimension2D;
  52 import javafx.scene.image.Image;
  53 import javafx.scene.input.*;
  54 import javafx.scene.paint.Color;
  55 import javafx.scene.paint.ImagePattern;
  56 import javafx.scene.paint.LinearGradient;
  57 import javafx.scene.paint.RadialGradient;
  58 import javafx.scene.shape.*;
  59 import javafx.stage.FileChooser.ExtensionFilter;
  60 import javafx.stage.Modality;
  61 import javafx.stage.StageStyle;
  62 import javafx.stage.Window;
  63 import javafx.util.Pair;
  64 
  65 import java.io.File;
  66 import java.io.InputStream;
  67 import java.security.AccessControlContext;
  68 import java.util.*;
  69 import java.util.concurrent.Future;
  70 
  71 /**
  72  * A Toolkit implementation for use with Testing.
  73  *
  74  * @author Richard
  75  */
  76 public class StubToolkit extends Toolkit {
  77 
  78     private Map<Object, Object> contextMap = new HashMap<Object, Object>();
  79 
  80     private StubMasterTimer masterTimer = new StubMasterTimer();
  81 
  82     private PerformanceTracker performanceTracker = new StubPerformanceTracker();
  83 
  84     private final StubImageLoaderFactory imageLoaderFactory =
  85             new StubImageLoaderFactory();
  86 
  87     private CursorSizeConverter cursorSizeConverter =
  88             CursorSizeConverter.NO_CURSOR_SUPPORT;
  89 
  90     private int maximumCursorColors = 2;
  91 
  92     private TKScreenConfigurationListener screenConfigurationListener;
  93 
  94     private static final ScreenConfiguration[] DEFAULT_SCREEN_CONFIG = {
  95                 new ScreenConfiguration(0, 0, 1920, 1200, 0, 0, 1920, 1172, 96)
  96             };
  97 
  98     private ScreenConfiguration[] screenConfigurations = DEFAULT_SCREEN_CONFIG;
  99 
 100     static {
 101         try {
 102             // ugly hack to initialize "runLater" method in Platform.java
 103             PlatformImpl.startup(() -> {});
 104         } catch (Exception ex) {}
 105 
 106         // allow tests to access PG scenegraph
 107         // so that they can run with assertion enabled
 108         javafx.scene.Scene.impl_setAllowPGAccess(true);
 109     }
 110     private boolean pulseRequested;
 111 
 112     /*
 113      * overrides of Toolkit's abstract functions
 114      */
 115 
 116     @Override
 117     public boolean init() {
 118         return true;
 119     }
 120 
 121     @Override
 122     public TKStage createTKStage(Window peerWindow, boolean securityDialog, StageStyle stageStyle, boolean primary, Modality modality, TKStage owner, boolean rtl, AccessControlContext acc) {
 123 
 124         return new StubStage();
 125     }
 126 
 127     @Override
 128     public TKStage createTKPopupStage(Window peerWindow, StageStyle popupStyle, TKStage owner, AccessControlContext acc) {
 129         return new StubPopupStage();
 130     }
 131 
 132     @Override
 133     public TKStage createTKEmbeddedStage(HostInterface host, AccessControlContext acc) {
 134         return new StubStage();
 135     }
 136 
 137     @Override
 138     public AppletWindow createAppletWindow(long parent, String serverName) {
 139         // unsupported
 140         return null;
 141     }
 142 
 143     @Override
 144     public void closeAppletWindow() {
 145         // unsupported
 146     }
 147 
 148     private final TKSystemMenu systemMenu = new StubSystemMenu();
 149     @Override
 150     public TKSystemMenu getSystemMenu() {
 151         return systemMenu;
 152     }
 153 
 154     @Override
 155     public void startup(Runnable runnable) {
 156         runnable.run();
 157     }
 158 
 159     @Override
 160     public void checkFxUserThread() {
 161         // Do nothing
 162     }
 163 
 164     @Override
 165     public boolean isFxUserThread() {
 166         // Always on the FX app thread
 167         return true;
 168     }
 169 
 170     @Override
 171     public void defer(Runnable runnable) {
 172         runnable.run();
 173     }
 174 
 175     @Override
 176     public void exit() {
 177         System.exit(0);
 178     }
 179 
 180     @Override
 181     public Future addRenderJob(RenderJob rj) {
 182         return rj;
 183     }
 184 
 185     @Override
 186     public Map<Object, Object> getContextMap() {
 187         return contextMap;
 188     }
 189 
 190     @Override public int getRefreshRate() {
 191         return -1;
 192     }
 193 
 194     private DelayedRunnable animationRunnable;
 195 
 196     @Override
 197     public void setAnimationRunnable(DelayedRunnable animationRunnable) {
 198         this.animationRunnable = animationRunnable;
 199     }
 200 
 201     @Override
 202     public PerformanceTracker getPerformanceTracker() {
 203         return performanceTracker;
 204     }
 205 
 206     @Override public PerformanceTracker createPerformanceTracker() {
 207         return new StubPerformanceTracker();
 208     }
 209 
 210     @Override
 211     protected Object createColorPaint(Color paint) {
 212         return new com.sun.prism.paint.Color((float) paint.getRed(),
 213                     (float) paint.getGreen(),
 214                     (float) paint.getBlue(),
 215                     (float) paint.getOpacity());
 216     }
 217 
 218     @Override
 219     protected Object createLinearGradientPaint(LinearGradient paint) {
 220         // Non functioning but compiles
 221         return new com.sun.prism.paint.Color(1, 1, 1, 1);
 222     }
 223 
 224     @Override
 225     protected Object createRadialGradientPaint(RadialGradient paint) {
 226         // Non functioning but compiles
 227         return new com.sun.prism.paint.Color(1, 1, 1, 1);
 228     }
 229 
 230     @Override
 231     protected Object createImagePatternPaint(ImagePattern paint) {
 232         // Non functioning but compiles
 233         return new com.sun.prism.paint.Color(1, 1, 1, 1);
 234     }
 235 
 236     static BasicStroke tmpStroke = new BasicStroke();
 237     void initStroke(StrokeType pgtype, double strokewidth,
 238                     StrokeLineCap pgcap,
 239                     StrokeLineJoin pgjoin, float miterLimit)
 240     {
 241         int type;
 242         if (pgtype == StrokeType.CENTERED) {
 243             type = BasicStroke.TYPE_CENTERED;
 244         } else if (pgtype == StrokeType.INSIDE) {
 245             type = BasicStroke.TYPE_INNER;
 246         } else {
 247             type = BasicStroke.TYPE_OUTER;
 248         }
 249 
 250         int cap;
 251         if (pgcap == StrokeLineCap.BUTT) {
 252             cap = BasicStroke.CAP_BUTT;
 253         } else if (pgcap == StrokeLineCap.SQUARE) {
 254             cap = BasicStroke.CAP_SQUARE;
 255         } else {
 256             cap = BasicStroke.CAP_ROUND;
 257         }
 258 
 259         int join;
 260         if (pgjoin == StrokeLineJoin.BEVEL) {
 261             join = BasicStroke.JOIN_BEVEL;
 262         } else if (pgjoin == StrokeLineJoin.MITER) {
 263             join = BasicStroke.JOIN_MITER;
 264         } else {
 265             join = BasicStroke.JOIN_ROUND;
 266         }
 267 
 268         tmpStroke.set(type, (float) strokewidth, cap, join, miterLimit);
 269     }
 270 
 271     @Override
 272     public void accumulateStrokeBounds(Shape shape, float bbox[],
 273                                        StrokeType pgtype,
 274                                        double strokewidth,
 275                                        StrokeLineCap pgcap,
 276                                        StrokeLineJoin pgjoin,
 277                                        float miterLimit,
 278                                        BaseTransform tx)
 279     {
 280 
 281         initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit);
 282         // TODO: The accumulation could be done directly without creating a Shape
 283         Shape.accumulate(bbox, tmpStroke.createStrokedShape(shape), tx);
 284     }
 285 
 286     @Override
 287     public boolean strokeContains(Shape shape, double x, double y,
 288                                   StrokeType pgtype,
 289                                   double strokewidth,
 290                                   StrokeLineCap pgcap,
 291                                   StrokeLineJoin pgjoin,
 292                                   float miterLimit)
 293     {
 294         initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit);
 295         // TODO: The contains testing could be done directly without creating a Shape
 296         return tmpStroke.createStrokedShape(shape).contains((float) x, (float) y);
 297     }
 298 
 299     @Override
 300     public Shape createStrokedShape(Shape shape,
 301                                     StrokeType pgtype,
 302                                     double strokewidth,
 303                                     StrokeLineCap pgcap,
 304                                     StrokeLineJoin pgjoin,
 305                                     float miterLimit,
 306                                     float[] dashArray,
 307                                     float dashOffset) {
 308         initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit);
 309         return tmpStroke.createStrokedShape(shape);
 310     }
 311 
 312     public CursorSizeConverter getCursorSizeConverter() {
 313         return cursorSizeConverter;
 314     }
 315 
 316     public void setCursorSizeConverter(
 317             CursorSizeConverter cursorSizeConverter) {
 318         this.cursorSizeConverter = cursorSizeConverter;
 319     }
 320 
 321     @Override
 322     public Dimension2D getBestCursorSize(int preferredWidth, int preferredHeight) {
 323         return cursorSizeConverter.getBestCursorSize(preferredWidth,
 324                                                      preferredHeight);
 325     }
 326 
 327     @Override
 328     public int getMaximumCursorColors() {
 329         return maximumCursorColors;
 330     }
 331 
 332     public void setMaximumCursorColors(int maximumCursorColors) {
 333         this.maximumCursorColors = maximumCursorColors;
 334     }
 335 
 336     @Override
 337     public AbstractMasterTimer getMasterTimer() {
 338         return masterTimer;
 339     }
 340 
 341     @Override
 342     public FontLoader getFontLoader() {
 343         return new StubFontLoader();
 344     }
 345 
 346     @Override
 347     public TextLayoutFactory getTextLayoutFactory() {
 348         return new StubTextLayoutFactory();
 349     }
 350 
 351     @Override public boolean isSupported(ConditionalFeature feature) {
 352         if (feature == ConditionalFeature.SCENE3D) {
 353             return true;
 354         } else if (feature == ConditionalFeature.TRANSPARENT_WINDOW) {
 355             return true;
 356         }
 357         return false;
 358     }
 359 
 360     /*
 361      * additional testing functions
 362      */
 363     public void fireTestPulse() {
 364         firePulse();
 365     }
 366 
 367     public boolean isPulseRequested() {
 368         return pulseRequested;
 369     }
 370 
 371     public void clearPulseRequested() {
 372         pulseRequested = false;
 373     }
 374 
 375     // do nothing -- bringing in FrameJob and MasterTimer also bring in
 376     // Settings and crap which isn't setup for the testing stuff because
 377     // we don't run through a RuntimeProvider or do normal startup
 378     // public @Override public void triggerNextPulse():Void { }
 379     @Override public void requestNextPulse() {
 380         pulseRequested = true;
 381     }
 382 
 383     private TKClipboard clipboard = new TKClipboard() {
 384         private Map<DataFormat, Object> map = new HashMap<DataFormat, Object>();
 385         private Image image;
 386         private double offsetX;
 387         private double offsetY;
 388 
 389         @Override
 390         public void setSecurityContext(AccessControlContext ctx) {
 391         }
 392 
 393         @Override public Set<DataFormat> getContentTypes() {
 394             return map.keySet();
 395         }
 396 
 397         @Override public boolean putContent(Pair<DataFormat, Object>... content) {
 398             boolean good;
 399             for (Pair<DataFormat,Object> pair : content) {
 400                 good = map.put(pair.getKey(), pair.getValue()) == pair.getValue();
 401                 if (!good) return false;
 402             }
 403             return true;
 404         }
 405 
 406         @Override public Object getContent(DataFormat dataFormat) {
 407             return map.get(dataFormat);
 408         }
 409 
 410         @Override public boolean hasContent(DataFormat dataFormat) {
 411             return map.containsKey(dataFormat);
 412         }
 413 
 414         @Override public Set<TransferMode> getTransferModes() {
 415             Set<TransferMode> modes = new HashSet<TransferMode>();
 416             modes.add(TransferMode.COPY);
 417             return modes;
 418         }
 419 
 420         @Override
 421         public void setDragView(Image image) {
 422             this.image = image;
 423         }
 424 
 425         @Override
 426         public void setDragViewOffsetX(double offsetX) {
 427             this.offsetX = offsetX;
 428         }
 429 
 430         @Override
 431         public void setDragViewOffsetY(double offsetY) {
 432             this.offsetY = offsetY;
 433         }
 434 
 435         @Override
 436         public Image getDragView() {
 437             return image;
 438         }
 439 
 440         @Override
 441         public double getDragViewOffsetX() {
 442             return offsetX;
 443         }
 444 
 445         @Override
 446         public double getDragViewOffsetY() {
 447             return offsetY;
 448         }
 449     };
 450 
 451 
 452     @Override
 453     public TKClipboard getSystemClipboard() {
 454         return clipboard;
 455     }
 456 
 457     @Override public TKClipboard getNamedClipboard(String name) {
 458         return null;
 459     }
 460 
 461     public static TKClipboard createDragboard() {
 462         StubToolkit tk = (StubToolkit)Toolkit.getToolkit();
 463         if (tk.dndDelegate != null) {
 464             return tk.dndDelegate.createDragboard();
 465         }
 466         return null;
 467     }
 468 
 469     @Override
 470     public void enableDrop(TKScene s, TKDropTargetListener l) {
 471         if (dndDelegate != null) {
 472             dndDelegate.enableDrop(l);
 473         }
 474     }
 475 
 476     private ScreenConfigurationAccessor accessor = new ScreenConfigurationAccessor() {
 477         @Override
 478         public int getMinX(Object obj) {
 479             return ((ScreenConfiguration) obj).getMinX();
 480         }
 481 
 482         @Override
 483         public int getMinY(Object obj) {
 484             return ((ScreenConfiguration) obj).getMinY();
 485         }
 486 
 487         @Override
 488         public int getWidth(Object obj) {
 489             return ((ScreenConfiguration) obj).getWidth();
 490         }
 491 
 492         @Override
 493         public int getHeight(Object obj) {
 494             return ((ScreenConfiguration) obj).getHeight();
 495         }
 496 
 497         @Override
 498         public int getVisualMinX(Object obj) {
 499             return ((ScreenConfiguration) obj).getVisualMinX();
 500         }
 501 
 502         @Override
 503         public int getVisualMinY(Object obj) {
 504             return ((ScreenConfiguration) obj).getVisualMinY();
 505         }
 506 
 507         @Override
 508         public int getVisualWidth(Object obj) {
 509             return ((ScreenConfiguration) obj).getVisualWidth();
 510         }
 511 
 512         @Override
 513         public int getVisualHeight(Object obj) {
 514             return ((ScreenConfiguration) obj).getVisualHeight();
 515         }
 516 
 517         @Override
 518         public float getDPI(Object obj) {
 519             return ((ScreenConfiguration) obj).getDPI();
 520         }
 521 
 522         @Override
 523         public float getUIScale(Object obj) {
 524             return ((ScreenConfiguration) obj).getScale();
 525         }
 526 
 527         @Override
 528         public float getRenderScale(Object obj) {
 529             return ((ScreenConfiguration) obj).getScale();
 530         }
 531     };
 532 
 533     @Override
 534     public ScreenConfigurationAccessor setScreenConfigurationListener(
 535             TKScreenConfigurationListener listener) {
 536         screenConfigurationListener = listener;
 537         return accessor;
 538     }
 539 
 540     @Override
 541     public ScreenConfiguration getPrimaryScreen() {
 542         return screenConfigurations[0];
 543     }
 544 
 545     public void setScreens(ScreenConfiguration... screenConfigurations) {
 546         this.screenConfigurations = screenConfigurations.clone();
 547         if (screenConfigurationListener != null) {
 548             screenConfigurationListener.screenConfigurationChanged();
 549         }
 550     }
 551 
 552     public void resetScreens() {
 553         setScreens(DEFAULT_SCREEN_CONFIG);
 554     }
 555 
 556     @Override
 557     public List<ScreenConfiguration> getScreens() {
 558         return Arrays.asList(screenConfigurations);
 559     }
 560 
 561     @Override
 562     public ScreenConfigurationAccessor getScreenConfigurationAccessor() {
 563         return accessor;
 564     }
 565 
 566     @Override public void registerDragGestureListener(TKScene s, Set<TransferMode> tm, TKDragGestureListener l) {
 567         if (dndDelegate != null) {
 568             dndDelegate.registerListener(l);
 569         }
 570     }
 571 
 572     @Override public void startDrag(TKScene scene, Set<TransferMode> tm, TKDragSourceListener l, Dragboard dragboard) {
 573         if (dndDelegate != null) {
 574             dndDelegate.startDrag(scene, tm, l, dragboard);
 575         }
 576     }
 577 
 578     @Override
 579     public ImageLoader loadImage(String url, int width, int height,
 580             boolean preserveRatio, boolean smooth) {
 581         return imageLoaderFactory.createImageLoader(url, width, height,
 582                                                     preserveRatio, smooth);
 583     }
 584 
 585     @Override
 586     public ImageLoader loadImage(InputStream stream, int width, int height,
 587             boolean preserveRatio, boolean smooth) {
 588         return imageLoaderFactory.createImageLoader(stream, width, height,
 589                                                     preserveRatio, smooth);
 590     }
 591 
 592     @Override
 593     public AsyncOperation loadImageAsync(
 594             AsyncOperationListener listener, String url, int width, int height,
 595             boolean preserveRatio, boolean smooth) {
 596         return imageLoaderFactory.createAsyncImageLoader(
 597                 listener, url, width, height, preserveRatio, smooth);
 598     }
 599 
 600     @Override
 601     public ImageLoader loadPlatformImage(Object platformImage) {
 602         return imageLoaderFactory.createImageLoader(platformImage,
 603                                                     0, 0, false, false);
 604     }
 605 
 606     @Override
 607     public PlatformImage createPlatformImage(int w, int h) {
 608         PlatformImage image = new StubWritablePlatformImage(w, h);
 609         imageLoaderFactory.registerImage(image, new StubPlatformImageInfo(w, h));
 610         return image;
 611     }
 612 
 613     @Override
 614     public void waitFor(Task t) {
 615         throw new UnsupportedOperationException();
 616     }
 617 
 618     @Override
 619     public int getKeyCodeForChar(String character) {
 620         if (charToKeyCodeMap != null) {
 621             final KeyCode keyCode = charToKeyCodeMap.get(character);
 622             if (keyCode != null) {
 623                 return keyCode.getCode();
 624             }
 625         }
 626 
 627         return 0;
 628     }
 629 
 630     @Override
 631     public PathElement[] convertShapeToFXPath(Object shape) {
 632         // Had to be mocked up for TextField tests (for the caret!)
 633         // Since the "shape" could be anything, I'm just returning
 634         // something here, doesn't matter what.
 635         return new PathElement[0];
 636     }
 637 
 638     @Override
 639     public HitInfo convertHitInfoToFX(Object hit) {
 640         return (HitInfo) hit;
 641     }
 642 
 643     @Override
 644     public Filterable toFilterable(Image img) {
 645         return StubFilterable.create((StubPlatformImage)img.impl_getPlatformImage());
 646     }
 647 
 648     @Override
 649     public FilterContext getFilterContext(Object config) {
 650         throw new UnsupportedOperationException();
 651     }
 652 
 653     @Override
 654     public boolean isForwardTraversalKey(KeyEvent e) {
 655         throw new UnsupportedOperationException();
 656     }
 657 
 658     @Override
 659     public boolean isBackwardTraversalKey(KeyEvent e) {
 660         throw new UnsupportedOperationException();
 661     }
 662 
 663     @Override
 664     public Object createSVGPathObject(SVGPath svgpath) {
 665         int windingRule = (svgpath.getFillRule() == FillRule.NON_ZERO) ?
 666                            Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD;
 667 
 668         return new SVGPathImpl(svgpath.getContent(), windingRule);
 669     }
 670 
 671     @Override
 672     public Path2D createSVGPath2D(SVGPath svgpath) {
 673         int windingRule = (svgpath.getFillRule() == FillRule.NON_ZERO) ?
 674                            Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD;
 675 
 676         return new SVGPathImpl(svgpath.getContent(), windingRule);
 677     }
 678 
 679     @Override
 680     public boolean imageContains(Object image, float x, float y) {
 681         return ((StubPlatformImage) image).getImageInfo()
 682                                           .contains((int) x, (int) y);
 683     }
 684 
 685     public void setCurrentTime(long millis) {
 686         masterTimer.setCurrentTime(millis);
 687     }
 688 
 689     public void handleAnimation() {
 690         if (animationRunnable != null) {
 691             try {
 692                 animationRunnable.run();
 693             } catch (Throwable t) {
 694                 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), t);
 695             }
 696         }
 697     }
 698 
 699     public StubImageLoaderFactory getImageLoaderFactory() {
 700         return imageLoaderFactory;
 701     }
 702 
 703     public void setAnimationTime(final long millis) {
 704         setCurrentTime(millis);
 705         handleAnimation();
 706         fireTestPulse();
 707     }
 708 
 709     @Override
 710     public void installInputMethodRequests(TKScene scene, InputMethodRequests requests) {
 711         // just do nothing here.
 712     }
 713 
 714     private Map<String, KeyCode> charToKeyCodeMap;
 715 
 716     public void setCharToKeyCodeMap(Map<String, KeyCode> charToKeyCodeMap) {
 717         this.charToKeyCodeMap = charToKeyCodeMap;
 718     }
 719 
 720     @Override
 721     public Object renderToImage(ImageRenderingContext context) {
 722         throw new UnsupportedOperationException();
 723     }
 724 
 725     @Override public boolean canStartNestedEventLoop() {
 726         return false;
 727     }
 728 
 729     @Override public Object enterNestedEventLoop(Object key) {
 730         throw new UnsupportedOperationException("Not supported yet.");
 731     }
 732 
 733     @Override public void exitNestedEventLoop(Object key, Object rval) {
 734         throw new UnsupportedOperationException("Not supported yet.");
 735     }
 736 
 737     @Override
 738     public boolean isNestedLoopRunning() {
 739         return false;
 740     }
 741 
 742     private KeyCode platformShortcutKey = KeyCode.SHORTCUT;
 743 
 744     public void setPlatformShortcutKey(final KeyCode platformShortcutKey) {
 745         this.platformShortcutKey = platformShortcutKey;
 746     }
 747 
 748     public KeyCode getPlatformShortcutKey() {
 749         return platformShortcutKey;
 750     }
 751 
 752     private DndDelegate dndDelegate;
 753     public void setDndDelegate(DndDelegate dndDelegate) {
 754         this.dndDelegate = dndDelegate;
 755     }
 756 
 757 
 758     public interface DndDelegate {
 759         void startDrag(TKScene scene, Set<TransferMode> tm,
 760                 TKDragSourceListener l, Dragboard dragboard);
 761 
 762         TKClipboard createDragboard();
 763 
 764         DragEvent convertDragEventToFx(Object event, Dragboard dragboard);
 765 
 766         void registerListener(TKDragGestureListener l);
 767 
 768         void enableDrop(TKDropTargetListener l);
 769     }
 770 
 771     public interface CommonDialogsSupport {
 772         FileChooserResult showFileChooser(TKStage ownerWindow,
 773                                    String title,
 774                                    File initialDirectory,
 775                                    String initialFileName,
 776                                    FileChooserType fileChooserType,
 777                                    List<ExtensionFilter> extensionFilters,
 778                                    ExtensionFilter selectedFilter);
 779 
 780         File showDirectoryChooser(TKStage ownerWindow,
 781                                   String title,
 782                                   File initialDirectory);
 783     }
 784 
 785     private CommonDialogsSupport commonDialogsSupport;
 786     public void setCommonDialogsSupport(
 787             final CommonDialogsSupport commonDialogsSupport) {
 788         this.commonDialogsSupport = commonDialogsSupport;
 789     }
 790 
 791     @Override
 792     public FileChooserResult showFileChooser(TKStage ownerWindow,
 793                                       String title,
 794                                       File initialDirectory,
 795                                       String initialFileName,
 796                                       FileChooserType fileChooserType,
 797                                       List<ExtensionFilter> extensionFilters,
 798                                       ExtensionFilter selectedFilter) {
 799         return commonDialogsSupport.showFileChooser(
 800                                         ownerWindow,
 801                                         title,
 802                                         initialDirectory,
 803                                         initialFileName,
 804                                         fileChooserType,
 805                                         extensionFilters,
 806                                         selectedFilter);
 807     }
 808 
 809 
 810     @Override
 811     public File showDirectoryChooser(TKStage ownerWindow,
 812                                      String title,
 813                                      File initialDirectory) {
 814         return commonDialogsSupport.showDirectoryChooser(
 815                                         ownerWindow,
 816                                         title,
 817                                         initialDirectory);
 818     }
 819 
 820     @Override
 821     public long getMultiClickTime() {
 822         return 500L;
 823     }
 824 
 825     @Override
 826     public int getMultiClickMaxX() {
 827         return 5;
 828     }
 829 
 830     @Override
 831     public int getMultiClickMaxY() {
 832         return 5;
 833     }
 834 
 835     public static final class ScreenConfiguration {
 836         private final int minX;
 837         private final int minY;
 838         private final int width;
 839         private final int height;
 840         private final int visualMinX;
 841         private final int visualMinY;
 842         private final int visualWidth;
 843         private final int visualHeight;
 844         private final float dpi;
 845         private final float scale;
 846 
 847         public ScreenConfiguration(final int minX, final int minY,
 848                                    final int width, final int height,
 849                                    final int visualMinX,
 850                                    final int visualMinY,
 851                                    final int visualWidth,
 852                                    final int visualHeight,
 853                                    final float dpi) {
 854             this.minX = minX;
 855             this.minY = minY;
 856             this.width = width;
 857             this.height = height;
 858             this.visualMinX = visualMinX;
 859             this.visualMinY = visualMinY;
 860             this.visualWidth = visualWidth;
 861             this.visualHeight = visualHeight;
 862             this.dpi = dpi;
 863             this.scale = 1;  // TODO: add a constructor that takes scale
 864         }
 865 
 866         public int getMinX() {
 867             return minX;
 868         }
 869 
 870         public int getMinY() {
 871             return minY;
 872         }
 873 
 874         public int getWidth() {
 875             return width;
 876         }
 877 
 878         public int getHeight() {
 879             return height;
 880         }
 881 
 882         public int getVisualMinX() {
 883             return visualMinX;
 884         }
 885 
 886         public int getVisualMinY() {
 887             return visualMinY;
 888         }
 889 
 890         public int getVisualWidth() {
 891             return visualWidth;
 892         }
 893 
 894         public int getVisualHeight() {
 895             return visualHeight;
 896         }
 897 
 898         public float getDPI() {
 899             return dpi;
 900         }
 901 
 902         public float getScale() {
 903             return scale;
 904         }
 905     }
 906 
 907     public static class StubSystemMenu implements TKSystemMenu {
 908 
 909         private  List<MenuBase> menus = null;
 910 
 911         @Override
 912         public boolean isSupported() {
 913             // Although not all platforms have a system menu, the only real
 914             // interaction with the system menu is this TKSystemMenu instance
 915             // so we'll return true on all platforms.
 916             return true;
 917 //                    final String os = System.getProperty("os.name");
 918 //                    return (os != null && os.startsWith("Mac"));
 919         }
 920 
 921         @Override
 922         public void setMenus(List<MenuBase> menus) {
 923             this.menus = menus;
 924         }
 925 
 926         // make menus accessible to unit tests
 927         public List<MenuBase> getMenus() {
 928             return menus;
 929         }
 930 
 931     }
 932 }