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 }