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 getRecommendedOutputScaleX(Object obj) {
 523             return ((ScreenConfiguration) obj).getScale();
 524         }
 525 
 526         @Override
 527         public float getRecommendedOutputScaleY(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)
 640                 Toolkit.getImageAccessor().getPlatformImage(img));
 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                 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), t);
 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 boolean canStartNestedEventLoop() {
 721         return false;
 722     }
 723 
 724     @Override public Object enterNestedEventLoop(Object key) {
 725         throw new UnsupportedOperationException("Not supported yet.");
 726     }
 727 
 728     @Override public void exitNestedEventLoop(Object key, Object rval) {
 729         throw new UnsupportedOperationException("Not supported yet.");
 730     }
 731 
 732     @Override
 733     public boolean isNestedLoopRunning() {
 734         return false;
 735     }
 736 
 737     private KeyCode platformShortcutKey = KeyCode.SHORTCUT;
 738 
 739     public void setPlatformShortcutKey(final KeyCode platformShortcutKey) {
 740         this.platformShortcutKey = platformShortcutKey;
 741     }
 742 
 743     public KeyCode getPlatformShortcutKey() {
 744         return platformShortcutKey;
 745     }
 746 
 747     private DndDelegate dndDelegate;
 748     public void setDndDelegate(DndDelegate dndDelegate) {
 749         this.dndDelegate = dndDelegate;
 750     }
 751 
 752 
 753     public interface DndDelegate {
 754         void startDrag(TKScene scene, Set<TransferMode> tm,
 755                 TKDragSourceListener l, Dragboard dragboard);
 756 
 757         TKClipboard createDragboard();
 758 
 759         DragEvent convertDragEventToFx(Object event, Dragboard dragboard);
 760 
 761         void registerListener(TKDragGestureListener l);
 762 
 763         void enableDrop(TKDropTargetListener l);
 764     }
 765 
 766     public interface CommonDialogsSupport {
 767         FileChooserResult showFileChooser(TKStage ownerWindow,
 768                                    String title,
 769                                    File initialDirectory,
 770                                    String initialFileName,
 771                                    FileChooserType fileChooserType,
 772                                    List<ExtensionFilter> extensionFilters,
 773                                    ExtensionFilter selectedFilter);
 774 
 775         File showDirectoryChooser(TKStage ownerWindow,
 776                                   String title,
 777                                   File initialDirectory);
 778     }
 779 
 780     private CommonDialogsSupport commonDialogsSupport;
 781     public void setCommonDialogsSupport(
 782             final CommonDialogsSupport commonDialogsSupport) {
 783         this.commonDialogsSupport = commonDialogsSupport;
 784     }
 785 
 786     @Override
 787     public FileChooserResult showFileChooser(TKStage ownerWindow,
 788                                       String title,
 789                                       File initialDirectory,
 790                                       String initialFileName,
 791                                       FileChooserType fileChooserType,
 792                                       List<ExtensionFilter> extensionFilters,
 793                                       ExtensionFilter selectedFilter) {
 794         return commonDialogsSupport.showFileChooser(
 795                                         ownerWindow,
 796                                         title,
 797                                         initialDirectory,
 798                                         initialFileName,
 799                                         fileChooserType,
 800                                         extensionFilters,
 801                                         selectedFilter);
 802     }
 803 
 804 
 805     @Override
 806     public File showDirectoryChooser(TKStage ownerWindow,
 807                                      String title,
 808                                      File initialDirectory) {
 809         return commonDialogsSupport.showDirectoryChooser(
 810                                         ownerWindow,
 811                                         title,
 812                                         initialDirectory);
 813     }
 814 
 815     @Override
 816     public long getMultiClickTime() {
 817         return 500L;
 818     }
 819 
 820     @Override
 821     public int getMultiClickMaxX() {
 822         return 5;
 823     }
 824 
 825     @Override
 826     public int getMultiClickMaxY() {
 827         return 5;
 828     }
 829 
 830     public static final class ScreenConfiguration {
 831         private final int minX;
 832         private final int minY;
 833         private final int width;
 834         private final int height;
 835         private final int visualMinX;
 836         private final int visualMinY;
 837         private final int visualWidth;
 838         private final int visualHeight;
 839         private final float dpi;
 840         private final float scale;
 841 
 842         public ScreenConfiguration(final int minX, final int minY,
 843                                    final int width, final int height,
 844                                    final int visualMinX,
 845                                    final int visualMinY,
 846                                    final int visualWidth,
 847                                    final int visualHeight,
 848                                    final float dpi) {
 849             this.minX = minX;
 850             this.minY = minY;
 851             this.width = width;
 852             this.height = height;
 853             this.visualMinX = visualMinX;
 854             this.visualMinY = visualMinY;
 855             this.visualWidth = visualWidth;
 856             this.visualHeight = visualHeight;
 857             this.dpi = dpi;
 858             this.scale = 1;  // TODO: add a constructor that takes scale
 859         }
 860 
 861         public int getMinX() {
 862             return minX;
 863         }
 864 
 865         public int getMinY() {
 866             return minY;
 867         }
 868 
 869         public int getWidth() {
 870             return width;
 871         }
 872 
 873         public int getHeight() {
 874             return height;
 875         }
 876 
 877         public int getVisualMinX() {
 878             return visualMinX;
 879         }
 880 
 881         public int getVisualMinY() {
 882             return visualMinY;
 883         }
 884 
 885         public int getVisualWidth() {
 886             return visualWidth;
 887         }
 888 
 889         public int getVisualHeight() {
 890             return visualHeight;
 891         }
 892 
 893         public float getDPI() {
 894             return dpi;
 895         }
 896 
 897         public float getScale() {
 898             return scale;
 899         }
 900     }
 901 
 902     public static class StubSystemMenu implements TKSystemMenu {
 903 
 904         private  List<MenuBase> menus = null;
 905 
 906         @Override
 907         public boolean isSupported() {
 908             // Although not all platforms have a system menu, the only real
 909             // interaction with the system menu is this TKSystemMenu instance
 910             // so we'll return true on all platforms.
 911             return true;
 912 //                    final String os = System.getProperty("os.name");
 913 //                    return (os != null && os.startsWith("Mac"));
 914         }
 915 
 916         @Override
 917         public void setMenus(List<MenuBase> menus) {
 918             this.menus = menus;
 919         }
 920 
 921         // make menus accessible to unit tests
 922         public List<MenuBase> getMenus() {
 923             return menus;
 924         }
 925 
 926     }
 927 }