/******************************************************************************* * Copyright (c) 2009-2013 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Bert Lisser - Bert.Lisser@cwi.nl (CWI) * * Paul Klint - Paul.Klint@cwi.nl - CWI *******************************************************************************/ package org.rascalmpl.eclipse.library.vis.swt.applet; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageLoader; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.rascalmpl.interpreter.IEvaluatorContext; import org.rascalmpl.eclipse.library.vis.figure.Figure; import org.rascalmpl.eclipse.library.vis.figure.FigureFactory; import org.rascalmpl.eclipse.library.vis.figure.combine.Overlap; import org.rascalmpl.eclipse.library.vis.figure.combine.containers.WhiteSpace; import org.rascalmpl.eclipse.library.vis.graphics.SWTGraphicsContext; import org.rascalmpl.eclipse.library.vis.properties.IRunTimePropertyChanges; import org.rascalmpl.eclipse.library.vis.properties.PropertyManager; import org.rascalmpl.eclipse.library.vis.swt.Animation; import org.rascalmpl.eclipse.library.vis.swt.FigureExecutionEnvironment; import org.rascalmpl.eclipse.library.vis.swt.ICallbackEnv; import org.rascalmpl.eclipse.library.vis.swt.IFigureConstructionEnv; import org.rascalmpl.eclipse.library.vis.util.vector.Coordinate; import org.rascalmpl.eclipse.library.vis.util.vector.Rectangle; import io.usethesource.vallang.IConstructor; public class FigureSWTApplet extends Composite implements IFigureConstructionEnv, DisposeListener{ public static final double ANIMATION_TARGET_FPS = 50; public static final int ANIMATION_DELAY = (int)(1000.0/ANIMATION_TARGET_FPS); private static final int SWT_FLAGS = SWT.BORDER | SWT.NO_BACKGROUND | SWT.NO_MERGE_PAINTS ; private List<FigureSWTApplet> children; private Figure figure; private FigureExecutionEnvironment env; private InputHandler inputHandler; private ViewPortHandler viewPortHandler; private RunTimePropertyAdjuster runTimePropertyAdjuster; private List<Overlap> overlapFigures; // shared by inputhandler and viewport handler private boolean redrawRequested; private boolean busy; private int computeClock = -1; private Set<Animation> animations; public FigureSWTApplet(Composite parent, IConstructor cfig, FigureExecutionEnvironment env){ this(parent,cfig,env,true,true); } static int getFlags(boolean hscroll,boolean vscroll){ int result = SWT_FLAGS; if(hscroll) { result = result | SWT.H_SCROLL; } if(vscroll){ result = result | SWT.V_SCROLL; } return result; } public FigureSWTApplet(Composite parent, IConstructor cfig, FigureExecutionEnvironment env,boolean hscroll,boolean vscroll) { super(parent, getFlags(hscroll,vscroll)); this.env = env; runTimePropertyAdjuster = new RunTimePropertyAdjuster(this); children = new ArrayList<FigureSWTApplet>(); overlapFigures = new LinkedList<Overlap>(); Figure fig = FigureFactory.make(this, cfig, null, null); if(!fig.widthDependsOnHeight()){ fig = new WhiteSpace( fig, new PropertyManager()); } this.figure = fig; inputHandler = new InputHandler(this, overlapFigures); viewPortHandler = new ViewPortHandler(this,overlapFigures); animations = new HashSet<Animation>(); addPaintListener(viewPortHandler); addControlListener(viewPortHandler); addMouseListener(inputHandler); addMouseMoveListener(inputHandler); addMouseTrackListener(inputHandler); addKeyListener(inputHandler); triggerRecompute(); } public boolean isUpToDate(){ return computeClock == env.getComputeClock(); } public void animate(){ List<Animation> remove = new ArrayList<Animation>(); for(Animation a : animations){ if(!a.moreFrames()){ remove.add(a); } } for(Animation a : remove){ unregisterAnimation(a); } if(animations.isEmpty()){ return; } getDisplay().timerExec(ANIMATION_DELAY, new Runnable() { @Override public void run() { for(Animation a : animations){ System.out.printf("Animating %d!\n", animations.size()); a.animate(); } try { redraw(); for(FigureSWTApplet app : children){ app.redraw(); } } catch (SWTException e) { if (e.code != SWT.ERROR_WIDGET_DISPOSED) { throw e; // rethrow if its unexpected } // otherwise the window was closed before the animation ended } } }); } public void triggerRecompute(){ redrawRequested = false; busy = true; viewPortHandler.beforeInitialise(); overlapFigures.clear(); for(FigureSWTApplet child : children){ child.triggerRecompute(); } figure.registerIds(this, env.getNameResolver()); figure.registerConverts(env.getNameResolver()); figure.init(this, env.getNameResolver(), null, false, true); viewPortHandler.notifyFigureChanged(); } public void notifyLayoutChanged(){ inputHandler.notifyFigureChanged(); runTimePropertyAdjuster.notifyFigureChanged(); computeClock = env.getComputeClock(); busy = false; if(redrawRequested){ redraw(); } } public void requestRedraw() { if(busy){ redrawRequested = true; } else { if(!isDisposed()){ redraw(); } } } public void handleNonCapturedKeyPress(KeyEvent e){ runTimePropertyAdjuster.handleKeyPress(e); if(e.keyCode == SWT.PRINT_SCREEN){ viewPortHandler.makeScreenShot(); } } public void writeScreenshot(OutputStream to){ viewPortHandler.writeScreenShot(to); } /** * Draw the figure to a new Image. This way the whole figure gets saved unlike with the writeScreenshot method which * only saves that part of the figure which is visible in the applet * * @param to - the output stream to write the image data to */ public void saveImage(OutputStream to) {; int figureWidth = (int)Math.ceil(figure.size.getX()); int figureHeight = (int)Math.ceil(figure.size.getY()); Image image = new Image(getDisplay(), figureWidth, figureHeight); GC gc = new GC(image); SWTGraphicsContext swtGC = new SWTGraphicsContext(); swtGC.setGC(gc); gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE)); figure.draw(new Coordinate(1.0, 1.0), swtGC, new Rectangle(0, 0, figureWidth, figureHeight), new SWTElementsVisibilityManager().getVisibleSWTElementsVector()); ImageLoader il = new ImageLoader(); il.data = new ImageData[] {image.getImageData()}; il.save(to, SWT.IMAGE_PNG); } public FigureExecutionEnvironment getExectutionEnv(){ return env; } @Override public void widgetDisposed(DisposeEvent e) { figure.destroy(this); viewPortHandler.dispose(); inputHandler.dispose(); runTimePropertyAdjuster.dispose(); for(FigureSWTApplet child : children){ child.dispose(); } } public void registerChild(FigureSWTApplet child){ System.out.printf("registering child %s\n", child); children.add(child); } @Override public FigureSWTApplet getSWTParent() { return this; } @Override public ICallbackEnv getCallBackEnv() { return env; } @Override public IEvaluatorContext getRascalContext() { return env.getRascalContext(); } @Override public FigureExecutionEnvironment getFigureExecEnv() { return env; } public Figure getFigure(){ return figure; } public void addOverlapFigure(Overlap overlap){ overlapFigures.add(overlap); } public String toString(){ return "FigureSWTApplet" + figure.toString(); } @Override public IRunTimePropertyChanges getRunTimePropertyChanges() { return runTimePropertyAdjuster; } public void translateFromViewPortToFigure(Coordinate screenLocation) { viewPortHandler.translateFromViewPortToFigure(screenLocation); } public InputHandler getInputHandler(){ return inputHandler; } public void mouseMove(MouseEvent e) { inputHandler.mouseMove(e); } @Override public void registerOverlap(Overlap o) { overlapFigures.add(o); } @Override public void unregisterOverlap(Overlap o) { overlapFigures.remove(o); } @Override public void addSWTElement(Control c) { viewPortHandler.addSWTElement(c); } @Override public void addAboveSWTElement(Figure fig) { viewPortHandler.addAboveSWTElement(fig); } public Image getFigureImage() { return viewPortHandler.getFigureImage(); } public void registerAnimation(Animation a) { animations.add(a); } public void unregisterAnimation(Animation a) { animations.remove(a); } /* private void zoom(double factorX, double factorY){ BoundingBox zoomFactors = new BoundingBox(factorX, factorY); BoundingBox oldFigureSize = new BoundingBox(figureSize); zoom.setWidth(zoom.getWidth() * factorX); zoom.setHeight(zoom.getHeight() * factorY); resize(); for(boolean flip : Figure.BOTH_DIMENSIONS){ if(zoomFactors.getWidth(flip) != 1.0 && oldFigureSize.getWidth(flip) == figureSize.getWidth(flip)){ zoom.setWidth(flip,zoom.getWidth(flip) / zoomFactors.getWidth(flip)); } } if(factorX != 1.0 && factorY != 1.0 && (oldFigureSize.getWidth() == figureSize.getWidth() || oldFigureSize.getHeight() == figureSize.getHeight())){ for(boolean flip : Figure.BOTH_DIMENSIONS){ zoom.setWidth(flip,zoom.getWidth(flip) / factorX); } } else { for(boolean flip : Figure.BOTH_DIMENSIONS){ if(zoomFactors.getWidth(flip) != 1.0 && oldFigureSize.getWidth(flip) == figureSize.getWidth(flip)){ zoom.setWidth(flip,zoom.getWidth(flip) / zoomFactors.getWidth(flip)); } } } for(boolean flip : Figure.BOTH_DIMENSIONS){ double diff = figureSize.getWidth(flip) - viewPortSize.getWidth(flip); viewPortLocation.setX(flip,(viewPortLocation.getX(flip) + mouseLocation.getX(flip)) * zoomFactors.getWidth(flip) - mouseLocation.getX(flip)); viewPortLocation.setX(flip,Math.max(0,Math.min(diff,viewPortLocation.getX(flip)))); } if(!oldFigureSize.isEq(figureSize)){ redraw(); } } */ /* private void scrollOnMouseDrag() { double factor = 1.0; if(!mouseDown){ long newTime = System.nanoTime(); long diff = newTime - lastTime; factor = (double)diff / 10000000; lastTime = newTime; } for(boolean flip : Figure.BOTH_DIMENSIONS){ double diff = figureSize.getWidth(flip) - viewPortSize.getWidth(flip); viewPortLocation.addX(flip, -(mouseSpeed.getX(flip) * factor)); if(viewPortLocation.getX(flip) > diff || viewPortLocation.getX(flip) < 0){ mouseSpeed.setX(flip,-mouseSpeed.getX(flip)); } viewPortLocation.capXBelow(flip, diff); viewPortLocation.capXAbove(flip,0); } getHorizontalBar().setSelection((int)viewPortLocation.getX()); getVerticalBar().setSelection((int)viewPortLocation.getY()); redraw(); } */ }