1 /*
   2  * Copyright (c) 2011, 2014, 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 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 getScale(Object obj) {
 524             return ((ScreenConfiguration) obj).getScale();
 525         }
 526     };
 527 
 528     @Override
 529     public ScreenConfigurationAccessor setScreenConfigurationListener(
 530             TKScreenConfigurationListener listener) {
 531         screenConfigurationListener = listener;
 532         return accessor;
 533     }
 534 
 535     @Override
 536     public ScreenConfiguration getPrimaryScreen() {
 537         return screenConfigurations[0];
 538     }
 539 
 540     public void setScreens(ScreenConfiguration... screenConfigurations) {
 541         this.screenConfigurations = screenConfigurations.clone();
 542         if (screenConfigurationListener != null) {
 543             screenConfigurationListener.screenConfigurationChanged();
 544         }
 545     }
 546 
 547     public void resetScreens() {
 548         setScreens(DEFAULT_SCREEN_CONFIG);
 549     }
 550 
 551     @Override
 552     public List<ScreenConfiguration> getScreens() {
 553         return Arrays.asList(screenConfigurations);
 554     }
 555 
 556     @Override
 557     public ScreenConfigurationAccessor getScreenConfigurationAccessor() {
 558         return accessor;
 559     }
 560 
 561     @Override public void registerDragGestureListener(TKScene s, Set<TransferMode> tm, TKDragGestureListener l) {
 562         if (dndDelegate != null) {
 563             dndDelegate.registerListener(l);
 564         }
 565     }
 566 
 567     @Override public void startDrag(TKScene scene, Set<TransferMode> tm, TKDragSourceListener l, Dragboard dragboard) {
 568         if (dndDelegate != null) {
 569             dndDelegate.startDrag(scene, tm, l, dragboard);
 570         }
 571     }
 572 
 573     @Override
 574     public ImageLoader loadImage(String url, int width, int height,
 575             boolean preserveRatio, boolean smooth) {
 576         return imageLoaderFactory.createImageLoader(url, width, height,
 577                                                     preserveRatio, smooth);
 578     }
 579 
 580     @Override
 581     public ImageLoader loadImage(InputStream stream, int width, int height,
 582             boolean preserveRatio, boolean smooth) {
 583         return imageLoaderFactory.createImageLoader(stream, width, height,
 584                                                     preserveRatio, smooth);
 585     }
 586 
 587     @Override
 588     public AsyncOperation loadImageAsync(
 589             AsyncOperationListener listener, String url, int width, int height,
 590             boolean preserveRatio, boolean smooth) {
 591         return imageLoaderFactory.createAsyncImageLoader(
 592                 listener, url, width, height, preserveRatio, smooth);
 593     }
 594 
 595     @Override
 596     public ImageLoader loadPlatformImage(Object platformImage) {
 597         return imageLoaderFactory.createImageLoader(platformImage,
 598                                                     0, 0, false, false);
 599     }
 600 
 601     @Override
 602     public PlatformImage createPlatformImage(int w, int h) {
 603         PlatformImage image = new StubWritablePlatformImage(w, h);
 604         imageLoaderFactory.registerImage(image, new StubPlatformImageInfo(w, h));
 605         return image;
 606     }
 607 
 608     @Override
 609     public void waitFor(Task t) {
 610         throw new UnsupportedOperationException();
 611     }
 612 
 613     @Override
 614     public int getKeyCodeForChar(String character) {
 615         if (charToKeyCodeMap != null) {
 616             final KeyCode keyCode = charToKeyCodeMap.get(character);
 617             if (keyCode != null) {
 618                 return keyCode.impl_getCode();
 619             }
 620         }
 621 
 622         return 0;
 623     }
 624 
 625     @Override
 626     public PathElement[] convertShapeToFXPath(Object shape) {
 627         // Had to be mocked up for TextField tests (for the caret!)
 628         // Since the "shape" could be anything, I'm just returning
 629         // something here, doesn't matter what.
 630         return new PathElement[0];
 631     }
 632 
 633     @Override
 634     public HitInfo convertHitInfoToFX(Object hit) {
 635         return (HitInfo) hit;
 636     }
 637 
 638     @Override
 639     public Filterable toFilterable(Image img) {
 640         return StubFilterable.create((StubPlatformImage)img.impl_getPlatformImage());
 641     }
 642 
 643     @Override
 644     public FilterContext getFilterContext(Object config) {
 645         throw new UnsupportedOperationException();
 646     }
 647 
 648     @Override
 649     public boolean isForwardTraversalKey(KeyEvent e) {
 650         throw new UnsupportedOperationException();
 651     }
 652 
 653     @Override
 654     public boolean isBackwardTraversalKey(KeyEvent e) {
 655         throw new UnsupportedOperationException();
 656     }
 657 
 658     @Override
 659     public Object createSVGPathObject(SVGPath svgpath) {
 660         int windingRule = (svgpath.getFillRule() == FillRule.NON_ZERO) ?
 661                            Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD;
 662 
 663         return new SVGPathImpl(svgpath.getContent(), windingRule);
 664     }
 665 
 666     @Override
 667     public Path2D createSVGPath2D(SVGPath svgpath) {
 668         int windingRule = (svgpath.getFillRule() == FillRule.NON_ZERO) ?
 669                            Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD;
 670 
 671         return new SVGPathImpl(svgpath.getContent(), windingRule);
 672     }
 673 
 674     @Override
 675     public boolean imageContains(Object image, float x, float y) {
 676         return ((StubPlatformImage) image).getImageInfo()
 677                                           .contains((int) x, (int) y);
 678     }
 679 
 680     public void setCurrentTime(long millis) {
 681         masterTimer.setCurrentTime(millis);
 682     }
 683 
 684     public void handleAnimation() {
 685         if (animationRunnable != null) {
 686             try {
 687                 animationRunnable.run();
 688             } catch (Throwable t) {
 689                 t.printStackTrace();
 690             }
 691         }
 692     }
 693 
 694     public StubImageLoaderFactory getImageLoaderFactory() {
 695         return imageLoaderFactory;
 696     }
 697 
 698     public void setAnimationTime(final long millis) {
 699         setCurrentTime(millis);
 700         handleAnimation();
 701         fireTestPulse();
 702     }
 703 
 704     @Override
 705     public void installInputMethodRequests(TKScene scene, InputMethodRequests requests) {
 706         // just do nothing here.
 707     }
 708 
 709     private Map<String, KeyCode> charToKeyCodeMap;
 710 
 711     public void setCharToKeyCodeMap(Map<String, KeyCode> charToKeyCodeMap) {
 712         this.charToKeyCodeMap = charToKeyCodeMap;
 713     }
 714 
 715     @Override
 716     public Object renderToImage(ImageRenderingContext context) {
 717         throw new UnsupportedOperationException();
 718     }
 719 
 720     @Override public Object enterNestedEventLoop(Object key) {
 721         throw new UnsupportedOperationException("Not supported yet.");
 722     }
 723 
 724     @Override public void exitNestedEventLoop(Object key, Object rval) {
 725         throw new UnsupportedOperationException("Not supported yet.");
 726     }
 727 
 728     @Override
 729     public boolean isNestedLoopRunning() {
 730         return false;
 731     }
 732 
 733     private KeyCode platformShortcutKey = KeyCode.SHORTCUT;
 734 
 735     public void setPlatformShortcutKey(final KeyCode platformShortcutKey) {
 736         this.platformShortcutKey = platformShortcutKey;
 737     }
 738 
 739     public KeyCode getPlatformShortcutKey() {
 740         return platformShortcutKey;
 741     }
 742 
 743     private DndDelegate dndDelegate;
 744     public void setDndDelegate(DndDelegate dndDelegate) {
 745         this.dndDelegate = dndDelegate;
 746     }
 747 
 748 
 749     public interface DndDelegate {
 750         void startDrag(TKScene scene, Set<TransferMode> tm,
 751                 TKDragSourceListener l, Dragboard dragboard);
 752 
 753         TKClipboard createDragboard();
 754 
 755         DragEvent convertDragEventToFx(Object event, Dragboard dragboard);
 756 
 757         void registerListener(TKDragGestureListener l);
 758 
 759         void enableDrop(TKDropTargetListener l);
 760     }
 761 
 762     public interface CommonDialogsSupport {
 763         FileChooserResult showFileChooser(TKStage ownerWindow,
 764                                    String title,
 765                                    File initialDirectory,
 766                                    String initialFileName,
 767                                    FileChooserType fileChooserType,
 768                                    List<ExtensionFilter> extensionFilters,
 769                                    ExtensionFilter selectedFilter);
 770 
 771         File showDirectoryChooser(TKStage ownerWindow,
 772                                   String title,
 773                                   File initialDirectory);
 774     }
 775 
 776     private CommonDialogsSupport commonDialogsSupport;
 777     public void setCommonDialogsSupport(
 778             final CommonDialogsSupport commonDialogsSupport) {
 779         this.commonDialogsSupport = commonDialogsSupport;
 780     }
 781 
 782     @Override
 783     public FileChooserResult showFileChooser(TKStage ownerWindow,
 784                                       String title,
 785                                       File initialDirectory,
 786                                       String initialFileName,
 787                                       FileChooserType fileChooserType,
 788                                       List<ExtensionFilter> extensionFilters,
 789                                       ExtensionFilter selectedFilter) {
 790         return commonDialogsSupport.showFileChooser(
 791                                         ownerWindow,
 792                                         title,
 793                                         initialDirectory,
 794                                         initialFileName,
 795                                         fileChooserType,
 796                                         extensionFilters,
 797                                         selectedFilter);
 798     }
 799 
 800 
 801     @Override
 802     public File showDirectoryChooser(TKStage ownerWindow,
 803                                      String title,
 804                                      File initialDirectory) {
 805         return commonDialogsSupport.showDirectoryChooser(
 806                                         ownerWindow,
 807                                         title,
 808                                         initialDirectory);
 809     }
 810 
 811     @Override
 812     public long getMultiClickTime() {
 813         return 500L;
 814     }
 815 
 816     @Override
 817     public int getMultiClickMaxX() {
 818         return 5;
 819     }
 820 
 821     @Override
 822     public int getMultiClickMaxY() {
 823         return 5;
 824     }
 825 
 826     public static final class ScreenConfiguration {
 827         private final int minX;
 828         private final int minY;
 829         private final int width;
 830         private final int height;
 831         private final int visualMinX;
 832         private final int visualMinY;
 833         private final int visualWidth;
 834         private final int visualHeight;
 835         private final float dpi;
 836         private final float scale;
 837 
 838         public ScreenConfiguration(final int minX, final int minY,
 839                                    final int width, final int height,
 840                                    final int visualMinX,
 841                                    final int visualMinY,
 842                                    final int visualWidth,
 843                                    final int visualHeight,
 844                                    final float dpi) {
 845             this.minX = minX;
 846             this.minY = minY;
 847             this.width = width;
 848             this.height = height;
 849             this.visualMinX = visualMinX;
 850             this.visualMinY = visualMinY;
 851             this.visualWidth = visualWidth;
 852             this.visualHeight = visualHeight;
 853             this.dpi = dpi;
 854             this.scale = 1;  // TODO: add a constructor that takes scale
 855         }
 856 
 857         public int getMinX() {
 858             return minX;
 859         }
 860 
 861         public int getMinY() {
 862             return minY;
 863         }
 864 
 865         public int getWidth() {
 866             return width;
 867         }
 868 
 869         public int getHeight() {
 870             return height;
 871         }
 872 
 873         public int getVisualMinX() {
 874             return visualMinX;
 875         }
 876 
 877         public int getVisualMinY() {
 878             return visualMinY;
 879         }
 880 
 881         public int getVisualWidth() {
 882             return visualWidth;
 883         }
 884 
 885         public int getVisualHeight() {
 886             return visualHeight;
 887         }
 888 
 889         public float getDPI() {
 890             return dpi;
 891         }
 892 
 893         public float getScale() {
 894             return scale;
 895         }
 896     }
 897 
 898     public static class StubSystemMenu implements TKSystemMenu {
 899 
 900         private  List<MenuBase> menus = null;
 901 
 902         @Override
 903         public boolean isSupported() {
 904             // Although not all platforms have a system menu, the only real
 905             // interaction with the system menu is this TKSystemMenu instance
 906             // so we'll return true on all platforms.
 907             return true;
 908 //                    final String os = System.getProperty("os.name");
 909 //                    return (os != null && os.startsWith("Mac"));
 910         }
 911 
 912         @Override
 913         public void setMenus(List<MenuBase> menus) {
 914             this.menus = menus;
 915         }
 916 
 917         // make menus accessible to unit tests
 918         public List<MenuBase> getMenus() {
 919             return menus;
 920         }
 921 
 922     }
 923 }