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