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 }