/* * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * StubToolkit.java */ package com.sun.javafx.pgstub; import com.sun.glass.ui.CommonDialogs.FileChooserResult; import com.sun.javafx.application.PlatformImpl; import com.sun.javafx.embed.HostInterface; import com.sun.javafx.geom.Path2D; import com.sun.javafx.geom.Shape; import com.sun.javafx.geom.transform.BaseTransform; import com.sun.javafx.menu.MenuBase; import com.sun.javafx.perf.PerformanceTracker; import com.sun.javafx.runtime.async.AsyncOperation; import com.sun.javafx.runtime.async.AsyncOperationListener; import com.sun.javafx.scene.text.HitInfo; import com.sun.javafx.scene.text.TextLayoutFactory; import com.sun.javafx.tk.*; import com.sun.prism.BasicStroke; import com.sun.scenario.DelayedRunnable; import com.sun.scenario.animation.AbstractMasterTimer; import com.sun.scenario.effect.FilterContext; import com.sun.scenario.effect.Filterable; import javafx.application.ConditionalFeature; import javafx.geometry.Dimension2D; import javafx.scene.image.Image; import javafx.scene.input.*; import javafx.scene.paint.Color; import javafx.scene.paint.ImagePattern; import javafx.scene.paint.LinearGradient; import javafx.scene.paint.RadialGradient; import javafx.scene.shape.*; import javafx.stage.FileChooser.ExtensionFilter; import javafx.stage.Modality; import javafx.stage.StageStyle; import javafx.stage.Window; import javafx.util.Pair; import java.io.File; import java.io.InputStream; import java.security.AccessControlContext; import java.util.*; import java.util.concurrent.Future; /** * A Toolkit implementation for use with Testing. * * @author Richard */ public class StubToolkit extends Toolkit { private Map contextMap = new HashMap(); private StubMasterTimer masterTimer = new StubMasterTimer(); private PerformanceTracker performanceTracker = new StubPerformanceTracker(); private final StubImageLoaderFactory imageLoaderFactory = new StubImageLoaderFactory(); private CursorSizeConverter cursorSizeConverter = CursorSizeConverter.NO_CURSOR_SUPPORT; private int maximumCursorColors = 2; private TKScreenConfigurationListener screenConfigurationListener; private static final ScreenConfiguration[] DEFAULT_SCREEN_CONFIG = { new ScreenConfiguration(0, 0, 1920, 1200, 0, 0, 1920, 1172, 96) }; private ScreenConfiguration[] screenConfigurations = DEFAULT_SCREEN_CONFIG; static { try { // ugly hack to initialize "runLater" method in Platform.java PlatformImpl.startup(() -> {}); } catch (Exception ex) {} // allow tests to access PG scenegraph // so that they can run with assertion enabled javafx.scene.Scene.impl_setAllowPGAccess(true); } private boolean pulseRequested; /* * overrides of Toolkit's abstract functions */ @Override public boolean init() { return true; } @Override public TKStage createTKStage(Window peerWindow, boolean securityDialog, StageStyle stageStyle, boolean primary, Modality modality, TKStage owner, boolean rtl, AccessControlContext acc) { return new StubStage(); } @Override public TKStage createTKPopupStage(Window peerWindow, StageStyle popupStyle, TKStage owner, AccessControlContext acc) { return new StubPopupStage(); } @Override public TKStage createTKEmbeddedStage(HostInterface host, AccessControlContext acc) { return new StubStage(); } @Override public AppletWindow createAppletWindow(long parent, String serverName) { // unsupported return null; } @Override public void closeAppletWindow() { // unsupported } private final TKSystemMenu systemMenu = new StubSystemMenu(); @Override public TKSystemMenu getSystemMenu() { return systemMenu; } @Override public void startup(Runnable runnable) { runnable.run(); } @Override public void checkFxUserThread() { // Do nothing } @Override public boolean isFxUserThread() { // Always on the FX app thread return true; } @Override public void defer(Runnable runnable) { runnable.run(); } @Override public void exit() { System.exit(0); } @Override public Future addRenderJob(RenderJob rj) { return rj; } @Override public Map getContextMap() { return contextMap; } @Override public int getRefreshRate() { return -1; } private DelayedRunnable animationRunnable; @Override public void setAnimationRunnable(DelayedRunnable animationRunnable) { this.animationRunnable = animationRunnable; } @Override public PerformanceTracker getPerformanceTracker() { return performanceTracker; } @Override public PerformanceTracker createPerformanceTracker() { return new StubPerformanceTracker(); } @Override protected Object createColorPaint(Color paint) { return new com.sun.prism.paint.Color((float) paint.getRed(), (float) paint.getGreen(), (float) paint.getBlue(), (float) paint.getOpacity()); } @Override protected Object createLinearGradientPaint(LinearGradient paint) { // Non functioning but compiles return new com.sun.prism.paint.Color(1, 1, 1, 1); } @Override protected Object createRadialGradientPaint(RadialGradient paint) { // Non functioning but compiles return new com.sun.prism.paint.Color(1, 1, 1, 1); } @Override protected Object createImagePatternPaint(ImagePattern paint) { // Non functioning but compiles return new com.sun.prism.paint.Color(1, 1, 1, 1); } static BasicStroke tmpStroke = new BasicStroke(); void initStroke(StrokeType pgtype, double strokewidth, StrokeLineCap pgcap, StrokeLineJoin pgjoin, float miterLimit) { int type; if (pgtype == StrokeType.CENTERED) { type = BasicStroke.TYPE_CENTERED; } else if (pgtype == StrokeType.INSIDE) { type = BasicStroke.TYPE_INNER; } else { type = BasicStroke.TYPE_OUTER; } int cap; if (pgcap == StrokeLineCap.BUTT) { cap = BasicStroke.CAP_BUTT; } else if (pgcap == StrokeLineCap.SQUARE) { cap = BasicStroke.CAP_SQUARE; } else { cap = BasicStroke.CAP_ROUND; } int join; if (pgjoin == StrokeLineJoin.BEVEL) { join = BasicStroke.JOIN_BEVEL; } else if (pgjoin == StrokeLineJoin.MITER) { join = BasicStroke.JOIN_MITER; } else { join = BasicStroke.JOIN_ROUND; } tmpStroke.set(type, (float) strokewidth, cap, join, miterLimit); } @Override public void accumulateStrokeBounds(Shape shape, float bbox[], StrokeType pgtype, double strokewidth, StrokeLineCap pgcap, StrokeLineJoin pgjoin, float miterLimit, BaseTransform tx) { initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit); // TODO: The accumulation could be done directly without creating a Shape Shape.accumulate(bbox, tmpStroke.createStrokedShape(shape), tx); } @Override public boolean strokeContains(Shape shape, double x, double y, StrokeType pgtype, double strokewidth, StrokeLineCap pgcap, StrokeLineJoin pgjoin, float miterLimit) { initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit); // TODO: The contains testing could be done directly without creating a Shape return tmpStroke.createStrokedShape(shape).contains((float) x, (float) y); } @Override public Shape createStrokedShape(Shape shape, StrokeType pgtype, double strokewidth, StrokeLineCap pgcap, StrokeLineJoin pgjoin, float miterLimit, float[] dashArray, float dashOffset) { initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit); return tmpStroke.createStrokedShape(shape); } public CursorSizeConverter getCursorSizeConverter() { return cursorSizeConverter; } public void setCursorSizeConverter( CursorSizeConverter cursorSizeConverter) { this.cursorSizeConverter = cursorSizeConverter; } @Override public Dimension2D getBestCursorSize(int preferredWidth, int preferredHeight) { return cursorSizeConverter.getBestCursorSize(preferredWidth, preferredHeight); } @Override public int getMaximumCursorColors() { return maximumCursorColors; } public void setMaximumCursorColors(int maximumCursorColors) { this.maximumCursorColors = maximumCursorColors; } @Override public AbstractMasterTimer getMasterTimer() { return masterTimer; } @Override public FontLoader getFontLoader() { return new StubFontLoader(); } @Override public TextLayoutFactory getTextLayoutFactory() { return new StubTextLayoutFactory(); } @Override public boolean isSupported(ConditionalFeature feature) { if (feature == ConditionalFeature.SCENE3D) { return true; } else if (feature == ConditionalFeature.TRANSPARENT_WINDOW) { return true; } return false; } /* * additional testing functions */ public void fireTestPulse() { firePulse(); } public boolean isPulseRequested() { return pulseRequested; } public void clearPulseRequested() { pulseRequested = false; } // do nothing -- bringing in FrameJob and MasterTimer also bring in // Settings and crap which isn't setup for the testing stuff because // we don't run through a RuntimeProvider or do normal startup // public @Override public void triggerNextPulse():Void { } @Override public void requestNextPulse() { pulseRequested = true; } private TKClipboard clipboard = new TKClipboard() { private Map map = new HashMap(); private Image image; private double offsetX; private double offsetY; @Override public void setSecurityContext(AccessControlContext ctx) { } @Override public Set getContentTypes() { return map.keySet(); } @Override public boolean putContent(Pair... content) { boolean good; for (Pair pair : content) { good = map.put(pair.getKey(), pair.getValue()) == pair.getValue(); if (!good) return false; } return true; } @Override public Object getContent(DataFormat dataFormat) { return map.get(dataFormat); } @Override public boolean hasContent(DataFormat dataFormat) { return map.containsKey(dataFormat); } @Override public Set getTransferModes() { Set modes = new HashSet(); modes.add(TransferMode.COPY); return modes; } @Override public void setDragView(Image image) { this.image = image; } @Override public void setDragViewOffsetX(double offsetX) { this.offsetX = offsetX; } @Override public void setDragViewOffsetY(double offsetY) { this.offsetY = offsetY; } @Override public Image getDragView() { return image; } @Override public double getDragViewOffsetX() { return offsetX; } @Override public double getDragViewOffsetY() { return offsetY; } }; @Override public TKClipboard getSystemClipboard() { return clipboard; } @Override public TKClipboard getNamedClipboard(String name) { return null; } public static TKClipboard createDragboard() { StubToolkit tk = (StubToolkit)Toolkit.getToolkit(); if (tk.dndDelegate != null) { return tk.dndDelegate.createDragboard(); } return null; } @Override public void enableDrop(TKScene s, TKDropTargetListener l) { if (dndDelegate != null) { dndDelegate.enableDrop(l); } } private ScreenConfigurationAccessor accessor = new ScreenConfigurationAccessor() { @Override public int getMinX(Object obj) { return ((ScreenConfiguration) obj).getMinX(); } @Override public int getMinY(Object obj) { return ((ScreenConfiguration) obj).getMinY(); } @Override public int getWidth(Object obj) { return ((ScreenConfiguration) obj).getWidth(); } @Override public int getHeight(Object obj) { return ((ScreenConfiguration) obj).getHeight(); } @Override public int getVisualMinX(Object obj) { return ((ScreenConfiguration) obj).getVisualMinX(); } @Override public int getVisualMinY(Object obj) { return ((ScreenConfiguration) obj).getVisualMinY(); } @Override public int getVisualWidth(Object obj) { return ((ScreenConfiguration) obj).getVisualWidth(); } @Override public int getVisualHeight(Object obj) { return ((ScreenConfiguration) obj).getVisualHeight(); } @Override public float getDPI(Object obj) { return ((ScreenConfiguration) obj).getDPI(); } @Override public float getScale(Object obj) { return ((ScreenConfiguration) obj).getScale(); } }; @Override public ScreenConfigurationAccessor setScreenConfigurationListener( TKScreenConfigurationListener listener) { screenConfigurationListener = listener; return accessor; } @Override public ScreenConfiguration getPrimaryScreen() { return screenConfigurations[0]; } public void setScreens(ScreenConfiguration... screenConfigurations) { this.screenConfigurations = screenConfigurations.clone(); if (screenConfigurationListener != null) { screenConfigurationListener.screenConfigurationChanged(); } } public void resetScreens() { setScreens(DEFAULT_SCREEN_CONFIG); } @Override public List getScreens() { return Arrays.asList(screenConfigurations); } @Override public ScreenConfigurationAccessor getScreenConfigurationAccessor() { return accessor; } @Override public void registerDragGestureListener(TKScene s, Set tm, TKDragGestureListener l) { if (dndDelegate != null) { dndDelegate.registerListener(l); } } @Override public void startDrag(TKScene scene, Set tm, TKDragSourceListener l, Dragboard dragboard) { if (dndDelegate != null) { dndDelegate.startDrag(scene, tm, l, dragboard); } } @Override public ImageLoader loadImage(String url, int width, int height, boolean preserveRatio, boolean smooth) { return imageLoaderFactory.createImageLoader(url, width, height, preserveRatio, smooth); } @Override public ImageLoader loadImage(InputStream stream, int width, int height, boolean preserveRatio, boolean smooth) { return imageLoaderFactory.createImageLoader(stream, width, height, preserveRatio, smooth); } @Override public AsyncOperation loadImageAsync( AsyncOperationListener listener, String url, int width, int height, boolean preserveRatio, boolean smooth) { return imageLoaderFactory.createAsyncImageLoader( listener, url, width, height, preserveRatio, smooth); } @Override public ImageLoader loadPlatformImage(Object platformImage) { return imageLoaderFactory.createImageLoader(platformImage, 0, 0, false, false); } @Override public PlatformImage createPlatformImage(int w, int h) { PlatformImage image = new StubWritablePlatformImage(w, h); imageLoaderFactory.registerImage(image, new StubPlatformImageInfo(w, h)); return image; } @Override public void waitFor(Task t) { throw new UnsupportedOperationException(); } @Override public int getKeyCodeForChar(String character) { if (charToKeyCodeMap != null) { final KeyCode keyCode = charToKeyCodeMap.get(character); if (keyCode != null) { return keyCode.impl_getCode(); } } return 0; } @Override public PathElement[] convertShapeToFXPath(Object shape) { // Had to be mocked up for TextField tests (for the caret!) // Since the "shape" could be anything, I'm just returning // something here, doesn't matter what. return new PathElement[0]; } @Override public HitInfo convertHitInfoToFX(Object hit) { return (HitInfo) hit; } @Override public Filterable toFilterable(Image img) { return StubFilterable.create((StubPlatformImage)img.impl_getPlatformImage()); } @Override public FilterContext getFilterContext(Object config) { throw new UnsupportedOperationException(); } @Override public boolean isForwardTraversalKey(KeyEvent e) { throw new UnsupportedOperationException(); } @Override public boolean isBackwardTraversalKey(KeyEvent e) { throw new UnsupportedOperationException(); } @Override public Object createSVGPathObject(SVGPath svgpath) { int windingRule = (svgpath.getFillRule() == FillRule.NON_ZERO) ? Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD; return new SVGPathImpl(svgpath.getContent(), windingRule); } @Override public Path2D createSVGPath2D(SVGPath svgpath) { int windingRule = (svgpath.getFillRule() == FillRule.NON_ZERO) ? Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD; return new SVGPathImpl(svgpath.getContent(), windingRule); } @Override public boolean imageContains(Object image, float x, float y) { return ((StubPlatformImage) image).getImageInfo() .contains((int) x, (int) y); } public void setCurrentTime(long millis) { masterTimer.setCurrentTime(millis); } public void handleAnimation() { if (animationRunnable != null) { try { animationRunnable.run(); } catch (Throwable t) { t.printStackTrace(); } } } public StubImageLoaderFactory getImageLoaderFactory() { return imageLoaderFactory; } public void setAnimationTime(final long millis) { setCurrentTime(millis); handleAnimation(); fireTestPulse(); } @Override public void installInputMethodRequests(TKScene scene, InputMethodRequests requests) { // just do nothing here. } private Map charToKeyCodeMap; public void setCharToKeyCodeMap(Map charToKeyCodeMap) { this.charToKeyCodeMap = charToKeyCodeMap; } @Override public Object renderToImage(ImageRenderingContext context) { throw new UnsupportedOperationException(); } @Override public boolean canStartNestedEventLoop() { return false; } @Override public Object enterNestedEventLoop(Object key) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void exitNestedEventLoop(Object key, Object rval) { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean isNestedLoopRunning() { return false; } private KeyCode platformShortcutKey = KeyCode.SHORTCUT; public void setPlatformShortcutKey(final KeyCode platformShortcutKey) { this.platformShortcutKey = platformShortcutKey; } public KeyCode getPlatformShortcutKey() { return platformShortcutKey; } private DndDelegate dndDelegate; public void setDndDelegate(DndDelegate dndDelegate) { this.dndDelegate = dndDelegate; } public interface DndDelegate { void startDrag(TKScene scene, Set tm, TKDragSourceListener l, Dragboard dragboard); TKClipboard createDragboard(); DragEvent convertDragEventToFx(Object event, Dragboard dragboard); void registerListener(TKDragGestureListener l); void enableDrop(TKDropTargetListener l); } public interface CommonDialogsSupport { FileChooserResult showFileChooser(TKStage ownerWindow, String title, File initialDirectory, String initialFileName, FileChooserType fileChooserType, List extensionFilters, ExtensionFilter selectedFilter); File showDirectoryChooser(TKStage ownerWindow, String title, File initialDirectory); } private CommonDialogsSupport commonDialogsSupport; public void setCommonDialogsSupport( final CommonDialogsSupport commonDialogsSupport) { this.commonDialogsSupport = commonDialogsSupport; } @Override public FileChooserResult showFileChooser(TKStage ownerWindow, String title, File initialDirectory, String initialFileName, FileChooserType fileChooserType, List extensionFilters, ExtensionFilter selectedFilter) { return commonDialogsSupport.showFileChooser( ownerWindow, title, initialDirectory, initialFileName, fileChooserType, extensionFilters, selectedFilter); } @Override public File showDirectoryChooser(TKStage ownerWindow, String title, File initialDirectory) { return commonDialogsSupport.showDirectoryChooser( ownerWindow, title, initialDirectory); } @Override public long getMultiClickTime() { return 500L; } @Override public int getMultiClickMaxX() { return 5; } @Override public int getMultiClickMaxY() { return 5; } public static final class ScreenConfiguration { private final int minX; private final int minY; private final int width; private final int height; private final int visualMinX; private final int visualMinY; private final int visualWidth; private final int visualHeight; private final float dpi; private final float scale; public ScreenConfiguration(final int minX, final int minY, final int width, final int height, final int visualMinX, final int visualMinY, final int visualWidth, final int visualHeight, final float dpi) { this.minX = minX; this.minY = minY; this.width = width; this.height = height; this.visualMinX = visualMinX; this.visualMinY = visualMinY; this.visualWidth = visualWidth; this.visualHeight = visualHeight; this.dpi = dpi; this.scale = 1; // TODO: add a constructor that takes scale } public int getMinX() { return minX; } public int getMinY() { return minY; } public int getWidth() { return width; } public int getHeight() { return height; } public int getVisualMinX() { return visualMinX; } public int getVisualMinY() { return visualMinY; } public int getVisualWidth() { return visualWidth; } public int getVisualHeight() { return visualHeight; } public float getDPI() { return dpi; } public float getScale() { return scale; } } public static class StubSystemMenu implements TKSystemMenu { private List menus = null; @Override public boolean isSupported() { // Although not all platforms have a system menu, the only real // interaction with the system menu is this TKSystemMenu instance // so we'll return true on all platforms. return true; // final String os = System.getProperty("os.name"); // return (os != null && os.startsWith("Mac")); } @Override public void setMenus(List menus) { this.menus = menus; } // make menus accessible to unit tests public List getMenus() { return menus; } } }